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(&register.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(&register.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  }