From 5179c9bfcdc7540c063d69207dd06d456c5af66b Mon Sep 17 00:00:00 2001 From: XiaoMeiZheng Date: Fri, 27 Sep 2019 14:11:23 +0800 Subject: [PATCH] providers: add initial zVM hypervisor support --- doc/supported-platforms.md | 1 + internal/distro/distro.go | 11 ++++ internal/platform/platform.go | 5 ++ internal/providers/zvm/zvm.go | 108 ++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 internal/providers/zvm/zvm.go diff --git a/doc/supported-platforms.md b/doc/supported-platforms.md index 2d705f2f71..b108d73bd2 100644 --- a/doc/supported-platforms.md +++ b/doc/supported-platforms.md @@ -10,6 +10,7 @@ Ignition is currently only supported for the following platforms: * [Packet] - Ignition will read its configuration from the instance userdata. SSH keys are handled by coreos-metadata. * [QEMU] - Ignition will read its configuration from the 'opt/com.coreos/config' key on the QEMU Firmware Configuration Device (available in QEMU 2.4.0 and higher). * [DigitalOcean] - Ignition will read its configuration from the droplet userdata. SSH keys and network configuration are handled by coreos-metadata. +* [zVM] - Ignition will read its configuration from the reader device directly. The vmur program is necessary, which requires the vmcp and vmur kernel module as prerequisite, and the corresponding z/VM virtual unit record devices (in most cases 000c as reader, 000d as punch) must be set online. Ignition is under active development so expect this list to expand in the coming months. diff --git a/internal/distro/distro.go b/internal/distro/distro.go index d2c96c0510..de18a9ce1f 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -37,6 +37,7 @@ var ( mdadmCmd = "mdadm" mountCmd = "mount" sgdiskCmd = "sgdisk" + modprobeCmd = "modprobe" udevadmCmd = "udevadm" usermodCmd = "usermod" useraddCmd = "useradd" @@ -52,6 +53,11 @@ var ( vfatMkfsCmd = "mkfs.vfat" xfsMkfsCmd = "mkfs.xfs" + //zVM programs + vmurCmd = "vmur" + chccwdevCmd = "chccwdev" + cioIgnoreCmd = "cio_ignore" + // Flags selinuxRelabel = "true" blackboxTesting = "false" @@ -73,6 +79,7 @@ func GroupaddCmd() string { return groupaddCmd } func MdadmCmd() string { return mdadmCmd } func MountCmd() string { return mountCmd } func SgdiskCmd() string { return sgdiskCmd } +func ModprobeCmd() string { return modprobeCmd } func UdevadmCmd() string { return udevadmCmd } func UsermodCmd() string { return usermodCmd } func UseraddCmd() string { return useraddCmd } @@ -84,6 +91,10 @@ func SwapMkfsCmd() string { return swapMkfsCmd } func VfatMkfsCmd() string { return vfatMkfsCmd } func XfsMkfsCmd() string { return xfsMkfsCmd } +func VmurCmd() string { return vmurCmd } +func ChccwdevCmd() string { return chccwdevCmd } +func CioIgnoreCmd() string { return cioIgnoreCmd } + func SelinuxRelabel() bool { return bakedStringToBool(selinuxRelabel) && !BlackboxTesting() } func BlackboxTesting() bool { return bakedStringToBool(blackboxTesting) } func WriteAuthorizedKeysFragment() bool { diff --git a/internal/platform/platform.go b/internal/platform/platform.go index 7e9f9a6a7b..3fc8ab219e 100644 --- a/internal/platform/platform.go +++ b/internal/platform/platform.go @@ -32,6 +32,7 @@ import ( "github.com/coreos/ignition/v2/internal/providers/qemu" "github.com/coreos/ignition/v2/internal/providers/virtualbox" "github.com/coreos/ignition/v2/internal/providers/vmware" + "github.com/coreos/ignition/v2/internal/providers/zvm" "github.com/coreos/ignition/v2/internal/registry" "github.com/coreos/ignition/v2/internal/resource" ) @@ -132,6 +133,10 @@ func init() { name: "metal", fetch: noop.FetchConfig, }) + configs.Register(Config{ + name: "zvm", + fetch: zvm.FetchConfig, + }) } func Get(name string) (config Config, ok bool) { diff --git a/internal/providers/zvm/zvm.go b/internal/providers/zvm/zvm.go new file mode 100644 index 0000000000..923fe6a68f --- /dev/null +++ b/internal/providers/zvm/zvm.go @@ -0,0 +1,108 @@ +// Copyright 2019 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. + +// The zVM provider fetches a local configuration from the virtual unit +// record devices. + +package zvm + +import ( + "bytes" + "fmt" + "io/ioutil" + "os/exec" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/v3_1_experimental/types" + "github.com/coreos/ignition/v2/internal/distro" + "github.com/coreos/ignition/v2/internal/log" + "github.com/coreos/ignition/v2/internal/providers/util" + "github.com/coreos/ignition/v2/internal/resource" + "github.com/coreos/vcontext/report" +) + +const readerDevice string = "000c" + +func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { + // Fetch config files directly from reader device. + _, err := f.Logger.LogCmd(exec.Command(distro.ModprobeCmd(), "vmur"), "Loading zVM control program module") + if err != nil { + f.Logger.Err("Couldn't install vmur module: %v", err) + errors := fmt.Errorf("Couldn't install vmur module: %v", err) + return types.Config{}, report.Report{}, errors + } + // Online the reader device. + logger := f.Logger + err = onlineDevice(logger) + if err != nil { + return types.Config{}, report.Report{}, err + } + // Read files from the z/VM reader queue. + readerInfo, err := exec.Command(distro.VmurCmd(), "li").CombinedOutput() + if err != nil { + f.Logger.Err("Can not get reader device: %v", err) + errors := fmt.Errorf("Can not get reader device: %v", err) + return types.Config{}, report.Report{}, errors + } + for _, records := range strings.Split(string(readerInfo), "\n") { + record := strings.Fields(records) + if len(record) <= 1 { + break + } + if len(record) < 11 { + continue + } + spoolid := record[1] + ftype := record[10] + file := record[9] + "." + record[10] + // Receive the spool file. + if ftype == "ign" { + _, err := f.Logger.LogCmd(exec.Command(distro.VmurCmd(), "re", "-f", spoolid, file), "Receive the spool file") + if err != nil { + return types.Config{}, report.Report{}, err + } + f.Logger.Info("using config file at %q", file) + rawConfig, err := ioutil.ReadFile(file) + if err != nil { + f.Logger.Err("Couldn't read config from configFile %q: %v", file, err) + break + } + jsonConfig := bytes.Trim(rawConfig, string(byte(0))) + return util.ParseConfig(f.Logger, jsonConfig) + } + } + return types.Config{}, report.Report{}, errors.ErrEmpty +} + +func onlineDevice(logger *log.Logger) error { + _, err := logger.LogCmd(exec.Command(distro.ChccwdevCmd(), "-e", readerDevice), "Brings a Linux device online") + if err != nil { + // If online failed, expose the device firstly. + _, err = logger.LogCmd(exec.Command(distro.CioIgnoreCmd(), "-r", readerDevice), "Expose reader device") + if err != nil { + logger.Err("Couldn't expose reader device %q: %v", readerDevice, err) + errors := fmt.Errorf("Couldn't expose reader device %q: %v", readerDevice, err) + return errors + } + _, err = logger.LogCmd(exec.Command(distro.ChccwdevCmd(), "-e", readerDevice), "Brings a Linux device online") + if err != nil { + logger.Err("Couldn't online reader device") + errors := fmt.Errorf("Couldn't online reader device") + return errors + } + } + _, err = logger.LogCmd(exec.Command(distro.UdevadmCmd(), "settle"), "Settle udev device") + return err +}