惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
N
News and Events Feed by Topic
P
Privacy International News Feed
The Hacker News
The Hacker News
Schneier on Security
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
Security Latest
Security Latest
L
LINUX DO - 最新话题
阮一峰的网络日志
阮一峰的网络日志
Cisco Talos Blog
Cisco Talos Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
博客园 - 【当耐特】
博客园 - Franky
P
Privacy & Cybersecurity Law Blog
Attack and Defense Labs
Attack and Defense Labs
云风的 BLOG
云风的 BLOG
月光博客
月光博客
D
Docker
Webroot Blog
Webroot Blog
The GitHub Blog
The GitHub Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
W
WeLiveSecurity
S
Security Affairs
Martin Fowler
Martin Fowler
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
Microsoft Azure Blog
Microsoft Azure Blog
C
CERT Recently Published Vulnerability Notes
B
Blog
L
Lohrmann on Cybersecurity
T
Threatpost
量子位
S
Schneier on Security
V
Visual Studio Blog
S
Securelist
T
The Exploit Database - CXSecurity.com
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
aimingoo的专栏
aimingoo的专栏
The Register - Security
The Register - Security
I
Intezer
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 聂微东
小众软件
小众软件
罗磊的独立博客
雷峰网
雷峰网
Recorded Future
Recorded Future

Reimu's blog

端午节游玩:徒步、骑行、跑山 考驾照和买二手车 使用 BuildKit on Kubernetes 构建多架构容器镜像 写在上海封城一年之后 使用 Packer 构建虚拟机镜像踩的坑 使用 Redfish 自动化安装 ESXi OS 流水线中使用 docker in pod 方式构建容器镜像 使用 kubectl 自动归档 argo workflow 日志 VMware Tanzu kubernetes 发行版部署尝鲜
搬砖工具 govc 使用记录
Reimu · 2022-03-31 · via Reimu's blog

最近由于工作原因需要在 ESXi 主机上完成一些参数配置、虚拟交换机/网创建、虚拟机创建、VIB 安装、PCI 直通、虚拟机创建等工作,于是抽空整理了一下在使用 govc 时踩的一些坑。

govc 是 VMware 官方 govmomi 库的一个封装实现。使用它可以完成对 ESXi 主机或 vCenter 的一些操作。比如创建虚拟机、管理快照等。基本上能在 ESXi 或 vCenter 上的操作,在 govmomi 中都有对应的实现。目前 govc 支持的 ESXi / vCenter 版本有 7.0, 6.7, 6.5 , 6.0 (5.x 版本太老了,干脆放弃吧),另外也支持 VMware Workstation 的某些版本。

使用 govc 连接 ESXi 主机或 vCenter 可以通过设置环境变量或者命令行参数,建议使用环境变量,如果通过命令行 flag 的话,将明文规定用户名和密码输出来有一定的安全风险。

Options:
  -cert=                           Certificate [GOVC_CERTIFICATE]
  -dc=                             Datacenter [GOVC_DATACENTER]
  -debug=false                     Store debug logs [GOVC_DEBUG]
  -dump=false                      Enable Go output
  -host=                           Host system [GOVC_HOST]
  -host.dns=                       Find host by FQDN
  -host.ip=                        Find host by IP address
  -host.ipath=                     Find host by inventory path
  -host.uuid=                      Find host by UUID
  -json=false                      Enable JSON output
  -k=false                         Skip verification of server certificate [GOVC_INSECURE]
  -key=                            Private key [GOVC_PRIVATE_KEY]
  -persist-session=true            Persist session to disk [GOVC_PERSIST_SESSION]
  -tls-ca-certs=                   TLS CA certificates file [GOVC_TLS_CA_CERTS]
  -tls-known-hosts=                TLS known hosts file [GOVC_TLS_KNOWN_HOSTS]
  -trace=false                     Write SOAP/REST traffic to stderr
  -u=https://[email protected]/sdk  ESX or vCenter URL [GOVC_URL]
  -verbose=false                   Write request/response data to stderr
  -vim-namespace=vim25             Vim namespace [GOVC_VIM_NAMESPACE]
  -vim-version=7.0                 Vim version [GOVC_VIM_VERSION]
  -xml=false                       Enable XML output

通过 GOVC_URL 环境变量指定 ESXi 主机或 vCenter 的 URL,登录的用户名和密码可设置在 GOVC_URL 中或者单独设置 GOVC_USERNAMEGOVC_PASSWORD。如果 https 证书是自签的域名或者 IP 需要通过设置 GOVC_INSECURE=true 参数来允许不安全的 https 连接。

$ export GOVC_URL="https://root:[email protected]"
$ export GOVC_INSECURE=true
$ govc about
FullName:     VMware ESXi 7.0.2 build-17867351
Name:         VMware ESXi
Vendor:       VMware, Inc.
Version:      7.0.2
Build:        17867351
OS type:      vmnix-x86
API type:     HostAgent
API version:  7.0.2.0
Product ID:   embeddedEsx
UUID:

如果用户名和密码当中有特殊字符比如 \ @ /,建议分别设置 GOVC_URLGOVC_USERNAMEGOVC_PASSWORD 这样能避免特殊字符在 GOVC_URL 出现一些奇奇怪怪的问题。

获取主机信息

govc host.info

通过 host.info 自命命令可以得到 ESXi 主机的基本信息,

  • Path: 当前主机在集群中的资源路径
  • Manufacturer: 硬件设备制造商
  • Logical CPUs: 逻辑 CPU 的数量,以及 CPU 的基础频率
  • Processor type: CPU 的具体型号,由于我的是 ES 版的 E-2126G,所以无法显示出具体的型号 🤣
  • CPU usage: CPU 使用的负载情况
  • Memory: 主机安装的内存大小
  • Memory usage: 内存使用的负载情况
  • Boot time: 开机时间
  • State: 连接状态
$ govc host.info
Name:              hp-esxi.lan
  Path:            /ha-datacenter/host/hp-esxi.lan/hp-esxi.lan
  Manufacturer:    HPE
  Logical CPUs:    6 CPUs @ 3000MHz
  Processor type:  Genuine Intel(R) CPU 0000 @ 3.00GHz
  CPU usage:       3444 MHz (19.1%)
  Memory:          32613MB
  Memory usage:    26745 MB (82.0%)
  Boot time:       2021-12-05 06:11:53.42802 +0000 UTC
  State:           connected

如果加上 -json 参数会得到一个至少 3w 行的 json 输出,里面包含的 ESXi 主机的所有信息,然后可以使用 jq 命令去过滤出一些自己所需要的参数。

╭─root@esxi-debian-devbox ~
╰─# govc host.info -json=true > host_info.json
╭─root@esxi-debian-devbox ~
╰─# wc host_info.json
  34522   73430 1188718 host_info.json
╭─root@esxi-debian-devbox ~
╰─# govc host.info -json | jq '.HostSystems[0].Summary.Hardware'
{
  "Vendor": "HPE",
  "Model": "ProLiant MicroServer Gen10 Plus",
  "Uuid": "30363150",
  "MemorySize": 34197471232,
  "CpuModel": "Genuine Intel(R) CPU 0000 @ 3.00GHz",
  "CpuMhz": 3000,
  "NumCpuPkgs": 1,
  "NumCpuCores": 6,
  "NumCpuThreads": 6,
  "NumNics": 4,
  "NumHBAs": 3
}

如果加上 -dump 参数,则会以 Golang 结构体的格式来输出,输出的内容也是包含了 ESXi 主机的所有信息,用它可以比较方便地定位某个信息的结构体,这一点对基于 govmomi 来开发其他的功能来说十分方便。尤其是在写单元测试的时候,可以从这里 dump 出一些数据来进行 mock。需要注意的是,并不是所有的子命令都支持 json 格式的输出。

mo.HostSystem{
    ManagedEntity: mo.ManagedEntity{
        ExtensibleManagedObject: mo.ExtensibleManagedObject{
            Self:           types.ManagedObjectReference{Type:"HostSystem", Value:"ha-host"},
            Value:          nil,
            AvailableField: nil,
        },
        Parent:        &types.ManagedObjectReference{Type:"ComputeResource", Value:"ha-compute-res"},
        CustomValue:   nil,
        OverallStatus: "green",
        ConfigStatus:  "yellow",
        ConfigIssue:   []types.BaseEvent{
            &types.RemoteTSMEnabledEvent{
                HostEvent: types.HostEvent{
                    Event: types.Event{
                        Key:             1,
                        ChainId:         0,
                        CreatedTime:     time.Now(),
                        UserName:        "",
                        Datacenter:      (*types.DatacenterEventArgument)(nil),
                        ComputeResource: (*types.ComputeResourceEventArgument)(nil),
                        Host:            &types.HostEventArgument{
                            EntityEventArgument: types.EntityEventArgument{
                                EventArgument: types.EventArgument{},
                                Name:          "hp-esxi.lan",
                            },
                            Host: types.ManagedObjectReference{Type:"HostSystem", Value:"ha-host"},
                        },
                        Vm:                   (*types.VmEventArgument)(nil),
                        Ds:                   (*types.DatastoreEventArgument)(nil),
                        Net:                  (*types.NetworkEventArgument)(nil),
                        Dvs:                  (*types.DvsEventArgument)(nil),
                        FullFormattedMessage: "SSH for the host hp-esxi.lan has been enabled",
                        ChangeTag:            "",
                    },
                },
            },
        },

在写单元测试的时候,我经常用它来 mock 一些特殊硬件设备的信息,这比自己手写这些结构体要方便很多。比如以 mpx.vmhba<Adapter>:C<Channel>:T<Target>:L<LUN> 命名的硬盘可以通过 PlugStoreTopology 这个结构体来获取该硬盘的 NAA 号:

func getDiskIDByHostPlugStoreTopology(hpst *types.HostPlugStoreTopology, diskName string) string {
	for _, path := range hpst.Path {
		if path.Name == diskName {
			s := strings.Split(path.Target, "-sas.")
			return s[len(s)-1]
		}
	}
	return ""
}

// 单元测试代码如下:
var plugStoreTopology = &types.HostPlugStoreTopology{
	Path: []types.HostPlugStoreTopologyPath{
		{
			Key:           "key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T1:L0",
			Name:          "vmhba0:C0:T1:L0",
			ChannelNumber: 0,
			TargetNumber:  1,
			LunNumber:     0,
			Adapter:       "key-vim.host.PlugStoreTopology.Adapter-vmhba0",
			Target:        "key-vim.host.PlugStoreTopology.Target-sas.500056b3d93828c0",
			Device:        "key-vim.host.PlugStoreTopology.Device-020000000055cd2e414dc39d4e494e54454c20",
		},
	},
}


func TestGetDiskIDByHostPlugStoreTopology(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		{
			name: "vmhba0:C0:T1:L0",
			want: "500056b3d93828c0",
		},
		{
			name: "vmhba0:C0:T2:L0",
			want: "",
		},
		{
			name: "",
			want: "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := getDiskIDByHostPlugStoreTopology(plugStoreTopology, tt.name); got != tt.want {
				t.Errorf("getDiskIDByHostPlugStoreTopology() = %v, want %v", got, tt.want)
			}
		})
	}
}

再比如 NVMe 硬盘可以通过 NvmeTopology 这个数据对象获取它的序列号

func getNVMeIDByHostNvmeTopology(hnt *types.HostNvmeTopology, diskName string) string {
	for _, adapter := range hnt.Adapter {
		for _, controller := range adapter.ConnectedController {
			for _, ns := range controller.AttachedNamespace {
				if ns.Name == diskName {
					return strings.TrimSpace(controller.SerialNumber)
				}
			}
		}
	}
	return ""
}

// 单元测试代码如下:
var nvmeTopology = &types.HostNvmeTopology{
	Adapter: []types.HostNvmeTopologyInterface{
		{
			Key:     "key-vim.host.NvmeTopology.Interface-vmhba0",
			Adapter: "key-vim.host.PcieHba-vmhba0",
			ConnectedController: []types.HostNvmeController{
				{
					Key:                     "key-vim.host.NvmeController-256",
					ControllerNumber:        256,
					Subnqn:                  "nqn.2021-06.com.intel:PHAB123502CU1P9SGN  ",
					Name:                    "nqn.2021-06.com.intel:PHAB123502CU1P9SGN",
					AssociatedAdapter:       "key-vim.host.PcieHba-vmhba0",
					TransportType:           "pcie",
					FusedOperationSupported: false,
					NumberOfQueues:          2,
					QueueSize:               1024,
					AttachedNamespace: []types.HostNvmeNamespace{
						{
							Key:              "key-vim.host.NvmeNamespace-t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB________00035CB406E4D25C@256",
							Name:             "t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB________00035CB406E4D25C",
							Id:               1,
							BlockSize:        512,
							CapacityInBlocks: 3125627568,
						},
					},
					VendorId:        "0x8086",
					Model:           "Dell Ent NVMe P5600 MU U.2 1.6TB        ",
					SerialNumber:    "PHAB123502CU1P9SGN  ",
					FirmwareVersion: "1.0.0   PCIe",
				},
			},
		},
		{
			Key:     "key-vim.host.NvmeTopology.Interface-vmhba1",
			Adapter: "key-vim.host.PcieHba-vmhba1",
			ConnectedController: []types.HostNvmeController{
				{
					Key:                     "key-vim.host.NvmeController-257",
					ControllerNumber:        257,
					Subnqn:                  "nqn.2021-06.com.intel:PHAB123602H81P9SGN  ",
					Name:                    "nqn.2021-06.com.intel:PHAB123602H81P9SGN",
					AssociatedAdapter:       "key-vim.host.PcieHba-vmhba1",
					TransportType:           "pcie",
					FusedOperationSupported: false,
					NumberOfQueues:          2,
					QueueSize:               1024,
					AttachedNamespace: []types.HostNvmeNamespace{
						{
							Key:              "key-vim.host.NvmeNamespace-t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB________00035CEE23E4D25C@257",
							Name:             "t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB________00035CEE23E4D25C",
							Id:               1,
							BlockSize:        512,
							CapacityInBlocks: 3125627568,
						},
					},
					VendorId:        "0x8086",
					Model:           "Dell Ent NVMe P5600 MU U.2 1.6TB        ",
					SerialNumber:    "PHAB123602H81P9SGN  ",
					FirmwareVersion: "1.0.0   PCIe",
				},
			},
		},
	},
}

func TestGetNVMeIDByHostNvmeTopology(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		{
			name: "t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB________00035CEE23E4D25C",
			want: "PHAB123602H81P9SGN",
		},
		{
			name: "t10.NVMe____Dell_Ent_NVMe_P5600_MU_U.2_1.6TB",
			want: "",
		},
		{
			name: "",
			want: "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := getNVMeIDByHostNvmeTopology(nvmeTopology, tt.name); got != tt.want {
				t.Errorf("getNVMeIDByHostNvmeTopology() = %v, want %v", got, tt.want)
			}
		})
	}
}

配置 ESXi 主机参数

通过 host.option.ls 可以列出当前 ESXi 主机所有的配置选项

$ govc host.option.ls
Annotations.WelcomeMessage:
BufferCache.FlushInterval:                          30000
BufferCache.HardMaxDirty:                           95
BufferCache.PerFileHardMaxDirty:                    50
BufferCache.SoftMaxDirty:                           15
CBRC.DCacheMemReserved:                             400
CBRC.Enable:                                        false
COW.COWMaxHeapSizeMB:                               192
COW.COWMaxREPageCacheszMB:                          256
COW.COWMinREPageCacheszMB:                          0
COW.COWREPageCacheEviction:                         1
Config.Defaults.host.TAAworkaround:                 true
Config.Defaults.monitor.if_pschange_mc_workaround:  false
Config.Defaults.security.host.ruissl:               true
Config.Defaults.vGPU.consolidation:                 false
Config.Etc.issue:
Config.Etc.motd:                                    The time and date of this login have been sent to the system logs.

通过 host.option.set 可以设置 ESXi 主机参数,例如如果想要配置 NFS 存储心跳超时时间可以通过如下方式

$ govc host.option.set NFS.HeartbeatTimeout 30

开启 ssh 服务

通过 host.service 可以对 ESXi 主机上的服务进行相关操作。

$ govc host.service
Where ACTION is one of: start, stop, restart, status, enable, disable

# 启动 ssh 服务
$ govc host.service start TSM-SSH
# 将 ssh 服务设置为开机自启
$ govc host.service enable TSM-SSH
# 查看 ssh 服务的状态
$ govc host.service status TSM-SSH

创建虚拟机

$ VM_NAME="centos-test"
$ govc vm.create -ds='datastore*' -net='VM Network' -net.adapter=vmxnet3 -disk 1G -on=false ${VM_NAME}
$ govc vm.change -cpu.reservation=%d -memory-pin=true -vm ${VM_NAME}
$ govc vm.change -g centos7_64Guest -c %d -m 16384 -latency high -vm ${VM_NAME}
$ govc device.cdrom.add -vm ${VM_NAME}
$ govc device.cdrom.insert -vm ${VM_NAME} -device cdrom-3000
$ govc device.connect -vm ${VM_NAME} cdrom-3000
$ govc vm.power -on=true ${VM_NAME}

参考