/
switchkernel.go
253 lines (220 loc) · 7.15 KB
/
switchkernel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright 2020 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"encoding/base64"
"fmt"
"os"
"regexp"
"strings"
"github.com/coreos/coreos-assembler/mantle/kola"
"github.com/coreos/coreos-assembler/mantle/platform"
"github.com/coreos/coreos-assembler/mantle/platform/conf"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
cmdSwitchKernel = &cobra.Command{
RunE: runSwitchKernel,
PreRunE: preRun,
Use: "switch-kernel",
Short: "Test on switching between Default and RT Kernel",
SilenceUsage: true,
}
rtKernelRpmDir string
)
var (
homeDir = `/var/home/core`
switchKernelScript = `#!/usr/bin/env bash
# This script is a shameless translation of: https://github.com/openshift/machine-config-operator/blob/f363c7be6d2d506d900e196fa2e2d05ca08b93b6/pkg/daemon/update.go#L651
# Usage:
# switch-kernel oldkernel newkernel rt-kernel-repo
# {oldkernel, newkernel}: either of {default, rt-kernel}
# rt-kernel-repo (optional): repository of kernel-rt packages
set -xeuo pipefail
FROM_KERNEL="${1:?old kernel should be either of \{default, rt-kernel\}}"
TO_KERNEL="${2:?new kernel should be either of \{default, rt-kernel\}}"
DEFAULT_KERNEL_PKG="kernel kernel-core kernel-modules kernel-modules-extra"
RT_KERNEL_PKG="kernel-rt-core kernel-rt-modules kernel-rt-modules-extra"
if [[ $FROM_KERNEL == "default" && $TO_KERNEL == "rt-kernel" ]]; then
# Switch from default to RT Kernel
# https://github.com/openshift/machine-config-operator/blob/e246be62e7839a086bc4494203472349c406dcae/pkg/daemon/update.go#L711
RT_KERNEL_REPO=$3
if [[ -z $(ls ${RT_KERNEL_REPO}) ]]; then
echo "No kernel-rt package available in the repo: ${RT_KERNEL_REPO}"
exit 1
fi
ARGS="override remove ${DEFAULT_KERNEL_PKG}"
for RPM in $(ls ${RT_KERNEL_REPO})
do
ARGS+=" --install ${RT_KERNEL_REPO}/${RPM}"
done
rpm-ostree ${ARGS}
elif [[ $FROM_KERNEL == "rt-kernel" && $TO_KERNEL == "default" ]]; then
# Switch from RT Kernel to default
# https://github.com/openshift/machine-config-operator/blob/e246be62e7839a086bc4494203472349c406dcae/pkg/daemon/update.go#L667
ARGS="override reset ${DEFAULT_KERNEL_PKG}"
for PKG in $RT_KERNEL_PKG
do
ARGS+=" --uninstall $PKG"
done
rpm-ostree ${ARGS}
else
echo -e "Invalid options: $@" && exit 1
fi
`
)
func init() {
cmdSwitchKernel.Flags().StringVar(&rtKernelRpmDir, "kernel-rt", "", "Path to kernel rt rpm directory")
err := cmdSwitchKernel.MarkFlagRequired("kernel-rt")
if err != nil {
panic(err)
}
root.AddCommand(cmdSwitchKernel)
}
func runSwitchKernel(cmd *cobra.Command, args []string) error {
// currently only supports RHCOS
if kola.Options.Distribution != "rhcos" {
return fmt.Errorf("Only supports `rhcos`")
}
var userdata *conf.UserData = conf.Ignition(fmt.Sprintf(`{
"ignition": {
"version": "2.2.0"
},
"storage": {
"files": [
{
"filesystem": "root",
"path": "/var/home/core/switch-kernel.sh",
"contents": {
"source": "data:text/plain;base64,%s"
},
"mode": 110
}
]
}
}`, base64.StdEncoding.EncodeToString([]byte(switchKernelScript))))
flight, err := kola.NewFlight(kolaPlatform)
if err != nil {
return errors.Wrapf(err, "failed to create new flight")
}
defer flight.Destroy()
c, err := flight.NewCluster(&platform.RuntimeConfig{})
if err != nil {
return errors.Wrapf(err, "failed to create new cluster")
}
defer c.Destroy()
m, err := c.NewMachine(userdata)
if err != nil {
return errors.Wrapf(err, "failed to spawn new machine")
}
defer m.Destroy()
err = testSwitchKernel(c)
if err != nil {
return errors.Wrapf(err, "failed switch kernel test")
}
return nil
}
// Drops Kernel RT RPM files under the directory `localPath` to `$homeDir/kernel-rt-rpms` directory in m
func dropRpmFilesAll(m platform.Machine, localPath string) error {
fmt.Println("Dropping RT Kernel RPMs...")
re := regexp.MustCompile(`^kernel-rt-.*\.rpm$`)
files, err := os.ReadDir(localPath)
if err != nil {
return err
}
for _, f := range files {
filename := f.Name()
filepath := strings.TrimSuffix(localPath, "/") + "/" + filename
targetPath := fmt.Sprintf("%s/kernel-rt-rpms/%s", homeDir, filename)
if re.MatchString(filename) {
in, err := os.Open(filepath)
if err != nil {
return err
}
if err := platform.InstallFile(in, m, targetPath); err != nil {
return errors.Wrapf(err, "failed to upload %s", filename)
}
}
}
return nil
}
func switchDefaultToRtKernel(c platform.Cluster, m platform.Machine) error {
// run the script to switch from default kernel to rt kernel
fmt.Println("Switching from Default to RT Kernel...")
cmd := "sudo " + homeDir + "/switch-kernel.sh default rt-kernel " + homeDir + "/kernel-rt-rpms/"
stdout, stderr, err := m.SSH(cmd)
if err != nil {
return errors.Wrapf(err, "failed to run %s", cmd)
}
fmt.Printf("%s\n", stderr)
fmt.Printf("%s\n", stdout)
// reboot the machine to switch kernel
fmt.Println("Rebooting machine...")
err = m.Reboot()
if err != nil {
return errors.Wrapf(err, "failed to reboot machine")
}
// check if the kernel has switched to rt kernel
fmt.Println("Checking kernel type...")
cmd = "uname -v | grep -q 'PREEMPT RT'"
_, _, err = m.SSH(cmd)
if err != nil {
return errors.Wrapf(err, "failed to run %s", cmd)
}
fmt.Println("Switched to RT Kernel successfully!")
return nil
}
func switchRtKernelToDefault(c platform.Cluster, m platform.Machine) error {
// run the script to switch from rt kernel to default
fmt.Println("Switching from RT to Default Kernel...")
cmd := "sudo " + homeDir + "/switch-kernel.sh rt-kernel default"
stdout, stderr, err := m.SSH(cmd)
if err != nil {
return errors.Wrapf(err, "failed to run %s", cmd)
}
fmt.Printf("%s\n", stderr)
fmt.Printf("%s\n", stdout)
// reboot the machine to switch kernel
fmt.Println("Rebooting machine...")
err = m.Reboot()
if err != nil {
return errors.Wrapf(err, "failed to reboot machine")
}
// check if the kernel has switched back to default kernel
fmt.Println("Checking kernel type...")
cmd = "uname -v | grep -qv 'PREEMPT RT'"
_, _, err = m.SSH(cmd)
if err != nil {
return errors.Wrapf(err, "failed to run %s", cmd)
}
fmt.Println("Switched back to Default Kernel successfully!")
return nil
}
func testSwitchKernel(c platform.Cluster) error {
m := c.Machines()[0]
err := dropRpmFilesAll(m, rtKernelRpmDir)
if err != nil {
return errors.Wrapf(err, "failed to drop Kernel RT RPM files")
}
err = switchDefaultToRtKernel(c, m)
if err != nil {
return errors.Wrapf(err, "failed to switch from Default to RT Kernel")
}
err = switchRtKernelToDefault(c, m)
if err != nil {
return errors.Wrapf(err, "failed switching from RT to Default Kernel")
}
return nil
}