summaryrefslogtreecommitdiff
path: root/cinderclient/v1
diff options
context:
space:
mode:
Diffstat (limited to 'cinderclient/v1')
-rw-r--r--cinderclient/v1/__init__.py17
-rw-r--r--cinderclient/v1/client.py71
-rw-r--r--cinderclient/v1/contrib/__init__.py0
-rw-r--r--cinderclient/v1/shell.py241
-rw-r--r--cinderclient/v1/volume_snapshots.py88
-rw-r--r--cinderclient/v1/volume_types.py77
-rw-r--r--cinderclient/v1/volumes.py135
7 files changed, 629 insertions, 0 deletions
diff --git a/cinderclient/v1/__init__.py b/cinderclient/v1/__init__.py
new file mode 100644
index 0000000..cecfacd
--- /dev/null
+++ b/cinderclient/v1/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2012 OpenStack, LLC.
+#
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from cinderclient.v1.client import Client
diff --git a/cinderclient/v1/client.py b/cinderclient/v1/client.py
new file mode 100644
index 0000000..cbee8ba
--- /dev/null
+++ b/cinderclient/v1/client.py
@@ -0,0 +1,71 @@
+from cinderclient import client
+from cinderclient.v1 import volumes
+from cinderclient.v1 import volume_snapshots
+from cinderclient.v1 import volume_types
+
+
+class Client(object):
+ """
+ Top-level object to access the OpenStack Compute API.
+
+ Create an instance with your creds::
+
+ >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)
+
+ Then call methods on its managers::
+
+ >>> client.servers.list()
+ ...
+ >>> client.flavors.list()
+ ...
+
+ """
+
+ # FIXME(jesse): project_id isn't required to authenticate
+ def __init__(self, username, api_key, project_id, auth_url,
+ insecure=False, timeout=None, proxy_tenant_id=None,
+ proxy_token=None, region_name=None,
+ endpoint_type='publicURL', extensions=None,
+ service_type='compute', service_name=None,
+ volume_service_name=None):
+ # FIXME(comstud): Rename the api_key argument above when we
+ # know it's not being used as keyword argument
+ password = api_key
+
+ # extensions
+ self.volumes = volumes.VolumeManager(self)
+ self.volume_snapshots = volume_snapshots.SnapshotManager(self)
+ self.volume_types = volume_types.VolumeTypeManager(self)
+
+ # Add in any extensions...
+ if extensions:
+ for extension in extensions:
+ if extension.manager_class:
+ setattr(self, extension.name,
+ extension.manager_class(self))
+
+ self.client = client.HTTPClient(username,
+ password,
+ project_id,
+ auth_url,
+ insecure=insecure,
+ timeout=timeout,
+ proxy_token=proxy_token,
+ proxy_tenant_id=proxy_tenant_id,
+ region_name=region_name,
+ endpoint_type=endpoint_type,
+ service_type=service_type,
+ service_name=service_name,
+ volume_service_name=volume_service_name)
+
+ def authenticate(self):
+ """
+ Authenticate against the server.
+
+ Normally this is called automatically when you first access the API,
+ but you can call this method to force authentication right now.
+
+ Returns on success; raises :exc:`exceptions.Unauthorized` if the
+ credentials are wrong.
+ """
+ self.client.authenticate()
diff --git a/cinderclient/v1/contrib/__init__.py b/cinderclient/v1/contrib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cinderclient/v1/contrib/__init__.py
diff --git a/cinderclient/v1/shell.py b/cinderclient/v1/shell.py
new file mode 100644
index 0000000..6b8b7bb
--- /dev/null
+++ b/cinderclient/v1/shell.py
@@ -0,0 +1,241 @@
+# Copyright 2010 Jacob Kaplan-Moss
+
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import sys
+import time
+
+from cinderclient import utils
+
+
+def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
+ poll_period=5, show_progress=True):
+ """Block while an action is being performed, periodically printing
+ progress.
+ """
+ def print_progress(progress):
+ if show_progress:
+ msg = ('\rInstance %(action)s... %(progress)s%% complete'
+ % dict(action=action, progress=progress))
+ else:
+ msg = '\rInstance %(action)s...' % dict(action=action)
+
+ sys.stdout.write(msg)
+ sys.stdout.flush()
+
+ print
+ while True:
+ obj = poll_fn(obj_id)
+ status = obj.status.lower()
+ progress = getattr(obj, 'progress', None) or 0
+ if status in final_ok_states:
+ print_progress(100)
+ print "\nFinished"
+ break
+ elif status == "error":
+ print "\nError %(action)s instance" % locals()
+ break
+ else:
+ print_progress(progress)
+ time.sleep(poll_period)
+
+
+def _find_volume(cs, volume):
+ """Get a volume by ID."""
+ return utils.find_resource(cs.volumes, volume)
+
+
+def _find_volume_snapshot(cs, snapshot):
+ """Get a volume snapshot by ID."""
+ return utils.find_resource(cs.volume_snapshots, snapshot)
+
+
+def _print_volume(cs, volume):
+ utils.print_dict(volume._info)
+
+
+def _print_volume_snapshot(cs, snapshot):
+ utils.print_dict(snapshot._info)
+
+
+def _translate_volume_keys(collection):
+ convert = [('displayName', 'display_name'), ('volumeType', 'volume_type')]
+ for item in collection:
+ keys = item.__dict__.keys()
+ for from_key, to_key in convert:
+ if from_key in keys and to_key not in keys:
+ setattr(item, to_key, item._info[from_key])
+
+
+def _translate_volume_snapshot_keys(collection):
+ convert = [('displayName', 'display_name'), ('volumeId', 'volume_id')]
+ for item in collection:
+ keys = item.__dict__.keys()
+ for from_key, to_key in convert:
+ if from_key in keys and to_key not in keys:
+ setattr(item, to_key, item._info[from_key])
+
+
+@utils.service_type('volume')
+def do_list(cs, args):
+ """List all the volumes."""
+ volumes = cs.volumes.list()
+ _translate_volume_keys(volumes)
+
+ # Create a list of servers to which the volume is attached
+ for vol in volumes:
+ servers = [s.get('server_id') for s in vol.attachments]
+ setattr(vol, 'attached_to', ','.join(map(str, servers)))
+ utils.print_list(volumes, ['ID', 'Status', 'Display Name',
+ 'Size', 'Volume Type', 'Attached to'])
+
+
+@utils.arg('volume', metavar='<volume>', help='ID of the volume.')
+@utils.service_type('volume')
+def do_show(cs, args):
+ """Show details about a volume."""
+ volume = _find_volume(cs, args.volume)
+ _print_volume(cs, volume)
+
+
+@utils.arg('size',
+ metavar='<size>',
+ type=int,
+ help='Size of volume in GB')
+@utils.arg('--snapshot_id',
+ metavar='<snapshot_id>',
+ help='Optional snapshot id to create the volume from. (Default=None)',
+ default=None)
+@utils.arg('--display_name', metavar='<display_name>',
+ help='Optional volume name. (Default=None)',
+ default=None)
+@utils.arg('--display_description', metavar='<display_description>',
+ help='Optional volume description. (Default=None)',
+ default=None)
+@utils.arg('--volume_type',
+ metavar='<volume_type>',
+ help='Optional volume type. (Default=None)',
+ default=None)
+@utils.service_type('volume')
+def do_create(cs, args):
+ """Add a new volume."""
+ cs.volumes.create(args.size,
+ args.snapshot_id,
+ args.display_name,
+ args.display_description,
+ args.volume_type)
+
+
+@utils.arg('volume', metavar='<volume>', help='ID of the volume to delete.')
+@utils.service_type('volume')
+def do_delete(cs, args):
+ """Remove a volume."""
+ volume = _find_volume(cs, args.volume)
+ volume.delete()
+
+
+@utils.service_type('volume')
+def do_snapshot_list(cs, args):
+ """List all the snapshots."""
+ snapshots = cs.volume_snapshots.list()
+ _translate_volume_snapshot_keys(snapshots)
+ utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name',
+ 'Size'])
+
+
+@utils.arg('snapshot', metavar='<snapshot>', help='ID of the snapshot.')
+@utils.service_type('volume')
+def do_snapshot_show(cs, args):
+ """Show details about a snapshot."""
+ snapshot = _find_volume_snapshot(cs, args.snapshot)
+ _print_volume_snapshot(cs, snapshot)
+
+
+@utils.arg('volume_id',
+ metavar='<volume_id>',
+ help='ID of the volume to snapshot')
+@utils.arg('--force',
+ metavar='<True|False>',
+ help='Optional flag to indicate whether to snapshot a volume even if its '
+ 'attached to an instance. (Default=False)',
+ default=False)
+@utils.arg('--display_name', metavar='<display_name>',
+ help='Optional snapshot name. (Default=None)',
+ default=None)
+@utils.arg('--display_description', metavar='<display_description>',
+ help='Optional snapshot description. (Default=None)',
+ default=None)
+@utils.service_type('volume')
+def do_snapshot_create(cs, args):
+ """Add a new snapshot."""
+ cs.volume_snapshots.create(args.volume_id,
+ args.force,
+ args.display_name,
+ args.display_description)
+
+
+@utils.arg('snapshot_id',
+ metavar='<snapshot_id>',
+ help='ID of the snapshot to delete.')
+@utils.service_type('volume')
+def do_snapshot_delete(cs, args):
+ """Remove a snapshot."""
+ snapshot = _find_volume_snapshot(cs, args.snapshot_id)
+ snapshot.delete()
+
+
+def _print_volume_type_list(vtypes):
+ utils.print_list(vtypes, ['ID', 'Name'])
+
+
+@utils.service_type('volume')
+def do_type_list(cs, args):
+ """Print a list of available 'volume types'."""
+ vtypes = cs.volume_types.list()
+ _print_volume_type_list(vtypes)
+
+
+@utils.arg('name',
+ metavar='<name>',
+ help="Name of the new flavor")
+@utils.service_type('volume')
+def do_type_create(cs, args):
+ """Create a new volume type."""
+ vtype = cs.volume_types.create(args.name)
+ _print_volume_type_list([vtype])
+
+
+@utils.arg('id',
+ metavar='<id>',
+ help="Unique ID of the volume type to delete")
+@utils.service_type('volume')
+def do_type_delete(cs, args):
+ """Delete a specific flavor"""
+ cs.volume_types.delete(args.id)
+
+
+def do_endpoints(cs, args):
+ """Discover endpoints that get returned from the authenticate services"""
+ catalog = cs.client.service_catalog.catalog
+ for e in catalog['access']['serviceCatalog']:
+ utils.print_dict(e['endpoints'][0], e['name'])
+
+
+def do_credentials(cs, args):
+ """Show user credentials returned from auth"""
+ catalog = cs.client.service_catalog.catalog
+ utils.print_dict(catalog['access']['user'], "User Credentials")
+ utils.print_dict(catalog['access']['token'], "Token")
diff --git a/cinderclient/v1/volume_snapshots.py b/cinderclient/v1/volume_snapshots.py
new file mode 100644
index 0000000..fa6c4b4
--- /dev/null
+++ b/cinderclient/v1/volume_snapshots.py
@@ -0,0 +1,88 @@
+# Copyright 2011 Denali Systems, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Volume snapshot interface (1.1 extension).
+"""
+
+from cinderclient import base
+
+
+class Snapshot(base.Resource):
+ """
+ A Snapshot is a point-in-time snapshot of an openstack volume.
+ """
+ def __repr__(self):
+ return "<Snapshot: %s>" % self.id
+
+ def delete(self):
+ """
+ Delete this snapshot.
+ """
+ self.manager.delete(self)
+
+
+class SnapshotManager(base.ManagerWithFind):
+ """
+ Manage :class:`Snapshot` resources.
+ """
+ resource_class = Snapshot
+
+ def create(self, volume_id, force=False,
+ display_name=None, display_description=None):
+
+ """
+ Create a snapshot of the given volume.
+
+ :param volume_id: The ID of the volume to snapshot.
+ :param force: If force is True, create a snapshot even if the volume is
+ attached to an instance. Default is False.
+ :param display_name: Name of the snapshot
+ :param display_description: Description of the snapshot
+ :rtype: :class:`Snapshot`
+ """
+ body = {'snapshot': {'volume_id': volume_id,
+ 'force': force,
+ 'display_name': display_name,
+ 'display_description': display_description}}
+ return self._create('/snapshots', body, 'snapshot')
+
+ def get(self, snapshot_id):
+ """
+ Get a snapshot.
+
+ :param snapshot_id: The ID of the snapshot to get.
+ :rtype: :class:`Snapshot`
+ """
+ return self._get("/snapshots/%s" % snapshot_id, "snapshot")
+
+ def list(self, detailed=True):
+ """
+ Get a list of all snapshots.
+
+ :rtype: list of :class:`Snapshot`
+ """
+ if detailed is True:
+ return self._list("/snapshots/detail", "snapshots")
+ else:
+ return self._list("/snapshots", "snapshots")
+
+ def delete(self, snapshot):
+ """
+ Delete a snapshot.
+
+ :param snapshot: The :class:`Snapshot` to delete.
+ """
+ self._delete("/snapshots/%s" % base.getid(snapshot))
diff --git a/cinderclient/v1/volume_types.py b/cinderclient/v1/volume_types.py
new file mode 100644
index 0000000..e6d644d
--- /dev/null
+++ b/cinderclient/v1/volume_types.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2011 Rackspace US, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Volume Type interface.
+"""
+
+from cinderclient import base
+
+
+class VolumeType(base.Resource):
+ """
+ A Volume Type is the type of volume to be created
+ """
+ def __repr__(self):
+ return "<Volume Type: %s>" % self.name
+
+
+class VolumeTypeManager(base.ManagerWithFind):
+ """
+ Manage :class:`VolumeType` resources.
+ """
+ resource_class = VolumeType
+
+ def list(self):
+ """
+ Get a list of all volume types.
+
+ :rtype: list of :class:`VolumeType`.
+ """
+ return self._list("/types", "volume_types")
+
+ def get(self, volume_type):
+ """
+ Get a specific volume type.
+
+ :param volume_type: The ID of the :class:`VolumeType` to get.
+ :rtype: :class:`VolumeType`
+ """
+ return self._get("/types/%s" % base.getid(volume_type), "volume_type")
+
+ def delete(self, volume_type):
+ """
+ Delete a specific volume_type.
+
+ :param volume_type: The ID of the :class:`VolumeType` to get.
+ """
+ self._delete("/types/%s" % base.getid(volume_type))
+
+ def create(self, name):
+ """
+ Create a volume type.
+
+ :param name: Descriptive name of the volume type
+ :rtype: :class:`VolumeType`
+ """
+
+ body = {
+ "volume_type": {
+ "name": name,
+ }
+ }
+
+ return self._create("/types", body, "volume_type")
diff --git a/cinderclient/v1/volumes.py b/cinderclient/v1/volumes.py
new file mode 100644
index 0000000..d465724
--- /dev/null
+++ b/cinderclient/v1/volumes.py
@@ -0,0 +1,135 @@
+# Copyright 2011 Denali Systems, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Volume interface (1.1 extension).
+"""
+
+from cinderclient import base
+
+
+class Volume(base.Resource):
+ """
+ A volume is an extra block level storage to the OpenStack instances.
+ """
+ def __repr__(self):
+ return "<Volume: %s>" % self.id
+
+ def delete(self):
+ """
+ Delete this volume.
+ """
+ self.manager.delete(self)
+
+
+class VolumeManager(base.ManagerWithFind):
+ """
+ Manage :class:`Volume` resources.
+ """
+ resource_class = Volume
+
+ def create(self, size, snapshot_id=None,
+ display_name=None, display_description=None,
+ volume_type=None):
+ """
+ Create a volume.
+
+ :param size: Size of volume in GB
+ :param snapshot_id: ID of the snapshot
+ :param display_name: Name of the volume
+ :param display_description: Description of the volume
+ :param volume_type: Type of volume
+ :rtype: :class:`Volume`
+ """
+ body = {'volume': {'size': size,
+ 'snapshot_id': snapshot_id,
+ 'display_name': display_name,
+ 'display_description': display_description,
+ 'volume_type': volume_type}}
+ return self._create('/volumes', body, 'volume')
+
+ def get(self, volume_id):
+ """
+ Get a volume.
+
+ :param volume_id: The ID of the volume to delete.
+ :rtype: :class:`Volume`
+ """
+ return self._get("/volumes/%s" % volume_id, "volume")
+
+ def list(self, detailed=True):
+ """
+ Get a list of all volumes.
+
+ :rtype: list of :class:`Volume`
+ """
+ if detailed is True:
+ return self._list("/volumes/detail", "volumes")
+ else:
+ return self._list("/volumes", "volumes")
+
+ def delete(self, volume):
+ """
+ Delete a volume.
+
+ :param volume: The :class:`Volume` to delete.
+ """
+ self._delete("/volumes/%s" % base.getid(volume))
+
+ def create_server_volume(self, server_id, volume_id, device):
+ """
+ Attach a volume identified by the volume ID to the given server ID
+
+ :param server_id: The ID of the server
+ :param volume_id: The ID of the volume to attach.
+ :param device: The device name
+ :rtype: :class:`Volume`
+ """
+ body = {'volumeAttachment': {'volumeId': volume_id,
+ 'device': device}}
+ return self._create("/servers/%s/os-volume_attachments" % server_id,
+ body, "volumeAttachment")
+
+ def get_server_volume(self, server_id, attachment_id):
+ """
+ Get the volume identified by the attachment ID, that is attached to
+ the given server ID
+
+ :param server_id: The ID of the server
+ :param attachment_id: The ID of the attachment
+ :rtype: :class:`Volume`
+ """
+ return self._get("/servers/%s/os-volume_attachments/%s" % (server_id,
+ attachment_id,), "volumeAttachment")
+
+ def get_server_volumes(self, server_id):
+ """
+ Get a list of all the attached volumes for the given server ID
+
+ :param server_id: The ID of the server
+ :rtype: list of :class:`Volume`
+ """
+ return self._list("/servers/%s/os-volume_attachments" % server_id,
+ "volumeAttachments")
+
+ def delete_server_volume(self, server_id, attachment_id):
+ """
+ Detach a volume identified by the attachment ID from the given server
+
+ :param server_id: The ID of the server
+ :param attachment_id: The ID of the attachment
+ """
+ self._delete("/servers/%s/os-volume_attachments/%s" %
+ (server_id, attachment_id,))