summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSalvatore Orlando <salvatore.orlando@eu.citrix.com>2011-03-22 12:36:53 +0000
committerSalvatore Orlando <salvatore.orlando@eu.citrix.com>2011-03-22 12:36:53 +0000
commit25f0743f813a59c464bc117da9ec1931ed30bbf3 (patch)
treecd8ed2f4106a848bf8f7544e8ee877a88dcaa50e
parente5e4408cb4701372057137deef52111969b16956 (diff)
parente40d692c55a02fa686e83bd87eca29a3cfa3d15e (diff)
merge trunk
-rw-r--r--Authors1
-rwxr-xr-xbin/nova-manage5
-rw-r--r--nova/api/openstack/flavors.py15
-rw-r--r--nova/compute/manager.py14
-rw-r--r--nova/image/local.py17
-rw-r--r--nova/network/linux_net.py1
-rw-r--r--nova/network/manager.py2
-rw-r--r--nova/tests/api/openstack/test_flavors.py72
-rw-r--r--nova/tests/api/openstack/test_images.py12
-rw-r--r--nova/tests/test_xenapi.py7
-rw-r--r--nova/virt/xenapi/vm_utils.py11
-rw-r--r--nova/virt/xenapi/vmops.py144
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance3
13 files changed, 199 insertions, 105 deletions
diff --git a/Authors b/Authors
index 6aaad4d13..30034e2d6 100644
--- a/Authors
+++ b/Authors
@@ -34,6 +34,7 @@ Jonathan Bryce <jbryce@jbryce.com>
Jordan Rinke <jordan@openstack.org>
Josh Durgin <joshd@hq.newdream.net>
Josh Kearney <josh@jk0.org>
+Josh Kleinpeter <josh@kleinpeter.org>
Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com>
Kei Masumoto <masumotok@nttdata.co.jp>
diff --git a/bin/nova-manage b/bin/nova-manage
index 013a6077b..69cbf6f95 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -518,11 +518,12 @@ class NetworkCommands(object):
network_size=None, vlan_start=None,
vpn_start=None, fixed_range_v6=None, label='public'):
"""Creates fixed ips for host by range
- arguments: [fixed_range=FLAG], [num_networks=FLAG],
+ arguments: fixed_range=FLAG, [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
- fixed_range = FLAGS.fixed_range
+ raise TypeError(_('Fixed range in the form of 10.0.0.0/8 is '
+ 'required to create networks.'))
if not num_networks:
num_networks = FLAGS.num_networks
if not network_size:
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index 1c440b3a9..c99b945fb 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -22,6 +22,7 @@ from nova import context
from nova.api.openstack import faults
from nova.api.openstack import common
from nova.compute import instance_types
+from nova.api.openstack.views import flavors as flavors_views
from nova import wsgi
import nova.api.openstack
@@ -36,7 +37,7 @@ class Controller(wsgi.Controller):
def index(self, req):
"""Return all flavors in brief."""
- return dict(flavors=[dict(id=flavor['flavorid'], name=flavor['name'])
+ return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
for flavor in self.detail(req)['flavors']])
def detail(self, req):
@@ -47,14 +48,18 @@ class Controller(wsgi.Controller):
def show(self, req, id):
"""Return data about the given flavor id."""
ctxt = req.environ['nova.context']
- values = db.instance_type_get_by_flavor_id(ctxt, id)
- values['id'] = values['flavorid']
+ flavor = db.api.instance_type_get_by_flavor_id(ctxt, id)
+ values = {
+ "id": flavor["flavorid"],
+ "name": flavor["name"],
+ "ram": flavor["memory_mb"],
+ "disk": flavor["local_gb"],
+ }
return dict(flavor=values)
- raise faults.Fault(exc.HTTPNotFound())
def _all_ids(self, req):
"""Return the list of all flavorids."""
ctxt = req.environ['nova.context']
- inst_types = db.instance_type_get_all(ctxt)
+ inst_types = db.api.instance_type_get_all(ctxt)
flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()]
return sorted(flavor_ids)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index c2781f6fb..576937cd8 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -39,6 +39,7 @@ import os
import random
import string
import socket
+import sys
import tempfile
import time
import functools
@@ -114,7 +115,13 @@ class ComputeManager(manager.Manager):
# and redocument the module docstring
if not compute_driver:
compute_driver = FLAGS.compute_driver
- self.driver = utils.import_object(compute_driver)
+
+ try:
+ self.driver = utils.import_object(compute_driver)
+ except ImportError:
+ LOG.error("Unable to load the virtualization driver.")
+ sys.exit(1)
+
self.network_manager = utils.import_object(FLAGS.network_manager)
self.volume_manager = utils.import_object(FLAGS.volume_manager)
super(ComputeManager, self).__init__(*args, **kwargs)
@@ -221,8 +228,9 @@ class ComputeManager(manager.Manager):
instance_id,
{'launched_at': now})
except Exception: # pylint: disable=W0702
- LOG.exception(_("instance %s: Failed to spawn"), instance_id,
- context=context)
+ LOG.exception(_("Instance '%s' failed to spawn. Is virtualization"
+ " enabled in the BIOS?"), instance_id,
+ context=context)
self.db.instance_set_state(context,
instance_id,
power_state.SHUTDOWN)
diff --git a/nova/image/local.py b/nova/image/local.py
index c4ac3baaa..609d6c42a 100644
--- a/nova/image/local.py
+++ b/nova/image/local.py
@@ -20,8 +20,9 @@ import os.path
import random
import shutil
-from nova import flags
from nova import exception
+from nova import flags
+from nova import log as logging
from nova.image import service
@@ -29,6 +30,8 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('images_path', '$state_path/images',
'path to decrypted images')
+LOG = logging.getLogger('nova.image.local')
+
class LocalImageService(service.BaseImageService):
"""Image service storing images to local disk.
@@ -47,7 +50,17 @@ class LocalImageService(service.BaseImageService):
def _ids(self):
"""The list of all image ids."""
- return [int(i, 16) for i in os.listdir(self._path)]
+ images = []
+ for image_dir in os.listdir(self._path):
+ try:
+ unhexed_image_id = int(image_dir, 16)
+ except ValueError:
+ LOG.error(
+ _("%s is not in correct directory naming format"\
+ % image_dir))
+ else:
+ images.append(unhexed_image_id)
+ return images
def index(self, context):
return [dict(image_id=i['id'], name=i.get('name'))
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 565732869..ee36407a6 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -557,6 +557,7 @@ def get_dhcp_hosts(context, network_id):
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
+@utils.synchronized('dnsmasq_start')
def update_dhcp(context, network_id):
"""(Re)starts a dnsmasq server for a given network
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 0decb126a..91519a2ab 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -73,7 +73,7 @@ flags.DEFINE_string('flat_interface', None,
flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2',
'Dhcp start for FlatDhcp')
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
-flags.DEFINE_integer('num_networks', 1000, 'Number of networks to support')
+flags.DEFINE_integer('num_networks', 1, 'Number of networks to support')
flags.DEFINE_string('vpn_ip', '$my_ip',
'Public IP for the cloudpipe VPN servers')
flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 30326dc50..4f504808c 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -22,11 +22,32 @@ import webob
from nova import test
import nova.api
from nova import context
-from nova import db
from nova.api.openstack import flavors
+from nova import db
from nova.tests.api.openstack import fakes
+def stub_flavor(flavorid, name, memory_mb="256", local_gb="10"):
+ return {
+ "flavorid": str(flavorid),
+ "name": name,
+ "memory_mb": memory_mb,
+ "local_gb": local_gb,
+ }
+
+
+def return_instance_type_by_flavor_id(context, flavorid):
+ return stub_flavor(flavorid, "flavor %s" % (flavorid,))
+
+
+def return_instance_types(context, num=2):
+ instance_types = {}
+ for i in xrange(1, num + 1):
+ name = "flavor %s" % (i,)
+ instance_types[name] = stub_flavor(i, name)
+ return instance_types
+
+
class FlavorsTest(test.TestCase):
def setUp(self):
super(FlavorsTest, self).setUp()
@@ -36,6 +57,10 @@ class FlavorsTest(test.TestCase):
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
+ self.stubs.Set(nova.db.api, "instance_type_get_all",
+ return_instance_types)
+ self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id",
+ return_instance_type_by_flavor_id)
self.context = context.get_admin_context()
def tearDown(self):
@@ -46,10 +71,49 @@ class FlavorsTest(test.TestCase):
req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
+ flavors = json.loads(res.body)["flavors"]
+ expected = [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ },
+ ]
+ self.assertEqual(flavors, expected)
+
+ def test_get_flavor_list_detail(self):
+ req = webob.Request.blank('/v1.0/flavors/detail')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ flavors = json.loads(res.body)["flavors"]
+ expected = [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ "ram": "256",
+ "disk": "10",
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ "ram": "256",
+ "disk": "10",
+ },
+ ]
+ self.assertEqual(flavors, expected)
def test_get_flavor_by_id(self):
- req = webob.Request.blank('/v1.0/flavors/1')
+ req = webob.Request.blank('/v1.0/flavors/12')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
- body = json.loads(res.body)
- self.assertEqual(body['flavor']['id'], 1)
+ flavor = json.loads(res.body)["flavor"]
+ expected = {
+ "id": "12",
+ "name": "flavor 12",
+ "ram": "256",
+ "disk": "10",
+ }
+ self.assertEqual(flavor, expected)
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 76f758929..a674ccefe 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -22,6 +22,7 @@ and as a WSGI layer
import json
import datetime
+import os
import shutil
import tempfile
@@ -151,6 +152,17 @@ class LocalImageServiceTest(test.TestCase,
self.stubs.UnsetAll()
super(LocalImageServiceTest, self).tearDown()
+ def test_get_all_ids_with_incorrect_directory_formats(self):
+ # create some old-style image directories (starting with 'ami-')
+ for x in [1, 2, 3]:
+ tempfile.mkstemp(prefix='ami-', dir=self.tempdir)
+ # create some valid image directories names
+ for x in ["1485baed", "1a60f0ee", "3123a73d"]:
+ os.makedirs(os.path.join(self.tempdir, x))
+ found_image_ids = self.service._ids()
+ self.assertEqual(True, isinstance(found_image_ids, list))
+ self.assertEqual(3, len(found_image_ids), len(found_image_ids))
+
class GlanceImageServiceTest(test.TestCase,
BaseImageServiceTests):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index ec67dd6d7..89ff85b7d 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -432,11 +432,8 @@ class XenAPIVMTestCase(test.TestCase):
# When mounting, create real files under the mountpoint to simulate
# files in the mounted filesystem
- # RegExp extracts the path of the mountpoint
- cmd_str = ' '.join(cmd)
- match = re.match(r'(sudo\s+)?mount[^"]*"[^"]*"\s+"([^"]*)"',
- cmd_str)
- self._tmpdir = match.group(2)
+ # mount point will be the last item of the command list
+ self._tmpdir = cmd[len(cmd) - 1]
LOG.debug(_('Creating files in %s to simulate guest agent' %
self._tmpdir))
os.makedirs(os.path.join(self._tmpdir, 'usr', 'sbin'))
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 287a293f0..7b6f2b5da 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -238,11 +238,11 @@ class VMHelper(HelperBase):
@classmethod
def create_vif(cls, session, vm_ref, network_ref, mac_address,
- dev="0", rxtx_cap=0):
+ dev, rxtx_cap=0):
"""Create a VIF record. Returns a Deferred that gives the new
VIF reference."""
vif_rec = {}
- vif_rec['device'] = dev
+ vif_rec['device'] = str(dev)
vif_rec['network'] = network_ref
vif_rec['VM'] = vm_ref
vif_rec['MAC'] = mac_address
@@ -699,8 +699,7 @@ class VMHelper(HelperBase):
try:
out, err = utils.execute('sudo', 'mount',
'-t', 'ext2,ext3',
- '%s' % dev_path,
- '%s' % tmpdir)
+ dev_path, tmpdir)
except exception.ProcessExecutionError as e:
err = str(e)
if err:
@@ -1067,8 +1066,8 @@ def _write_partition(virtual_size, dev):
def execute(*cmd, **kwargs):
return utils.execute(*cmd, **kwargs)
- execute('parted', '--script', dest, 'mklabel', 'msdos')
- execute('parted', '--script', dest, 'mkpart', 'primary',
+ execute('sudo', 'parted', '--script', dest, 'mklabel', 'msdos')
+ execute('sudo', 'parted', '--script', dest, 'mkpart', 'primary',
'%ds' % primary_first,
'%ds' % primary_last)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index f0f7dc100..4bac251b0 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -82,11 +82,11 @@ class VMOps(object):
instance.image_id, user, project, disk_image_type)
return vdi_uuid
- def spawn(self, instance):
+ def spawn(self, instance, network_info=None):
vdi_uuid = self.create_disk(instance)
- self._spawn_with_disk(instance, vdi_uuid=vdi_uuid)
+ self._spawn_with_disk(instance, vdi_uuid, network_info)
- def _spawn_with_disk(self, instance, vdi_uuid):
+ def _spawn_with_disk(self, instance, vdi_uuid, network_info=None):
"""Create VM instance"""
instance_name = instance.name
vm_ref = VMHelper.lookup(self._session, instance_name)
@@ -134,8 +134,12 @@ class VMOps(object):
VMHelper.preconfigure_instance(self._session, instance, vdi_ref)
# inject_network_info and create vifs
- networks = self.inject_network_info(instance)
- self.create_vifs(instance, networks)
+ # TODO(tr3buchet) - check to make sure we have network info, otherwise
+ # create it now. This goes away once nova-multi-nic hits.
+ if network_info is None:
+ network_info = self._get_network_info(instance)
+ self.create_vifs(vm_ref, network_info)
+ self.inject_network_info(instance, vm_ref, network_info)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._start(instance, vm_ref)
@@ -187,7 +191,7 @@ class VMOps(object):
timer.f = _wait_for_boot
# call to reset network to configure network from xenstore
- self.reset_network(instance)
+ self.reset_network(instance, vm_ref)
return timer.start(interval=0.5, now=True)
@@ -682,24 +686,17 @@ class VMOps(object):
# TODO: implement this!
return 'http://fakeajaxconsole/fake_url'
- def inject_network_info(self, instance):
- """
- Generate the network info and make calls to place it into the
- xenstore and the xenstore param list
-
- """
- # TODO(tr3buchet) - remove comment in multi-nic
- # I've decided to go ahead and consider multiple IPs and networks
- # at this stage even though they aren't implemented because these will
- # be needed for multi-nic and there was no sense writing it for single
- # network/single IP and then having to turn around and re-write it
- vm_ref = self._get_vm_opaque_ref(instance.id)
- logging.debug(_("injecting network info to xenstore for vm: |%s|"),
- vm_ref)
+ # TODO(tr3buchet) - remove this function after nova multi-nic
+ def _get_network_info(self, instance):
+ """creates network info list for instance"""
admin_context = context.get_admin_context()
- IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id'])
+ IPs = db.fixed_ip_get_all_by_instance(admin_context,
+ instance['id'])
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
+ flavor = db.instance_type_get_by_name(admin_context,
+ instance['instance_type'])
+ network_info = []
for network in networks:
network_IPs = [ip for ip in IPs if ip.network_id == network.id]
@@ -716,69 +713,65 @@ class VMOps(object):
"gateway": ip6.gatewayV6,
"enabled": "1"}
- mac_id = instance.mac_address.replace(':', '')
- location = 'vm-data/networking/%s' % mac_id
- #salvatore-orlando: key for broadcast address
- #provisionally set to 'broadcast'
- mapping = {'label': network['label'],
- 'gateway': network['gateway'],
- 'broadcast': network['broadcast'],
- 'mac': instance.mac_address,
- 'dns': [network['dns']],
- 'ips': [ip_dict(ip) for ip in network_IPs],
- 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
+ info = {
+ 'label': network['label'],
+ 'gateway': network['gateway'],
+ 'broadcast': network['broadcast'],
+ 'mac': instance.mac_address,
+ 'rxtx_cap': flavor['rxtx_cap'],
+ 'dns': [network['dns']],
+ 'ips': [ip_dict(ip) for ip in network_IPs],
+ 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
+ network_info.append((network, info))
+ return network_info
+
+ def inject_network_info(self, instance, vm_ref, network_info):
+ """
+ Generate the network info and make calls to place it into the
+ xenstore and the xenstore param list
+ """
+ logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref)
- self.write_to_param_xenstore(vm_ref, {location: mapping})
+ # this function raises if vm_ref is not a vm_opaque_ref
+ self._session.get_xenapi().VM.get_record(vm_ref)
+ for (network, info) in network_info:
+ location = 'vm-data/networking/%s' % info['mac'].replace(':', '')
+ self.write_to_param_xenstore(vm_ref, {location: info})
try:
- self.write_to_xenstore(vm_ref, location, mapping['location'])
+ # TODO(tr3buchet): fix function call after refactor
+ #self.write_to_xenstore(vm_ref, location, info)
+ self._make_plugin_call('xenstore.py', 'write_record', instance,
+ location, {'value': json.dumps(info)},
+ vm_ref)
except KeyError:
# catch KeyError for domid if instance isn't running
pass
- return networks
+ def create_vifs(self, vm_ref, network_info):
+ """Creates vifs for an instance"""
+ logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
- def create_vifs(self, instance, networks=None):
- """
- Creates vifs for an instance
+ # this function raises if vm_ref is not a vm_opaque_ref
+ self._session.get_xenapi().VM.get_record(vm_ref)
- """
- vm_ref = self._get_vm_opaque_ref(instance['id'])
- admin_context = context.get_admin_context()
- flavor = db.instance_type_get_by_name(admin_context,
- instance.instance_type)
- logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
- rxtx_cap = flavor['rxtx_cap']
- if networks is None:
- networks = db.network_get_all_by_instance(admin_context,
- instance['id'])
- # TODO(tr3buchet) - remove comment in multi-nic
- # this bit here about creating the vifs will be updated
- # in multi-nic to handle multiple IPs on the same network
- # and multiple networks
- # for now it works as there is only one of each
- for network in networks:
+ for device, (network, info) in enumerate(network_info):
+ mac_address = info['mac']
bridge = network['bridge']
+ rxtx_cap = info.pop('rxtx_cap')
network_ref = \
NetworkHelper.find_network_with_bridge(self._session, bridge)
- if network_ref:
- try:
- device = "1" if instance._rescue else "0"
- except (AttributeError, KeyError):
- device = "0"
+ VMHelper.create_vif(self._session, vm_ref, network_ref,
+ mac_address, device, rxtx_cap)
- VMHelper.create_vif(self._session, vm_ref, network_ref,
- instance.mac_address, device,
- rxtx_cap=rxtx_cap)
-
- def reset_network(self, instance):
- """
- Creates uuid arg to pass to make_agent_call and calls it.
-
- """
+ def reset_network(self, instance, vm_ref):
+ """Creates uuid arg to pass to make_agent_call and calls it."""
args = {'id': str(uuid.uuid4())}
- resp = self._make_agent_call('resetnetwork', instance, '', args)
+ # TODO(tr3buchet): fix function call after refactor
+ #resp = self._make_agent_call('resetnetwork', instance, '', args)
+ resp = self._make_plugin_call('agent', 'resetnetwork', instance, '',
+ args, vm_ref)
def list_from_xenstore(self, vm, path):
"""Runs the xenstore-ls command to get a listing of all records
@@ -819,25 +812,26 @@ class VMOps(object):
"""
self._make_xenstore_call('delete_record', vm, path)
- def _make_xenstore_call(self, method, vm, path, addl_args={}):
+ def _make_xenstore_call(self, method, vm, path, addl_args=None):
"""Handles calls to the xenstore xenapi plugin."""
return self._make_plugin_call('xenstore.py', method=method, vm=vm,
path=path, addl_args=addl_args)
- def _make_agent_call(self, method, vm, path, addl_args={}):
+ def _make_agent_call(self, method, vm, path, addl_args=None):
"""Abstracts out the interaction with the agent xenapi plugin."""
return self._make_plugin_call('agent', method=method, vm=vm,
path=path, addl_args=addl_args)
- def _make_plugin_call(self, plugin, method, vm, path, addl_args={}):
+ def _make_plugin_call(self, plugin, method, vm, path, addl_args=None,
+ vm_ref=None):
"""Abstracts out the process of calling a method of a xenapi plugin.
Any errors raised by the plugin will in turn raise a RuntimeError here.
"""
instance_id = vm.id
- vm_ref = self._get_vm_opaque_ref(vm)
- vm_rec = self._session.call_xenapi('VM.get_record', vm_ref)
+ vm_ref = vm_ref or self._get_vm_opaque_ref(vm)
+ vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
args = {'dom_id': vm_rec['domid'], 'path': path}
- args.update(addl_args)
+ args.update(addl_args or {})
try:
task = self._session.async_call_plugin(plugin, method, args)
ret = self._session.wait_for_task(task, instance_id)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index db39cb0f4..0a45f3873 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -216,8 +216,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type):
'x-image-meta-status': 'queued',
'x-image-meta-disk-format': 'vhd',
'x-image-meta-container-format': 'ovf',
- 'x-image-meta-property-os-type': os_type,
- }
+ 'x-image-meta-property-os-type': os_type}
for header, value in headers.iteritems():
conn.putheader(header, value)