summaryrefslogtreecommitdiff
path: root/novaclient/v1_1/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'novaclient/v1_1/base.py')
-rw-r--r--novaclient/v1_1/base.py231
1 files changed, 231 insertions, 0 deletions
diff --git a/novaclient/v1_1/base.py b/novaclient/v1_1/base.py
new file mode 100644
index 00000000..9762f73e
--- /dev/null
+++ b/novaclient/v1_1/base.py
@@ -0,0 +1,231 @@
+# 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.
+
+"""
+Base utilities to build API operation managers and objects on top of.
+"""
+
+from novaclient import exceptions
+
+# Python 2.4 compat
+try:
+ all
+except NameError:
+ def all(iterable):
+ return True not in (not x for x in iterable)
+
+
+def getid(obj):
+ """
+ Abstracts the common pattern of allowing both an object or an object's ID
+ (UUID) as a parameter when dealing with relationships.
+ """
+
+ # Try to return the object's UUID first, if we have a UUID.
+ try:
+ if obj.uuid:
+ return obj.uuid
+ except AttributeError:
+ pass
+ try:
+ return obj.id
+ except AttributeError:
+ return obj
+
+
+class Manager(object):
+ """
+ Managers interact with a particular type of API (servers, flavors, images,
+ etc.) and provide CRUD operations for them.
+ """
+ resource_class = None
+
+ def __init__(self, api):
+ self.api = api
+
+ def _list(self, url, response_key, obj_class=None, body=None):
+ resp = None
+ if body:
+ resp, body = self.api.client.post(url, body=body)
+ else:
+ resp, body = self.api.client.get(url)
+
+ if obj_class is None:
+ obj_class = self.resource_class
+ return [obj_class(self, res)
+ for res in body[response_key] if res]
+
+ def _get(self, url, response_key):
+ resp, body = self.api.client.get(url)
+ return self.resource_class(self, body[response_key])
+
+ def _create(self, url, body, response_key, return_raw=False):
+ resp, body = self.api.client.post(url, body=body)
+ if return_raw:
+ return body[response_key]
+ return self.resource_class(self, body[response_key])
+
+ def _delete(self, url):
+ resp, body = self.api.client.delete(url)
+
+ def _update(self, url, body):
+ resp, body = self.api.client.put(url, body=body)
+
+
+class ManagerWithFind(Manager):
+ """
+ Like a `Manager`, but with additional `find()`/`findall()` methods.
+ """
+ def 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.
+ """
+ rl = self.findall(**kwargs)
+ try:
+ return rl[0]
+ except IndexError:
+ raise exceptions.NotFound(404, "No %s matching %s." %
+ (self.resource_class.__name__, kwargs))
+
+ def 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.
+ """
+ found = []
+ searches = kwargs.items()
+
+ for obj in self.list():
+ try:
+ if all(getattr(obj, attr) == value
+ for (attr, value) in searches):
+ found.append(obj)
+ except AttributeError:
+ continue
+
+ return found
+
+
+class BootingManagerWithFind(ManagerWithFind):
+ """Like a `ManagerWithFind`, but has the ability to boot servers."""
+ def _boot(self, resource_url, response_key, name, image, flavor,
+ meta=None, files=None, zone_blob=None,
+ reservation_id=None, return_raw=False, min_count=None,
+ max_count=None):
+ """
+ Create (boot) a new server.
+
+ :param name: Something to name the server.
+ :param image: The :class:`Image` to boot with.
+ :param flavor: The :class:`Flavor` to boot onto.
+ :param meta: A dict of arbitrary key/value metadata to store for this
+ server. A maximum of five entries is allowed, and both
+ keys and values must be 255 characters or less.
+ :param files: A dict of files to overrwrite on the server upon boot.
+ Keys are file names (i.e. ``/etc/passwd``) and values
+ are the file contents (either as a string or as a
+ file-like object). A maximum of five entries is allowed,
+ and each file must be 10k or less.
+ :param zone_blob: a single (encrypted) string which is used internally
+ by Nova for routing between Zones. Users cannot populate
+ this field.
+ :param reservation_id: a UUID for the set of servers being requested.
+ :param return_raw: If True, don't try to coearse the result into
+ a Resource object.
+ """
+ body = {"server": {
+ "name": name,
+ "imageRef": getid(image),
+ "flavorRef": getid(flavor),
+ }}
+ if meta:
+ body["server"]["metadata"] = meta
+ if reservation_id:
+ body["server"]["reservation_id"] = reservation_id
+ if zone_blob:
+ body["server"]["zone_blob"] = zone_blob
+
+ if not min_count:
+ min_count = 1
+ if not max_count:
+ max_count = min_count
+ body["server"]["min_count"] = min_count
+ body["server"]["max_count"] = max_count
+
+ # Files are a slight bit tricky. They're passed in a "personality"
+ # list to the POST. Each item is a dict giving a file name and the
+ # base64-encoded contents of the file. We want to allow passing
+ # either an open file *or* some contents as files here.
+ if files:
+ personality = body['server']['personality'] = []
+ for filepath, file_or_string in files.items():
+ if hasattr(file_or_string, 'read'):
+ data = file_or_string.read()
+ else:
+ data = file_or_string
+ personality.append({
+ 'path': filepath,
+ 'contents': data.encode('base64'),
+ })
+
+ return self._create(resource_url, body, response_key,
+ return_raw=return_raw)
+
+
+class Resource(object):
+ """
+ A resource represents a particular instance of an object (server, flavor,
+ etc). This is pretty much just a bag for attributes.
+ """
+ def __init__(self, manager, info):
+ self.manager = manager
+ self._info = info
+ self._add_details(info)
+
+ def _add_details(self, info):
+ for (k, v) in info.iteritems():
+ setattr(self, k, v)
+
+ def __getattr__(self, k):
+ self.get()
+ if k not in self.__dict__:
+ raise AttributeError(k)
+ else:
+ return self.__dict__[k]
+
+ def __repr__(self):
+ reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
+ k != 'manager')
+ info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
+ return "<%s %s>" % (self.__class__.__name__, info)
+
+ def get(self):
+ new = self.manager.get(self.id)
+ if new:
+ self._add_details(new._info)
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return False
+ if hasattr(self, 'id') and hasattr(other, 'id'):
+ return self.id == other.id
+ return self._info == other._info