summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-07-27 12:06:59 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-07-27 12:06:59 -0400
commitdd72466c12ab6d64ee56257befe7e13cfd3ff5fa (patch)
treead6ed97de62398cbff519cdc18319b6959bbb9d6
parent6dead0b1706f3b2279504437aca63a3291dc2347 (diff)
parenta1152e7361ed887fc38de42e8fc770cf2f7df7cb (diff)
merge from trunk
-rw-r--r--nova/api/ec2/cloud.py70
-rw-r--r--nova/api/openstack/contrib/volumes.py53
-rw-r--r--nova/api/openstack/servers.py6
-rw-r--r--nova/api/openstack/views/servers.py45
-rw-r--r--nova/compute/api.py6
-rw-r--r--nova/compute/manager.py7
-rw-r--r--nova/db/sqlalchemy/api.py6
-rw-r--r--nova/network/api.py3
-rw-r--r--nova/tests/api/openstack/test_servers.py662
-rw-r--r--nova/tests/test_api.py6
-rw-r--r--nova/tests/test_cloud.py19
-rw-r--r--nova/tests/test_db_api.py86
12 files changed, 879 insertions, 90 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 10720a804..0294c09c5 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -539,15 +539,18 @@ class CloudController(object):
return rules
if 'ip_ranges' in kwargs:
rules = self._cidr_args_split(kwargs)
+ else:
+ rules = [kwargs]
finalset = []
for rule in rules:
if 'groups' in rule:
groups_values = self._groups_args_split(rule)
for groups_value in groups_values:
- finalset.append(groups_value)
+ final = self._rule_dict_last_step(context, **groups_value)
+ finalset.append(final)
else:
- if rule:
- finalset.append(rule)
+ final = self._rule_dict_last_step(context, **rule)
+ finalset.append(final)
return finalset
def _cidr_args_split(self, kwargs):
@@ -590,6 +593,9 @@ class CloudController(object):
db.security_group_get_by_name(context.elevated(),
source_project_id,
source_security_group_name)
+ notfound = exception.SecurityGroupNotFound
+ if not source_security_group:
+ raise notfound(security_group_id=source_security_group_name)
values['group_id'] = source_security_group['id']
elif cidr_ip:
# If this fails, it throws an exception. This is what we want.
@@ -628,7 +634,7 @@ class CloudController(object):
for rule in security_group.rules:
if 'group_id' in values:
if rule['group_id'] == values['group_id']:
- return True
+ return rule['id']
else:
is_duplicate = True
for key in ('cidr', 'from_port', 'to_port', 'protocol'):
@@ -636,7 +642,7 @@ class CloudController(object):
is_duplicate = False
break
if is_duplicate:
- return True
+ return rule['id']
return False
def revoke_security_group_ingress(self, context, group_name=None,
@@ -659,22 +665,30 @@ class CloudController(object):
msg = "Revoke security group ingress %s"
LOG.audit(_(msg), security_group['name'], context=context)
+ prevalues = []
+ try:
+ prevalues = kwargs['ip_permissions']
+ except KeyError:
+ prevalues.append(kwargs)
+ rule_id = None
+ for values in prevalues:
+ rulesvalues = self._rule_args_to_dict(context, values)
+ if not rulesvalues:
+ err = "%s Not enough parameters to build a valid rule"
+ raise exception.ApiError(_(err % rulesvalues))
- criteria = self._rule_args_to_dict(context, kwargs)[0]
- if criteria is None:
- raise exception.ApiError(_("Not enough parameters to build a "
- "valid rule."))
-
- for rule in security_group.rules:
- match = True
- for (k, v) in criteria.iteritems():
- if getattr(rule, k, False) != v:
- match = False
- 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
+ for values_for_rule in rulesvalues:
+ values_for_rule['parent_group_id'] = security_group.id
+ rule_id = self._security_group_rule_exists(security_group,
+ values_for_rule)
+ if rule_id:
+ db.security_group_rule_destroy(context, rule_id)
+ if rule_id:
+ # NOTE(vish): we removed a rule, so refresh
+ 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."))
# TODO(soren): This has only been tested with Boto as the client.
@@ -721,15 +735,17 @@ class CloudController(object):
postvalues.append(values_for_rule)
for values_for_rule in postvalues:
- security_group_rule = db.security_group_rule_create(context,
- values_for_rule)
+ security_group_rule = db.security_group_rule_create(
+ context,
+ values_for_rule)
- self.compute_api.trigger_security_group_rules_refresh(context,
- security_group_id=security_group['id'])
+ if postvalues:
+ self.compute_api.trigger_security_group_rules_refresh(
+ context,
+ security_group_id=security_group['id'])
+ return True
- group = db.security_group_get_by_name(context, context.project_id,
- security_group['name'])
- return True
+ raise exception.ApiError(_("No rule for the specified parameters."))
def _get_source_project_id(self, context, source_security_group_owner_id):
if source_security_group_owner_id:
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index 827e36097..867fe301e 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -22,10 +22,12 @@ from nova import compute
from nova import exception
from nova import flags
from nova import log as logging
+from nova import quota
from nova import volume
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import faults
+from nova.api.openstack import servers
LOG = logging.getLogger("nova.api.volumes")
@@ -297,6 +299,53 @@ class VolumeAttachmentController(object):
return {'volumeAttachments': res}
+class BootFromVolumeController(servers.ControllerV11):
+ """The boot from volume API controller for the Openstack API."""
+
+ def _create_instance(self, context, instance_type, image_href, **kwargs):
+ try:
+ return self.compute_api.create(context, instance_type,
+ image_href, **kwargs)
+ except quota.QuotaError as error:
+ self.helper._handle_quota_error(error)
+ except exception.ImageNotFound as error:
+ msg = _("Can not find requested image")
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ def create(self, req, body):
+ """ Creates a new server for a given user """
+ extra_values = None
+ try:
+
+ def get_kwargs(context, instance_type, image_href, **kwargs):
+ kwargs['context'] = context
+ kwargs['instance_type'] = instance_type
+ kwargs['image_href'] = image_href
+ return kwargs
+
+ extra_values, kwargs = self.helper.create_instance(req, body,
+ get_kwargs)
+
+ block_device_mapping = body['server'].get('block_device_mapping')
+ kwargs['block_device_mapping'] = block_device_mapping
+
+ instances = self._create_instance(**kwargs)
+ except faults.Fault, f:
+ return f
+
+ # We can only return 1 instance via the API, if we happen to
+ # build more than one... instances is a list, so we'll just
+ # use the first one..
+ inst = instances[0]
+ for key in ['instance_type', 'image_ref']:
+ inst[key] = extra_values[key]
+
+ builder = self._get_view_builder(req)
+ server = builder.build(inst, is_detail=True)
+ server['server']['adminPass'] = extra_values['password']
+ return server
+
+
class Volumes(extensions.ExtensionDescriptor):
def get_name(self):
return "Volumes"
@@ -330,4 +379,8 @@ class Volumes(extensions.ExtensionDescriptor):
collection_name='servers'))
resources.append(res)
+ res = extensions.ResourceExtension('os-volumes_boot',
+ BootFromVolumeController())
+ resources.append(res)
+
return resources
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 7bef1d9b2..d7cabb067 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -447,7 +447,6 @@ class ControllerV10(Controller):
def _action_rebuild(self, info, request, instance_id):
context = request.environ['nova.context']
- instance_id = int(instance_id)
try:
image_id = info["rebuild"]["imageId"]
@@ -459,7 +458,7 @@ class ControllerV10(Controller):
try:
self.compute_api.rebuild(context, instance_id, image_id)
except exception.BuildInProgress:
- msg = _("Instance %d is currently being rebuilt.") % instance_id
+ msg = _("Instance %s is currently being rebuilt.") % instance_id
LOG.debug(msg)
raise exc.HTTPConflict(explanation=msg)
@@ -560,7 +559,6 @@ class ControllerV11(Controller):
def _action_rebuild(self, info, request, instance_id):
context = request.environ['nova.context']
- instance_id = int(instance_id)
try:
image_href = info["rebuild"]["imageRef"]
@@ -581,7 +579,7 @@ class ControllerV11(Controller):
self.compute_api.rebuild(context, instance_id, image_href, name,
metadata, personalities)
except exception.BuildInProgress:
- msg = _("Instance %d is currently being rebuilt.") % instance_id
+ msg = _("Instance %s is currently being rebuilt.") % instance_id
LOG.debug(msg)
raise exc.HTTPConflict(explanation=msg)
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 7131db088..be25e1e40 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -50,7 +50,7 @@ class ViewBuilder(object):
else:
server = self._build_simple(inst)
- self._build_extra(server, inst)
+ self._build_extra(server['server'], inst)
return server
@@ -99,7 +99,6 @@ class ViewBuilder(object):
self._build_flavor(inst_dict, inst)
self._build_addresses(inst_dict, inst)
- inst_dict['uuid'] = inst['uuid']
return dict(server=inst_dict)
def _build_addresses(self, response, inst):
@@ -121,6 +120,9 @@ class ViewBuilder(object):
class ViewBuilderV10(ViewBuilder):
"""Model an Openstack API V1.0 server response."""
+ def _build_extra(self, response, inst):
+ response['uuid'] = inst['uuid']
+
def _build_image(self, response, inst):
if 'image_ref' in dict(inst):
image_ref = inst['image_ref']
@@ -145,18 +147,46 @@ class ViewBuilderV11(ViewBuilder):
self.image_builder = image_builder
self.base_url = base_url
+ def _build_detail(self, inst):
+ response = super(ViewBuilderV11, self)._build_detail(inst)
+ response['server']['created'] = inst['created_at']
+ response['server']['updated'] = inst['updated_at']
+ if 'status' in response['server']:
+ if response['server']['status'] == "ACTIVE":
+ response['server']['progress'] = 100
+ elif response['server']['status'] == "BUILD":
+ response['server']['progress'] = 0
+ return response
+
def _build_image(self, response, inst):
if 'image_ref' in dict(inst):
image_href = inst['image_ref']
- if str(image_href).isdigit():
- image_href = int(image_href)
- response['imageRef'] = image_href
+ image_id = str(common.get_id_from_href(image_href))
+ _bookmark = self.image_builder.generate_bookmark(image_id)
+ response['image'] = {
+ "id": image_id,
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": _bookmark,
+ },
+ ]
+ }
def _build_flavor(self, response, inst):
if "instance_type" in dict(inst):
flavor_id = inst["instance_type"]['flavorid']
flavor_ref = self.flavor_builder.generate_href(flavor_id)
- response["flavorRef"] = flavor_ref
+ flavor_bookmark = self.flavor_builder.generate_bookmark(flavor_id)
+ response["flavor"] = {
+ "id": str(common.get_id_from_href(flavor_ref)),
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ]
+ }
def _build_addresses(self, response, inst):
interfaces = inst.get('virtual_interfaces', [])
@@ -164,6 +194,7 @@ class ViewBuilderV11(ViewBuilder):
def _build_extra(self, response, inst):
self._build_links(response, inst)
+ response['uuid'] = inst['uuid']
def _build_links(self, response, inst):
href = self.generate_href(inst["id"])
@@ -180,7 +211,7 @@ class ViewBuilderV11(ViewBuilder):
},
]
- response["server"]["links"] = links
+ response["links"] = links
def generate_href(self, server_id):
"""Create an url that refers to a specific server id."""
diff --git a/nova/compute/api.py b/nova/compute/api.py
index c49c0d95c..d1e5647d2 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -561,6 +561,7 @@ class API(base.Base):
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{'method': 'refresh_provider_fw_rules', 'args': {}})
+ @scheduler_api.reroute_compute("update")
def update(self, context, instance_id, **kwargs):
"""Updates the instance in the datastore.
@@ -776,6 +777,7 @@ class API(base.Base):
raise exception.Error(_("Unable to find host for Instance %s")
% instance_id)
+ @scheduler_api.reroute_compute("backup")
def backup(self, context, instance_id, name, backup_type, rotation,
extra_properties=None):
"""Backup the given instance
@@ -792,6 +794,7 @@ class API(base.Base):
extra_properties=extra_properties)
return recv_meta
+ @scheduler_api.reroute_compute("snapshot")
def snapshot(self, context, instance_id, name, extra_properties=None):
"""Snapshot the given instance.
@@ -834,10 +837,12 @@ class API(base.Base):
params=params)
return recv_meta
+ @scheduler_api.reroute_compute("reboot")
def reboot(self, context, instance_id):
"""Reboot the given instance."""
self._cast_compute_message('reboot_instance', context, instance_id)
+ @scheduler_api.reroute_compute("rebuild")
def rebuild(self, context, instance_id, image_href, name=None,
metadata=None, files_to_inject=None):
"""Rebuild the given instance with the provided metadata."""
@@ -1024,6 +1029,7 @@ class API(base.Base):
"""Unrescue the given instance."""
self._cast_compute_message('unrescue_instance', context, instance_id)
+ @scheduler_api.reroute_compute("set_admin_password")
def set_admin_password(self, context, instance_id, password=None):
"""Set the root/admin password for the given instance."""
host = self._find_host(context, instance_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 31627fe3b..173469bc3 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -430,7 +430,10 @@ class ComputeManager(manager.SchedulerDependentManager):
image_ref = kwargs.get('image_ref')
instance_ref.image_ref = image_ref
instance_ref.injected_files = kwargs.get('injected_files', [])
- self.driver.spawn(instance_ref, network_info)
+ network_info = self.network_api.get_instance_nw_info(context,
+ instance_ref)
+ bd_mapping = self._setup_block_device_mapping(context, instance_id)
+ self.driver.spawn(instance_ref, network_info, bd_mapping)
self._update_image_ref(context, instance_id, image_ref)
self._update_launched_at(context, instance_id)
@@ -868,7 +871,7 @@ class ComputeManager(manager.SchedulerDependentManager):
"""
self.network_api.add_fixed_ip_to_instance(context, instance_id,
- network_id)
+ self.host, network_id)
self.inject_network_info(context, instance_id)
self.reset_network(context, instance_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index ba03cabbc..d7810098a 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1173,9 +1173,9 @@ def instance_get_active_by_window(context, begin, end=None):
"""Return instances that were continuously active over the given window"""
session = get_session()
query = session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('instance_type')).\
filter(models.Instance.launched_at < begin)
if end:
@@ -1269,7 +1269,7 @@ def instance_get_project_vpn(context, project_id):
options(joinedload_all('fixed_ips.floating_ips')).\
options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
diff --git a/nova/network/api.py b/nova/network/api.py
index 33a9fe239..247768722 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -164,9 +164,10 @@ class API(base.Base):
{'method': 'deallocate_for_instance',
'args': args})
- def add_fixed_ip_to_instance(self, context, instance_id, network_id):
+ def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
"""Adds a fixed ip to instance from specified network."""
args = {'instance_id': instance_id,
+ 'host': host,
'network_id': network_id}
rpc.cast(context, FLAGS.network_topic,
{'method': 'add_fixed_ip_to_instance',
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 56bc2cf10..4ca79434f 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -78,6 +78,12 @@ def return_virtual_interface_instance_nonexistant(interfaces):
return _return_virtual_interface_by_instance
+def return_server_with_attributes(**kwargs):
+ def _return_server(context, id):
+ return stub_instance(id, **kwargs)
+ return _return_server
+
+
def return_server_with_addresses(private, public):
def _return_server(context, id):
return stub_instance(id, private_address=private,
@@ -85,15 +91,15 @@ def return_server_with_addresses(private, public):
return _return_server
-def return_server_with_interfaces(interfaces):
+def return_server_with_power_state(power_state):
def _return_server(context, id):
- return stub_instance(id, interfaces=interfaces)
+ return stub_instance(id, power_state=power_state)
return _return_server
-def return_server_with_power_state(power_state):
+def return_server_with_uuid_and_power_state(power_state):
def _return_server(context, id):
- return stub_instance(id, power_state=power_state)
+ return stub_instance(id, uuid=FAKE_UUID, power_state=power_state)
return _return_server
@@ -143,14 +149,15 @@ def instance_addresses(context, instance_id):
def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
host=None, power_state=0, reservation_id="",
- uuid=FAKE_UUID, interfaces=None):
+ uuid=FAKE_UUID, image_ref="10", flavor_id="1",
+ interfaces=None):
metadata = []
metadata.append(InstanceMetadata(key='seq', value=id))
if interfaces is None:
interfaces = []
- inst_type = instance_types.get_instance_type_by_flavor_id(1)
+ inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id))
if public_addresses is None:
public_addresses = list()
@@ -165,10 +172,12 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
instance = {
"id": int(id),
+ "created_at": "2010-10-10T12:00:00Z",
+ "updated_at": "2010-11-11T11:00:00Z",
"admin_pass": "",
"user_id": user_id,
"project_id": "",
- "image_ref": "10",
+ "image_ref": image_ref,
"kernel_id": "",
"ramdisk_id": "",
"launch_index": 0,
@@ -224,6 +233,7 @@ class MockSetAdminPassword(object):
class ServersTest(test.TestCase):
def setUp(self):
+ self.maxDiff = None
super(ServersTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.reset_fake_data()
@@ -300,24 +310,274 @@ class ServersTest(test.TestCase):
self.assertEqual(res_dict['server']['name'], 'server1')
def test_get_server_by_id_v1_1(self):
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+
+ public_ip = '192.168.0.3'
+ private_ip = '172.19.0.1'
+ interfaces = [
+ {
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
+ },
+ {
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
req = webob.Request.blank('/v1.1/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
- self.assertEqual(res_dict['server']['id'], 1)
- self.assertEqual(res_dict['server']['name'], 'server1')
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "server1",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ #FIXME(wwolf) Do we want the links to be id or uuid?
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ self.assertDictMatch(res_dict, expected_server)
+
+ def test_get_server_with_active_status_by_id_v1_1(self):
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+ private_ip = "192.168.0.3"
+ public_ip = "1.2.3.4"
+
+ interfaces = [
+ {
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
+ },
+ {
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces, power_state=1)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ req = webob.Request.blank('/v1.1/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "server1",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ self.assertDictMatch(res_dict, expected_server)
- expected_links = [
+ def test_get_server_with_id_image_ref_by_id_v1_1(self):
+ image_ref = "10"
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+ private_ip = "192.168.0.3"
+ public_ip = "1.2.3.4"
+
+ interfaces = [
{
- "rel": "self",
- "href": "http://localhost/v1.1/servers/1",
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
},
{
- "rel": "bookmark",
- "href": "http://localhost/servers/1",
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
},
]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces, power_state=1, image_ref=image_ref,
+ flavor_id=flavor_id)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ req = webob.Request.blank('/v1.1/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "server1",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
- self.assertEqual(res_dict['server']['links'], expected_links)
+ self.assertDictMatch(res_dict, expected_server)
def test_get_server_by_id_with_addresses_xml(self):
private = "192.168.0.3"
@@ -452,7 +712,8 @@ class ServersTest(test.TestCase):
'fixed_ipv6': '2001:4860::12',
},
]
- new_return_server = return_server_with_interfaces(interfaces)
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
@@ -495,7 +756,8 @@ class ServersTest(test.TestCase):
'fixed_ipv6': '2001:4860::12',
},
]
- new_return_server = return_server_with_interfaces(interfaces)
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
@@ -703,20 +965,20 @@ class ServersTest(test.TestCase):
for i, s in enumerate(res_dict['servers']):
self.assertEqual(s['id'], i)
self.assertEqual(s['name'], 'server%d' % i)
- self.assertEqual(s.get('imageId', None), None)
+ self.assertEqual(s.get('image', None), None)
expected_links = [
- {
- "rel": "self",
- "href": "http://localhost/v1.1/servers/%d" % (i,),
- },
- {
- "rel": "bookmark",
- "href": "http://localhost/servers/%d" % (i,),
- },
- ]
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/%s" % s['id'],
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/%s" % s['id'],
+ },
+ ]
- self.assertEqual(s['links'], expected_links)
+ self.assertEqual(s['links'], expected_links)
def test_get_servers_with_limit(self):
req = webob.Request.blank('/v1.0/servers?limit=3')
@@ -762,13 +1024,13 @@ class ServersTest(test.TestCase):
req = webob.Request.blank('/v1.1/servers?marker=2')
res = req.get_response(fakes.wsgi_app())
servers = json.loads(res.body)['servers']
- self.assertEqual([s['id'] for s in servers], [3, 4])
+ self.assertEqual([s['name'] for s in servers], ["server3", "server4"])
def test_get_servers_with_limit_and_marker(self):
req = webob.Request.blank('/v1.1/servers?limit=2&marker=1')
res = req.get_response(fakes.wsgi_app())
servers = json.loads(res.body)['servers']
- self.assertEqual([s['id'] for s in servers], [2, 3])
+ self.assertEqual([s['name'] for s in servers], ['server2', 'server3'])
def test_get_servers_with_bad_marker(self):
req = webob.Request.blank('/v1.1/servers?limit=2&marker=asdf')
@@ -779,8 +1041,16 @@ class ServersTest(test.TestCase):
def _setup_for_create_instance(self):
"""Shared implementation for tests below that create instance"""
def instance_create(context, inst):
- return {'id': 1, 'display_name': 'server_test',
- 'uuid': FAKE_UUID}
+ inst_type = instance_types.get_instance_type_by_flavor_id(3)
+ image_ref = 'http://localhost/images/2'
+ return {'id': 1,
+ 'display_name': 'server_test',
+ 'uuid': FAKE_UUID,
+ 'instance_type': dict(inst_type),
+ 'image_ref': image_ref,
+ 'created_at': '2010-10-10T12:00:00Z',
+ 'updated_at': '2010-11-11T11:00:00Z',
+ }
def server_update(context, id, params):
return instance_create(context, id)
@@ -980,8 +1250,26 @@ class ServersTest(test.TestCase):
def test_create_instance_v1_1(self):
self._setup_for_create_instance()
- image_href = 'http://localhost/v1.1/images/2'
- flavor_ref = 'http://localhost/v1.1/flavors/3'
+ image_href = 'http://localhost/images/2'
+ flavor_ref = 'http://localhost/flavors/3'
+ expected_flavor = {
+ "id": "3",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/3',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "2",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/2',
+ },
+ ],
+ }
body = {
'server': {
'name': 'server_test',
@@ -1006,9 +1294,10 @@ class ServersTest(test.TestCase):
server = json.loads(res.body)['server']
self.assertEqual(16, len(server['adminPass']))
self.assertEqual('server_test', server['name'])
- self.assertEqual(1, server['id'])
- self.assertEqual(flavor_ref, server['flavorRef'])
- self.assertEqual(image_href, server['imageRef'])
+ self.assertEqual(expected_flavor, server['flavor'])
+ self.assertEqual(expected_image, server['image'])
+ self.assertEqual(res.status_int, 200)
+ #self.assertEqual(1, server['id'])
def test_create_instance_v1_1_invalid_flavor_href(self):
self._setup_for_create_instance()
@@ -1061,8 +1350,26 @@ class ServersTest(test.TestCase):
def test_create_instance_v1_1_local_href(self):
self._setup_for_create_instance()
- image_id = 2
- flavor_ref = 'http://localhost/v1.1/flavors/3'
+ image_id = "2"
+ flavor_ref = 'http://localhost/flavors/3'
+ expected_flavor = {
+ "id": "3",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/3',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "2",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/2',
+ },
+ ],
+ }
body = {
'server': {
'name': 'server_test',
@@ -1079,9 +1386,8 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
server = json.loads(res.body)['server']
- self.assertEqual(1, server['id'])
- self.assertEqual(flavor_ref, server['flavorRef'])
- self.assertEqual(image_id, server['imageRef'])
+ self.assertEqual(expected_flavor, server['flavor'])
+ self.assertEqual(expected_image, server['image'])
self.assertEqual(res.status_int, 200)
def test_create_instance_with_admin_pass_v1_0(self):
@@ -1306,6 +1612,24 @@ class ServersTest(test.TestCase):
self.assertEqual(s['metadata']['seq'], str(i))
def test_get_all_server_details_v1_1(self):
+ expected_flavor = {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/1',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/10',
+ },
+ ],
+ }
req = webob.Request.blank('/v1.1/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -1314,8 +1638,8 @@ class ServersTest(test.TestCase):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
- self.assertEqual(s['imageRef'], 10)
- self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1')
+ self.assertEqual(s['image'], expected_image)
+ self.assertEqual(s['flavor'], expected_flavor)
self.assertEqual(s['status'], 'BUILD')
self.assertEqual(s['metadata']['seq'], str(i))
@@ -1536,6 +1860,8 @@ class ServersTest(test.TestCase):
state = power_state.BUILDING
new_return_server = return_server_with_power_state(state)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ self.stubs.Set(nova.db, 'instance_get_by_uuid',
+ return_server_with_uuid_and_power_state(state))
req = webob.Request.blank('/v1.0/servers/1/action')
req.method = 'POST'
@@ -1584,6 +1910,8 @@ class ServersTest(test.TestCase):
state = power_state.BUILDING
new_return_server = return_server_with_power_state(state)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ self.stubs.Set(nova.db, 'instance_get_by_uuid',
+ return_server_with_uuid_and_power_state(state))
req = webob.Request.blank('/v1.1/servers/1/action')
req.method = 'POST'
@@ -2559,3 +2887,249 @@ class TestGetKernelRamdiskFromImage(test.TestCase):
kernel_id, ramdisk_id = create_instance_helper.CreateInstanceHelper. \
_do_get_kernel_ramdisk_from_image(image_meta)
return kernel_id, ramdisk_id
+
+
+class ServersViewBuilderV11Test(test.TestCase):
+
+ def setUp(self):
+ self.instance = self._get_instance()
+ self.view_builder = self._get_view_builder()
+
+ def tearDown(self):
+ pass
+
+ def _get_instance(self):
+ instance = {
+ "id": 1,
+ "created_at": "2010-10-10T12:00:00Z",
+ "updated_at": "2010-11-11T11:00:00Z",
+ "admin_pass": "",
+ "user_id": "",
+ "project_id": "",
+ "image_ref": "5",
+ "kernel_id": "",
+ "ramdisk_id": "",
+ "launch_index": 0,
+ "key_name": "",
+ "key_data": "",
+ "state": 0,
+ "state_description": "",
+ "memory_mb": 0,
+ "vcpus": 0,
+ "local_gb": 0,
+ "hostname": "",
+ "host": "",
+ "instance_type": {
+ "flavorid": 1,
+ },
+ "user_data": "",
+ "reservation_id": "",
+ "mac_address": "",
+ "scheduled_at": utils.utcnow(),
+ "launched_at": utils.utcnow(),
+ "terminated_at": utils.utcnow(),
+ "availability_zone": "",
+ "display_name": "test_server",
+ "display_description": "",
+ "locked": False,
+ "metadata": [],
+ #"address": ,
+ #"floating_ips": [{"address":ip} for ip in public_addresses]}
+ "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"}
+
+ return instance
+
+ def _get_view_builder(self):
+ base_url = "http://localhost/v1.1"
+ views = nova.api.openstack.views
+ address_builder = views.addresses.ViewBuilderV11()
+ flavor_builder = views.flavors.ViewBuilderV11(base_url)
+ image_builder = views.images.ViewBuilderV11(base_url)
+
+ view_builder = nova.api.openstack.views.servers.ViewBuilderV11(
+ address_builder,
+ flavor_builder,
+ image_builder,
+ base_url
+ )
+ return view_builder
+
+ def test_build_server(self):
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "name": "test_server",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, False)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail(self):
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {},
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail_active_status(self):
+ #set the power state of the instance to running
+ self.instance['state'] = 1
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "test_server",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {},
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail_with_metadata(self):
+
+ metadata = []
+ metadata.append(InstanceMetadata(key="Open", value="Stack"))
+ metadata.append(InstanceMetadata(key="Number", value=1))
+ self.instance['metadata'] = metadata
+
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {
+ "Open": "Stack",
+ "Number": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index 26ac5ff24..fe7fd8402 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -213,7 +213,11 @@ class ApiEc2TestCase(test.TestCase):
self.http = FakeHttplibConnection(
self.app, '%s:8773' % (self.host), False)
# pylint: disable=E1103
- self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
+ if boto.Version >= '2':
+ self.ec2.new_http_connection(host or '%s:8773' % (self.host),
+ is_secure).AndReturn(self.http)
+ else:
+ self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
return self.http
def test_return_valid_isoformat(self):
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index 136082cc1..f87edc407 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -287,7 +287,7 @@ class CloudTestCase(test.TestCase):
'ip_protocol': u'tcp'}]}
self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
- def test_authorize_security_group_ingress_ip_permissions_groups(self):
+ def test_authorize_security_group_fail_missing_source_group(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
sec = db.security_group_create(self.context, kwargs)
authz = self.cloud.authorize_security_group_ingress
@@ -295,6 +295,23 @@ class CloudTestCase(test.TestCase):
'ip_ranges':{'1': {'cidr_ip': u'0.0.0.0/0'},
'2': {'cidr_ip': u'10.10.10.10/32'}},
'groups': {'1': {'user_id': u'someuser',
+ 'group_name': u'somegroup1'}},
+ 'ip_protocol': u'tcp'}]}
+ self.assertRaises(exception.SecurityGroupNotFound, authz,
+ self.context, group_name=sec['name'], **kwargs)
+
+ def test_authorize_security_group_ingress_ip_permissions_groups(self):
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
+ sec = db.security_group_create(self.context,
+ {'project_id': 'someuser',
+ 'name': 'somegroup1'})
+ sec = db.security_group_create(self.context,
+ {'project_id': 'someuser',
+ 'name': 'othergroup2'})
+ sec = db.security_group_create(self.context, kwargs)
+ authz = self.cloud.authorize_security_group_ingress
+ kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
+ 'groups': {'1': {'user_id': u'someuser',
'group_name': u'somegroup1'},
'2': {'user_id': u'someuser',
'group_name': u'othergroup2'}},
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
new file mode 100644
index 000000000..107fd03e3
--- /dev/null
+++ b/nova/tests/test_db_api.py
@@ -0,0 +1,86 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Unit tests for the DB API"""
+
+from nova import test
+from nova import context
+from nova import db
+from nova import flags
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+
+
+def _setup_networking(instance_id, ip='1.2.3.4', flo_addr='1.2.1.2'):
+ ctxt = context.get_admin_context()
+ network_ref = db.project_get_networks(ctxt,
+ 'fake',
+ associate=True)[0]
+ vif = {'address': '56:12:12:12:12:12',
+ 'network_id': network_ref['id'],
+ 'instance_id': instance_id}
+ vif_ref = db.virtual_interface_create(ctxt, vif)
+
+ fixed_ip = {'address': ip,
+ 'network_id': network_ref['id'],
+ 'virtual_interface_id': vif_ref['id'],
+ 'allocated': True,
+ 'instance_id': instance_id}
+ db.fixed_ip_create(ctxt, fixed_ip)
+ fix_ref = db.fixed_ip_get_by_address(ctxt, ip)
+ db.floating_ip_create(ctxt, {'address': flo_addr,
+ 'fixed_ip_id': fix_ref.id})
+
+
+class DbApiTestCase(test.TestCase):
+ def setUp(self):
+ super(DbApiTestCase, self).setUp()
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('admin', 'admin', 'admin', True)
+ self.project = self.manager.create_project('proj', 'admin', 'proj')
+ self.context = context.RequestContext(user=self.user,
+ project=self.project)
+
+ def tearDown(self):
+ self.manager.delete_project(self.project)
+ self.manager.delete_user(self.user)
+ super(DbApiTestCase, self).tearDown()
+
+ def test_instance_get_project_vpn(self):
+ result = db.fixed_ip_get_all(self.context)
+ values = {'instance_type_id': FLAGS.default_instance_type,
+ 'image_ref': FLAGS.vpn_image_id,
+ 'project_id': self.project.id
+ }
+ instance = db.instance_create(self.context, values)
+ result = db.instance_get_project_vpn(self.context, self.project.id)
+ self.assertEqual(instance.id, result.id)
+
+ def test_instance_get_project_vpn_joins(self):
+ result = db.fixed_ip_get_all(self.context)
+ values = {'instance_type_id': FLAGS.default_instance_type,
+ 'image_ref': FLAGS.vpn_image_id,
+ 'project_id': self.project.id
+ }
+ instance = db.instance_create(self.context, values)
+ _setup_networking(instance.id)
+ result = db.instance_get_project_vpn(self.context, self.project.id)
+ self.assertEqual(instance.id, result.id)
+ self.assertEqual(result['fixed_ips'][0]['floating_ips'][0].address,
+ '1.2.1.2')