summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorEd Leafe <ed@leafe.com>2011-01-12 13:22:01 -0600
committerEd Leafe <ed@leafe.com>2011-01-12 13:22:01 -0600
commitea1f5ad611a84931dc4a02b3c4bf2c6ef89f7360 (patch)
treefd74b5aa6b37916037e6f41fb9c6145ffe37f387 /nova/api
parent5b5fa0eb4b9ffd597b6e49b2cef5a2ad9028d55f (diff)
parent3d57735caf78fd421da6e660c4d56c635706fa7d (diff)
cleaned up prior merge mess
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/ec2/cloud.py57
-rw-r--r--nova/api/openstack/__init__.py11
-rw-r--r--nova/api/openstack/backup_schedules.py6
-rw-r--r--nova/api/openstack/common.py24
-rw-r--r--nova/api/openstack/images.py19
-rw-r--r--nova/api/openstack/servers.py38
-rw-r--r--nova/api/openstack/shared_ip_groups.py (renamed from nova/api/openstack/sharedipgroups.py)10
7 files changed, 147 insertions, 18 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 99a9677c4..832426b94 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -132,6 +132,21 @@ 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_availability_zone_by_host(self, context, host):
+ services = db.service_get_all_by_host(context, host)
+ if len(services) > 0:
+ return services[0]['availability_zone']
+ return 'unknown zone'
+
def get_metadata(self, address):
ctxt = context.get_admin_context()
instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address)
@@ -144,6 +159,8 @@ class CloudController(object):
else:
keys = ''
hostname = instance_ref['hostname']
+ host = instance_ref['host']
+ availability_zone = self._get_availability_zone_by_host(ctxt, host)
floating_ip = db.instance_get_floating_address(ctxt,
instance_ref['id'])
ec2_id = id_to_ec2_id(instance_ref['id'])
@@ -166,8 +183,7 @@ class CloudController(object):
'local-hostname': hostname,
'local-ipv4': address,
'kernel-id': instance_ref['kernel_id'],
- # TODO(vish): real zone
- 'placement': {'availability-zone': 'nova'},
+ 'placement': {'availability-zone': availability_zone},
'public-hostname': hostname,
'public-ipv4': floating_ip or '',
'public-keys': keys,
@@ -191,8 +207,26 @@ class CloudController(object):
return self._describe_availability_zones(context, **kwargs)
def _describe_availability_zones(self, context, **kwargs):
- return {'availabilityZoneInfo': [{'zoneName': 'nova',
- 'zoneState': 'available'}]}
+ enabled_services = db.service_get_all(context)
+ disabled_services = db.service_get_all(context, True)
+ available_zones = []
+ for zone in [service.availability_zone for service
+ in enabled_services]:
+ if not zone in available_zones:
+ available_zones.append(zone)
+ not_available_zones = []
+ for zone in [service.availability_zone for service in disabled_services
+ if not service['availability_zone'] in available_zones]:
+ if not zone in not_available_zones:
+ not_available_zones.append(zone)
+ result = []
+ for zone in available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': "available"})
+ for zone in not_available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': "not available"})
+ return {'availabilityZoneInfo': result}
def _describe_availability_zones_verbose(self, context, **kwargs):
rv = {'availabilityZoneInfo': [{'zoneName': 'nova',
@@ -399,8 +433,8 @@ class CloudController(object):
criteria = self._revoke_rule_args_to_dict(context, **kwargs)
if criteria == None:
- raise exception.ApiError(_("No rule for the specified "
- "parameters."))
+ raise exception.ApiError(_("Not enough parameters to build a "
+ "valid rule."))
for rule in security_group.rules:
match = True
@@ -427,6 +461,9 @@ class CloudController(object):
group_name)
values = self._revoke_rule_args_to_dict(context, **kwargs)
+ if values is None:
+ raise exception.ApiError(_("Not enough parameters to build a "
+ "valid rule."))
values['parent_group_id'] = security_group.id
if self._security_group_rule_exists(security_group, values):
@@ -498,6 +535,11 @@ class CloudController(object):
"Timestamp": now,
"output": base64.b64encode(output)}
+ def get_ajax_console(self, context, instance_id, **kwargs):
+ ec2_id = instance_id[0]
+ internal_id = ec2_id_to_id(ec2_id)
+ return self.compute_api.get_ajax_console(context, internal_id)
+
def describe_volumes(self, context, volume_id=None, **kwargs):
volumes = self.volume_api.get_all(context)
# NOTE(vish): volume_id is an optional list of volume ids to filter by.
@@ -646,6 +688,9 @@ class CloudController(object):
i['amiLaunchIndex'] = instance['launch_index']
i['displayName'] = instance['display_name']
i['displayDescription'] = instance['display_description']
+ host = instance['host']
+ zone = self._get_availability_zone_by_host(context, host)
+ i['placement'] = {'availabilityZone': zone}
if instance['reservation_id'] not in reservations:
r = {}
r['reservationId'] = instance['reservation_id']
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 7b999c87b..f96e2af91 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -35,7 +35,7 @@ from nova.api.openstack import consoles
from nova.api.openstack import flavors
from nova.api.openstack import images
from nova.api.openstack import servers
-from nova.api.openstack import sharedipgroups
+from nova.api.openstack import shared_ip_groups
LOG = logging.getLogger('nova.api.openstack')
@@ -48,6 +48,10 @@ flags.DEFINE_string('os_api_ratelimiting',
'nova.api.openstack.ratelimiting.RateLimitingMiddleware',
'Default ratelimiting implementation for the Openstack API')
+flags.DEFINE_string('os_krm_mapping_file',
+ 'krm_mapping.json',
+ 'Location of OpenStack Flavor/OS:EC2 Kernel/Ramdisk/Machine JSON file.')
+
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
@@ -110,8 +114,9 @@ class APIRouter(wsgi.Router):
collection={'detail': 'GET'})
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
collection={'detail': 'GET'})
- mapper.resource("sharedipgroup", "sharedipgroups",
- controller=sharedipgroups.Controller())
+ mapper.resource("shared_ip_group", "shared_ip_groups",
+ collection={'detail': 'GET'},
+ controller=shared_ip_groups.Controller())
super(APIRouter, self).__init__(mapper)
diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index fcc07bdd3..197125d86 100644
--- a/nova/api/openstack/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -15,7 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
import time
+
from webob import exc
from nova import wsgi
@@ -46,8 +48,8 @@ class Controller(wsgi.Controller):
def create(self, req, server_id):
""" No actual update method required, since the existing API allows
both create and update through a POST """
- return faults.Fault(exc.HTTPNotFound())
+ return faults.Fault(exc.HTTPNotImplemented())
def delete(self, req, server_id, id):
""" Deletes an existing backup schedule """
- return faults.Fault(exc.HTTPNotFound())
+ return faults.Fault(exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index ac0572c96..037ed47a0 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from nova import exception
+
def limited(items, req):
"""Return a slice of items according to requested offset and limit.
@@ -34,3 +36,25 @@ def limited(items, req):
limit = min(1000, limit)
range_end = offset + limit
return items[offset:range_end]
+
+
+def get_image_id_from_image_hash(image_service, context, image_hash):
+ """Given an Image ID Hash, return an objectstore Image ID.
+
+ image_service - reference to objectstore compatible image service.
+ context - security context for image service requests.
+ image_hash - hash of the image ID.
+ """
+
+ # FIX(sandy): This is terribly inefficient. It pulls all images
+ # from objectstore in order to find the match. ObjectStore
+ # should have a numeric counterpart to the string ID.
+ try:
+ items = image_service.detail(context)
+ except NotImplementedError:
+ items = image_service.index(context)
+ for image in items:
+ image_id = image['imageId']
+ if abs(hash(image_id)) == int(image_hash):
+ return image_id
+ raise exception.NotFound(image_hash)
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 0b239aab8..a5f55a489 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
+
from webob import exc
from nova import compute
@@ -26,6 +28,7 @@ from nova.api.openstack import common
from nova.api.openstack import faults
import nova.image.service
+
FLAGS = flags.FLAGS
@@ -88,6 +91,12 @@ def _filter_keys(item, keys):
return dict((k, v) for k, v in item.iteritems() if k in keys)
+def _convert_image_id_to_hash(image):
+ image_id = abs(hash(image['imageId']))
+ image['imageId'] = image_id
+ image['id'] = image_id
+
+
class Controller(wsgi.Controller):
_serialization_metadata = {
@@ -112,6 +121,9 @@ class Controller(wsgi.Controller):
items = self._service.detail(req.environ['nova.context'])
except NotImplementedError:
items = self._service.index(req.environ['nova.context'])
+ for image in items:
+ _convert_image_id_to_hash(image)
+
items = common.limited(items, req)
items = [_translate_keys(item) for item in items]
items = [_translate_status(item) for item in items]
@@ -119,7 +131,12 @@ class Controller(wsgi.Controller):
def show(self, req, id):
"""Return data about the given image id"""
- return dict(image=self._service.show(req.environ['nova.context'], id))
+ image_id = common.get_image_id_from_image_hash(self._service,
+ req.environ['nova.context'], id)
+
+ image = self._service.show(req.environ['nova.context'], image_id)
+ _convert_image_id_to_hash(image)
+ return dict(image=image)
def delete(self, req, id):
# Only public images are supported for now.
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 764c843ac..8cbcebed2 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -15,14 +15,17 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import traceback
from webob import exc
from nova import compute
from nova import exception
+from nova import flags
from nova import log as logging
from nova import wsgi
+from nova import utils
from nova.api.openstack import common
from nova.api.openstack import faults
from nova.auth import manager as auth_manager
@@ -35,6 +38,9 @@ LOG = logging.getLogger('server')
LOG.setLevel(logging.DEBUG)
+FLAGS = flags.FLAGS
+
+
def _translate_detail_keys(inst):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
@@ -44,7 +50,7 @@ def _translate_detail_keys(inst):
power_state.RUNNING: 'active',
power_state.BLOCKED: 'active',
power_state.SUSPENDED: 'suspended',
- power_state.PAUSED: 'error',
+ power_state.PAUSED: 'paused',
power_state.SHUTDOWN: 'active',
power_state.SHUTOFF: 'active',
power_state.CRASHED: 'error'}
@@ -81,6 +87,7 @@ class Controller(wsgi.Controller):
def __init__(self):
self.compute_api = compute.API()
+ self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
def index(self, req):
@@ -117,6 +124,18 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
+ def _get_kernel_ramdisk_from_image(self, image_id):
+ mapping_filename = FLAGS.os_krm_mapping_file
+
+ with open(mapping_filename) as f:
+ mapping = json.load(f)
+ if image_id in mapping:
+ return mapping[image_id]
+
+ raise exception.NotFound(
+ _("No entry for image '%s' in mapping file '%s'") %
+ (image_id, mapping_filename))
+
def create(self, req):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
@@ -125,10 +144,15 @@ class Controller(wsgi.Controller):
key_pair = auth_manager.AuthManager.get_key_pairs(
req.environ['nova.context'])[0]
+ image_id = common.get_image_id_from_image_hash(self._image_service,
+ req.environ['nova.context'], env['server']['imageId'])
+ kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(image_id)
instances = self.compute_api.create(
req.environ['nova.context'],
instance_types.get_by_flavor_id(env['server']['flavorId']),
- env['server']['imageId'],
+ image_id,
+ kernel_id=kernel_id,
+ ramdisk_id=ramdisk_id,
display_name=env['server']['name'],
display_description=env['server']['name'],
key_name=key_pair['name'],
@@ -161,6 +185,7 @@ class Controller(wsgi.Controller):
""" Multi-purpose method used to reboot, rebuild, and
resize a server """
input_dict = self._deserialize(req.body, req)
+ #TODO(sandy): rebuild/resize not supported.
try:
reboot_type = input_dict['reboot']['type']
except Exception:
@@ -261,6 +286,15 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
+ def get_ajax_console(self, req, id):
+ """ Returns a url to an instance's ajaxterm console. """
+ try:
+ self.compute_api.get_ajax_console(req.environ['nova.context'],
+ int(id))
+ except exception.NotFound:
+ return faults.Fault(exc.HTTPNotFound())
+ return exc.HTTPAccepted()
+
def diagnostics(self, req, id):
"""Permit Admins to retrieve server diagnostics."""
ctxt = req.environ["nova.context"]
diff --git a/nova/api/openstack/sharedipgroups.py b/nova/api/openstack/shared_ip_groups.py
index 845f5bead..bd3cc23a8 100644
--- a/nova/api/openstack/sharedipgroups.py
+++ b/nova/api/openstack/shared_ip_groups.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
+
from webob import exc
from nova import wsgi
@@ -29,7 +31,7 @@ def _translate_keys(inst):
def _translate_detail_keys(inst):
""" Coerces a shared IP group instance into proper dictionary format with
correctly mapped attributes """
- return dict(sharedIpGroup=inst)
+ return dict(sharedIpGroups=inst)
class Controller(wsgi.Controller):
@@ -54,12 +56,12 @@ class Controller(wsgi.Controller):
def delete(self, req, id):
""" Deletes a Shared IP Group """
- raise faults.Fault(exc.HTTPNotFound())
+ raise faults.Fault(exc.HTTPNotImplemented())
- def detail(self, req, id):
+ def detail(self, req):
""" Returns a complete list of Shared IP Groups """
return _translate_detail_keys({})
def create(self, req):
""" Creates a new Shared IP group """
- raise faults.Fault(exc.HTTPNotFound())
+ raise faults.Fault(exc.HTTPNotImplemented())