Python API:OpenStack 隐藏深处的秘密

JuliusThigp 8年前
   <p>OpenStack 是一个越来越流行的、用于部署基础架构即服务 (IaaS) 云的开源解决方案。OpenStack 附带了一个仪表板 Web 应用程序,非常适合执行手动任务,比如启动单个虚拟机 (VM) 实例,但是,如果希望自动化基于云的任务,则需要编写可操作 OpenStack 的脚本。</p>    <h2>OpenStack 中的服务</h2>    <p>OpenStack 中的 <em>服务e</em> 这个词承载了太多含义。它用于表示:</p>    <ul>     <li>一个 OpenStack 项目(比如 Compute Service ( nova ) 和 Identity Service ( keystone ))</li>     <li>Identity Service 目录中的一个条目(比如 Image、Compute 和 Volume)</li>     <li>一个 Linux® 守护进程(比如 nova-api 和 quantum-l3-agent )</li>    </ul>    <p>一个 OpenStack 项目与 Identity Service 目录中的一个或多个条目有关联,由一个或多个 Linux 守护进程实现。在本文中,我使用 “服务” 表示 OpenStack 项目。</p>    <p>许多用户直接为 OpenStack 具象状态传输 (REST) 应用编程接口 (API) 编写自动化脚本,或者编写调用命令行工具(比如 keystone 或 nova )的 shell 脚本。但 Python 中有一种编写 OpenStack 自动化脚本的更好方法。所有 OpenStack 服务都公开了原生 Python API,以公开与命令行工具相同的特性集。不幸的是,描述如何使用这些 API 的文档很少。</p>    <p>如果您是 Python 程序员,那么 Python API 会比命令行工具或 REST API 容易使用得多。在本文中,我将演示如何使用原生 OpenStack Python API 自动化常见的用户和管理性任务。</p>    <h2>OpenStack 项目和代号</h2>    <p><em>OpenStack</em> 这个词不是指单个应用程序。它是一个服务集合,这些服务协同工作来实现 IaaS 云。(参见边栏,了解这里所说的 “服务” 的含义。)每个 OpenStack 服务都有一个正式名称和一个代号,如表 1 所示,而且每个 OpenStack 服务公开了自己的 Python API。</p>    <p>表 1. OpenStack 服务和代号</p>    <table summary="2 column table with headings">     <thead>      <tr>       <th scope="col">正式名称</th>       <th scope="col">代号</th>      </tr>     </thead>     <tbody>      <tr>       <td>Identity Service</td>       <td>keystone</td>      </tr>      <tr>       <td>Image Service</td>       <td>glance</td>      </tr>      <tr>       <td>Compute Service</td>       <td>nova</td>      </tr>      <tr>       <td>Networking Service</td>       <td>quantum</td>      </tr>      <tr>       <td>Block Storage Service</td>       <td>cinder</td>      </tr>      <tr>       <td>Object Storage Service</td>       <td>swift</td>      </tr>     </tbody>    </table>    <h2>安装 Python 绑定</h2>    <p>Python 绑定与每个服务的命令行工具捆绑在一起。事实上,每个命令行工具使用相应的 Python API 实现。可从 Python Package Index(PyPi — 参见,获取相关链接)中使用 pip (一个 Python 包安装程序)来安装每个工具。 pip 包名称包括:</p>    <ul>     <li>python-keystoneclient</li>     <li>python-glanceclient</li>     <li>python-novaclient</li>     <li>python-quantumclient</li>     <li>python-cinderclient</li>     <li>python-swiftclient</li>    </ul>    <p>例如,要安装 keystone 客户端,可运行以下命令:</p>    <pre>  <code class="language-python">$ pipinstallpython-keystoneclient  </code></pre>    <p>可将这些包安装到 Python 虚拟环境或您的系统级 Python 包中,只要您拥有本地机器上的根用户特权。</p>    <p>所有 OpenStack API 都有一些版本,Python 绑定支持多个 API 版本,以保持向后兼容性。因此可以安全地下载这些包的最新版本,因为它们将会适合 OpenStack 服务的所有更低版本。</p>    <p>在本文中,我将重点介绍来自以下服务的 Python API 示例:</p>    <ul>     <li>OpenStack Identity Service ( keystone )</li>     <li>OpenStack Image Service ( glance )</li>     <li>OpenStack Compute Service ( nova )</li>    </ul>    <h2>设置一个测试环境</h2>    <p>为了最充分地掌握本文的内容,建议您使用管理员特权访问一个 OpenStack 云,以便试用这些代码段。如果目前没有 OpenStack 云的管理员访问权,那么最简单的方法就是在一个 VM 中部署 OpenStack。DevStack 项目(参见获取链接)旨在简化在单个机器上创建一个面向开发的 OpenStack 部署的过程。配合 VirtualBox 等虚拟化工具,您可以在笔记本电脑上(甚至在 Mac 或 Windows® 上)实现一个 OpenStack 云。</p>    <p>您还可以获得 TryStack 上的一个免费帐户,TryStack 是由社区维护的 OpenStack 沙盒(参见)。请注意,只能获取 TryStack 上的用户级特权,不能获得管理级特权,所以您无法使用 TryStack 测试需要管理特权的脚本。</p>    <h2>OpenStack Identity (keystone)</h2>    <p>客户端要对 Identity ( keystone ) API 发出请求,可实例化适当的 keystone 客户端 Python 对象并调用它的方法。因为 API 提供了版本控制,所以 Python 客户端始终与该 API 的一个特定版本有关联。</p>    <p>清单 1 显示了使用 keystone 客户端的 2.0 版将 Image Service 添加到服务目录的示例。</p>    <p>清单 1. 使用 keystone 创建一个管理员角色</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  # Replace the method arguments with the ones from your local config  keystone = ksclient.Client(auth_url="http://192.168.27.100:35357/v2.0",                            username="admin",                            password="devstack",                            tenant_name="demo")  glance_service = keystone.services.create(name="glance",                              service_type="image",                              description="OpenStack Image Service")  </code></pre>    <h3>凭据</h3>    <p>在实例化 keystoneclient.v2_0.client.Client 对象时必须提供凭据。 keystone 端点接受两种类型的凭据:令牌,或者用户名和密码。如果您是管理员,那么您可以使用 admin 令牌,这是一种具有管理员特权且永不过期的特殊令牌。要定义此令牌,可以使用运行 keystone 服务的机器上的 /etc/keystone/keystone.conf 文件中的 admin_token 选项(参见清单 2)。</p>    <p>清单 2. 使用身份验证令牌执行身份验证</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient     # Replace the values below with the ones from your local config  endpoint = "http://192.168.27.100:35357/v2.0"  admin_token = "devstack"     keystone = ksclient.Client(endpoint=endpoint, token=admin_token)  </code></pre>    <p>出于安全原因,一般不赞成使用 admin 令牌。相反,在创建了具有管理特权的用户之后,建议 OpenStack Identity 开发人员始终使用用户名和密码进行身份验证(参见清单 3)。</p>    <p>清单 3. 使用用户名和密码执行身份验证</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient     # Replace the values below  the ones from your local config,  auth_url = "http://192.168.27.100:35357/v2.0"  username = "admin"  password = "devstack"  tenant_name = "demo"     keystone = ksclient.Client(auth_url=auth_url, username=username,                            password=password, tenant_name=tenant_name)  </code></pre>    <h3>加载一个 openrc 文件</h3>    <p>为了简化身份验证,建议创建一个 openrc 文件,将凭据导出到环境变量中。这样做可以避免将登录信息硬编码到脚本中。清单 4 显示了一个 openrc 文件示例。</p>    <p>清单 4. 从环境变量加载凭据</p>    <pre>  <code class="language-python">exportOS_USERNAME="myname"  exportOS_PASSWORD="mypassword"  exportOS_TENANT_NAME="mytenant"  exportOS_AUTH_URL="http://10.20.0.2:5000/v2.0/"  </code></pre>    <p>环境变量 OS_USERNAME、OS_PASSWORD、OS_TENANT_NAME 和 OS_AUTH_URL 在所有 Python 命令行工具中已标准化。如果设置了这些环境变量,命令行工具( keystone 、 nova )将会使用它们对其 API 端点进行身份验证。</p>    <p>使用 Bash source 内置命令将这些环境变量加载到您当前的 shell 中。如果使用 Bash 作为标准 shell,那么您可能希望将这行代码添加到 .profile 中,以便在您每次登录时自动设置这些环境变量:</p>    <pre>  <code class="language-python">$ sourceopenrc  </code></pre>    <p>找到 openrc 文件后,Python 脚本就可以从环境中检索凭据。我们创建了一个名为 credentials.py 的 Python 文件(如清单 5 所示),以便从环境中提取登录信息。请注意, keystone 和 nova 在其客户端初始化器方法中使用了稍微不同的变量名,所以我为每个工具定义了不同的函数。</p>    <p>清单 5. credentials.py</p>    <pre>  <code class="language-python">#!/usr/bin/env python  importos     defget_keystone_creds():      d = {}      d['username'] = os.environ['OS_USERNAME']      d['password'] = os.environ['OS_PASSWORD']      d['auth_url'] = os.environ['OS_AUTH_URL']      d['tenant_name'] = os.environ['OS_TENANT_NAME']      return d     defget_nova_creds():      d = {}      d['username'] = os.environ['OS_USERNAME']      d['api_key'] = os.environ['OS_PASSWORD']      d['auth_url'] = os.environ['OS_AUTH_URL']      d['project_id'] = os.environ['OS_TENANT_NAME']      return d  </code></pre>    <h3>身份验证令牌</h3>    <p>如果客户端初始化器返回时没有抛出异常,则它已成功向端点验证。您可访问刚才通过返回的对象 auth_token 属性发出的 keystone 令牌,如清单 6 所示。当对 glance API 进行身份验证时,需要显式地将一个 keystone 身份验证令牌作为参数传递给初始化器,我们稍后会对此进行讨论。</p>    <p>清单 6. 在一个交互式 Python 会话中对一个 keystone 端点执行成功的验证</p>    <pre>  <code class="language-python">>>> importkeystoneclient.v2_0.clientas ksclient  >>> fromcredentialsimportget_keystone_creds  >>> creds = get_keystone_creds()  >>> keystone = ksclient.Client(**creds)  >>> keystone.auth_token  u'MIILkAYJKoZIhvcNAQcCoIILgTCCC30CAQExCTAHBgUrDgMCGjCCCmkGCSqGSIb3DQEHAaCCCloE  ggpWeyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAiMjAxMy0wNS0yNlQwMjoxMjo0Mi  42MDAwMjUiLCAiZXhwaXJlcyI6ICIyMDEzLTA1LTI3VDAyOjEyOjQyWiIsICJpZCI6ICJwbGFjZWhv  bGRlciIsICJ0ZW5hbnQiOiB7ImRlc2NyaXB0aW9uIjogbnVsbCwgImVuYWJsZWQiOiB0cnVlLCAiaW  ...  fI9JnOBZJwuoma8je0a1AvLff6AcJ1zFkVZGb'  </code></pre>    <p>备注:OpenStack Identity 的 Grizzly 版本默认情况下使用了公钥基础架构令牌,这些令牌比 OpenStack 的以前版本中使用的通用惟一标识符令牌(例如 7d9fde355f09458f8e97986a5a652bfe )要长得多。</p>    <h3>CRUD 操作</h3>    <p>keystone API 实质上是一个创建、读取、更新、删除 (CRUD) 接口:与 keystone API 的大多数交互都会读取 keystone 后端数据库或修改它。与该 API 的大多数交互都会调用 Manager 对象。一个 Manager 表示一个具有相同类型的对象集合。例如, UserManager 处理 keystone 用户, TenantManager 处理租户, RoleManager 处理角色,等等。这些管理器支持多种操作,比如 create (创建一个新对象)、 get (按 ID 检索一个对象)、 list (检索所有对象)和 delete 。</p>    <h3>创建用户、租户和角色</h3>    <p>通常,在部署 OpenStack 时,执行的第一个任务是创建一个 keystone 租户,然后创建一个具有管理特权的 keystone 用户。显示了一个使用 Python API 自动化此过程的示例。该脚本可执行以下任务:</p>    <ul>     <li>创建一个用户角色 ( Client.roles.create )。</li>     <li>创建一个管理角色 ( Client.roles.create )。</li>     <li>创建一个名为 acme 的租户 ( Client.tenants.create )。</li>     <li>创建一个名为 admin 的用户 ( Client.users.create )。</li>     <li>为 admin 用户分配 acme 租户中的管理角色 ( Client.roles.add_user_role )。</li>    </ul>    <p>这是一个适合使用 admin 令牌的场景,因为 Identity Service 不包含任何具有管理特权的用户。</p>    <p>清单 7. 创建一个用户、租户和角色</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  endpoint = "http://192.168.27.100:35357/v2.0"  admin_token = "devstack"     keystone = ksclient.Client(endpoint=endpoint, token=admin_token)  user_role = keystone.roles.create("user")  admin_role = keystone.roles.create("admin")  acme_tenant = keystone.tenants.create(tenant_name="Acme",                          description="Employees of Acme Corp.",                          enabled=True)  admin_user = keystone.users.create(name="admin",                  password="a.G'03134!j",                  email="cloudmaster@example.com", tenant_id=acme_tenant.id)  keystone.roles.add_user_role(admin_user, admin_role, acme_tenant)  </code></pre>    <h3>创建服务和端点</h3>    <p>通常,在 OpenStack 云中,部署 Identity Service 的下一个任务是在 keystone 中填充云和端点中的服务。清单 8 显示了使用 Client.services.create 和 Client.endpoints.create 方法为 Identity Service 添加一个服务和端点的示例。</p>    <p>清单 8. 创建一个服务和端点</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  creds = get_keystone_creds() # See <a href="#openrc">openrc-creds</a>  keystone = ksclient.Client(**creds)  service = keystone.services.create(name="keystone",                                    service_type="identity",                                    description="OpenStack Identity Service")     keystone_publicurl = "http://192.168.27.100:5000/v2.0"  keystone_adminurl = "http://192.168.27.100:35357/v2.0"  keystone.endpoints.create(service_id=service.id,                            region="Northeast",                            publicurl=keystone_publicurl,                            adminurl=keystone_adminurl,                            internalurl=keystone_publicurl)  </code></pre>    <h3>访问服务目录</h3>    <p>keystone 的主要功能之一就是充当服务目录。客户端可使用 keystone 查找 OpenStack 服务的端点 URL。该 API 通过 keystoneclient.v2_0.client.Client.service_catalog.url_for 方法提供此功能。此方法支持按类型(例如 image、volume、compute、network)和端点类型( publicURL 、 internalURL 、 adminURL )查找服务端点。</p>    <p>清单 9 演示了如何使用 url_for 方法检索 OpenStack Image ( glance ) Service 的端点。</p>    <p>清单 9. 在一个交互式 Python 会话中查询 glance 端点</p>    <pre>  <code class="language-python">>>> import keystoneclient.v2_0.clientas ksclient  >>> creds = get_keystone_creds() # See <a href="openrc-creds" />  >>> keystone = ksclient.Client(**creds)  >>> glance_endpoint = keystone.service_catalog.url_for(service_type='image',                                                        endpoint_type='publicURL')  >>> glance_endpoint  u'http://192.168.27.100:9292'  </code></pre>    <h2>OpenStack Compute (nova)</h2>    <h2>nova API 的 1.1 版和第 2 版</h2>    <p>nova API 的 1.1 版和第 2 版是相同的。可用 “2” 代替 “1.1”,传递它作为 novaclient.client.Client initializer 的第一个参数,但没有 novaclient.v2 模块,只有一个 novaclient.v1_1 模块。</p>    <p>OpenStack Compute ( nova ) Python API 的工作原理类似于 OpenStack Identity API。这里使用了 nova API 的 1.1 版,所以本文中使用的 nova Python 绑定的 1.1 版中的类位于 novaclient.v1_1 Python 命名空间中。</p>    <h3>对 nova-api 端点执行身份验证</h3>    <p>要向 nova-api 端点发出请求,可实例化一个 novaclient.v1_1.client.Client 对象并对它执行调用。可通过两种方式获取一个与该 API 的 1.1 版进行通信的客户端。清单 10 演示了如何通过传递版本字符串作为参数来获取合适的客户端。</p>    <p>清单 10. 传递版本作为参数</p>    <pre>  <code class="language-python">fromnovaclientimportclientas novaclient  fromcredentialsimportget_nova_creds  creds = get_nova_creds()  nova = novaclient.Client("1.1", **creds)  </code></pre>    <p>清单 11 演示了如何通过显式导入 1.1 版模块来获取合适的客户端。</p>    <p>清单 11. 直接导入版本</p>    <pre>  <code class="language-python">importnovaclient.v1_1.clientas nvclient  fromcredentialsimportget_nova_creds  creds = get_nova_creds()  nova = nvclient.Client(**creds)  </code></pre>    <h3>清单实例</h3>    <p>使用 Client.servers.list 方法列出当前的 VM 实例,如清单 12 所示。</p>    <p>清单 12. 在一个交互式 Python 会话中获取一个 VM 实例列表</p>    <pre>  <code class="language-python">>>> import novaclient.v1_1.clientas nvclient  >>> from credentialsimport get_nova_creds  >>> creds = get_nova_creds()  >>> nova = nvclient.Client(**creds)  >>> nova.servers.list()  [<Server: cirros>]  </code></pre>    <h3>按名称获取一个实例并关闭它</h3>    <p>如果不知道实例的 ID,只知道它的名称,那么可以使用 Server.find 方法。清单 13 显示了如何按名称找到一个实例,然后使用 Server.delete 方法终止它。</p>    <p>清单 13. 终止 “my-vm” 实例</p>    <pre>  <code class="language-python">importnovaclient.v1_1.clientas nvclient  fromcredentialsimportget_nova_creds  creds = get_nova_creds()  nova = nvclient.Client(**creds)     server = nova.servers.find(name="my-vm")  server.delete()  </code></pre>    <h3>启动一个实例并检查状态</h3>    <p>要启动一个新实例,可以使用 Client.servers.create 方法,如清单 14 所示。请注意,必须传递一个 image 对象和 flavor 对象,而不是 image 和 flavor 的名称。该示例还使用 Client.keypairs.create 方法将 Secure Shell (SSH) 公钥上传到 ~/.ssh/id_rsa.pub ,并将密钥对命名为 mykey(假设该密钥对还不存在)。最后,它使用 Client.servers.get 方法获取该实例的当前状态,使用它来轮询状态。</p>    <p>清单 14. 启动一个新实例</p>    <pre>  <code class="language-python">importos  importtime  importnovaclient.v1_1.clientas nvclient  fromcredentialsimportget_nova_creds  creds = get_nova_creds()  nova = nvclient.Client(**creds)  if not nova.keypairs.findall(name="mykey"):      withopen(os.path.expanduser('~/.ssh/id_rsa.pub')) as fpubkey:          nova.keypairs.create(name="mykey", public_key=fpubkey.read())  image = nova.images.find(name="cirros")  flavor = nova.flavors.find(name="m1.tiny")  instance = nova.servers.create(name="test", image=image, flavor=flavor, key_name="mykey")     # Poll at 5 second intervals, until the status is no longer 'BUILD'  status = instance.status  while status == 'BUILD':      time.sleep(5)      # Retrieve the instance again so the status field updates      instance = nova.servers.get(instance.id)      status = instance.status  print "status: %s" % status  </code></pre>    <h3>附加一个浮动 IP 地址</h3>    <p>要附加一个浮动 IP 地址,必须首先验证 OpenStack 是否拥有一个可用的浮动 IP 地址。使用 Client.floating_ips.list 方法获取可用的浮动 IP 地址列表。如果结果列表是空的,可使用 Client.floating_ips.create 方法分配一个新的浮动 IP 地址,然后使用 Server.add_floating_ip 方法将它分配给该实例,如清单 15 所示。</p>    <p>清单 15. 创建一个浮动 IP 地址</p>    <pre>  <code class="language-python">>>> nova.floating_ips.list()  []  >>> floating_ip = nova.floating_ips.create()  <FloatingIPfixed_ip=None, id=1, instance_id=None, ip=192.168.27.129, pool=public>  >>> instance = nova.servers.find(name="test")  >>> instance.add_floating_ip(floating_ip)  </code></pre>    <h3>更改一个安全组</h3>    <p>使用 Client.security_group_rules.create 方法向一个安全组添加规则。清单 16 中的示例修改了默认的安全组,以便支持 SSH(在端口 22 上运行)以及所有 Internet Control Message Protocol (ICMP) 流量。为此,我使用 Client.security_groups.find 方法获取了名为 default 的安全组。</p>    <p>清单 16. 允许使用 default 安全组中的端口 22 和 ICMP</p>    <pre>  <code class="language-python">importnovaclient.v1_1.clientas nvclient  fromcredentialsimportget_nova_creds  creds = get_nova_creds()  nova = nvclient.Client(**creds)     secgroup = nova.security_groups.find(name="default")  nova.security_group_rules.create(secgroup.id,                                ip_protocol="tcp",                                from_port=22,                                to_port=22)  nova.security_group_rules.create(secgroup.id,                                ip_protocol="icmp",                                from_port=-1,                                to_port=-1)  </code></pre>    <h3>获取控制台日志</h3>    <p>Server.get_console_output 方法获取了在启动 VM 时发送给控制台的文本。可以分析控制台输出,以解决更改主机密钥的常见问题。</p>    <p>IaaS 云(比如 OpenStack)的一个缺陷是,它不能很好地与 SSH 主机密钥检查功能进行互操作。如果登录到(比如 10.40.1.150 上的)某个实例,而该 IP 地址之前被您过去登录的另一个实例使用过,那么您将获得一个类似清单 17 的错误。</p>    <p>清单 17. ID 更改后的错误</p>    <pre>  <code class="language-python">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @    WARNING: REMOTEHOSTIDENTIFICATIONHASCHANGED!    @  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ITIS POSSIBLETHATSOMEONEIS DOINGSOMETHINGNASTY!  Someonecouldbeeavesdroppingonyourightnow (man-in-the-middleattack)!  Itis alsopossiblethat a hostkeyhasjustbeenchanged.  Thefingerprintfor theRSAkeysentbytheremotehostis  6f:2b:59:46:cb:8c:81:48:06:f3:c5:db:40:23:d3:be.  Pleasecontactyoursystemadministrator.  Addcorrecthostkeyin /home/mylogin/.ssh/known_hoststo getridofthis message.  Offendingkeyin /home/mylogin/.ssh/known_hosts:1  RSAhostkeyfor 10.40.1.150 haschangedand youhaverequestedstrictchecking.  Hostkeyverificationfailed.  </code></pre>    <p>如果您的 VM 映像安装了 cloud-init 包(参见),那么它会将主机密钥输出到控制台,如清单 8 所示。</p>    <p>清单 18. 控制台中的 SSH 密钥输出示例</p>    <pre>  <code class="language-python">-----BEGINSSHHOSTKEYKEYS-----  ecdsa-sha2-nistp256AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDciNMyzj0osyPOM+  1OyseTWgkzw+M43zp5H2CchG8daRDHel7V3OHETVdI6WofNn  SdBJAwIoisRFPxyroNGiVw= root@my-name  ssh-rsaAAAAB3NzaC1yc2EAAAADAQABAAABAQDU854+fNdcKMZTLCUejMOZllQmmphr6V5Aaz1F2+x2jXql5rqKQ  d5/h6OdFszcp+gdTeVtfgG++/298qodTemVVrvqwjp4eN87iHvhPxH6GDEevAKlEed2ckdAmgvzI9rcOYgR/46G9x  Iea0IdgNjMvN1baj6WPtv+HfcfH/ZV58G306lSJfbz/GVxNTIxW+Wg7ZQCAe6jWgm4oQ+66sco+7Fub24EPue3kO8  jqufqq3mY5+MFlzEHSX5B04ioG5Alw/JuqVx5+7zHt9I2wA3nzsyUdKtCTrw8V4fYEhWDm53WLOpW+8CeYCXuv+yL  7EjwLqhIH/TUuzGQiWmFGvyzroot@my-name  -----END SSHHOSTKEYKEYS-----  </code></pre>    <p>清单 19 给出的脚本使用 Server.get_console_output API 方法从控制台中提取 SSH 主机密钥,然后更新 ~/.ssh/known_hosts 文件,所以在首次使用 SSH 访问浮动 IP 地址时,您不会获得此 SSH 警告。</p>    <p>清单 19. 从控制台提取 SSH 主机密钥</p>    <pre>  <code class="language-python">importos  importsubprocess  importnovaclient.v1_1.clientas nvclient  fromcredentialsimportget_nova_creds        defget_server(creds, servername):      nova = nvclient.Client(**creds)      return nova.servers.find(name=servername)        defremove_hostkey(ip):      subprocess.call(["ssh-keygen", "-R", ip])        defget_hostkey_from_console(text):      lines = text.split('n')      start = '-----BEGIN SSH HOST KEY KEYS-----r'      end = '-----END SSH HOST KEY KEYS-----r'      start_ind = lines.index(start)      end_ind = lines.index(end)      for i in range(start_ind+1, end_ind):          key = lines[i].rstrip()          if key.startswith('ssh-rsa'):              return key      raiseKeyError("ssh host key not found")        defmain():      server = get_server(get_nova_creds(), "my-server")      netname = "my-network"      (fixed_ip, floating_ip) = server.networks[netname]      # Remove existing key, if any      remove_hostkey(floating_ip)      output = server.get_console_output()      key = get_hostkey_from_console(output)      withopen(os.path.expanduser("~/.ssh/known_hosts"), 'a') as f:          f.write("{0} {1}n".format(floating_ip, key))        if __name__ == '__main__':      main()  </code></pre>    <h2>OpenStack Image (glance)</h2>    <p>OpenStack Image Service ( glance ) 负责管理 Compute Service 所使用的一个 VM 映像目录。</p>    <h3>对 glance 端点执行身份验证</h3>    <p>OpenStack Image ( glance ) Python API 在执行初始身份验证时与 Compute API 有一些细微的差别。 glance API 依赖于必须从 keystone API 获取的信息:</p>    <ul>     <li>glance 端点 URL</li>     <li>一个 keystone 身份验证令牌</li>    </ul>    <p>像 nova API 一样,对于 glance API,您可以传递 API 的版本作为参数或直接导入该模块。清单 20 中的示例展示了如何使用该 API 的第 2 版,对 glance 端点执行身份验证。</p>    <p>清单 20. 对 glance API 执行身份验证</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  importglanceclient  creds = get_keystone_creds()  keystone = ksclient.Client(**creds)  glance_endpoint = keystone.service_catalog.url_for(service_type='image',                                                    endpoint_type='publicURL')  glance = glanceclient.Client('2',glance_endpoint, token=keystone.auth_token)  </code></pre>    <p>清单 21 中的示例直接导入了相关的 glance 模块。</p>    <p>清单 21. 直接导入 glance 模块</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  importglanceclient.v2.clientas glclient  creds = get_keystone_creds()  keystone = ksclient.Client(**creds)  glance_endpoint = keystone.service_catalog.url_for(service_type='image',                                                    endpoint_type='publicURL')  glance = glclient.Client(glance_endpoint, token=keystone.auth_token)  </code></pre>    <h3>列出可用映像</h3>    <p>使用 Client.images.list 方法列出当前的映像,如清单 22 所示。请注意,此方法会返回一个生成器,其中 nova API 中的 list 方法将会返回列表对象。</p>    <p>清单 22. 获取一个 VM 映像列表</p>    <pre>  <code class="language-python">>>> import keystoneclient.v2_0.clientas ksclient  >>> import glanceclient.v2.clientas glclient  >>> creds = get_keystone_creds()  >>> keystone = ksclient.Client(**creds)  >>> glance_endpoint = keystone.service_catalog.url_for(service_type='image',  ...                                                    endpoint_type='publicURL')  >>> glance = glclient.Client(glance_endpoint, token=keystone.auth_token)  >>> images = glance.images.list()  >>> images  <generatorobject list at 0x10c8efd70>  >>> images.next()  {u'status': u'active', u'tags': [], u'kernel_id':  u'8ab02091-21ea-434c-9b7b-9b4e2ae49591', u'container_format': u'ami', u'min_ram': 0,  u'ramdisk_id': u'd36267b5-7cae-4dec-b5bc-6d2de5c89c64', u'updated_at':  u'2013-05-28T00:44:21Z', u'visibility': u'public', u'file':  u'/v2/images/cac50405-f4d4-4715-b1f6-7f00ff5030e6/file', u'min_disk': 0,  u'id': u'cac50405-f4d4-4715-b1f6-7f00ff5030e6', u'size': 25165824, u'name':  u'cirros-0.3.1-x86_64-uec', u'checksum': u'f8a2eeee2dc65b3d9b6e63678955bd83',  u'created_at': u'2013-05-28T00:44:21Z', u'disk_format': u'ami', u'protected':  False, u'schema': u'/v2/schemas/image'}  </code></pre>    <h3>将一个映像上传到 glance</h3>    <p>清单 23 中的示例展示了如何使用 glance API 上传一个文件。这里需要使用该 API 的第 1 版创建一个映像,因为 Python API 绑定还未实现第 2 版的 create 方法。</p>    <p>清单 23. 将一个映像上传到 glance</p>    <pre>  <code class="language-python">importkeystoneclient.v2_0.clientas ksclient  importglanceclient  creds = get_keystone_creds()  keystone = ksclient.Client(**creds)  glance_endpoint = keystone.service_catalog.url_for(service_type='image',                                                    endpoint_type='publicURL')  glance = glanceclient.Client('1',glance_endpoint, token=keystone.auth_token)  withopen('/tmp/cirros-0.3.0-x86_64-disk.img') as fimage:      glance.images.create(name="cirros", is_public=True, disk_format="qcow2",                          container_format="bare", data=fimage)  </code></pre>    <h2>后续步骤</h2>    <p>本文仅简短概述了 OpenStack Python API 公开的功能。可通过多种方式进一步了解这些 API 的工作原理。</p>    <h3>官方 API 文档</h3>    <p>OpenStack 项目维护着所有 OpenStack Python API 的文档(参见)。所有这些 API 都拥有每个模块、类和方法的自动生成的文档。一些 API 的文档中包含使用示例,而其他文档没有。</p>    <h3>Introspect the API</h3>    <p>了解 API 的一种最佳方式是在一个交互式命令行 Python 解释器中使用它们。bpython 解释器是一个增强的 Python 解释器,会在您键入时显示有效的方法名称,还会自动显示一个函数的文档字符串(参见图 1)。</p>    <p>图 1. bpython 的自动帮助显示界面</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1d756a7b1531c0e52f392a42fabed738.jpg"></p>    <h3>查阅 CLI 源代码</h3>    <p>Python 的一个优势在于它的可读性,没有任何方法能够比阅读源代码更好地了解 API。所有包都托管在 openstack 小组中的 github 上。例如,要获取 nova API 源代码的副本,可运行以下命令:</p>    <pre>  <code class="language-python">gitclone http://github.com/openstack/python-novaclient  </code></pre>    <p>因为这些命令行客户端是使用该 API 实现的,所以每个包都提供了一个示例应用程序。</p>    <p>对于 novaclient API,最有趣的文件包含在 novaclient/v1_1 目录中,该目录包含形成此 API 的 Python 类。shell 上的命令行命令被实现为 novaclient/v1_1/shell.py 中的 do_* 方法。例如, nova flavor-list 被实现为 do_flavor_list 方法,该方法最终会调用 Client.flavors.list API 方法。</p>    <h3>查阅其他使用 Python API 的应用程序</h3>    <p>其他一些应用程序也使用了 OpenStack Python API。OpenStack Dashboard(参见,获取相关链接)完全使用 Python API 与各种 OpenStack 服务进行通信。它是使用 Python API 的应用程序的一个很好的例子。具体来讲,您可以查看 openstack_dashboard/api 目录,看看该仪表板是如何使用 Python API 的。</p>    <p>OpenStack Client(参见)用于将现有客户端上的功能统一到单个命令行接口中。它使用了来自其他所有项目的 Python API。</p>    <p>Heat 项目是一个设计为使用 OpenStack 的业务流程层,它使用了这些 API。具体来讲,请查看 heat/engine/clients.py 文件。</p>    <p>Ansible 是一个基于 Python 的配置管理工具,拥有多个使用 Python API 的 OpenStack 模块。具体来讲,请查看 library/cloud 目录,其中包含 Ansible OpenStack 模块。</p>    <p>了解 Python API 的工作原理后,很难想像再返回使用 REST API 或命令行工具构建您的 OpenStack 自动化脚本的情形。</p>    <p> </p>    <p>来自:http://python.jobbole.com/87408/</p>    <p> </p>