github.com/coreos/mantle@v0.13.0/kola/tests/misc/raid.go (about) 1 // Copyright 2017 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package misc 16 17 import ( 18 "encoding/json" 19 20 "github.com/coreos/mantle/kola/cluster" 21 "github.com/coreos/mantle/kola/register" 22 "github.com/coreos/mantle/platform" 23 "github.com/coreos/mantle/platform/conf" 24 "github.com/coreos/mantle/platform/machine/qemu" 25 "github.com/coreos/mantle/platform/machine/unprivqemu" 26 ) 27 28 var ( 29 raidRootUserData = conf.ContainerLinuxConfig(`storage: 30 disks: 31 - device: "/dev/disk/by-id/virtio-secondary" 32 wipe_table: true 33 partitions: 34 - label: root1 35 number: 1 36 size: 256MiB 37 type_guid: be9067b9-ea49-4f15-b4f6-f36f8c9e1818 38 - label: root2 39 number: 2 40 size: 256MiB 41 type_guid: be9067b9-ea49-4f15-b4f6-f36f8c9e1818 42 raid: 43 - name: "rootarray" 44 level: "raid1" 45 devices: 46 - "/dev/disk/by-partlabel/root1" 47 - "/dev/disk/by-partlabel/root2" 48 filesystems: 49 - name: "ROOT" 50 mount: 51 device: "/dev/md/rootarray" 52 format: "ext4" 53 label: ROOT 54 - name: "NOT_ROOT" 55 mount: 56 device: "/dev/disk/by-id/virtio-primary-disk-part9" 57 format: "ext4" 58 label: wasteland 59 wipe_filesystem: true`) 60 ) 61 62 func init() { 63 register.Register(®ister.Test{ 64 // This test needs additional disks which is only supported on qemu since Ignition 65 // does not support deleting partitions without wiping the partition table and the 66 // disk doesn't have room for new partitions. 67 // TODO(ajeddeloh): change this to delete partition 9 and replace it with 9 and 10 68 // once Ignition supports it. 69 Run: RootOnRaid, 70 ClusterSize: 0, 71 Platforms: []string{"qemu"}, 72 Name: "cl.disk.raid.root", 73 Distros: []string{"cl"}, 74 }) 75 register.Register(®ister.Test{ 76 Run: DataOnRaid, 77 ClusterSize: 1, 78 Name: "cl.disk.raid.data", 79 UserData: conf.ContainerLinuxConfig(`storage: 80 raid: 81 - name: "DATA" 82 level: "raid1" 83 devices: 84 - "/dev/disk/by-partlabel/OEM-CONFIG" 85 - "/dev/disk/by-partlabel/USR-B" 86 filesystems: 87 - name: "DATA" 88 mount: 89 device: "/dev/md/DATA" 90 format: "ext4" 91 label: DATA 92 systemd: 93 units: 94 - name: "var-lib-data.mount" 95 enable: true 96 contents: | 97 [Mount] 98 What=/dev/md/DATA 99 Where=/var/lib/data 100 Type=ext4 101 102 [Install] 103 WantedBy=local-fs.target`), 104 Distros: []string{"cl"}, 105 }) 106 } 107 108 func RootOnRaid(c cluster.TestCluster) { 109 var m platform.Machine 110 var err error 111 options := platform.MachineOptions{ 112 AdditionalDisks: []platform.Disk{ 113 {Size: "520M", DeviceOpts: []string{"serial=secondary"}}, 114 }, 115 } 116 switch pc := c.Cluster.(type) { 117 // These cases have to be separated because when put together to the same case statement 118 // the golang compiler no longer checks that the individual types in the case have the 119 // NewMachineWithOptions function, but rather whether platform.Cluster does which fails 120 case *qemu.Cluster: 121 m, err = pc.NewMachineWithOptions(raidRootUserData, options) 122 case *unprivqemu.Cluster: 123 m, err = pc.NewMachineWithOptions(raidRootUserData, options) 124 default: 125 c.Fatal("unknown cluster type") 126 } 127 if err != nil { 128 c.Fatal(err) 129 } 130 131 checkIfMountpointIsRaid(c, m, "/") 132 133 // reboot it to make sure it comes up again 134 err = m.Reboot() 135 if err != nil { 136 c.Fatalf("could not reboot machine: %v", err) 137 } 138 139 checkIfMountpointIsRaid(c, m, "/") 140 } 141 142 func DataOnRaid(c cluster.TestCluster) { 143 m := c.Machines()[0] 144 145 checkIfMountpointIsRaid(c, m, "/var/lib/data") 146 147 // reboot it to make sure it comes up again 148 err := m.Reboot() 149 if err != nil { 150 c.Fatalf("could not reboot machine: %v", err) 151 } 152 153 checkIfMountpointIsRaid(c, m, "/var/lib/data") 154 } 155 156 type lsblkOutput struct { 157 Blockdevices []blockdevice `json:"blockdevices"` 158 } 159 160 type blockdevice struct { 161 Name string `json:"name"` 162 Type string `json:"type"` 163 Mountpoint *string `json:"mountpoint"` 164 Children []blockdevice `json:"children"` 165 } 166 167 // checkIfMountpointIsRaid will check if a given machine has a device of type 168 // raid1 mounted at the given mountpoint. If it does not, the test is failed. 169 func checkIfMountpointIsRaid(c cluster.TestCluster, m platform.Machine, mountpoint string) { 170 output := c.MustSSH(m, "lsblk --json") 171 172 l := lsblkOutput{} 173 err := json.Unmarshal(output, &l) 174 if err != nil { 175 c.Fatalf("couldn't unmarshal lsblk output: %v", err) 176 } 177 178 foundRoot := checkIfMountpointIsRaidWalker(c, l.Blockdevices, mountpoint) 179 if !foundRoot { 180 c.Fatalf("didn't find root mountpoint in lsblk output") 181 } 182 } 183 184 // checkIfMountpointIsRaidWalker will iterate over bs and recurse into its 185 // children, looking for a device mounted at / with type raid1. true is returned 186 // if such a device is found. The test is failed if a device of a different type 187 // is found to be mounted at /. 188 func checkIfMountpointIsRaidWalker(c cluster.TestCluster, bs []blockdevice, mountpoint string) bool { 189 for _, b := range bs { 190 if b.Mountpoint != nil && *b.Mountpoint == mountpoint { 191 if b.Type != "raid1" { 192 c.Fatalf("device %q is mounted at %q with type %q (was expecting raid1)", b.Name, mountpoint, b.Type) 193 } 194 return true 195 } 196 foundRoot := checkIfMountpointIsRaidWalker(c, b.Children, mountpoint) 197 if foundRoot { 198 return true 199 } 200 } 201 return false 202 }