diff options
| author | Todd Willey <todd@ansolabs.com> | 2010-09-28 23:38:32 -0400 |
|---|---|---|
| committer | Todd Willey <todd@ansolabs.com> | 2010-09-28 23:38:32 -0400 |
| commit | b784836118d5900330c76863decd504ec7bd6a77 (patch) | |
| tree | 8afdc39565488b872ed5360da340ea8284ad5ec1 /nova/api | |
| parent | c4df3d63c83c073664a9ed0abaefe2adbe4cd061 (diff) | |
| parent | 3ebea539c25f913a22f86e7dd500bf5d7771614f (diff) | |
| download | nova-b784836118d5900330c76863decd504ec7bd6a77.tar.gz nova-b784836118d5900330c76863decd504ec7bd6a77.tar.xz nova-b784836118d5900330c76863decd504ec7bd6a77.zip | |
Merge trunk and fix test.
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/ec2/cloud.py | 69 | ||||
| -rw-r--r-- | nova/api/rackspace/__init__.py | 18 | ||||
| -rw-r--r-- | nova/api/rackspace/_id_translator.py | 2 | ||||
| -rw-r--r-- | nova/api/rackspace/auth.py | 8 | ||||
| -rw-r--r-- | nova/api/rackspace/backup_schedules.py (renamed from nova/api/rackspace/base.py) | 26 | ||||
| -rw-r--r-- | nova/api/rackspace/flavors.py | 4 | ||||
| -rw-r--r-- | nova/api/rackspace/images.py | 9 | ||||
| -rw-r--r-- | nova/api/rackspace/servers.py | 203 | ||||
| -rw-r--r-- | nova/api/rackspace/sharedipgroups.py | 4 |
9 files changed, 248 insertions, 95 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 528380f0f..d3f54367b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -101,9 +101,9 @@ class CloudController(object): def _get_mpi_data(self, project_id): result = {} - for instance in db.instance_get_by_project(None, project_id): + for instance in db.instance_get_all_by_project(None, project_id): if instance['fixed_ip']: - line = '%s slots=%d' % (instance['fixed_ip']['str_id'], + line = '%s slots=%d' % (instance['fixed_ip']['address'], INSTANCE_TYPES[instance['instance_type']]['vcpus']) key = str(instance['key_name']) if key in result: @@ -143,7 +143,7 @@ class CloudController(object): }, 'hostname': hostname, 'instance-action': 'none', - 'instance-id': instance_ref['str_id'], + 'instance-id': instance_ref['ec2_id'], 'instance-type': instance_ref['instance_type'], 'local-hostname': hostname, 'local-ipv4': address, @@ -245,7 +245,7 @@ class CloudController(object): def get_console_output(self, context, instance_id, **kwargs): # instance_id is passed in as a list of instances - instance_ref = db.instance_get_by_str(context, instance_id[0]) + instance_ref = db.instance_get_by_ec2_id(context, instance_id[0]) return rpc.call('%s.%s' % (FLAGS.compute_topic, instance_ref['host']), {"method": "get_console_output", @@ -256,7 +256,7 @@ class CloudController(object): if context.user.is_admin(): volumes = db.volume_get_all(context) else: - volumes = db.volume_get_by_project(context, context.project.id) + volumes = db.volume_get_all_by_project(context, context.project.id) volumes = [self._format_volume(context, v) for v in volumes] @@ -264,7 +264,7 @@ class CloudController(object): def _format_volume(self, context, volume): v = {} - v['volumeId'] = volume['str_id'] + v['volumeId'] = volume['ec2_id'] v['status'] = volume['status'] v['size'] = volume['size'] v['availabilityZone'] = volume['availability_zone'] @@ -282,7 +282,7 @@ class CloudController(object): 'device': volume['mountpoint'], 'instanceId': volume['instance_id'], 'status': 'attached', - 'volume_id': volume['str_id']}] + 'volume_id': volume['ec2_id']}] else: v['attachmentSet'] = [{}] @@ -319,13 +319,13 @@ class CloudController(object): def attach_volume(self, context, volume_id, instance_id, device, **kwargs): - volume_ref = db.volume_get_by_str(context, volume_id) + volume_ref = db.volume_get_by_ec2_id(context, volume_id) # TODO(vish): abstract status checking? if volume_ref['status'] != "available": raise exception.ApiError("Volume status must be available") if volume_ref['attach_status'] == "attached": raise exception.ApiError("Volume is already attached") - instance_ref = db.instance_get_by_str(context, instance_id) + instance_ref = db.instance_get_by_ec2_id(context, instance_id) host = instance_ref['host'] rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "attach_volume", @@ -341,7 +341,7 @@ class CloudController(object): 'volumeId': volume_ref['id']} def detach_volume(self, context, volume_id, **kwargs): - volume_ref = db.volume_get_by_str(context, volume_id) + volume_ref = db.volume_get_by_ec2_id(context, volume_id) instance_ref = db.volume_get_instance(context, volume_ref['id']) if not instance_ref: raise exception.ApiError("Volume isn't attached to anything!") @@ -361,7 +361,7 @@ class CloudController(object): db.volume_detached(context) return {'attachTime': volume_ref['attach_time'], 'device': volume_ref['mountpoint'], - 'instanceId': instance_ref['str_id'], + 'instanceId': instance_ref['ec2_id'], 'requestId': context.request_id, 'status': volume_ref['attach_status'], 'volumeId': volume_ref['id']} @@ -397,20 +397,20 @@ class CloudController(object): def _format_instances(self, context, reservation_id=None): reservations = {} if reservation_id: - instances = db.instance_get_by_reservation(context, - reservation_id) + instances = db.instance_get_all_by_reservation(context, + reservation_id) else: if context.user.is_admin(): instances = db.instance_get_all(context) else: - instances = db.instance_get_by_project(context, - context.project.id) + instances = db.instance_get_all_by_project(context, + context.project.id) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: continue i = {} - i['instanceId'] = instance['str_id'] + i['instanceId'] = instance['ec2_id'] i['imageId'] = instance['image_id'] i['instanceState'] = { 'code': instance['state'], @@ -419,10 +419,10 @@ class CloudController(object): fixed_addr = None floating_addr = None if instance['fixed_ip']: - fixed_addr = instance['fixed_ip']['str_id'] + fixed_addr = instance['fixed_ip']['address'] if instance['fixed_ip']['floating_ips']: fixed = instance['fixed_ip'] - floating_addr = fixed['floating_ips'][0]['str_id'] + floating_addr = fixed['floating_ips'][0]['address'] i['privateDnsName'] = fixed_addr i['publicDnsName'] = floating_addr i['dnsName'] = i['publicDnsName'] or i['privateDnsName'] @@ -456,14 +456,14 @@ class CloudController(object): if context.user.is_admin(): iterator = db.floating_ip_get_all(context) else: - iterator = db.floating_ip_get_by_project(context, - context.project.id) + iterator = db.floating_ip_get_all_by_project(context, + context.project.id) for floating_ip_ref in iterator: - address = floating_ip_ref['str_id'] + address = floating_ip_ref['address'] instance_id = None if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): - instance_id = floating_ip_ref['fixed_ip']['instance']['str_id'] + instance_id = floating_ip_ref['fixed_ip']['instance']['ec2_id'] address_rv = {'public_ip': address, 'instance_id': instance_id} if context.user.is_admin(): @@ -494,19 +494,20 @@ class CloudController(object): rpc.cast(network_topic, {"method": "deallocate_floating_ip", "args": {"context": None, - "floating_address": floating_ip_ref['str_id']}}) + "floating_address": floating_ip_ref['address']}}) return {'releaseResponse': ["Address released."]} def associate_address(self, context, instance_id, public_ip, **kwargs): - instance_ref = db.instance_get_by_str(context, instance_id) - fixed_ip_ref = db.fixed_ip_get_by_instance(context, instance_ref['id']) + instance_ref = db.instance_get_by_ec2_id(context, instance_id) + fixed_address = db.instance_get_fixed_address(context, + instance_ref['id']) floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) network_topic = self._get_network_topic(context) rpc.cast(network_topic, {"method": "associate_floating_ip", "args": {"context": None, - "floating_address": floating_ip_ref['str_id'], - "fixed_address": fixed_ip_ref['str_id']}}) + "floating_address": floating_ip_ref['address'], + "fixed_address": fixed_address}}) return {'associateResponse': ["Address associated."]} def disassociate_address(self, context, public_ip, **kwargs): @@ -515,7 +516,7 @@ class CloudController(object): rpc.cast(network_topic, {"method": "disassociate_floating_ip", "args": {"context": None, - "floating_address": floating_ip_ref['str_id']}}) + "floating_address": floating_ip_ref['address']}}) return {'disassociateResponse': ["Address disassociated."]} def _get_network_topic(self, context): @@ -608,7 +609,7 @@ class CloudController(object): inst = {} inst['mac_address'] = utils.generate_mac() inst['launch_index'] = num - inst['hostname'] = instance_ref['str_id'] + inst['hostname'] = instance_ref['ec2_id'] db.instance_update(context, inst_id, inst) address = self.network_manager.allocate_fixed_ip(context, inst_id, @@ -637,7 +638,7 @@ class CloudController(object): for id_str in instance_id: logging.debug("Going to try and terminate %s" % id_str) try: - instance_ref = db.instance_get_by_str(context, id_str) + instance_ref = db.instance_get_by_ec2_id(context, id_str) except exception.NotFound: logging.warning("Instance %s was not found during terminate" % id_str) @@ -659,7 +660,7 @@ class CloudController(object): rpc.cast(network_topic, {"method": "disassociate_floating_ip", "args": {"context": None, - "address": address}}) + "floating_address": address}}) address = db.instance_get_fixed_address(context, instance_ref['id']) @@ -683,7 +684,7 @@ class CloudController(object): def reboot_instances(self, context, instance_id, **kwargs): """instance_id is a list of instance ids""" for id_str in instance_id: - instance_ref = db.instance_get_by_str(context, id_str) + instance_ref = db.instance_get_by_ec2_id(context, id_str) host = instance_ref['host'] rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "reboot_instance", @@ -699,13 +700,13 @@ class CloudController(object): changes[field] = kwargs[field] if changes: db_context = {} - inst = db.instance_get_by_str(db_context, instance_id) + inst = db.instance_get_by_ec2_id(db_context, instance_id) db.instance_update(db_context, inst['id'], kwargs) return True def delete_volume(self, context, volume_id, **kwargs): # TODO: return error if not authorized - volume_ref = db.volume_get_by_str(context, volume_id) + volume_ref = db.volume_get_by_ec2_id(context, volume_id) if volume_ref['status'] != "available": raise exception.ApiError("Volume status must be available") now = datetime.datetime.utcnow() diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py index c24d08585..98802663f 100644 --- a/nova/api/rackspace/__init__.py +++ b/nova/api/rackspace/__init__.py @@ -31,6 +31,7 @@ import webob from nova import flags from nova import utils from nova import wsgi +from nova.api.rackspace import backup_schedules from nova.api.rackspace import flavors from nova.api.rackspace import images from nova.api.rackspace import ratelimiting @@ -67,8 +68,10 @@ class AuthMiddleware(wsgi.Middleware): if not user: return webob.exc.HTTPUnauthorized() - context = {'user': user} - req.environ['nova.context'] = context + + if not req.environ.has_key('nova.context'): + req.environ['nova.context'] = {} + req.environ['nova.context']['user'] = user return self.application class RateLimitingMiddleware(wsgi.Middleware): @@ -145,11 +148,20 @@ class APIRouter(wsgi.Router): def __init__(self): mapper = routes.Mapper() - mapper.resource("server", "servers", controller=servers.Controller()) + mapper.resource("server", "servers", controller=servers.Controller(), + collection={ 'detail': 'GET'}, + member={'action':'POST'}) + + mapper.resource("backup_schedule", "backup_schedules", + controller=backup_schedules.Controller(), + parent_resource=dict(member_name='server', + collection_name = 'servers')) + mapper.resource("image", "images", controller=images.Controller(), collection={'detail': 'GET'}) mapper.resource("flavor", "flavors", controller=flavors.Controller(), collection={'detail': 'GET'}) mapper.resource("sharedipgroup", "sharedipgroups", controller=sharedipgroups.Controller()) + super(APIRouter, self).__init__(mapper) diff --git a/nova/api/rackspace/_id_translator.py b/nova/api/rackspace/_id_translator.py index aec5fb6a5..333aa8434 100644 --- a/nova/api/rackspace/_id_translator.py +++ b/nova/api/rackspace/_id_translator.py @@ -37,6 +37,6 @@ class RackspaceAPIIdTranslator(object): # every int id be used.) return int(self._store.hget(self._fwd_key, str(opaque_id))) - def from_rs_id(self, strategy_name, rs_id): + def from_rs_id(self, rs_id): """Convert a Rackspace id to a strategy-specific one.""" return self._store.hget(self._rev_key, rs_id) diff --git a/nova/api/rackspace/auth.py b/nova/api/rackspace/auth.py index ce5a967eb..8bfb0753e 100644 --- a/nova/api/rackspace/auth.py +++ b/nova/api/rackspace/auth.py @@ -1,13 +1,15 @@ import datetime +import hashlib import json import time + import webob.exc import webob.dec -import hashlib -from nova import flags + from nova import auth -from nova import manager from nova import db +from nova import flags +from nova import manager from nova import utils FLAGS = flags.FLAGS diff --git a/nova/api/rackspace/base.py b/nova/api/rackspace/backup_schedules.py index dd2c6543c..46da778ee 100644 --- a/nova/api/rackspace/base.py +++ b/nova/api/rackspace/backup_schedules.py @@ -15,16 +15,24 @@ # License for the specific language governing permissions and limitations # under the License. -from nova import wsgi +import time +from webob import exc +from nova import wsgi +from nova.api.rackspace import _id_translator +import nova.image.service class Controller(wsgi.Controller): - """TODO(eday): Base controller for all rackspace controllers. What is this - for? Is this just Rackspace specific? """ + def __init__(self): + pass + + def index(self, req, server_id): + return exc.HTTPNotFound() + + def create(self, req, server_id): + """ No actual update method required, since the existing API allows + both create and update through a POST """ + return exc.HTTPNotFound() - @classmethod - def render(cls, instance): - if isinstance(instance, list): - return {cls.entity_name: cls.render(instance)} - else: - return {"TODO": "TODO"} + def delete(self, req, server_id): + return exc.HTTPNotFound() diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py index 60b35c939..3bcf170e5 100644 --- a/nova/api/rackspace/flavors.py +++ b/nova/api/rackspace/flavors.py @@ -15,11 +15,11 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.rackspace import base from nova.compute import instance_types +from nova import wsgi from webob import exc -class Controller(base.Controller): +class Controller(wsgi.Controller): """Flavor controller for the Rackspace API.""" _serialization_metadata = { diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 2f3e928b9..11b058dec 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -15,12 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. -import nova.image.service -from nova.api.rackspace import base -from nova.api.rackspace import _id_translator from webob import exc -class Controller(base.Controller): +from nova import wsgi +from nova.api.rackspace import _id_translator +import nova.image.service + +class Controller(wsgi.Controller): _serialization_metadata = { 'application/xml': { diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 1815f7523..4ab04bde7 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -14,67 +14,194 @@ # 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 time -from nova import db +from webob import exc + from nova import flags from nova import rpc from nova import utils -from nova.api.rackspace import base +from nova import wsgi +from nova.api.rackspace import _id_translator +from nova.compute import power_state +import nova.image.service FLAGS = flags.FLAGS -class Controller(base.Controller): - entity_name = 'servers' - def index(self, **kwargs): - instances = [] - for inst in db.instance_get_all(None): - instances.append(instance_details(inst)) - def show(self, **kwargs): - instance_id = kwargs['id'] - return db.instance_get(None, instance_id) +def translator_instance(): + """ Helper method for initializing the image id translator """ + service = nova.image.service.ImageService.load() + return _id_translator.RackspaceAPIIdTranslator( + "image", service.__class__.__name__) - def delete(self, **kwargs): - instance_id = kwargs['id'] - instance = db.instance_get(None, instance_id) - if not instance: - raise ServerNotFound("The requested server was not found") - instance.destroy() - return True +def _filter_params(inst_dict): + """ Extracts all updatable parameters for a server update request """ + keys = ['name', 'adminPass'] + new_attrs = {} + for k in keys: + if inst_dict.has_key(k): + new_attrs[k] = inst_dict[k] + return new_attrs + +def _entity_list(entities): + """ Coerces a list of servers into proper dictionary format """ + return dict(servers=entities) + +def _entity_detail(inst): + """ Maps everything to Rackspace-like attributes for return""" + power_mapping = { + power_state.NOSTATE: 'build', + power_state.RUNNING: 'active', + power_state.BLOCKED: 'active', + power_state.PAUSED: 'suspended', + power_state.SHUTDOWN: 'active', + power_state.SHUTOFF: 'active', + power_state.CRASHED: 'error' + } + inst_dict = {} + + mapped_keys = dict(status='state', imageId='image_id', + flavorId='instance_type', name='server_name', id='id') + + for k, v in mapped_keys.iteritems(): + inst_dict[k] = inst[v] + + inst_dict['status'] = power_mapping[inst_dict['status']] + inst_dict['addresses'] = dict(public=[], private=[]) + inst_dict['metadata'] = {} + inst_dict['hostId'] = '' + + return dict(server=inst_dict) + +def _entity_inst(inst): + """ Filters all model attributes save for id and name """ + return dict(server=dict(id=inst['id'], name=inst['server_name'])) + +class Controller(wsgi.Controller): + """ The Server API controller for the Openstack API """ + + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "server": [ "id", "imageId", "name", "flavorId", "hostId", + "status", "progress", "progress" ] + } + } + } + + def __init__(self, db_driver=None): + if not db_driver: + db_driver = FLAGS.db_driver + self.db_driver = utils.import_object(db_driver) + super(Controller, self).__init__() + + def index(self, req): + """ Returns a list of server names and ids for a given user """ + user_id = req.environ['nova.context']['user']['id'] + instance_list = self.db_driver.instance_get_all_by_user(None, user_id) + res = [_entity_inst(inst)['server'] for inst in instance_list] + return _entity_list(res) + + def detail(self, req): + """ Returns a list of server details for a given user """ + user_id = req.environ['nova.context']['user']['id'] + res = [_entity_detail(inst)['server'] for inst in + self.db_driver.instance_get_all_by_user(None, user_id)] + return _entity_list(res) + + def show(self, req, id): + """ Returns server details by server id """ + user_id = req.environ['nova.context']['user']['id'] + inst = self.db_driver.instance_get(None, id) + if inst: + if inst.user_id == user_id: + return _entity_detail(inst) + raise exc.HTTPNotFound() + + def delete(self, req, id): + """ Destroys a server """ + user_id = req.environ['nova.context']['user']['id'] + instance = self.db_driver.instance_get(None, id) + if instance and instance['user_id'] == user_id: + self.db_driver.instance_destroy(None, id) + return exc.HTTPAccepted() + return exc.HTTPNotFound() + + def create(self, req): + """ Creates a new server for a given user """ + if not req.environ.has_key('inst_dict'): + return exc.HTTPUnprocessableEntity() + + inst = self._build_server_instance(req) - def create(self, **kwargs): - inst = self.build_server_instance(kwargs['server']) rpc.cast( FLAGS.compute_topic, { "method": "run_instance", "args": {"instance_id": inst['id']}}) + return _entity_inst(inst) - def update(self, **kwargs): - instance_id = kwargs['id'] - instance = db.instance_get(None, instance_id) + def update(self, req, id): + """ Updates the server name or password """ + if not req.environ.has_key('inst_dict'): + return exc.HTTPUnprocessableEntity() + + instance = self.db_driver.instance_get(None, id) if not instance: - raise ServerNotFound("The requested server was not found") - instance.update(kwargs['server']) - instance.save() + return exc.HTTPNotFound() + + attrs = req.environ['nova.context'].get('model_attributes', None) + if attrs: + self.db_driver.instance_update(None, id, _filter_params(attrs)) + return exc.HTTPNoContent() + + def action(self, req, id): + """ multi-purpose method used to reboot, rebuild, and + resize a server """ + if not req.environ.has_key('inst_dict'): + return exc.HTTPUnprocessableEntity() - def build_server_instance(self, env): + def _build_server_instance(self, req): """Build instance data structure and save it to the data store.""" - reservation = utils.generate_uid('r') ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) inst = {} - inst['name'] = env['server']['name'] - inst['image_id'] = env['server']['imageId'] + + env = req.environ['inst_dict'] + + image_id = env['server']['imageId'] + opaque_id = translator_instance().from_rs_id(image_id) + + inst['name'] = env['server']['server_name'] + inst['image_id'] = opaque_id inst['instance_type'] = env['server']['flavorId'] - inst['user_id'] = env['user']['id'] - inst['project_id'] = env['project']['id'] - inst['reservation_id'] = reservation + + user_id = req.environ['nova.context']['user']['id'] + inst['user_id'] = user_id + inst['launch_time'] = ltime inst['mac_address'] = utils.generate_mac() - inst_id = db.instance_create(None, inst)['id'] - address = self.network_manager.allocate_fixed_ip(None, inst_id) - # key_data, key_name, ami_launch_index - # TODO(todd): key data or root password - inst.save() + + inst['project_id'] = env['project']['id'] + inst['reservation_id'] = reservation + reservation = utils.generate_uid('r') + + address = self.network.allocate_ip( + inst['user_id'], + inst['project_id'], + mac=inst['mac_address']) + + inst['private_dns_name'] = str(address) + inst['bridge_name'] = network.BridgedNetwork.get_network_for_project( + inst['user_id'], + inst['project_id'], + 'default')['bridge_name'] + + ref = self.db_driver.instance_create(None, inst) + inst['id'] = ref.id + return inst + + diff --git a/nova/api/rackspace/sharedipgroups.py b/nova/api/rackspace/sharedipgroups.py index 986f11434..4d2d0ede1 100644 --- a/nova/api/rackspace/sharedipgroups.py +++ b/nova/api/rackspace/sharedipgroups.py @@ -15,4 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -class Controller(object): pass +from nova import wsgi + +class Controller(wsgi.Controller): pass |
