2016年3月21日月曜日

OpenStack Python APIでCinderボリュームを作成する

OpenStackのPython APIを使ってCinderボリュームを作成し、Ubuntuインスタンスにマウントしてみましょう。
まずはUbuntu 14.0インスタンスを用意します。

Python APIを導入する

次のコマンドを実行してPython APIを導入します。

sudo apt-get install python-openstackclient


環境変数をセットする


OpenStackのHorizonダッシュボードからopenrc.shをダウンロードします。
Compute→Access & Security→API Accessを開き、Download OpenStack RC Fileボタンを押します。

Ubuntu上にopenrc.shというファイルを作成し、中身をコピー&ペーストします。
こんな感じです。

#!/bin/bash

# To use an OpenStack cloud you need to authenticate against the Identity
# service named keystone, which returns a **Token** and **Service Catalog**.
# The catalog contains the endpoints for all services the user/tenant has
# access to - such as Compute, Image Service, Identity, Object Storage, Block
# Storage, and Networking (code-named nova, glance, keystone, swift,
# cinder, and neutron).
#
# *NOTE*: Using the 2.0 *Identity API* does not necessarily mean any other
# OpenStack API is version 2.0. For example, your cloud provider may implement
# Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is
# only for the Identity API served through keystone.
export OS_AUTH_URL=https://xxxxxxxxxxxx.com:5000/v2.0

# With the addition of Keystone we have standardized on the term **tenant**
# as the entity that owns the resources.
export OS_TENANT_ID=588ce32358c4494088c070548f1f233a
export OS_TENANT_NAME="sandbox"
export OS_PROJECT_NAME="sandbox"

# In addition to the owning entity (tenant), OpenStack stores the entity
# performing the action as the **user**.
export OS_USERNAME="matsuos"

# With Keystone you pass the keystone password.
echo "Please enter your OpenStack Password: "
read -sr OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT

# If your configuration has multiple regions, we set that information here.
# OS_REGION_NAME is optional and only valid in certain environments.
export OS_REGION_NAME="RegionOne"
# Don't leave a blank variable, unset it if it was empty
if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi

実行権限を付与します。
chmod +x openrc.sh


openrc.shを実行してOpenStack APIの認証情報をセットします。
. ./openrc.sh

パスワードを入力します。

次のコマンドを入力して実行できる状態かどうか確認します。

nova list
+--------------------------------------+-----------------------+--------+------------+-------------+---------------------------------------+
| ID                                   | Name                  | Status | Task State | Power State | Networks                              |
+--------------------------------------+-----------------------+--------+------------+-------------+---------------------------------------+
| e7bc2bd0-37db-409e-84bc-b2adf868d14d | NW_test1              | ACTIVE | -          | Running     | internal=10.220.0.232, 161.202.195.75 |
こんな具合にインスタンス一覧が表示されれば準備完了です。

どんなAPIがあるか

次のコマンドを実行して、APIドキュメントを参照します。
pydoc cinderclient.v2.volumes


---------------------------------------------------------------------------------
Help on module cinderclient.v2.volumes in cinderclient.v2:

NAME
    cinderclient.v2.volumes - Volume interface (v2 extension).

FILE
    /usr/lib/python2.7/dist-packages/cinderclient/v2/volumes.py

CLASSES
    cinderclient.base.ManagerWithFind(abc.NewBase)
        VolumeManager
    cinderclient.base.Resource(__builtin__.object)
        Volume

    class Volume(cinderclient.base.Resource)
     |  A volume is an extra block level storage to the OpenStack instances.
     |
     |  Method resolution order:
     |      Volume
     |      cinderclient.base.Resource
     |      __builtin__.object
     |
     |  Methods defined here:
     |
     |  __repr__(self)
     |
     |  attach(self, instance_uuid, mountpoint, mode='rw')
     |      Set attachment metadata.
     |
     |      :param instance_uuid: uuid of the attaching instance.
     |      :param mountpoint: mountpoint on the attaching instance.
     |      :param mode: the access mode.
     |
     |  begin_detaching(self, volume)
     |      Begin detaching volume.
     |
     |  delete(self)
     |      Delete this volume.
     |
     |  detach(self)
     |      Clear attachment metadata.
     |
     |  extend(self, volume, new_size)
     |      Extend the size of the specified volume.
     |      :param volume: The UUID of the volume to extend
     |      :param new_size: The desired size to extend volume to.
     |
     |  force_delete(self)
     |      Delete the specified volume ignoring its current state.
     |
     |      :param volume: The UUID of the volume to force-delete.
     |
     |  initialize_connection(self, volume, connector)
     |      Initialize a volume connection.
     |
     |      :param connector: connector dict from nova.
     |
     |  migrate_volume(self, host, force_host_copy)
     |      Migrate the volume to a new host.
     |
     |  reserve(self, volume)
     |      Reserve this volume.
     |
     |  reset_state(self, state)
     |      Update the volume with the provided state.
     |
     |  retype(self, volume_type, policy)
     |      Change a volume's type.
     |
     |  roll_detaching(self, volume)
     |      Roll detaching volume.
     |
     |  set_metadata(self, volume, metadata)
     |      Set or Append metadata to a volume.
     |
     |      :param volume : The :class: `Volume` to set metadata on
     |      :param metadata: A dict of key/value pairs to set
     |
     |  terminate_connection(self, volume, connector)
     |      Terminate a volume connection.
     |
     |      :param connector: connector dict from nova.
     |
     |  unreserve(self, volume)
     |      Unreserve this volume.
     |
     |  update(self, **kwargs)
     |      Update the name or description for this volume.
     |
     |  update_all_metadata(self, metadata)
     |      Update all metadata of this volume.
     |
     |  update_readonly_flag(self, volume, read_only)
     |      Update the read-only access mode flag of the specified volume.
     |
     |      :param volume: The UUID of the volume to update.
     |      :param read_only: The value to indicate whether to update volume to
     |          read-only access mode.
     |
     |  upload_to_image(self, force, image_name, container_format, disk_format)
     |      Upload a volume to image service as an image.
     |
     |  ----------------------------------------------------------------------
     |  Methods inherited from cinderclient.base.Resource:
     |
     |  __eq__(self, other)
     |
     |  __getattr__(self, k)
     |
     |  __init__(self, manager, info, loaded=False)
     |
     |  get(self)
     |
     |  is_loaded(self)
     |
     |  set_loaded(self, val)
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from cinderclient.base.Resource:
     |
     |  __dict__
     |      dictionary for instance variables (if defined)
     |
     |  __weakref__
     |      list of weak references to the object (if defined)
     |
     |  human_id
     |      Subclasses may override this provide a pretty ID which can be used
     |      for bash completion.
     |
     |  ----------------------------------------------------------------------
     |  Data and other attributes inherited from cinderclient.base.Resource:
     |
     |  HUMAN_ID = False

    class VolumeManager(cinderclient.base.ManagerWithFind)
     |  Manage :class:`Volume` resources.
     |
     |  Method resolution order:
     |      VolumeManager
     |      cinderclient.base.ManagerWithFind
     |      abc.NewBase
     |      cinderclient.base.Manager
     |      cinderclient.utils.HookableMixin
     |      __builtin__.object
     |
     |  Methods defined here:
     |
     |  attach(self, volume, instance_uuid, mountpoint, mode='rw')
     |      Set attachment metadata.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to attach.
     |      :param instance_uuid: uuid of the attaching instance.
     |      :param mountpoint: mountpoint on the attaching instance.
     |      :param mode: the access mode.
     |
     |  begin_detaching(self, volume)
     |      Begin detaching this volume.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to detach.
     |
     |  create(self, size, snapshot_id=None, source_volid=None, name=None, description=None, volume_type=None, user_id=None, project_id=None, availability_zone=None, metadata=None, imageRef=None, scheduler_hints=None)
     |      Create a volume.
     |
     |      :param size: Size of volume in GB
     |      :param snapshot_id: ID of the snapshot
     |      :param name: Name of the volume
     |      :param description: Description of the volume
     |      :param volume_type: Type of volume
     |      :param user_id: User id derived from context
     |      :param project_id: Project id derived from context
     |      :param availability_zone: Availability Zone to use
     |      :param metadata: Optional metadata to set on volume creation
     |      :param imageRef: reference to an image stored in glance
     |      :param source_volid: ID of source volume to clone from
     |      :param scheduler_hints: (optional extension) arbitrary key-value pairs
     |                          specified by the client to help boot an instance
     |      :rtype: :class:`Volume`
     |
     |  delete(self, volume)
     |      Delete a volume.
     |
     |      :param volume: The :class:`Volume` to delete.
     |
     |  delete_metadata(self, volume, keys)
     |      Delete specified keys from volumes metadata.
     |
     |      :param volume: The :class:`Volume`.
     |      :param keys: A list of keys to be removed.
     |
     |  detach(self, volume)
     |      Clear attachment metadata.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to detach.
     |
     |  extend(self, volume, new_size)
     |
     |  force_delete(self, volume)
     |
     |  get(self, volume_id)
     |      Get a volume.
     |
     |      :param volume_id: The ID of the volume to delete.
     |      :rtype: :class:`Volume`
     |
     |  get_encryption_metadata(self, volume_id)
     |      Retrieve the encryption metadata from the desired volume.
     |
     |      :param volume_id: the id of the volume to query
     |      :return: a dictionary of volume encryption metadata
     |
     |  initialize_connection(self, volume, connector)
     |      Initialize a volume connection.
     |
     |      :param volume: The :class:`Volume` (or its ID).
     |      :param connector: connector dict from nova.
     |
     |  list(self, detailed=True, search_opts=None)
     |      Get a list of all volumes.
     |
     |      :rtype: list of :class:`Volume`
     |
     |  migrate_volume(self, volume, host, force_host_copy)
     |      Migrate volume to new host.
     |
     |      :param volume: The :class:`Volume` to migrate
     |      :param host: The destination host
     |      :param force_host_copy: Skip driver optimizations
     |
     |  migrate_volume_completion(self, old_volume, new_volume, error)
     |      Complete the migration from the old volume to the temp new one.
     |
     |      :param old_volume: The original :class:`Volume` in the migration
     |      :param new_volume: The new temporary :class:`Volume` in the migration
     |      :param error: Inform of an error to cause migration cleanup
     |
     |  reserve(self, volume)
     |      Reserve this volume.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to reserve.
     |
     |  reset_state(self, volume, state)
     |      Update the provided volume with the provided state.
     |
     |  retype(self, volume, volume_type, policy)
     |      Change a volume's type.
     |
     |      :param volume: The :class:`Volume` to retype
     |      :param volume_type: New volume type
     |      :param policy: Policy for migration during the retype
     |
     |  roll_detaching(self, volume)
     |      Roll detaching this volume.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to roll detaching.
     |
     |  set_metadata(self, volume, metadata)
     |      Update/Set a volumes metadata.
     |
     |      :param volume: The :class:`Volume`.
     |      :param metadata: A list of keys to be set.
     |
     |  terminate_connection(self, volume, connector)
     |      Terminate a volume connection.
     |
     |      :param volume: The :class:`Volume` (or its ID).
     |      :param connector: connector dict from nova.
     |
     |  unreserve(self, volume)
     |      Unreserve this volume.
     |
     |      :param volume: The :class:`Volume` (or its ID)
     |                     you would like to unreserve.
     |
     |  update(self, volume, **kwargs)
     |      Update the name or description for a volume.
     |
     |      :param volume: The :class:`Volume` to delete.
     |
     |  update_all_metadata(self, volume, metadata)
     |      Update all metadata of a volume.
     |
     |      :param volume: The :class:`Volume`.
     |      :param metadata: A list of keys to be updated.
     |
     |  update_readonly_flag(self, volume, flag)
     |
     |  upload_to_image(self, volume, force, image_name, container_format, disk_format)
     |      Upload volume to image service as image.
     |
     |      :param volume: The :class:`Volume` to upload.
     |
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |
     |  __abstractmethods__ = frozenset([])
     |
     |  resource_class = <class 'cinderclient.v2.volumes.Volume'>
     |      A volume is an extra block level storage to the OpenStack instances.
     |
     |  ----------------------------------------------------------------------
     |  Methods inherited from cinderclient.base.ManagerWithFind:
     |
     |  find(self, **kwargs)
     |      Find a single item with attributes matching ``**kwargs``.
     |
     |      This isn't very efficient: it loads the entire list then filters on
     |      the Python side.
     |
     |  findall(self, **kwargs)
     |      Find all items with attributes matching ``**kwargs``.
     |
     |      This isn't very efficient: it loads the entire list then filters on
     |      the Python side.
     |
     |  ----------------------------------------------------------------------
     |  Methods inherited from cinderclient.base.Manager:
     |
     |  __init__(self, api)
     |
     |  completion_cache(*args, **kwds)
     |      The completion cache store items that can be used for bash
     |      autocompletion, like UUIDs or human-friendly IDs.
     |
     |      A resource listing will clear and repopulate the cache.
     |
     |      A resource create will append to the cache.
     |
     |      Delete is not handled because listings are assumed to be performed
     |      often enough to keep the cache reasonably up-to-date.
     |
     |  write_to_completion_cache(self, cache_type, val)
     |
     |  ----------------------------------------------------------------------
     |  Class methods inherited from cinderclient.utils.HookableMixin:
     |
     |  add_hook(cls, hook_type, hook_func) from abc.ABCMeta
     |
     |  run_hooks(cls, hook_type, *args, **kwargs) from abc.ABCMeta
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from cinderclient.utils.HookableMixin:
     |
     |  __dict__
     |      dictionary for instance variables (if defined)
     |
     |  __weakref__
     |      list of weak references to the object (if defined)




---------------------------------------------------------------------------------

Pythonコードを書く

create_volume.pyというファイル名のPythonコードを書いてみましょう。
このコードサンプルはmatsuos-vol1という名前で1GBのCinderボリュームを1個作成します。
すでに同じ名前のボリュームがある場合は作成しません。



from os import environ as env

volume_name='matsuos-vol1'
volume_size=1

import cinderclient.client
cinder = cinderclient.client.Client("2",auth_url=env['OS_AUTH_URL'],
                                username=env['OS_USERNAME'],
                                api_key=env['OS_PASSWORD'],
                                project_id=env['OS_TENANT_NAME'])


volumes = cinder.volumes.findall(name = volume_name)
if len(volumes) > 0 :
  print volume_name +' is already exist'
  exit()
else :
  volume = cinder.volumes.create(volume_size, name=volume_name)
  print volume_name +' has been created'


Pythonコードを実行する


次のコマンドを実行します。
python create_volume.py

初めて実行すると次のようなメッセージが返ります。
matsuos-vol1 has been created

2回目以降に実行すると次のようなメッセージが返ります。
matsuos-vol1 is already exist

作成したボリュームは次のコマンドで削除できます。

cinder list
で一覧を表示し、IDの部分をコピーして
cinder delete の後ろにペーストして実行すると削除されます。
IDは複数指定可能です。


いかがですか?
Python APIを使ってCinderボリュームを作成するのは簡単ですね。











0 件のコメント:

コメントを投稿