想自动分 VMware 虚拟机,所以抽空研究了下它的 Python api。VMware api 本质上是 restapi,只不过有多种语言的封装,如果有兴趣可以关注下 VMware 其他语言的实现。
Python api 有 pyvmomi 以及下面要讲到的 vsphere-automation-sdk 这两种,其中 pyvmomi 使用基于 SOAP 的 API 的 vSphere Web 服务 API,是比较老的东西了;而 vsphere-automation-sdk 使用新版基于 REST 的 api,有类似于 Tagging 和 Content Library 这样的新功能。Appliance Management、NSX、VMware Cloud 等 api 只能使用 vsphere-automation-sdk。
简单来说,以后新的功能的实现就在 vsphere automation sdk 上。虽然如此,但是 vsphere automation sdk 未必就一定适合你。而且 vsphere automation sdk 不管你用还是不用,pyvmomi 是一定要使用的。
为什么这么说呢?vsphere-automation-sdk-python 由于使用了新的 api,可以使用类似于内容库(关于内容库的内容,接下来会提到)以及 tagging 这样的新功能,但是其他功能就乏善可陈了,比如它对虚拟机本身的管理就没有丝毫办法。
就说一个很现实的问题,使用 automation sdk 虽然可以从内容库中的 ovf 模板创建虚拟机,但是它只能根据模板的配置创建虚拟机,无法修改 cpu、内存、磁盘、ip 等参数(或许是我不知道?),而这些功能恰好 pyvmomi 都能胜任。
因此,在存在多个 vcenter 的环境,可以使用内容库保持多个 vcenter 之间 ovf 模板的一致,这样不管在哪个 vcenter 中创建虚拟机操作都一样。然后创建完虚拟机之后,再使用 pyvmomi 对虚拟机配置进行修改。当然,如果你的环境中只有一个 vcenter,那么完全就用不到 vsphere-automation-sdk,因为使用 pyvmomi 直接克隆机器速度更快,因为它少了从内容库下载模板的时间。
这个文章要讲的是,使用 vsphere automation sdk 从内容库中的 ovf 模板创建虚拟机,然后通过 pyvmomi 对虚拟机配置进行修改。需要注意的是,内容库是 VSphere 6.0 才有的功能,这篇文章基于 6.5。
我们可以先进入其 github 主页。
这个 SDK api 没什么文档,你得看它提供的示例才能对它有所了解。它所有的示例都在 samples 目录,有意思的是,它的示例不是割裂的,而是一个整体,整个示例就是一个 Python 包,你可以通过传递参数给它来实现创建简单的虚拟机等操作。
说实话,示例写的比较烂,坑很多。有些简单的、几行代码能搞定的事情,它能用更复杂的方式帮你实现。有感于此,我就将我研究的东西写出来,让大家少走弯路吧。
本文基于 VSphere 6.5 + Python 3.6。Python 2.7 也能用,差别不大。
首先安装它的 SDK:
git clone https://github.com/vmware/vsphere-automation-sdk-python.git
cd vsphere-automation-sdk-python
pip3 install --upgrade --force-reinstall -r requirements.txt --extra-index-url file:///`pwd`/lib
复制代码
--extra-index-url
:默认 pip 会去 pypi.org/simple 下载 Python 包,你还可以指定其他的 URL 进行下载。
网络的问题会导致中断,不要以为是依赖出现问题,要仔细看报错信息。如果是网络问题,多安装几次,一直安装总会成功的。而且它会自动安装 pyvmomi。
列出所有虚拟机
安装完成之后就可以使用了,我们从最简单的开始,登录,然后列出 vCenter 所有虚拟机:
import requests
import urllib3
from vmware.vapi.vsphere.client import create_vsphere_client
session = requests.session()
session.verify = False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
client = create_vsphere_client(server='IP', username='USER', password='PASSWORD', session=session)
client.vcenter.VM.list()
复制代码
list 列出来的虚拟机最多只能返回 1000 台,超过这个数量它会抛出异常,这时你需要使用过滤器来减少它返回的数量。过滤器可以使用 client.vcenter.VM.FilterSpec
这个类来指定,它可以通过下面这些属性来进行过滤。
vms=None, names=None, folders=None, datacenters=None, hosts=None, clusters=None, resource_pools=None, power_states=None
复制代码
比如通过虚拟机的名称来进行过滤:
client.vcenter.VM.list(client.vcenter.VM.FilterSpec(names={
'10.20.6.77_xyz.tomcat.xyz-common-main.01.sit'}))
复制代码
根据集群名称过滤虚拟机
直接输入集群名称给 vm.FilterSpec 过滤器并不能获得任何结果,而是应该先通过集群名称获得类似集群 id 这样的字串(我是这样理解的)输入进去才行。其实不光是集群,在这个 SDK 中,VMware 中的所有概念,包括网络、存储、文件夹、资源池、虚拟机等它都只认 id,而不是它们的名称(我的理解是名称随时都可以变,但是 id 不会变)。
虽然你不能直接将集群名称给它,但是你可以根据集群名称来获取它的 id,具体做法如下:
from com.vmware.vcenter_client import Cluster
# 获取名为 POC 集群的 id
cluster_id = client.vcenter.Cluster.list(Cluster.FilterSpec(names={
'POC'}))[0].cluster
client.vcenter.VM.list(client.vcenter.VM.FilterSpec(clusters={cluster_id}))
复制代码
根据文件夹过滤虚拟机
其实和使用集群名称一样,只不过文件夹有多种类型:
DATACENTER
= Type(string=u'DATACENTER')DATASTORE
= Type(string=u'DATASTORE')HOST
= Type(string=u'HOST')NETWORK
= Type(string=u'NETWORK')VIRTUAL_MACHINE
= Type(string=u'VIRTUAL_MACHINE')
我们在使用的时候最好精确的指定其类型。
from com.vmware.vcenter_client import Folder
# 指定文件夹类型为虚拟机文件夹,文件夹名称为 35
filter_spec = Folder.FilterSpec(type=Folder.Type.VIRTUAL_MACHINE, names={
'35'})
folder_id = client.vcenter.Folder.list(filter_spec)[0].folder
client.vcenter.VM.list(client.vcenter.VM.FilterSpec(folders={folder_id}))
复制代码
还有其他的过滤方式,使用起来都一样,这里就不多提了。
从模板创建虚拟机
一些基础的用法先介绍到这,直接进入“创建虚拟机”这一正题,更多的用法也会在下面伴随着创建虚拟机一一展示。遗憾的是,我并没有在 vsphere-automation-sdk-python 中找到直接从模板中创建虚拟机的方式,所以只能退而求其次使用内容库的 ovf 模板进行部署。
在部署之前,说说这个内容库。
Content Libraries
Content Libraries(内容库)在 VSphere 6.0 中是一个容器对象,用来存储虚拟机模板、vAPP 模板、ISO 文件,还有其他你能在你网络中共享的文件。
内容库中的所有文件都可以跨 vCenter,有了内容库,你就不需要在每个 vCenter 中维护一套模板了。在 Container Libraries 中存在的虚拟机模板、vAPP 模板,还有其他文件都被定义为 library items,library items 可以包含单个或多个文件,例如 VOF、ISO 等等。
如果在多个 vCenter 之间可以进行 http/https 访问,那么这些 library items 就可以在这些 vCenter 之间共享。
在 vSphere 中可以创建两种类型的 Content Library:
- Local:items 会被存放在某个的 vCenter 上,但是可以发布出来,让其他 vCenter 中的用户来订阅;
- Subscribed:订阅发布出来的内容库会创建一个订阅库,订阅库可以创建在和发布库相同或不同的 vCenter 上。订阅库通过手动或者自动地同步发布库让其内容保持最新。管理员可以改变文件内容,但订阅者只能使用文件。
内容库中除了可以直接上传 ovf 文件到其中作为模板,也可以直接将 VMware 中的模板直接 copy 进去。从内容库中的模板创建机器会比直接从 VMware 上模板创建机器慢,因为它多了一个下载的过程,如果网速够快,差距会缩短。
内容库的创建方式在最下面的参考中网址中。
获取内容库中的模板
因为有了内容库的存在,我们就可以从内容库的模板中创建虚拟机了。我们要获得内容库中的内容,要先创建所谓的 stub_config,这个里面包含的是你登录 vCenter 的信息,然后将它传递给内容库相关的类,就能够获取内容库的内容了。
官方的示例文档是先创建 stub_config,我把步骤先列出来,有兴趣的人可以看看,但是这种方式明显是多此一举,不知道写示例的人咋想的。
import requests
from com.vmware.cis_client import Session
from vmware.vapi.lib.connect import get_requests_connector
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from vmware.vapi.security.session import create_session_security_context
from vmware.vapi.stdlib.client.factories import StubConfigurationFactory
from vmware.vapi.security.user_password import \
create_user_password_security_context
server = '10.20.9.100'
username = 'administrator@vsphere.local'
password = '....'
skip_verification = True
host_url = f'https://{server}/api'
session = requests.Session()
session.verify = False
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
connector = get_requests_connector(session=session, url=host_url)
stub_config = StubConfigurationFactory.new_std_configuration(connector)
user_password_security_context = create_user_password_security_context(username,
password)
stub_config.connector.set_security_context(user_password_security_context)
session_svc = Session(stub_config)
session_id = session_svc.create()
session_security_context = create_session_security_context(session_id)
stub_config.connector.set_security_context(session_security_context)
复制代码
为什么说他多此一举呢?因为一开始我们使用很简单的方式就登录了,并列出了 vCenter 中所有的虚拟机,那登陆的对象 client 中就包含了 stub_config。如果用它这种方式,你要登录两次,一次是创建 client,另一次是创建这个 stub。
简单的获取 stub_config:
import requests
import urllib3
from vmware.vapi.vsphere.client import create_vsphere_client
session = requests.session()
session.verify = False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
client = create_vsphere_client(server='IP', username='USER', password='PASSWORD', session=session)
# 类中的变量使用 _ 开头一般是不希望在类外直接访问,但是它有这样的功能肯定直接用啊,就不管那么多了
stub_config = client._stub_config
复制代码
获得内容库 id:
from com.vmware.content_client import (Library,
LibraryModel,
LocalLibrary,
SubscribedLibrary)
# 实例化内容库对象
library_service = Library(stub_config)
# 根据内容库的名字以及它的类型来获得内容库 id,你也可以不指定内容
# 你可以使用 list 而非 find 来列出所有的内容库 id,但是你获得的 id 并不知道它对应的名称是啥
# 因为它的返回值是字符串而非对象
library_id = library_service.find(Library.FindSpec(name='new', type=LibraryModel.LibraryType(u'LOCAL')))[0]
复制代码
根据内容库 ID 获取内容库中的模板:
from com.vmware.content.library_client import (Item,
ItemModel,
StorageBacking,
SubscribedItem)
template_name = 'CentOS 7.5'
library_item_id = library_item_service.find(Item.FindSpec(name=template_name, library_id=library_id))[0]
library_item_obj = library_item_service.get(library_item_id)
复制代码
上面的 library_item_id 就是模板的 id,下面的 library_item_obj 则是这个模板的对象,对象中包含了该模板的一些简要信息。其实如果只是部署虚拟机的话,只需要模板 id,模板对象不重要。
创建部署目标
有了模板 id,我们还需要创建一个 DeploymentTarget,用于指定虚拟机应该部署到哪儿。它可以指定三个属性:资源池、主机和文件夹。
class LibraryItem.DeploymentTarget(resource_pool_id=None, host_id=None, folder_id=None)
复制代码
为啥没有集群呢?在没有资源池的情况下,资源池就是集群;而有资源池的情况下,也就不用指定集群了,所以资源池必须指定。剩下的主机和文件夹看需求。这三个参数的值同样不是它们的名称,而是其 id。获得资源池 id 的方法在此,主机 id 和文件夹 id 的获取方式也是如此,这里就不赘述了。
from com.vmware.vcenter.ovf_client import LibraryItem
library_item = LibraryItem(stub_config)
deployment_target = library_item.DeploymentTarget(resource_pool_id=resource_pool_id)
复制代码
创建 ResourcePoolDeploymentSpec
就如同创建虚拟机会创建一个所谓的 spec 一样,部署虚拟机同样先要创建这么个 spec,通过这个就能部署虚拟机了。这个 spec 是一个类型,支持的参数挺多:
class LibraryItem.ResourcePoolDeploymentSpec(name=None, annotation=None, accept_all_eula=None, network_mappings=None, storage_mappings=None, storage_provisioning=None, storage_profile_id=None, locale=None, flags=None, additional_parameters=None, default_datastore_id=None)
复制代码
它的所有选项针对的都是这个 o