This is a basic implementation of a detection method for whether mgmt is running in a virtualized environment. We achieve this by doing two types of checks: on one hand, we check if the CPU flags can confirm the presence of a virtualized env; on the other, we check if the presence of known files related with DMI (and their contents) can confirm whether we're inside a virt env. Either of these situations will cause the function to return true, with the default case being false. All of these checks are relatively naive and can be improved by looking at the main inspiration for this implementation, which was systemd's own check for the presence of virtualization.
161 lines
5.0 KiB
Go
161 lines
5.0 KiB
Go
// Mgmt
|
|
// Copyright (C) James Shubin and the project contributors
|
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// Additional permission under GNU GPL version 3 section 7
|
|
//
|
|
// If you modify this program, or any covered work, by linking or combining it
|
|
// with embedded mcl code and modules (and that the embedded mcl code and
|
|
// modules which link with this program, contain a copy of their source code in
|
|
// the authoritative form) containing parts covered by the terms of any other
|
|
// license, the licensors of this program grant you additional permission to
|
|
// convey the resulting work. Furthermore, the licensors of this program grant
|
|
// the original author, James Shubin, additional permission to update this
|
|
// additional permission if he deems it necessary to achieve the goals of this
|
|
// additional permission.
|
|
|
|
package coreos
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/purpleidea/mgmt/lang/funcs/simple"
|
|
"github.com/purpleidea/mgmt/lang/types"
|
|
"github.com/purpleidea/mgmt/util/errwrap"
|
|
)
|
|
|
|
// The Desktop Management Interface (DMI) is a framwork for exposing system data
|
|
// to software. We will make use of it to determine whether we're using a
|
|
// virtualization platform. dmiFilesSlice is a slice of file paths which might
|
|
// contain information related to the presence of a virtualization platform.
|
|
// This is where we might find the values present in virtualizationVendorSlice.
|
|
var dmiFilesSlice = []string{
|
|
"/sys/class/dmi/id/product_name",
|
|
"/sys/class/dmi/id/sys_vendor",
|
|
"/sys/class/dmi/id/board_vendor",
|
|
"/sys/class/dmi/id/bios_vendor",
|
|
"/sys/class/dmi/id/product_version",
|
|
}
|
|
|
|
// virtualizationVendorSlice is a slice of strings that might be found in DMI
|
|
// related files (dmiFilesSlice) during the checks performed for the presence of
|
|
// virtualization.
|
|
var virtualizationVendorSlice = []string{
|
|
"Amazon EC2",
|
|
"Apple Virtualization",
|
|
"BHYVE",
|
|
"Bochs",
|
|
"Google Computer Engine",
|
|
"Hyper-V",
|
|
"innotek GmbH",
|
|
"KubeVirt",
|
|
"KVM",
|
|
"OpenStack",
|
|
"Oracle Corporation",
|
|
"Parallels",
|
|
"QEMU",
|
|
"VMware",
|
|
"Xen",
|
|
}
|
|
|
|
func init() {
|
|
simple.ModuleRegister(ModuleName, "is_virtual", &simple.Scaffold{
|
|
T: types.NewType("func() bool"),
|
|
F: IsVirtual,
|
|
})
|
|
}
|
|
|
|
// IsVirtual is a function that executes two types of checks: first, it checks
|
|
// whether we're running on Linux. If that's the case, we run checks related
|
|
// with the presence of virtualization platforms. If any of those checks returns
|
|
// true, then so does this function. Otherwise, it's assumed that it's not a
|
|
// virtualized environment.
|
|
func IsVirtual(ctx context.Context, input []types.Value) (types.Value, error) {
|
|
b, err := isVirtual(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &types.BoolValue{V: b}, nil
|
|
}
|
|
|
|
// isVirtual is the implementation for the IsVirtual function.
|
|
func isVirtual(ctx context.Context) (bool, error) {
|
|
// TODO: If we implement detection for OS other than Linux, this logic
|
|
// will have to change.
|
|
if runtime.GOOS != "linux" {
|
|
return false, errors.New("operating system is not Linux")
|
|
}
|
|
|
|
cpuInfoCheck, err1 := cpuInfoCheck(ctx)
|
|
if err1 == nil && cpuInfoCheck {
|
|
return true, nil
|
|
}
|
|
|
|
dmiFileCheck, err2 := dmiFileCheck(ctx)
|
|
if err2 == nil && dmiFileCheck {
|
|
return true, nil
|
|
}
|
|
|
|
if err1 != nil || err2 != nil {
|
|
return false, errwrap.Append(err1, err2)
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
// In an x86 system, there's a check to detect virt envs. The Linux kernel adds
|
|
// the "hypervisor" flag to the CPU flags.
|
|
func cpuInfoCheck(ctx context.Context) (bool, error) {
|
|
cpuInfo, err := os.ReadFile("/proc/cpuinfo")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if strings.Contains(string(cpuInfo), "hypervisor") {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// Check if any of the slices of virtualizationVendorSlice are present in any of
|
|
// the DMI files contained on dmiFilesSlice. If that's the case, then we return
|
|
// this function as true. This approach was inspired on systemd's work for a
|
|
// similar purpose.
|
|
// https://github.com/systemd/systemd/blob/main/src/basic/virt.c#L158
|
|
func dmiFileCheck(ctx context.Context) (bool, error) {
|
|
for _, dmiFile := range dmiFilesSlice {
|
|
dmiFileContent, err := os.ReadFile(dmiFile)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return false, err
|
|
} else if err != nil {
|
|
continue
|
|
}
|
|
|
|
s := string(dmiFileContent)
|
|
for _, vendor := range virtualizationVendorSlice {
|
|
if strings.Contains(s, vendor) {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|