summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-07-15 12:09:21 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-07-15 12:09:21 -0400
commit3a2dbab191ded08da47fbe184d8f3865a8c835fe (patch)
tree2ec1bcd7918eccfedac210431e652150e159f609
parent70e4516173ed0e3efa5a4a6c88dafe8869530e06 (diff)
parent4432dc1fdf820f58a8afd701d6a87fee7725d10b (diff)
merge with trunk
-rw-r--r--Authors2
-rwxr-xr-xbin/nova-manage57
-rw-r--r--nova/api/openstack/__init__.py12
-rw-r--r--nova/api/openstack/common.py13
-rw-r--r--nova/api/openstack/ips.py79
-rw-r--r--nova/api/openstack/servers.py26
-rw-r--r--nova/api/openstack/views/addresses.py39
-rw-r--r--nova/api/openstack/views/servers.py13
-rw-r--r--nova/db/sqlalchemy/api.py24
-rw-r--r--nova/network/manager.py4
-rw-r--r--nova/tests/api/openstack/test_common.py24
-rw-r--r--nova/tests/api/openstack/test_servers.py292
-rw-r--r--nova/virt/xenapi/vmops.py12
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/agent2
14 files changed, 465 insertions, 134 deletions
diff --git a/Authors b/Authors
index 2e50cfbe0..8ffb7d8d4 100644
--- a/Authors
+++ b/Authors
@@ -85,7 +85,9 @@ Ryan Lucio <rlucio@internap.com>
Salvatore Orlando <salvatore.orlando@eu.citrix.com>
Sandy Walsh <sandy.walsh@rackspace.com>
Sateesh Chodapuneedi <sateesh.chodapuneedi@citrix.com>
+Scott Moser <smoser@ubuntu.com>
Soren Hansen <soren.hansen@rackspace.com>
+Stephanie Reese <reese.sm@gmail.com>
Thierry Carrez <thierry@openstack.org>
Todd Willey <todd@ansolabs.com>
Trey Morris <trey.morris@rackspace.com>
diff --git a/bin/nova-manage b/bin/nova-manage
index 7dfe91698..b892d958a 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -414,8 +414,11 @@ class ProjectCommands(object):
except (exception.UserNotFound, exception.ProjectNotFound) as ex:
print ex
raise
- with open(filename, 'w') as f:
- f.write(rc)
+ if filename == "-":
+ sys.stdout.write(rc)
+ else:
+ with open(filename, 'w') as f:
+ f.write(rc)
def list(self, username=None):
"""Lists all projects
@@ -465,8 +468,11 @@ class ProjectCommands(object):
arguments: project_id user_id [filename='nova.zip]"""
try:
zip_file = self.manager.get_credentials(user_id, project_id)
- with open(filename, 'w') as f:
- f.write(zip_file)
+ if filename == "-":
+ sys.stdout.write(zip_file)
+ else:
+ with open(filename, 'w') as f:
+ f.write(zip_file)
except (exception.UserNotFound, exception.ProjectNotFound) as ex:
print ex
raise
@@ -615,15 +621,19 @@ class NetworkCommands(object):
def list(self):
"""List all created networks"""
- print "%-18s\t%-15s\t%-15s\t%-15s" % (_('network'),
- _('netmask'),
- _('start address'),
- 'DNS')
+ print "%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (_('network'),
+ _('netmask'),
+ _('start address'),
+ _('DNS'),
+ _('VlanID'),
+ 'project')
for network in db.network_get_all(context.get_admin_context()):
- print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr,
- network.netmask,
- network.dhcp_start,
- network.dns)
+ print "%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (network.cidr,
+ network.netmask,
+ network.dhcp_start,
+ network.dns,
+ network.vlan,
+ network.project_id)
def delete(self, fixed_range):
"""Deletes a network"""
@@ -812,6 +822,28 @@ class ServiceCommands(object):
{"method": "update_available_resource"})
+class HostCommands(object):
+ """List hosts"""
+
+ def list(self, zone=None):
+ """Show a list of all physical hosts. Filter by zone.
+ args: [zone]"""
+ print "%-25s\t%-15s" % (_('host'),
+ _('zone'))
+ ctxt = context.get_admin_context()
+ now = utils.utcnow()
+ services = db.service_get_all(ctxt)
+ if zone:
+ services = [s for s in services if s['availability_zone'] == zone]
+ hosts = []
+ for srv in services:
+ if not [h for h in hosts if h['host'] == srv['host']]:
+ hosts.append(srv)
+
+ for h in hosts:
+ print "%-25s\t%-15s" % (h['host'], h['availability_zone'])
+
+
class DbCommands(object):
"""Class for managing the database."""
@@ -1185,6 +1217,7 @@ CATEGORIES = [
('fixed', FixedIpCommands),
('flavor', InstanceTypeCommands),
('floating', FloatingIpCommands),
+ ('host', HostCommands),
('instance_type', InstanceTypeCommands),
('image', ImageCommands),
('network', NetworkCommands),
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index f24017df0..e87d7c754 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -125,6 +125,10 @@ class APIRouter(base_wsgi.Router):
collection={'detail': 'GET'},
member=self.server_members)
+ mapper.resource("ip", "ips", controller=ips.create_resource(version),
+ parent_resource=dict(member_name='server',
+ collection_name='servers'))
+
mapper.resource("image", "images",
controller=images.create_resource(version),
collection={'detail': 'GET'})
@@ -144,9 +148,6 @@ class APIRouterV10(APIRouter):
def _setup_routes(self, mapper):
super(APIRouterV10, self)._setup_routes(mapper, '1.0')
- mapper.resource("image", "images",
- controller=images.create_resource('1.0'),
- collection={'detail': 'GET'})
mapper.resource("shared_ip_group", "shared_ip_groups",
collection={'detail': 'GET'},
@@ -157,11 +158,6 @@ class APIRouterV10(APIRouter):
parent_resource=dict(member_name='server',
collection_name='servers'))
- mapper.resource("ip", "ips", controller=ips.create_resource(),
- collection=dict(public='GET', private='GET'),
- parent_resource=dict(member_name='server',
- collection_name='servers'))
-
class APIRouterV11(APIRouter):
"""Define routes specific to OpenStack API V1.1."""
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 26b8c1946..e7aa7d693 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -160,15 +160,22 @@ def get_uuid_from_href(href):
def remove_version_from_href(href):
- """Removes the api version from the href.
+ """Removes the first api version from the href.
Given: 'http://www.nova.com/v1.1/123'
Returns: 'http://www.nova.com/123'
+ Given: 'http://www.nova.com/v1.1'
+ Returns: 'http://www.nova.com'
+
"""
try:
- #matches /v#.#
- new_href = re.sub(r'[/][v][0-9]*.[0-9]*', '', href)
+ #removes the first instance that matches /v#.#/
+ new_href = re.sub(r'[/][v][0-9]+\.[0-9]+[/]', '/', href, count=1)
+
+ #if no version was found, try finding /v#.# at the end of the string
+ if new_href == href:
+ new_href = re.sub(r'[/][v][0-9]+\.[0-9]+$', '', href, count=1)
except:
LOG.debug(_("Error removing version from href: %s") % href)
msg = _('could not parse version from href')
diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py
index 23e5432d6..1ebfdb831 100644
--- a/nova/api/openstack/ips.py
+++ b/nova/api/openstack/ips.py
@@ -23,6 +23,7 @@ import nova
from nova.api.openstack import faults
import nova.api.openstack.views.addresses
from nova.api.openstack import wsgi
+from nova import db
class Controller(object):
@@ -30,7 +31,6 @@ class Controller(object):
def __init__(self):
self.compute_api = nova.compute.API()
- self.builder = nova.api.openstack.views.addresses.ViewBuilderV10()
def _get_instance(self, req, server_id):
try:
@@ -40,29 +40,78 @@ class Controller(object):
return faults.Fault(exc.HTTPNotFound())
return instance
+ def create(self, req, server_id, body):
+ return faults.Fault(exc.HTTPNotImplemented())
+
+ def delete(self, req, server_id, id):
+ return faults.Fault(exc.HTTPNotImplemented())
+
+
+class ControllerV10(Controller):
+
def index(self, req, server_id):
instance = self._get_instance(req, server_id)
- return {'addresses': self.builder.build(instance)}
+ builder = nova.api.openstack.views.addresses.ViewBuilderV10()
+ return {'addresses': builder.build(instance)}
- def public(self, req, server_id):
+ def show(self, req, server_id, id):
instance = self._get_instance(req, server_id)
- return {'public': self.builder.build_public_parts(instance)}
+ builder = self._get_view_builder(req)
+ if id == 'private':
+ view = builder.build_private_parts(instance)
+ elif id == 'public':
+ view = builder.build_public_parts(instance)
+ else:
+ msg = _("Only private and public networks available")
+ return faults.Fault(exc.HTTPNotFound(explanation=msg))
- def private(self, req, server_id):
- instance = self._get_instance(req, server_id)
- return {'private': self.builder.build_private_parts(instance)}
+ return {id: view}
+
+ def _get_view_builder(self, req):
+ return nova.api.openstack.views.addresses.ViewBuilderV10()
+
+
+class ControllerV11(Controller):
+
+ def index(self, req, server_id):
+ context = req.environ['nova.context']
+ interfaces = self._get_virtual_interfaces(context, server_id)
+ networks = self._get_view_builder(req).build(interfaces)
+ return {'addresses': networks}
def show(self, req, server_id, id):
- return faults.Fault(exc.HTTPNotImplemented())
+ context = req.environ['nova.context']
+ interfaces = self._get_virtual_interfaces(context, server_id)
+ network = self._get_view_builder(req).build_network(interfaces, id)
- def create(self, req, server_id, body):
- return faults.Fault(exc.HTTPNotImplemented())
+ if network is None:
+ msg = _("Instance is not a member of specified network")
+ return faults.Fault(exc.HTTPNotFound(explanation=msg))
- def delete(self, req, server_id, id):
- return faults.Fault(exc.HTTPNotImplemented())
+ return network
+
+ def _get_virtual_interfaces(self, context, server_id):
+ try:
+ return db.api.virtual_interface_get_by_instance(context, server_id)
+ except nova.exception.InstanceNotFound:
+ msg = _("Instance does not exist")
+ raise exc.HTTPNotFound(explanation=msg)
+
+ def _get_view_builder(self, req):
+ return nova.api.openstack.views.addresses.ViewBuilderV11()
+
+
+def create_resource(version):
+ controller = {
+ '1.0': ControllerV10,
+ '1.1': ControllerV11,
+ }[version]()
+ xmlns = {
+ '1.0': wsgi.XMLNS_V10,
+ '1.1': wsgi.XMLNS_V11,
+ }[version]
-def create_resource():
metadata = {
'list_collections': {
'public': {'item_name': 'ip', 'item_key': 'addr'},
@@ -72,8 +121,8 @@ def create_resource():
body_serializers = {
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
- xmlns=wsgi.XMLNS_V10),
+ xmlns=xmlns),
}
serializer = wsgi.ResponseSerializer(body_serializers)
- return wsgi.Resource(Controller(), serializer=serializer)
+ return wsgi.Resource(controller, serializer=serializer)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 12af44a8d..93f8e832c 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -19,6 +19,7 @@ import traceback
from webob import exc
from nova import compute
+from nova import db
from nova import exception
from nova import flags
from nova import log as logging
@@ -62,7 +63,7 @@ class Controller(object):
return exc.HTTPBadRequest(explanation=str(err))
return servers
- def _get_view_builder(self, req):
+ def _build_view(self, req, instance, is_detail=False):
raise NotImplementedError()
def _limit_items(self, items, req):
@@ -88,8 +89,7 @@ class Controller(object):
fixed_ip=fixed_ip,
recurse_zones=recurse_zones)
limited_list = self._limit_items(instance_list, req)
- builder = self._get_view_builder(req)
- servers = [builder.build(inst, is_detail)['server']
+ servers = [self._build_view(req, inst, is_detail)['server']
for inst in limited_list]
return dict(servers=servers)
@@ -99,8 +99,7 @@ class Controller(object):
try:
instance = self.compute_api.routing_get(
req.environ['nova.context'], id)
- builder = self._get_view_builder(req)
- return builder.build(instance, is_detail=True)
+ return self._build_view(req, instance, is_detail=True)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -121,8 +120,7 @@ class Controller(object):
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 = self._build_view(req, inst, is_detail=True)
server['server']['adminPass'] = extra_values['password']
return server
@@ -426,10 +424,10 @@ class ControllerV10(Controller):
def _flavor_id_from_req_data(self, data):
return data['server']['flavorId']
- def _get_view_builder(self, req):
- addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV10()
- return nova.api.openstack.views.servers.ViewBuilderV10(
- addresses_builder)
+ def _build_view(self, req, instance, is_detail=False):
+ addresses = nova.api.openstack.views.addresses.ViewBuilderV10()
+ builder = nova.api.openstack.views.servers.ViewBuilderV10(addresses)
+ return builder.build(instance, is_detail=is_detail)
def _limit_items(self, items, req):
return common.limited(items, req)
@@ -498,16 +496,18 @@ class ControllerV11(Controller):
href = data['server']['flavorRef']
return common.get_id_from_href(href)
- def _get_view_builder(self, req):
+ def _build_view(self, req, instance, is_detail=False):
base_url = req.application_url
flavor_builder = nova.api.openstack.views.flavors.ViewBuilderV11(
base_url)
image_builder = nova.api.openstack.views.images.ViewBuilderV11(
base_url)
addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV11()
- return nova.api.openstack.views.servers.ViewBuilderV11(
+ builder = nova.api.openstack.views.servers.ViewBuilderV11(
addresses_builder, flavor_builder, image_builder, base_url)
+ return builder.build(instance, is_detail=is_detail)
+
def _action_change_password(self, input_dict, req, id):
context = req.environ['nova.context']
if (not 'changePassword' in input_dict
diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py
index b59eb4751..a242efa45 100644
--- a/nova/api/openstack/views/addresses.py
+++ b/nova/api/openstack/views/addresses.py
@@ -20,13 +20,14 @@ from nova.api.openstack import common
class ViewBuilder(object):
- ''' Models a server addresses response as a python dictionary.'''
+ """Models a server addresses response as a python dictionary."""
def build(self, inst):
raise NotImplementedError()
class ViewBuilderV10(ViewBuilder):
+
def build(self, inst):
private_ips = self.build_private_parts(inst)
public_ips = self.build_public_parts(inst)
@@ -40,11 +41,31 @@ class ViewBuilderV10(ViewBuilder):
class ViewBuilderV11(ViewBuilder):
- def build(self, inst):
- # TODO(tr3buchet) - this shouldn't be hard coded to 4...
- private_ips = utils.get_from_path(inst, 'fixed_ips/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst,
- 'fixed_ips/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
+
+ def build(self, interfaces):
+ networks = {}
+ for interface in interfaces:
+ network_label = interface['network']['label']
+
+ if network_label not in networks:
+ networks[network_label] = []
+
+ networks[network_label].extend(self._extract_ipv4(interface))
+
+ return networks
+
+ def build_network(self, interfaces, network_label):
+ for interface in interfaces:
+ if interface['network']['label'] == network_label:
+ ips = self._extract_ipv4(interface)
+ return {network_label: list(ips)}
+ return None
+
+ def _extract_ipv4(self, interface):
+ for fixed_ip in interface['fixed_ips']:
+ yield self._build_ip_entity(fixed_ip['address'], 4)
+ for floating_ip in fixed_ip.get('floating_ips', []):
+ yield self._build_ip_entity(floating_ip['address'], 4)
+
+ def _build_ip_entity(self, address, version):
+ return {'addr': address, 'version': version}
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 570488cfa..a4c9dd95a 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -77,7 +77,6 @@ class ViewBuilder(object):
inst_dict = {
'id': inst['id'],
'name': inst['display_name'],
- 'addresses': self.addresses_builder.build(inst),
'status': power_mapping[inst.get('state')]}
ctxt = nova.context.get_admin_context()
@@ -98,9 +97,14 @@ class ViewBuilder(object):
self._build_image(inst_dict, inst)
self._build_flavor(inst_dict, inst)
+ self._build_addresses(inst_dict, inst)
return dict(server=inst_dict)
+ def _build_addresses(self, response, inst):
+ """Return the addresses sub-resource of a server."""
+ raise NotImplementedError()
+
def _build_image(self, response, inst):
"""Return the image sub-resource of a server."""
raise NotImplementedError()
@@ -130,6 +134,9 @@ class ViewBuilderV10(ViewBuilder):
if 'instance_type' in dict(inst):
response['flavorId'] = inst['instance_type']['flavorid']
+ def _build_addresses(self, response, inst):
+ response['addresses'] = self.addresses_builder.build(inst)
+
class ViewBuilderV11(ViewBuilder):
"""Model an Openstack API V1.0 server response."""
@@ -193,6 +200,10 @@ class ViewBuilderV11(ViewBuilder):
]
}
+ def _build_addresses(self, response, inst):
+ interfaces = inst.get('virtual_interfaces', [])
+ response['addresses'] = self.addresses_builder.build(interfaces)
+
def _build_extra(self, response, inst):
self._build_links(response, inst)
response['id'] = inst['uuid']
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index ffd009513..a831516a8 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -118,8 +118,23 @@ def require_context(f):
return wrapper
+def require_instance_exists(f):
+ """Decorator to require the specified instance to exist.
+
+ Requres the wrapped function to use context and instance_id as
+ their first two arguments.
+ """
+
+ def wrapper(context, instance_id, *args, **kwargs):
+ db.api.instance_get(context, instance_id)
+ return f(context, instance_id, *args, **kwargs)
+ wrapper.__name__ = f.__name__
+ return wrapper
+
+
###################
+
@require_admin_context
def service_destroy(context, service_id):
session = get_session()
@@ -921,6 +936,7 @@ def virtual_interface_get_by_fixed_ip(context, fixed_ip_id):
@require_context
+@require_instance_exists
def virtual_interface_get_by_instance(context, instance_id):
"""Gets all virtual interfaces for instance.
@@ -3071,14 +3087,6 @@ def zone_get_all(context):
####################
-def require_instance_exists(func):
- def new_func(context, instance_id, *args, **kwargs):
- db.api.instance_get(context, instance_id)
- return func(context, instance_id, *args, **kwargs)
- new_func.__name__ = func.__name__
- return new_func
-
-
@require_context
@require_instance_exists
def instance_metadata_get(context, instance_id):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 824e8d24d..24736f53d 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -340,7 +340,7 @@ class NetworkManager(manager.SchedulerDependentManager):
"""Set the network hosts for any networks which are unset."""
try:
networks = self.db.network_get_all(context)
- except Exception.NoNetworksFound:
+ except exception.NoNetworksFound:
# we don't care if no networks are found
pass
@@ -357,7 +357,7 @@ class NetworkManager(manager.SchedulerDependentManager):
# a non-vlan instance should connect to
try:
networks = self.db.network_get_all(context)
- except Exception.NoNetworksFound:
+ except exception.NoNetworksFound:
# we don't care if no networks are found
pass
diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py
index 864bbd8cc..7a5beb006 100644
--- a/nova/tests/api/openstack/test_common.py
+++ b/nova/tests/api/openstack/test_common.py
@@ -206,12 +206,36 @@ class MiscFunctionsTest(test.TestCase):
actual = common.remove_version_from_href(fixture)
self.assertEqual(actual, expected)
+ def test_remove_version_from_href_3(self):
+ fixture = 'http://www.testsite.com/v10.10'
+ expected = 'http://www.testsite.com'
+ actual = common.remove_version_from_href(fixture)
+ self.assertEqual(actual, expected)
+
+ def test_remove_version_from_href_4(self):
+ fixture = 'http://www.testsite.com/v1.1/images/v10.5'
+ expected = 'http://www.testsite.com/images/v10.5'
+ actual = common.remove_version_from_href(fixture)
+ self.assertEqual(actual, expected)
+
def test_remove_version_from_href_bad_request(self):
fixture = 'http://www.testsite.com/1.1/images'
self.assertRaises(ValueError,
common.remove_version_from_href,
fixture)
+ def test_remove_version_from_href_bad_request_2(self):
+ fixture = 'http://www.testsite.com/v/images'
+ self.assertRaises(ValueError,
+ common.remove_version_from_href,
+ fixture)
+
+ def test_remove_version_from_href_bad_request_3(self):
+ fixture = 'http://www.testsite.com/v1.1images'
+ self.assertRaises(ValueError,
+ common.remove_version_from_href,
+ fixture)
+
def test_get_id_from_href(self):
fixture = 'http://www.testsite.com/dir/45'
actual = common.get_id_from_href(fixture)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 3389b378a..bca3dad1d 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -66,6 +66,18 @@ def return_server_by_uuid(context, uuid):
return stub_instance(id, uuid=uuid)
+def return_virtual_interface_by_instance(interfaces):
+ def _return_virtual_interface_by_instance(context, instance_id):
+ return interfaces
+ return _return_virtual_interface_by_instance
+
+
+def return_virtual_interface_instance_nonexistant(interfaces):
+ def _return_virtual_interface_by_instance(context, instance_id):
+ raise exception.InstanceNotFound(instance_id=instance_id)
+ return _return_virtual_interface_by_instance
+
+
def return_server_with_addresses(private, public):
def _return_server(context, id):
return stub_instance(id, private_address=private,
@@ -73,6 +85,12 @@ def return_server_with_addresses(private, public):
return _return_server
+def return_server_with_interfaces(interfaces, **kwargs):
+ def _return_server(context, id):
+ return stub_instance(id, interfaces=interfaces, **kwargs)
+ return _return_server
+
+
def return_server_with_power_state(power_state):
def _return_server(context, id):
return stub_instance(id, power_state=power_state)
@@ -125,10 +143,13 @@ 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, image_ref="10", flavor_id="1"):
+ 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(int(flavor_id))
if public_addresses is None:
@@ -174,7 +195,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
"display_description": "",
"locked": False,
"metadata": metadata,
- "uuid": uuid}
+ "uuid": uuid,
+ "virtual_interfaces": interfaces}
instance["fixed_ips"] = {
"address": private_address,
@@ -285,19 +307,25 @@ class ServersTest(test.TestCase):
flavor_ref = "http://localhost/v1.1/flavors/1"
flavor_id = "1"
flavor_bookmark = "http://localhost/flavors/1"
- private = "192.168.0.3"
- public = ["1.2.3.4"]
-
- def _return_server(context, id):
- return stub_instance(1,
- private_address=private,
- public_addresses=public,
- power_state=0,
- image_ref=image_bookmark,
- flavor_id=flavor_id,
- )
- self.stubs.Set(nova.db.api, 'instance_get', _return_server)
+ 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_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())
@@ -311,9 +339,8 @@ class ServersTest(test.TestCase):
"name": "server1",
"status": "BUILD",
"hostId": '',
- #"accessIPv4" : "67.23.10.132",
- #"accessIPv6" : "::babe:67.23.10.132",
"image": {
+ "id":"10",
"links": [
{
"rel": "bookmark",
@@ -334,13 +361,13 @@ class ServersTest(test.TestCase):
"public": [
{
"version": 4,
- "addr": public[0],
+ "addr": public_ip,
},
],
"private": [
{
"version": 4,
- "addr": private,
+ "addr": private_ip,
},
],
},
@@ -368,19 +395,26 @@ class ServersTest(test.TestCase):
flavor_ref = "http://localhost/v1.1/flavors/1"
flavor_id = "1"
flavor_bookmark = "http://localhost/flavors/1"
- private = "192.168.0.3"
- public = ["1.2.3.4"]
-
- def _return_server(context, id):
-
- return stub_instance(1,
- private_address=private,
- public_addresses=public,
- power_state=1,
- image_ref=image_bookmark,
- flavor_id=flavor_id,
- )
- self.stubs.Set(nova.db.api, 'instance_get', _return_server)
+ 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_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())
@@ -397,6 +431,7 @@ class ServersTest(test.TestCase):
#"accessIPv4" : "67.23.10.132",
#"accessIPv6" : "::babe:67.23.10.132",
"image": {
+ "id" : "10",
"links": [
{
"rel": "bookmark",
@@ -417,13 +452,13 @@ class ServersTest(test.TestCase):
"public": [
{
"version": 4,
- "addr": public[0],
+ "addr": public_ip,
},
],
"private": [
{
"version": 4,
- "addr": private,
+ "addr": private_ip,
},
],
},
@@ -452,19 +487,28 @@ class ServersTest(test.TestCase):
flavor_ref = "http://localhost/v1.1/flavors/1"
flavor_id = "1"
flavor_bookmark = "http://localhost/flavors/1"
- private = "192.168.0.3"
- public = ["1.2.3.4"]
-
- def _return_server(context, id):
-
- return stub_instance(1,
- private_address=private,
- public_addresses=public,
- power_state=1,
- image_ref=image_ref,
- flavor_id=flavor_id,
- )
- self.stubs.Set(nova.db.api, 'instance_get', _return_server)
+ 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_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())
@@ -502,13 +546,13 @@ class ServersTest(test.TestCase):
"public": [
{
"version": 4,
- "addr": public[0],
+ "addr": public_ip,
},
],
"private": [
{
"version": 4,
- "addr": private,
+ "addr": private_ip,
},
],
},
@@ -645,23 +689,152 @@ class ServersTest(test.TestCase):
self.assertEquals(ip.getAttribute('addr'), private)
def test_get_server_by_id_with_addresses_v1_1(self):
- private = "192.168.0.3"
- public = ["1.2.3.4"]
- new_return_server = return_server_with_addresses(private, public)
+ interfaces = [
+ {
+ 'network': {'label': 'network_1'},
+ 'fixed_ips': [
+ {'address': '192.168.0.3'},
+ {'address': '192.168.0.4'},
+ ],
+ },
+ {
+ 'network': {'label': 'network_2'},
+ 'fixed_ips': [
+ {'address': '172.19.0.1'},
+ {'address': '172.19.0.2'},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_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'], FAKE_UUID)
self.assertEqual(res_dict['server']['name'], 'server1')
addresses = res_dict['server']['addresses']
- # RM(4047): Figure otu what is up with the 1.1 api and multi-nic
- #self.assertEqual(len(addresses["public"]), len(public))
- #self.assertEqual(addresses["public"][0],
- # {"version": 4, "addr": public[0]})
- #self.assertEqual(len(addresses["private"]), 1)
- #self.assertEqual(addresses["private"][0],
- # {"version": 4, "addr": private})
+ expected = {
+ 'network_1': [
+ {'addr': '192.168.0.3', 'version': 4},
+ {'addr': '192.168.0.4', 'version': 4},
+ ],
+ 'network_2': [
+ {'addr': '172.19.0.1', 'version': 4},
+ {'addr': '172.19.0.2', 'version': 4},
+ ],
+ }
+
+ self.assertEqual(addresses, expected)
+
+ def test_get_server_addresses_v1_1(self):
+ interfaces = [
+ {
+ 'network': {'label': 'network_1'},
+ 'fixed_ips': [
+ {'address': '192.168.0.3'},
+ {'address': '192.168.0.4'},
+ ],
+ },
+ {
+ 'network': {'label': 'network_2'},
+ 'fixed_ips': [
+ {
+ 'address': '172.19.0.1',
+ 'floating_ips': [
+ {'address': '1.2.3.4'},
+ ],
+ },
+ {'address': '172.19.0.2'},
+ ],
+ },
+ ]
+
+ _return_vifs = return_virtual_interface_by_instance(interfaces)
+ self.stubs.Set(nova.db.api,
+ 'virtual_interface_get_by_instance',
+ _return_vifs)
+
+ req = webob.Request.blank('/v1.1/servers/1/ips')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ expected = {
+ 'addresses': {
+ 'network_1': [
+ {'version': 4, 'addr': '192.168.0.3'},
+ {'version': 4, 'addr': '192.168.0.4'},
+ ],
+ 'network_2': [
+ {'version': 4, 'addr': '172.19.0.1'},
+ {'version': 4, 'addr': '1.2.3.4'},
+ {'version': 4, 'addr': '172.19.0.2'},
+ ],
+ },
+ }
+
+ self.assertEqual(res_dict, expected)
+
+ def test_get_server_addresses_single_network_v1_1(self):
+ interfaces = [
+ {
+ 'network': {'label': 'network_1'},
+ 'fixed_ips': [
+ {'address': '192.168.0.3'},
+ {'address': '192.168.0.4'},
+ ],
+ },
+ {
+ 'network': {'label': 'network_2'},
+ 'fixed_ips': [
+ {
+ 'address': '172.19.0.1',
+ 'floating_ips': [
+ {'address': '1.2.3.4'},
+ ],
+ },
+ {'address': '172.19.0.2'},
+ ],
+ },
+ ]
+ _return_vifs = return_virtual_interface_by_instance(interfaces)
+ self.stubs.Set(nova.db.api,
+ 'virtual_interface_get_by_instance',
+ _return_vifs)
+
+ req = webob.Request.blank('/v1.1/servers/1/ips/network_2')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ res_dict = json.loads(res.body)
+ expected = {
+ 'network_2': [
+ {'version': 4, 'addr': '172.19.0.1'},
+ {'version': 4, 'addr': '1.2.3.4'},
+ {'version': 4, 'addr': '172.19.0.2'},
+ ],
+ }
+ self.assertEqual(res_dict, expected)
+
+ def test_get_server_addresses_nonexistant_network_v1_1(self):
+ _return_vifs = return_virtual_interface_by_instance([])
+ self.stubs.Set(nova.db.api,
+ 'virtual_interface_get_by_instance',
+ _return_vifs)
+
+ req = webob.Request.blank('/v1.1/servers/1/ips/network_0')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 404)
+
+ def test_get_server_addresses_nonexistant_server_v1_1(self):
+ _return_vifs = return_virtual_interface_instance_nonexistant([])
+ self.stubs.Set(nova.db.api,
+ 'virtual_interface_get_by_instance',
+ _return_vifs)
+
+ req = webob.Request.blank('/v1.1/servers/600/ips')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 404)
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
@@ -1046,12 +1219,14 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
server = json.loads(res.body)['server']
self.assertEqual(16, len(server['adminPass']))
self.assertEqual('server_test', server['name'])
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_bad_href(self):
self._setup_for_create_instance()
@@ -2622,7 +2797,6 @@ class ServersViewBuilderV11Test(test.TestCase):
}
output = self.view_builder.build(self.instance, False)
- print output
self.assertDictEqual(output, expected_server)
def test_build_server_detail(self):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 56718f8e8..c332c27b0 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -597,7 +597,9 @@ class VMOps(object):
# No response from the agent
return
resp_dict = json.loads(resp)
- return resp_dict['message']
+ # Some old versions of the Windows agent have a trailing \\r\\n
+ # (ie CRLF escaped) for some reason. Strip that off.
+ return resp_dict['message'].replace('\\r\\n', '')
if timeout:
vm_ref = self._get_vm_opaque_ref(instance)
@@ -662,9 +664,13 @@ class VMOps(object):
# There was some sort of error; the message will contain
# a description of the error.
raise RuntimeError(resp_dict['message'])
- agent_pub = int(resp_dict['message'])
+ # Some old versions of the Windows agent have a trailing \\r\\n
+ # (ie CRLF escaped) for some reason. Strip that off.
+ agent_pub = int(resp_dict['message'].replace('\\r\\n', ''))
dh.compute_shared(agent_pub)
- enc_pass = dh.encrypt(new_pass)
+ # Some old versions of Linux and Windows agent expect trailing \n
+ # on password to work correctly.
+ enc_pass = dh.encrypt(new_pass + '\n')
# Send the encrypted password
password_transaction_id = str(uuid.uuid4())
password_args = {'id': password_transaction_id, 'enc_pass': enc_pass}
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index b8a1b936a..68d7e7bff 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -127,7 +127,7 @@ def inject_file(self, arg_dict):
been disabled, and raise a NotImplemented error if that is the case.
"""
b64_path = arg_dict["b64_path"]
- b64_file = arg_dict["b64_file"]
+ b64_file = arg_dict["b64_contents"]
request_id = arg_dict["id"]
if self._agent_has_method("file_inject"):
# New version of the agent. Agent should receive a 'value'