From 77d7e022fd5f2c8709a6784cc83429494d126a3b Mon Sep 17 00:00:00 2001 From: Eric Day Date: Thu, 9 Dec 2010 13:59:50 -0800 Subject: Converted the instance table to use a uuid instead of a auto_increment ID and a random internal_id. I had to use a String(32) column with hex and not a String(16) with bytes because SQLAlchemy doesn't like non-unicode strings going in for String types. We could try another type, but I didn't want a primary_key on blob types. --- nova/api/ec2/cloud.py | 70 ++++++++++++++++++++----------------------- nova/api/openstack/servers.py | 4 +-- 2 files changed, 35 insertions(+), 39 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 05f8c3d0b..a831b2a62 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -72,18 +72,14 @@ def _gen_key(context, user_id, key_name): return {'private_key': private_key, 'fingerprint': fingerprint} -def ec2_id_to_internal_id(ec2_id): - """Convert an ec2 ID (i-[base 36 number]) to an internal id (int)""" - return int(ec2_id[2:], 36) +def ec2_id_to_id(ec2_id): + """Convert an ec2 ID and an instance ID.""" + return ec2_id[2:] -def internal_id_to_ec2_id(internal_id): - """Convert an internal ID (int) to an ec2 ID (i-[base 36 number])""" - digits = [] - while internal_id != 0: - internal_id, remainder = divmod(internal_id, 36) - digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) - return "i-%s" % ''.join(reversed(digits)) +def id_to_ec2_id(instance_id): + """Convert an instance ID to an ec2 ID.""" + return "i-%s" % instance_id class CloudController(object): @@ -153,7 +149,7 @@ class CloudController(object): hostname = instance_ref['hostname'] floating_ip = db.instance_get_floating_address(ctxt, instance_ref['id']) - ec2_id = internal_id_to_ec2_id(instance_ref['internal_id']) + ec2_id = id_to_ec2_id(instance_ref['id']) data = { 'user-data': base64.b64decode(instance_ref['user_data']), 'meta-data': { @@ -437,8 +433,8 @@ class CloudController(object): def get_console_output(self, context, instance_id, **kwargs): # instance_id is passed in as a list of instances ec2_id = instance_id[0] - internal_id = ec2_id_to_internal_id(ec2_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_id = ec2_id_to_id(ec2_id) + instance_ref = self.compute_api.get_instance(context, instance_id) output = rpc.call(context, '%s.%s' % (FLAGS.compute_topic, instance_ref['host']), @@ -464,8 +460,8 @@ class CloudController(object): instance_ec2_id = None instance_data = None if volume.get('instance', None): - internal_id = volume['instance']['internal_id'] - instance_ec2_id = internal_id_to_ec2_id(internal_id) + instance_id = volume['instance']['id'] + instance_ec2_id = id_to_ec2_id(instance_id) instance_data = '%s[%s]' % (instance_ec2_id, volume['instance']['host']) v = {} @@ -534,8 +530,8 @@ class CloudController(object): raise exception.ApiError("Volume status must be available") if volume_ref['attach_status'] == "attached": raise exception.ApiError("Volume is already attached") - internal_id = ec2_id_to_internal_id(instance_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_id = ec2_id_to_id(instance_id) + instance_ref = self.compute_api.get_instance(context, instance_id) host = instance_ref['host'] rpc.cast(context, db.queue_get_for(context, FLAGS.compute_topic, host), @@ -570,11 +566,11 @@ class CloudController(object): # If the instance doesn't exist anymore, # then we need to call detach blind db.volume_detached(context) - internal_id = instance_ref['internal_id'] - ec2_id = internal_id_to_ec2_id(internal_id) + instance_id = instance_ref['id'] + ec2_id = id_to_ec2_id(instance_id) return {'attachTime': volume_ref['attach_time'], 'device': volume_ref['mountpoint'], - 'instanceId': internal_id, + 'instanceId': instance_id, 'requestId': context.request_id, 'status': volume_ref['attach_status'], 'volumeId': volume_ref['id']} @@ -619,8 +615,8 @@ class CloudController(object): if instance['image_id'] == FLAGS.vpn_image_id: continue i = {} - internal_id = instance['internal_id'] - ec2_id = internal_id_to_ec2_id(internal_id) + instance_id = instance['id'] + ec2_id = id_to_ec2_id(instance_id) i['instanceId'] = ec2_id i['imageId'] = instance['image_id'] i['instanceState'] = { @@ -673,8 +669,8 @@ class CloudController(object): ec2_id = None if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): - internal_id = floating_ip_ref['fixed_ip']['instance']['ec2_id'] - ec2_id = internal_id_to_ec2_id(internal_id) + instance_id = floating_ip_ref['fixed_ip']['instance']['ec2_id'] + ec2_id = id_to_ec2_id(instance_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} if context.user.is_admin(): @@ -709,8 +705,8 @@ class CloudController(object): return {'releaseResponse': ["Address released."]} def associate_address(self, context, instance_id, public_ip, **kwargs): - internal_id = ec2_id_to_internal_id(instance_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_id = ec2_id_to_id(instance_id) + instance_ref = self.compute_api.get_instance(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) @@ -755,7 +751,7 @@ class CloudController(object): description=kwargs.get('display_description'), key_name=kwargs.get('key_name'), security_group=kwargs.get('security_group'), - generate_hostname=internal_id_to_ec2_id) + generate_hostname=id_to_ec2_id) return self._format_run_instances(context, instances[0]['reservation_id']) @@ -764,27 +760,27 @@ class CloudController(object): instance_id is a kwarg so its name cannot be modified.""" logging.debug("Going to start terminating instances") for ec2_id in instance_id: - internal_id = ec2_id_to_internal_id(ec2_id) - self.compute_api.delete_instance(context, internal_id) + instance_id = ec2_id_to_id(ec2_id) + self.compute_api.delete_instance(context, instance_id) return True def reboot_instances(self, context, instance_id, **kwargs): """instance_id is a list of instance ids""" for ec2_id in instance_id: - internal_id = ec2_id_to_internal_id(ec2_id) - self.compute_api.reboot(context, internal_id) + instance_id = ec2_id_to_id(ec2_id) + self.compute_api.reboot(context, instance_id) return True def rescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" - internal_id = ec2_id_to_internal_id(instance_id) - self.compute_api.rescue(context, internal_id) + instance_id = ec2_id_to_id(instance_id) + self.compute_api.rescue(context, instance_id) return True def unrescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" - internal_id = ec2_id_to_internal_id(instance_id) - self.compute_api.unrescue(context, internal_id) + instance_id = ec2_id_to_id(instance_id) + self.compute_api.unrescue(context, instance_id) return True def update_instance(self, context, ec2_id, **kwargs): @@ -794,8 +790,8 @@ class CloudController(object): if field in kwargs: changes[field] = kwargs[field] if changes: - internal_id = ec2_id_to_internal_id(ec2_id) - inst = self.compute_api.get_instance(context, internal_id) + instance_id = ec2_id_to_id(ec2_id) + inst = self.compute_api.get_instance(context, instance_id) db.instance_update(context, inst['id'], kwargs) return True diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 6f2f6fed9..f8f40b764 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -46,7 +46,7 @@ def _entity_detail(inst): inst_dict = {} mapped_keys = dict(status='state', imageId='image_id', - flavorId='instance_type', name='display_name', id='internal_id') + flavorId='instance_type', name='display_name', id='id') for k, v in mapped_keys.iteritems(): inst_dict[k] = inst[v] @@ -61,7 +61,7 @@ def _entity_detail(inst): def _entity_inst(inst): """ Filters all model attributes save for id and name """ - return dict(server=dict(id=inst['internal_id'], name=inst['display_name'])) + return dict(server=dict(id=inst['id'], name=inst['display_name'])) class Controller(wsgi.Controller): -- cgit From be9a3cd7e17edac4032c8ae554f75d725b0ad54a Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Dec 2010 16:42:35 +0100 Subject: Move security group refresh logic into ComputeAPI. Add a trigger_security_group_members_refresh to ComputeAPI which finds the hosts that have instances that have security groups that reference a security group in which a new instance has just been placed, and sends a refresh_security_group_members to each of them. --- nova/api/ec2/cloud.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 05f8c3d0b..2694b8b00 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -130,15 +130,6 @@ class CloudController(object): result[key] = [line] return result - def _trigger_refresh_security_group(self, context, security_group): - nodes = set([instance['host'] for instance in security_group.instances - if instance['host'] is not None]) - for node in nodes: - rpc.cast(context, - '%s.%s' % (FLAGS.compute_topic, node), - {"method": "refresh_security_group", - "args": {"security_group_id": security_group.id}}) - def get_metadata(self, address): ctxt = context.get_admin_context() instance_ref = db.fixed_ip_get_instance(ctxt, address) @@ -369,7 +360,8 @@ class CloudController(object): match = False if match: db.security_group_rule_destroy(context, rule['id']) - self._trigger_refresh_security_group(context, security_group) + self.compute_api.trigger_security_group_rules_refresh(context, + security_group['id']) return True raise exception.ApiError("No rule for the specified parameters.") @@ -392,7 +384,8 @@ class CloudController(object): security_group_rule = db.security_group_rule_create(context, values) - self._trigger_refresh_security_group(context, security_group) + self.compute_api.trigger_security_group_rules_refresh(context, + security_group['id']) return True -- cgit From b420a3daa5f1b827f49e5d6557aaa0f8d396b81b Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 15 Dec 2010 14:04:06 +0100 Subject: Lots of PEP-8 work. --- nova/api/ec2/cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 74be6d05b..018139634 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -361,7 +361,7 @@ class CloudController(object): if match: db.security_group_rule_destroy(context, rule['id']) self.compute_api.trigger_security_group_rules_refresh(context, - security_group['id']) + security_group['id']) return True raise exception.ApiError("No rule for the specified parameters.") @@ -385,7 +385,7 @@ class CloudController(object): security_group_rule = db.security_group_rule_create(context, values) self.compute_api.trigger_security_group_rules_refresh(context, - security_group['id']) + security_group['id']) return True -- cgit From c364724a0dc7a658058fcb167af66ee7eb5bcd2a Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 21 Dec 2010 01:41:28 -0500 Subject: Use paste.deploy for running the api server. --- nova/api/__init__.py | 1 - nova/api/cloudpipe/__init__.py | 3 ++ nova/api/ec2/__init__.py | 50 ++++++++++++++++++++++++++++++++-- nova/api/ec2/metadatarequesthandler.py | 3 ++ nova/api/openstack/__init__.py | 13 +++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/__init__.py b/nova/api/__init__.py index 80f9f2109..92b495e8c 100644 --- a/nova/api/__init__.py +++ b/nova/api/__init__.py @@ -29,7 +29,6 @@ import routes import webob.dec from nova import flags -from nova import utils from nova import wsgi from nova.api import cloudpipe from nova.api import ec2 diff --git a/nova/api/cloudpipe/__init__.py b/nova/api/cloudpipe/__init__.py index 6d40990a8..47349d9f9 100644 --- a/nova/api/cloudpipe/__init__.py +++ b/nova/api/cloudpipe/__init__.py @@ -67,3 +67,6 @@ class API(wsgi.Application): project_id = self.get_project_id_from_ip(req.remote_addr) cert = self.str_params['cert'] return crypto.sign_csr(urllib.unquote(cert), project_id) + +def cloudpipe_factory(global_opts, **local_opts): + return API() diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index a6ee16c33..50cb18078 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -225,10 +225,9 @@ class Executor(wsgi.Application): args = req.environ['ec2.action_args'] api_request = apirequest.APIRequest(controller, action) + result = None try: result = api_request.send(context, **args) - req.headers['Content-Type'] = 'text/xml' - return result except exception.ApiError as ex: if ex.code: @@ -238,6 +237,13 @@ class Executor(wsgi.Application): # TODO(vish): do something more useful with unknown exceptions except Exception as ex: return self._error(req, type(ex).__name__, str(ex)) + else: + resp = webob.Response() + resp.status = 200 + resp.headers['Content-Type'] = 'text/xml' + resp.body = str(result) + return resp + def _error(self, req, code, message): logging.error("%s: %s", code, message) @@ -249,3 +255,43 @@ class Executor(wsgi.Application): '%s' '?' % (code, message)) return resp + +class Versions(wsgi.Application): + + @webob.dec.wsgify + def __call__(self, req): + """Respond to a request for all EC2 versions.""" + # available api versions + versions = [ + '1.0', + '2007-01-19', + '2007-03-01', + '2007-08-29', + '2007-10-10', + '2007-12-15', + '2008-02-01', + '2008-09-01', + '2009-04-04', + ] + return ''.join('%s\n' % v for v in versions) + +def authenticate_factory(global_args, **local_args): + def authenticator(app): + return Authenticate(app) + return authenticator + +def router_factory(global_args, **local_args): + def router(app): + return Router(app) + return router + +def authorizer_factory(global_args, **local_args): + def authorizer(app): + return Authorizer(app) + return authorizer + +def executor_factory(global_args, **local_args): + return Executor() + +def versions_factory(global_args, **local_args): + return Versions() diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index 2f4f414cc..fffefb97b 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -72,3 +72,6 @@ class MetadataRequestHandler(object): if data is None: raise webob.exc.HTTPNotFound() return self.print_data(data) + +def metadata_factory(global_args, **local_args): + return MetadataRequestHandler() diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index b9ecbd9b8..cb825cf41 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -210,3 +210,16 @@ def limited(items, req): limit = min(1000, limit) range_end = offset + limit return items[offset:range_end] + +def auth_factory(global_conf, **local_conf): + def auth(app): + return AuthMiddleware(app) + return auth + +def ratelimit_factory(global_conf, **local_conf): + def rl(app): + return RateLimitingMiddleware(app) + return rl + +def router_factory(global_cof, **local_conf): + return APIRouter() -- cgit From 8c8b289f2626b1d9bad76bc5d4819904ace5800d Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 21 Dec 2010 02:21:01 -0500 Subject: Remove ec2 config chain and move openstack versions to top-level application. --- nova/api/openstack/__init__.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index cb825cf41..f3686997f 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -20,7 +20,6 @@ WSGI middleware for OpenStack API controllers. """ -import json import time import logging @@ -41,7 +40,6 @@ from nova.api.openstack import images from nova.api.openstack import ratelimiting from nova.api.openstack import servers from nova.api.openstack import sharedipgroups -from nova.auth import manager FLAGS = flags.FLAGS @@ -193,6 +191,19 @@ class APIRouter(wsgi.Router): super(APIRouter, self).__init__(mapper) +class Versions(wsgi.Application): + @webob.dec.wsgify + def __call__(self, req): + """Respond to a request for all OpenStack API versions.""" + response = { + "versions": [ + dict(status="CURRENT", id="v1.0")]} + metadata = { + "application/xml": { + "attributes": dict(version=["status", "id"])}} + return wsgi.Serializer(req.environ, metadata).to_content_type(response) + + def limited(items, req): """Return a slice of items according to requested offset and limit. @@ -223,3 +234,6 @@ def ratelimit_factory(global_conf, **local_conf): def router_factory(global_cof, **local_conf): return APIRouter() + +def versions_factory(global_conf, **local_conf): + return Versions() -- cgit From 729468d0be1bf97c869b1169414154a76d9b96b2 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 21 Dec 2010 19:20:28 -0500 Subject: Burnin support by specifying a specific host via availability_zone for running instances and volumes on. --- nova/api/ec2/cloud.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8375c4399..b181a947f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -189,9 +189,45 @@ class CloudController(object): return data def describe_availability_zones(self, context, **kwargs): + if ('zone_name' in kwargs and + 'verbose' in kwargs['zone_name'] and + context.is_admin): + return self._describe_availability_zones_verbose(context, + **kwargs) + else: + return self._describe_availability_zones(context, **kwargs) + + def _describe_availability_zones(self, context, **kwargs): return {'availabilityZoneInfo': [{'zoneName': 'nova', 'zoneState': 'available'}]} + def _describe_availability_zones_verbose(self, context, **kwargs): + rv = {'availabilityZoneInfo': [{'zoneName': 'nova', + 'zoneState': 'available'}]} + + services = db.service_get_all(context) + now = db.get_time() + hosts = [] + for host in [service['host'] for service in services]: + if not host in hosts: + hosts.append(host) + for host in hosts: + rv['availabilityZoneInfo'].append({'zoneName': '|- %s' % host, + 'zoneState': ''}) + hsvcs = [service for service in services if service['host'] == host] + for svc in hsvcs: + delta = now - (svc['updated_at'] or svc['created_at']) + alive = (delta.seconds <= FLAGS.service_down_time) + art = (alive and ":-)") or "XXX" + active = 'enabled' + if svc['disabled']: + active = 'disabled' + rv['availabilityZoneInfo'].append({ + 'zoneName': '| |- %s' % svc['binary'], + 'zoneState': '%s %s %s' % (active, art, + svc['updated_at'])}) + return rv + def describe_regions(self, context, region_name=None, **kwargs): if FLAGS.region_list: regions = [] @@ -757,6 +793,8 @@ class CloudController(object): description=kwargs.get('display_description'), key_name=kwargs.get('key_name'), security_group=kwargs.get('security_group'), + availability_zone=kwargs.get('placement', {}).get( + 'AvailabilityZone'), generate_hostname=internal_id_to_ec2_id) return self._format_run_instances(context, instances[0]['reservation_id']) -- cgit From 6debe20395d6ab476bfd2a237df8c2b08050e0e6 Mon Sep 17 00:00:00 2001 From: Eric Day Date: Mon, 27 Dec 2010 12:19:36 -0800 Subject: Converted Volume model and operation to use UUIDs. --- nova/api/ec2/cloud.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index d50a95095..8c687f173 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -458,7 +458,7 @@ class CloudController(object): # NOTE(vish): volume_id is an optional list of volume ids to filter by. volumes = [self._format_volume(context, v) for v in volumes - if volume_id is None or v['ec2_id'] in volume_id] + if volume_id is None or v['id'] in volume_id] return {'volumeSet': volumes} @@ -471,7 +471,7 @@ class CloudController(object): instance_data = '%s[%s]' % (instance_ec2_id, volume['instance']['host']) v = {} - v['volumeId'] = volume['ec2_id'] + v['volumeId'] = volume['id'] v['status'] = volume['status'] v['size'] = volume['size'] v['availabilityZone'] = volume['availability_zone'] @@ -527,7 +527,7 @@ class CloudController(object): return {'volumeSet': [self._format_volume(context, dict(volume_ref))]} def attach_volume(self, context, volume_id, instance_id, device, **kwargs): - volume_ref = db.volume_get_by_ec2_id(context, volume_id) + volume_ref = db.volume_get(context, volume_id) if not re.match("^/dev/[a-z]d[a-z]+$", device): raise exception.ApiError(_("Invalid device specified: %s. " "Example device: /dev/vdb") % device) @@ -553,7 +553,7 @@ class CloudController(object): 'volumeId': volume_ref['id']} def detach_volume(self, context, volume_id, **kwargs): - volume_ref = db.volume_get_by_ec2_id(context, volume_id) + volume_ref = db.volume_get(context, volume_id) instance_ref = db.volume_get_instance(context.elevated(), volume_ref['id']) if not instance_ref: @@ -807,7 +807,7 @@ class CloudController(object): def delete_volume(self, context, volume_id, **kwargs): # TODO: return error if not authorized - volume_ref = db.volume_get_by_ec2_id(context, volume_id) + volume_ref = db.volume_get(context, volume_id) if volume_ref['status'] != "available": raise exception.ApiError(_("Volume status must be available")) now = datetime.datetime.utcnow() -- cgit From 8aea573bd2e44e152fb4ef1627640bab1818dede Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 28 Dec 2010 23:55:58 -0600 Subject: initial lock functionality commit --- nova/api/openstack/__init__.py | 73 +++++++++++++++++++++++++++++++++++ nova/api/openstack/servers.py | 86 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index bebcdc18c..b3bb65550 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -22,6 +22,7 @@ WSGI middleware for OpenStack API controllers. import json import time +import functools import logging import routes @@ -113,3 +114,75 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) + + +#class CheckLock(object): +# """ +# decorator used for preventing action against locked instances +# unless, of course, you happen to be admin +# +# """ +# def __init__(self, function): +# self.function = function +# +# def __getattribute__(self, attr): +# if attr == "function": +# return super(CheckLock, self).__getattribute__(attr) +# return self.function.__getattribute__(attr) +# +# def __call__(self, *args, **kwargs): +# logging.info(_("Calling %s. Checking locks and privileges"), +# self.function.__name__) +# +# # get req +# if 'req' is in kwargs: +# req = kwargs['req'] +# else: +# req = args[1] +# +# # check table for lock +# locked = True +# if(locked): +# # check context for admin +# if(req.environ['nova.context'].is_admin): +# self.function(*args, **kwargs) +# else: +# pass +# # return 404 +# +# def __get__(self, obj, objtype): +# f = functools.partial(self.__call__, obj) +# f.__doc__ = self.function.__doc__ +# return f + + + + +#def checks_lock(function): +# """ +# decorator used for preventing action against locked instances +# unless, of course, you happen to be admin +# +# """ +# +# @functools.wraps(function) +# def decorated_function(*args, **kwargs): +# +# # check table for lock +# locked = True +# if(locked): +# try: +# # get context from req and check for admin +# if 'req' is in kwargs: +# req = kwargs['req'] +# else: +# req = args[1] +# if(req.environ['nova.context'].is_admin): +# function(*args, **kwargs) +# else: +# pass +# # return 404 +# except: +# logging.error(_("CheckLock: error getting context")) +# +# return decorated_function diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 10c397384..46e65ca83 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -35,6 +35,40 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) +def checks_lock(function): + """ + decorator used for preventing action against locked instances + unless, of course, you happen to be admin + + """ + + @functools.wraps(function) + def decorated_function(*args, **kwargs): + + # grab args to function + try: + if 'req' is in kwargs: + req = kwargs['req'] + else: + req = args[1] + if 'id' is in kwargs: + _id = kwargs['id'] + else: + req = args[2] + context = req.environ['nova.context'] + except: + logging.error(_("CheckLock: argument error")) + + # if locked and admin call function, otherwise 404 + if(compute_api.ComputeAPI().get_lock(context, _id)): + if(req.environ['nova.context'].is_admin): + function(*args, **kwargs) + # return 404 + return faults.Fault(exc.HTTPUnprocessableEntity()) + + return decorated_function + + def _entity_list(entities): """ Coerces a list of servers into proper dictionary format """ return dict(servers=entities) @@ -104,6 +138,7 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return _entity_list(res) + @checks_lock def show(self, req, id): """ Returns server details by server id """ try: @@ -113,6 +148,7 @@ class Controller(wsgi.Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) + @checks_lock def delete(self, req, id): """ Destroys a server """ try: @@ -140,6 +176,7 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key']) return _entity_inst(instances[0]) + @checks_lock def update(self, req, id): """ Updates the server name or password """ inst_dict = self._deserialize(req.body, req) @@ -160,6 +197,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() + @checks_lock def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ @@ -176,6 +214,51 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + def lock(self, req, id): + """ + lock the instance with id + admin only operation + + """ + context = req.environ['nova.context'] + try: + self.compute_api.lock(context, id) + except: + readable = traceback.format_exc() + logging.error(_("Compute.api::lock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def unlock(self, req, id): + """ + unlock the instance with id + admin only operation + + """ + context = req.environ['nova.context'] + try: + self.compute_api.unlock(context, id) + except: + readable = traceback.format_exc() + logging.error(_("Compute.api::unlock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def get_lock(self, req, id): + """ + return the boolean state of (instance with id)'s lock + + """ + context = req.environ['nova.context'] + try: + self.compute_api.get_lock(context, id) + except: + readable = traceback.format_exc() + logging.error(_("Compute.api::get_lock %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + @checks_lock def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] @@ -187,6 +270,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def unpause(self, req, id): """ Permit Admins to Unpause the server. """ ctxt = req.environ['nova.context'] @@ -198,6 +282,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def suspend(self, req, id): """permit admins to suspend the server""" context = req.environ['nova.context'] @@ -209,6 +294,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + @checks_lock def resume(self, req, id): """permit admins to resume the server from suspend""" context = req.environ['nova.context'] -- cgit From 3a85ba4fa4215737731b2e755abfa350c509e46f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:04:41 -0600 Subject: syntax error --- nova/api/openstack/servers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 46e65ca83..7744815fc 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -47,11 +47,11 @@ def checks_lock(function): # grab args to function try: - if 'req' is in kwargs: + if 'req' in kwargs: req = kwargs['req'] else: req = args[1] - if 'id' is in kwargs: + if 'id' in kwargs: _id = kwargs['id'] else: req = args[2] -- cgit From b6e5c68d65701b840006cea49367879ee88c9b80 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:09:49 -0600 Subject: forgot import --- nova/api/openstack/__init__.py | 1 - nova/api/openstack/servers.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index b3bb65550..c0bd37fef 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -22,7 +22,6 @@ WSGI middleware for OpenStack API controllers. import json import time -import functools import logging import routes diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 7744815fc..8b837e6fc 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -17,6 +17,7 @@ import logging import traceback +import functools from webob import exc -- cgit From 0afb4a06dcb94ae41d04b3d78304746b0cc5b26f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 13:33:51 -0600 Subject: refactor --- nova/api/openstack/servers.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8b837e6fc..292a664b7 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -58,14 +58,15 @@ def checks_lock(function): req = args[2] context = req.environ['nova.context'] except: - logging.error(_("CheckLock: argument error")) - - # if locked and admin call function, otherwise 404 - if(compute_api.ComputeAPI().get_lock(context, _id)): - if(req.environ['nova.context'].is_admin): - function(*args, **kwargs) - # return 404 - return faults.Fault(exc.HTTPUnprocessableEntity()) + logging.error(_("CheckLock: argument error: |%s|, |%s|"), args, + kwargs) + # if admin or unlocked call function, otherwise 404 + locked = compute_api.ComputeAPI().get_lock(context, _id) + admin = req.environ['nova.context'].is_admin + if(admin or not locked): + return function(*args, **kwargs) + + return faults.Fault(exc.HTTPNotFound()) return decorated_function -- cgit From 6202b21b42615cf15b0dd60089026472e6836c69 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 15:31:53 -0600 Subject: removed some code i didn't end up using --- nova/api/openstack/__init__.py | 72 ------------------------------------------ 1 file changed, 72 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7cceb7733..66aceee2d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -113,75 +113,3 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) - - -#class CheckLock(object): -# """ -# decorator used for preventing action against locked instances -# unless, of course, you happen to be admin -# -# """ -# def __init__(self, function): -# self.function = function -# -# def __getattribute__(self, attr): -# if attr == "function": -# return super(CheckLock, self).__getattribute__(attr) -# return self.function.__getattribute__(attr) -# -# def __call__(self, *args, **kwargs): -# logging.info(_("Calling %s. Checking locks and privileges"), -# self.function.__name__) -# -# # get req -# if 'req' is in kwargs: -# req = kwargs['req'] -# else: -# req = args[1] -# -# # check table for lock -# locked = True -# if(locked): -# # check context for admin -# if(req.environ['nova.context'].is_admin): -# self.function(*args, **kwargs) -# else: -# pass -# # return 404 -# -# def __get__(self, obj, objtype): -# f = functools.partial(self.__call__, obj) -# f.__doc__ = self.function.__doc__ -# return f - - - - -#def checks_lock(function): -# """ -# decorator used for preventing action against locked instances -# unless, of course, you happen to be admin -# -# """ -# -# @functools.wraps(function) -# def decorated_function(*args, **kwargs): -# -# # check table for lock -# locked = True -# if(locked): -# try: -# # get context from req and check for admin -# if 'req' is in kwargs: -# req = kwargs['req'] -# else: -# req = args[1] -# if(req.environ['nova.context'].is_admin): -# function(*args, **kwargs) -# else: -# pass -# # return 404 -# except: -# logging.error(_("CheckLock: error getting context")) -# -# return decorated_function -- cgit From aac25e8cc6e75d5d0abc41a8cf979300e58bcc3b Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 17:04:40 -0600 Subject: removed () from if (can't believe i did that) and renamed checks_lock decorator --- nova/api/openstack/servers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c7263273c..74b4f55b5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -36,7 +36,7 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) -def checks_lock(function): +def checks_instance_lock(function): """ decorator used for preventing action against locked instances unless, of course, you happen to be admin @@ -58,12 +58,12 @@ def checks_lock(function): req = args[2] context = req.environ['nova.context'] except: - logging.error(_("CheckLock: argument error: |%s|, |%s|"), args, + logging.error(_("check_lock: argument error: |%s|, |%s|"), args, kwargs) # if admin or unlocked call function, otherwise 404 locked = compute_api.ComputeAPI().get_lock(context, _id) admin = req.environ['nova.context'].is_admin - if(admin or not locked): + if admin or not locked: return function(*args, **kwargs) return faults.Fault(exc.HTTPNotFound()) @@ -138,7 +138,7 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return dict(servers=res) - @checks_lock + @checks_instance_lock def show(self, req, id): """ Returns server details by server id """ try: @@ -148,7 +148,7 @@ class Controller(wsgi.Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - @checks_lock + @checks_instance_lock def delete(self, req, id): """ Destroys a server """ try: @@ -176,7 +176,7 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key']) return _translate_keys(instances[0]) - @checks_lock + @checks_instance_lock def update(self, req, id): """ Updates the server name or password """ inst_dict = self._deserialize(req.body, req) @@ -198,7 +198,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() - @checks_lock + @checks_instance_lock def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ @@ -259,7 +259,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] @@ -271,7 +271,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def unpause(self, req, id): """ Permit Admins to Unpause the server. """ ctxt = req.environ['nova.context'] @@ -283,7 +283,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def suspend(self, req, id): """permit admins to suspend the server""" context = req.environ['nova.context'] @@ -295,7 +295,7 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_lock + @checks_instance_lock def resume(self, req, id): """permit admins to resume the server from suspend""" context = req.environ['nova.context'] -- cgit From be6750a77e5121fe8f0d95016da4e96c9de3b5aa Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 17:40:18 -0600 Subject: removed lock check from show and changed returning 404 to 405 --- nova/api/openstack/servers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 74b4f55b5..24fd5000c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -66,7 +66,7 @@ def checks_instance_lock(function): if admin or not locked: return function(*args, **kwargs) - return faults.Fault(exc.HTTPNotFound()) + return faults.Fault(exc.HTTPMethodNotAllowed()) return decorated_function @@ -138,7 +138,6 @@ class Controller(wsgi.Controller): res = [entity_maker(inst)['server'] for inst in limited_list] return dict(servers=res) - @checks_instance_lock def show(self, req, id): """ Returns server details by server id """ try: -- cgit From 64078137ce12ee52fff710f5a262d57b4ace2809 Mon Sep 17 00:00:00 2001 From: Eric Day Date: Wed, 29 Dec 2010 16:29:15 -0800 Subject: Moved ec2 volume operations into a volume API interface for other components to use. Added attach/detach as compute.api methods, since they operate in the context of instances (and to avoid a dependency loop). --- nova/api/ec2/cloud.py | 141 +++++++++++++------------------------------------- 1 file changed, 36 insertions(+), 105 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8c687f173..74c73e0dd 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -38,12 +38,12 @@ from nova import flags from nova import quota from nova import rpc from nova import utils +from nova import volume from nova.compute import api as compute_api from nova.compute import instance_types FLAGS = flags.FLAGS -flags.DECLARE('storage_availability_zone', 'nova.volume.manager') InvalidInputException = exception.InvalidInputException @@ -89,8 +89,10 @@ class CloudController(object): def __init__(self): self.network_manager = utils.import_object(FLAGS.network_manager) self.image_service = utils.import_object(FLAGS.image_service) + self.volume_api = volume.API() self.compute_api = compute_api.ComputeAPI(self.network_manager, - self.image_service) + self.image_service, + self.volume_api) self.setup() def __str__(self): @@ -451,15 +453,10 @@ class CloudController(object): "output": base64.b64encode(output)} def describe_volumes(self, context, volume_id=None, **kwargs): - if context.user.is_admin(): - volumes = db.volume_get_all(context) - else: - volumes = db.volume_get_all_by_project(context, context.project_id) - + volumes = self.volume_api.get(context) # NOTE(vish): volume_id is an optional list of volume ids to filter by. volumes = [self._format_volume(context, v) for v in volumes if volume_id is None or v['id'] in volume_id] - return {'volumeSet': volumes} def _format_volume(self, context, volume): @@ -498,88 +495,47 @@ class CloudController(object): return v def create_volume(self, context, size, **kwargs): - # check quota - if quota.allowed_volumes(context, 1, size) < 1: - logging.warn("Quota exceeeded for %s, tried to create %sG volume", - context.project_id, size) - raise quota.QuotaError("Volume quota exceeded. You cannot " - "create a volume of size %s" % size) - vol = {} - vol['size'] = size - vol['user_id'] = context.user.id - vol['project_id'] = context.project_id - vol['availability_zone'] = FLAGS.storage_availability_zone - vol['status'] = "creating" - vol['attach_status'] = "detached" - vol['display_name'] = kwargs.get('display_name') - vol['display_description'] = kwargs.get('display_description') - volume_ref = db.volume_create(context, vol) - - rpc.cast(context, - FLAGS.scheduler_topic, - {"method": "create_volume", - "args": {"topic": FLAGS.volume_topic, - "volume_id": volume_ref['id']}}) - + volume = self.volume_api.create(context, size, + kwargs.get('display_name'), + kwargs.get('display_description')) # TODO(vish): Instance should be None at db layer instead of # trying to lazy load, but for now we turn it into # a dict to avoid an error. return {'volumeSet': [self._format_volume(context, dict(volume_ref))]} + def delete_volume(self, context, volume_id, **kwargs): + self.volume_api.delete(context, volume_id) + return True + + def update_volume(self, context, volume_id, **kwargs): + updatable_fields = ['display_name', 'display_description'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + self.volume_api.update(context, volume_id, kwargs) + return True + def attach_volume(self, context, volume_id, instance_id, device, **kwargs): - volume_ref = db.volume_get(context, volume_id) - if not re.match("^/dev/[a-z]d[a-z]+$", device): - raise exception.ApiError(_("Invalid device specified: %s. " - "Example device: /dev/vdb") % device) - # 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_id = ec2_id_to_id(instance_id) - instance_ref = self.compute_api.get_instance(context, instance_id) - host = instance_ref['host'] - rpc.cast(context, - db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "attach_volume", - "args": {"volume_id": volume_ref['id'], - "instance_id": instance_ref['id'], - "mountpoint": device}}) - return {'attachTime': volume_ref['attach_time'], - 'device': volume_ref['mountpoint'], - 'instanceId': instance_ref['id'], + self.compute_api.attach_volume(context, instance_id, volume_id, device) + volume = self.volume_api.get(context, volume_id) + return {'attachTime': volume['attach_time'], + 'device': volume['mountpoint'], + 'instanceId': instance_id, 'requestId': context.request_id, - 'status': volume_ref['attach_status'], - 'volumeId': volume_ref['id']} + 'status': volume['attach_status'], + 'volumeId': volume_id} def detach_volume(self, context, volume_id, **kwargs): - volume_ref = db.volume_get(context, volume_id) - instance_ref = db.volume_get_instance(context.elevated(), - volume_ref['id']) - if not instance_ref: - raise exception.ApiError(_("Volume isn't attached to anything!")) - # TODO(vish): abstract status checking? - if volume_ref['status'] == "available": - raise exception.ApiError(_("Volume is already detached")) - try: - host = instance_ref['host'] - rpc.cast(context, - db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "detach_volume", - "args": {"instance_id": instance_ref['id'], - "volume_id": volume_ref['id']}}) - except exception.NotFound: - # If the instance doesn't exist anymore, - # then we need to call detach blind - db.volume_detached(context) - instance_id = instance_ref['id'] - ec2_id = id_to_ec2_id(instance_id) - return {'attachTime': volume_ref['attach_time'], - 'device': volume_ref['mountpoint'], - 'instanceId': instance_id, + volume = self.volume_api.get(context, volume_id) + instance = self.compute_api.detach_volume(context, volume_id) + return {'attachTime': volume['attach_time'], + 'device': volume['mountpoint'], + 'instanceId': id_to_ec2_id(instance['id']), 'requestId': context.request_id, - 'status': volume_ref['attach_status'], - 'volumeId': volume_ref['id']} + 'status': volume['attach_status'], + 'volumeId': volume_id} def _convert_to_set(self, lst, label): if lst == None or lst == []: @@ -588,16 +544,6 @@ class CloudController(object): lst = [lst] return [{label: x} for x in lst] - def update_volume(self, context, volume_id, **kwargs): - updatable_fields = ['display_name', 'display_description'] - changes = {} - for field in updatable_fields: - if field in kwargs: - changes[field] = kwargs[field] - if changes: - db.volume_update(context, volume_id, kwargs) - return True - def describe_instances(self, context, **kwargs): return self._format_describe_instances(context) @@ -805,21 +751,6 @@ class CloudController(object): db.instance_update(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(context, volume_id) - if volume_ref['status'] != "available": - raise exception.ApiError(_("Volume status must be available")) - now = datetime.datetime.utcnow() - db.volume_update(context, volume_ref['id'], {'status': 'deleting', - 'terminated_at': now}) - host = volume_ref['host'] - rpc.cast(context, - db.queue_get_for(context, FLAGS.volume_topic, host), - {"method": "delete_volume", - "args": {"volume_id": volume_ref['id']}}) - return True - def describe_images(self, context, image_id=None, **kwargs): # Note: image_id is a list! images = self.image_service.index(context) -- cgit From 24e253a1feaa0a39e4095f447f62f7ea9b43c8bb Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 29 Dec 2010 18:30:01 -0600 Subject: moved check lock decorator to compute api level. altered openstack.test_servers according and wrote test for lock in tests.test_compute --- nova/api/openstack/servers.py | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 24fd5000c..497a04ae3 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -17,7 +17,6 @@ import logging import traceback -import functools from webob import exc @@ -36,41 +35,6 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) -def checks_instance_lock(function): - """ - decorator used for preventing action against locked instances - unless, of course, you happen to be admin - - """ - - @functools.wraps(function) - def decorated_function(*args, **kwargs): - - # grab args to function - try: - if 'req' in kwargs: - req = kwargs['req'] - else: - req = args[1] - if 'id' in kwargs: - _id = kwargs['id'] - else: - req = args[2] - context = req.environ['nova.context'] - except: - logging.error(_("check_lock: argument error: |%s|, |%s|"), args, - kwargs) - # if admin or unlocked call function, otherwise 404 - locked = compute_api.ComputeAPI().get_lock(context, _id) - admin = req.environ['nova.context'].is_admin - if admin or not locked: - return function(*args, **kwargs) - - return faults.Fault(exc.HTTPMethodNotAllowed()) - - return decorated_function - - def _translate_detail_keys(inst): """ Coerces into dictionary format, mapping everything to Rackspace-like attributes for return""" @@ -147,7 +111,6 @@ class Controller(wsgi.Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - @checks_instance_lock def delete(self, req, id): """ Destroys a server """ try: @@ -175,7 +138,6 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key']) return _translate_keys(instances[0]) - @checks_instance_lock def update(self, req, id): """ Updates the server name or password """ inst_dict = self._deserialize(req.body, req) @@ -197,7 +159,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() - @checks_instance_lock def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ @@ -258,7 +219,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_instance_lock def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] @@ -270,7 +230,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_instance_lock def unpause(self, req, id): """ Permit Admins to Unpause the server. """ ctxt = req.environ['nova.context'] @@ -282,7 +241,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_instance_lock def suspend(self, req, id): """permit admins to suspend the server""" context = req.environ['nova.context'] @@ -294,7 +252,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() - @checks_instance_lock def resume(self, req, id): """permit admins to resume the server from suspend""" context = req.environ['nova.context'] -- cgit From b1a08af498ed6b52e3373a23196ded0396e6d34b Mon Sep 17 00:00:00 2001 From: Eric Day Date: Wed, 29 Dec 2010 20:30:36 -0800 Subject: Cleaned up the compute API, mostly consistency with other parts of the system and renaming redundant module names. --- nova/api/ec2/cloud.py | 24 ++++++++++++------------ nova/api/openstack/servers.py | 22 +++++++++------------- 2 files changed, 21 insertions(+), 25 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 74c73e0dd..cc58f3cfe 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -31,6 +31,7 @@ import os from nova import context import IPy +from nova import compute from nova import crypto from nova import db from nova import exception @@ -39,7 +40,6 @@ from nova import quota from nova import rpc from nova import utils from nova import volume -from nova.compute import api as compute_api from nova.compute import instance_types @@ -90,9 +90,9 @@ class CloudController(object): self.network_manager = utils.import_object(FLAGS.network_manager) self.image_service = utils.import_object(FLAGS.image_service) self.volume_api = volume.API() - self.compute_api = compute_api.ComputeAPI(self.network_manager, - self.image_service, - self.volume_api) + self.compute_api = compute.API(self.network_manager, + self.image_service, + self.volume_api) self.setup() def __str__(self): @@ -116,7 +116,7 @@ class CloudController(object): def _get_mpi_data(self, context, project_id): result = {} - for instance in self.compute_api.get_instances(context, project_id): + for instance in self.compute_api.get(context, project_id=project_id): if instance['fixed_ip']: line = '%s slots=%d' % (instance['fixed_ip']['address'], instance['vcpus']) @@ -440,7 +440,7 @@ class CloudController(object): # instance_id is passed in as a list of instances ec2_id = instance_id[0] instance_id = ec2_id_to_id(ec2_id) - instance_ref = self.compute_api.get_instance(context, instance_id) + instance_ref = self.compute_api.get(context, instance_id) output = rpc.call(context, '%s.%s' % (FLAGS.compute_topic, instance_ref['host']), @@ -561,7 +561,7 @@ class CloudController(object): instances = db.instance_get_all_by_reservation(context, reservation_id) else: - instances = self.compute_api.get_instances(context) + instances = self.compute_api.get(context) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: @@ -664,7 +664,7 @@ class CloudController(object): def associate_address(self, context, instance_id, public_ip, **kwargs): instance_id = ec2_id_to_id(instance_id) - instance_ref = self.compute_api.get_instance(context, instance_id) + instance_ref = self.compute_api.get(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) @@ -695,7 +695,7 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) - instances = self.compute_api.create_instances(context, + instances = self.compute_api.create(context, instance_types.get_by_type(kwargs.get('instance_type', None)), kwargs['image_id'], min_count=int(kwargs.get('min_count', max_count)), @@ -703,7 +703,7 @@ class CloudController(object): kernel_id=kwargs.get('kernel_id', None), ramdisk_id=kwargs.get('ramdisk_id'), display_name=kwargs.get('display_name'), - description=kwargs.get('display_description'), + display_description=kwargs.get('display_description'), key_name=kwargs.get('key_name'), user_data=kwargs.get('user_data'), security_group=kwargs.get('security_group'), @@ -717,7 +717,7 @@ class CloudController(object): logging.debug("Going to start terminating instances") for ec2_id in instance_id: instance_id = ec2_id_to_id(ec2_id) - self.compute_api.delete_instance(context, instance_id) + self.compute_api.delete(context, instance_id) return True def reboot_instances(self, context, instance_id, **kwargs): @@ -747,7 +747,7 @@ class CloudController(object): changes[field] = kwargs[field] if changes: instance_id = ec2_id_to_id(ec2_id) - inst = self.compute_api.get_instance(context, instance_id) + inst = self.compute_api.get(context, instance_id) db.instance_update(context, inst['id'], kwargs) return True diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index cac5428b9..30b2160bd 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -20,12 +20,12 @@ import traceback from webob import exc +from nova import compute from nova import exception from nova import wsgi from nova.api.openstack import common from nova.api.openstack import faults from nova.auth import manager as auth_manager -from nova.compute import api as compute_api from nova.compute import instance_types from nova.compute import power_state import nova.api.openstack @@ -81,7 +81,7 @@ class Controller(wsgi.Controller): "status", "progress"]}}} def __init__(self): - self.compute_api = compute_api.ComputeAPI() + self.compute_api = compute.API() super(Controller, self).__init__() def index(self, req): @@ -97,8 +97,7 @@ class Controller(wsgi.Controller): entity_maker - either _entity_detail or _entity_inst """ - instance_list = self.compute_api.get_instances( - req.environ['nova.context']) + instance_list = self.compute_api.get(req.environ['nova.context']) limited_list = common.limited(instance_list, req) res = [entity_maker(inst)['server'] for inst in limited_list] return _entity_list(res) @@ -106,8 +105,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ try: - instance = self.compute_api.get_instance( - req.environ['nova.context'], int(id)) + instance = self.compute_api.get(req.environ['nova.context'], id) return _entity_detail(instance) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -115,8 +113,7 @@ class Controller(wsgi.Controller): def delete(self, req, id): """ Destroys a server """ try: - self.compute_api.delete_instance(req.environ['nova.context'], - int(id)) + self.compute_api.delete(req.environ['nova.context'], id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) return exc.HTTPAccepted() @@ -129,12 +126,12 @@ class Controller(wsgi.Controller): key_pair = auth_manager.AuthManager.get_key_pairs( req.environ['nova.context'])[0] - instances = self.compute_api.create_instances( + instances = self.compute_api.create( req.environ['nova.context'], instance_types.get_by_flavor_id(env['server']['flavorId']), env['server']['imageId'], display_name=env['server']['name'], - description=env['server']['name'], + display_description=env['server']['name'], key_name=key_pair['name'], key_data=key_pair['public_key']) return _entity_inst(instances[0]) @@ -152,9 +149,8 @@ class Controller(wsgi.Controller): update_dict['display_name'] = inst_dict['server']['name'] try: - self.compute_api.update_instance(req.environ['nova.context'], - instance['id'], - **update_dict) + self.compute_api.update(req.environ['nova.context'], id, + **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent() -- cgit From 750a0c9b413ad3912d522355332cffadd9667d0c Mon Sep 17 00:00:00 2001 From: Eric Day Date: Wed, 29 Dec 2010 21:41:42 -0800 Subject: Converted a few more ec2 calls to use compute api. --- nova/api/ec2/cloud.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index cc58f3cfe..5ffc301be 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -138,7 +138,7 @@ class CloudController(object): def get_metadata(self, address): ctxt = context.get_admin_context() - instance_ref = db.fixed_ip_get_instance(ctxt, address) + instance_ref = self.compute_api.get(ctxt, fixed_ip=address) if instance_ref is None: return None mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) @@ -555,13 +555,9 @@ class CloudController(object): assert len(i) == 1 return i[0] - def _format_instances(self, context, reservation_id=None): + def _format_instances(self, context, **kwargs): reservations = {} - if reservation_id: - instances = db.instance_get_all_by_reservation(context, - reservation_id) - else: - instances = self.compute_api.get(context) + instances = self.compute_api.get(context, **kwargs) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: @@ -747,8 +743,7 @@ class CloudController(object): changes[field] = kwargs[field] if changes: instance_id = ec2_id_to_id(ec2_id) - inst = self.compute_api.get(context, instance_id) - db.instance_update(context, inst['id'], kwargs) + self.compute_api.update(context, instance_id, **kwargs) return True def describe_images(self, context, image_id=None, **kwargs): -- cgit From ba31a61ae6348bffbd70d5875f12a540d49e8885 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 30 Dec 2010 02:20:31 -0500 Subject: pep 8 --- nova/api/ec2/__init__.py | 7 ++++++- nova/api/ec2/metadatarequesthandler.py | 1 + nova/api/openstack/__init__.py | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 7bec7f81e..aa3bfaeb4 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -313,7 +313,6 @@ class Executor(wsgi.Application): resp.body = str(result) return resp - def _error(self, req, code, message): logging.error("%s: %s", code, message) resp = webob.Response() @@ -325,6 +324,7 @@ class Executor(wsgi.Application): '?' % (code, message)) return resp + class Versions(wsgi.Application): @webob.dec.wsgify @@ -344,23 +344,28 @@ class Versions(wsgi.Application): ] return ''.join('%s\n' % v for v in versions) + def authenticate_factory(global_args, **local_args): def authenticator(app): return Authenticate(app) return authenticator + def router_factory(global_args, **local_args): def router(app): return Router(app) return router + def authorizer_factory(global_args, **local_args): def authorizer(app): return Authorizer(app) return authorizer + def executor_factory(global_args, **local_args): return Executor() + def versions_factory(global_args, **local_args): return Versions() diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index 9d1594ae7..a57a6698a 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -80,5 +80,6 @@ class MetadataRequestHandler(object): raise webob.exc.HTTPNotFound() return self.print_data(data) + def metadata_factory(global_args, **local_args): return MetadataRequestHandler() diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 5dd092a1f..dc7794876 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -129,5 +129,6 @@ class Versions(wsgi.Application): def router_factory(global_cof, **local_conf): return APIRouter() + def versions_factory(global_conf, **local_conf): return Versions() -- cgit From 71d715d422a746f4951877d8ff76e0ace355281e Mon Sep 17 00:00:00 2001 From: Eric Day Date: Thu, 30 Dec 2010 15:43:41 -0800 Subject: Moved network operation code in ec2 api into a generic network API class. Removed a circular dependency with compute/quota. --- nova/api/ec2/cloud.py | 59 ++++++--------------------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 5ffc301be..d68c598e9 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -36,7 +36,7 @@ from nova import crypto from nova import db from nova import exception from nova import flags -from nova import quota +from nova import network from nova import rpc from nova import utils from nova import volume @@ -87,11 +87,10 @@ class CloudController(object): sent to the other nodes. """ def __init__(self): - self.network_manager = utils.import_object(FLAGS.network_manager) self.image_service = utils.import_object(FLAGS.image_service) + self.network_api = network.API() self.volume_api = volume.API() - self.compute_api = compute.API(self.network_manager, - self.image_service, + self.compute_api = compute.API(self.image_service, self.network_api, self.volume_api) self.setup() @@ -629,64 +628,20 @@ class CloudController(object): return {'addressesSet': addresses} def allocate_address(self, context, **kwargs): - # check quota - if quota.allowed_floating_ips(context, 1) < 1: - logging.warn(_("Quota exceeeded for %s, tried to allocate " - "address"), - context.project_id) - raise quota.QuotaError(_("Address quota exceeded. You cannot " - "allocate any more addresses")) - # NOTE(vish): We don't know which network host should get the ip - # when we allocate, so just send it to any one. This - # will probably need to move into a network supervisor - # at some point. - public_ip = rpc.call(context, - FLAGS.network_topic, - {"method": "allocate_floating_ip", - "args": {"project_id": context.project_id}}) + public_ip = self.network_api.allocate_floating_ip(context) return {'addressSet': [{'publicIp': public_ip}]} def release_address(self, context, public_ip, **kwargs): - floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) - # NOTE(vish): We don't know which network host should get the ip - # when we deallocate, so just send it to any one. This - # will probably need to move into a network supervisor - # at some point. - rpc.cast(context, - FLAGS.network_topic, - {"method": "deallocate_floating_ip", - "args": {"floating_address": floating_ip_ref['address']}}) + self.network_api.release_floating_ip(context, public_ip) return {'releaseResponse': ["Address released."]} def associate_address(self, context, instance_id, public_ip, **kwargs): instance_id = ec2_id_to_id(instance_id) - instance_ref = self.compute_api.get(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) - # NOTE(vish): Perhaps we should just pass this on to compute and - # let compute communicate with network. - network_topic = self.compute_api.get_network_topic(context, - instance_id) - rpc.cast(context, - network_topic, - {"method": "associate_floating_ip", - "args": {"floating_address": floating_ip_ref['address'], - "fixed_address": fixed_address}}) + self.compute_api.associate_floating_ip(context, instance_id, public_ip) return {'associateResponse': ["Address associated."]} def disassociate_address(self, context, public_ip, **kwargs): - floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) - # NOTE(vish): Get the topic from the host name of the network of - # the associated fixed ip. - if not floating_ip_ref.get('fixed_ip'): - raise exception.ApiError('Address is not associated.') - host = floating_ip_ref['fixed_ip']['network']['host'] - topic = db.queue_get_for(context, FLAGS.network_topic, host) - rpc.cast(context, - topic, - {"method": "disassociate_floating_ip", - "args": {"floating_address": floating_ip_ref['address']}}) + self.network_api.disassociate_floating_ip(context, public_ip) return {'disassociateResponse': ["Address disassociated."]} def run_instances(self, context, **kwargs): -- cgit From c7305af78049f94dedcbb55480b91a3c6d843b9f Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 00:23:35 -0500 Subject: Apply logging changes as a giant patch to work around the cloudpipe delete + add issue in the original patch. --- nova/api/ec2/__init__.py | 92 +++++++++++++++++++++++++--------- nova/api/ec2/admin.py | 38 +++++++++++--- nova/api/ec2/apirequest.py | 12 ++--- nova/api/ec2/cloud.py | 45 +++++++++++++---- nova/api/ec2/metadatarequesthandler.py | 7 ++- nova/api/openstack/__init__.py | 13 ++--- 6 files changed, 149 insertions(+), 58 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index aa3bfaeb4..4dd2e55cd 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -20,7 +20,7 @@ Starting point for routing EC2 requests. """ -import logging +import datetime import routes import webob import webob.dec @@ -29,6 +29,7 @@ import webob.exc from nova import context from nova import exception from nova import flags +from nova import log as logging from nova import wsgi from nova.api.ec2 import apirequest from nova.api.ec2 import admin @@ -37,6 +38,7 @@ from nova.auth import manager FLAGS = flags.FLAGS +LOG = logging.getLogger("nova.api") flags.DEFINE_boolean('use_forwarded_for', False, 'Treat X-Forwarded-For as the canonical remote address. ' 'Only enable this if you have a sanitizing proxy.') @@ -52,10 +54,6 @@ flags.DEFINE_list('lockout_memcached_servers', None, 'Memcached servers or None for in process cache.') -_log = logging.getLogger("api") -_log.setLevel(logging.DEBUG) - - class API(wsgi.Middleware): """Routing for all EC2 API requests.""" @@ -64,6 +62,40 @@ class API(wsgi.Middleware): if FLAGS.use_lockout: self.application = Lockout(self.application) + @webob.dec.wsgify + def __call__(self, req): + rv = req.get_response(self.application) + self.log_request_completion(rv, req) + return rv + + def log_request_completion(self, response, request): + controller = request.environ.get('ec2.controller', None) + if controller: + controller = controller.__class__.__name__ + action = request.environ.get('ec2.action', None) + ctxt = request.environ.get('ec2.context', None) + seconds = 'X' + microseconds = 'X' + if ctxt: + delta = datetime.datetime.utcnow() - \ + ctxt.timestamp + seconds = delta.seconds + microseconds = delta.microseconds + LOG.info( + "%s.%ss %s %s %s %s:%s %s [%s] %s %s", + seconds, + microseconds, + request.remote_addr, + request.method, + request.path_info, + controller, + action, + response.status_int, + request.user_agent, + request.content_type, + response.content_type, + context=ctxt) + class Lockout(wsgi.Middleware): """Lockout for x minutes on y failed auths in a z minute period. @@ -107,14 +139,13 @@ class Lockout(wsgi.Middleware): # NOTE(vish): To use incr, failures has to be a string. self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60) elif failures >= FLAGS.lockout_attempts: - _log.warn('Access key %s has had %d failed authentications' - ' and will be locked out for %d minutes.' % - (access_key, failures, FLAGS.lockout_minutes)) + LOG.warn('Access key %s has had %d failed authentications' + ' and will be locked out for %d minutes.', + access_key, failures, FLAGS.lockout_minutes) self.mc.set(failures_key, str(failures), time=FLAGS.lockout_minutes * 60) return res - class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'ec2.context' to WSGI environ.""" @@ -142,8 +173,9 @@ class Authenticate(wsgi.Middleware): req.method, req.host, req.path) - except exception.Error, ex: - logging.debug(_("Authentication Failure: %s") % ex) + # Be explicit for what exceptions are 403, the rest bubble as 500 + except (exception.NotFound, exception.NotAuthorized) as ex: + LOG.audit(_("Authentication Failure: %s"), str(ex)) raise webob.exc.HTTPForbidden() # Authenticated! @@ -154,6 +186,8 @@ class Authenticate(wsgi.Middleware): project=project, remote_address=remote_address) req.environ['ec2.context'] = ctxt + LOG.audit(_('Authenticated Request For %s:%s)'), user.name, + project.name, context=req.environ['ec2.context']) return self.application @@ -189,9 +223,9 @@ class Router(wsgi.Middleware): except: raise webob.exc.HTTPBadRequest() - _log.debug(_('action: %s') % action) + LOG.debug(_('action: %s'), action) for key, value in args.items(): - _log.debug(_('arg: %s\t\tval: %s') % (key, value)) + LOG.debug(_('arg: %s\t\tval: %s'), key, value) # Success! req.environ['ec2.controller'] = controller @@ -263,6 +297,8 @@ class Authorizer(wsgi.Middleware): if self._matches_any_role(context, allowed_roles): return self.application else: + LOG.audit("Unauthorized request for controller=%s and action=%s", + controller_name, action, context=context) raise webob.exc.HTTPUnauthorized() def _matches_any_role(self, context, roles): @@ -297,15 +333,24 @@ class Executor(wsgi.Application): result = None try: result = api_request.send(context, **args) + except exception.NotFound as ex: + LOG.info(_('NotFound raised: %s'), str(ex), context=context) + return self._error(req, context, type(ex).__name__, str(ex)) except exception.ApiError as ex: - + LOG.exception('ApiError raised', context=context) if ex.code: - return self._error(req, ex.code, ex.message) + return self._error(req, context, ex.code, str(ex)) else: - return self._error(req, type(ex).__name__, ex.message) - # TODO(vish): do something more useful with unknown exceptions + return self._error(req, context, type(ex).__name__, str(ex)) except Exception as ex: - return self._error(req, type(ex).__name__, str(ex)) + extra = {'environment': req.environ} + LOG.exception(_('Unexpected error raised: %s'), str(ex), + extra=extra, context=context) + return self._error(req, + context, + 'UnknownError', + _('An unknown error has occurred. ' + 'Please try your request again.')) else: resp = webob.Response() resp.status = 200 @@ -313,15 +358,16 @@ class Executor(wsgi.Application): resp.body = str(result) return resp - def _error(self, req, code, message): - logging.error("%s: %s", code, message) + def _error(self, req, context, code, message): + LOG.error("%s: %s", code, message, context=context) resp = webob.Response() resp.status = 400 resp.headers['Content-Type'] = 'text/xml' resp.body = str('\n' - '%s' - '%s' - '?' % (code, message)) + '%s' + '%s' + '%s' % + (code, message, context.request_id)) return resp diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index fac01369e..758b612e8 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -24,9 +24,13 @@ import base64 from nova import db from nova import exception +from nova import log as logging from nova.auth import manager +LOG = logging.getLogger('nova.api.ec2.admin') + + def user_dict(user, base64_file=None): """Convert the user object to a result dict""" if user: @@ -75,17 +79,18 @@ class AdminController(object): return {'userSet': [user_dict(u) for u in manager.AuthManager().get_users()]} - def register_user(self, _context, name, **_kwargs): + def register_user(self, context, name, **_kwargs): """Creates a new user, and returns generated credentials.""" + LOG.audit(_("Creating new user: %s"), name, context=context) return user_dict(manager.AuthManager().create_user(name)) - def deregister_user(self, _context, name, **_kwargs): + def deregister_user(self, context, name, **_kwargs): """Deletes a single user (NOT undoable.) Should throw an exception if the user has instances, volumes, or buckets remaining. """ + LOG.audit(_("Deleting user: %s"), name, context=context) manager.AuthManager().delete_user(name) - return True def describe_roles(self, context, project_roles=True, **kwargs): @@ -105,15 +110,27 @@ class AdminController(object): operation='add', **kwargs): """Add or remove a role for a user and project.""" if operation == 'add': + if project: + LOG.audit(_("Adding role %s to user %s for project %s"), role, + user, project, context=context) + else: + LOG.audit(_("Adding sitewide role %s to user %s"), role, user, + context=context) manager.AuthManager().add_role(user, role, project) elif operation == 'remove': + if project: + LOG.audit(_("Removing role %s from user %s for project %s"), + role, user, project, context=context) + else: + LOG.audit(_("Removing sitewide role %s from user %s"), role, + user, context=context) manager.AuthManager().remove_role(user, role, project) else: - raise exception.ApiError('operation must be add or remove') + raise exception.ApiError(_('operation must be add or remove')) return True - def generate_x509_for_user(self, _context, name, project=None, **kwargs): + def generate_x509_for_user(self, context, name, project=None, **kwargs): """Generates and returns an x509 certificate for a single user. Is usually called from a client that will wrap this with access and secret key info, and return a zip file. @@ -122,6 +139,8 @@ class AdminController(object): project = name project = manager.AuthManager().get_project(project) user = manager.AuthManager().get_user(name) + LOG.audit(_("Getting x509 for user: %s on project: %s"), name, + project, context=context) return user_dict(user, base64.b64encode(project.get_credentials(user))) def describe_project(self, context, name, **kwargs): @@ -137,6 +156,8 @@ class AdminController(object): def register_project(self, context, name, manager_user, description=None, member_users=None, **kwargs): """Creates a new project""" + LOG.audit(_("Create project %s managed by %s"), name, manager_user, + context=context) return project_dict( manager.AuthManager().create_project( name, @@ -146,6 +167,7 @@ class AdminController(object): def deregister_project(self, context, name): """Permanently deletes a project.""" + LOG.audit(_("Delete project: %s"), name, context=context) manager.AuthManager().delete_project(name) return True @@ -159,11 +181,15 @@ class AdminController(object): **kwargs): """Add or remove a user from a project.""" if operation == 'add': + LOG.audit(_("Adding user %s to project %s"), user, project, + context=context) manager.AuthManager().add_to_project(user, project) elif operation == 'remove': + LOG.audit(_("Removing user %s from project %s"), user, project, + context=context) manager.AuthManager().remove_from_project(user, project) else: - raise exception.ApiError('operation must be add or remove') + raise exception.ApiError(_('operation must be add or remove')) return True # FIXME(vish): these host commands don't work yet, perhaps some of the diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index a90fbeb0c..d0b417db1 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -20,13 +20,13 @@ APIRequest class """ -import logging import re # TODO(termie): replace minidom with etree from xml.dom import minidom -_log = logging.getLogger("api") -_log.setLevel(logging.DEBUG) +from nova import log as logging + +LOG = logging.getLogger("nova.api.request") _c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))') @@ -94,7 +94,7 @@ class APIRequest(object): except AttributeError: _error = _('Unsupported API request: controller = %s,' 'action = %s') % (self.controller, self.action) - _log.warning(_error) + LOG.exception(_error) # TODO: Raise custom exception, trap in apiserver, # and reraise as 400 error. raise Exception(_error) @@ -142,7 +142,7 @@ class APIRequest(object): response = xml.toxml() xml.unlink() - _log.debug(response) + LOG.debug(response) return response def _render_dict(self, xml, el, data): @@ -151,7 +151,7 @@ class APIRequest(object): val = data[key] el.appendChild(self._render_data(xml, key, val)) except: - _log.debug(data) + LOG.debug(data) raise def _render_data(self, xml, el_name, data): diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 9fb6307a8..d0db08db7 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -24,17 +24,16 @@ datastore. import base64 import datetime -import logging +import IPy import re import os from nova import context -import IPy - from nova import crypto from nova import db from nova import exception from nova import flags +from nova import log as logging from nova import quota from nova import rpc from nova import utils @@ -45,6 +44,8 @@ from nova.compute import instance_types FLAGS = flags.FLAGS flags.DECLARE('storage_availability_zone', 'nova.volume.manager') +LOG = logging.getLogger("nova.api.cloud") + InvalidInputException = exception.InvalidInputException @@ -280,6 +281,7 @@ class CloudController(object): return {'keypairsSet': result} def create_key_pair(self, context, key_name, **kwargs): + LOG.audit(_("Create key pair %s"), key_name, context=context) data = _gen_key(context, context.user.id, key_name) return {'keyName': key_name, 'keyFingerprint': data['fingerprint'], @@ -287,6 +289,7 @@ class CloudController(object): # TODO(vish): when context is no longer an object, pass it here def delete_key_pair(self, context, key_name, **kwargs): + LOG.audit(_("Delete key pair %s"), key_name, context=context) try: db.key_pair_destroy(context, context.user.id, key_name) except exception.NotFound: @@ -393,6 +396,8 @@ class CloudController(object): return False def revoke_security_group_ingress(self, context, group_name, **kwargs): + LOG.audit(_("Revoke security group ingress %s"), group_name, + context=context) self.compute_api.ensure_default_security_group(context) security_group = db.security_group_get_by_name(context, context.project_id, @@ -419,6 +424,8 @@ class CloudController(object): # for these operations, so support for newer API versions # is sketchy. def authorize_security_group_ingress(self, context, group_name, **kwargs): + LOG.audit(_("Authorize security group ingress %s"), group_name, + context=context) self.compute_api.ensure_default_security_group(context) security_group = db.security_group_get_by_name(context, context.project_id, @@ -455,6 +462,7 @@ class CloudController(object): return source_project_id def create_security_group(self, context, group_name, group_description): + LOG.audit(_("Create Security Group %s"), group_name, context=context) self.compute_api.ensure_default_security_group(context) if db.security_group_exists(context, context.project_id, group_name): raise exception.ApiError(_('group %s already exists') % group_name) @@ -469,6 +477,7 @@ class CloudController(object): group_ref)]} def delete_security_group(self, context, group_name, **kwargs): + LOG.audit(_("Delete security group %s"), group_name, context=context) security_group = db.security_group_get_by_name(context, context.project_id, group_name) @@ -476,6 +485,8 @@ class CloudController(object): return True def get_console_output(self, context, instance_id, **kwargs): + LOG.audit(_("Get console output for instance %s"), instance_id, + context=context) # instance_id is passed in as a list of instances ec2_id = instance_id[0] internal_id = ec2_id_to_internal_id(ec2_id) @@ -539,10 +550,12 @@ class CloudController(object): return v def create_volume(self, context, size, **kwargs): + LOG.audit(_("Create volume of %s GB"), size, context=context) # check quota if quota.allowed_volumes(context, 1, size) < 1: - logging.warn("Quota exceeeded for %s, tried to create %sG volume", - context.project_id, size) + LOG.warn(_("Quota exceeeded for project %s, tried to create " + "%sG volume"), context.project_id, size, + context=context) raise quota.QuotaError("Volume quota exceeded. You cannot " "create a volume of size %s" % size) vol = {} @@ -568,6 +581,8 @@ class CloudController(object): return {'volumeSet': [self._format_volume(context, dict(volume_ref))]} def attach_volume(self, context, volume_id, instance_id, device, **kwargs): + LOG.audit(_("Attach volume %s to instacne %s at %s"), volume_id, + instance_id, device, context=context) volume_ref = db.volume_get_by_ec2_id(context, volume_id) if not re.match("^/dev/[a-z]d[a-z]+$", device): raise exception.ApiError(_("Invalid device specified: %s. " @@ -594,6 +609,7 @@ class CloudController(object): 'volumeId': volume_ref['id']} def detach_volume(self, context, volume_id, **kwargs): + LOG.audit("Detach volume %s", volume_id, context=context) volume_ref = db.volume_get_by_ec2_id(context, volume_id) instance_ref = db.volume_get_instance(context.elevated(), volume_ref['id']) @@ -728,11 +744,11 @@ class CloudController(object): return {'addressesSet': addresses} def allocate_address(self, context, **kwargs): + LOG.audit(_("Allocate address"), context=context) # check quota if quota.allowed_floating_ips(context, 1) < 1: - logging.warn(_("Quota exceeeded for %s, tried to allocate " - "address"), - context.project_id) + LOG.warn(_("Quota exceeeded for %s, tried to allocate address"), + context.project_id, context=context) raise quota.QuotaError(_("Address quota exceeded. You cannot " "allocate any more addresses")) # NOTE(vish): We don't know which network host should get the ip @@ -746,6 +762,7 @@ class CloudController(object): return {'addressSet': [{'publicIp': public_ip}]} def release_address(self, context, public_ip, **kwargs): + LOG.audit(_("Release address %s"), public_ip, context=context) floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) # NOTE(vish): We don't know which network host should get the ip # when we deallocate, so just send it to any one. This @@ -758,6 +775,8 @@ class CloudController(object): return {'releaseResponse': ["Address released."]} def associate_address(self, context, instance_id, public_ip, **kwargs): + LOG.audit(_("Associate address %s to instance %s"), public_ip, + instance_id, context=context) internal_id = ec2_id_to_internal_id(instance_id) instance_ref = self.compute_api.get_instance(context, internal_id) fixed_address = db.instance_get_fixed_address(context, @@ -775,6 +794,7 @@ class CloudController(object): return {'associateResponse': ["Address associated."]} def disassociate_address(self, context, public_ip, **kwargs): + LOG.audit(_("Disassociate address %s"), public_ip, context=context) floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) # NOTE(vish): Get the topic from the host name of the network of # the associated fixed ip. @@ -811,7 +831,7 @@ class CloudController(object): def terminate_instances(self, context, instance_id, **kwargs): """Terminate each instance in instance_id, which is a list of ec2 ids. instance_id is a kwarg so its name cannot be modified.""" - logging.debug("Going to start terminating instances") + LOG.debug(_("Going to start terminating instances")) for ec2_id in instance_id: internal_id = ec2_id_to_internal_id(ec2_id) self.compute_api.delete_instance(context, internal_id) @@ -819,6 +839,7 @@ class CloudController(object): def reboot_instances(self, context, instance_id, **kwargs): """instance_id is a list of instance ids""" + LOG.audit(_("Reboot instance %r"), instance_id, context=context) for ec2_id in instance_id: internal_id = ec2_id_to_internal_id(ec2_id) self.compute_api.reboot(context, internal_id) @@ -850,6 +871,7 @@ class CloudController(object): def delete_volume(self, context, volume_id, **kwargs): # TODO: return error if not authorized + LOG.audit(_("Deleting volume %s"), volume_id, context=context) volume_ref = db.volume_get_by_ec2_id(context, volume_id) if volume_ref['status'] != "available": raise exception.ApiError(_("Volume status must be available")) @@ -871,6 +893,7 @@ class CloudController(object): return {'imagesSet': images} def deregister_image(self, context, image_id, **kwargs): + LOG.audit("De-registering image %s", image_id, context=context) self.image_service.deregister(context, image_id) return {'imageId': image_id} @@ -878,7 +901,8 @@ class CloudController(object): if image_location is None and 'name' in kwargs: image_location = kwargs['name'] image_id = self.image_service.register(context, image_location) - logging.debug("Registered %s as %s" % (image_location, image_id)) + LOG.audit(_("Registered image %s with id %s"), image_location, + image_id, context=context) return {'imageId': image_id} def describe_image_attribute(self, context, image_id, attribute, **kwargs): @@ -906,6 +930,7 @@ class CloudController(object): raise exception.ApiError(_('only group "all" is supported')) if not operation_type in ['add', 'remove']: raise exception.ApiError(_('operation_type must be add or remove')) + LOG.audit(_("Updating image %s publicity"), image_id, context=context) return self.image_service.modify(context, image_id, operation_type) def update_image(self, context, image_id, **kwargs): diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index a57a6698a..848f0b034 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -18,15 +18,15 @@ """Metadata request handler.""" -import logging - import webob.dec import webob.exc +from nova import log as logging from nova import flags from nova.api.ec2 import cloud +LOG = logging.getLogger('nova.api.ec2.metadata') FLAGS = flags.FLAGS @@ -72,8 +72,7 @@ class MetadataRequestHandler(object): remote_address = req.headers.get('X-Forwarded-For', remote_address) meta_data = cc.get_metadata(remote_address) if meta_data is None: - logging.error(_('Failed to get metadata for ip: %s') % - remote_address) + LOG.error(_('Failed to get metadata for ip: %s'), remote_address) raise webob.exc.HTTPNotFound() data = self.lookup(req.path_info, meta_data) if data is None: diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index a1430caed..7e1c03d9f 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -20,28 +20,24 @@ WSGI middleware for OpenStack API controllers. """ -import time - -import logging import routes -import traceback import webob.dec import webob.exc import webob -from nova import context from nova import flags +from nova import log as logging from nova import utils from nova import wsgi from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import flavors from nova.api.openstack import images -from nova.api.openstack import ratelimiting from nova.api.openstack import servers from nova.api.openstack import sharedipgroups +LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS flags.DEFINE_string('os_api_auth', 'nova.api.openstack.auth.AuthMiddleware', @@ -71,8 +67,7 @@ class API(wsgi.Middleware): try: return req.get_response(self.application) except Exception as ex: - logging.warn(_("Caught error: %s") % str(ex)) - logging.error(traceback.format_exc()) + LOG.exception(_("Caught error: %s"), str(ex)) exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) return faults.Fault(exc) @@ -88,7 +83,7 @@ class APIRouter(wsgi.Router): server_members = {'action': 'POST'} if FLAGS.allow_admin_api: - logging.debug("Including admin operations in API.") + LOG.debug("Including admin operations in API.") server_members['pause'] = 'POST' server_members['unpause'] = 'POST' server_members["diagnostics"] = "GET" -- cgit From b9576a9f73195656f4a0a1327cd6bee3c4a6b6c9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 00:26:41 -0500 Subject: Final few log tweaks, i18n, levels, including contexts, etc. --- nova/api/ec2/__init__.py | 14 ++++++++------ nova/api/ec2/cloud.py | 4 ++-- nova/api/openstack/__init__.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 4dd2e55cd..2fa1f636c 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -130,7 +130,7 @@ class Lockout(wsgi.Middleware): failures_key = "authfailures-%s" % access_key failures = int(self.mc.get(failures_key) or 0) if failures >= FLAGS.lockout_attempts: - detail = "Too many failed authentications." + detail = _("Too many failed authentications.") raise webob.exc.HTTPForbidden(detail=detail) res = req.get_response(self.application) if res.status_int == 403: @@ -139,13 +139,14 @@ class Lockout(wsgi.Middleware): # NOTE(vish): To use incr, failures has to be a string. self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60) elif failures >= FLAGS.lockout_attempts: - LOG.warn('Access key %s has had %d failed authentications' - ' and will be locked out for %d minutes.', + LOG.warn(_('Access key %s has had %d failed authentications' + ' and will be locked out for %d minutes.'), access_key, failures, FLAGS.lockout_minutes) self.mc.set(failures_key, str(failures), time=FLAGS.lockout_minutes * 60) return res + class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'ec2.context' to WSGI environ.""" @@ -297,8 +298,9 @@ class Authorizer(wsgi.Middleware): if self._matches_any_role(context, allowed_roles): return self.application else: - LOG.audit("Unauthorized request for controller=%s and action=%s", - controller_name, action, context=context) + LOG.audit(_("Unauthorized request for controller=%s " + "and action=%s"), controller_name, action, + context=context) raise webob.exc.HTTPUnauthorized() def _matches_any_role(self, context, roles): @@ -337,7 +339,7 @@ class Executor(wsgi.Application): LOG.info(_('NotFound raised: %s'), str(ex), context=context) return self._error(req, context, type(ex).__name__, str(ex)) except exception.ApiError as ex: - LOG.exception('ApiError raised', context=context) + LOG.exception(_('ApiError raised: %s'), str(ex), context=context) if ex.code: return self._error(req, context, ex.code, str(ex)) else: diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index d0db08db7..2fb4455f2 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -609,7 +609,7 @@ class CloudController(object): 'volumeId': volume_ref['id']} def detach_volume(self, context, volume_id, **kwargs): - LOG.audit("Detach volume %s", volume_id, context=context) + LOG.audit(_("Detach volume %s"), volume_id, context=context) volume_ref = db.volume_get_by_ec2_id(context, volume_id) instance_ref = db.volume_get_instance(context.elevated(), volume_ref['id']) @@ -893,7 +893,7 @@ class CloudController(object): return {'imagesSet': images} def deregister_image(self, context, image_id, **kwargs): - LOG.audit("De-registering image %s", image_id, context=context) + LOG.audit(_("De-registering image %s"), image_id, context=context) self.image_service.deregister(context, image_id) return {'imageId': image_id} diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7e1c03d9f..ad203c51f 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -83,7 +83,7 @@ class APIRouter(wsgi.Router): server_members = {'action': 'POST'} if FLAGS.allow_admin_api: - LOG.debug("Including admin operations in API.") + LOG.debug(_("Including admin operations in API.")) server_members['pause'] = 'POST' server_members['unpause'] = 'POST' server_members["diagnostics"] = "GET" -- cgit From 8bbcadafbc25c7ab478d6143293232f2cea24411 Mon Sep 17 00:00:00 2001 From: Eric Day Date: Tue, 4 Jan 2011 09:44:15 -0800 Subject: Removed UUID keys for instance and volume. --- nova/api/ec2/cloud.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index a6818855d..b1eaafc8b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -72,13 +72,17 @@ def _gen_key(context, user_id, key_name): def ec2_id_to_id(ec2_id): - """Convert an ec2 ID and an instance ID.""" - return ec2_id[2:] + """Convert an ec2 ID (i-[base 36 number]) to an instance id (int)""" + return int(ec2_id[2:], 36) def id_to_ec2_id(instance_id): - """Convert an instance ID to an ec2 ID.""" - return "i-%s" % instance_id + """Convert an instance ID (int) to an ec2 ID (i-[base 36 number])""" + digits = [] + while instance_id != 0: + instance_id, remainder = divmod(instance_id, 36) + digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) + return "i-%s" % ''.join(reversed(digits)) class CloudController(object): -- cgit From def5583469bd265c9107ed54d461441bc6303151 Mon Sep 17 00:00:00 2001 From: Eric Day Date: Wed, 5 Jan 2011 09:50:19 -0800 Subject: Split internal API get calls to get and get_all, where the former takes an ID and returns one resource, and the latter can optionally take a filter and return a list of resources. --- nova/api/ec2/cloud.py | 9 +++++---- nova/api/openstack/servers.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b1eaafc8b..0c0027287 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -119,7 +119,8 @@ class CloudController(object): def _get_mpi_data(self, context, project_id): result = {} - for instance in self.compute_api.get(context, project_id=project_id): + for instance in self.compute_api.get_all(context, + project_id=project_id): if instance['fixed_ip']: line = '%s slots=%d' % (instance['fixed_ip']['address'], instance['vcpus']) @@ -141,7 +142,7 @@ class CloudController(object): def get_metadata(self, address): ctxt = context.get_admin_context() - instance_ref = self.compute_api.get(ctxt, fixed_ip=address) + instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address) if instance_ref is None: return None mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) @@ -493,7 +494,7 @@ class CloudController(object): "output": base64.b64encode(output)} def describe_volumes(self, context, volume_id=None, **kwargs): - volumes = self.volume_api.get(context) + volumes = self.volume_api.get_all(context) # NOTE(vish): volume_id is an optional list of volume ids to filter by. volumes = [self._format_volume(context, v) for v in volumes if volume_id is None or v['id'] in volume_id] @@ -597,7 +598,7 @@ class CloudController(object): def _format_instances(self, context, **kwargs): reservations = {} - instances = self.compute_api.get(context, **kwargs) + instances = self.compute_api.get_all(context, **kwargs) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 6be24629a..ce64ac7ad 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -96,7 +96,7 @@ class Controller(wsgi.Controller): entity_maker - either _translate_detail_keys or _translate_keys """ - instance_list = self.compute_api.get(req.environ['nova.context']) + instance_list = self.compute_api.get_all(req.environ['nova.context']) limited_list = common.limited(instance_list, req) res = [entity_maker(inst)['server'] for inst in limited_list] return dict(servers=res) -- cgit From 4a7105898e45cf5b6393f68d8d2d921dd218724b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 6 Jan 2011 18:35:18 +0000 Subject: Fix format_run_instances to pass in reservation id as a kwarg --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 0c0027287..6619b5452 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -592,7 +592,7 @@ class CloudController(object): return {'reservationSet': self._format_instances(context)} def _format_run_instances(self, context, reservation_id): - i = self._format_instances(context, reservation_id) + i = self._format_instances(context, reservation_id=reservation_id) assert len(i) == 1 return i[0] -- cgit From 3dd9c56477078114bcd9b20a49a3413615539103 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 6 Jan 2011 17:50:44 -0600 Subject: Review feedback --- nova/api/openstack/servers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ce64ac7ad..ca2e5888b 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -222,4 +222,5 @@ class Controller(wsgi.Controller): def actions(self, req, id): """Permit Admins to retrieve server actions.""" ctxt = req.environ["nova.context"] - return self.compute_api.get_actions(ctxt, id) + items = self.compute_api.get_actions(ctxt, id) + return dict(actions=items) -- cgit From 6a162512cac5eafdbe46ba4df6117bfed6f40e4b Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 6 Jan 2011 18:12:22 -0600 Subject: Review feedback --- nova/api/openstack/servers.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ca2e5888b..9a62f2b96 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -223,4 +223,8 @@ class Controller(wsgi.Controller): """Permit Admins to retrieve server actions.""" ctxt = req.environ["nova.context"] items = self.compute_api.get_actions(ctxt, id) + # TODO(jk0): Do not do pre-serialization here once the default + # serializer is updated + for item in items: + item["date"] = str(item["date"]) return dict(actions=items) -- cgit From a0edf5a7ba372419ebfed987b8585171e7167e48 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 6 Jan 2011 18:18:28 -0600 Subject: Review feedback --- nova/api/openstack/servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 9a62f2b96..3ee161502 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -226,5 +226,5 @@ class Controller(wsgi.Controller): # TODO(jk0): Do not do pre-serialization here once the default # serializer is updated for item in items: - item["date"] = str(item["date"]) + item["created_at"] = str(item["created_at"]) return dict(actions=items) -- cgit From 59f8986df4d78f61528162e65f560064febef7af Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 6 Jan 2011 18:59:15 -0600 Subject: Review feedback --- nova/api/openstack/servers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3ee161502..9a0c417ae 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -223,8 +223,12 @@ class Controller(wsgi.Controller): """Permit Admins to retrieve server actions.""" ctxt = req.environ["nova.context"] items = self.compute_api.get_actions(ctxt, id) + actions = [] # TODO(jk0): Do not do pre-serialization here once the default # serializer is updated for item in items: - item["created_at"] = str(item["created_at"]) - return dict(actions=items) + actions.append(dict( + created_at=str(item.created_at), + action=item.action, + error=item.error)) + return dict(actions=actions) -- cgit From d757a1a10f0cbc5a3c0f5b1427d1d526584298ce Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 7 Jan 2011 23:22:52 +0000 Subject: Return region info in the proper format. --- nova/api/ec2/cloud.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index ccce83b84..b34488731 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -249,6 +249,7 @@ class CloudController(object): FLAGS.cc_host, FLAGS.cc_port, FLAGS.ec2_suffix)}] + return {'regionInfo': regions} def describe_snapshots(self, context, -- cgit From 6d05c3e5d9112aead1db23e942f24605a3301af9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 9 Jan 2011 23:01:10 -0800 Subject: fix describe instances + test --- nova/api/ec2/cloud.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b34488731..fd3141a97 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -25,7 +25,6 @@ datastore. import base64 import datetime import IPy -import re import os from nova import compute @@ -35,7 +34,6 @@ from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import quota from nova import network from nova import rpc from nova import utils @@ -603,19 +601,23 @@ class CloudController(object): return [{label: x} for x in lst] def describe_instances(self, context, **kwargs): - return self._format_describe_instances(context) + return self._format_describe_instances(context, **kwargs) - def _format_describe_instances(self, context): - return {'reservationSet': self._format_instances(context)} + def _format_describe_instances(self, context, **kwargs): + return {'reservationSet': self._format_instances(context, **kwargs)} def _format_run_instances(self, context, reservation_id): i = self._format_instances(context, reservation_id=reservation_id) assert len(i) == 1 return i[0] - def _format_instances(self, context, **kwargs): + def _format_instances(self, context, instance_id=None, **kwargs): reservations = {} instances = self.compute_api.get_all(context, **kwargs) + # NOTE(vish): instance_id is an optional list of ids to filter by + if instance_id: + instance_id = [ec2_id_to_id(x) for x in instance_id] + instances = [x for x in instances if x['id'] in instance_id] for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: -- cgit From c8566628d4c15bcaf16baf8fca2a31528e7eac13 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 9 Jan 2011 23:53:51 -0800 Subject: optimize to call get if instance_id is specified since most of the time people will just be requesting one id --- nova/api/ec2/cloud.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index fd3141a97..9166301c7 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -613,11 +613,12 @@ class CloudController(object): def _format_instances(self, context, instance_id=None, **kwargs): reservations = {} - instances = self.compute_api.get_all(context, **kwargs) # NOTE(vish): instance_id is an optional list of ids to filter by if instance_id: instance_id = [ec2_id_to_id(x) for x in instance_id] - instances = [x for x in instances if x['id'] in instance_id] + instances = [self.compute_api.get(context, x) for x in instance_id] + else: + instances = self.compute_api.get_all(context, **kwargs) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: -- cgit From ec8e7773b79ed52aa2950db185ead881c77632f7 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 10 Jan 2011 10:57:13 -0500 Subject: Fix describe_availablity_zones versobse. --- nova/api/ec2/cloud.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 5360f2de7..359498d11 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -44,6 +44,7 @@ from nova.compute import instance_types FLAGS = flags.FLAGS +flags.DECLARE('service_down_time', 'nova.scheduler.driver') LOG = logging.getLogger("nova.api.cloud") @@ -200,7 +201,7 @@ class CloudController(object): 'zoneState': 'available'}]} services = db.service_get_all(context) - now = db.get_time() + now = datetime.datetime.utcnow() hosts = [] for host in [service['host'] for service in services]: if not host in hosts: -- cgit