debian-forge-composer/vendor/github.com/vmware/govmomi/cli/vm/customize.go
Achilleas Koutsou 3fd7092db5 go.mod: update osbuild/images to v0.156.0
tag v0.155.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.155.0

----------------
  * Fedora 43: add shadow-utils when LockRoot is enabled, update cloud-init service name (osbuild/images#1618)
    * Author: Achilleas Koutsou, Reviewers: Gianluca Zuccarelli, Michael Vogt
  * Update osbuild dependency commit ID to latest (osbuild/images#1609)
    * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger, Tomáš Hozza
  * Update snapshots to 20250626 (osbuild/images#1623)
    * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * distro/rhel9: xz compress azure-cvm image type [HMS-8587] (osbuild/images#1620)
    * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza
  * distro/rhel: introduce new image type: Azure SAP Apps [HMS-8738] (osbuild/images#1612)
    * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza
  * distro/rhel: move ansible-core to sap_extras_pkgset (osbuild/images#1624)
    * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Tomáš Hozza
  * github/create-tag: allow passing the version when run manually (osbuild/images#1621)
    * Author: Achilleas Koutsou, Reviewers: Lukáš Zapletal, Tomáš Hozza
  * rhel9: move image-config into pure YAML (HMS-8593) (osbuild/images#1616)
    * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger
  * test: split manifest checksums into separate files (osbuild/images#1625)
    * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza

— Somewhere on the Internet, 2025-06-30

---

tag v0.156.0
Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com>

Changes with 0.156.0

----------------
  * Many: delete repositories for EOL distributions (HMS-7044) (osbuild/images#1607)
    * Author: Tomáš Hozza, Reviewers: Michael Vogt, Simon de Vlieger
  * RHSM/facts: add 'image-builder CLI' API type (osbuild/images#1640)
    * Author: Tomáš Hozza, Reviewers: Brian C. Lane, Simon de Vlieger
  * Update dependencies 2025-06-29 (osbuild/images#1628)
    * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza
  * Update osbuild dependency commit ID to latest (osbuild/images#1627)
    * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza
  * [RFC] image: drop `InstallWeakDeps` from image.DiskImage (osbuild/images#1642)
    * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger, Tomáš Hozza
  * build(deps): bump the go-deps group across 1 directory with 3 updates (osbuild/images#1632)
    * Author: dependabot[bot], Reviewers: SchutzBot, Tomáš Hozza
  * distro/rhel10: xz compress azure-cvm image type (osbuild/images#1638)
    * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Simon de Vlieger
  * distro: cleanup/refactor distro/{defs,generic} (HMS-8744) (osbuild/images#1570)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * distro: remove some hardcoded values from generic/images.go (osbuild/images#1636)
    * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza
  * distro: small tweaks for the YAML based imagetypes (osbuild/images#1622)
    * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger
  * fedora/wsl: packages and locale (osbuild/images#1635)
    * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza
  * image/many: make compression more generic (osbuild/images#1634)
    * Author: Simon de Vlieger, Reviewers: Brian C. Lane, Michael Vogt
  * manifest: handle content template name with spaces (osbuild/images#1641)
    * Author: Bryttanie, Reviewers: Brian C. Lane, Michael Vogt, Tomáš Hozza
  * many: implement gzip (osbuild/images#1633)
    * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza
  * rhel/azure: set GRUB_TERMINAL based on architecture [RHEL-91383] (osbuild/images#1626)
    * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza

— Somewhere on the Internet, 2025-07-07

---
2025-07-14 13:13:20 +02:00

309 lines
8.9 KiB
Go

// © Broadcom. All Rights Reserved.
// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
// SPDX-License-Identifier: Apache-2.0
package vm
import (
"context"
"flag"
"fmt"
"strconv"
"strings"
"github.com/vmware/govmomi/cli"
"github.com/vmware/govmomi/cli/flags"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
type customize struct {
*flags.VirtualMachineFlag
alc int
prefix types.CustomizationPrefixName
tz string
domain string
host types.CustomizationFixedName
mac flags.StringList
ip flags.StringList
ip6 flags.StringList
gateway flags.StringList
netmask flags.StringList
dnsserver flags.StringList
dnssuffix flags.StringList
kind string
username string
org string
}
func init() {
cli.Register("vm.customize", &customize{})
}
func (cmd *customize) Register(ctx context.Context, f *flag.FlagSet) {
cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
cmd.VirtualMachineFlag.Register(ctx, f)
f.IntVar(&cmd.alc, "auto-login", 0, "Number of times the VM should automatically login as an administrator")
f.StringVar(&cmd.prefix.Base, "prefix", "", "Host name generator prefix")
f.StringVar(&cmd.tz, "tz", "", "Time zone")
f.StringVar(&cmd.domain, "domain", "", "Domain name")
f.StringVar(&cmd.host.Name, "name", "", "Host name")
f.Var(&cmd.mac, "mac", "MAC address")
cmd.mac = nil
f.Var(&cmd.ip, "ip", "IPv4 address")
cmd.ip = nil
f.Var(&cmd.ip6, "ip6", "IPv6 addresses with optional netmask (defaults to /64), separated by comma")
cmd.ip6 = nil
f.Var(&cmd.gateway, "gateway", "Gateway")
cmd.gateway = nil
f.Var(&cmd.netmask, "netmask", "Netmask")
cmd.netmask = nil
f.Var(&cmd.dnsserver, "dns-server", "DNS server list")
cmd.dnsserver = nil
f.Var(&cmd.dnssuffix, "dns-suffix", "DNS suffix list")
cmd.dnssuffix = nil
f.StringVar(&cmd.kind, "type", "Linux", "Customization type if spec NAME is not specified (Linux|Windows)")
f.StringVar(&cmd.username, "username", "", "Windows only : full name of the end user in firstname lastname format")
f.StringVar(&cmd.org, "org", "", "Windows only : name of the org that owns the VM")
}
func (cmd *customize) Usage() string {
return "[NAME]"
}
func (cmd *customize) Description() string {
return `Customize VM.
Optionally specify a customization spec NAME.
The '-ip', '-netmask' and '-gateway' flags are for static IP configuration.
If the VM has multiple NICs, an '-ip' and '-netmask' must be specified for each.
The '-dns-server' and '-dns-suffix' flags can be specified multiple times.
Windows -tz value requires the Index (hex): https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values
Examples:
govc vm.customize -vm VM NAME
govc vm.customize -vm VM -name my-hostname -ip dhcp
govc vm.customize -vm VM -gateway GATEWAY -ip NEWIP -netmask NETMASK -dns-server DNS1,DNS2 NAME
# Multiple -ip without -mac are applied by vCenter in the order in which the NICs appear on the bus
govc vm.customize -vm VM -ip 10.0.0.178 -netmask 255.255.255.0 -ip 10.0.0.162 -netmask 255.255.255.0
# Multiple -ip with -mac are applied by vCenter to the NIC with the given MAC address
govc vm.customize -vm VM -mac 00:50:56:be:dd:f8 -ip 10.0.0.178 -netmask 255.255.255.0 -mac 00:50:56:be:60:cf -ip 10.0.0.162 -netmask 255.255.255.0
# Dual stack IPv4/IPv6, single NIC
govc vm.customize -vm VM -ip 10.0.0.1 -netmask 255.255.255.0 -ip6 '2001:db8::1/64' -name my-hostname NAME
# DHCPv6, single NIC
govc vm.customize -vm VM -ip6 dhcp6 NAME
# Static IPv6, three NICs, last one with two addresses
govc vm.customize -vm VM -ip6 2001:db8::1/64 -ip6 2001:db8::2/64 -ip6 2001:db8::3/64,2001:db8::4/64 NAME
govc vm.customize -vm VM -auto-login 3 NAME
govc vm.customize -vm VM -prefix demo NAME
govc vm.customize -vm VM -tz America/New_York NAME`
}
// Parse a string of multiple IPv6 addresses with optional netmask; separated by comma
func parseIPv6Argument(argv string) (ipconf []types.BaseCustomizationIpV6Generator, err error) {
for _, substring := range strings.Split(argv, ",") {
// remove leading and trailing white space
substring = strings.TrimSpace(substring)
// handle "dhcp6" and lists of static IPv6 addresses
switch substring {
case "dhcp6":
ipconf = append(
ipconf,
&types.CustomizationDhcpIpV6Generator{},
)
default:
// check if subnet mask was specified
switch strings.Count(substring, "/") {
// no mask, set default
case 0:
ipconf = append(ipconf, &types.CustomizationFixedIpV6{
IpAddress: substring,
SubnetMask: 64,
})
// a single forward slash was found: parse and use subnet mask
case 1:
parts := strings.Split(substring, "/")
mask, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("unable to convert subnet mask to int: %w", err)
}
ipconf = append(ipconf, &types.CustomizationFixedIpV6{
IpAddress: parts[0],
SubnetMask: int32(mask),
})
// too many forward slashes; return error
default:
return nil, fmt.Errorf("unable to parse IPv6 address (too many subnet separators): %s", substring)
}
}
}
return ipconf, nil
}
func (cmd *customize) Run(ctx context.Context, f *flag.FlagSet) error {
vm, err := cmd.VirtualMachineFlag.VirtualMachine()
if err != nil {
return err
}
if vm == nil {
return flag.ErrHelp
}
var spec *types.CustomizationSpec
name := f.Arg(0)
if name == "" {
spec = &types.CustomizationSpec{
NicSettingMap: make([]types.CustomizationAdapterMapping, len(cmd.ip)),
}
switch cmd.kind {
case "Linux":
spec.Identity = &types.CustomizationLinuxPrep{
HostName: new(types.CustomizationVirtualMachineName),
}
case "Windows":
spec.Identity = &types.CustomizationSysprep{
UserData: types.CustomizationUserData{
ComputerName: new(types.CustomizationVirtualMachineName),
},
}
default:
return flag.ErrHelp
}
} else {
m := object.NewCustomizationSpecManager(vm.Client())
exists, err := m.DoesCustomizationSpecExist(ctx, name)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("specification %q does not exist", name)
}
item, err := m.GetCustomizationSpec(ctx, name)
if err != nil {
return err
}
spec = &item.Spec
}
if len(cmd.ip) > len(spec.NicSettingMap) {
return fmt.Errorf("%d -ip specified, spec %q has %d", len(cmd.ip), name, len(spec.NicSettingMap))
}
sysprep, isWindows := spec.Identity.(*types.CustomizationSysprep)
linprep, _ := spec.Identity.(*types.CustomizationLinuxPrep)
if isWindows {
sysprep.Identification.JoinDomain = cmd.domain
sysprep.UserData.FullName = cmd.username
sysprep.UserData.OrgName = cmd.org
} else {
linprep.Domain = cmd.domain
}
if len(cmd.dnsserver) != 0 {
if !isWindows {
for _, s := range cmd.dnsserver {
spec.GlobalIPSettings.DnsServerList =
append(spec.GlobalIPSettings.DnsServerList, strings.Split(s, ",")...)
}
}
}
spec.GlobalIPSettings.DnsSuffixList = cmd.dnssuffix
if cmd.prefix.Base != "" {
if isWindows {
sysprep.UserData.ComputerName = &cmd.prefix
} else {
linprep.HostName = &cmd.prefix
}
}
if cmd.host.Name != "" {
if isWindows {
sysprep.UserData.ComputerName = &cmd.host
} else {
linprep.HostName = &cmd.host
}
}
if cmd.alc != 0 {
if !isWindows {
return fmt.Errorf("option '-auto-login' is Windows only")
}
sysprep.GuiUnattended.AutoLogon = true
sysprep.GuiUnattended.AutoLogonCount = int32(cmd.alc)
}
if cmd.tz != "" {
if isWindows {
tz, err := strconv.ParseInt(cmd.tz, 16, 32)
if err != nil {
return fmt.Errorf("converting -tz=%q: %s", cmd.tz, err)
}
sysprep.GuiUnattended.TimeZone = int32(tz)
} else {
linprep.TimeZone = cmd.tz
}
}
for i, ip := range cmd.ip {
nic := &spec.NicSettingMap[i]
switch ip {
case "dhcp":
nic.Adapter.Ip = new(types.CustomizationDhcpIpGenerator)
default:
nic.Adapter.Ip = &types.CustomizationFixedIp{IpAddress: ip}
}
if i < len(cmd.netmask) {
nic.Adapter.SubnetMask = cmd.netmask[i]
}
if i < len(cmd.mac) {
nic.MacAddress = cmd.mac[i]
}
if i < len(cmd.gateway) {
nic.Adapter.Gateway = strings.Split(cmd.gateway[i], ",")
}
if isWindows {
if i < len(cmd.dnsserver) {
nic.Adapter.DnsServerList = strings.Split(cmd.dnsserver[i], ",")
}
}
}
for i, ip6 := range cmd.ip6 {
ipconfig, err := parseIPv6Argument(ip6)
if err != nil {
return err
}
// use the same logic as the ip switch: the first occurrence of the ip6 switch is assigned to the first nic,
// the second to the second nic and so forth.
if spec.NicSettingMap == nil || len(spec.NicSettingMap) < i {
return fmt.Errorf("unable to find a network adapter for IPv6 settings %d (%s)", i, ip6)
}
nic := &spec.NicSettingMap[i]
if nic.Adapter.IpV6Spec == nil {
nic.Adapter.IpV6Spec = new(types.CustomizationIPSettingsIpV6AddressSpec)
}
nic.Adapter.IpV6Spec.Ip = append(nic.Adapter.IpV6Spec.Ip, ipconfig...)
}
task, err := vm.Customize(ctx, *spec)
if err != nil {
return err
}
return task.Wait(ctx)
}