From 91458f29f8b04407471d9d94a5ad165817f72a43 Mon Sep 17 00:00:00 2001 From: Muneyuki Noguchi Date: Fri, 25 Mar 2011 11:47:17 +0900 Subject: Restore volume state on migration failure. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2cecb010d..5c6baa36e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1370,7 +1370,7 @@ class LibvirtConnection(driver.ComputeDriver): FLAGS.live_migration_bandwidth) except Exception: - recover_method(ctxt, instance_ref) + recover_method(ctxt, instance_ref, None, dest) raise # Waiting for completion of live_migration. -- cgit From 5e6c69bc7a7e5ddaa1bf0fa83f64da116343dba8 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 29 Mar 2011 16:35:39 -0700 Subject: Narrowly focused bugfix - don't lose libvirt instances on host reboot or if they crash --- nova/virt/libvirt_conn.py | 59 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c144e827e..533ff9394 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -116,6 +116,8 @@ flags.DEFINE_integer('live_migration_bandwidth', 0, 'Define live migration behavior') flags.DEFINE_string('qemu_img', 'qemu-img', 'binary to use for qemu-img commands') +flags.DEFINE_bool('start_guests_on_host_boot', False, + 'Whether to restart guests when the host reboots') def get_connection(read_only): @@ -230,12 +232,14 @@ class LibvirtConnection(driver.ComputeDriver): {'name': instance['name'], 'state': state}) db.instance_set_state(ctxt, instance['id'], state) - if state == power_state.SHUTOFF: - # TODO(soren): This is what the compute manager does when you - # terminate # an instance. At some point I figure we'll have a - # "terminated" state and some sort of cleanup job that runs - # occasionally, cleaning them out. - db.instance_destroy(ctxt, instance['id']) + # NOTE(justinsb): We no longer delete these instances, + # the user may want to power them back on + #if state == power_state.SHUTOFF: + # # TODO(soren): This is what the compute manager does when you + # # terminate # an instance. At some point I figure we'll have a + # # "terminated" state and some sort of cleanup job that runs + # # occasionally, cleaning them out. + # db.instance_destroy(ctxt, instance['id']) if state != power_state.RUNNING: continue @@ -474,7 +478,7 @@ class LibvirtConnection(driver.ComputeDriver): xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) self.firewall_driver.prepare_instance_filter(instance) - self._conn.createXML(xml, 0) + self._create_new_domain(xml) self.firewall_driver.apply_instance_filter(instance) timer = utils.LoopingCall(f=None) @@ -522,7 +526,7 @@ class LibvirtConnection(driver.ComputeDriver): 'kernel_id': FLAGS.rescue_kernel_id, 'ramdisk_id': FLAGS.rescue_ramdisk_id} self._create_image(instance, xml, '.rescue', rescue_images) - self._conn.createXML(xml, 0) + self._create_new_domain(xml) timer = utils.LoopingCall(f=None) @@ -565,10 +569,15 @@ class LibvirtConnection(driver.ComputeDriver): self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(instance, xml, network_info) - self._conn.createXML(xml, 0) + domain = self._create_new_domain(xml) LOG.debug(_("instance %s: is running"), instance['name']) self.firewall_driver.apply_instance_filter(instance) + if FLAGS.start_guests_on_host_boot: + LOG.debug(_("instance %s: setting autostart ON") % + instance['name']) + domain.setAutostart(1) + timer = utils.LoopingCall(f=None) def _wait_for_boot(): @@ -964,11 +973,19 @@ class LibvirtConnection(driver.ComputeDriver): return xml def get_info(self, instance_name): + # NOTE(justinsb): When libvirt isn't running / can't connect, we get: + # libvir: Remote error : unable to connect to + # '/var/run/libvirt/libvirt-sock', libvirtd may need to be started: + # No such file or directory try: virt_dom = self._conn.lookupByName(instance_name) - except: - raise exception.NotFound(_("Instance %s not found") - % instance_name) + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_UNKNOWN_HOST: + raise exception.NotFound(_("Instance %s not found") + % instance_name) + LOG.warning(_("Error from libvirt during lookup: %s") % e) + raise + (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() return {'state': state, 'max_mem': max_mem, @@ -976,6 +993,24 @@ class LibvirtConnection(driver.ComputeDriver): 'num_cpu': num_cpu, 'cpu_time': cpu_time} + def _create_new_domain(self, xml, persistent=True, launch_flags=0): + # NOTE(justinsb): libvirt has two types of domain: + # * a transient domain disappears when the guest is shutdown + # or the host is rebooted. + # * a permanent domain is not automatically deleted + # NOTE(justinsb): Even for ephemeral instances, transient seems risky + + if persistent: + # To create a persistent domain, first define it, then launch it. + domain = self._conn.defineXML(xml) + + domain.createWithFlags(launch_flags) + else: + # createXML call creates a transient domain + domain = self._conn.createXML(xml, launch_flags) + + return domain + def get_diagnostics(self, instance_name): raise exception.ApiError(_("diagnostics are not supported " "for libvirt")) -- cgit From 1703592992ebdd5bbf19952f79f05022a4cdc849 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 12:34:10 -0700 Subject: remove all references to image_type and change nova-manage upload to set container format more intelligently --- nova/virt/libvirt_conn.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..adcb2ffa3 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -424,7 +424,6 @@ class LibvirtConnection(driver.ComputeDriver): 'container_format': base['container_format'], 'is_public': False, 'properties': {'architecture': base['architecture'], - 'type': base['type'], 'name': '%s.%s' % (base['name'], image_id), 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', -- cgit From 7688cbb07ffcfd6446dc9ede60fb9eb610809c1d Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 31 Mar 2011 16:46:08 -0400 Subject: Removal of instance_set_state from driver code, it shouldnt be there, but instead should be in the compute manager. --- nova/virt/libvirt_conn.py | 4 ++-- nova/virt/xenapi/vmops.py | 26 ++++++++++---------------- 2 files changed, 12 insertions(+), 18 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f998a592b..bc9a031f9 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -597,8 +597,8 @@ class LibvirtConnection(driver.ComputeDriver): try: state = self.get_info(name)['state'] except (exception.NotFound, libvirt.libvirtError) as ex: - msg = _("Error while waiting for VM to run: %s") % ex - LOG.debug(msg) + msg = _("Error while waiting for VM '%(_id)s' to run: %(ex)s") + LOG.debug(msg % locals()) timer.stop() if state == power_state.RUNNING: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c96c35a6e..fb3ca5306 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -206,33 +206,27 @@ class VMOps(object): # NOTE(armando): Do we really need to do this in virt? # NOTE(tr3buchet): not sure but wherever we do it, we need to call # reset_network afterwards - timer = utils.LoopingCall(f=None) def _wait_for_boot(): try: state = self.get_info(instance_name)['state'] - db.instance_set_state(context.get_admin_context(), - instance['id'], state) - if state == power_state.RUNNING: - LOG.debug(_('Instance %s: booted'), instance_name) - timer.stop() - _inject_files() - return True - except Exception, exc: - LOG.warn(exc) - LOG.exception(_('instance %s: failed to boot'), - instance_name) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) + except self.XenAPI.Failure as ex: + msg = _("Error while waiting for VM '%(instance_name)s' " + "to boot: %(ex)s") % locals() + LOG.debug(msg) timer.stop() return False - timer.f = _wait_for_boot + if state == power_state.RUNNING: + LOG.debug(_('VM %s is now running.') % name) + timer.stop() + _inject_files() + return True # call to reset network to configure network from xenstore self.reset_network(instance, vm_ref) + timer = utils.LoopingCall(f=_wait_for_boot) return timer.start(interval=0.5, now=True) def _get_vm_opaque_ref(self, instance_or_vm): -- cgit From a8d186d212ffbc628fc2b2672eca1e0557c57414 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sun, 3 Apr 2011 03:45:33 +0400 Subject: split up to_xml to creation xml_info and filling the template --- nova/virt/libvirt_conn.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..910d8a634 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -936,7 +936,7 @@ class LibvirtConnection(driver.ComputeDriver): return result - def to_xml(self, instance, rescue=False, network_info=None): + def _prepare_xml_info(self, instance, rescue=False, network_info=None): # TODO(termie): cache? LOG.debug(_('instance %s: starting toXML method'), instance['name']) @@ -947,8 +947,7 @@ class LibvirtConnection(driver.ComputeDriver): nics = [] for (network, mapping) in network_info: - nics.append(self._get_nic_for_xml(network, - mapping)) + nics.append(self._get_nic_for_xml(network, mapping)) # FIXME(vish): stick this in db instance_type_name = instance['instance_type'] instance_type = instance_types.get_instance_type(instance_type_name) @@ -979,10 +978,13 @@ class LibvirtConnection(driver.ComputeDriver): xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk" xml_info['disk'] = xml_info['basepath'] + "/disk" + + return xml_info + def to_xml(self, instance, rescue=False, network_info=None): + xml_info = self._prepare_xml_info(instance, rescue, network_info) xml = str(Template(self.libvirt_xml, searchList=[xml_info])) - LOG.debug(_('instance %s: finished toXML method'), - instance['name']) + LOG.debug(_('instance %s: finished toXML method'), instance['name']) return xml def get_info(self, instance_name): -- cgit From 74d9a325a452fb927e5edddca3f1b7edd35d1496 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sun, 3 Apr 2011 21:18:35 +0400 Subject: added preparing_xml test --- nova/virt/libvirt_conn.py | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 910d8a634..8af5eb025 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -899,26 +899,16 @@ class LibvirtConnection(driver.ComputeDriver): mac_id = mapping['mac'].replace(':', '') if FLAGS.allow_project_net_traffic: + template = "\n" + net, mask = _get_net_and_mask(network['cidr']) + values = [("PROJNET", net), ("PROJMASK", mask)] if FLAGS.use_ipv6: - net, mask = _get_net_and_mask(network['cidr']) net_v6, prefixlen_v6 = _get_net_and_prefixlen( network['cidr_v6']) - extra_params = ("\n" - "\n" - "\n" - "\n") % \ - (net, mask, net_v6, prefixlen_v6) - else: - net, mask = _get_net_and_mask(network['cidr']) - extra_params = ("\n" - "\n") % \ - (net, mask) + values.extend([("PROJNETV6", net_v6), + ("PROJMASKV6", prefixlen_v6)]) + + extra_params = "".join([template % value for value in values]) else: extra_params = "\n" -- cgit From 8969cb1f22a7760dc7e17c578a686f088b1a8d89 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sun, 3 Apr 2011 22:50:38 +0400 Subject: add multi_nic_test --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8af5eb025..5c7540927 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -908,7 +908,7 @@ class LibvirtConnection(driver.ComputeDriver): values.extend([("PROJNETV6", net_v6), ("PROJMASKV6", prefixlen_v6)]) - extra_params = "".join([template % value for value in values]) + extra_params = "".join([template % value for value in values]) else: extra_params = "\n" @@ -968,7 +968,6 @@ class LibvirtConnection(driver.ComputeDriver): xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk" xml_info['disk'] = xml_info['basepath'] + "/disk" - return xml_info def to_xml(self, instance, rescue=False, network_info=None): -- cgit From c1120caaa8c8ed8902b5634da56b2bd5478662e1 Mon Sep 17 00:00:00 2001 From: Muneyuki Noguchi Date: Mon, 4 Apr 2011 10:25:58 +0900 Subject: Use keyword arguments. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c5a71d244..c03c2ae1d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1475,7 +1475,7 @@ class LibvirtConnection(driver.ComputeDriver): FLAGS.live_migration_bandwidth) except Exception: - recover_method(ctxt, instance_ref, None, dest) + recover_method(ctxt, instance_ref, dest=dest) raise # Waiting for completion of live_migration. -- cgit From 80549a0085e7c3a90b117b4c9df5a77b4ecd0843 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Mon, 4 Apr 2011 18:33:50 +0400 Subject: improving tests --- nova/virt/libvirt_conn.py | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5c7540927..92519da65 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1877,34 +1877,21 @@ class IptablesFirewallDriver(FirewallDriver): self.add_filters_for_instance(instance, network_info) self.iptables.apply() - def add_filters_for_instance(self, instance, network_info=None): - if not network_info: - network_info = _get_network_info(instance) - chain_name = self._instance_chain_name(instance) - - self.iptables.ipv4['filter'].add_chain(chain_name) + def _create_filter(self, ips, chain_name): + return ['-d %s -j $%s' % (ip, chain_name) for ip in ips] + def _filters_for_instance(self, chain_name, network_info): ips_v4 = [ip['ip'] for (_, mapping) in network_info - for ip in mapping['ips']] - - for ipv4_address in ips_v4: - self.iptables.ipv4['filter'].add_rule('local', - '-d %s -j $%s' % - (ipv4_address, chain_name)) - - if FLAGS.use_ipv6: - self.iptables.ipv6['filter'].add_chain(chain_name) - ips_v6 = [ip['ip'] for (_, mapping) in network_info - for ip in mapping['ip6s']] + for ip in mapping['ips']] + ipv4_rules = self._create_filter(ips_v4, chain_name) - for ipv6_address in ips_v6: - self.iptables.ipv6['filter'].add_rule('local', - '-d %s -j $%s' % - (ipv6_address, - chain_name)) + ips_v6 = [ip['ip'] for (_, mapping) in network_info + for ip in mapping['ip6s']] - ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info) + ipv6_rules = self._create_filter(ips_v6, chain_name) + return ipv4_rules, ipv6_rules + def _add_filters(self, chain_name, ipv4_rules, ipv6_rules): for rule in ipv4_rules: self.iptables.ipv4['filter'].add_rule(chain_name, rule) @@ -1912,6 +1899,17 @@ class IptablesFirewallDriver(FirewallDriver): for rule in ipv6_rules: self.iptables.ipv6['filter'].add_rule(chain_name, rule) + def add_filters_for_instance(self, instance, network_info=None): + chain_name = self._instance_chain_name(instance) + if FLAGS.use_ipv6: + self.iptables.ipv6['filter'].add_chain(chain_name) + self.iptables.ipv4['filter'].add_chain(chain_name) + ipv4_rules, ipv6_rules = self._filters_for_instance(chain_name, + network_info) + self._add_filters('local', ipv4_rules, ipv6_rules) + ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info) + self._add_filters(chain_name, ipv4_rules, ipv6_rules) + def remove_filters_for_instance(self, instance): chain_name = self._instance_chain_name(instance) -- cgit From d7053efa810aa3d20ef7cd089429c6d96f451a7d Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Mon, 4 Apr 2011 21:05:38 +0400 Subject: Fixed network_info creating. --- nova/virt/libvirt_conn.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..93a250502 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -167,6 +167,9 @@ def _get_network_info(instance): 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 = [] def ip_dict(ip): @@ -191,7 +194,9 @@ def _get_network_info(instance): mapping = { '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]} -- cgit From 917f7aafbfa0a797687d10a600a218517f9b75e0 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Mon, 4 Apr 2011 22:22:27 +0400 Subject: add test for NWFilterFirewall --- nova/virt/libvirt_conn.py | 60 ++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6c99e5448..57d0f4355 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1740,10 +1740,7 @@ class NWFilterFirewall(FirewallDriver): """ if not network_info: network_info = _get_network_info(instance) - if instance['image_id'] == FLAGS.vpn_image_id: - base_filter = 'nova-vpn' - else: - base_filter = 'nova-base' + ctxt = context.get_admin_context() @@ -1755,41 +1752,60 @@ class NWFilterFirewall(FirewallDriver): 'nova-base-ipv6', 'nova-allow-dhcp-server'] + if FLAGS.use_ipv6: + networks = [network for (network, _) in network_info if + network['gateway_v6']] + + if networks: + instance_secgroup_filter_children.\ + append('nova-allow-ra-server') + for security_group in \ db.security_group_get_by_instance(ctxt, instance['id']): self.refresh_security_group_rules(security_group['id']) - instance_secgroup_filter_children += [('nova-secgroup-%s' % - security_group['id'])] + instance_secgroup_filter_children.append('nova-secgroup-%s' % + security_group['id']) self._define_filter( self._filter_container(instance_secgroup_filter_name, instance_secgroup_filter_children)) - for (network, mapping) in network_info: - nic_id = mapping['mac'].replace(':', '') - instance_filter_name = self._instance_filter_name(instance, nic_id) - instance_filter_children = \ - [base_filter, instance_secgroup_filter_name] + network_filters = self.\ + _create_network_filters(instance, network_info, + instance_secgroup_filter_name) - if FLAGS.use_ipv6: - gateway_v6 = network['gateway_v6'] + for (name, children) in network_filters: + self._define_filters(name, children) - if gateway_v6: - instance_secgroup_filter_children += \ - ['nova-allow-ra-server'] + + def _create_network_filters(self, instance, network_info, + instance_secgroup_filter_name): + if instance['image_id'] == FLAGS.vpn_image_id: + base_filter = 'nova-vpn' + else: + base_filter = 'nova-base' + + result = [] + for (_, mapping) in network_info: + nic_id = mapping['mac'].replace(':', '') + instance_filter_name = self._instance_filter_name(instance, nic_id) + instance_filter_children = [base_filter, + instance_secgroup_filter_name] if FLAGS.allow_project_net_traffic: - instance_filter_children += ['nova-project'] + instance_filter_children.append('nova-project') if FLAGS.use_ipv6: - instance_filter_children += ['nova-project-v6'] + instance_filter_children.append('nova-project-v6') - self._define_filter( - self._filter_container(instance_filter_name, - instance_filter_children)) + result.append((instance_filter_name, instance_filter_children)) - return + return result + + def _define_filters(self, filter_name, filter_children): + self._define_filter(self._filter_container(filter_name, + filter_children)) def refresh_security_group_rules(self, security_group_id): return self._define_filter( -- cgit From e057d7fd01def4db0c77b962fea925177de9a91f Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 4 Apr 2011 15:20:09 -0400 Subject: fixing log message --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fb3ca5306..1dc5624eb 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -218,7 +218,7 @@ class VMOps(object): return False if state == power_state.RUNNING: - LOG.debug(_('VM %s is now running.') % name) + LOG.debug(_('VM %s is now running.') % instance_name) timer.stop() _inject_files() return True -- cgit From 5e74b5a5f121c9f0be2c529b76878615812d9483 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Mon, 4 Apr 2011 23:43:26 +0400 Subject: splitting test_get_nic_for_xml into two functions --- nova/virt/libvirt_conn.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 57d0f4355..0ca2cce9a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1741,7 +1741,6 @@ class NWFilterFirewall(FirewallDriver): if not network_info: network_info = _get_network_info(instance) - ctxt = context.get_admin_context() instance_secgroup_filter_name = \ @@ -1779,7 +1778,6 @@ class NWFilterFirewall(FirewallDriver): for (name, children) in network_filters: self._define_filters(name, children) - def _create_network_filters(self, instance, network_info, instance_secgroup_filter_name): if instance['image_id'] == FLAGS.vpn_image_id: -- cgit From 38b4cd9e68d7e1c262b08474b277573440ec3e87 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 4 Apr 2011 16:17:04 -0400 Subject: Refactor so that instances.instance_type is now instances.instance_type_id. Update the Openstack API to return the correct flavor_id. --- nova/virt/libvirt_conn.py | 22 ++++++++++++---------- nova/virt/xenapi/vm_utils.py | 8 ++++---- nova/virt/xenapi/vmops.py | 8 +++++--- 3 files changed, 21 insertions(+), 17 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..6b7fce634 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -797,7 +797,10 @@ class LibvirtConnection(driver.ComputeDriver): root_fname = '%08x' % int(disk_images['image_id']) size = FLAGS.minimum_root_size - if inst['instance_type'] == 'm1.tiny' or suffix == '.rescue': + + inst_type_id = instance['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) + if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': size = None root_fname += "_sm" @@ -809,14 +812,13 @@ class LibvirtConnection(driver.ComputeDriver): user=user, project=project, size=size) - type_data = instance_types.get_instance_type(inst['instance_type']) - if type_data['local_gb']: + if inst_type['local_gb']: self._cache_image(fn=self._create_local, target=basepath('disk.local'), - fname="local_%s" % type_data['local_gb'], + fname="local_%s" % inst_type['local_gb'], cow=FLAGS.use_cow_images, - local_gb=type_data['local_gb']) + local_gb=inst_type['local_gb']) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -950,8 +952,8 @@ class LibvirtConnection(driver.ComputeDriver): nics.append(self._get_nic_for_xml(network, mapping)) # FIXME(vish): stick this in db - instance_type_name = instance['instance_type'] - instance_type = instance_types.get_instance_type(instance_type_name) + inst_type_id = instance['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) if FLAGS.use_cow_images: driver_type = 'qcow2' @@ -962,10 +964,10 @@ class LibvirtConnection(driver.ComputeDriver): 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, instance['name']), - 'memory_kb': instance_type['memory_mb'] * 1024, - 'vcpus': instance_type['vcpus'], + 'memory_kb': inst_type['memory_mb'] * 1024, + 'vcpus': inst_type['vcpus'], 'rescue': rescue, - 'local': instance_type['local_gb'], + 'local': inst_type['local_gb'], 'driver_type': driver_type, 'nics': nics} diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..fd4f3705a 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -101,8 +101,8 @@ class VMHelper(HelperBase): 3. Using hardware virtualization """ - instance_type = instance_types.\ - get_instance_type(instance.instance_type) + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { @@ -169,8 +169,8 @@ class VMHelper(HelperBase): @classmethod def ensure_free_mem(cls, session, instance): - instance_type = instance_types.get_instance_type( - instance.instance_type) + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c96c35a6e..c26965c9a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -802,8 +802,10 @@ class VMOps(object): 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']) + + inst_type = db.instance_type_get_by_id(admin_context, + instance['instance_type_id']) + network_info = [] for network in networks: network_IPs = [ip for ip in IPs if ip.network_id == network.id] @@ -827,7 +829,7 @@ class VMOps(object): 'gateway': network['gateway'], 'broadcast': network['broadcast'], 'mac': instance.mac_address, - 'rxtx_cap': flavor['rxtx_cap'], + 'rxtx_cap': inst_type['rxtx_cap'], 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_IPs]} if network['cidr_v6']: -- cgit From a18fece993c21f2ae1cbb44d8a0dea92d58d3b44 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 4 Apr 2011 22:16:53 -0400 Subject: Correct variable name. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d4cef8d7c..099ef647c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -798,7 +798,7 @@ class LibvirtConnection(driver.ComputeDriver): root_fname = '%08x' % int(disk_images['image_id']) size = FLAGS.minimum_root_size - inst_type_id = instance['instance_type_id'] + inst_type_id = inst['instance_type_id'] inst_type = instance_types.get_instance_type(inst_type_id) if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': size = None -- cgit From 2b3aea4be35f370c68ac3c24ab15d4851aa28e94 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Tue, 5 Apr 2011 20:10:10 +0900 Subject: fix bug 746821 --- nova/virt/libvirt_conn.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index babbc610d..bdf577825 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1401,18 +1401,13 @@ class LibvirtConnection(driver.ComputeDriver): # wait for completion timeout_count = range(FLAGS.live_migration_retry_count) while timeout_count: - try: - filter_name = 'nova-instance-%s' % instance_ref.name - self._conn.nwfilterLookupByName(filter_name) + if self.firewall_driver.instance_filter_exists(instance_ref): break - except libvirt.libvirtError: - timeout_count.pop() - if len(timeout_count) == 0: - ec2_id = instance_ref['hostname'] - iname = instance_ref.name - msg = _('Timeout migrating for %(ec2_id)s(%(iname)s)') - raise exception.Error(msg % locals()) - time.sleep(1) + timeout_count.pop() + if len(timeout_count) == 0: + msg = _('Timeout migrating for %s. nwfilter not found.') + raise exception.Error(msg % instance_ref.name) + time.sleep(1) def live_migration(self, ctxt, instance_ref, dest, post_method, recover_method): @@ -1541,6 +1536,10 @@ class FirewallDriver(object): """ raise NotImplementedError() + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + raise NotImplementedError() + class NWFilterFirewall(FirewallDriver): """ @@ -1848,6 +1847,19 @@ class NWFilterFirewall(FirewallDriver): return 'nova-instance-%s' % (instance['name']) return 'nova-instance-%s-%s' % (instance['name'], nic_id) + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + + network_info = _get_network_info(instance) + for (network, mapping) in network_info: + nic_id = mapping['mac'].replace(':', '') + instance_filter_name = self._instance_filter_name(instance, nic_id) + try: + self._conn.nwfilterLookupByName(instance_filter_name) + except libvirt.libvirtError: + return False + return True + class IptablesFirewallDriver(FirewallDriver): def __init__(self, execute=None, **kwargs): @@ -2037,6 +2049,10 @@ class IptablesFirewallDriver(FirewallDriver): return ipv4_rules, ipv6_rules + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + return self.nwfilter.instance_filter_exists(instance) + def refresh_security_group_members(self, security_group): pass -- cgit From 519c885a4e3622417cd78655a211a2f23033d610 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Tue, 5 Apr 2011 19:42:09 +0400 Subject: pep8 fixed --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 93a250502..1eec55e5f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -167,7 +167,7 @@ def _get_network_info(instance): 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 = [] -- cgit From 28568e51ab4eb84e66e7d15adc9648220684ab84 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Tue, 5 Apr 2011 20:40:52 +0400 Subject: removed blank lines for pep8 fix --- nova/virt/libvirt_conn.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1eec55e5f..d6f51a644 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -164,10 +164,8 @@ def _get_network_info(instance): ip_addresses = 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 = [] -- cgit From a254fd9b63c48f64a62fd38df3a2caae81ce63c7 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 5 Apr 2011 18:29:53 -0500 Subject: typo --- nova/virt/xenapi/vmops.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c96c35a6e..ef2ab09b9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -814,12 +814,11 @@ class VMOps(object): "netmask": network["netmask"], "enabled": "1"} - def ip6_dict(ip6): + def ip6_dict(): return { "ip": utils.to_global_ipv6(network['cidr_v6'], instance['mac_address']), "netmask": network['netmask_v6'], - "gateway": network['gateway_v6'], "enabled": "1"} info = { @@ -831,7 +830,9 @@ class VMOps(object): 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_IPs]} if network['cidr_v6']: - info['ip6s'] = [ip6_dict(ip) for ip in network_IPs] + info['ip6s'] = [ip6_dict()] + if network['gateway_v6']: + info['gateway6'] = network['gateway_v6'], network_info.append((network, info)) return network_info -- cgit From dbeab47bbdeceab0bef896c9d7646ae346c9dd3a Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 6 Apr 2011 09:05:12 -0700 Subject: Wait for device node to be created after mounting image VDI --- nova/virt/xenapi/vm_utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..d29456ec6 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -49,6 +49,8 @@ LOG = logging.getLogger("nova.virt.xenapi.vm_utils") FLAGS = flags.FLAGS flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') +flags.DEFINE_integer('timeout_block', 10, + 'time to wait for a block device to be created') XENAPI_POWER_STATE = { 'Halted': power_state.SHUTDOWN, @@ -896,6 +898,16 @@ def remap_vbd_dev(dev): return remapped_dev +def _wait_for_device(dev): + """Wait for device node to appear""" + for i in xrange(0, FLAGS.timeout_block): + if os.path.exists('/dev/%s' % dev): + return + time.sleep(1) + + raise StorageError(_('Timeout waiting for device %s to be created') % dev) + + def with_vdi_attached_here(session, vdi_ref, read_only, f): this_vm_ref = get_this_vm_ref(session) vbd_rec = {} @@ -924,6 +936,7 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f): if dev != orig_dev: LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, ' 'remapping to %(dev)s') % locals()) + _wait_for_device(dev) return f(dev) finally: LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref) -- cgit From 481a77134a4e0e1d668fa488d7c5b1d7e1bc5429 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 11:15:35 -0500 Subject: modified behavior of inject_network_info and reset_network related to a vm_ref not being passed in --- nova/virt/xenapi/vmops.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ef2ab09b9..f02beda24 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -176,7 +176,7 @@ class VMOps(object): vdi_ref, network_info) self.create_vifs(vm_ref, network_info) - self.inject_network_info(instance, vm_ref, network_info) + self.inject_network_info(instance, network_info, vm_ref) return vm_ref def _spawn(self, instance, vm_ref): @@ -836,15 +836,31 @@ class VMOps(object): network_info.append((network, info)) return network_info - def inject_network_info(self, instance, vm_ref, network_info): + #TODO{tr3buchet) remove this shim with nova-multi-nic + def inject_network_info(self, instance, network_info=None, vm_ref=None): + """ + shim in place which makes inject_network_info work without being + passed network_info. + shim goes away after nova-multi-nic + """ + if not network_info: + network_info = self._get_network_info(instance) + self._inject_network_info(instance, network_info, vm_ref) + + def _inject_network_info(self, instance, network_info, vm_ref=None): """ Generate the network info and make calls to place it into the xenstore and the xenstore param list. + vm_ref can be passed in because it will sometimes be different than + what VMHelper.lookup(session, instance.name) will find (ex: rescue) """ logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref) - # this function raises if vm_ref is not a vm_opaque_ref - self._session.get_xenapi().VM.get_record(vm_ref) + if vm_ref: + # this function raises if vm_ref is not a vm_opaque_ref + self._session.get_xenapi().VM.get_record(vm_ref) + else: + vm_ref = VMHelper.lookup(self._session, instance.name) for (network, info) in network_info: location = 'vm-data/networking/%s' % info['mac'].replace(':', '') @@ -876,8 +892,10 @@ class VMOps(object): VMHelper.create_vif(self._session, vm_ref, network_ref, mac_address, device, rxtx_cap) - def reset_network(self, instance, vm_ref): + def reset_network(self, instance, vm_ref=None): """Creates uuid arg to pass to make_agent_call and calls it.""" + if not vm_ref: + vm_ref = VMHelper.lookup(self._session, instance.name) args = {'id': str(uuid.uuid4())} # TODO(tr3buchet): fix function call after refactor #resp = self._make_agent_call('resetnetwork', instance, '', args) -- cgit From e46d78218eec77f8502579496ee0922ce401e84a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 12:33:07 -0500 Subject: updated _prepare_injectables() to use info[gateway6] instead of looking inside the ip6 address dict for the gateway6 information --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..886f1ec88 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1130,7 +1130,7 @@ def _prepare_injectables(inst, networks_info): 'dns': dns, 'address_v6': ip_v6 and ip_v6['ip'] or '', 'netmask_v6': ip_v6 and ip_v6['netmask'] or '', - 'gateway_v6': ip_v6 and ip_v6['gateway'] or '', + 'gateway_v6': ip_v6 and info['gateway6'] or '', 'use_ipv6': FLAGS.use_ipv6} interfaces_info.append(interface_info) -- cgit From c18bf716f08e6b9fbdc259755cf172b5a6cf096a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 12:52:25 -0500 Subject: updated get_network_info in libvirt_conn to correctly insert ip6s and gateway6 into the network info, also small style fixes --- nova/virt/libvirt_conn.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2be190256..f1fa859ed 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -169,34 +169,34 @@ def _get_network_info(instance): instance['id']) network_info = [] - def ip_dict(ip): - return { - "ip": ip.address, - "netmask": network["netmask"], - "enabled": "1"} - - def ip6_dict(ip6): - prefix = ip6.network.cidr_v6 - mac = instance.mac_address - return { - "ip": utils.to_global_ipv6(prefix, mac), - "netmask": ip6.network.netmask_v6, - "gateway": ip6.network.gateway_v6, - "enabled": "1"} - for network in networks: network_ips = [ip for ip in ip_addresses - if ip.network_id == network.id] + if ip['network_id'] == network['id']] + + def ip_dict(ip): + return { + 'ip': ip['address'], + 'netmask': network['netmask'], + 'enabled': '1'} + + def ip6_dict(): + prefix = network['cidr_v6'] + mac = instance['mac_address'] + return { + 'ip': utils.to_global_ipv6(prefix, mac), + 'netmask': network['netmask_v6'], + 'enabled': '1'} mapping = { 'label': network['label'], 'gateway': network['gateway'], - 'mac': instance.mac_address, + 'mac': instance['mac_address'], 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_ips]} if FLAGS.use_ipv6: - mapping['ip6s'] = [ip6_dict(ip) for ip in network_ips] + mapping['ip6s'] = [ip6_dict()] + mapping['gateway6'] = network['gateway_v6'], network_info.append((network, mapping)) return network_info -- cgit From d3fec5c2c3de2d3a1ef0fd1fd809ff248b6df5a8 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 13:31:51 -0500 Subject: syntax error --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f1fa859ed..50b09d19b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -196,7 +196,7 @@ def _get_network_info(instance): if FLAGS.use_ipv6: mapping['ip6s'] = [ip6_dict()] - mapping['gateway6'] = network['gateway_v6'], + mapping['gateway6'] = network['gateway_v6'] network_info.append((network, mapping)) return network_info -- cgit From c649c8d5febab7d0dfa329bc5d78f0147383c5ee Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 6 Apr 2011 11:36:08 -0700 Subject: Use a more descriptive name for the flag to make it easier to understand the purpose --- nova/virt/xenapi/vm_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d29456ec6..cf2adb44c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -49,7 +49,7 @@ LOG = logging.getLogger("nova.virt.xenapi.vm_utils") FLAGS = flags.FLAGS flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') -flags.DEFINE_integer('timeout_block', 10, +flags.DEFINE_integer('block_device_creation_timeout', 10, 'time to wait for a block device to be created') XENAPI_POWER_STATE = { @@ -900,7 +900,7 @@ def remap_vbd_dev(dev): def _wait_for_device(dev): """Wait for device node to appear""" - for i in xrange(0, FLAGS.timeout_block): + for i in xrange(0, FLAGS.block_device_creation_timeout): if os.path.exists('/dev/%s' % dev): return time.sleep(1) -- cgit From 560d36e7ad87ca7e8f8619e146ed4965f33dd391 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 13:43:02 -0500 Subject: another syntax error --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f02beda24..30754b7b6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -832,7 +832,7 @@ class VMOps(object): if network['cidr_v6']: info['ip6s'] = [ip6_dict()] if network['gateway_v6']: - info['gateway6'] = network['gateway_v6'], + info['gateway6'] = network['gateway_v6'] network_info.append((network, info)) return network_info -- cgit From 2bc0e744162276048ddd9c1a1eeacbd647cda6f4 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Thu, 7 Apr 2011 13:32:19 +0900 Subject: fixed based on reviewer's comment - 1. erase unnecessary blank line, 2. adding LOG.debug --- nova/virt/libvirt_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bdf577825..eab54c53e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1849,7 +1849,6 @@ class NWFilterFirewall(FirewallDriver): def instance_filter_exists(self, instance): """Check nova-instance-instance-xxx exists""" - network_info = _get_network_info(instance) for (network, mapping) in network_info: nic_id = mapping['mac'].replace(':', '') @@ -1857,6 +1856,9 @@ class NWFilterFirewall(FirewallDriver): try: self._conn.nwfilterLookupByName(instance_filter_name) except libvirt.libvirtError: + name = instance.name + LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' + '%(name)s is not found.') % locals()) return False return True -- cgit From 7cf0deda8f7ab410005c556779353d599c8e8a63 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 10:34:14 -0300 Subject: adds timeout to login_with_password --- nova/virt/xenapi_conn.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 99fd35c61..6dfe0b9a9 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -63,6 +63,7 @@ import xmlrpclib from eventlet import event from eventlet import tpool +from eventlet import timeout from nova import context from nova import db @@ -140,7 +141,9 @@ flags.DEFINE_bool('xenapi_remap_vbd_dev', False, flags.DEFINE_string('xenapi_remap_vbd_dev_prefix', 'sd', 'Specify prefix to remap VBD dev to ' '(ex. /dev/xvdb -> /dev/sdb)') - +flags.DEFINE_integer('xenapi_login_timeout', + 10, + 'Timeout in seconds for XenAPI login.') def get_connection(_): """Note that XenAPI doesn't have a read-only connection mode, so @@ -318,7 +321,9 @@ class XenAPISession(object): def __init__(self, url, user, pw): self.XenAPI = self.get_imported_xenapi() self._session = self._create_session(url) - self._session.login_with_password(user, pw) + exception = self.XenAPI.Failure(_("Unable to log in to XenAPI.")) + with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): + self._session.login_with_password(user, pw) self.loop = None def get_imported_xenapi(self): -- cgit From 9b24c399c5689a1492b96dcd6725590c2a97c6e3 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 10:42:29 -0300 Subject: pep8 --- nova/virt/xenapi_conn.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6dfe0b9a9..f10aa6eb5 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -145,6 +145,7 @@ flags.DEFINE_integer('xenapi_login_timeout', 10, 'Timeout in seconds for XenAPI login.') + def get_connection(_): """Note that XenAPI doesn't have a read-only connection mode, so the read_only parameter is ignored.""" -- cgit From 9f57f78efab4a31bfe29e2edab1e86eedf4352fd Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 11:59:40 -0300 Subject: better error message --- nova/virt/xenapi_conn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f10aa6eb5..0cabccf08 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -322,7 +322,8 @@ class XenAPISession(object): def __init__(self, url, user, pw): self.XenAPI = self.get_imported_xenapi() self._session = self._create_session(url) - exception = self.XenAPI.Failure(_("Unable to log in to XenAPI.")) + exception = self.XenAPI.Failure(_("Unable to log in to XenAPI " + "(is the Dom0 disk full?)")) with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): self._session.login_with_password(user, pw) self.loop = None -- cgit From b5310d58d418f123b2d5d2953d6b4082a70120cd Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Fri, 8 Apr 2011 06:32:53 +0900 Subject: fix pep8 violation --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index eab54c53e..256e6e635 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1857,7 +1857,7 @@ class NWFilterFirewall(FirewallDriver): self._conn.nwfilterLookupByName(instance_filter_name) except libvirt.libvirtError: name = instance.name - LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' + LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' '%(name)s is not found.') % locals()) return False return True -- cgit From b54be0e29cdcd91e3d106fb587b89c39ca3a0bff Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 15:52:27 -0700 Subject: Removed commented-out old 'delete instance on SHUTOFF' code --- nova/virt/libvirt_conn.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 533ff9394..4523cdd2f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -232,14 +232,8 @@ class LibvirtConnection(driver.ComputeDriver): {'name': instance['name'], 'state': state}) db.instance_set_state(ctxt, instance['id'], state) - # NOTE(justinsb): We no longer delete these instances, + # NOTE(justinsb): We no longer delete SHUTOFF instances, # the user may want to power them back on - #if state == power_state.SHUTOFF: - # # TODO(soren): This is what the compute manager does when you - # # terminate # an instance. At some point I figure we'll have a - # # "terminated" state and some sort of cleanup job that runs - # # occasionally, cleaning them out. - # db.instance_destroy(ctxt, instance['id']) if state != power_state.RUNNING: continue -- cgit From 52478e039b094861e7d783b7995b9cafa68e32b9 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 15:56:16 -0700 Subject: Fix to correct libvirt error code when the domain is not found --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4523cdd2f..a7a8a14b1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -974,7 +974,7 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom = self._conn.lookupByName(instance_name) except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_UNKNOWN_HOST: + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: raise exception.NotFound(_("Instance %s not found") % instance_name) LOG.warning(_("Error from libvirt during lookup: %s") % e) -- cgit From 0c5f70c0bcf9395fb25a231057d997b075d04fda Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 16:00:55 -0700 Subject: Log libvirt errcode on exception --- nova/virt/libvirt_conn.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a7a8a14b1..5f1c12ab3 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -974,10 +974,13 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom = self._conn.lookupByName(instance_name) except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + errcode = e.get_error_code() + if errcode == libvirt.VIR_ERR_NO_DOMAIN: raise exception.NotFound(_("Instance %s not found") % instance_name) - LOG.warning(_("Error from libvirt during lookup: %s") % e) + LOG.warning(_("Error from libvirt during lookup. " + "Code=%(errcode)s Error=%(e)s") % + locals()) raise (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() -- cgit From 5deb4796bc26d98eeea94065c5098f7ce30ac2af Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 8 Apr 2011 11:21:36 -0700 Subject: Short circuit non-existant device during unit tests. It won't ever be created because of the stubs used during the unit tests --- nova/virt/xenapi/vm_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 73a1e2a3a..d2045a557 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -936,7 +936,11 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f): if dev != orig_dev: LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, ' 'remapping to %(dev)s') % locals()) - _wait_for_device(dev) + if dev != 'autodetect': + # NOTE(johannes): Unit tests will end up with a device called + # 'autodetect' which obviously won't exist. It's not ideal, + # but the alternatives were much messier + _wait_for_device(dev) return f(dev) finally: LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref) -- cgit From 5ea0991db9526969f100f3361661731aaf4d24d5 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 8 Apr 2011 15:22:15 -0400 Subject: Fixes bug which hangs nova-compute when terminating an instance when using libvirt backend. --- nova/virt/libvirt_conn.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b949e6c92..51a0a3380 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -325,12 +325,13 @@ class LibvirtConnection(driver.ComputeDriver): state = self.get_info(instance['name'])['state'] db.instance_set_state(context.get_admin_context(), instance['id'], state) - if state == power_state.SHUTDOWN: + if state == power_state.SHUTOFF: break - except Exception: + except Exception as ex: + LOG.debug(ex) db.instance_set_state(context.get_admin_context(), instance['id'], - power_state.SHUTDOWN) + power_state.SHUTOFF) break self.firewall_driver.unfilter_instance(instance) -- cgit From a1c40feb0cd592829b63df1cf19109bc322f81a7 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 8 Apr 2011 15:54:17 -0400 Subject: Added error message to exception logging. --- nova/virt/libvirt_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 51a0a3380..bfa9ff688 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -328,7 +328,9 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.SHUTOFF: break except Exception as ex: - LOG.debug(ex) + msg = _("Error encountered when destroying instance '%(id)s': " + "%(ex)s") % locals().update({"id": instance["id"]}) + LOG.debug(msg) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTOFF) -- cgit From d3de6cd1b2997e495a000b998b321346e2a75306 Mon Sep 17 00:00:00 2001 From: Renuka Apte Date: Fri, 8 Apr 2011 14:46:26 -0700 Subject: Fixes euca-attach-volume for iscsi using Xenserver Minor changes required to xenapi functions to get correct format for volume-id, iscsi-host, etc. --- nova/virt/xenapi/volume_utils.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 72284ac02..27964cac0 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -209,9 +209,9 @@ def _get_volume_id(path_or_id): # see compute/manager->setup_compute_volume volume_id = path_or_id[path_or_id.find('/vol-') + 1:] if volume_id == path_or_id: - volume_id = path_or_id[path_or_id.find('-vol-') + 1:] - volume_id = volume_id.replace('--', '-') - return volume_id + volume_id = path_or_id[path_or_id.find('-volume--') + 1:] + volume_id = volume_id.replace('volume--', '') + return int(volume_id) def _get_target_host(iscsi_string): @@ -244,25 +244,21 @@ def _get_target(volume_id): Gets iscsi name and portal from volume name and host. For this method to work the following are needed: 1) volume_ref['host'] must resolve to something rather than loopback - 2) ietd must bind only to the address as resolved above - If any of the two conditions are not met, fall back on Flags. """ - volume_ref = db.volume_get_by_ec2_id(context.get_admin_context(), + volume_ref = db.volume_get(context.get_admin_context(), volume_id) result = (None, None) try: - (r, _e) = utils.execute("sudo iscsiadm -m discovery -t " - "sendtargets -p %s" % - volume_ref['host']) + (r, _e) = utils.execute('sudo', 'iscsiadm', '-m', 'discovery', + '-t', 'sendtargets', '-p', volume_ref['host']) except exception.ProcessExecutionError, exc: LOG.exception(exc) else: - targets = r.splitlines() - if len(_e) == 0 and len(targets) == 1: - for target in targets: - if volume_id in target: + volume_name = "volume-%08x" % volume_id + for target in r.splitlines(): + if FLAGS.iscsi_ip_prefix in target and volume_name in target: (location, _sep, iscsi_name) = target.partition(" ") break - iscsi_portal = location.split(",")[0] - result = (iscsi_name, iscsi_portal) + iscsi_portal = location.split(",")[0] + result = (iscsi_name, iscsi_portal) return result -- cgit From 79ebe165f255037b0d5eaad7afe81b51cf85ed63 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Sat, 9 Apr 2011 11:08:47 -0400 Subject: Fixed log message gaffe. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bfa9ff688..9c665ab15 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -329,7 +329,7 @@ class LibvirtConnection(driver.ComputeDriver): break except Exception as ex: msg = _("Error encountered when destroying instance '%(id)s': " - "%(ex)s") % locals().update({"id": instance["id"]}) + "%(ex)s") % {"id": instance["id"], "ex": ex} LOG.debug(msg) db.instance_set_state(context.get_admin_context(), instance['id'], -- cgit From c47c545b293d5b73f46ff18ace2f4b9db61a771f Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Sun, 10 Apr 2011 02:49:38 +0900 Subject: debug tree status checkpoint. --- nova/virt/libvirt_conn.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2be190256..300672ae5 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -471,6 +471,9 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def reboot(self, instance): + # NOTE(itoumsn): self.shutdown() and wait instead of destroy would be + # better because we cannot ensure flushing dirty buffers + # in the guest OS. But, in case of KVM, shutdown often fails... self.destroy(instance, False) xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) @@ -496,7 +499,26 @@ class LibvirtConnection(driver.ComputeDriver): timer.stop() timer.f = _wait_for_reboot - return timer.start(interval=0.5, now=True) + timer_result=timer.start(interval=0.5, now=True) + + # Fix lp747922 + instance_id = instance['id'] + for vol in db.volume_get_all_by_instance(context.get_admin_context(), + instance_id): +# LOG.debug(_("re-attaching: %s") % vol['ec2_id']) +# instance-id : instance-00000001 +# device_path : /dev/etherd/e0.1, /dev/mapper/nova--volumes-volume--00000001 +# mountpoint : sdh +# BTW, is iSCSI working? + LOG.debug(_("instance_id: %s, volume_id: %s, mountpoint: %s") % + (instance_id, vol['id'], vol['mountpoint'])) +# self.attach_volume(instance['name'], vol['id'], vol['mountpoint']) + + self.attach_volume(instance['name'], + '/dev/mapper/nova--volumes-volume--00000001', + 'vdb') + + return timer_result @exception.wrap_exception def pause(self, instance, callback): -- cgit From a572b49e376cd6da4265c2807eaed8f0a2daf954 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 11:57:14 -0700 Subject: Remove the XML definition when we destroy a machine --- nova/virt/libvirt_conn.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b949e6c92..1ad6f8b32 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,10 +309,18 @@ class LibvirtConnection(driver.ComputeDriver): return infos def destroy(self, instance, cleanup=True): + name = instance['name'] try: - virt_dom = self._conn.lookupByName(instance['name']) + virt_dom = self._conn.lookupByName(name) virt_dom.destroy() - except Exception as _err: + # NOTE(justinsb): We remove the domain definition. We probably + # would do better to keep it if cleanup=False (e.g. volumes?) + # (e.g. #2 - not losing machines on failure) + virt_dom.undefine() + except Exception as e: + # TODO(justinsb): We really should check the error is 'not found' + LOG.warn(_("Ignoring error destroying domain %(name)s: %(e)s") % + locals()) pass # If the instance is already terminated, we're still happy -- cgit From be386ee614777212da2a14ebd8211f4b3d90ce66 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:33:24 -0700 Subject: Split logic on shutdown and undefine, so that even if the machine is already shutdown we will be able to proceed --- nova/virt/libvirt_conn.py | 53 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1ad6f8b32..47eb17abb 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,20 +309,47 @@ class LibvirtConnection(driver.ComputeDriver): return infos def destroy(self, instance, cleanup=True): - name = instance['name'] + instance_name = instance['name'] + + # TODO(justinsb): Refactor all lookupByName calls for error-handling try: - virt_dom = self._conn.lookupByName(name) - virt_dom.destroy() - # NOTE(justinsb): We remove the domain definition. We probably - # would do better to keep it if cleanup=False (e.g. volumes?) - # (e.g. #2 - not losing machines on failure) - virt_dom.undefine() - except Exception as e: - # TODO(justinsb): We really should check the error is 'not found' - LOG.warn(_("Ignoring error destroying domain %(name)s: %(e)s") % - locals()) - pass - # If the instance is already terminated, we're still happy + virt_dom = self._conn.lookupByName(instance_name) + except libvirt.libvirtError as e: + errcode = e.get_error_code() + if errcode == libvirt.VIR_ERR_NO_DOMAIN: + virt_dom = None + else: + LOG.warning(_("Error from libvirt during lookup of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + # If the instance is already terminated, we're still happy + # Otherwise, destroy it + if virt_dom is not None: + try: + virt_dom.destroy() + except libvirt.libvirtError as e: + errcode = e.get_error_code() + LOG.warning(_("Error from libvirt during destroy of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + try: + # NOTE(justinsb): We remove the domain definition. We probably + # would do better to keep it if cleanup=False (e.g. volumes?) + # (e.g. #2 - not losing machines on failure) + virt_dom.undefine() + except libvirt.libvirtError as e: + errcode = e.get_error_code() + LOG.warning(_("Error from libvirt during undefine of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise # We'll save this for when we do shutdown, # instead of destroy - but destroy returns immediately -- cgit From c6923ec603288e1d46fdb80e874c8e71361442f5 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:41:30 -0700 Subject: Handle the case when the machine is already SHUTOFF --- nova/virt/libvirt_conn.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 47eb17abb..7771aad7a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -331,12 +331,22 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom.destroy() except libvirt.libvirtError as e: + is_okay = False errcode = e.get_error_code() - LOG.warning(_("Error from libvirt during destroy of " - "%(instance_name)s. Code=%(errcode)s " - "Error=%(e)s") % - locals()) - raise + if errcode == libvirt.VIR_ERR_OPERATION_INVALID: + # If the instance if already shut off, we get this: + # Code=55 Error=Requested operation is not valid: + # domain is not running + (state, _, _, _, _) = virt_dom.info() + if state == power_state.SHUTOFF: + is_okay = True + + if not is_okay: + LOG.warning(_("Error from libvirt during destroy of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise try: # NOTE(justinsb): We remove the domain definition. We probably -- cgit From f59f8e8fcbde6f0d8d4c19b00bfc5f4141287772 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:57:32 -0700 Subject: Ooops - redefining the _ variable seems like a _really_ bad idea --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7771aad7a..22b0e1103 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -337,7 +337,7 @@ class LibvirtConnection(driver.ComputeDriver): # If the instance if already shut off, we get this: # Code=55 Error=Requested operation is not valid: # domain is not running - (state, _, _, _, _) = virt_dom.info() + (state, _max_mem, _mem, _cpus, _t) = virt_dom.info() if state == power_state.SHUTOFF: is_okay = True -- cgit From 92df1dc754126895a052bb648c1613201455f714 Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 12 Apr 2011 02:09:29 +0900 Subject: debug tree status checkpoint 2. --- nova/virt/libvirt_conn.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 567e17a91..0b1ed2a93 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -487,6 +487,28 @@ class LibvirtConnection(driver.ComputeDriver): instance['id'], state) if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) + + # Fix lp747922 + instance_id = instance['id'] + for vol in db.volume_get_all_by_instance( + context.get_admin_context(), instance_id): + # LOG.debug(_("re-attaching: %s") % vol['ec2_id']) + # instance-id : instance-00000001 + # device_path : /dev/etherd/e0.1, etc + # mountpoint : /dev/sdh + # dev_path is not stored anywhere, and it has driver + # specific format. Therefore, noway other than calling + # discover_driver here. + dev_path = nova.volume.driver.discover_volume(self, + context, + vol) + LOG.debug(_("instance_id: %s, volume_id: %s, mountpoint: %s") % + (instance_id, dev_path, vol['mountpoint'])) + self.attach_volume(instance['name'], + dev_path, + vol['mountpoint']); + # Fix lp747922 + timer.stop() except Exception, exn: LOG.exception(_('_wait_for_reboot failed: %s'), exn) @@ -497,24 +519,6 @@ class LibvirtConnection(driver.ComputeDriver): timer.f = _wait_for_reboot timer_result=timer.start(interval=0.5, now=True) - - # Fix lp747922 - instance_id = instance['id'] - for vol in db.volume_get_all_by_instance(context.get_admin_context(), - instance_id): -# LOG.debug(_("re-attaching: %s") % vol['ec2_id']) -# instance-id : instance-00000001 -# device_path : /dev/etherd/e0.1, /dev/mapper/nova--volumes-volume--00000001 -# mountpoint : sdh -# BTW, is iSCSI working? - LOG.debug(_("instance_id: %s, volume_id: %s, mountpoint: %s") % - (instance_id, vol['id'], vol['mountpoint'])) -# self.attach_volume(instance['name'], vol['id'], vol['mountpoint']) - - self.attach_volume(instance['name'], - '/dev/mapper/nova--volumes-volume--00000001', - 'vdb') - return timer_result @exception.wrap_exception -- cgit From b342b1b63a860b9f4abdc28224ab7a6a0f3b00dd Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 11 Apr 2011 12:15:22 -0500 Subject: Remove unused self.interfaces_xml --- nova/virt/libvirt_conn.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6ec15fbb8..4b6cfa6a4 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -211,7 +211,6 @@ class LibvirtConnection(driver.ComputeDriver): self.libvirt_uri = self.get_uri() self.libvirt_xml = open(FLAGS.libvirt_xml_template).read() - self.interfaces_xml = open(FLAGS.injected_network_template).read() self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read() self._wrapped_conn = None self.read_only = read_only -- cgit From 2295e47b1f85fb199c7e4bf514f6781d7033dd77 Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 12 Apr 2011 02:30:31 +0900 Subject: A minor blush up. --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b9f6c482e..d47e8f422 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -566,8 +566,7 @@ class LibvirtConnection(driver.ComputeDriver): timer.stop() timer.f = _wait_for_reboot - timer_result=timer.start(interval=0.5, now=True) - return timer_result + return timer.start(interval=0.5, now=True) @exception.wrap_exception def pause(self, instance, callback): -- cgit From 7a2f3d6007a1da365a008cca163cf493668a71de Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 12 Apr 2011 02:32:19 +0900 Subject: A minor blush up. --- nova/virt/libvirt_conn.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d47e8f422..7670d3989 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -535,12 +535,10 @@ class LibvirtConnection(driver.ComputeDriver): instance['id'], state) if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) - # Fix lp747922 instance_id = instance['id'] for vol in db.volume_get_all_by_instance( context.get_admin_context(), instance_id): - # LOG.debug(_("re-attaching: %s") % vol['ec2_id']) # instance-id : instance-00000001 # device_path : /dev/etherd/e0.1, etc # mountpoint : /dev/sdh -- cgit From b6975a79e91a531ea7501aeb0dbf6c7c07a6722b Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 12 Apr 2011 03:13:58 +0900 Subject: Minor blush ups. --- nova/virt/libvirt_conn.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7670d3989..0a9ee688a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -536,7 +536,7 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) # Fix lp747922 - instance_id = instance['id'] + instance_id = instance['id'] for vol in db.volume_get_all_by_instance( context.get_admin_context(), instance_id): # instance-id : instance-00000001 @@ -548,11 +548,11 @@ class LibvirtConnection(driver.ComputeDriver): dev_path = nova.volume.driver.discover_volume(self, context, vol) - LOG.debug(_("instance_id: %s, volume_id: %s, mountpoint: %s") % - (instance_id, dev_path, vol['mountpoint'])) + LOG.debug( + _("Re-attaching %(dev_path)s to %(mountpoint)s") % + (dev_path, vol['mountpoint'])) self.attach_volume(instance['name'], - dev_path, - vol['mountpoint']); + dev_path, vol['mountpoint']) # Fix lp747922 timer.stop() -- cgit From b2f693f63d73e3e51cb3be40b5deae720c773340 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 12 Apr 2011 09:23:52 -0400 Subject: Reverted some superfluous changes to make MP more concise. --- nova/virt/libvirt_conn.py | 67 +++++++++-------------------------------------- 1 file changed, 13 insertions(+), 54 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9714773b2..53382a315 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,27 +309,6 @@ class LibvirtConnection(driver.ComputeDriver): return infos def destroy(self, instance, cleanup=True): -<<<<<<< TREE - """Delete the VM instance from the hypervisor. - - :param instance: Object representing the instance to destroy - :param cleanup: Should we erase all of the VM's associated files? - """ - name = instance['name'] - - try: - virt_dom = self._conn.lookupByName(name) - except libvirt.libvirtError as ex: - msg = _("Instance %s not found.") % name - raise exception.NotFound(msg) - - try: - virt_dom.destroy() - except libvirt.libvirtError as ex: - # If the instance is already terminated, we're still happy - msg = _("Error encountered during `libvirt.destroy`: %s") % ex - LOG.debug(msg) -======= instance_name = instance['name'] # TODO(justinsb): Refactor all lookupByName calls for error-handling @@ -624,38 +603,16 @@ class LibvirtConnection(driver.ComputeDriver): # for xenapi(tr3buchet) @exception.wrap_exception def spawn(self, instance, network_info=None): - """Create the given VM instance using the libvirt connection. - - :param instance: Object representing the instance to create - :param network_info: Associated network information - """ - _id = instance['id'] - name = instance['name'] xml = self.to_xml(instance, network_info) self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(instance, xml, network_info) -<<<<<<< TREE - - try: - self._conn.createXML(xml, 0) - except libvirt.libvirtError as ex: - msg = _("Error encountered creating VM '%(name)s': %(ex)s") - LOG.error(msg % locals()) - return False - - LOG.debug(_("VM %s successfully created.") % name) - -======= domain = self._create_new_domain(xml) LOG.debug(_("instance %s: is running"), instance['name']) ->>>>>>> MERGE-SOURCE self.firewall_driver.apply_instance_filter(instance) -<<<<<<< TREE -======= if FLAGS.start_guests_on_host_boot: LOG.debug(_("instance %s: setting autostart ON") % instance['name']) @@ -663,21 +620,23 @@ class LibvirtConnection(driver.ComputeDriver): timer = utils.LoopingCall(f=None) ->>>>>>> MERGE-SOURCE def _wait_for_boot(): - """Check to see if the VM is running.""" try: - state = self.get_info(name)['state'] - except (exception.NotFound, libvirt.libvirtError) as ex: - msg = _("Error while waiting for VM '%(_id)s' to run: %(ex)s") - LOG.debug(msg % locals()) - timer.stop() - - if state == power_state.RUNNING: - LOG.debug(_('VM %s is now running.') % name) + state = self.get_info(instance['name'])['state'] + db.instance_set_state(context.get_admin_context(), + instance['id'], state) + if state == power_state.RUNNING: + LOG.debug(_('instance %s: booted'), instance['name']) + timer.stop() + except: + LOG.exception(_('instance %s: failed to boot'), + instance['name']) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) timer.stop() - timer = utils.LoopingCall(f=_wait_for_boot) + timer.f = _wait_for_boot return timer.start(interval=0.5, now=True) def _flush_xen_console(self, virsh_output): -- cgit From 764862180657dbc16b2d57d3b2027c23b86ea649 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 12 Apr 2011 09:34:52 -0400 Subject: Reverted some superfluous changes to make MP more concise. --- nova/virt/xenapi/vmops.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6ed065280..135e59a34 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -206,27 +206,27 @@ class VMOps(object): # NOTE(armando): Do we really need to do this in virt? # NOTE(tr3buchet): not sure but wherever we do it, we need to call # reset_network afterwards + timer = utils.LoopingCall(f=None) def _wait_for_boot(): try: state = self.get_info(instance_name)['state'] - except self.XenAPI.Failure as ex: - msg = _("Error while waiting for VM '%(instance_name)s' " - "to boot: %(ex)s") % locals() - LOG.debug(msg) + if state == power_state.RUNNING: + LOG.debug(_('Instance %s: booted'), instance_name) + timer.stop() + _inject_files() + return True + except Exception, exc: + LOG.warn(exc) + LOG.exception(_('Instance %s: failed to boot'), instance_name) timer.stop() return False - if state == power_state.RUNNING: - LOG.debug(_('VM %s is now running.') % instance_name) - timer.stop() - _inject_files() - return True + timer.f = _wait_for_boot # call to reset network to configure network from xenstore self.reset_network(instance, vm_ref) - timer = utils.LoopingCall(f=_wait_for_boot) return timer.start(interval=0.5, now=True) def _get_vm_opaque_ref(self, instance_or_vm): -- cgit From 12ec5f5c0d6a88779780b15b6ef38a016d6aae4a Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Tue, 12 Apr 2011 08:04:55 -0700 Subject: Add new flag 'max_kernel_ramdisk_size' to specify a maximum size of kernel or ramdisk so we don't copy large files to dom0 and fill up /boot/guest --- nova/virt/xenapi/vm_utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d2045a557..dd1fd9383 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -51,6 +51,8 @@ FLAGS = flags.FLAGS flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') flags.DEFINE_integer('block_device_creation_timeout', 10, 'time to wait for a block device to be created') +flags.DEFINE_integer('max_kernel_ramdisk_size', 16 * 1024 * 1024, + 'maximum size in bytes of kernel or ramdisk images') XENAPI_POWER_STATE = { 'Halted': power_state.SHUTDOWN, @@ -448,6 +450,12 @@ class VMHelper(HelperBase): if image_type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES + elif image_type == ImageType.KERNEL_RAMDISK and \ + vdi_size > FLAGS.max_kernel_ramdisk_size: + max_size = FLAGS.max_kernel_ramdisk_size + raise exception.Error( + _("Kernel/Ramdisk image is too large, %(vdi_size)d bytes " + "(max %(max_size)d bytes)") % locals()) name_label = get_name_label_for_image(image) vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False) -- cgit From 2576c733c05dfd9872423f52319c28a65834ee61 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 12 Apr 2011 11:13:31 -0400 Subject: Dangerous whitespace mistake! :) --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 135e59a34..7f9814a10 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -222,7 +222,7 @@ class VMOps(object): timer.stop() return False - timer.f = _wait_for_boot + timer.f = _wait_for_boot # call to reset network to configure network from xenstore self.reset_network(instance, vm_ref) -- cgit From a4791a2d2b4f44c636b7f7694e92bed615309070 Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Wed, 13 Apr 2011 02:41:33 +0900 Subject: Rework importing volume_manager. --- nova/virt/libvirt_conn.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 0a9ee688a..990779d47 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -33,7 +33,8 @@ Supports KVM, LXC, QEMU, UML, and XEN. :rescue_ramdisk_id: Rescue ari image (default: ari-rescue). :injected_network_template: Template file for injected network :allow_project_net_traffic: Whether to allow in project network traffic - +:volume_manager: Name of class that handles persistent storage, loaded by + :func:`nova.utils.import_object` """ import multiprocessing @@ -218,6 +219,8 @@ class LibvirtConnection(driver.ComputeDriver): fw_class = utils.import_class(FLAGS.firewall_driver) self.firewall_driver = fw_class(get_connection=self._get_connection) + # NOTE(itoumsn): This is an ugly hack to re-attach volumes on reboot. + self.volume_manager = utils.import_object(FLAGS.volume_manager) def init_host(self, host): # Adopt existing VM's running here @@ -539,15 +542,12 @@ class LibvirtConnection(driver.ComputeDriver): instance_id = instance['id'] for vol in db.volume_get_all_by_instance( context.get_admin_context(), instance_id): - # instance-id : instance-00000001 - # device_path : /dev/etherd/e0.1, etc - # mountpoint : /dev/sdh # dev_path is not stored anywhere, and it has driver - # specific format. Therefore, noway other than calling - # discover_driver here. - dev_path = nova.volume.driver.discover_volume(self, - context, - vol) + # specific format. Furthermore, compute node specific. + # Therefore, noway other than calling discover_driver + # here. + dev_path = self.volume_manager.driver.discover_volume( + context, vol) LOG.debug( _("Re-attaching %(dev_path)s to %(mountpoint)s") % (dev_path, vol['mountpoint'])) -- cgit From acfa9d4e3ae2185a0d6d9afdddf3e8a2e7f6f398 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 12 Apr 2011 14:43:07 -0700 Subject: Make VMWare Connection inherit from ComputeDriver --- nova/virt/vmwareapi_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 20c1b2b45..1c6d2572d 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -42,6 +42,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import utils +from nova.virt import driver from nova.virt.vmwareapi import error_util from nova.virt.vmwareapi import vim from nova.virt.vmwareapi import vim_util @@ -104,11 +105,12 @@ def get_connection(_): api_retry_count) -class VMWareESXConnection(object): +class VMWareESXConnection(driver.ComputeDriver): """The ESX host connection object.""" def __init__(self, host_ip, host_username, host_password, api_retry_count, scheme="https"): + super(VMWareESXConnection, self).__init__() session = VMWareAPISession(host_ip, host_username, host_password, api_retry_count, scheme=scheme) self._vmops = VMWareVMOps(session) -- cgit From bc953f37560b7353b9b8c86e8d0bdaa5672d3acd Mon Sep 17 00:00:00 2001 From: Renuka Apte Date: Tue, 12 Apr 2011 15:20:30 -0700 Subject: Minor fixes --- nova/virt/xenapi/volume_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 27964cac0..819c48be5 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -250,7 +250,7 @@ def _get_target(volume_id): result = (None, None) try: (r, _e) = utils.execute('sudo', 'iscsiadm', '-m', 'discovery', - '-t', 'sendtargets', '-p', volume_ref['host']) + '-t', 'sendtargets', '-p', volume_ref['host']) except exception.ProcessExecutionError, exc: LOG.exception(exc) else: -- cgit From 1ca1e83040cb2899c108415c899eee54c760afe3 Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Wed, 13 Apr 2011 14:51:26 +0900 Subject: iSCSI/KVM test completed. --- nova/virt/libvirt_conn.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 990779d47..fae48ba4a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -538,23 +538,23 @@ class LibvirtConnection(driver.ComputeDriver): instance['id'], state) if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) - # Fix lp747922 + # Re-attach volumes instance_id = instance['id'] for vol in db.volume_get_all_by_instance( context.get_admin_context(), instance_id): - # dev_path is not stored anywhere, and it has driver - # specific format. Furthermore, compute node specific. - # Therefore, noway other than calling discover_driver - # here. + # NOTE(itoumsn): dev_path is not stored anywhere, + # and it has driver specific format. Furthermore, it's + # also compute node specific in general. + # Therefore, no way other than calling + # undiscover/discover_driver here at this moment. + self.volume_manager.driver.undiscover_volume(vol) dev_path = self.volume_manager.driver.discover_volume( context, vol) LOG.debug( - _("Re-attaching %(dev_path)s to %(mountpoint)s") % + _("Re-attaching %s to %s") % (dev_path, vol['mountpoint'])) self.attach_volume(instance['name'], dev_path, vol['mountpoint']) - # Fix lp747922 - timer.stop() except Exception, exn: LOG.exception(_('_wait_for_reboot failed: %s'), exn) -- cgit From c04b0caca4a725be390271be30bf8a034aa5ca9d Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 13 Apr 2011 10:10:40 -0700 Subject: Minor formatting cleanup --- nova/virt/xenapi/vm_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index dd1fd9383..32a617ef4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -454,8 +454,8 @@ class VMHelper(HelperBase): vdi_size > FLAGS.max_kernel_ramdisk_size: max_size = FLAGS.max_kernel_ramdisk_size raise exception.Error( - _("Kernel/Ramdisk image is too large, %(vdi_size)d bytes " - "(max %(max_size)d bytes)") % locals()) + _("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, " + "max %(max_size)d bytes") % locals()) name_label = get_name_label_for_image(image) vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False) -- cgit From ea07b74b8b0fd912555b4193f6e29a2dcd86f4b0 Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Thu, 14 Apr 2011 02:22:41 +0900 Subject: An ultimate workaround workd... :( --- nova/virt/libvirt_conn.py | 93 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index fae48ba4a..870deae31 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -33,8 +33,6 @@ Supports KVM, LXC, QEMU, UML, and XEN. :rescue_ramdisk_id: Rescue ari image (default: ari-rescue). :injected_network_template: Template file for injected network :allow_project_net_traffic: Whether to allow in project network traffic -:volume_manager: Name of class that handles persistent storage, loaded by - :func:`nova.utils.import_object` """ import multiprocessing @@ -219,8 +217,6 @@ class LibvirtConnection(driver.ComputeDriver): fw_class = utils.import_class(FLAGS.firewall_driver) self.firewall_driver = fw_class(get_connection=self._get_connection) - # NOTE(itoumsn): This is an ugly hack to re-attach volumes on reboot. - self.volume_manager = utils.import_object(FLAGS.volume_manager) def init_host(self, host): # Adopt existing VM's running here @@ -522,8 +518,66 @@ class LibvirtConnection(driver.ComputeDriver): # NOTE(itoumsn): self.shutdown() and wait instead of destroy would be # better because we cannot ensure flushing dirty buffers # in the guest OS. But, in case of KVM, shutdown often fails... +# instance_id = instance['id'] +# volume_list = [] +# dev_path_list = {} +# vols = db.volume_get_all_by_instance(context.get_admin_context(), +# instance_id) +# LOG.debug(_("DEBUG: vols %s") % vols) +# for vol in vols: +# LOG.debug(_("DEBUG: reboot: %s %s") % (vol['mountpoint'], +# vol['mountpoint'].rpartition("/")[2])) +# volume_list.append(vol['mountpoint'].partition("/")[2]) +# +# if len(volume_list) != 0: +# LOG.debug(_("DEBUG: volume_list is not empty")) +# virt_dom = self._conn.lookupByName(instance['name']) +# xml = virt_dom.XMLDesc(0) +# try: +# doc = libxml2.parseDoc(xml) +# except: +# LOG.exception(_('Failed to get xml description %s'), +# instance_id) +# ctx = doc.xpathNewContext() +# try: +# ret = ctx.xpathEval('/domain/devices/disk') +# for node in ret: +# LOG.debug(_("DEBUG: node.name %s") % node.name) +# target_dev = '' +# source_dev = '' +# for child in node.children: +# LOG.debug( +# _("child.name: %s prop.dev %s") % +# (child.name, child.prop('dev'))) +# +# if child.name == 'source': +# source_dev = child.prop('dev') +# elif child.name == 'target': +# target_dev = child.prop('dev') +# LOG.debug( +# _("source: %s target %s") % +# (source_dev, target_dev)) +# +# if target_dev in volume_list: +# dev_path_list[target_dev] = source_dev +# LOG.debug( +# _("append to dev_path_list source: %s target: %s") % +# (source_dev, target_dev)) +# else: +# LOG.debug(_("DEBUG: %s not found in volume_list") % +# (target_dev)) +# finally: +# LOG.debug(_("DEBUG: finally block")) +# if ctx != None: +# ctx.xpathFreeContext() +# if doc != None: +# doc.freeDoc() + + virt_dom = self._conn.lookupByName(instance['name']) + xml = virt_dom.XMLDesc(0) + self.destroy(instance, False) - xml = self.to_xml(instance) +# xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) self.firewall_driver.prepare_instance_filter(instance) self._create_new_domain(xml) @@ -539,22 +593,29 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) # Re-attach volumes - instance_id = instance['id'] - for vol in db.volume_get_all_by_instance( - context.get_admin_context(), instance_id): +# for mp in dev_path_list.iterkeys(): +# LOG.debug( +# _("Re-attaching %s to %s") % +# (dev_path_list[mp], mp)) +# self.attach_volume(instance['name'], +# dev_path_list[mp], mp) +# +# instance_id = instance['id'] +# for vol in db.volume_get_all_by_instance( +# context.get_admin_context(), instance_id): # NOTE(itoumsn): dev_path is not stored anywhere, # and it has driver specific format. Furthermore, it's # also compute node specific in general. # Therefore, no way other than calling # undiscover/discover_driver here at this moment. - self.volume_manager.driver.undiscover_volume(vol) - dev_path = self.volume_manager.driver.discover_volume( - context, vol) - LOG.debug( - _("Re-attaching %s to %s") % - (dev_path, vol['mountpoint'])) - self.attach_volume(instance['name'], - dev_path, vol['mountpoint']) +# self.volume_manager.driver.undiscover_volume(vol) +# dev_path = self.volume_manager.driver.discover_volume( +# context, vol) +# LOG.debug( +# _("Re-attaching %s to %s") % +# (dev_path, vol['mountpoint'])) +# self.attach_volume(instance['name'], +# dev_path, vol['mountpoint']) timer.stop() except Exception, exn: LOG.exception(_('_wait_for_reboot failed: %s'), exn) -- cgit From 2d1235ea404d55f1cdf764798d7a071b3b60dc7e Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Thu, 14 Apr 2011 02:38:14 +0900 Subject: Debug code clean up. --- nova/virt/libvirt_conn.py | 91 ++++------------------------------------------- 1 file changed, 7 insertions(+), 84 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 870deae31..f273d47ce 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -33,6 +33,7 @@ Supports KVM, LXC, QEMU, UML, and XEN. :rescue_ramdisk_id: Rescue ari image (default: ari-rescue). :injected_network_template: Template file for injected network :allow_project_net_traffic: Whether to allow in project network traffic + """ import multiprocessing @@ -515,69 +516,15 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def reboot(self, instance): - # NOTE(itoumsn): self.shutdown() and wait instead of destroy would be - # better because we cannot ensure flushing dirty buffers - # in the guest OS. But, in case of KVM, shutdown often fails... -# instance_id = instance['id'] -# volume_list = [] -# dev_path_list = {} -# vols = db.volume_get_all_by_instance(context.get_admin_context(), -# instance_id) -# LOG.debug(_("DEBUG: vols %s") % vols) -# for vol in vols: -# LOG.debug(_("DEBUG: reboot: %s %s") % (vol['mountpoint'], -# vol['mountpoint'].rpartition("/")[2])) -# volume_list.append(vol['mountpoint'].partition("/")[2]) -# -# if len(volume_list) != 0: -# LOG.debug(_("DEBUG: volume_list is not empty")) -# virt_dom = self._conn.lookupByName(instance['name']) -# xml = virt_dom.XMLDesc(0) -# try: -# doc = libxml2.parseDoc(xml) -# except: -# LOG.exception(_('Failed to get xml description %s'), -# instance_id) -# ctx = doc.xpathNewContext() -# try: -# ret = ctx.xpathEval('/domain/devices/disk') -# for node in ret: -# LOG.debug(_("DEBUG: node.name %s") % node.name) -# target_dev = '' -# source_dev = '' -# for child in node.children: -# LOG.debug( -# _("child.name: %s prop.dev %s") % -# (child.name, child.prop('dev'))) -# -# if child.name == 'source': -# source_dev = child.prop('dev') -# elif child.name == 'target': -# target_dev = child.prop('dev') -# LOG.debug( -# _("source: %s target %s") % -# (source_dev, target_dev)) -# -# if target_dev in volume_list: -# dev_path_list[target_dev] = source_dev -# LOG.debug( -# _("append to dev_path_list source: %s target: %s") % -# (source_dev, target_dev)) -# else: -# LOG.debug(_("DEBUG: %s not found in volume_list") % -# (target_dev)) -# finally: -# LOG.debug(_("DEBUG: finally block")) -# if ctx != None: -# ctx.xpathFreeContext() -# if doc != None: -# doc.freeDoc() - virt_dom = self._conn.lookupByName(instance['name']) + # NOTE(itoumsn): Use XML delived from the running instance + # instead of using to_xml(instance). This is almost the ultimate + # stupid workaround. xml = virt_dom.XMLDesc(0) - + # NOTE(itoumsn): self.shutdown() and wait instead of self.destroy() is + # better because we cannot ensure flushing dirty buffers + # in the guest OS. But, in case of KVM, shutdown() does not work... self.destroy(instance, False) -# xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) self.firewall_driver.prepare_instance_filter(instance) self._create_new_domain(xml) @@ -592,30 +539,6 @@ class LibvirtConnection(driver.ComputeDriver): instance['id'], state) if state == power_state.RUNNING: LOG.debug(_('instance %s: rebooted'), instance['name']) - # Re-attach volumes -# for mp in dev_path_list.iterkeys(): -# LOG.debug( -# _("Re-attaching %s to %s") % -# (dev_path_list[mp], mp)) -# self.attach_volume(instance['name'], -# dev_path_list[mp], mp) -# -# instance_id = instance['id'] -# for vol in db.volume_get_all_by_instance( -# context.get_admin_context(), instance_id): - # NOTE(itoumsn): dev_path is not stored anywhere, - # and it has driver specific format. Furthermore, it's - # also compute node specific in general. - # Therefore, no way other than calling - # undiscover/discover_driver here at this moment. -# self.volume_manager.driver.undiscover_volume(vol) -# dev_path = self.volume_manager.driver.discover_volume( -# context, vol) -# LOG.debug( -# _("Re-attaching %s to %s") % -# (dev_path, vol['mountpoint'])) -# self.attach_volume(instance['name'], -# dev_path, vol['mountpoint']) timer.stop() except Exception, exn: LOG.exception(_('_wait_for_reboot failed: %s'), exn) -- cgit From eda350a605b5711b8373849f389e3fe472670ca0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 13 Apr 2011 13:35:32 -0500 Subject: Don't hammer on the DB --- nova/virt/libvirt_conn.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6ec15fbb8..94410003e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -372,6 +372,9 @@ class LibvirtConnection(driver.ComputeDriver): instance['id'], state) if state == power_state.SHUTOFF: break + + # Let's not hammer on the DB + time.sleep(1) except Exception as ex: msg = _("Error encountered when destroying instance '%(id)s': " "%(ex)s") % {"id": instance["id"], "ex": ex} -- cgit From ba69d58d21a6164626835e5dd7f45f75dfca07bd Mon Sep 17 00:00:00 2001 From: Yoshiaki Tamura Date: Thu, 14 Apr 2011 21:38:55 +0900 Subject: Fix parameter mismatch calling to_xml() from spawn() in libvirt_conn.py Insert 'False' between instance and network_info. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 94410003e..72896bb20 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -606,7 +606,7 @@ class LibvirtConnection(driver.ComputeDriver): # for xenapi(tr3buchet) @exception.wrap_exception def spawn(self, instance, network_info=None): - xml = self.to_xml(instance, network_info) + xml = self.to_xml(instance, False, network_info) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.NOSTATE, -- cgit From 4b0785632ba626d34a8a9fae5e0a5c742660e2dc Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 14 Apr 2011 17:34:09 -0400 Subject: initial roundup of all 'exception.Invalid' cases --- nova/virt/libvirt_conn.py | 19 ++++++++----------- nova/virt/vmwareapi/vmops.py | 13 +++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 94410003e..78190cb6d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -421,7 +421,7 @@ class LibvirtConnection(driver.ComputeDriver): name, mount_device) else: - raise exception.Invalid(_("Invalid device path %s") % device_path) + raise exception.InvalidDevicePath(path=device_path) virt_dom.attachDevice(xml) @@ -1302,9 +1302,9 @@ class LibvirtConnection(driver.ComputeDriver): xml = libxml2.parseDoc(xml) nodes = xml.xpathEval('//host/cpu') if len(nodes) != 1: - raise exception.Invalid(_("Invalid xml. '' must be 1," - "but %d\n") % len(nodes) - + xml.serialize()) + reason = _("'' must be 1, but %d\n") % len(nodes) + reason += xml.serialize() + raise exception.InvalidCPUInfo(reason=reason) cpu_info = dict() @@ -1333,9 +1333,8 @@ class LibvirtConnection(driver.ComputeDriver): tkeys = topology.keys() if set(tkeys) != set(keys): ks = ', '.join(keys) - raise exception.Invalid(_("Invalid xml: topology" - "(%(topology)s) must have " - "%(ks)s") % locals()) + reason = _("topology (%(topology)s) must have %(ks)s") + raise exception.InvalidCPUInfo(reason=reason % locals()) feature_nodes = xml.xpathEval('//host/cpu/feature') features = list() @@ -1390,9 +1389,7 @@ class LibvirtConnection(driver.ComputeDriver): try: service_ref = db.service_get_all_compute_by_host(ctxt, host)[0] except exception.NotFound: - raise exception.Invalid(_("Cannot update compute manager " - "specific info, because no service " - "record was found.")) + raise exception.ComputeServiceUnavailable() # Updating host information dic = {'vcpus': self.get_vcpu_total(), @@ -1445,7 +1442,7 @@ class LibvirtConnection(driver.ComputeDriver): raise if ret <= 0: - raise exception.Invalid(m % locals()) + raise exception.InvalidCPUInfo(reason=m % locals()) return diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index cf6c88bbd..f2f68ec22 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -501,8 +501,8 @@ class VMWareVMOps(object): # Raise an exception if the VM is not powered On. if pwr_state not in ["poweredOn"]: - raise exception.Invalid(_("instance - %s not poweredOn. So can't " - "be rebooted.") % instance.name) + reason = _("instance is not powered on") + raise exception.InstanceRebootFailure(reason=reason) # If latest vmware tools are installed in the VM, and that the tools # are running, then only do a guest reboot. Otherwise do a hard reset. @@ -620,8 +620,9 @@ class VMWareVMOps(object): LOG.debug(_("Suspended the VM %s ") % instance.name) # Raise Exception if VM is poweredOff elif pwr_state == "poweredOff": - raise exception.Invalid(_("instance - %s is poweredOff and hence " - " can't be suspended.") % instance.name) + reason = _("instance is poweredOff and can not be suspended.") + raise exception.InstanceSuspendFailure(reason=reason) + LOG.debug(_("VM %s was already in suspended state. So returning " "without doing anything") % instance.name) @@ -643,8 +644,8 @@ class VMWareVMOps(object): self._wait_with_callback(instance.id, suspend_task, callback) LOG.debug(_("Resumed the VM %s ") % instance.name) else: - raise exception.Invalid(_("instance - %s not in Suspended state " - "and hence can't be Resumed.") % instance.name) + reason = _("instance is not in a suspended state") + raise exception.InstanceResumeFailure(reason=reason) def get_info(self, instance_name): """Return data about the VM instance.""" -- cgit From ad138a5a50868531f34ba358600f1270588ce80b Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 15 Apr 2011 14:24:17 -0400 Subject: correcting tests; pep8 --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 78190cb6d..2b78dceb2 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1389,7 +1389,7 @@ class LibvirtConnection(driver.ComputeDriver): try: service_ref = db.service_get_all_compute_by_host(ctxt, host)[0] except exception.NotFound: - raise exception.ComputeServiceUnavailable() + raise exception.ComputeServiceUnavailable(host=host) # Updating host information dic = {'vcpus': self.get_vcpu_total(), -- cgit From 25d95c9f9ba0000773902186a5838fbe57a25a8c Mon Sep 17 00:00:00 2001 From: termie Date: Sat, 16 Apr 2011 20:23:06 -0700 Subject: change libvirt snapshot to new style execute --- nova/virt/libvirt_conn.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ccfce39e4..7f25a8503 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -58,7 +58,6 @@ from nova import db from nova import exception from nova import flags from nova import log as logging -#from nova import test from nova import utils from nova import vnc from nova.auth import manager @@ -499,12 +498,17 @@ class LibvirtConnection(driver.ComputeDriver): # Export the snapshot to a raw image temp_dir = tempfile.mkdtemp() out_path = os.path.join(temp_dir, snapshot_name) - qemu_img_cmd = '%s convert -f qcow2 -O raw -s %s %s %s' % ( - FLAGS.qemu_img, - snapshot_name, - disk_path, - out_path) - utils.execute(qemu_img_cmd) + qemu_img_cmd = (FLAGS.qemu_img, + 'convert', + '-f', + 'qcow2', + '-O', + 'raw', + '-s', + snapshot_name, + disk_path, + out_path) + utils.execute(*qemu_img_cmd) # Upload that image to the image service with open(out_path) as image_file: -- cgit From dbb0ff6b7720d4715d26b470f0ee39f27b1e187c Mon Sep 17 00:00:00 2001 From: termie Date: Sat, 16 Apr 2011 20:23:06 -0700 Subject: move name into main metadata instead of properties --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7f25a8503..d62cb5224 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -470,8 +470,8 @@ class LibvirtConnection(driver.ComputeDriver): metadata = {'disk_format': base['disk_format'], 'container_format': base['container_format'], 'is_public': False, + 'name': '%s.%s' % (base['name'], image_id), 'properties': {'architecture': base['architecture'], - 'name': '%s.%s' % (base['name'], image_id), 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', 'image_state': 'available', -- cgit From eb20dd53832577f94f5f251bd97e866435f6aeb9 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 18 Apr 2011 15:40:16 -0500 Subject: Change '== None' to 'is None' --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ccfce39e4..a405b43fe 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1116,7 +1116,7 @@ class LibvirtConnection(driver.ComputeDriver): if child.name == 'target': devdst = child.prop('dev') - if devdst == None: + if devdst is None: continue disks.append(devdst) @@ -1158,7 +1158,7 @@ class LibvirtConnection(driver.ComputeDriver): if child.name == 'target': devdst = child.prop('dev') - if devdst == None: + if devdst is None: continue interfaces.append(devdst) -- cgit From 65ca5ba31f2c5ecea05290390ad66e65543aa83d Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 18 Apr 2011 15:49:06 -0500 Subject: pep8 fixes --- nova/virt/vmwareapi/vim.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/virt') diff --git a/nova/virt/vmwareapi/vim.py b/nova/virt/vmwareapi/vim.py index 159e16a80..0cbdba363 100644 --- a/nova/virt/vmwareapi/vim.py +++ b/nova/virt/vmwareapi/vim.py @@ -43,6 +43,7 @@ flags.DEFINE_string('vmwareapi_wsdl_loc', if suds: + class VIMMessagePlugin(suds.plugin.MessagePlugin): def addAttributeForValue(self, node): -- cgit From f59f792c83c7f18e48903165df8d3dd78f45dd4c Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 18 Apr 2011 15:53:09 -0500 Subject: use 'is not None' instead of '!= None' --- nova/virt/libvirt_conn.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6ec15fbb8..bf5d0c00a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -437,9 +437,9 @@ class LibvirtConnection(driver.ComputeDriver): if child.prop('dev') == device: return str(node) finally: - if ctx != None: + if ctx is not None: ctx.xpathFreeContext() - if doc != None: + if doc is not None: doc.freeDoc() @exception.wrap_exception @@ -1119,9 +1119,9 @@ class LibvirtConnection(driver.ComputeDriver): disks.append(devdst) finally: - if ctx != None: + if ctx is not None: ctx.xpathFreeContext() - if doc != None: + if doc is not None: doc.freeDoc() return disks @@ -1161,9 +1161,9 @@ class LibvirtConnection(driver.ComputeDriver): interfaces.append(devdst) finally: - if ctx != None: + if ctx is not None: ctx.xpathFreeContext() - if doc != None: + if doc is not None: doc.freeDoc() return interfaces -- cgit From d9628e8ba927074b6e80433de80d745b34acaa28 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 18 Apr 2011 17:00:39 -0500 Subject: First round of pylint cleanup. --- nova/virt/xenapi/fake.py | 2 +- nova/virt/xenapi/vm_utils.py | 9 ++------- nova/virt/xenapi/vmops.py | 10 ++++------ 3 files changed, 7 insertions(+), 14 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 4434dbf0b..e36ef3288 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -294,7 +294,7 @@ class Failure(Exception): def __str__(self): try: return str(self.details) - except Exception, exc: + except Exception: return "XenAPI Fake Failure: %s" % str(self.details) def _details_map(self): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d2045a557..1927500ad 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -28,10 +28,7 @@ import urllib import uuid from xml.dom import minidom -from eventlet import event import glance.client -from nova import context -from nova import db from nova import exception from nova import flags from nova import log as logging @@ -306,7 +303,6 @@ class VMHelper(HelperBase): % locals()) vm_vdi_ref, vm_vdi_rec = cls.get_vdi_for_vm_safely(session, vm_ref) - vm_vdi_uuid = vm_vdi_rec["uuid"] sr_ref = vm_vdi_rec["SR"] original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) @@ -755,14 +751,14 @@ class VMHelper(HelperBase): session.call_xenapi('SR.scan', sr_ref) -def get_rrd(host, uuid): +def get_rrd(host, vm_uuid): """Return the VM RRD XML as a string""" try: xml = urllib.urlopen("http://%s:%s@%s/vm_rrd?uuid=%s" % ( FLAGS.xenapi_connection_username, FLAGS.xenapi_connection_password, host, - uuid)) + vm_uuid)) return xml.read() except IOError: return None @@ -1020,7 +1016,6 @@ def _stream_disk(dev, image_type, virtual_size, image_file): def _write_partition(virtual_size, dev): dest = '/dev/%s' % dev - mbr_last = MBR_SIZE_SECTORS - 1 primary_first = MBR_SIZE_SECTORS primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1 diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7c7aa8e98..8b6a35f74 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -387,7 +387,6 @@ class VMOps(object): def link_disks(self, instance, base_copy_uuid, cow_uuid): """Links the base copy VHD to the COW via the XAPI plugin.""" - vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, @@ -760,7 +759,6 @@ class VMOps(object): instance))) for vm in rescue_vms: - rescue_name = vm["name"] rescue_vm_ref = vm["vm_ref"] self._destroy_rescue_instance(rescue_vm_ref) @@ -798,7 +796,7 @@ class VMOps(object): 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, + ips = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) networks = db.network_get_all_by_instance(admin_context, instance['id']) @@ -808,7 +806,7 @@ class VMOps(object): network_info = [] for network in networks: - network_IPs = [ip for ip in IPs if ip.network_id == network.id] + network_ips = [ip for ip in ips if ip.network_id == network.id] def ip_dict(ip): return { @@ -830,7 +828,7 @@ class VMOps(object): 'mac': instance.mac_address, 'rxtx_cap': inst_type['rxtx_cap'], 'dns': [network['dns']], - 'ips': [ip_dict(ip) for ip in network_IPs]} + 'ips': [ip_dict(ip) for ip in network_ips]} if network['cidr_v6']: info['ip6s'] = [ip6_dict()] if network['gateway_v6']: @@ -923,7 +921,7 @@ class VMOps(object): try: ret = self._make_xenstore_call('read_record', vm, path, {'ignore_missing_path': 'True'}) - except self.XenAPI.Failure, e: + except self.XenAPI.Failure: return None ret = json.loads(ret) if ret == "None": -- cgit From 2ef03c6a0a8c5705249c3b5be755e0a13ca39332 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 18 Apr 2011 22:02:54 -0400 Subject: Implement get_host_ip_addr in the libvirt compute driver. --- nova/virt/libvirt_conn.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d212be3c9..511bfde36 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -40,6 +40,7 @@ import multiprocessing import os import random import shutil +import socket import subprocess import sys import tempfile @@ -732,6 +733,11 @@ class LibvirtConnection(driver.ComputeDriver): subprocess.Popen(cmd, shell=True) return {'token': token, 'host': host, 'port': port} + def get_host_ip_addr(self): + hostname = self._conn.getHostname() + ip = socket.gethostbyname(hostname) + return ip + @exception.wrap_exception def get_vnc_console(self, instance): def get_vnc_port_for_instance(instance_name): -- cgit From 9c6cbd448088f5096bba9866d8057300256c6d34 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 19 Apr 2011 09:48:07 -0400 Subject: moving dynamic i18n to static --- nova/virt/vmwareapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index f2f68ec22..b700c438f 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -620,7 +620,7 @@ class VMWareVMOps(object): LOG.debug(_("Suspended the VM %s ") % instance.name) # Raise Exception if VM is poweredOff elif pwr_state == "poweredOff": - reason = _("instance is poweredOff and can not be suspended.") + reason = _("instance is powered off and can not be suspended.") raise exception.InstanceSuspendFailure(reason=reason) LOG.debug(_("VM %s was already in suspended state. So returning " -- cgit From 9812ae8d3c113475f8ef5d609874317d0b330425 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 11:05:37 -0400 Subject: Removed extra calls in exception handling and standardized the way LoopingCalls are done. --- nova/virt/libvirt_conn.py | 186 ++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 88 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d212be3c9..4e96b4e97 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -154,8 +154,8 @@ def _get_net_and_prefixlen(cidr): def _get_ip_version(cidr): - net = IPy.IP(cidr) - return int(net.version()) + net = IPy.IP(cidr) + return int(net.version()) def _get_network_info(instance): @@ -359,28 +359,24 @@ class LibvirtConnection(driver.ComputeDriver): locals()) raise - # We'll save this for when we do shutdown, - # instead of destroy - but destroy returns immediately - timer = utils.LoopingCall(f=None) + def _wait_for_destroy(): + """Called at an interval until the VM is running again.""" + instance_name = insatnce['name'] - while True: try: - state = self.get_info(instance['name'])['state'] - db.instance_set_state(context.get_admin_context(), - instance['id'], state) - if state == power_state.SHUTOFF: - break - - # Let's not hammer on the DB - time.sleep(1) - except Exception as ex: - msg = _("Error encountered when destroying instance '%(id)s': " - "%(ex)s") % {"id": instance["id"], "ex": ex} - LOG.debug(msg) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTOFF) - break + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During destroy, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.SHUTOFF: + msg = _("Instance %s destroyed successfully.") % instance_name + LOG.debug(instance_name) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_destroy) + timer.start(interval=0.5, now=True) self.firewall_driver.unfilter_instance(instance) @@ -522,6 +518,12 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def reboot(self, instance): + """Reboot a virtual machine, given an instance reference. + + This method actually destroys and re-creates the domain to ensure the + reboot happens, as the guest OS cannot ignore this action. + + """ self.destroy(instance, False) xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) @@ -529,24 +531,23 @@ class LibvirtConnection(driver.ComputeDriver): self._create_new_domain(xml) self.firewall_driver.apply_instance_filter(instance) - timer = utils.LoopingCall(f=None) - def _wait_for_reboot(): + """Called at an interval until the VM is running again.""" + instance_name = insatnce['name'] + try: - state = self.get_info(instance['name'])['state'] - db.instance_set_state(context.get_admin_context(), - instance['id'], state) - if state == power_state.RUNNING: - LOG.debug(_('instance %s: rebooted'), instance['name']) - timer.stop() - except Exception, exn: - LOG.exception(_('_wait_for_reboot failed: %s'), exn) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) - timer.stop() + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s rebooted successfully.") % instance_name + LOG.debug(instance_name) + raise utils.LoopingCallDone - timer.f = _wait_for_reboot + timer = utils.LoopingCall(_wait_for_reboot) return timer.start(interval=0.5, now=True) @exception.wrap_exception @@ -566,7 +567,15 @@ class LibvirtConnection(driver.ComputeDriver): raise exception.ApiError("resume not supported for libvirt") @exception.wrap_exception - def rescue(self, instance, callback=None): + def rescue(self, instance): + """Loads a VM using rescue images. + + A rescue is normally performed when something goes wrong with the + primary images and data needs to be corrected/recovered. Rescuing + should not edit or over-ride the original image, only allow for + data recovery. + + """ self.destroy(instance, False) xml = self.to_xml(instance, rescue=True) @@ -576,29 +585,33 @@ class LibvirtConnection(driver.ComputeDriver): self._create_image(instance, xml, '.rescue', rescue_images) self._create_new_domain(xml) - timer = utils.LoopingCall(f=None) - def _wait_for_rescue(): + """Called at an interval until the VM is running again.""" + instance_name = instance['name'] + try: - state = self.get_info(instance['name'])['state'] - db.instance_set_state(None, instance['id'], state) - if state == power_state.RUNNING: - LOG.debug(_('instance %s: rescued'), instance['name']) - timer.stop() - except Exception, exn: - LOG.exception(_('_wait_for_rescue failed: %s'), exn) - db.instance_set_state(None, - instance['id'], - power_state.SHUTDOWN) - timer.stop() + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s rescued successfully.") % instance_name + LOG.debug(instance_name) + raise utils.LoopingCallDone - timer.f = _wait_for_rescue + timer = utils.LoopingCall(_wait_for_rescue) return timer.start(interval=0.5, now=True) @exception.wrap_exception - def unrescue(self, instance, callback=None): - # NOTE(vish): Because reboot destroys and recreates an instance using - # the normal xml file, we can just call reboot here + def unrescue(self, instance): + """Reboot the VM which is being rescued back into primary images. + + Because reboot destroys and re-creates instances, unresue should + simply call reboot. + + """ self.reboot(instance) @exception.wrap_exception @@ -610,10 +623,6 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def spawn(self, instance, network_info=None): xml = self.to_xml(instance, False, network_info) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.NOSTATE, - 'launching') self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(instance, xml, network_info) @@ -626,25 +635,23 @@ class LibvirtConnection(driver.ComputeDriver): instance['name']) domain.setAutostart(1) - timer = utils.LoopingCall(f=None) - def _wait_for_boot(): + """Called at an interval until the VM is running.""" + instance_name = insatnce['name'] + try: - state = self.get_info(instance['name'])['state'] - db.instance_set_state(context.get_admin_context(), - instance['id'], state) - if state == power_state.RUNNING: - LOG.debug(_('instance %s: booted'), instance['name']) - timer.stop() - except: - LOG.exception(_('instance %s: failed to boot'), - instance['name']) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) - timer.stop() + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone - timer.f = _wait_for_boot + if state == power_state.RUNNING: + msg = _("Instance %s spawned successfully.") % instance_name + LOG.debug(instance_name) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_boot) return timer.start(interval=0.5, now=True) def _flush_xen_console(self, virsh_output): @@ -1045,21 +1052,24 @@ class LibvirtConnection(driver.ComputeDriver): return xml def get_info(self, instance_name): - # NOTE(justinsb): When libvirt isn't running / can't connect, we get: - # libvir: Remote error : unable to connect to - # '/var/run/libvirt/libvirt-sock', libvirtd may need to be started: - # No such file or directory + """Retrieve information from libvirt for a specific instance name. + + If a libvirt error is encountered during lookup, we might raise a + NotFound exception or Error exception depending on how severe the + libvirt error is. + + """ try: virt_dom = self._conn.lookupByName(instance_name) - except libvirt.libvirtError as e: - errcode = e.get_error_code() - if errcode == libvirt.VIR_ERR_NO_DOMAIN: - raise exception.NotFound(_("Instance %s not found") - % instance_name) - LOG.warning(_("Error from libvirt during lookup. " - "Code=%(errcode)s Error=%(e)s") % - locals()) - raise + except libvirt.libvirtError as ex: + error_code = ex.get_error_code() + if error_code == libvirt.VIR_ERR_NO_DOMAIN: + msg = _("Instance %s not found") % instance_name + raise exception.NotFound(msg) + + msg = _("Error from libvirt while looking up %(instance_name)s: " + "[Error Code %(error_code)s] %(ex)s") % locals() + raise exception.Error(msg) (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() return {'state': state, -- cgit From 8e98888323d4308640ab5061cdae5ccd4e3ebabf Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 11:09:07 -0400 Subject: Pretty critical spelling error. --- nova/virt/libvirt_conn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e96b4e97..fad8dd52a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -361,7 +361,7 @@ class LibvirtConnection(driver.ComputeDriver): def _wait_for_destroy(): """Called at an interval until the VM is running again.""" - instance_name = insatnce['name'] + instance_name = instance['name'] try: state = self.get_info(instance_name)['state'] @@ -533,7 +533,7 @@ class LibvirtConnection(driver.ComputeDriver): def _wait_for_reboot(): """Called at an interval until the VM is running again.""" - instance_name = insatnce['name'] + instance_name = instance['name'] try: state = self.get_info(instance_name)['state'] @@ -637,7 +637,7 @@ class LibvirtConnection(driver.ComputeDriver): def _wait_for_boot(): """Called at an interval until the VM is running.""" - instance_name = insatnce['name'] + instance_name = instance['name'] try: state = self.get_info(instance_name)['state'] -- cgit From 3e31785d86c59dbda62e3a3ba3a1e23452e52562 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 11:16:46 -0400 Subject: Tweak to destroy loop logic. --- nova/virt/libvirt_conn.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index fad8dd52a..53137395e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -366,11 +366,6 @@ class LibvirtConnection(driver.ComputeDriver): try: state = self.get_info(instance_name)['state'] except exception.NotFound: - msg = _("During destroy, %s disappeared.") % instance_name - LOG.error(msg) - raise utils.LoopingCallDone - - if state == power_state.SHUTOFF: msg = _("Instance %s destroyed successfully.") % instance_name LOG.debug(instance_name) raise utils.LoopingCallDone -- cgit From ad2d97972d63f50500ec8215c7f8f04d87468060 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 11:29:26 -0400 Subject: Fixed info messages. --- nova/virt/libvirt_conn.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 53137395e..13378bbd2 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -367,7 +367,7 @@ class LibvirtConnection(driver.ComputeDriver): state = self.get_info(instance_name)['state'] except exception.NotFound: msg = _("Instance %s destroyed successfully.") % instance_name - LOG.debug(instance_name) + LOG.info(msg) raise utils.LoopingCallDone timer = utils.LoopingCall(_wait_for_destroy) @@ -539,7 +539,7 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.RUNNING: msg = _("Instance %s rebooted successfully.") % instance_name - LOG.debug(instance_name) + LOG.info(msg) raise utils.LoopingCallDone timer = utils.LoopingCall(_wait_for_reboot) @@ -593,7 +593,7 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.RUNNING: msg = _("Instance %s rescued successfully.") % instance_name - LOG.debug(instance_name) + LOG.info(msg) raise utils.LoopingCallDone timer = utils.LoopingCall(_wait_for_rescue) @@ -643,7 +643,7 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.RUNNING: msg = _("Instance %s spawned successfully.") % instance_name - LOG.debug(instance_name) + LOG.info(msg) raise utils.LoopingCallDone timer = utils.LoopingCall(_wait_for_boot) -- cgit From 25e1e2d64ad43638ad4231e6e6edd84d96e14bdb Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 11:33:51 -0400 Subject: Merged trunk and fixed small comment. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 13378bbd2..2582b9730 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -360,7 +360,7 @@ class LibvirtConnection(driver.ComputeDriver): raise def _wait_for_destroy(): - """Called at an interval until the VM is running again.""" + """Called at an interval until the VM is gone.""" instance_name = instance['name'] try: -- cgit From 745351d1e2a98a98de0a5f955385a92c01110684 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 19 Apr 2011 09:19:52 -0700 Subject: Fixes cloudpipe to get the proper ip address. * Changes FLAGS.vpn_image_id to integer * Converts to str when comparing because instance['image_id'] is a str * Removes unused method from db * Converts integer_id to ami when launching * Adds docs for setting up interface in cloudpipe image --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5da091920..9c8d64446 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1837,7 +1837,7 @@ class NWFilterFirewall(FirewallDriver): """ if not network_info: network_info = _get_network_info(instance) - if instance['image_id'] == FLAGS.vpn_image_id: + if instance['image_id'] == str(FLAGS.vpn_image_id): base_filter = 'nova-vpn' else: base_filter = 'nova-base' -- cgit From 3e3f8e1f09d0615e66cc1be0b656d0d8e1d69671 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 19 Apr 2011 12:36:07 -0400 Subject: Abstracted lookupByName calls to _lookup_by_name for centralized error handling. --- nova/virt/libvirt_conn.py | 53 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 27 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2582b9730..c1f62c391 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,19 +309,10 @@ class LibvirtConnection(driver.ComputeDriver): def destroy(self, instance, cleanup=True): instance_name = instance['name'] - # TODO(justinsb): Refactor all lookupByName calls for error-handling try: - virt_dom = self._conn.lookupByName(instance_name) - except libvirt.libvirtError as e: - errcode = e.get_error_code() - if errcode == libvirt.VIR_ERR_NO_DOMAIN: - virt_dom = None - else: - LOG.warning(_("Error from libvirt during lookup of " - "%(instance_name)s. Code=%(errcode)s " - "Error=%(e)s") % - locals()) - raise + virt_dom = self._lookup_by_name(instance_name) + except exception.NotFound: + virt_dom = None # If the instance is already terminated, we're still happy # Otherwise, destroy it @@ -392,7 +383,7 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def attach_volume(self, instance_name, device_path, mountpoint): - virt_dom = self._conn.lookupByName(instance_name) + virt_dom = self._lookup_by_name(instance_name) mount_device = mountpoint.rpartition("/")[2] if device_path.startswith('/dev/'): xml = """ @@ -436,7 +427,7 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def detach_volume(self, instance_name, mountpoint): - virt_dom = self._conn.lookupByName(instance_name) + virt_dom = self._lookup_by_name(instance_name) mount_device = mountpoint.rpartition("/")[2] xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device) if not xml: @@ -453,7 +444,7 @@ class LibvirtConnection(driver.ComputeDriver): """ image_service = utils.import_object(FLAGS.image_service) - virt_dom = self._conn.lookupByName(instance['name']) + virt_dom = self._lookup_by_name(instance['name']) elevated = context.get_admin_context() base = image_service.show(elevated, instance['image_id']) @@ -712,7 +703,7 @@ class LibvirtConnection(driver.ComputeDriver): raise Exception(_('Unable to find an open port')) def get_pty_for_instance(instance_name): - virt_dom = self._conn.lookupByName(instance_name) + virt_dom = self._lookup_by_name(instance_name) xml = virt_dom.XMLDesc(0) dom = minidom.parseString(xml) @@ -737,7 +728,7 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def get_vnc_console(self, instance): def get_vnc_port_for_instance(instance_name): - virt_dom = self._conn.lookupByName(instance_name) + virt_dom = self._lookup_by_name(instance_name) xml = virt_dom.XMLDesc(0) # TODO: use etree instead of minidom dom = minidom.parseString(xml) @@ -1046,16 +1037,15 @@ class LibvirtConnection(driver.ComputeDriver): instance['name']) return xml - def get_info(self, instance_name): - """Retrieve information from libvirt for a specific instance name. + def _lookup_by_name(self, instance_name): + """Retrieve libvirt domain object given an instance name. - If a libvirt error is encountered during lookup, we might raise a - NotFound exception or Error exception depending on how severe the - libvirt error is. + All libvirt error handling should be handled in this method and + relevant nova exceptions should be raised in response. """ try: - virt_dom = self._conn.lookupByName(instance_name) + return self._conn.lookupByName(instance_name) except libvirt.libvirtError as ex: error_code = ex.get_error_code() if error_code == libvirt.VIR_ERR_NO_DOMAIN: @@ -1066,6 +1056,15 @@ class LibvirtConnection(driver.ComputeDriver): "[Error Code %(error_code)s] %(ex)s") % locals() raise exception.Error(msg) + def get_info(self, instance_name): + """Retrieve information from libvirt for a specific instance name. + + If a libvirt error is encountered during lookup, we might raise a + NotFound exception or Error exception depending on how severe the + libvirt error is. + + """ + virt_dom = self._lookup_by_name(instance_name) (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() return {'state': state, 'max_mem': max_mem, @@ -1102,7 +1101,7 @@ class LibvirtConnection(driver.ComputeDriver): Returns a list of all block devices for this domain. """ - domain = self._conn.lookupByName(instance_name) + domain = self._lookup_by_name(instance_name) # TODO(devcamcar): Replace libxml2 with etree. xml = domain.XMLDesc(0) doc = None @@ -1144,7 +1143,7 @@ class LibvirtConnection(driver.ComputeDriver): Returns a list of all network interfaces for this instance. """ - domain = self._conn.lookupByName(instance_name) + domain = self._lookup_by_name(instance_name) # TODO(devcamcar): Replace libxml2 with etree. xml = domain.XMLDesc(0) doc = None @@ -1359,7 +1358,7 @@ class LibvirtConnection(driver.ComputeDriver): Note that this function takes an instance name, not an Instance, so that it can be called by monitor. """ - domain = self._conn.lookupByName(instance_name) + domain = self._lookup_by_name(instance_name) return domain.blockStats(disk) def interface_stats(self, instance_name, interface): @@ -1367,7 +1366,7 @@ class LibvirtConnection(driver.ComputeDriver): Note that this function takes an instance name, not an Instance, so that it can be called by monitor. """ - domain = self._conn.lookupByName(instance_name) + domain = self._lookup_by_name(instance_name) return domain.interfaceStats(interface) def get_console_pool_info(self, console_type): -- cgit From 1378b117b7ea2bb05219b5a0e48f4b1ae8cac9ae Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 19 Apr 2011 13:17:21 -0400 Subject: refactoring usage of exception.Duplicate errors --- nova/virt/hyperv.py | 3 +-- nova/virt/vmwareapi/vmops.py | 3 +-- nova/virt/xenapi/vm_utils.py | 3 +-- nova/virt/xenapi/vmops.py | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 13f403a66..85d5190fb 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -143,8 +143,7 @@ class HyperVConnection(driver.ComputeDriver): """ Create a new VM and start it.""" vm = self._lookup(instance.name) if vm is not None: - raise exception.Duplicate(_('Attempt to create duplicate vm %s') % - instance.name) + raise exception.InstanceExists(name=instance.name) user = manager.AuthManager().get_user(instance['user_id']) project = manager.AuthManager().get_project(instance['project_id']) diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index b700c438f..d77f9f8cb 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -100,8 +100,7 @@ class VMWareVMOps(object): """ vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref: - raise exception.Duplicate(_("Attempted to create a VM with a name" - " %s, but that already exists on the host") % instance.name) + raise exception.InstanceExists(name=instance.name) client_factory = self._session._get_vim().client.factory service_content = self._session._get_vim().get_service_content() diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d2045a557..4b00b45ca 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -647,8 +647,7 @@ class VMHelper(HelperBase): if n == 0: return None elif n > 1: - raise exception.Duplicate(_('duplicate name found: %s') % - name_label) + raise exception.InstanceExists(name=name_label) else: return vm_refs[0] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7c7aa8e98..6f2870501 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -127,8 +127,7 @@ class VMOps(object): instance_name = instance.name vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is not None: - raise exception.Duplicate(_('Attempted to create' - ' non-unique name %s') % instance_name) + raise exception.InstanceExists(name=instance_name) #ensure enough free memory is available if not VMHelper.ensure_free_mem(self._session, instance): -- cgit From 803d246c35256e0578837226b1a91003e451ab6f Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Wed, 20 Apr 2011 18:35:07 +0400 Subject: instance type get approach changed. tests fixed --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c12b6e91e..d5a88ebed 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -167,8 +167,8 @@ def _get_network_info(instance): 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']) + flavor = db.instance_type_get_by_id(admin_context, + instance['instance_type_id']) network_info = [] for network in networks: -- cgit From bdbfcb49179d32da5fcecd75fb849efe71469b00 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 20 Apr 2011 11:16:35 -0400 Subject: Reverted bad merge. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6b417124e..715512507 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -609,7 +609,7 @@ class LibvirtConnection(driver.ComputeDriver): # for xenapi(tr3buchet) @exception.wrap_exception def spawn(self, instance, network_info=None): - xml = self.to_xml(instance, network_info) + xml = self.to_xml(instance, False, network_info) self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) -- cgit From a4b78306d31e1ef84d5dc9550ef2dcb1ed030fa2 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Wed, 20 Apr 2011 21:34:55 +0400 Subject: fix after review: style, improving tests, replacing underscore --- nova/virt/libvirt_conn.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 73a804014..7e8ff409a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1839,12 +1839,12 @@ class NWFilterFirewall(FirewallDriver): 'nova-allow-dhcp-server'] if FLAGS.use_ipv6: - networks = [network for (network, _) in network_info if + networks = [network for (network, _m) in network_info if network['gateway_v6']] if networks: instance_secgroup_filter_children.\ - append('nova-allow-ra-server') + append('nova-allow-ra-server') for security_group in \ db.security_group_get_by_instance(ctxt, instance['id']): @@ -1859,8 +1859,8 @@ class NWFilterFirewall(FirewallDriver): instance_secgroup_filter_children)) network_filters = self.\ - _create_network_filters(instance, network_info, - instance_secgroup_filter_name) + _create_network_filters(instance, network_info, + instance_secgroup_filter_name) for (name, children) in network_filters: self._define_filters(name, children) @@ -1873,7 +1873,7 @@ class NWFilterFirewall(FirewallDriver): base_filter = 'nova-base' result = [] - for (_, mapping) in network_info: + for (_n, mapping) in network_info: nic_id = mapping['mac'].replace(':', '') instance_filter_name = self._instance_filter_name(instance, nic_id) instance_filter_children = [base_filter, @@ -1996,11 +1996,11 @@ class IptablesFirewallDriver(FirewallDriver): return ['-d %s -j $%s' % (ip, chain_name) for ip in ips] def _filters_for_instance(self, chain_name, network_info): - ips_v4 = [ip['ip'] for (_, mapping) in network_info + ips_v4 = [ip['ip'] for (_n, mapping) in network_info for ip in mapping['ips']] ipv4_rules = self._create_filter(ips_v4, chain_name) - ips_v6 = [ip['ip'] for (_, mapping) in network_info + ips_v6 = [ip['ip'] for (_n, mapping) in network_info for ip in mapping['ip6s']] ipv6_rules = self._create_filter(ips_v6, chain_name) -- cgit From 783cea4dc4497176b57b7a718a29bde102fb92bc Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Thu, 21 Apr 2011 04:31:17 +0400 Subject: style fix --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d5a88ebed..a42433fed 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -168,7 +168,7 @@ def _get_network_info(instance): networks = db.network_get_all_by_instance(admin_context, instance['id']) flavor = db.instance_type_get_by_id(admin_context, - instance['instance_type_id']) + instance['instance_type_id']) network_info = [] for network in networks: -- cgit From ba00a83490d6f442688d42f7f58c5f6cc566e1ee Mon Sep 17 00:00:00 2001 From: Yoshiaki Tamura Date: Thu, 21 Apr 2011 16:54:59 +0900 Subject: Fix parameter mismatch calling _create_image() from spawn() in libvirt_conn.py --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9e815799f..a8de7147b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -612,7 +612,7 @@ class LibvirtConnection(driver.ComputeDriver): 'launching') self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) - self._create_image(instance, xml, network_info) + self._create_image(instance, xml, network_info=network_info) domain = self._create_new_domain(xml) LOG.debug(_("instance %s: is running"), instance['name']) self.firewall_driver.apply_instance_filter(instance) -- cgit From 2d82195d59240ea53d4726879d2a28a5872e58f7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 21 Apr 2011 07:39:49 -0700 Subject: use vpn filter in basic filtering so cloudpipe works with iptables driver --- nova/virt/libvirt_conn.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9c8d64446..3dcb8ae42 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1734,11 +1734,16 @@ class NWFilterFirewall(FirewallDriver): logging.info('ensuring static filters') self._ensure_static_filters() + if instance['image_id'] == str(FLAGS.vpn_image_id): + base_filter = 'nova-vpn' + else: + base_filter = 'nova-base' + for (network, mapping) in network_info: nic_id = mapping['mac'].replace(':', '') instance_filter_name = self._instance_filter_name(instance, nic_id) self._define_filter(self._filter_container(instance_filter_name, - ['nova-base'])) + [base_filter])) def _ensure_static_filters(self): if self.static_filters_configured: @@ -1749,11 +1754,12 @@ class NWFilterFirewall(FirewallDriver): 'no-ip-spoofing', 'no-arp-spoofing', 'allow-dhcp-server'])) + self._define_filter(self._filter_container('nova-vpn', + ['allow-dhcp-server'])) self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) self._define_filter(self.nova_ra_filter) - self._define_filter(self.nova_vpn_filter) if FLAGS.allow_project_net_traffic: self._define_filter(self.nova_project_filter) if FLAGS.use_ipv6: @@ -1767,14 +1773,6 @@ class NWFilterFirewall(FirewallDriver): ''.join(["" % (f,) for f in filters])) return xml - nova_vpn_filter = ''' - 2086015e-cf03-11df-8c5d-080027c27973 - - - - - ''' - def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: -- cgit From 8681db3aa9104f97a84a3323b102ed10af269888 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 21 Apr 2011 15:50:04 -0400 Subject: Addressing exception.NotFound across the project --- nova/virt/fake.py | 3 +-- nova/virt/hyperv.py | 10 ++++------ nova/virt/libvirt_conn.py | 5 ++--- nova/virt/vmwareapi/fake.py | 9 ++++----- nova/virt/vmwareapi/vmops.py | 27 +++++++++------------------ nova/virt/xenapi/vm_utils.py | 6 ++---- nova/virt/xenapi/vmops.py | 11 ++++------- nova/virt/xenapi/volumeops.py | 6 ++---- 8 files changed, 28 insertions(+), 49 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index c3d5230df..33f37b512 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -288,8 +288,7 @@ class FakeConnection(driver.ComputeDriver): knowledge of the instance """ if instance_name not in self.instances: - raise exception.NotFound(_("Instance %s Not Found") - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) i = self.instances[instance_name] return {'state': i.state, 'max_mem': 0, diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 13f403a66..507ea5457 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -368,7 +368,7 @@ class HyperVConnection(driver.ComputeDriver): """Reboot the specified instance.""" vm = self._lookup(instance.name) if vm is None: - raise exception.NotFound('instance not present %s' % instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) self._set_vm_state(instance.name, 'Reboot') def destroy(self, instance): @@ -412,7 +412,7 @@ class HyperVConnection(driver.ComputeDriver): """Get information about the VM""" vm = self._lookup(instance_id) if vm is None: - raise exception.NotFound('instance not present %s' % instance_id) + raise exception.InstanceNotFound(instance_id=instance_id) vm = self._conn.Msvm_ComputerSystem(ElementName=instance_id)[0] vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = vm.associators( @@ -474,14 +474,12 @@ class HyperVConnection(driver.ComputeDriver): def attach_volume(self, instance_name, device_path, mountpoint): vm = self._lookup(instance_name) if vm is None: - raise exception.NotFound('Cannot attach volume to missing %s vm' - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) def detach_volume(self, instance_name, mountpoint): vm = self._lookup(instance_name) if vm is None: - raise exception.NotFound('Cannot detach volume from missing %s ' - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) def poll_rescued_instances(self, timeout): pass diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 55e1d4295..705b6380c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -449,7 +449,7 @@ class LibvirtConnection(driver.ComputeDriver): mount_device = mountpoint.rpartition("/")[2] xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device) if not xml: - raise exception.NotFound(_("No disk at %s") % mount_device) + raise exception.DiskNotFound(location=mount_device) virt_dom.detachDevice(xml) @exception.wrap_exception @@ -1054,8 +1054,7 @@ class LibvirtConnection(driver.ComputeDriver): except libvirt.libvirtError as e: errcode = e.get_error_code() if errcode == libvirt.VIR_ERR_NO_DOMAIN: - raise exception.NotFound(_("Instance %s not found") - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) LOG.warning(_("Error from libvirt during lookup. " "Code=%(errcode)s Error=%(e)s") % locals()) diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 4bb467fa9..7370684bd 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -387,12 +387,11 @@ def _add_file(file_path): def _remove_file(file_path): """Removes a file reference from the db.""" if _db_content.get("files") is None: - raise exception.NotFound(_("No files have been added yet")) + raise exception.NoFilesFound() # Check if the remove is for a single file object or for a folder if file_path.find(".vmdk") != -1: if file_path not in _db_content.get("files"): - raise exception.NotFound(_("File- '%s' is not there in the " - "datastore") % file_path) + raise exception.FileNotFound(file_path=file_path) _db_content.get("files").remove(file_path) else: # Removes the files in the folder and the folder too from the db @@ -579,7 +578,7 @@ class FakeVim(object): """Searches the datastore for a file.""" ds_path = kwargs.get("datastorePath") if _db_content.get("files", None) is None: - raise exception.NotFound(_("No files have been added yet")) + raise exception.NoFilesFound() for file in _db_content.get("files"): if file.find(ds_path) != -1: task_mdo = create_task(method, "success") @@ -591,7 +590,7 @@ class FakeVim(object): """Creates a directory in the datastore.""" ds_path = kwargs.get("name") if _db_content.get("files", None) is None: - raise exception.NotFound(_("No files have been added yet")) + raise exception.NoFilesFound() _db_content["files"].append(ds_path) def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"): diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index b700c438f..033a511f8 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -116,8 +116,7 @@ class VMWareVMOps(object): network_utils.get_network_with_the_name(self._session, net_name) if network_ref is None: - raise exception.NotFound(_("Network with the name '%s' doesn't" - " exist on the ESX host") % net_name) + raise exception.NetworkNotFoundForBridge(bridge=net_name) _check_if_network_bridge_exists() @@ -337,8 +336,7 @@ class VMWareVMOps(object): """ vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) client_factory = self._session._get_vim().client.factory service_content = self._session._get_vim().get_service_content() @@ -388,8 +386,7 @@ class VMWareVMOps(object): "VirtualMachine", "datastore") if not ds_ref_ret: - raise exception.NotFound(_("Failed to get the datastore " - "reference(s) which the VM uses")) + raise exception.DatastoreNotFound() ds_ref = ds_ref_ret.ManagedObjectReference[0] ds_browser = vim_util.get_dynamic_property( self._session._get_vim(), @@ -480,8 +477,7 @@ class VMWareVMOps(object): """Reboot a VM instance.""" vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) lst_properties = ["summary.guest.toolsStatus", "runtime.powerState", "summary.guest.toolsRunningStatus"] props = self._session._call_method(vim_util, "get_object_properties", @@ -605,8 +601,7 @@ class VMWareVMOps(object): """Suspend the specified instance.""" vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) pwr_state = self._session._call_method(vim_util, "get_dynamic_property", vm_ref, @@ -630,8 +625,7 @@ class VMWareVMOps(object): """Resume the specified instance.""" vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) pwr_state = self._session._call_method(vim_util, "get_dynamic_property", vm_ref, @@ -651,8 +645,7 @@ class VMWareVMOps(object): """Return data about the VM instance.""" vm_ref = self._get_vm_ref_from_the_name(instance_name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) lst_properties = ["summary.config.numCpu", "summary.config.memorySizeMB", @@ -688,8 +681,7 @@ class VMWareVMOps(object): """Return snapshot of console.""" vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) param_list = {"id": str(vm_ref)} base_url = "%s://%s/screen?%s" % (self._session._scheme, self._session._host_ip, @@ -717,8 +709,7 @@ class VMWareVMOps(object): """ vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: - raise exception.NotFound(_("instance - %s not present") % - instance.name) + raise exception.InstanceNotFound(instance_id=instance.id) network = db.network_get_by_instance(context.get_admin_context(), instance['id']) mac_addr = instance.mac_address diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 1927500ad..8f30a6d7c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -506,9 +506,7 @@ class VMHelper(HelperBase): try: return glance_disk_format2nova_type[disk_format] except KeyError: - raise exception.NotFound( - _("Unrecognized disk_format '%(disk_format)s'") - % locals()) + raise exception.InvalidDiskFormat(disk_format=disk_format) def determine_from_instance(): if instance.kernel_id: @@ -853,7 +851,7 @@ def safe_find_sr(session): """ sr_ref = find_sr(session) if sr_ref is None: - raise exception.NotFound(_('Cannot find SR to read/write VDI')) + raise exception.StorageRepositoryNotFound() return sr_ref diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8b6a35f74..69b1a163d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -260,8 +260,7 @@ class VMOps(object): instance_name = instance_or_vm.name vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is None: - raise exception.NotFound( - _('Instance not present %s') % instance_name) + raise exception.InstanceNotFound(instance_id=instance_obj.id) return vm_ref def _acquire_bootlock(self, vm): @@ -578,9 +577,8 @@ class VMOps(object): if not (instance.kernel_id and instance.ramdisk_id): # 2. We only have kernel xor ramdisk - raise exception.NotFound( - _("Instance %(instance_id)s has a kernel or ramdisk but not " - "both" % locals())) + raise exception.InstanceUnacceptable(instance_id=instance_id, + reason=_("instance has a kernel or ramdisk but not both")) # 3. We have both kernel and ramdisk (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk(self._session, @@ -721,8 +719,7 @@ class VMOps(object): "%s-rescue" % instance.name) if not rescue_vm_ref: - raise exception.NotFound(_( - "Instance is not in Rescue Mode: %s" % instance.name)) + raise exception.InstanceNotInRescueMode(instance_id=instance.id) original_vm_ref = VMHelper.lookup(self._session, instance.name) instance._rescue = False diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index 757ecf5ad..afcb8cf47 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -45,8 +45,7 @@ class VolumeOps(object): # Before we start, check that the VM exists vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is None: - raise exception.NotFound(_('Instance %s not found') - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) # NOTE: No Resource Pool concept so far LOG.debug(_("Attach_volume: %(instance_name)s, %(device_path)s," " %(mountpoint)s") % locals()) @@ -98,8 +97,7 @@ class VolumeOps(object): # Before we start, check that the VM exists vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is None: - raise exception.NotFound(_('Instance %s not found') - % instance_name) + raise exception.InstanceNotFound(instance_id=instance_name) # Detach VBD from VM LOG.debug(_("Detach_volume: %(instance_name)s, %(mountpoint)s") % locals()) -- cgit From 4eac8d2c4252eb866e99ef260c0c5d7df1d927d2 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 22 Apr 2011 12:47:09 -0400 Subject: Created new libvirt directory, moved libvirt_conn.py to libvirt/connection.py, moved libvirt templates, broke out firewall and network utilities. --- nova/virt/connection.py | 2 +- nova/virt/cpuinfo.xml.template | 9 - nova/virt/libvirt.xml.template | 122 -- nova/virt/libvirt/__init__.py | 0 nova/virt/libvirt/connection.py | 1527 ++++++++++++++++++++++ nova/virt/libvirt/cpuinfo.xml.template | 9 + nova/virt/libvirt/firewall.py | 630 ++++++++++ nova/virt/libvirt/libvirt.xml.template | 122 ++ nova/virt/libvirt/netutils.py | 95 ++ nova/virt/libvirt_conn.py | 2168 -------------------------------- 10 files changed, 2384 insertions(+), 2300 deletions(-) delete mode 100644 nova/virt/cpuinfo.xml.template delete mode 100644 nova/virt/libvirt.xml.template create mode 100644 nova/virt/libvirt/__init__.py create mode 100644 nova/virt/libvirt/connection.py create mode 100644 nova/virt/libvirt/cpuinfo.xml.template create mode 100644 nova/virt/libvirt/firewall.py create mode 100644 nova/virt/libvirt/libvirt.xml.template create mode 100644 nova/virt/libvirt/netutils.py delete mode 100644 nova/virt/libvirt_conn.py (limited to 'nova/virt') diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 99a8849f1..aeec17c98 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -27,9 +27,9 @@ from nova import utils from nova.virt import driver from nova.virt import fake from nova.virt import hyperv -from nova.virt import libvirt_conn from nova.virt import vmwareapi_conn from nova.virt import xenapi_conn +from nova.virt.libvirt import connection as libvirt_conn LOG = logging.getLogger("nova.virt.connection") diff --git a/nova/virt/cpuinfo.xml.template b/nova/virt/cpuinfo.xml.template deleted file mode 100644 index 48842b29d..000000000 --- a/nova/virt/cpuinfo.xml.template +++ /dev/null @@ -1,9 +0,0 @@ - - $arch - $model - $vendor - -#for $var in $features - -#end for - diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template deleted file mode 100644 index de2497a76..000000000 --- a/nova/virt/libvirt.xml.template +++ /dev/null @@ -1,122 +0,0 @@ - - ${name} - ${memory_kb} - -#if $type == 'lxc' - #set $disk_prefix = '' - #set $disk_bus = '' - exe - /sbin/init -#else if $type == 'uml' - #set $disk_prefix = 'ubd' - #set $disk_bus = 'uml' - uml - /usr/bin/linux - /dev/ubda -#else - #if $type == 'xen' - #set $disk_prefix = 'sd' - #set $disk_bus = 'scsi' - linux - /dev/xvda - #else - #set $disk_prefix = 'vd' - #set $disk_bus = 'virtio' - hvm - #end if - #if $getVar('rescue', False) - ${basepath}/kernel.rescue - ${basepath}/ramdisk.rescue - #else - #if $getVar('kernel', None) - ${kernel} - #if $type == 'xen' - ro - #else - root=/dev/vda console=ttyS0 - #end if - #if $getVar('ramdisk', None) - ${ramdisk} - #end if - #else - - #end if - #end if -#end if - - - - - ${vcpus} - -#if $type == 'lxc' - - - - -#else - #if $getVar('rescue', False) - - - - - - - - - - - #else - - - - - - #if $getVar('local', False) - - - - - - #end if - #end if -#end if - -#for $nic in $nics - - - - - - - -#if $getVar('nic.extra_params', False) - ${nic.extra_params} -#end if -#if $getVar('nic.gateway_v6', False) - -#end if - - -#end for - - - - - - - - - - - - - - - - -#if $getVar('vncserver_host', False) - -#end if - - diff --git a/nova/virt/libvirt/__init__.py b/nova/virt/libvirt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py new file mode 100644 index 000000000..972ac1bb9 --- /dev/null +++ b/nova/virt/libvirt/connection.py @@ -0,0 +1,1527 @@ +# 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. +# Copyright (c) 2010 Citrix Systems, Inc. +# +# 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. + +""" +A connection to a hypervisor through libvirt. + +Supports KVM, LXC, QEMU, UML, and XEN. + +**Related Flags** + +:libvirt_type: Libvirt domain type. Can be kvm, qemu, uml, xen + (default: kvm). +:libvirt_uri: Override for the default libvirt URI (depends on libvirt_type). +:libvirt_xml_template: Libvirt XML Template. +:rescue_image_id: Rescue ami image (default: ami-rescue). +:rescue_kernel_id: Rescue aki image (default: aki-rescue). +:rescue_ramdisk_id: Rescue ari image (default: ari-rescue). +:injected_network_template: Template file for injected network +:allow_project_net_traffic: Whether to allow in project network traffic + +""" + +import multiprocessing +import os +import random +import shutil +import subprocess +import sys +import tempfile +import time +import uuid +from xml.dom import minidom +from xml.etree import ElementTree + +from eventlet import greenthread +from eventlet import tpool + +import IPy + +from nova import context +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import utils +from nova import vnc +from nova.auth import manager +from nova.compute import instance_types +from nova.compute import power_state +from nova.virt import disk +from nova.virt import driver +from nova.virt import images +from nova.virt.libvirt import netutils + + +libvirt = None +libxml2 = None +Template = None + + +LOG = logging.getLogger('nova.virt.libvirt_conn') + + +FLAGS = flags.FLAGS +flags.DECLARE('live_migration_retry_count', 'nova.compute.manager') +# TODO(vish): These flags should probably go into a shared location +flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') +flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') +flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') +flags.DEFINE_string('libvirt_xml_template', + utils.abspath('virt/libvirt/libvirt.xml.template'), + 'Libvirt XML Template') +flags.DEFINE_string('libvirt_type', + 'kvm', + 'Libvirt domain type (valid options are: ' + 'kvm, lxc, qemu, uml, xen)') +flags.DEFINE_string('libvirt_uri', + '', + 'Override the default libvirt URI (which is dependent' + ' on libvirt_type)') +flags.DEFINE_bool('allow_project_net_traffic', + True, + 'Whether to allow in project network traffic') +flags.DEFINE_bool('use_cow_images', + True, + 'Whether to use cow images') +flags.DEFINE_string('ajaxterm_portrange', + '10000-12000', + 'Range of ports that ajaxterm should randomly try to bind') +flags.DEFINE_string('firewall_driver', + 'nova.virt.libvirt.firewall.IptablesFirewallDriver', + 'Firewall driver (defaults to iptables)') +flags.DEFINE_string('cpuinfo_xml_template', + utils.abspath('virt/libvirt/cpuinfo.xml.template'), + 'CpuInfo XML Template (Used only live migration now)') +flags.DEFINE_string('live_migration_uri', + "qemu+tcp://%s/system", + 'Define protocol used by live_migration feature') +flags.DEFINE_string('live_migration_flag', + "VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER", + 'Define live migration behavior.') +flags.DEFINE_integer('live_migration_bandwidth', 0, + 'Define live migration behavior') +flags.DEFINE_string('qemu_img', 'qemu-img', + 'binary to use for qemu-img commands') +flags.DEFINE_bool('start_guests_on_host_boot', False, + 'Whether to restart guests when the host reboots') + + +def get_connection(read_only): + # These are loaded late so that there's no need to install these + # libraries when not using libvirt. + # Cheetah is separate because the unit tests want to load Cheetah, + # but not libvirt. + global libvirt + global libxml2 + if libvirt is None: + libvirt = __import__('libvirt') + if libxml2 is None: + libxml2 = __import__('libxml2') + _late_load_cheetah() + return LibvirtConnection(read_only) + + +def _late_load_cheetah(): + global Template + if Template is None: + t = __import__('Cheetah.Template', globals(), locals(), + ['Template'], -1) + Template = t.Template + + +class LibvirtConnection(driver.ComputeDriver): + + def __init__(self, read_only): + super(LibvirtConnection, self).__init__() + self.libvirt_uri = self.get_uri() + + self.libvirt_xml = open(FLAGS.libvirt_xml_template).read() + self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read() + self._wrapped_conn = None + self.read_only = read_only + + fw_class = utils.import_class(FLAGS.firewall_driver) + self.firewall_driver = fw_class(get_connection=self._get_connection) + + def init_host(self, host): + # Adopt existing VM's running here + ctxt = context.get_admin_context() + for instance in db.instance_get_all_by_host(ctxt, host): + try: + LOG.debug(_('Checking state of %s'), instance['name']) + state = self.get_info(instance['name'])['state'] + except exception.NotFound: + state = power_state.SHUTOFF + + LOG.debug(_('Current state of %(name)s was %(state)s.'), + {'name': instance['name'], 'state': state}) + db.instance_set_state(ctxt, instance['id'], state) + + # NOTE(justinsb): We no longer delete SHUTOFF instances, + # the user may want to power them back on + + if state != power_state.RUNNING: + continue + self.firewall_driver.prepare_instance_filter(instance) + self.firewall_driver.apply_instance_filter(instance) + + def _get_connection(self): + if not self._wrapped_conn or not self._test_connection(): + LOG.debug(_('Connecting to libvirt: %s'), self.libvirt_uri) + self._wrapped_conn = self._connect(self.libvirt_uri, + self.read_only) + return self._wrapped_conn + _conn = property(_get_connection) + + def _test_connection(self): + try: + self._wrapped_conn.getInfo() + return True + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ + e.get_error_domain() == libvirt.VIR_FROM_REMOTE: + LOG.debug(_('Connection to libvirt broke')) + return False + raise + + def get_uri(self): + if FLAGS.libvirt_type == 'uml': + uri = FLAGS.libvirt_uri or 'uml:///system' + elif FLAGS.libvirt_type == 'xen': + uri = FLAGS.libvirt_uri or 'xen:///' + elif FLAGS.libvirt_type == 'lxc': + uri = FLAGS.libvirt_uri or 'lxc:///' + else: + uri = FLAGS.libvirt_uri or 'qemu:///system' + return uri + + def _connect(self, uri, read_only): + auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], + 'root', + None] + + if read_only: + return libvirt.openReadOnly(uri) + else: + return libvirt.openAuth(uri, auth, 0) + + def list_instances(self): + return [self._conn.lookupByID(x).name() + for x in self._conn.listDomainsID()] + + def _map_to_instance_info(self, domain): + """Gets info from a virsh domain object into an InstanceInfo""" + + # domain.info() returns a list of: + # state: one of the state values (virDomainState) + # maxMemory: the maximum memory used by the domain + # memory: the current amount of memory used by the domain + # nbVirtCPU: the number of virtual CPU + # puTime: the time used by the domain in nanoseconds + + (state, _max_mem, _mem, _num_cpu, _cpu_time) = domain.info() + name = domain.name() + + return driver.InstanceInfo(name, state) + + def list_instances_detail(self): + infos = [] + for domain_id in self._conn.listDomainsID(): + domain = self._conn.lookupByID(domain_id) + info = self._map_to_instance_info(domain) + infos.append(info) + return infos + + def destroy(self, instance, cleanup=True): + instance_name = instance['name'] + + try: + virt_dom = self._lookup_by_name(instance_name) + except exception.NotFound: + virt_dom = None + + # If the instance is already terminated, we're still happy + # Otherwise, destroy it + if virt_dom is not None: + try: + virt_dom.destroy() + except libvirt.libvirtError as e: + is_okay = False + errcode = e.get_error_code() + if errcode == libvirt.VIR_ERR_OPERATION_INVALID: + # If the instance if already shut off, we get this: + # Code=55 Error=Requested operation is not valid: + # domain is not running + (state, _max_mem, _mem, _cpus, _t) = virt_dom.info() + if state == power_state.SHUTOFF: + is_okay = True + + if not is_okay: + LOG.warning(_("Error from libvirt during destroy of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + try: + # NOTE(justinsb): We remove the domain definition. We probably + # would do better to keep it if cleanup=False (e.g. volumes?) + # (e.g. #2 - not losing machines on failure) + virt_dom.undefine() + except libvirt.libvirtError as e: + errcode = e.get_error_code() + LOG.warning(_("Error from libvirt during undefine of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + def _wait_for_destroy(): + """Called at an interval until the VM is gone.""" + instance_name = instance['name'] + + try: + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("Instance %s destroyed successfully.") % instance_name + LOG.info(msg) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_destroy) + timer.start(interval=0.5, now=True) + + self.firewall_driver.unfilter_instance(instance) + + if cleanup: + self._cleanup(instance) + + return True + + def _cleanup(self, instance): + target = os.path.join(FLAGS.instances_path, instance['name']) + instance_name = instance['name'] + LOG.info(_('instance %(instance_name)s: deleting instance files' + ' %(target)s') % locals()) + if FLAGS.libvirt_type == 'lxc': + disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images) + if os.path.exists(target): + shutil.rmtree(target) + + @exception.wrap_exception + def attach_volume(self, instance_name, device_path, mountpoint): + virt_dom = self._lookup_by_name(instance_name) + mount_device = mountpoint.rpartition("/")[2] + if device_path.startswith('/dev/'): + xml = """ + + + + """ % (device_path, mount_device) + elif ':' in device_path: + (protocol, name) = device_path.split(':') + xml = """ + + + + """ % (protocol, + name, + mount_device) + else: + raise exception.Invalid(_("Invalid device path %s") % device_path) + + virt_dom.attachDevice(xml) + + def _get_disk_xml(self, xml, device): + """Returns the xml for the disk mounted at device""" + try: + doc = libxml2.parseDoc(xml) + except: + return None + ctx = doc.xpathNewContext() + try: + ret = ctx.xpathEval('/domain/devices/disk') + for node in ret: + for child in node.children: + if child.name == 'target': + if child.prop('dev') == device: + return str(node) + finally: + if ctx is not None: + ctx.xpathFreeContext() + if doc is not None: + doc.freeDoc() + + @exception.wrap_exception + def detach_volume(self, instance_name, mountpoint): + virt_dom = self._lookup_by_name(instance_name) + mount_device = mountpoint.rpartition("/")[2] + xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device) + if not xml: + raise exception.NotFound(_("No disk at %s") % mount_device) + virt_dom.detachDevice(xml) + + @exception.wrap_exception + def snapshot(self, instance, image_id): + """Create snapshot from a running VM instance. + + This command only works with qemu 0.14+, the qemu_img flag is + provided so that a locally compiled binary of qemu-img can be used + to support this command. + + """ + image_service = utils.import_object(FLAGS.image_service) + virt_dom = self._lookup_by_name(instance['name']) + elevated = context.get_admin_context() + + base = image_service.show(elevated, instance['image_id']) + + metadata = {'disk_format': base['disk_format'], + 'container_format': base['container_format'], + 'is_public': False, + 'name': '%s.%s' % (base['name'], image_id), + 'properties': {'architecture': base['architecture'], + 'kernel_id': instance['kernel_id'], + 'image_location': 'snapshot', + 'image_state': 'available', + 'owner_id': instance['project_id'], + 'ramdisk_id': instance['ramdisk_id'], + } + } + + # Make the snapshot + snapshot_name = uuid.uuid4().hex + snapshot_xml = """ + + %s + + """ % snapshot_name + snapshot_ptr = virt_dom.snapshotCreateXML(snapshot_xml, 0) + + # Find the disk + xml_desc = virt_dom.XMLDesc(0) + domain = ElementTree.fromstring(xml_desc) + source = domain.find('devices/disk/source') + disk_path = source.get('file') + + # Export the snapshot to a raw image + temp_dir = tempfile.mkdtemp() + out_path = os.path.join(temp_dir, snapshot_name) + qemu_img_cmd = (FLAGS.qemu_img, + 'convert', + '-f', + 'qcow2', + '-O', + 'raw', + '-s', + snapshot_name, + disk_path, + out_path) + utils.execute(*qemu_img_cmd) + + # Upload that image to the image service + with open(out_path) as image_file: + image_service.update(elevated, + image_id, + metadata, + image_file) + + # Clean up + shutil.rmtree(temp_dir) + + @exception.wrap_exception + def reboot(self, instance): + """Reboot a virtual machine, given an instance reference. + + This method actually destroys and re-creates the domain to ensure the + reboot happens, as the guest OS cannot ignore this action. + + """ + self.destroy(instance, False) + xml = self.to_xml(instance) + self.firewall_driver.setup_basic_filtering(instance) + self.firewall_driver.prepare_instance_filter(instance) + self._create_new_domain(xml) + self.firewall_driver.apply_instance_filter(instance) + + def _wait_for_reboot(): + """Called at an interval until the VM is running again.""" + instance_name = instance['name'] + + try: + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s rebooted successfully.") % instance_name + LOG.info(msg) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_reboot) + return timer.start(interval=0.5, now=True) + + @exception.wrap_exception + def pause(self, instance, callback): + raise exception.ApiError("pause not supported for libvirt.") + + @exception.wrap_exception + def unpause(self, instance, callback): + raise exception.ApiError("unpause not supported for libvirt.") + + @exception.wrap_exception + def suspend(self, instance, callback): + raise exception.ApiError("suspend not supported for libvirt") + + @exception.wrap_exception + def resume(self, instance, callback): + raise exception.ApiError("resume not supported for libvirt") + + @exception.wrap_exception + def rescue(self, instance): + """Loads a VM using rescue images. + + A rescue is normally performed when something goes wrong with the + primary images and data needs to be corrected/recovered. Rescuing + should not edit or over-ride the original image, only allow for + data recovery. + + """ + self.destroy(instance, False) + + xml = self.to_xml(instance, rescue=True) + rescue_images = {'image_id': FLAGS.rescue_image_id, + 'kernel_id': FLAGS.rescue_kernel_id, + 'ramdisk_id': FLAGS.rescue_ramdisk_id} + self._create_image(instance, xml, '.rescue', rescue_images) + self._create_new_domain(xml) + + def _wait_for_rescue(): + """Called at an interval until the VM is running again.""" + instance_name = instance['name'] + + try: + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s rescued successfully.") % instance_name + LOG.info(msg) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_rescue) + return timer.start(interval=0.5, now=True) + + @exception.wrap_exception + def unrescue(self, instance): + """Reboot the VM which is being rescued back into primary images. + + Because reboot destroys and re-creates instances, unresue should + simply call reboot. + + """ + self.reboot(instance) + + @exception.wrap_exception + def poll_rescued_instances(self, timeout): + pass + + # NOTE(ilyaalekseyev): Implementation like in multinics + # for xenapi(tr3buchet) + @exception.wrap_exception + def spawn(self, instance, network_info=None): + xml = self.to_xml(instance, False, network_info) + self.firewall_driver.setup_basic_filtering(instance, network_info) + self.firewall_driver.prepare_instance_filter(instance, network_info) + self._create_image(instance, xml, network_info) + domain = self._create_new_domain(xml) + LOG.debug(_("instance %s: is running"), instance['name']) + self.firewall_driver.apply_instance_filter(instance) + + if FLAGS.start_guests_on_host_boot: + LOG.debug(_("instance %s: setting autostart ON") % + instance['name']) + domain.setAutostart(1) + + def _wait_for_boot(): + """Called at an interval until the VM is running.""" + instance_name = instance['name'] + + try: + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During reboot, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s spawned successfully.") % instance_name + LOG.info(msg) + raise utils.LoopingCallDone + + timer = utils.LoopingCall(_wait_for_boot) + return timer.start(interval=0.5, now=True) + + def _flush_xen_console(self, virsh_output): + LOG.info(_('virsh said: %r'), virsh_output) + virsh_output = virsh_output[0].strip() + + if virsh_output.startswith('/dev/'): + LOG.info(_("cool, it's a device")) + out, err = utils.execute('sudo', 'dd', + "if=%s" % virsh_output, + 'iflag=nonblock', + check_exit_code=False) + return out + else: + return '' + + def _append_to_file(self, data, fpath): + LOG.info(_('data: %(data)r, fpath: %(fpath)r') % locals()) + fp = open(fpath, 'a+') + fp.write(data) + return fpath + + def _dump_file(self, fpath): + fp = open(fpath, 'r+') + contents = fp.read() + LOG.info(_('Contents of file %(fpath)s: %(contents)r') % locals()) + return contents + + @exception.wrap_exception + def get_console_output(self, instance): + console_log = os.path.join(FLAGS.instances_path, instance['name'], + 'console.log') + + utils.execute('sudo', 'chown', os.getuid(), console_log) + + if FLAGS.libvirt_type == 'xen': + # Xen is special + virsh_output = utils.execute('virsh', 'ttyconsole', + instance['name']) + data = self._flush_xen_console(virsh_output) + fpath = self._append_to_file(data, console_log) + elif FLAGS.libvirt_type == 'lxc': + # LXC is also special + LOG.info(_("Unable to read LXC console")) + else: + fpath = console_log + + return self._dump_file(fpath) + + @exception.wrap_exception + def get_ajax_console(self, instance): + def get_open_port(): + start_port, end_port = FLAGS.ajaxterm_portrange.split("-") + for i in xrange(0, 100): # don't loop forever + port = random.randint(int(start_port), int(end_port)) + # netcat will exit with 0 only if the port is in use, + # so a nonzero return value implies it is unused + cmd = 'netcat', '0.0.0.0', port, '-w', '1' + try: + stdout, stderr = utils.execute(*cmd, process_input='') + except exception.ProcessExecutionError: + return port + raise Exception(_('Unable to find an open port')) + + def get_pty_for_instance(instance_name): + virt_dom = self._lookup_by_name(instance_name) + xml = virt_dom.XMLDesc(0) + dom = minidom.parseString(xml) + + for serial in dom.getElementsByTagName('serial'): + if serial.getAttribute('type') == 'pty': + source = serial.getElementsByTagName('source')[0] + return source.getAttribute('path') + + port = get_open_port() + token = str(uuid.uuid4()) + host = instance['host'] + + ajaxterm_cmd = 'sudo socat - %s' \ + % get_pty_for_instance(instance['name']) + + cmd = '%s/tools/ajaxterm/ajaxterm.py --command "%s" -t %s -p %s' \ + % (utils.novadir(), ajaxterm_cmd, token, port) + + subprocess.Popen(cmd, shell=True) + return {'token': token, 'host': host, 'port': port} + + @exception.wrap_exception + def get_vnc_console(self, instance): + def get_vnc_port_for_instance(instance_name): + virt_dom = self._lookup_by_name(instance_name) + xml = virt_dom.XMLDesc(0) + # TODO: use etree instead of minidom + dom = minidom.parseString(xml) + + for graphic in dom.getElementsByTagName('graphics'): + if graphic.getAttribute('type') == 'vnc': + return graphic.getAttribute('port') + + port = get_vnc_port_for_instance(instance['name']) + token = str(uuid.uuid4()) + host = instance['host'] + + return {'token': token, 'host': host, 'port': port} + + @staticmethod + def _cache_image(fn, target, fname, cow=False, *args, **kwargs): + """Wrapper for a method that creates an image that caches the image. + + This wrapper will save the image into a common store and create a + copy for use by the hypervisor. + + The underlying method should specify a kwarg of target representing + where the image will be saved. + + fname is used as the filename of the base image. The filename needs + to be unique to a given image. + + If cow is True, it will make a CoW image instead of a copy. + """ + if not os.path.exists(target): + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + base = os.path.join(base_dir, fname) + + @utils.synchronized(fname) + def call_if_not_exists(base, fn, *args, **kwargs): + if not os.path.exists(base): + fn(target=base, *args, **kwargs) + + call_if_not_exists(base, fn, *args, **kwargs) + + if cow: + utils.execute('qemu-img', 'create', '-f', 'qcow2', '-o', + 'cluster_size=2M,backing_file=%s' % base, + target) + else: + utils.execute('cp', base, target) + + def _fetch_image(self, target, image_id, user, project, size=None): + """Grab image and optionally attempt to resize it""" + images.fetch(image_id, target, user, project) + if size: + disk.extend(target, size) + + def _create_local(self, target, local_gb): + """Create a blank image of specified size""" + utils.execute('truncate', target, '-s', "%dG" % local_gb) + # TODO(vish): should we format disk by default? + + def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None, + network_info=None): + if not network_info: + network_info = netutils.get_network_info(inst) + + if not suffix: + suffix = '' + + # syntactic nicety + def basepath(fname='', suffix=suffix): + return os.path.join(FLAGS.instances_path, + inst['name'], + fname + suffix) + + # ensure directories exist and are writable + utils.execute('mkdir', '-p', basepath(suffix='')) + + LOG.info(_('instance %s: Creating image'), inst['name']) + f = open(basepath('libvirt.xml'), 'w') + f.write(libvirt_xml) + f.close() + + if FLAGS.libvirt_type == 'lxc': + container_dir = '%s/rootfs' % basepath(suffix='') + utils.execute('mkdir', '-p', container_dir) + + # NOTE(vish): No need add the suffix to console.log + os.close(os.open(basepath('console.log', ''), + os.O_CREAT | os.O_WRONLY, 0660)) + + user = manager.AuthManager().get_user(inst['user_id']) + project = manager.AuthManager().get_project(inst['project_id']) + + if not disk_images: + disk_images = {'image_id': inst['image_id'], + 'kernel_id': inst['kernel_id'], + 'ramdisk_id': inst['ramdisk_id']} + + if disk_images['kernel_id']: + fname = '%08x' % int(disk_images['kernel_id']) + self._cache_image(fn=self._fetch_image, + target=basepath('kernel'), + fname=fname, + image_id=disk_images['kernel_id'], + user=user, + project=project) + if disk_images['ramdisk_id']: + fname = '%08x' % int(disk_images['ramdisk_id']) + self._cache_image(fn=self._fetch_image, + target=basepath('ramdisk'), + fname=fname, + image_id=disk_images['ramdisk_id'], + user=user, + project=project) + + root_fname = '%08x' % int(disk_images['image_id']) + size = FLAGS.minimum_root_size + + inst_type_id = inst['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) + if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': + size = None + root_fname += "_sm" + + self._cache_image(fn=self._fetch_image, + target=basepath('disk'), + fname=root_fname, + cow=FLAGS.use_cow_images, + image_id=disk_images['image_id'], + user=user, + project=project, + size=size) + + if inst_type['local_gb']: + self._cache_image(fn=self._create_local, + target=basepath('disk.local'), + fname="local_%s" % inst_type['local_gb'], + cow=FLAGS.use_cow_images, + local_gb=inst_type['local_gb']) + + # For now, we assume that if we're not using a kernel, we're using a + # partitioned disk image where the target partition is the first + # partition + target_partition = None + if not inst['kernel_id']: + target_partition = "1" + + if FLAGS.libvirt_type == 'lxc': + target_partition = None + + if inst['key_data']: + key = str(inst['key_data']) + else: + key = None + net = None + + nets = [] + ifc_template = open(FLAGS.injected_network_template).read() + ifc_num = -1 + have_injected_networks = False + admin_context = context.get_admin_context() + for (network_ref, mapping) in network_info: + ifc_num += 1 + + if not network_ref['injected']: + continue + + have_injected_networks = True + address = mapping['ips'][0]['ip'] + address_v6 = None + if FLAGS.use_ipv6: + address_v6 = mapping['ip6s'][0]['ip'] + net_info = {'name': 'eth%d' % ifc_num, + 'address': address, + 'netmask': network_ref['netmask'], + 'gateway': network_ref['gateway'], + 'broadcast': network_ref['broadcast'], + 'dns': network_ref['dns'], + 'address_v6': address_v6, + 'gateway_v6': network_ref['gateway_v6'], + 'netmask_v6': network_ref['netmask_v6']} + nets.append(net_info) + + if have_injected_networks: + net = str(Template(ifc_template, + searchList=[{'interfaces': nets, + 'use_ipv6': FLAGS.use_ipv6}])) + + if key or net: + inst_name = inst['name'] + img_id = inst.image_id + if key: + LOG.info(_('instance %(inst_name)s: injecting key into' + ' image %(img_id)s') % locals()) + if net: + LOG.info(_('instance %(inst_name)s: injecting net into' + ' image %(img_id)s') % locals()) + try: + disk.inject_data(basepath('disk'), key, net, + partition=target_partition, + nbd=FLAGS.use_cow_images) + + if FLAGS.libvirt_type == 'lxc': + disk.setup_container(basepath('disk'), + container_dir=container_dir, + nbd=FLAGS.use_cow_images) + except Exception as e: + # This could be a windows image, or a vmdk format disk + LOG.warn(_('instance %(inst_name)s: ignoring error injecting' + ' data into image %(img_id)s (%(e)s)') % locals()) + + if FLAGS.libvirt_type == 'uml': + utils.execute('sudo', 'chown', 'root', basepath('disk')) + + def _get_nic_for_xml(self, network, mapping): + # Assume that the gateway also acts as the dhcp server. + dhcp_server = network['gateway'] + gateway_v6 = network['gateway_v6'] + mac_id = mapping['mac'].replace(':', '') + + if FLAGS.allow_project_net_traffic: + if FLAGS.use_ipv6: + net, mask = netutils.get_net_and_mask(network['cidr']) + net_v6, prefixlen_v6 = netutils.get_net_and_prefixlen( + network['cidr_v6']) + extra_params = ("\n" + "\n" + "\n" + "\n") % \ + (net, mask, net_v6, prefixlen_v6) + else: + net, mask = netutils.get_net_and_mask(network['cidr']) + extra_params = ("\n" + "\n") % \ + (net, mask) + else: + extra_params = "\n" + + result = { + 'id': mac_id, + 'bridge_name': network['bridge'], + 'mac_address': mapping['mac'], + 'ip_address': mapping['ips'][0]['ip'], + 'dhcp_server': dhcp_server, + 'extra_params': extra_params, + } + + if gateway_v6: + result['gateway_v6'] = gateway_v6 + "/128" + + return result + + def to_xml(self, instance, rescue=False, network_info=None): + # TODO(termie): cache? + LOG.debug(_('instance %s: starting toXML method'), instance['name']) + + # TODO(adiantum) remove network_info creation code + # when multinics will be completed + if not network_info: + network_info = netutils.get_network_info(instance) + + nics = [] + for (network, mapping) in network_info: + nics.append(self._get_nic_for_xml(network, + mapping)) + # FIXME(vish): stick this in db + inst_type_id = instance['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) + + if FLAGS.use_cow_images: + driver_type = 'qcow2' + else: + driver_type = 'raw' + + xml_info = {'type': FLAGS.libvirt_type, + 'name': instance['name'], + 'basepath': os.path.join(FLAGS.instances_path, + instance['name']), + 'memory_kb': inst_type['memory_mb'] * 1024, + 'vcpus': inst_type['vcpus'], + 'rescue': rescue, + 'local': inst_type['local_gb'], + 'driver_type': driver_type, + 'nics': nics} + + if FLAGS.vnc_enabled: + if FLAGS.libvirt_type != 'lxc': + xml_info['vncserver_host'] = FLAGS.vncserver_host + if not rescue: + if instance['kernel_id']: + xml_info['kernel'] = xml_info['basepath'] + "/kernel" + + if instance['ramdisk_id']: + xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk" + + xml_info['disk'] = xml_info['basepath'] + "/disk" + + xml = str(Template(self.libvirt_xml, searchList=[xml_info])) + LOG.debug(_('instance %s: finished toXML method'), + instance['name']) + return xml + + def _lookup_by_name(self, instance_name): + """Retrieve libvirt domain object given an instance name. + + All libvirt error handling should be handled in this method and + relevant nova exceptions should be raised in response. + + """ + try: + return self._conn.lookupByName(instance_name) + except libvirt.libvirtError as ex: + error_code = ex.get_error_code() + if error_code == libvirt.VIR_ERR_NO_DOMAIN: + msg = _("Instance %s not found") % instance_name + raise exception.NotFound(msg) + + msg = _("Error from libvirt while looking up %(instance_name)s: " + "[Error Code %(error_code)s] %(ex)s") % locals() + raise exception.Error(msg) + + def get_info(self, instance_name): + """Retrieve information from libvirt for a specific instance name. + + If a libvirt error is encountered during lookup, we might raise a + NotFound exception or Error exception depending on how severe the + libvirt error is. + + """ + virt_dom = self._lookup_by_name(instance_name) + (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() + return {'state': state, + 'max_mem': max_mem, + 'mem': mem, + 'num_cpu': num_cpu, + 'cpu_time': cpu_time} + + def _create_new_domain(self, xml, persistent=True, launch_flags=0): + # NOTE(justinsb): libvirt has two types of domain: + # * a transient domain disappears when the guest is shutdown + # or the host is rebooted. + # * a permanent domain is not automatically deleted + # NOTE(justinsb): Even for ephemeral instances, transient seems risky + + if persistent: + # To create a persistent domain, first define it, then launch it. + domain = self._conn.defineXML(xml) + + domain.createWithFlags(launch_flags) + else: + # createXML call creates a transient domain + domain = self._conn.createXML(xml, launch_flags) + + return domain + + def get_diagnostics(self, instance_name): + raise exception.ApiError(_("diagnostics are not supported " + "for libvirt")) + + def get_disks(self, instance_name): + """ + Note that this function takes an instance name, not an Instance, so + that it can be called by monitor. + + Returns a list of all block devices for this domain. + """ + domain = self._lookup_by_name(instance_name) + # TODO(devcamcar): Replace libxml2 with etree. + xml = domain.XMLDesc(0) + doc = None + + try: + doc = libxml2.parseDoc(xml) + except: + return [] + + ctx = doc.xpathNewContext() + disks = [] + + try: + ret = ctx.xpathEval('/domain/devices/disk') + + for node in ret: + devdst = None + + for child in node.children: + if child.name == 'target': + devdst = child.prop('dev') + + if devdst is None: + continue + + disks.append(devdst) + finally: + if ctx is not None: + ctx.xpathFreeContext() + if doc is not None: + doc.freeDoc() + + return disks + + def get_interfaces(self, instance_name): + """ + Note that this function takes an instance name, not an Instance, so + that it can be called by monitor. + + Returns a list of all network interfaces for this instance. + """ + domain = self._lookup_by_name(instance_name) + # TODO(devcamcar): Replace libxml2 with etree. + xml = domain.XMLDesc(0) + doc = None + + try: + doc = libxml2.parseDoc(xml) + except: + return [] + + ctx = doc.xpathNewContext() + interfaces = [] + + try: + ret = ctx.xpathEval('/domain/devices/interface') + + for node in ret: + devdst = None + + for child in node.children: + if child.name == 'target': + devdst = child.prop('dev') + + if devdst is None: + continue + + interfaces.append(devdst) + finally: + if ctx is not None: + ctx.xpathFreeContext() + if doc is not None: + doc.freeDoc() + + return interfaces + + def get_vcpu_total(self): + """Get vcpu number of physical computer. + + :returns: the number of cpu core. + + """ + + # On certain platforms, this will raise a NotImplementedError. + try: + return multiprocessing.cpu_count() + except NotImplementedError: + LOG.warn(_("Cannot get the number of cpu, because this " + "function is not implemented for this platform. " + "This error can be safely ignored for now.")) + return 0 + + def get_memory_mb_total(self): + """Get the total memory size(MB) of physical computer. + + :returns: the total amount of memory(MB). + + """ + + if sys.platform.upper() != 'LINUX2': + return 0 + + meminfo = open('/proc/meminfo').read().split() + idx = meminfo.index('MemTotal:') + # transforming kb to mb. + return int(meminfo[idx + 1]) / 1024 + + def get_local_gb_total(self): + """Get the total hdd size(GB) of physical computer. + + :returns: + The total amount of HDD(GB). + Note that this value shows a partition where + NOVA-INST-DIR/instances mounts. + + """ + + hddinfo = os.statvfs(FLAGS.instances_path) + return hddinfo.f_frsize * hddinfo.f_blocks / 1024 / 1024 / 1024 + + def get_vcpu_used(self): + """ Get vcpu usage number of physical computer. + + :returns: The total number of vcpu that currently used. + + """ + + total = 0 + for dom_id in self._conn.listDomainsID(): + dom = self._conn.lookupByID(dom_id) + total += len(dom.vcpus()[1]) + return total + + def get_memory_mb_used(self): + """Get the free memory size(MB) of physical computer. + + :returns: the total usage of memory(MB). + + """ + + if sys.platform.upper() != 'LINUX2': + return 0 + + m = open('/proc/meminfo').read().split() + idx1 = m.index('MemFree:') + idx2 = m.index('Buffers:') + idx3 = m.index('Cached:') + avail = (int(m[idx1 + 1]) + int(m[idx2 + 1]) + int(m[idx3 + 1])) / 1024 + return self.get_memory_mb_total() - avail + + def get_local_gb_used(self): + """Get the free hdd size(GB) of physical computer. + + :returns: + The total usage of HDD(GB). + Note that this value shows a partition where + NOVA-INST-DIR/instances mounts. + + """ + + hddinfo = os.statvfs(FLAGS.instances_path) + avail = hddinfo.f_frsize * hddinfo.f_bavail / 1024 / 1024 / 1024 + return self.get_local_gb_total() - avail + + def get_hypervisor_type(self): + """Get hypervisor type. + + :returns: hypervisor type (ex. qemu) + + """ + + return self._conn.getType() + + def get_hypervisor_version(self): + """Get hypervisor version. + + :returns: hypervisor version (ex. 12003) + + """ + + # NOTE(justinsb): getVersion moved between libvirt versions + # Trying to do be compatible with older versions is a lost cause + # But ... we can at least give the user a nice message + method = getattr(self._conn, 'getVersion', None) + if method is None: + raise exception.Error(_("libvirt version is too old" + " (does not support getVersion)")) + # NOTE(justinsb): If we wanted to get the version, we could: + # method = getattr(libvirt, 'getVersion', None) + # NOTE(justinsb): This would then rely on a proper version check + + return method() + + def get_cpu_info(self): + """Get cpuinfo information. + + Obtains cpu feature from virConnect.getCapabilities, + and returns as a json string. + + :return: see above description + + """ + + xml = self._conn.getCapabilities() + xml = libxml2.parseDoc(xml) + nodes = xml.xpathEval('//host/cpu') + if len(nodes) != 1: + raise exception.Invalid(_("Invalid xml. '' must be 1," + "but %d\n") % len(nodes) + + xml.serialize()) + + cpu_info = dict() + + arch_nodes = xml.xpathEval('//host/cpu/arch') + if arch_nodes: + cpu_info['arch'] = arch_nodes[0].getContent() + + model_nodes = xml.xpathEval('//host/cpu/model') + if model_nodes: + cpu_info['model'] = model_nodes[0].getContent() + + vendor_nodes = xml.xpathEval('//host/cpu/vendor') + if vendor_nodes: + cpu_info['vendor'] = vendor_nodes[0].getContent() + + topology_nodes = xml.xpathEval('//host/cpu/topology') + topology = dict() + if topology_nodes: + topology_node = topology_nodes[0].get_properties() + while topology_node: + name = topology_node.get_name() + topology[name] = topology_node.getContent() + topology_node = topology_node.get_next() + + keys = ['cores', 'sockets', 'threads'] + tkeys = topology.keys() + if set(tkeys) != set(keys): + ks = ', '.join(keys) + raise exception.Invalid(_("Invalid xml: topology" + "(%(topology)s) must have " + "%(ks)s") % locals()) + + feature_nodes = xml.xpathEval('//host/cpu/feature') + features = list() + for nodes in feature_nodes: + features.append(nodes.get_properties().getContent()) + + cpu_info['topology'] = topology + cpu_info['features'] = features + return utils.dumps(cpu_info) + + def block_stats(self, instance_name, disk): + """ + Note that this function takes an instance name, not an Instance, so + that it can be called by monitor. + """ + domain = self._lookup_by_name(instance_name) + return domain.blockStats(disk) + + def interface_stats(self, instance_name, interface): + """ + Note that this function takes an instance name, not an Instance, so + that it can be called by monitor. + """ + domain = self._lookup_by_name(instance_name) + return domain.interfaceStats(interface) + + def get_console_pool_info(self, console_type): + #TODO(mdragon): console proxy should be implemented for libvirt, + # in case someone wants to use it with kvm or + # such. For now return fake data. + return {'address': '127.0.0.1', + 'username': 'fakeuser', + 'password': 'fakepassword'} + + def refresh_security_group_rules(self, security_group_id): + self.firewall_driver.refresh_security_group_rules(security_group_id) + + def refresh_security_group_members(self, security_group_id): + self.firewall_driver.refresh_security_group_members(security_group_id) + + def update_available_resource(self, ctxt, host): + """Updates compute manager resource info on ComputeNode table. + + This method is called when nova-coompute launches, and + whenever admin executes "nova-manage service update_resource". + + :param ctxt: security context + :param host: hostname that compute manager is currently running + + """ + + try: + service_ref = db.service_get_all_compute_by_host(ctxt, host)[0] + except exception.NotFound: + raise exception.Invalid(_("Cannot update compute manager " + "specific info, because no service " + "record was found.")) + + # Updating host information + dic = {'vcpus': self.get_vcpu_total(), + 'memory_mb': self.get_memory_mb_total(), + 'local_gb': self.get_local_gb_total(), + 'vcpus_used': self.get_vcpu_used(), + 'memory_mb_used': self.get_memory_mb_used(), + 'local_gb_used': self.get_local_gb_used(), + 'hypervisor_type': self.get_hypervisor_type(), + 'hypervisor_version': self.get_hypervisor_version(), + 'cpu_info': self.get_cpu_info()} + + compute_node_ref = service_ref['compute_node'] + if not compute_node_ref: + LOG.info(_('Compute_service record created for %s ') % host) + dic['service_id'] = service_ref['id'] + db.compute_node_create(ctxt, dic) + else: + LOG.info(_('Compute_service record updated for %s ') % host) + db.compute_node_update(ctxt, compute_node_ref[0]['id'], dic) + + def compare_cpu(self, cpu_info): + """Checks the host cpu is compatible to a cpu given by xml. + + "xml" must be a part of libvirt.openReadonly().getCapabilities(). + return values follows by virCPUCompareResult. + if 0 > return value, do live migration. + 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' + + :param cpu_info: json string that shows cpu feature(see get_cpu_info()) + :returns: + None. if given cpu info is not compatible to this server, + raise exception. + + """ + + LOG.info(_('Instance launched has CPU info:\n%s') % cpu_info) + dic = utils.loads(cpu_info) + xml = str(Template(self.cpuinfo_xml, searchList=dic)) + LOG.info(_('to xml...\n:%s ' % xml)) + + u = "http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult" + m = _("CPU doesn't have compatibility.\n\n%(ret)s\n\nRefer to %(u)s") + # unknown character exists in xml, then libvirt complains + try: + ret = self._conn.compareCPU(xml, 0) + except libvirt.libvirtError, e: + ret = e.message + LOG.error(m % locals()) + raise + + if ret <= 0: + raise exception.Invalid(m % locals()) + + return + + def ensure_filtering_rules_for_instance(self, instance_ref, + time=None): + """Setting up filtering rules and waiting for its completion. + + To migrate an instance, filtering rules to hypervisors + and firewalls are inevitable on destination host. + ( Waiting only for filterling rules to hypervisor, + since filtering rules to firewall rules can be set faster). + + Concretely, the below method must be called. + - setup_basic_filtering (for nova-basic, etc.) + - prepare_instance_filter(for nova-instance-instance-xxx, etc.) + + to_xml may have to be called since it defines PROJNET, PROJMASK. + but libvirt migrates those value through migrateToURI(), + so , no need to be called. + + Don't use thread for this method since migration should + not be started when setting-up filtering rules operations + are not completed. + + :params instance_ref: nova.db.sqlalchemy.models.Instance object + + """ + + if not time: + time = greenthread + + # If any instances never launch at destination host, + # basic-filtering must be set here. + self.firewall_driver.setup_basic_filtering(instance_ref) + # setting up n)ova-instance-instance-xx mainly. + self.firewall_driver.prepare_instance_filter(instance_ref) + + # wait for completion + timeout_count = range(FLAGS.live_migration_retry_count) + while timeout_count: + if self.firewall_driver.instance_filter_exists(instance_ref): + break + timeout_count.pop() + if len(timeout_count) == 0: + msg = _('Timeout migrating for %s. nwfilter not found.') + raise exception.Error(msg % instance_ref.name) + time.sleep(1) + + def live_migration(self, ctxt, instance_ref, dest, + post_method, recover_method): + """Spawning live_migration operation for distributing high-load. + + :params ctxt: security context + :params instance_ref: + nova.db.sqlalchemy.models.Instance object + instance object that is migrated. + :params dest: destination host + :params post_method: + post operation method. + expected nova.compute.manager.post_live_migration. + :params recover_method: + recovery method when any exception occurs. + expected nova.compute.manager.recover_live_migration. + + """ + + greenthread.spawn(self._live_migration, ctxt, instance_ref, dest, + post_method, recover_method) + + def _live_migration(self, ctxt, instance_ref, dest, + post_method, recover_method): + """Do live migration. + + :params ctxt: security context + :params instance_ref: + nova.db.sqlalchemy.models.Instance object + instance object that is migrated. + :params dest: destination host + :params post_method: + post operation method. + expected nova.compute.manager.post_live_migration. + :params recover_method: + recovery method when any exception occurs. + expected nova.compute.manager.recover_live_migration. + + """ + + # Do live migration. + try: + flaglist = FLAGS.live_migration_flag.split(',') + flagvals = [getattr(libvirt, x.strip()) for x in flaglist] + logical_sum = reduce(lambda x, y: x | y, flagvals) + + if self.read_only: + tmpconn = self._connect(self.libvirt_uri, False) + dom = tmpconn.lookupByName(instance_ref.name) + dom.migrateToURI(FLAGS.live_migration_uri % dest, + logical_sum, + None, + FLAGS.live_migration_bandwidth) + tmpconn.close() + else: + dom = self._conn.lookupByName(instance_ref.name) + dom.migrateToURI(FLAGS.live_migration_uri % dest, + logical_sum, + None, + FLAGS.live_migration_bandwidth) + + except Exception: + recover_method(ctxt, instance_ref, dest=dest) + raise + + # Waiting for completion of live_migration. + timer = utils.LoopingCall(f=None) + + def wait_for_live_migration(): + """waiting for live migration completion""" + try: + self.get_info(instance_ref.name)['state'] + except exception.NotFound: + timer.stop() + post_method(ctxt, instance_ref, dest) + + timer.f = wait_for_live_migration + timer.start(interval=0.5, now=True) + + def unfilter_instance(self, instance_ref): + """See comments of same method in firewall_driver.""" + self.firewall_driver.unfilter_instance(instance_ref) diff --git a/nova/virt/libvirt/cpuinfo.xml.template b/nova/virt/libvirt/cpuinfo.xml.template new file mode 100644 index 000000000..48842b29d --- /dev/null +++ b/nova/virt/libvirt/cpuinfo.xml.template @@ -0,0 +1,9 @@ + + $arch + $model + $vendor + +#for $var in $features + +#end for + diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py new file mode 100644 index 000000000..99ba02aaa --- /dev/null +++ b/nova/virt/libvirt/firewall.py @@ -0,0 +1,630 @@ +# 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. +# Copyright (c) 2010 Citrix Systems, Inc. +# +# 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. + + +from eventlet import tpool + +from nova import context +from nova import db +from nova import flags +from nova import log as logging +from nova import utils +from nova.virt.libvirt import netutils + + +LOG = logging.getLogger("nova.virt.libvirt.firewall") +FLAGS = flags.FLAGS + + +try: + import libvirt +except ImportError: + LOG.warn(_("Libvirt module could not be loaded. NWFilterFirewall will " + "not work correctly.")) + + +class FirewallDriver(object): + + def prepare_instance_filter(self, instance, network_info=None): + """Prepare filters for the instance. + + At this point, the instance isn't running yet. + + """ + raise NotImplementedError() + + def unfilter_instance(self, instance): + """Stop filtering instance.""" + raise NotImplementedError() + + def apply_instance_filter(self, instance): + """Apply instance filter. + + Once this method returns, the instance should be firewalled + appropriately. This method should as far as possible be a + no-op. It's vastly preferred to get everything set up in + prepare_instance_filter. + + """ + raise NotImplementedError() + + def refresh_security_group_rules(self, security_group_id): + """Refresh security group rules from data store. + + Gets called when a rule has been added to or removed from + the security group. + + """ + raise NotImplementedError() + + def refresh_security_group_members(self, security_group_id): + """Refresh security group members from data store. + + Gets called when an instance gets added to or removed from + the security group. + + """ + raise NotImplementedError() + + def setup_basic_filtering(self, instance, network_info=None): + """Create rules to block spoofing and allow dhcp. + + This gets called when spawning an instance, before + :method:`prepare_instance_filter`. + + """ + raise NotImplementedError() + + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists.""" + raise NotImplementedError() + + +class NWFilterFirewall(FirewallDriver): + """Network filter firewall implementation. + + This class implements a network filtering mechanism versatile + enough for EC2 style Security Group filtering by leveraging + libvirt's nwfilter. + + First, all instances get a filter ("nova-base-filter") applied. + This filter provides some basic security such as protection against + MAC spoofing, IP spoofing, and ARP spoofing. + + This filter drops all incoming ipv4 and ipv6 connections. + Outgoing connections are never blocked. + + Second, every security group maps to a nwfilter filter(*). + NWFilters can be updated at runtime and changes are applied + immediately, so changes to security groups can be applied at + runtime (as mandated by the spec). + + Security group rules are named "nova-secgroup-" where + is the internal id of the security group. They're applied only on + hosts that have instances in the security group in question. + + Updates to security groups are done by updating the data model + (in response to API calls) followed by a request sent to all + the nodes with instances in the security group to refresh the + security group. + + Each instance has its own NWFilter, which references the above + mentioned security group NWFilters. This was done because + interfaces can only reference one filter while filters can + reference multiple other filters. This has the added benefit of + actually being able to add and remove security groups from an + instance at run time. This functionality is not exposed anywhere, + though. + + Outstanding questions: + + The name is unique, so would there be any good reason to sync + the uuid across the nodes (by assigning it from the datamodel)? + + + (*) This sentence brought to you by the redundancy department of + redundancy. + + """ + + def __init__(self, get_connection, **kwargs): + self._libvirt_get_connection = get_connection + self.static_filters_configured = False + self.handle_security_groups = False + + def apply_instance_filter(self, instance): + pass + + def _get_connection(self): + return self._libvirt_get_connection() + + _conn = property(_get_connection) + + def nova_dhcp_filter(self): + """Defines nova DHCP filter. + + The standard allow-dhcp-server filter is an one, so it uses + ebtables to allow traffic through. Without a corresponding rule in + iptables, it'll get blocked anyway. + + """ + return ''' + 891e4787-e5c0-d59b-cbd6-41bc3c6b36fc + + + + + + + ''' + + def nova_ra_filter(self): + return ''' + d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 + + + + ''' + + def setup_basic_filtering(self, instance, network_info=None): + """Set up basic filtering (MAC, IP, and ARP spoofing protection).""" + logging.info('called setup_basic_filtering in nwfilter') + + if not network_info: + network_info = netutils.get_network_info(instance) + + if self.handle_security_groups: + # No point in setting up a filter set that we'll be overriding + # anyway. + return + + logging.info('ensuring static filters') + self._ensure_static_filters() + + if instance['image_id'] == str(FLAGS.vpn_image_id): + base_filter = 'nova-vpn' + else: + base_filter = 'nova-base' + + for (network, mapping) in network_info: + nic_id = mapping['mac'].replace(':', '') + instance_filter_name = self._instance_filter_name(instance, nic_id) + self._define_filter(self._filter_container(instance_filter_name, + [base_filter])) + + def _ensure_static_filters(self): + if self.static_filters_configured: + return + + self._define_filter(self._filter_container('nova-base', + ['no-mac-spoofing', + 'no-ip-spoofing', + 'no-arp-spoofing', + 'allow-dhcp-server'])) + self._define_filter(self._filter_container('nova-vpn', + ['allow-dhcp-server'])) + self._define_filter(self.nova_base_ipv4_filter) + self._define_filter(self.nova_base_ipv6_filter) + self._define_filter(self.nova_dhcp_filter) + self._define_filter(self.nova_ra_filter) + if FLAGS.allow_project_net_traffic: + self._define_filter(self.nova_project_filter) + if FLAGS.use_ipv6: + self._define_filter(self.nova_project_filter_v6) + + self.static_filters_configured = True + + def _filter_container(self, name, filters): + xml = '''%s''' % ( + name, + ''.join(["" % (f,) for f in filters])) + return xml + + def nova_base_ipv4_filter(self): + retval = "" + for protocol in ['tcp', 'udp', 'icmp']: + for direction, action, priority in [('out', 'accept', 399), + ('in', 'drop', 400)]: + retval += """ + <%s /> + """ % (action, direction, + priority, protocol) + retval += '' + return retval + + def nova_base_ipv6_filter(self): + retval = "" + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: + for direction, action, priority in [('out', 'accept', 399), + ('in', 'drop', 400)]: + retval += """ + <%s /> + """ % (action, direction, + priority, protocol) + retval += '' + return retval + + def nova_project_filter(self): + retval = "" + for protocol in ['tcp', 'udp', 'icmp']: + retval += """ + <%s srcipaddr='$PROJNET' srcipmask='$PROJMASK' /> + """ % protocol + retval += '' + return retval + + def nova_project_filter_v6(self): + retval = "" + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: + retval += """ + <%s srcipaddr='$PROJNETV6' + srcipmask='$PROJMASKV6' /> + """ % (protocol) + retval += '' + return retval + + def _define_filter(self, xml): + if callable(xml): + xml = xml() + # execute in a native thread and block current greenthread until done + tpool.execute(self._conn.nwfilterDefineXML, xml) + + def unfilter_instance(self, instance): + # Nothing to do + pass + + def prepare_instance_filter(self, instance, network_info=None): + """Creates an NWFilter for the given instance. + + In the process, it makes sure the filters for the security groups as + well as the base filter are all in place. + + """ + if not network_info: + network_info = netutils.get_network_info(instance) + if instance['image_id'] == str(FLAGS.vpn_image_id): + base_filter = 'nova-vpn' + else: + base_filter = 'nova-base' + + ctxt = context.get_admin_context() + + instance_secgroup_filter_name = \ + '%s-secgroup' % (self._instance_filter_name(instance)) + #% (instance_filter_name,) + + instance_secgroup_filter_children = ['nova-base-ipv4', + 'nova-base-ipv6', + 'nova-allow-dhcp-server'] + + for security_group in \ + db.security_group_get_by_instance(ctxt, instance['id']): + + self.refresh_security_group_rules(security_group['id']) + + instance_secgroup_filter_children += [('nova-secgroup-%s' % + security_group['id'])] + + self._define_filter( + self._filter_container(instance_secgroup_filter_name, + instance_secgroup_filter_children)) + + for (network, mapping) in network_info: + nic_id = mapping['mac'].replace(':', '') + instance_filter_name = self._instance_filter_name(instance, nic_id) + instance_filter_children = \ + [base_filter, instance_secgroup_filter_name] + + if FLAGS.use_ipv6: + gateway_v6 = network['gateway_v6'] + + if gateway_v6: + instance_secgroup_filter_children += \ + ['nova-allow-ra-server'] + + if FLAGS.allow_project_net_traffic: + instance_filter_children += ['nova-project'] + if FLAGS.use_ipv6: + instance_filter_children += ['nova-project-v6'] + + self._define_filter( + self._filter_container(instance_filter_name, + instance_filter_children)) + + return + + def refresh_security_group_rules(self, security_group_id): + return self._define_filter( + self.security_group_to_nwfilter_xml(security_group_id)) + + def security_group_to_nwfilter_xml(self, security_group_id): + security_group = db.security_group_get(context.get_admin_context(), + security_group_id) + rule_xml = "" + v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} + for rule in security_group.rules: + rule_xml += "" + if rule.cidr: + version = netutils.get_ip_version(rule.cidr) + if(FLAGS.use_ipv6 and version == 6): + net, prefixlen = netutils.get_net_and_prefixlen(rule.cidr) + rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ + (v6protocol[rule.protocol], net, prefixlen) + else: + net, mask = netutils.get_net_and_mask(rule.cidr) + rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ + (rule.protocol, net, mask) + if rule.protocol in ['tcp', 'udp']: + rule_xml += "dstportstart='%s' dstportend='%s' " % \ + (rule.from_port, rule.to_port) + elif rule.protocol == 'icmp': + LOG.info('rule.protocol: %r, rule.from_port: %r, ' + 'rule.to_port: %r', rule.protocol, + rule.from_port, rule.to_port) + if rule.from_port != -1: + rule_xml += "type='%s' " % rule.from_port + if rule.to_port != -1: + rule_xml += "code='%s' " % rule.to_port + + rule_xml += '/>\n' + rule_xml += "\n" + xml = " + ${name} + ${memory_kb} + +#if $type == 'lxc' + #set $disk_prefix = '' + #set $disk_bus = '' + exe + /sbin/init +#else if $type == 'uml' + #set $disk_prefix = 'ubd' + #set $disk_bus = 'uml' + uml + /usr/bin/linux + /dev/ubda +#else + #if $type == 'xen' + #set $disk_prefix = 'sd' + #set $disk_bus = 'scsi' + linux + /dev/xvda + #else + #set $disk_prefix = 'vd' + #set $disk_bus = 'virtio' + hvm + #end if + #if $getVar('rescue', False) + ${basepath}/kernel.rescue + ${basepath}/ramdisk.rescue + #else + #if $getVar('kernel', None) + ${kernel} + #if $type == 'xen' + ro + #else + root=/dev/vda console=ttyS0 + #end if + #if $getVar('ramdisk', None) + ${ramdisk} + #end if + #else + + #end if + #end if +#end if + + + + + ${vcpus} + +#if $type == 'lxc' + + + + +#else + #if $getVar('rescue', False) + + + + + + + + + + + #else + + + + + + #if $getVar('local', False) + + + + + + #end if + #end if +#end if + +#for $nic in $nics + + + + + + + +#if $getVar('nic.extra_params', False) + ${nic.extra_params} +#end if +#if $getVar('nic.gateway_v6', False) + +#end if + + +#end for + + + + + + + + + + + + + + + + +#if $getVar('vncserver_host', False) + +#end if + + diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py new file mode 100644 index 000000000..3ed9a0fdc --- /dev/null +++ b/nova/virt/libvirt/netutils.py @@ -0,0 +1,95 @@ +# 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. +# Copyright (c) 2010 Citrix Systems, Inc. +# +# 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. + + +"""Network-releated utilities for supporting libvirt connection code.""" + + +import IPy + +from nova import context +from nova import db +from nova import flags +from nova import utils + + +FLAGS = flags.FLAGS + + +def get_net_and_mask(cidr): + net = IPy.IP(cidr) + return str(net.net()), str(net.netmask()) + + +def get_net_and_prefixlen(cidr): + net = IPy.IP(cidr) + return str(net.net()), str(net.prefixlen()) + + +def get_ip_version(cidr): + net = IPy.IP(cidr) + return int(net.version()) + + +def get_network_info(instance): + # TODO(adiantum) If we will keep this function + # we should cache network_info + admin_context = context.get_admin_context() + + ip_addresses = 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_id(admin_context, + instance['instance_type_id']) + network_info = [] + + for network in networks: + network_ips = [ip for ip in ip_addresses + if ip['network_id'] == network['id']] + + def ip_dict(ip): + return { + 'ip': ip['address'], + 'netmask': network['netmask'], + 'enabled': '1'} + + def ip6_dict(): + prefix = network['cidr_v6'] + mac = instance['mac_address'] + return { + 'ip': utils.to_global_ipv6(prefix, mac), + 'netmask': network['netmask_v6'], + 'enabled': '1'} + + mapping = { + '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]} + + if FLAGS.use_ipv6: + mapping['ip6s'] = [ip6_dict()] + mapping['gateway6'] = network['gateway_v6'] + + network_info.append((network, mapping)) + return network_info diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py deleted file mode 100644 index e76de47db..000000000 --- a/nova/virt/libvirt_conn.py +++ /dev/null @@ -1,2168 +0,0 @@ -# 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. -# Copyright (c) 2010 Citrix Systems, Inc. -# -# 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. - -""" -A connection to a hypervisor through libvirt. - -Supports KVM, LXC, QEMU, UML, and XEN. - -**Related Flags** - -:libvirt_type: Libvirt domain type. Can be kvm, qemu, uml, xen - (default: kvm). -:libvirt_uri: Override for the default libvirt URI (depends on libvirt_type). -:libvirt_xml_template: Libvirt XML Template. -:rescue_image_id: Rescue ami image (default: ami-rescue). -:rescue_kernel_id: Rescue aki image (default: aki-rescue). -:rescue_ramdisk_id: Rescue ari image (default: ari-rescue). -:injected_network_template: Template file for injected network -:allow_project_net_traffic: Whether to allow in project network traffic - -""" - -import multiprocessing -import os -import random -import shutil -import subprocess -import sys -import tempfile -import time -import uuid -from xml.dom import minidom -from xml.etree import ElementTree - -from eventlet import greenthread -from eventlet import tpool - -import IPy - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova import log as logging -from nova import utils -from nova import vnc -from nova.auth import manager -from nova.compute import instance_types -from nova.compute import power_state -from nova.virt import disk -from nova.virt import driver -from nova.virt import images - -libvirt = None -libxml2 = None -Template = None - -LOG = logging.getLogger('nova.virt.libvirt_conn') - -FLAGS = flags.FLAGS -flags.DECLARE('live_migration_retry_count', 'nova.compute.manager') -# TODO(vish): These flags should probably go into a shared location -flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') -flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') -flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') - -flags.DEFINE_string('libvirt_xml_template', - utils.abspath('virt/libvirt.xml.template'), - 'Libvirt XML Template') -flags.DEFINE_string('libvirt_type', - 'kvm', - 'Libvirt domain type (valid options are: ' - 'kvm, lxc, qemu, uml, xen)') -flags.DEFINE_string('libvirt_uri', - '', - 'Override the default libvirt URI (which is dependent' - ' on libvirt_type)') -flags.DEFINE_bool('allow_project_net_traffic', - True, - 'Whether to allow in project network traffic') -flags.DEFINE_bool('use_cow_images', - True, - 'Whether to use cow images') -flags.DEFINE_string('ajaxterm_portrange', - '10000-12000', - 'Range of ports that ajaxterm should randomly try to bind') -flags.DEFINE_string('firewall_driver', - 'nova.virt.libvirt_conn.IptablesFirewallDriver', - 'Firewall driver (defaults to iptables)') -flags.DEFINE_string('cpuinfo_xml_template', - utils.abspath('virt/cpuinfo.xml.template'), - 'CpuInfo XML Template (Used only live migration now)') -flags.DEFINE_string('live_migration_uri', - "qemu+tcp://%s/system", - 'Define protocol used by live_migration feature') -flags.DEFINE_string('live_migration_flag', - "VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER", - 'Define live migration behavior.') -flags.DEFINE_integer('live_migration_bandwidth', 0, - 'Define live migration behavior') -flags.DEFINE_string('qemu_img', 'qemu-img', - 'binary to use for qemu-img commands') -flags.DEFINE_bool('start_guests_on_host_boot', False, - 'Whether to restart guests when the host reboots') - - -def get_connection(read_only): - # These are loaded late so that there's no need to install these - # libraries when not using libvirt. - # Cheetah is separate because the unit tests want to load Cheetah, - # but not libvirt. - global libvirt - global libxml2 - if libvirt is None: - libvirt = __import__('libvirt') - if libxml2 is None: - libxml2 = __import__('libxml2') - _late_load_cheetah() - return LibvirtConnection(read_only) - - -def _late_load_cheetah(): - global Template - if Template is None: - t = __import__('Cheetah.Template', globals(), locals(), - ['Template'], -1) - Template = t.Template - - -def _get_net_and_mask(cidr): - net = IPy.IP(cidr) - return str(net.net()), str(net.netmask()) - - -def _get_net_and_prefixlen(cidr): - net = IPy.IP(cidr) - return str(net.net()), str(net.prefixlen()) - - -def _get_ip_version(cidr): - net = IPy.IP(cidr) - return int(net.version()) - - -def _get_network_info(instance): - # TODO(adiantum) If we will keep this function - # we should cache network_info - admin_context = context.get_admin_context() - - ip_addresses = 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_id(admin_context, - instance['instance_type_id']) - network_info = [] - - for network in networks: - network_ips = [ip for ip in ip_addresses - if ip['network_id'] == network['id']] - - def ip_dict(ip): - return { - 'ip': ip['address'], - 'netmask': network['netmask'], - 'enabled': '1'} - - def ip6_dict(): - prefix = network['cidr_v6'] - mac = instance['mac_address'] - return { - 'ip': utils.to_global_ipv6(prefix, mac), - 'netmask': network['netmask_v6'], - 'enabled': '1'} - - mapping = { - '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]} - - if FLAGS.use_ipv6: - mapping['ip6s'] = [ip6_dict()] - mapping['gateway6'] = network['gateway_v6'] - - network_info.append((network, mapping)) - return network_info - - -class LibvirtConnection(driver.ComputeDriver): - - def __init__(self, read_only): - super(LibvirtConnection, self).__init__() - self.libvirt_uri = self.get_uri() - - self.libvirt_xml = open(FLAGS.libvirt_xml_template).read() - self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read() - self._wrapped_conn = None - self.read_only = read_only - - fw_class = utils.import_class(FLAGS.firewall_driver) - self.firewall_driver = fw_class(get_connection=self._get_connection) - - def init_host(self, host): - # Adopt existing VM's running here - ctxt = context.get_admin_context() - for instance in db.instance_get_all_by_host(ctxt, host): - try: - LOG.debug(_('Checking state of %s'), instance['name']) - state = self.get_info(instance['name'])['state'] - except exception.NotFound: - state = power_state.SHUTOFF - - LOG.debug(_('Current state of %(name)s was %(state)s.'), - {'name': instance['name'], 'state': state}) - db.instance_set_state(ctxt, instance['id'], state) - - # NOTE(justinsb): We no longer delete SHUTOFF instances, - # the user may want to power them back on - - if state != power_state.RUNNING: - continue - self.firewall_driver.prepare_instance_filter(instance) - self.firewall_driver.apply_instance_filter(instance) - - def _get_connection(self): - if not self._wrapped_conn or not self._test_connection(): - LOG.debug(_('Connecting to libvirt: %s'), self.libvirt_uri) - self._wrapped_conn = self._connect(self.libvirt_uri, - self.read_only) - return self._wrapped_conn - _conn = property(_get_connection) - - def _test_connection(self): - try: - self._wrapped_conn.getInfo() - return True - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ - e.get_error_domain() == libvirt.VIR_FROM_REMOTE: - LOG.debug(_('Connection to libvirt broke')) - return False - raise - - def get_uri(self): - if FLAGS.libvirt_type == 'uml': - uri = FLAGS.libvirt_uri or 'uml:///system' - elif FLAGS.libvirt_type == 'xen': - uri = FLAGS.libvirt_uri or 'xen:///' - elif FLAGS.libvirt_type == 'lxc': - uri = FLAGS.libvirt_uri or 'lxc:///' - else: - uri = FLAGS.libvirt_uri or 'qemu:///system' - return uri - - def _connect(self, uri, read_only): - auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], - 'root', - None] - - if read_only: - return libvirt.openReadOnly(uri) - else: - return libvirt.openAuth(uri, auth, 0) - - def list_instances(self): - return [self._conn.lookupByID(x).name() - for x in self._conn.listDomainsID()] - - def _map_to_instance_info(self, domain): - """Gets info from a virsh domain object into an InstanceInfo""" - - # domain.info() returns a list of: - # state: one of the state values (virDomainState) - # maxMemory: the maximum memory used by the domain - # memory: the current amount of memory used by the domain - # nbVirtCPU: the number of virtual CPU - # puTime: the time used by the domain in nanoseconds - - (state, _max_mem, _mem, _num_cpu, _cpu_time) = domain.info() - name = domain.name() - - return driver.InstanceInfo(name, state) - - def list_instances_detail(self): - infos = [] - for domain_id in self._conn.listDomainsID(): - domain = self._conn.lookupByID(domain_id) - info = self._map_to_instance_info(domain) - infos.append(info) - return infos - - def destroy(self, instance, cleanup=True): - instance_name = instance['name'] - - try: - virt_dom = self._lookup_by_name(instance_name) - except exception.NotFound: - virt_dom = None - - # If the instance is already terminated, we're still happy - # Otherwise, destroy it - if virt_dom is not None: - try: - virt_dom.destroy() - except libvirt.libvirtError as e: - is_okay = False - errcode = e.get_error_code() - if errcode == libvirt.VIR_ERR_OPERATION_INVALID: - # If the instance if already shut off, we get this: - # Code=55 Error=Requested operation is not valid: - # domain is not running - (state, _max_mem, _mem, _cpus, _t) = virt_dom.info() - if state == power_state.SHUTOFF: - is_okay = True - - if not is_okay: - LOG.warning(_("Error from libvirt during destroy of " - "%(instance_name)s. Code=%(errcode)s " - "Error=%(e)s") % - locals()) - raise - - try: - # NOTE(justinsb): We remove the domain definition. We probably - # would do better to keep it if cleanup=False (e.g. volumes?) - # (e.g. #2 - not losing machines on failure) - virt_dom.undefine() - except libvirt.libvirtError as e: - errcode = e.get_error_code() - LOG.warning(_("Error from libvirt during undefine of " - "%(instance_name)s. Code=%(errcode)s " - "Error=%(e)s") % - locals()) - raise - - def _wait_for_destroy(): - """Called at an interval until the VM is gone.""" - instance_name = instance['name'] - - try: - state = self.get_info(instance_name)['state'] - except exception.NotFound: - msg = _("Instance %s destroyed successfully.") % instance_name - LOG.info(msg) - raise utils.LoopingCallDone - - timer = utils.LoopingCall(_wait_for_destroy) - timer.start(interval=0.5, now=True) - - self.firewall_driver.unfilter_instance(instance) - - if cleanup: - self._cleanup(instance) - - return True - - def _cleanup(self, instance): - target = os.path.join(FLAGS.instances_path, instance['name']) - instance_name = instance['name'] - LOG.info(_('instance %(instance_name)s: deleting instance files' - ' %(target)s') % locals()) - if FLAGS.libvirt_type == 'lxc': - disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images) - if os.path.exists(target): - shutil.rmtree(target) - - @exception.wrap_exception - def attach_volume(self, instance_name, device_path, mountpoint): - virt_dom = self._lookup_by_name(instance_name) - mount_device = mountpoint.rpartition("/")[2] - if device_path.startswith('/dev/'): - xml = """ - - - - """ % (device_path, mount_device) - elif ':' in device_path: - (protocol, name) = device_path.split(':') - xml = """ - - - - """ % (protocol, - name, - mount_device) - else: - raise exception.Invalid(_("Invalid device path %s") % device_path) - - virt_dom.attachDevice(xml) - - def _get_disk_xml(self, xml, device): - """Returns the xml for the disk mounted at device""" - try: - doc = libxml2.parseDoc(xml) - except: - return None - ctx = doc.xpathNewContext() - try: - ret = ctx.xpathEval('/domain/devices/disk') - for node in ret: - for child in node.children: - if child.name == 'target': - if child.prop('dev') == device: - return str(node) - finally: - if ctx is not None: - ctx.xpathFreeContext() - if doc is not None: - doc.freeDoc() - - @exception.wrap_exception - def detach_volume(self, instance_name, mountpoint): - virt_dom = self._lookup_by_name(instance_name) - mount_device = mountpoint.rpartition("/")[2] - xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device) - if not xml: - raise exception.NotFound(_("No disk at %s") % mount_device) - virt_dom.detachDevice(xml) - - @exception.wrap_exception - def snapshot(self, instance, image_id): - """Create snapshot from a running VM instance. - - This command only works with qemu 0.14+, the qemu_img flag is - provided so that a locally compiled binary of qemu-img can be used - to support this command. - - """ - image_service = utils.import_object(FLAGS.image_service) - virt_dom = self._lookup_by_name(instance['name']) - elevated = context.get_admin_context() - - base = image_service.show(elevated, instance['image_id']) - - metadata = {'disk_format': base['disk_format'], - 'container_format': base['container_format'], - 'is_public': False, - 'name': '%s.%s' % (base['name'], image_id), - 'properties': {'architecture': base['architecture'], - 'kernel_id': instance['kernel_id'], - 'image_location': 'snapshot', - 'image_state': 'available', - 'owner_id': instance['project_id'], - 'ramdisk_id': instance['ramdisk_id'], - } - } - - # Make the snapshot - snapshot_name = uuid.uuid4().hex - snapshot_xml = """ - - %s - - """ % snapshot_name - snapshot_ptr = virt_dom.snapshotCreateXML(snapshot_xml, 0) - - # Find the disk - xml_desc = virt_dom.XMLDesc(0) - domain = ElementTree.fromstring(xml_desc) - source = domain.find('devices/disk/source') - disk_path = source.get('file') - - # Export the snapshot to a raw image - temp_dir = tempfile.mkdtemp() - out_path = os.path.join(temp_dir, snapshot_name) - qemu_img_cmd = (FLAGS.qemu_img, - 'convert', - '-f', - 'qcow2', - '-O', - 'raw', - '-s', - snapshot_name, - disk_path, - out_path) - utils.execute(*qemu_img_cmd) - - # Upload that image to the image service - with open(out_path) as image_file: - image_service.update(elevated, - image_id, - metadata, - image_file) - - # Clean up - shutil.rmtree(temp_dir) - - @exception.wrap_exception - def reboot(self, instance): - """Reboot a virtual machine, given an instance reference. - - This method actually destroys and re-creates the domain to ensure the - reboot happens, as the guest OS cannot ignore this action. - - """ - self.destroy(instance, False) - xml = self.to_xml(instance) - self.firewall_driver.setup_basic_filtering(instance) - self.firewall_driver.prepare_instance_filter(instance) - self._create_new_domain(xml) - self.firewall_driver.apply_instance_filter(instance) - - def _wait_for_reboot(): - """Called at an interval until the VM is running again.""" - instance_name = instance['name'] - - try: - state = self.get_info(instance_name)['state'] - except exception.NotFound: - msg = _("During reboot, %s disappeared.") % instance_name - LOG.error(msg) - raise utils.LoopingCallDone - - if state == power_state.RUNNING: - msg = _("Instance %s rebooted successfully.") % instance_name - LOG.info(msg) - raise utils.LoopingCallDone - - timer = utils.LoopingCall(_wait_for_reboot) - return timer.start(interval=0.5, now=True) - - @exception.wrap_exception - def pause(self, instance, callback): - raise exception.ApiError("pause not supported for libvirt.") - - @exception.wrap_exception - def unpause(self, instance, callback): - raise exception.ApiError("unpause not supported for libvirt.") - - @exception.wrap_exception - def suspend(self, instance, callback): - raise exception.ApiError("suspend not supported for libvirt") - - @exception.wrap_exception - def resume(self, instance, callback): - raise exception.ApiError("resume not supported for libvirt") - - @exception.wrap_exception - def rescue(self, instance): - """Loads a VM using rescue images. - - A rescue is normally performed when something goes wrong with the - primary images and data needs to be corrected/recovered. Rescuing - should not edit or over-ride the original image, only allow for - data recovery. - - """ - self.destroy(instance, False) - - xml = self.to_xml(instance, rescue=True) - rescue_images = {'image_id': FLAGS.rescue_image_id, - 'kernel_id': FLAGS.rescue_kernel_id, - 'ramdisk_id': FLAGS.rescue_ramdisk_id} - self._create_image(instance, xml, '.rescue', rescue_images) - self._create_new_domain(xml) - - def _wait_for_rescue(): - """Called at an interval until the VM is running again.""" - instance_name = instance['name'] - - try: - state = self.get_info(instance_name)['state'] - except exception.NotFound: - msg = _("During reboot, %s disappeared.") % instance_name - LOG.error(msg) - raise utils.LoopingCallDone - - if state == power_state.RUNNING: - msg = _("Instance %s rescued successfully.") % instance_name - LOG.info(msg) - raise utils.LoopingCallDone - - timer = utils.LoopingCall(_wait_for_rescue) - return timer.start(interval=0.5, now=True) - - @exception.wrap_exception - def unrescue(self, instance): - """Reboot the VM which is being rescued back into primary images. - - Because reboot destroys and re-creates instances, unresue should - simply call reboot. - - """ - self.reboot(instance) - - @exception.wrap_exception - def poll_rescued_instances(self, timeout): - pass - - # NOTE(ilyaalekseyev): Implementation like in multinics - # for xenapi(tr3buchet) - @exception.wrap_exception - def spawn(self, instance, network_info=None): - xml = self.to_xml(instance, False, network_info) - self.firewall_driver.setup_basic_filtering(instance, network_info) - self.firewall_driver.prepare_instance_filter(instance, network_info) - self._create_image(instance, xml, network_info) - domain = self._create_new_domain(xml) - LOG.debug(_("instance %s: is running"), instance['name']) - self.firewall_driver.apply_instance_filter(instance) - - if FLAGS.start_guests_on_host_boot: - LOG.debug(_("instance %s: setting autostart ON") % - instance['name']) - domain.setAutostart(1) - - def _wait_for_boot(): - """Called at an interval until the VM is running.""" - instance_name = instance['name'] - - try: - state = self.get_info(instance_name)['state'] - except exception.NotFound: - msg = _("During reboot, %s disappeared.") % instance_name - LOG.error(msg) - raise utils.LoopingCallDone - - if state == power_state.RUNNING: - msg = _("Instance %s spawned successfully.") % instance_name - LOG.info(msg) - raise utils.LoopingCallDone - - timer = utils.LoopingCall(_wait_for_boot) - return timer.start(interval=0.5, now=True) - - def _flush_xen_console(self, virsh_output): - LOG.info(_('virsh said: %r'), virsh_output) - virsh_output = virsh_output[0].strip() - - if virsh_output.startswith('/dev/'): - LOG.info(_("cool, it's a device")) - out, err = utils.execute('sudo', 'dd', - "if=%s" % virsh_output, - 'iflag=nonblock', - check_exit_code=False) - return out - else: - return '' - - def _append_to_file(self, data, fpath): - LOG.info(_('data: %(data)r, fpath: %(fpath)r') % locals()) - fp = open(fpath, 'a+') - fp.write(data) - return fpath - - def _dump_file(self, fpath): - fp = open(fpath, 'r+') - contents = fp.read() - LOG.info(_('Contents of file %(fpath)s: %(contents)r') % locals()) - return contents - - @exception.wrap_exception - def get_console_output(self, instance): - console_log = os.path.join(FLAGS.instances_path, instance['name'], - 'console.log') - - utils.execute('sudo', 'chown', os.getuid(), console_log) - - if FLAGS.libvirt_type == 'xen': - # Xen is special - virsh_output = utils.execute('virsh', 'ttyconsole', - instance['name']) - data = self._flush_xen_console(virsh_output) - fpath = self._append_to_file(data, console_log) - elif FLAGS.libvirt_type == 'lxc': - # LXC is also special - LOG.info(_("Unable to read LXC console")) - else: - fpath = console_log - - return self._dump_file(fpath) - - @exception.wrap_exception - def get_ajax_console(self, instance): - def get_open_port(): - start_port, end_port = FLAGS.ajaxterm_portrange.split("-") - for i in xrange(0, 100): # don't loop forever - port = random.randint(int(start_port), int(end_port)) - # netcat will exit with 0 only if the port is in use, - # so a nonzero return value implies it is unused - cmd = 'netcat', '0.0.0.0', port, '-w', '1' - try: - stdout, stderr = utils.execute(*cmd, process_input='') - except exception.ProcessExecutionError: - return port - raise Exception(_('Unable to find an open port')) - - def get_pty_for_instance(instance_name): - virt_dom = self._lookup_by_name(instance_name) - xml = virt_dom.XMLDesc(0) - dom = minidom.parseString(xml) - - for serial in dom.getElementsByTagName('serial'): - if serial.getAttribute('type') == 'pty': - source = serial.getElementsByTagName('source')[0] - return source.getAttribute('path') - - port = get_open_port() - token = str(uuid.uuid4()) - host = instance['host'] - - ajaxterm_cmd = 'sudo socat - %s' \ - % get_pty_for_instance(instance['name']) - - cmd = '%s/tools/ajaxterm/ajaxterm.py --command "%s" -t %s -p %s' \ - % (utils.novadir(), ajaxterm_cmd, token, port) - - subprocess.Popen(cmd, shell=True) - return {'token': token, 'host': host, 'port': port} - - @exception.wrap_exception - def get_vnc_console(self, instance): - def get_vnc_port_for_instance(instance_name): - virt_dom = self._lookup_by_name(instance_name) - xml = virt_dom.XMLDesc(0) - # TODO: use etree instead of minidom - dom = minidom.parseString(xml) - - for graphic in dom.getElementsByTagName('graphics'): - if graphic.getAttribute('type') == 'vnc': - return graphic.getAttribute('port') - - port = get_vnc_port_for_instance(instance['name']) - token = str(uuid.uuid4()) - host = instance['host'] - - return {'token': token, 'host': host, 'port': port} - - @staticmethod - def _cache_image(fn, target, fname, cow=False, *args, **kwargs): - """Wrapper for a method that creates an image that caches the image. - - This wrapper will save the image into a common store and create a - copy for use by the hypervisor. - - The underlying method should specify a kwarg of target representing - where the image will be saved. - - fname is used as the filename of the base image. The filename needs - to be unique to a given image. - - If cow is True, it will make a CoW image instead of a copy. - """ - if not os.path.exists(target): - base_dir = os.path.join(FLAGS.instances_path, '_base') - if not os.path.exists(base_dir): - os.mkdir(base_dir) - base = os.path.join(base_dir, fname) - - @utils.synchronized(fname) - def call_if_not_exists(base, fn, *args, **kwargs): - if not os.path.exists(base): - fn(target=base, *args, **kwargs) - - call_if_not_exists(base, fn, *args, **kwargs) - - if cow: - utils.execute('qemu-img', 'create', '-f', 'qcow2', '-o', - 'cluster_size=2M,backing_file=%s' % base, - target) - else: - utils.execute('cp', base, target) - - def _fetch_image(self, target, image_id, user, project, size=None): - """Grab image and optionally attempt to resize it""" - images.fetch(image_id, target, user, project) - if size: - disk.extend(target, size) - - def _create_local(self, target, local_gb): - """Create a blank image of specified size""" - utils.execute('truncate', target, '-s', "%dG" % local_gb) - # TODO(vish): should we format disk by default? - - def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None, - network_info=None): - if not network_info: - network_info = _get_network_info(inst) - - if not suffix: - suffix = '' - - # syntactic nicety - def basepath(fname='', suffix=suffix): - return os.path.join(FLAGS.instances_path, - inst['name'], - fname + suffix) - - # ensure directories exist and are writable - utils.execute('mkdir', '-p', basepath(suffix='')) - - LOG.info(_('instance %s: Creating image'), inst['name']) - f = open(basepath('libvirt.xml'), 'w') - f.write(libvirt_xml) - f.close() - - if FLAGS.libvirt_type == 'lxc': - container_dir = '%s/rootfs' % basepath(suffix='') - utils.execute('mkdir', '-p', container_dir) - - # NOTE(vish): No need add the suffix to console.log - os.close(os.open(basepath('console.log', ''), - os.O_CREAT | os.O_WRONLY, 0660)) - - user = manager.AuthManager().get_user(inst['user_id']) - project = manager.AuthManager().get_project(inst['project_id']) - - if not disk_images: - disk_images = {'image_id': inst['image_id'], - 'kernel_id': inst['kernel_id'], - 'ramdisk_id': inst['ramdisk_id']} - - if disk_images['kernel_id']: - fname = '%08x' % int(disk_images['kernel_id']) - self._cache_image(fn=self._fetch_image, - target=basepath('kernel'), - fname=fname, - image_id=disk_images['kernel_id'], - user=user, - project=project) - if disk_images['ramdisk_id']: - fname = '%08x' % int(disk_images['ramdisk_id']) - self._cache_image(fn=self._fetch_image, - target=basepath('ramdisk'), - fname=fname, - image_id=disk_images['ramdisk_id'], - user=user, - project=project) - - root_fname = '%08x' % int(disk_images['image_id']) - size = FLAGS.minimum_root_size - - inst_type_id = inst['instance_type_id'] - inst_type = instance_types.get_instance_type(inst_type_id) - if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': - size = None - root_fname += "_sm" - - self._cache_image(fn=self._fetch_image, - target=basepath('disk'), - fname=root_fname, - cow=FLAGS.use_cow_images, - image_id=disk_images['image_id'], - user=user, - project=project, - size=size) - - if inst_type['local_gb']: - self._cache_image(fn=self._create_local, - target=basepath('disk.local'), - fname="local_%s" % inst_type['local_gb'], - cow=FLAGS.use_cow_images, - local_gb=inst_type['local_gb']) - - # For now, we assume that if we're not using a kernel, we're using a - # partitioned disk image where the target partition is the first - # partition - target_partition = None - if not inst['kernel_id']: - target_partition = "1" - - if FLAGS.libvirt_type == 'lxc': - target_partition = None - - if inst['key_data']: - key = str(inst['key_data']) - else: - key = None - net = None - - nets = [] - ifc_template = open(FLAGS.injected_network_template).read() - ifc_num = -1 - have_injected_networks = False - admin_context = context.get_admin_context() - for (network_ref, mapping) in network_info: - ifc_num += 1 - - if not network_ref['injected']: - continue - - have_injected_networks = True - address = mapping['ips'][0]['ip'] - address_v6 = None - if FLAGS.use_ipv6: - address_v6 = mapping['ip6s'][0]['ip'] - net_info = {'name': 'eth%d' % ifc_num, - 'address': address, - 'netmask': network_ref['netmask'], - 'gateway': network_ref['gateway'], - 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns'], - 'address_v6': address_v6, - 'gateway_v6': network_ref['gateway_v6'], - 'netmask_v6': network_ref['netmask_v6']} - nets.append(net_info) - - if have_injected_networks: - net = str(Template(ifc_template, - searchList=[{'interfaces': nets, - 'use_ipv6': FLAGS.use_ipv6}])) - - if key or net: - inst_name = inst['name'] - img_id = inst.image_id - if key: - LOG.info(_('instance %(inst_name)s: injecting key into' - ' image %(img_id)s') % locals()) - if net: - LOG.info(_('instance %(inst_name)s: injecting net into' - ' image %(img_id)s') % locals()) - try: - disk.inject_data(basepath('disk'), key, net, - partition=target_partition, - nbd=FLAGS.use_cow_images) - - if FLAGS.libvirt_type == 'lxc': - disk.setup_container(basepath('disk'), - container_dir=container_dir, - nbd=FLAGS.use_cow_images) - except Exception as e: - # This could be a windows image, or a vmdk format disk - LOG.warn(_('instance %(inst_name)s: ignoring error injecting' - ' data into image %(img_id)s (%(e)s)') % locals()) - - if FLAGS.libvirt_type == 'uml': - utils.execute('sudo', 'chown', 'root', basepath('disk')) - - def _get_nic_for_xml(self, network, mapping): - # Assume that the gateway also acts as the dhcp server. - dhcp_server = network['gateway'] - gateway_v6 = network['gateway_v6'] - mac_id = mapping['mac'].replace(':', '') - - if FLAGS.allow_project_net_traffic: - if FLAGS.use_ipv6: - net, mask = _get_net_and_mask(network['cidr']) - net_v6, prefixlen_v6 = _get_net_and_prefixlen( - network['cidr_v6']) - extra_params = ("\n" - "\n" - "\n" - "\n") % \ - (net, mask, net_v6, prefixlen_v6) - else: - net, mask = _get_net_and_mask(network['cidr']) - extra_params = ("\n" - "\n") % \ - (net, mask) - else: - extra_params = "\n" - - result = { - 'id': mac_id, - 'bridge_name': network['bridge'], - 'mac_address': mapping['mac'], - 'ip_address': mapping['ips'][0]['ip'], - 'dhcp_server': dhcp_server, - 'extra_params': extra_params, - } - - if gateway_v6: - result['gateway_v6'] = gateway_v6 + "/128" - - return result - - def to_xml(self, instance, rescue=False, network_info=None): - # TODO(termie): cache? - LOG.debug(_('instance %s: starting toXML method'), instance['name']) - - # TODO(adiantum) remove network_info creation code - # when multinics will be completed - if not network_info: - network_info = _get_network_info(instance) - - nics = [] - for (network, mapping) in network_info: - nics.append(self._get_nic_for_xml(network, - mapping)) - # FIXME(vish): stick this in db - inst_type_id = instance['instance_type_id'] - inst_type = instance_types.get_instance_type(inst_type_id) - - if FLAGS.use_cow_images: - driver_type = 'qcow2' - else: - driver_type = 'raw' - - xml_info = {'type': FLAGS.libvirt_type, - 'name': instance['name'], - 'basepath': os.path.join(FLAGS.instances_path, - instance['name']), - 'memory_kb': inst_type['memory_mb'] * 1024, - 'vcpus': inst_type['vcpus'], - 'rescue': rescue, - 'local': inst_type['local_gb'], - 'driver_type': driver_type, - 'nics': nics} - - if FLAGS.vnc_enabled: - if FLAGS.libvirt_type != 'lxc': - xml_info['vncserver_host'] = FLAGS.vncserver_host - if not rescue: - if instance['kernel_id']: - xml_info['kernel'] = xml_info['basepath'] + "/kernel" - - if instance['ramdisk_id']: - xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk" - - xml_info['disk'] = xml_info['basepath'] + "/disk" - - xml = str(Template(self.libvirt_xml, searchList=[xml_info])) - LOG.debug(_('instance %s: finished toXML method'), - instance['name']) - return xml - - def _lookup_by_name(self, instance_name): - """Retrieve libvirt domain object given an instance name. - - All libvirt error handling should be handled in this method and - relevant nova exceptions should be raised in response. - - """ - try: - return self._conn.lookupByName(instance_name) - except libvirt.libvirtError as ex: - error_code = ex.get_error_code() - if error_code == libvirt.VIR_ERR_NO_DOMAIN: - msg = _("Instance %s not found") % instance_name - raise exception.NotFound(msg) - - msg = _("Error from libvirt while looking up %(instance_name)s: " - "[Error Code %(error_code)s] %(ex)s") % locals() - raise exception.Error(msg) - - def get_info(self, instance_name): - """Retrieve information from libvirt for a specific instance name. - - If a libvirt error is encountered during lookup, we might raise a - NotFound exception or Error exception depending on how severe the - libvirt error is. - - """ - virt_dom = self._lookup_by_name(instance_name) - (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() - return {'state': state, - 'max_mem': max_mem, - 'mem': mem, - 'num_cpu': num_cpu, - 'cpu_time': cpu_time} - - def _create_new_domain(self, xml, persistent=True, launch_flags=0): - # NOTE(justinsb): libvirt has two types of domain: - # * a transient domain disappears when the guest is shutdown - # or the host is rebooted. - # * a permanent domain is not automatically deleted - # NOTE(justinsb): Even for ephemeral instances, transient seems risky - - if persistent: - # To create a persistent domain, first define it, then launch it. - domain = self._conn.defineXML(xml) - - domain.createWithFlags(launch_flags) - else: - # createXML call creates a transient domain - domain = self._conn.createXML(xml, launch_flags) - - return domain - - def get_diagnostics(self, instance_name): - raise exception.ApiError(_("diagnostics are not supported " - "for libvirt")) - - def get_disks(self, instance_name): - """ - Note that this function takes an instance name, not an Instance, so - that it can be called by monitor. - - Returns a list of all block devices for this domain. - """ - domain = self._lookup_by_name(instance_name) - # TODO(devcamcar): Replace libxml2 with etree. - xml = domain.XMLDesc(0) - doc = None - - try: - doc = libxml2.parseDoc(xml) - except: - return [] - - ctx = doc.xpathNewContext() - disks = [] - - try: - ret = ctx.xpathEval('/domain/devices/disk') - - for node in ret: - devdst = None - - for child in node.children: - if child.name == 'target': - devdst = child.prop('dev') - - if devdst is None: - continue - - disks.append(devdst) - finally: - if ctx is not None: - ctx.xpathFreeContext() - if doc is not None: - doc.freeDoc() - - return disks - - def get_interfaces(self, instance_name): - """ - Note that this function takes an instance name, not an Instance, so - that it can be called by monitor. - - Returns a list of all network interfaces for this instance. - """ - domain = self._lookup_by_name(instance_name) - # TODO(devcamcar): Replace libxml2 with etree. - xml = domain.XMLDesc(0) - doc = None - - try: - doc = libxml2.parseDoc(xml) - except: - return [] - - ctx = doc.xpathNewContext() - interfaces = [] - - try: - ret = ctx.xpathEval('/domain/devices/interface') - - for node in ret: - devdst = None - - for child in node.children: - if child.name == 'target': - devdst = child.prop('dev') - - if devdst is None: - continue - - interfaces.append(devdst) - finally: - if ctx is not None: - ctx.xpathFreeContext() - if doc is not None: - doc.freeDoc() - - return interfaces - - def get_vcpu_total(self): - """Get vcpu number of physical computer. - - :returns: the number of cpu core. - - """ - - # On certain platforms, this will raise a NotImplementedError. - try: - return multiprocessing.cpu_count() - except NotImplementedError: - LOG.warn(_("Cannot get the number of cpu, because this " - "function is not implemented for this platform. " - "This error can be safely ignored for now.")) - return 0 - - def get_memory_mb_total(self): - """Get the total memory size(MB) of physical computer. - - :returns: the total amount of memory(MB). - - """ - - if sys.platform.upper() != 'LINUX2': - return 0 - - meminfo = open('/proc/meminfo').read().split() - idx = meminfo.index('MemTotal:') - # transforming kb to mb. - return int(meminfo[idx + 1]) / 1024 - - def get_local_gb_total(self): - """Get the total hdd size(GB) of physical computer. - - :returns: - The total amount of HDD(GB). - Note that this value shows a partition where - NOVA-INST-DIR/instances mounts. - - """ - - hddinfo = os.statvfs(FLAGS.instances_path) - return hddinfo.f_frsize * hddinfo.f_blocks / 1024 / 1024 / 1024 - - def get_vcpu_used(self): - """ Get vcpu usage number of physical computer. - - :returns: The total number of vcpu that currently used. - - """ - - total = 0 - for dom_id in self._conn.listDomainsID(): - dom = self._conn.lookupByID(dom_id) - total += len(dom.vcpus()[1]) - return total - - def get_memory_mb_used(self): - """Get the free memory size(MB) of physical computer. - - :returns: the total usage of memory(MB). - - """ - - if sys.platform.upper() != 'LINUX2': - return 0 - - m = open('/proc/meminfo').read().split() - idx1 = m.index('MemFree:') - idx2 = m.index('Buffers:') - idx3 = m.index('Cached:') - avail = (int(m[idx1 + 1]) + int(m[idx2 + 1]) + int(m[idx3 + 1])) / 1024 - return self.get_memory_mb_total() - avail - - def get_local_gb_used(self): - """Get the free hdd size(GB) of physical computer. - - :returns: - The total usage of HDD(GB). - Note that this value shows a partition where - NOVA-INST-DIR/instances mounts. - - """ - - hddinfo = os.statvfs(FLAGS.instances_path) - avail = hddinfo.f_frsize * hddinfo.f_bavail / 1024 / 1024 / 1024 - return self.get_local_gb_total() - avail - - def get_hypervisor_type(self): - """Get hypervisor type. - - :returns: hypervisor type (ex. qemu) - - """ - - return self._conn.getType() - - def get_hypervisor_version(self): - """Get hypervisor version. - - :returns: hypervisor version (ex. 12003) - - """ - - # NOTE(justinsb): getVersion moved between libvirt versions - # Trying to do be compatible with older versions is a lost cause - # But ... we can at least give the user a nice message - method = getattr(self._conn, 'getVersion', None) - if method is None: - raise exception.Error(_("libvirt version is too old" - " (does not support getVersion)")) - # NOTE(justinsb): If we wanted to get the version, we could: - # method = getattr(libvirt, 'getVersion', None) - # NOTE(justinsb): This would then rely on a proper version check - - return method() - - def get_cpu_info(self): - """Get cpuinfo information. - - Obtains cpu feature from virConnect.getCapabilities, - and returns as a json string. - - :return: see above description - - """ - - xml = self._conn.getCapabilities() - xml = libxml2.parseDoc(xml) - nodes = xml.xpathEval('//host/cpu') - if len(nodes) != 1: - raise exception.Invalid(_("Invalid xml. '' must be 1," - "but %d\n") % len(nodes) - + xml.serialize()) - - cpu_info = dict() - - arch_nodes = xml.xpathEval('//host/cpu/arch') - if arch_nodes: - cpu_info['arch'] = arch_nodes[0].getContent() - - model_nodes = xml.xpathEval('//host/cpu/model') - if model_nodes: - cpu_info['model'] = model_nodes[0].getContent() - - vendor_nodes = xml.xpathEval('//host/cpu/vendor') - if vendor_nodes: - cpu_info['vendor'] = vendor_nodes[0].getContent() - - topology_nodes = xml.xpathEval('//host/cpu/topology') - topology = dict() - if topology_nodes: - topology_node = topology_nodes[0].get_properties() - while topology_node: - name = topology_node.get_name() - topology[name] = topology_node.getContent() - topology_node = topology_node.get_next() - - keys = ['cores', 'sockets', 'threads'] - tkeys = topology.keys() - if set(tkeys) != set(keys): - ks = ', '.join(keys) - raise exception.Invalid(_("Invalid xml: topology" - "(%(topology)s) must have " - "%(ks)s") % locals()) - - feature_nodes = xml.xpathEval('//host/cpu/feature') - features = list() - for nodes in feature_nodes: - features.append(nodes.get_properties().getContent()) - - cpu_info['topology'] = topology - cpu_info['features'] = features - return utils.dumps(cpu_info) - - def block_stats(self, instance_name, disk): - """ - Note that this function takes an instance name, not an Instance, so - that it can be called by monitor. - """ - domain = self._lookup_by_name(instance_name) - return domain.blockStats(disk) - - def interface_stats(self, instance_name, interface): - """ - Note that this function takes an instance name, not an Instance, so - that it can be called by monitor. - """ - domain = self._lookup_by_name(instance_name) - return domain.interfaceStats(interface) - - def get_console_pool_info(self, console_type): - #TODO(mdragon): console proxy should be implemented for libvirt, - # in case someone wants to use it with kvm or - # such. For now return fake data. - return {'address': '127.0.0.1', - 'username': 'fakeuser', - 'password': 'fakepassword'} - - def refresh_security_group_rules(self, security_group_id): - self.firewall_driver.refresh_security_group_rules(security_group_id) - - def refresh_security_group_members(self, security_group_id): - self.firewall_driver.refresh_security_group_members(security_group_id) - - def update_available_resource(self, ctxt, host): - """Updates compute manager resource info on ComputeNode table. - - This method is called when nova-coompute launches, and - whenever admin executes "nova-manage service update_resource". - - :param ctxt: security context - :param host: hostname that compute manager is currently running - - """ - - try: - service_ref = db.service_get_all_compute_by_host(ctxt, host)[0] - except exception.NotFound: - raise exception.Invalid(_("Cannot update compute manager " - "specific info, because no service " - "record was found.")) - - # Updating host information - dic = {'vcpus': self.get_vcpu_total(), - 'memory_mb': self.get_memory_mb_total(), - 'local_gb': self.get_local_gb_total(), - 'vcpus_used': self.get_vcpu_used(), - 'memory_mb_used': self.get_memory_mb_used(), - 'local_gb_used': self.get_local_gb_used(), - 'hypervisor_type': self.get_hypervisor_type(), - 'hypervisor_version': self.get_hypervisor_version(), - 'cpu_info': self.get_cpu_info()} - - compute_node_ref = service_ref['compute_node'] - if not compute_node_ref: - LOG.info(_('Compute_service record created for %s ') % host) - dic['service_id'] = service_ref['id'] - db.compute_node_create(ctxt, dic) - else: - LOG.info(_('Compute_service record updated for %s ') % host) - db.compute_node_update(ctxt, compute_node_ref[0]['id'], dic) - - def compare_cpu(self, cpu_info): - """Checks the host cpu is compatible to a cpu given by xml. - - "xml" must be a part of libvirt.openReadonly().getCapabilities(). - return values follows by virCPUCompareResult. - if 0 > return value, do live migration. - 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' - - :param cpu_info: json string that shows cpu feature(see get_cpu_info()) - :returns: - None. if given cpu info is not compatible to this server, - raise exception. - - """ - - LOG.info(_('Instance launched has CPU info:\n%s') % cpu_info) - dic = utils.loads(cpu_info) - xml = str(Template(self.cpuinfo_xml, searchList=dic)) - LOG.info(_('to xml...\n:%s ' % xml)) - - u = "http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult" - m = _("CPU doesn't have compatibility.\n\n%(ret)s\n\nRefer to %(u)s") - # unknown character exists in xml, then libvirt complains - try: - ret = self._conn.compareCPU(xml, 0) - except libvirt.libvirtError, e: - ret = e.message - LOG.error(m % locals()) - raise - - if ret <= 0: - raise exception.Invalid(m % locals()) - - return - - def ensure_filtering_rules_for_instance(self, instance_ref, - time=None): - """Setting up filtering rules and waiting for its completion. - - To migrate an instance, filtering rules to hypervisors - and firewalls are inevitable on destination host. - ( Waiting only for filterling rules to hypervisor, - since filtering rules to firewall rules can be set faster). - - Concretely, the below method must be called. - - setup_basic_filtering (for nova-basic, etc.) - - prepare_instance_filter(for nova-instance-instance-xxx, etc.) - - to_xml may have to be called since it defines PROJNET, PROJMASK. - but libvirt migrates those value through migrateToURI(), - so , no need to be called. - - Don't use thread for this method since migration should - not be started when setting-up filtering rules operations - are not completed. - - :params instance_ref: nova.db.sqlalchemy.models.Instance object - - """ - - if not time: - time = greenthread - - # If any instances never launch at destination host, - # basic-filtering must be set here. - self.firewall_driver.setup_basic_filtering(instance_ref) - # setting up n)ova-instance-instance-xx mainly. - self.firewall_driver.prepare_instance_filter(instance_ref) - - # wait for completion - timeout_count = range(FLAGS.live_migration_retry_count) - while timeout_count: - if self.firewall_driver.instance_filter_exists(instance_ref): - break - timeout_count.pop() - if len(timeout_count) == 0: - msg = _('Timeout migrating for %s. nwfilter not found.') - raise exception.Error(msg % instance_ref.name) - time.sleep(1) - - def live_migration(self, ctxt, instance_ref, dest, - post_method, recover_method): - """Spawning live_migration operation for distributing high-load. - - :params ctxt: security context - :params instance_ref: - nova.db.sqlalchemy.models.Instance object - instance object that is migrated. - :params dest: destination host - :params post_method: - post operation method. - expected nova.compute.manager.post_live_migration. - :params recover_method: - recovery method when any exception occurs. - expected nova.compute.manager.recover_live_migration. - - """ - - greenthread.spawn(self._live_migration, ctxt, instance_ref, dest, - post_method, recover_method) - - def _live_migration(self, ctxt, instance_ref, dest, - post_method, recover_method): - """Do live migration. - - :params ctxt: security context - :params instance_ref: - nova.db.sqlalchemy.models.Instance object - instance object that is migrated. - :params dest: destination host - :params post_method: - post operation method. - expected nova.compute.manager.post_live_migration. - :params recover_method: - recovery method when any exception occurs. - expected nova.compute.manager.recover_live_migration. - - """ - - # Do live migration. - try: - flaglist = FLAGS.live_migration_flag.split(',') - flagvals = [getattr(libvirt, x.strip()) for x in flaglist] - logical_sum = reduce(lambda x, y: x | y, flagvals) - - if self.read_only: - tmpconn = self._connect(self.libvirt_uri, False) - dom = tmpconn.lookupByName(instance_ref.name) - dom.migrateToURI(FLAGS.live_migration_uri % dest, - logical_sum, - None, - FLAGS.live_migration_bandwidth) - tmpconn.close() - else: - dom = self._conn.lookupByName(instance_ref.name) - dom.migrateToURI(FLAGS.live_migration_uri % dest, - logical_sum, - None, - FLAGS.live_migration_bandwidth) - - except Exception: - recover_method(ctxt, instance_ref, dest=dest) - raise - - # Waiting for completion of live_migration. - timer = utils.LoopingCall(f=None) - - def wait_for_live_migration(): - """waiting for live migration completion""" - try: - self.get_info(instance_ref.name)['state'] - except exception.NotFound: - timer.stop() - post_method(ctxt, instance_ref, dest) - - timer.f = wait_for_live_migration - timer.start(interval=0.5, now=True) - - def unfilter_instance(self, instance_ref): - """See comments of same method in firewall_driver.""" - self.firewall_driver.unfilter_instance(instance_ref) - - -class FirewallDriver(object): - def prepare_instance_filter(self, instance, network_info=None): - """Prepare filters for the instance. - - At this point, the instance isn't running yet.""" - raise NotImplementedError() - - def unfilter_instance(self, instance): - """Stop filtering instance""" - raise NotImplementedError() - - def apply_instance_filter(self, instance): - """Apply instance filter. - - Once this method returns, the instance should be firewalled - appropriately. This method should as far as possible be a - no-op. It's vastly preferred to get everything set up in - prepare_instance_filter. - """ - raise NotImplementedError() - - def refresh_security_group_rules(self, security_group_id): - """Refresh security group rules from data store - - Gets called when a rule has been added to or removed from - the security group.""" - raise NotImplementedError() - - def refresh_security_group_members(self, security_group_id): - """Refresh security group members from data store - - Gets called when an instance gets added to or removed from - the security group.""" - raise NotImplementedError() - - def setup_basic_filtering(self, instance, network_info=None): - """Create rules to block spoofing and allow dhcp. - - This gets called when spawning an instance, before - :method:`prepare_instance_filter`. - - """ - raise NotImplementedError() - - def instance_filter_exists(self, instance): - """Check nova-instance-instance-xxx exists""" - raise NotImplementedError() - - -class NWFilterFirewall(FirewallDriver): - """ - This class implements a network filtering mechanism versatile - enough for EC2 style Security Group filtering by leveraging - libvirt's nwfilter. - - First, all instances get a filter ("nova-base-filter") applied. - This filter provides some basic security such as protection against - MAC spoofing, IP spoofing, and ARP spoofing. - - This filter drops all incoming ipv4 and ipv6 connections. - Outgoing connections are never blocked. - - Second, every security group maps to a nwfilter filter(*). - NWFilters can be updated at runtime and changes are applied - immediately, so changes to security groups can be applied at - runtime (as mandated by the spec). - - Security group rules are named "nova-secgroup-" where - is the internal id of the security group. They're applied only on - hosts that have instances in the security group in question. - - Updates to security groups are done by updating the data model - (in response to API calls) followed by a request sent to all - the nodes with instances in the security group to refresh the - security group. - - Each instance has its own NWFilter, which references the above - mentioned security group NWFilters. This was done because - interfaces can only reference one filter while filters can - reference multiple other filters. This has the added benefit of - actually being able to add and remove security groups from an - instance at run time. This functionality is not exposed anywhere, - though. - - Outstanding questions: - - The name is unique, so would there be any good reason to sync - the uuid across the nodes (by assigning it from the datamodel)? - - - (*) This sentence brought to you by the redundancy department of - redundancy. - - """ - - def __init__(self, get_connection, **kwargs): - self._libvirt_get_connection = get_connection - self.static_filters_configured = False - self.handle_security_groups = False - - def apply_instance_filter(self, instance): - """No-op. Everything is done in prepare_instance_filter""" - pass - - def _get_connection(self): - return self._libvirt_get_connection() - _conn = property(_get_connection) - - def nova_dhcp_filter(self): - """The standard allow-dhcp-server filter is an one, so it uses - ebtables to allow traffic through. Without a corresponding rule in - iptables, it'll get blocked anyway.""" - - return ''' - 891e4787-e5c0-d59b-cbd6-41bc3c6b36fc - - - - - - - ''' - - def nova_ra_filter(self): - return ''' - d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 - - - - ''' - - def setup_basic_filtering(self, instance, network_info=None): - """Set up basic filtering (MAC, IP, and ARP spoofing protection)""" - logging.info('called setup_basic_filtering in nwfilter') - - if not network_info: - network_info = _get_network_info(instance) - - if self.handle_security_groups: - # No point in setting up a filter set that we'll be overriding - # anyway. - return - - logging.info('ensuring static filters') - self._ensure_static_filters() - - if instance['image_id'] == str(FLAGS.vpn_image_id): - base_filter = 'nova-vpn' - else: - base_filter = 'nova-base' - - for (network, mapping) in network_info: - nic_id = mapping['mac'].replace(':', '') - instance_filter_name = self._instance_filter_name(instance, nic_id) - self._define_filter(self._filter_container(instance_filter_name, - [base_filter])) - - def _ensure_static_filters(self): - if self.static_filters_configured: - return - - self._define_filter(self._filter_container('nova-base', - ['no-mac-spoofing', - 'no-ip-spoofing', - 'no-arp-spoofing', - 'allow-dhcp-server'])) - self._define_filter(self._filter_container('nova-vpn', - ['allow-dhcp-server'])) - self._define_filter(self.nova_base_ipv4_filter) - self._define_filter(self.nova_base_ipv6_filter) - self._define_filter(self.nova_dhcp_filter) - self._define_filter(self.nova_ra_filter) - if FLAGS.allow_project_net_traffic: - self._define_filter(self.nova_project_filter) - if FLAGS.use_ipv6: - self._define_filter(self.nova_project_filter_v6) - - self.static_filters_configured = True - - def _filter_container(self, name, filters): - xml = '''%s''' % ( - name, - ''.join(["" % (f,) for f in filters])) - return xml - - def nova_base_ipv4_filter(self): - retval = "" - for protocol in ['tcp', 'udp', 'icmp']: - for direction, action, priority in [('out', 'accept', 399), - ('in', 'drop', 400)]: - retval += """ - <%s /> - """ % (action, direction, - priority, protocol) - retval += '' - return retval - - def nova_base_ipv6_filter(self): - retval = "" - for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - for direction, action, priority in [('out', 'accept', 399), - ('in', 'drop', 400)]: - retval += """ - <%s /> - """ % (action, direction, - priority, protocol) - retval += '' - return retval - - def nova_project_filter(self): - retval = "" - for protocol in ['tcp', 'udp', 'icmp']: - retval += """ - <%s srcipaddr='$PROJNET' srcipmask='$PROJMASK' /> - """ % protocol - retval += '' - return retval - - def nova_project_filter_v6(self): - retval = "" - for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """ - <%s srcipaddr='$PROJNETV6' - srcipmask='$PROJMASKV6' /> - """ % (protocol) - retval += '' - return retval - - def _define_filter(self, xml): - if callable(xml): - xml = xml() - # execute in a native thread and block current greenthread until done - tpool.execute(self._conn.nwfilterDefineXML, xml) - - def unfilter_instance(self, instance): - # Nothing to do - pass - - def prepare_instance_filter(self, instance, network_info=None): - """ - Creates an NWFilter for the given instance. In the process, - it makes sure the filters for the security groups as well as - the base filter are all in place. - """ - if not network_info: - network_info = _get_network_info(instance) - if instance['image_id'] == str(FLAGS.vpn_image_id): - base_filter = 'nova-vpn' - else: - base_filter = 'nova-base' - - ctxt = context.get_admin_context() - - instance_secgroup_filter_name = \ - '%s-secgroup' % (self._instance_filter_name(instance)) - #% (instance_filter_name,) - - instance_secgroup_filter_children = ['nova-base-ipv4', - 'nova-base-ipv6', - 'nova-allow-dhcp-server'] - - for security_group in \ - db.security_group_get_by_instance(ctxt, instance['id']): - - self.refresh_security_group_rules(security_group['id']) - - instance_secgroup_filter_children += [('nova-secgroup-%s' % - security_group['id'])] - - self._define_filter( - self._filter_container(instance_secgroup_filter_name, - instance_secgroup_filter_children)) - - for (network, mapping) in network_info: - nic_id = mapping['mac'].replace(':', '') - instance_filter_name = self._instance_filter_name(instance, nic_id) - instance_filter_children = \ - [base_filter, instance_secgroup_filter_name] - - if FLAGS.use_ipv6: - gateway_v6 = network['gateway_v6'] - - if gateway_v6: - instance_secgroup_filter_children += \ - ['nova-allow-ra-server'] - - if FLAGS.allow_project_net_traffic: - instance_filter_children += ['nova-project'] - if FLAGS.use_ipv6: - instance_filter_children += ['nova-project-v6'] - - self._define_filter( - self._filter_container(instance_filter_name, - instance_filter_children)) - - return - - def refresh_security_group_rules(self, security_group_id): - return self._define_filter( - self.security_group_to_nwfilter_xml(security_group_id)) - - def security_group_to_nwfilter_xml(self, security_group_id): - security_group = db.security_group_get(context.get_admin_context(), - security_group_id) - rule_xml = "" - v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} - for rule in security_group.rules: - rule_xml += "" - if rule.cidr: - version = _get_ip_version(rule.cidr) - if(FLAGS.use_ipv6 and version == 6): - net, prefixlen = _get_net_and_prefixlen(rule.cidr) - rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (v6protocol[rule.protocol], net, prefixlen) - else: - net, mask = _get_net_and_mask(rule.cidr) - rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (rule.protocol, net, mask) - if rule.protocol in ['tcp', 'udp']: - rule_xml += "dstportstart='%s' dstportend='%s' " % \ - (rule.from_port, rule.to_port) - elif rule.protocol == 'icmp': - LOG.info('rule.protocol: %r, rule.from_port: %r, ' - 'rule.to_port: %r', rule.protocol, - rule.from_port, rule.to_port) - if rule.from_port != -1: - rule_xml += "type='%s' " % rule.from_port - if rule.to_port != -1: - rule_xml += "code='%s' " % rule.to_port - - rule_xml += '/>\n' - rule_xml += "\n" - xml = " Date: Fri, 22 Apr 2011 15:14:36 -0400 Subject: fixing bad merge --- nova/virt/libvirt_conn.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ee0b7ab98..7a78ce9e2 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -619,10 +619,6 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def spawn(self, instance, network_info=None): xml = self.to_xml(instance, False, network_info) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.NOSTATE, - 'launching') self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(instance, xml, network_info) -- cgit From 8e6875e8c9b45a03396d5e4312c4f9136b1dc552 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 27 Apr 2011 14:03:05 -0700 Subject: further cleanup of nova/exceptions.py --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 15adcccee..8cb971d95 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1059,8 +1059,7 @@ class LibvirtConnection(driver.ComputeDriver): except libvirt.libvirtError as ex: error_code = ex.get_error_code() if error_code == libvirt.VIR_ERR_NO_DOMAIN: - msg = _("Instance %s not found") % instance_name - raise exception.NotFound(msg) + raise exception.InstanceNotFound(instance_id=instance_name) msg = _("Error from libvirt while looking up %(instance_name)s: " "[Error Code %(error_code)s] %(ex)s") % locals() -- cgit From 542909e52a6f3f2a9891b710f3755ea7c033a8d0 Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Thu, 28 Apr 2011 14:41:56 +0400 Subject: Sanitize get_console_output in libvirt_conn --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 15adcccee..54186ced0 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -695,7 +695,7 @@ class LibvirtConnection(driver.ComputeDriver): else: fpath = console_log - return self._dump_file(fpath) + return self._dump_file(fpath).decode('utf8','replace').encode('ascii','replace') @exception.wrap_exception def get_ajax_console(self, instance): -- cgit From e49ef5187491d4143de8d0707595c9fb566d4211 Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Fri, 29 Apr 2011 13:20:31 +0400 Subject: Changed test_cloud and fake virt driver to show out the fix. --- nova/virt/fake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 33f37b512..59189277d 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -367,7 +367,8 @@ class FakeConnection(driver.ComputeDriver): return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] def get_console_output(self, instance): - return 'FAKE CONSOLE OUTPUT' + return 'FAKE CONSOLE\xffOUTPUT'.decode('utf8','replace').encode('ascii','replace') + def get_ajax_console(self, instance): return {'token': 'FAKETOKEN', -- cgit From 6db188a3311ed62a24ba7202de2a6101c0d35c93 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sun, 1 May 2011 01:01:01 -0700 Subject: Added checking ip_v6 flag and test for it --- nova/virt/libvirt_conn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 879534f59..4e1ec1a64 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -2008,8 +2008,10 @@ class IptablesFirewallDriver(FirewallDriver): for ip in mapping['ips']] ipv4_rules = self._create_filter(ips_v4, chain_name) - ips_v6 = [ip['ip'] for (_n, mapping) in network_info - for ip in mapping['ip6s']] + ips_v6 = [] + if FLAGS.use_ipv6: + ips_v6 = [ip['ip'] for (_n, mapping) in network_info + for ip in mapping['ip6s']] ipv6_rules = self._create_filter(ips_v6, chain_name) return ipv4_rules, ipv6_rules -- cgit From 103ed1e5ca489de0064decc91bccf25dfbadc761 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sun, 1 May 2011 12:10:54 -0700 Subject: place ipv6_rules creation under if ip_v6 section --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e1ec1a64..46643ce73 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -2008,12 +2008,12 @@ class IptablesFirewallDriver(FirewallDriver): for ip in mapping['ips']] ipv4_rules = self._create_filter(ips_v4, chain_name) - ips_v6 = [] + ipv6_rules = [] if FLAGS.use_ipv6: ips_v6 = [ip['ip'] for (_n, mapping) in network_info for ip in mapping['ip6s']] + ipv6_rules = self._create_filter(ips_v6, chain_name) - ipv6_rules = self._create_filter(ips_v6, chain_name) return ipv4_rules, ipv6_rules def _add_filters(self, chain_name, ipv4_rules, ipv6_rules): -- cgit From 3be272f432b4385cf77787416762a360687a36bd Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 2 May 2011 10:17:51 -0400 Subject: Use my_ip for libvirt version of get_host_ip_addr. --- nova/virt/libvirt_conn.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6b56622ff..a62deb4a6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -40,7 +40,6 @@ import multiprocessing import os import random import shutil -import socket import subprocess import sys import tempfile @@ -737,9 +736,7 @@ class LibvirtConnection(driver.ComputeDriver): return {'token': token, 'host': host, 'port': port} def get_host_ip_addr(self): - hostname = self._conn.getHostname() - ip = socket.gethostbyname(hostname) - return ip + return FLAGS.my_ip @exception.wrap_exception def get_vnc_console(self, instance): -- cgit From c38871690702ad3b6b39845ae33ee71465a8e95c Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Tue, 3 May 2011 11:05:45 +0400 Subject: Moved reencoding logic to compute manager and cloud EC2 API. --- nova/virt/fake.py | 2 +- nova/virt/libvirt_conn.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 59189277d..832487deb 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -367,7 +367,7 @@ class FakeConnection(driver.ComputeDriver): return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] def get_console_output(self, instance): - return 'FAKE CONSOLE\xffOUTPUT'.decode('utf8','replace').encode('ascii','replace') + return 'FAKE CONSOLE\xffOUTPUT' def get_ajax_console(self, instance): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 54186ced0..15adcccee 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -695,7 +695,7 @@ class LibvirtConnection(driver.ComputeDriver): else: fpath = console_log - return self._dump_file(fpath).decode('utf8','replace').encode('ascii','replace') + return self._dump_file(fpath) @exception.wrap_exception def get_ajax_console(self, instance): -- cgit From e171e7511c6b1a2baacf0ab9857643cc1fe82eb3 Mon Sep 17 00:00:00 2001 From: Renuka Apte Date: Tue, 3 May 2011 17:08:04 -0700 Subject: Fix indentation. --- nova/virt/xenapi/volume_utils.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 819c48be5..55c11a4ad 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -246,19 +246,21 @@ def _get_target(volume_id): 1) volume_ref['host'] must resolve to something rather than loopback """ volume_ref = db.volume_get(context.get_admin_context(), - volume_id) + volume_id) result = (None, None) try: - (r, _e) = utils.execute('sudo', 'iscsiadm', '-m', 'discovery', - '-t', 'sendtargets', '-p', volume_ref['host']) + (r, _e) = utils.execute('sudo', 'iscsiadm', + '-m', 'discovery', + '-t', 'sendtargets', + '-p', volume_ref['host']) except exception.ProcessExecutionError, exc: LOG.exception(exc) else: volume_name = "volume-%08x" % volume_id for target in r.splitlines(): if FLAGS.iscsi_ip_prefix in target and volume_name in target: - (location, _sep, iscsi_name) = target.partition(" ") - break + (location, _sep, iscsi_name) = target.partition(" ") + break iscsi_portal = location.split(",")[0] result = (iscsi_name, iscsi_portal) return result -- cgit From 6ee9c2f2b9a7b359336cfad0c5c6b4e1ef78a0da Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Thu, 5 May 2011 07:53:04 +0400 Subject: Removed extra newline after get_console_output in fake virt driver. --- nova/virt/fake.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 832487deb..5ac376e46 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -369,7 +369,6 @@ class FakeConnection(driver.ComputeDriver): def get_console_output(self, instance): return 'FAKE CONSOLE\xffOUTPUT' - def get_ajax_console(self, instance): return {'token': 'FAKETOKEN', 'host': 'fakeajaxconsole.com', -- cgit From fa9eeb65533d897f6e81067986dc614582fb310a Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 6 May 2011 07:19:57 -0700 Subject: grabbed from dist-sched branch --- nova/virt/hyperv.py | 9 +++++++ nova/virt/libvirt_conn.py | 8 ++++++ nova/virt/xenapi_conn.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 9026e737e..573e5130e 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -486,3 +486,12 @@ class HyperVConnection(driver.ComputeDriver): def update_available_resource(self, ctxt, host): """This method is supported only by libvirt.""" return + + def update_host_status(self): + """See xenapi_conn.py implementation.""" + pass + + def get_host_stats(self, refresh=False): + """See xenapi_conn.py implementation.""" + pass + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9780c69a6..555e44ce2 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1582,6 +1582,14 @@ class LibvirtConnection(driver.ComputeDriver): """See comments of same method in firewall_driver.""" self.firewall_driver.unfilter_instance(instance_ref) + def update_host_status(self): + """See xenapi_conn.py implementation.""" + pass + + def get_host_stats(self, refresh=False): + """See xenapi_conn.py implementation.""" + pass + class FirewallDriver(object): def prepare_instance_filter(self, instance, network_info=None): diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0cabccf08..63a53af2e 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -168,6 +168,13 @@ class XenAPIConnection(driver.ComputeDriver): session = XenAPISession(url, user, pw) self._vmops = VMOps(session) self._volumeops = VolumeOps(session) + self._host_state = None + + @property + def HostState(self): + if not self._host_state: + self._host_state = HostState(self.session) + return self._host_state def init_host(self, host): #FIXME(armando): implement this @@ -315,6 +322,16 @@ class XenAPIConnection(driver.ComputeDriver): """This method is supported only by libvirt.""" raise NotImplementedError('This method is supported only by libvirt.') + def update_host_status(self): + """Update the status info of the host, and return those values + to the calling program.""" + return self.HostState.update_status() + + def get_host_stats(self, refresh=False): + """Return the current state of the host. If 'refresh' is + True, run the update first.""" + return self.HostState.get_host_stats(refresh=refresh) + class XenAPISession(object): """The session to invoke XenAPI SDK calls""" @@ -436,6 +453,58 @@ class XenAPISession(object): raise +class HostState(object): + """Manages information about the XenServer host this compute + node is running on. + """ + def __init__(self, session): + super(HostState, self).__init__() + self._session = session + self._stats = {} + self.update_status() + + def get_host_stats(self, refresh=False): + """Return the current state of the host. If 'refresh' is + True, run the update first. + """ + if refresh: + self.update_status() + return self._stats + + def update_status(self): + """Since under Xenserver, a compute node runs on a given host, + we can get host status information using xenapi. + """ + LOG.debug(_("Updating host stats")) + # Make it something unlikely to match any actual instance ID + task_id = random.randint(-80000, -70000) + task = self._session.async_call_plugin("xenhost", "host_data", {}) + task_result = self._session.wait_for_task(task, task_id) + if not task_result: + task_result = json.dumps("") + try: + data = json.loads(task_result) + except ValueError as e: + # Invalid JSON object + LOG.error(_("Unable to get updated status: %s") % e) + return + # Get the SR usage + try: + sr_ref = vm_utils.safe_find_sr(self._session) + except exception.NotFound as e: + # No SR configured + LOG.error(_("Unable to get SR for this host: %s") % e) + return + sr_rec = self._session.get_xenapi().SR.get_record(sr_ref) + total = int(sr_rec["virtual_allocation"]) + used = int(sr_rec["physical_utilisation"]) + data["disk"] = dd = {} + dd["total"] = total + dd["used"] = used + dd["available"] = total - used + self._stats = data + + def _parse_xmlrpc_value(val): """Parse the given value as if it were an XML-RPC value. This is sometimes used as the format for the task.result field.""" -- cgit From 595f742763336bb4edeb55f7556ce618bf85481e Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 6 May 2011 11:04:00 -0700 Subject: tests pass again --- nova/virt/hyperv.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 573e5130e..1142e97a4 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -494,4 +494,3 @@ class HyperVConnection(driver.ComputeDriver): def get_host_stats(self, refresh=False): """See xenapi_conn.py implementation.""" pass - -- cgit From 791f8dc895b97caa9395a52113823bde37ae6cfa Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 6 May 2011 13:47:47 -0500 Subject: Set root password upon XenServer instance creation. --- nova/virt/xenapi/vmops.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 30f31517d..a13febdab 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -428,15 +428,16 @@ class VMOps(object): """ # Need to uniquely identify this request. - transaction_id = str(uuid.uuid4()) + key_init_transaction_id = str(uuid.uuid4()) # The simple Diffie-Hellman class is used to manage key exchange. dh = SimpleDH() - args = {'id': transaction_id, 'pub': str(dh.get_public())} - resp = self._make_agent_call('key_init', instance, '', args) + key_init_args = {'id': key_init_transaction_id, + 'pub': str(dh.get_public())} + resp = self._make_agent_call('key_init', instance, '', key_init_args) if resp is None: # No response from the agent return - resp_dict = json.loads(resp) + resp_dict = json.loads(json.loads(resp)) # Successful return code from key_init is 'D0' if resp_dict['returncode'] != 'D0': # There was some sort of error; the message will contain @@ -446,12 +447,13 @@ class VMOps(object): dh.compute_shared(agent_pub) enc_pass = dh.encrypt(new_pass) # Send the encrypted password - args['enc_pass'] = enc_pass - resp = self._make_agent_call('password', instance, '', args) + password_transaction_id = str(uuid.uuid4()) + password_args = {'id': password_transaction_id, 'enc_pass': enc_pass} + resp = self._make_agent_call('password', instance, '', password_args) if resp is None: # No response from the agent return - resp_dict = json.loads(resp) + resp_dict = json.loads(json.loads(resp)) # Successful return code from password is '0' if resp_dict['returncode'] != '0': raise RuntimeError(resp_dict['message']) -- cgit From 311c774e1109d6ce0449f0d06346078020ffa4e0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 6 May 2011 14:48:54 -0500 Subject: Review feedback --- nova/virt/xenapi/vmops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a13febdab..fe9a74dd6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -437,7 +437,7 @@ class VMOps(object): if resp is None: # No response from the agent return - resp_dict = json.loads(json.loads(resp)) + resp_dict = json.loads(resp) # Successful return code from key_init is 'D0' if resp_dict['returncode'] != 'D0': # There was some sort of error; the message will contain @@ -453,7 +453,7 @@ class VMOps(object): if resp is None: # No response from the agent return - resp_dict = json.loads(json.loads(resp)) + resp_dict = json.loads(resp) # Successful return code from password is '0' if resp_dict['returncode'] != '0': raise RuntimeError(resp_dict['message']) -- cgit From 3c0d31a1ae91e30e06f1b33d35915037472b3691 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 9 May 2011 08:23:25 -0700 Subject: basic test working --- nova/virt/xenapi_conn.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 63a53af2e..0e545150f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -57,6 +57,8 @@ reactor thread if the VM.get_by_name_label or VM.get_record calls block. - suffix "_rec" for record objects """ +import json +import random import sys import urlparse import xmlrpclib @@ -67,10 +69,12 @@ from eventlet import timeout from nova import context from nova import db +from nova import exception from nova import utils from nova import flags from nova import log as logging from nova.virt import driver +from nova.virt.xenapi import vm_utils from nova.virt.xenapi.vmops import VMOps from nova.virt.xenapi.volumeops import VolumeOps -- cgit From 1c1a06c3731dd82b331f317ba52edbfe2110a40e Mon Sep 17 00:00:00 2001 From: William Wolf Date: Mon, 9 May 2011 11:47:33 -0400 Subject: clean up unused functions from virt/images.py --- nova/virt/images.py | 61 ----------------------------------------------------- 1 file changed, 61 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 2e3f2ee4d..1eb6f4a5f 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -51,67 +51,6 @@ def fetch(image_id, path, _user, _project): metadata = image_service.get(elevated, image_id, image_file) return metadata - -# NOTE(vish): The methods below should be unnecessary, but I'm leaving -# them in case the glance client does not work on windows. -def _fetch_image_no_curl(url, path, headers): - request = urllib2.Request(url) - for (k, v) in headers.iteritems(): - request.add_header(k, v) - - def urlretrieve(urlfile, fpath): - chunk = 1 * 1024 * 1024 - f = open(fpath, "wb") - while 1: - data = urlfile.read(chunk) - if not data: - break - f.write(data) - - urlopened = urllib2.urlopen(request) - urlretrieve(urlopened, path) - LOG.debug(_("Finished retreving %(url)s -- placed in %(path)s") % locals()) - - -def _fetch_s3_image(image, path, user, project): - url = image_url(image) - - # This should probably move somewhere else, like e.g. a download_as - # method on User objects and at the same time get rewritten to use - # a web client. - headers = {} - headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - - (_, _, url_path, _, _, _) = urlparse.urlparse(url) - access = manager.AuthManager().get_access_key(user, project) - signature = signer.Signer(user.secret.encode()).s3_authorization(headers, - 'GET', - url_path) - headers['Authorization'] = 'AWS %s:%s' % (access, signature) - - if sys.platform.startswith('win'): - return _fetch_image_no_curl(url, path, headers) - else: - cmd = ['/usr/bin/curl', '--fail', '--silent', url] - for (k, v) in headers.iteritems(): - cmd += ['-H', '\'%s: %s\'' % (k, v)] - - cmd += ['-o', path] - return utils.execute(*cmd) - - -def _fetch_local_image(image, path, user, project): - source = _image_path(os.path.join(image, 'image')) - if sys.platform.startswith('win'): - return shutil.copy(source, path) - else: - return utils.execute('cp', source, path) - - -def _image_path(path): - return os.path.join(FLAGS.images_path, path) - - # TODO(vish): xenapi should use the glance client code directly instead # of retrieving the image using this method. def image_url(image): -- cgit From d087e1d0f0e235de01a8f140815fbe905008cb36 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 9 May 2011 09:08:56 -0700 Subject: capabilities flattened and tests fixed --- nova/virt/xenapi_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 563a1da77..671a340c7 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -505,8 +505,8 @@ class HostState(object): data["disk_total"] = total data["disk_used"] = used data["disk_available"] = total - used - memory = data.get('host_memory', None) - if memory: + host_memory = data.get('host_memory', None) + if host_memory: data["host_memory_total"] = host_memory.get('total', 0) data["host_memory_overhead"] = host_memory.get('overhead', 0) data["host_memory_free"] = host_memory.get('free', 0) -- cgit From 559bba1270378a430cc85abec144c0c574e65294 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 9 May 2011 12:57:56 -0700 Subject: unified underscore/dash issue --- nova/virt/xenapi_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 671a340c7..8e9085277 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -510,7 +510,7 @@ class HostState(object): data["host_memory_total"] = host_memory.get('total', 0) data["host_memory_overhead"] = host_memory.get('overhead', 0) data["host_memory_free"] = host_memory.get('free', 0) - data["host_memory_free-computed"] = \ + data["host_memory_free_computed"] = \ host_memory.get('free-computed', 0) del data['host_memory'] self._stats = data -- cgit From 09b795b8d6f0b925dbd4bcd203f471607c42f368 Mon Sep 17 00:00:00 2001 From: William Wolf Date: Mon, 9 May 2011 19:46:15 -0400 Subject: got rid of unnecessary imports --- nova/virt/images.py | 9 --------- 1 file changed, 9 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 1eb6f4a5f..8689c0ed3 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -21,19 +21,10 @@ Handling of VM disk images. """ -import os.path -import shutil -import sys -import time -import urllib2 -import urlparse - from nova import context from nova import flags from nova import log as logging from nova import utils -from nova.auth import manager -from nova.auth import signer FLAGS = flags.FLAGS -- cgit From aa73995f9ba7e4aaaee00ddd0db0b92dcd92dd54 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 10 May 2011 16:11:59 +0000 Subject: Change xenapi's wait_for_task to handle multiple simultaenous queries to fix lp:766404 --- nova/virt/xenapi_conn.py | 86 ++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0cabccf08..5dc6b034f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -326,7 +326,6 @@ class XenAPISession(object): "(is the Dom0 disk full?)")) with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): self._session.login_with_password(user, pw) - self.loop = None def get_imported_xenapi(self): """Stubout point. This can be replaced with a mock xenapi module.""" @@ -363,57 +362,52 @@ class XenAPISession(object): def wait_for_task(self, task, id=None): """Return the result of the given task. The task is polled - until it completes. Not re-entrant.""" + until it completes.""" done = event.Event() - self.loop = utils.LoopingCall(self._poll_task, id, task, done) - self.loop.start(FLAGS.xenapi_task_poll_interval, now=True) - rv = done.wait() - self.loop.stop() - return rv - - def _stop_loop(self): - """Stop polling for task to finish.""" - #NOTE(sandy-walsh) Had to break this call out to support unit tests. - if self.loop: - self.loop.stop() + loop = utils.LoopingCall(f=None) + + def _poll_task(): + """Poll the given XenAPI task, and return the result if the + action was completed successfully or not. + """ + try: + name = self._session.xenapi.task.get_name_label(task) + status = self._session.xenapi.task.get_status(task) + if id: + action = dict( + instance_id=int(id), + action=name[0:255], # Ensure action is never > 255 + error=None) + if status == "pending": + return + elif status == "success": + result = self._session.xenapi.task.get_result(task) + LOG.info(_("Task [%(name)s] %(task)s status:" + " success %(result)s") % locals()) + done.send(_parse_xmlrpc_value(result)) + else: + error_info = self._session.xenapi.task.get_error_info(task) + action["error"] = str(error_info) + LOG.warn(_("Task [%(name)s] %(task)s status:" + " %(status)s %(error_info)s") % locals()) + done.send_exception(self.XenAPI.Failure(error_info)) + + if id: + db.instance_action_create(context.get_admin_context(), + action) + except self.XenAPI.Failure, exc: + LOG.warn(exc) + done.send_exception(*sys.exc_info()) + loop.stop() + + loop.f = _poll_task + loop.start(FLAGS.xenapi_task_poll_interval, now=True) + return done.wait() def _create_session(self, url): """Stubout point. This can be replaced with a mock session.""" return self.XenAPI.Session(url) - def _poll_task(self, id, task, done): - """Poll the given XenAPI task, and fire the given action if we - get a result. - """ - try: - name = self._session.xenapi.task.get_name_label(task) - status = self._session.xenapi.task.get_status(task) - if id: - action = dict( - instance_id=int(id), - action=name[0:255], # Ensure action is never > 255 - error=None) - if status == "pending": - return - elif status == "success": - result = self._session.xenapi.task.get_result(task) - LOG.info(_("Task [%(name)s] %(task)s status:" - " success %(result)s") % locals()) - done.send(_parse_xmlrpc_value(result)) - else: - error_info = self._session.xenapi.task.get_error_info(task) - action["error"] = str(error_info) - LOG.warn(_("Task [%(name)s] %(task)s status:" - " %(status)s %(error_info)s") % locals()) - done.send_exception(self.XenAPI.Failure(error_info)) - - if id: - db.instance_action_create(context.get_admin_context(), action) - except self.XenAPI.Failure, exc: - LOG.warn(exc) - done.send_exception(*sys.exc_info()) - self._stop_loop() - def _unwrap_plugin_exceptions(self, func, *args, **kwargs): """Parse exception details""" try: -- cgit From 43fa5afac9e5af74e2e3977a5dafd9640d064cf1 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 11 May 2011 15:12:12 +0000 Subject: Abstract out IPv6 address generation to pluggable backends --- nova/virt/libvirt_conn.py | 3 ++- nova/virt/xenapi/vmops.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9780c69a6..4dce3b41f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -60,6 +60,7 @@ from nova import flags from nova import log as logging from nova import utils from nova import vnc +from nova import ipv6 from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state @@ -185,7 +186,7 @@ def _get_network_info(instance): prefix = network['cidr_v6'] mac = instance['mac_address'] return { - 'ip': utils.to_global_ipv6(prefix, mac), + 'ip': ipv6.to_global(prefix, mac), 'netmask': network['netmask_v6'], 'enabled': '1'} diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fe9a74dd6..0b05e702a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -34,6 +34,7 @@ from nova import log as logging from nova import exception from nova import utils from nova import flags +from nova import ipv6 from nova.auth.manager import AuthManager from nova.compute import power_state @@ -808,7 +809,7 @@ class VMOps(object): def ip6_dict(): return { - "ip": utils.to_global_ipv6(network['cidr_v6'], + "ip": ipv6.to_global(network['cidr_v6'], instance['mac_address']), "netmask": network['netmask_v6'], "enabled": "1"} -- cgit From d2b8350a026e0f00eae7cadbacaa15d4b44331af Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 11 May 2011 21:04:40 +0000 Subject: Implement IPv6 address generation that includes account identifier --- nova/virt/libvirt_conn.py | 3 ++- nova/virt/xenapi/vmops.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index cde864b0d..80e1a1f85 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -185,8 +185,9 @@ def _get_network_info(instance): def ip6_dict(): prefix = network['cidr_v6'] mac = instance['mac_address'] + project_id = instance['project_id'] return { - 'ip': ipv6.to_global(prefix, mac), + 'ip': ipv6.to_global(prefix, mac, project_id), 'netmask': network['netmask_v6'], 'enabled': '1'} diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0b05e702a..cc2b54331 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -810,7 +810,8 @@ class VMOps(object): def ip6_dict(): return { "ip": ipv6.to_global(network['cidr_v6'], - instance['mac_address']), + instance['mac_address'], + instance['project_id']), "netmask": network['netmask_v6'], "enabled": "1"} -- cgit From fd8b9eb204b77da583f1aee4022920367730823f Mon Sep 17 00:00:00 2001 From: Renuka Apte Date: Wed, 11 May 2011 17:06:56 -0700 Subject: Fix remote volume code --- nova/virt/xenapi/volume_utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 55c11a4ad..7821a4f7e 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -204,13 +204,16 @@ def _get_volume_id(path_or_id): if isinstance(path_or_id, int): return path_or_id # n must contain at least the volume_id - # /vol- is for remote volumes - # -vol- is for local volumes + # :volume- is for remote volumes + # -volume- is for local volumes # see compute/manager->setup_compute_volume - volume_id = path_or_id[path_or_id.find('/vol-') + 1:] + volume_id = path_or_id[path_or_id.find(':volume-') + 1:] if volume_id == path_or_id: volume_id = path_or_id[path_or_id.find('-volume--') + 1:] volume_id = volume_id.replace('volume--', '') + else: + volume_id = volume_id.replace('volume-', '') + volume_id = volume_id[0:volume_id.find('-')] return int(volume_id) -- cgit From ad3f578a37001957361014c7400dbe2e8ddd0baf Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Thu, 12 May 2011 17:44:07 +0400 Subject: Added network_info into refresh_security_group_rules --- nova/virt/libvirt_conn.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 555e44ce2..1e0a25a17 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1612,7 +1612,9 @@ class FirewallDriver(object): """ raise NotImplementedError() - def refresh_security_group_rules(self, security_group_id): + def refresh_security_group_rules(self, + security_group_id, + network_info=None): """Refresh security group rules from data store Gets called when a rule has been added to or removed from @@ -1911,7 +1913,9 @@ class NWFilterFirewall(FirewallDriver): self._define_filter(self._filter_container(filter_name, filter_children)) - def refresh_security_group_rules(self, security_group_id): + def refresh_security_group_rules(self, + security_group_id, + network_info=None): return self._define_filter( self.security_group_to_nwfilter_xml(security_group_id)) @@ -2169,15 +2173,19 @@ class IptablesFirewallDriver(FirewallDriver): def refresh_security_group_members(self, security_group): pass - def refresh_security_group_rules(self, security_group): - self.do_refresh_security_group_rules(security_group) + def refresh_security_group_rules(self, security_group, network_info=None): + self.do_refresh_security_group_rules(security_group, network_info) self.iptables.apply() @utils.synchronized('iptables', external=True) - def do_refresh_security_group_rules(self, security_group): + def do_refresh_security_group_rules(self, + security_group, + network_info=None): for instance in self.instances.values(): self.remove_filters_for_instance(instance) - self.add_filters_for_instance(instance) + if not network_info: + network_info = _get_network_info(instance) + self.add_filters_for_instance(instance, network_info) def _security_group_chain_name(self, security_group_id): return 'nova-sg-%s' % (security_group_id,) -- cgit From e7662bfcead8df8cc1fc655af6da15dc47777565 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Thu, 12 May 2011 13:42:04 -0700 Subject: fix for lp760921. Previously, if tune2fs failed, as it does on windows hosts, kpartx -d also failed to be called which leaves mapped partitions that retain holds on the nbd device. These holds cause the observed errors. --- nova/virt/disk.py | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/disk.py b/nova/virt/disk.py index ddea1a1f7..f8aea1f34 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -81,34 +81,36 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): else: mapped_device = device - # We can only loopback mount raw images. If the device isn't there, - # it's normally because it's a .vmdk or a .vdi etc - if not os.path.exists(mapped_device): - raise exception.Error('Mapped device was not found (we can' - ' only inject raw disk images): %s' % - mapped_device) - - # Configure ext2fs so that it doesn't auto-check every N boots - out, err = utils.execute('sudo', 'tune2fs', - '-c', 0, '-i', 0, mapped_device) - - tmpdir = tempfile.mkdtemp() try: - # mount loopback to dir - out, err = utils.execute( - 'sudo', 'mount', mapped_device, tmpdir) - if err: - raise exception.Error(_('Failed to mount filesystem: %s') - % err) - + # We can only loopback mount raw images. If the device isn't there, + # it's normally because it's a .vmdk or a .vdi etc + if not os.path.exists(mapped_device): + raise exception.Error('Mapped device was not found (we can' + ' only inject raw disk images): %s' % + mapped_device) + + # Configure ext2fs so that it doesn't auto-check every N boots + out, err = utils.execute('sudo', 'tune2fs', + '-c', 0, '-i', 0, mapped_device) + + tmpdir = tempfile.mkdtemp() try: - inject_data_into_fs(tmpdir, key, net, utils.execute) + # mount loopback to dir + out, err = utils.execute( + 'sudo', 'mount', mapped_device, tmpdir) + if err: + raise exception.Error(_('Failed to mount filesystem: %s') + % err) + + try: + inject_data_into_fs(tmpdir, key, net, utils.execute) + finally: + # unmount device + utils.execute('sudo', 'umount', mapped_device) finally: - # unmount device - utils.execute('sudo', 'umount', mapped_device) + # remove temporary directory + utils.execute('rmdir', tmpdir) finally: - # remove temporary directory - utils.execute('rmdir', tmpdir) if not partition is None: # remove partitions utils.execute('sudo', 'kpartx', '-d', device) -- cgit From 1aad930383fa425b88e59929aa1698e31978eb62 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 12 May 2011 22:19:52 +0000 Subject: Make sure imports are in alphabetical order --- nova/virt/libvirt_conn.py | 2 +- nova/virt/xenapi/vmops.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 80e1a1f85..6ee23d1df 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -57,10 +57,10 @@ from nova import context from nova import db from nova import exception from nova import flags +from nova import ipv6 from nova import log as logging from nova import utils from nova import vnc -from nova import ipv6 from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index cc2b54331..13d7d215b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -28,13 +28,13 @@ import subprocess import tempfile import uuid -from nova import db from nova import context -from nova import log as logging +from nova import db from nova import exception -from nova import utils from nova import flags from nova import ipv6 +from nova import log as logging +from nova import utils from nova.auth.manager import AuthManager from nova.compute import power_state -- cgit From f51bd03c9ce5f4248cb6f10e3ed662ae6ba33ebd Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Tue, 17 May 2011 15:49:31 +0000 Subject: Instead of using a temp file with openssl, just write directly to stdin --- nova/virt/xenapi/vmops.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 13d7d215b..0074444f8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -25,7 +25,6 @@ import M2Crypto import os import pickle import subprocess -import tempfile import uuid from nova import context @@ -1163,18 +1162,17 @@ class SimpleDH(object): return mpi def _run_ssl(self, text, which): - base_cmd = ('cat %(tmpfile)s | openssl enc -aes-128-cbc ' - '-a -pass pass:%(shared)s -nosalt %(dec_flag)s') + base_cmd = ('openssl enc -aes-128-cbc -a -pass pass:%(shared)s ' + '-nosalt %(dec_flag)s') if which.lower()[0] == 'd': dec_flag = ' -d' else: dec_flag = '' - fd, tmpfile = tempfile.mkstemp() - os.close(fd) - file(tmpfile, 'w').write(text) shared = self._shared cmd = base_cmd % locals() proc = _runproc(cmd) + proc.stdin.write(text) + proc.stdin.close() proc.wait() err = proc.stderr.read() if err: -- cgit From 4d025ef1d2b2b97c13d710cb5080b78e246215bc Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 18 May 2011 11:27:39 -0500 Subject: Added missing xenhost plugin. --- nova/virt/xenapi_conn.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index eb572f295..6d828e109 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -169,15 +169,15 @@ class XenAPIConnection(driver.ComputeDriver): def __init__(self, url, user, pw): super(XenAPIConnection, self).__init__() - session = XenAPISession(url, user, pw) - self._vmops = VMOps(session) - self._volumeops = VolumeOps(session) + self._session = XenAPISession(url, user, pw) + self._vmops = VMOps(self._session) + self._volumeops = VolumeOps(self._session) self._host_state = None @property def HostState(self): if not self._host_state: - self._host_state = HostState(self.session) + self._host_state = HostState(self._session) return self._host_state def init_host(self, host): -- cgit From 99bab1b99bf4388a0dba89300c4fb71095681276 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 19 May 2011 16:25:57 -0400 Subject: Moved back templates and fixed pep8 issue. Template move was due to breaking packaging with template moves. That will need to happen in a later merge. --- nova/virt/cpuinfo.xml.template | 9 +++ nova/virt/libvirt.xml.template | 122 +++++++++++++++++++++++++++++++++ nova/virt/libvirt/connection.py | 5 +- nova/virt/libvirt/cpuinfo.xml.template | 9 --- nova/virt/libvirt/libvirt.xml.template | 122 --------------------------------- nova/virt/libvirt/netutils.py | 4 +- 6 files changed, 135 insertions(+), 136 deletions(-) create mode 100644 nova/virt/cpuinfo.xml.template create mode 100644 nova/virt/libvirt.xml.template delete mode 100644 nova/virt/libvirt/cpuinfo.xml.template delete mode 100644 nova/virt/libvirt/libvirt.xml.template (limited to 'nova/virt') diff --git a/nova/virt/cpuinfo.xml.template b/nova/virt/cpuinfo.xml.template new file mode 100644 index 000000000..48842b29d --- /dev/null +++ b/nova/virt/cpuinfo.xml.template @@ -0,0 +1,9 @@ + + $arch + $model + $vendor + +#for $var in $features + +#end for + diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template new file mode 100644 index 000000000..de2497a76 --- /dev/null +++ b/nova/virt/libvirt.xml.template @@ -0,0 +1,122 @@ + + ${name} + ${memory_kb} + +#if $type == 'lxc' + #set $disk_prefix = '' + #set $disk_bus = '' + exe + /sbin/init +#else if $type == 'uml' + #set $disk_prefix = 'ubd' + #set $disk_bus = 'uml' + uml + /usr/bin/linux + /dev/ubda +#else + #if $type == 'xen' + #set $disk_prefix = 'sd' + #set $disk_bus = 'scsi' + linux + /dev/xvda + #else + #set $disk_prefix = 'vd' + #set $disk_bus = 'virtio' + hvm + #end if + #if $getVar('rescue', False) + ${basepath}/kernel.rescue + ${basepath}/ramdisk.rescue + #else + #if $getVar('kernel', None) + ${kernel} + #if $type == 'xen' + ro + #else + root=/dev/vda console=ttyS0 + #end if + #if $getVar('ramdisk', None) + ${ramdisk} + #end if + #else + + #end if + #end if +#end if + + + + + ${vcpus} + +#if $type == 'lxc' + + + + +#else + #if $getVar('rescue', False) + + + + + + + + + + + #else + + + + + + #if $getVar('local', False) + + + + + + #end if + #end if +#end if + +#for $nic in $nics + + + + + + + +#if $getVar('nic.extra_params', False) + ${nic.extra_params} +#end if +#if $getVar('nic.gateway_v6', False) + +#end if + + +#end for + + + + + + + + + + + + + + + + +#if $getVar('vncserver_host', False) + +#end if + + diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 87ba3fec3..94a703954 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -57,7 +57,6 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import ipv6 from nova import log as logging from nova import utils from nova import vnc @@ -85,7 +84,7 @@ flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') flags.DEFINE_string('libvirt_xml_template', - utils.abspath('virt/libvirt/libvirt.xml.template'), + utils.abspath('virt/libvirt.xml.template'), 'Libvirt XML Template') flags.DEFINE_string('libvirt_type', 'kvm', @@ -108,7 +107,7 @@ flags.DEFINE_string('firewall_driver', 'nova.virt.libvirt.firewall.IptablesFirewallDriver', 'Firewall driver (defaults to iptables)') flags.DEFINE_string('cpuinfo_xml_template', - utils.abspath('virt/libvirt/cpuinfo.xml.template'), + utils.abspath('virt/cpuinfo.xml.template'), 'CpuInfo XML Template (Used only live migration now)') flags.DEFINE_string('live_migration_uri', "qemu+tcp://%s/system", diff --git a/nova/virt/libvirt/cpuinfo.xml.template b/nova/virt/libvirt/cpuinfo.xml.template deleted file mode 100644 index 48842b29d..000000000 --- a/nova/virt/libvirt/cpuinfo.xml.template +++ /dev/null @@ -1,9 +0,0 @@ - - $arch - $model - $vendor - -#for $var in $features - -#end for - diff --git a/nova/virt/libvirt/libvirt.xml.template b/nova/virt/libvirt/libvirt.xml.template deleted file mode 100644 index de2497a76..000000000 --- a/nova/virt/libvirt/libvirt.xml.template +++ /dev/null @@ -1,122 +0,0 @@ - - ${name} - ${memory_kb} - -#if $type == 'lxc' - #set $disk_prefix = '' - #set $disk_bus = '' - exe - /sbin/init -#else if $type == 'uml' - #set $disk_prefix = 'ubd' - #set $disk_bus = 'uml' - uml - /usr/bin/linux - /dev/ubda -#else - #if $type == 'xen' - #set $disk_prefix = 'sd' - #set $disk_bus = 'scsi' - linux - /dev/xvda - #else - #set $disk_prefix = 'vd' - #set $disk_bus = 'virtio' - hvm - #end if - #if $getVar('rescue', False) - ${basepath}/kernel.rescue - ${basepath}/ramdisk.rescue - #else - #if $getVar('kernel', None) - ${kernel} - #if $type == 'xen' - ro - #else - root=/dev/vda console=ttyS0 - #end if - #if $getVar('ramdisk', None) - ${ramdisk} - #end if - #else - - #end if - #end if -#end if - - - - - ${vcpus} - -#if $type == 'lxc' - - - - -#else - #if $getVar('rescue', False) - - - - - - - - - - - #else - - - - - - #if $getVar('local', False) - - - - - - #end if - #end if -#end if - -#for $nic in $nics - - - - - - - -#if $getVar('nic.extra_params', False) - ${nic.extra_params} -#end if -#if $getVar('nic.gateway_v6', False) - -#end if - - -#end for - - - - - - - - - - - - - - - - -#if $getVar('vncserver_host', False) - -#end if - - diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py index 9225d8929..4d596078a 100644 --- a/nova/virt/libvirt/netutils.py +++ b/nova/virt/libvirt/netutils.py @@ -26,6 +26,7 @@ import IPy from nova import context from nova import db from nova import flags +from nova import ipv6 from nova import utils @@ -46,6 +47,7 @@ def get_ip_version(cidr): net = IPy.IP(cidr) return int(net.version()) + def get_network_info(instance): # TODO(adiantum) If we will keep this function # we should cache network_info @@ -93,5 +95,3 @@ def get_network_info(instance): network_info.append((network, mapping)) return network_info - - -- cgit From b2db9895c271825d1a58ade9c6de85ac90f760a7 Mon Sep 17 00:00:00 2001 From: William Wolf Date: Thu, 19 May 2011 22:56:23 -0400 Subject: fixed pep8 issue --- nova/virt/images.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 8689c0ed3..02c898fda 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -42,6 +42,7 @@ def fetch(image_id, path, _user, _project): metadata = image_service.get(elevated, image_id, image_file) return metadata + # TODO(vish): xenapi should use the glance client code directly instead # of retrieving the image using this method. def image_url(image): -- cgit From 36a3b9dc172a109e1f17dbc531a574ebf9e37453 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 24 May 2011 21:41:44 +0000 Subject: need to strip newline from openssl stdout data --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0074444f8..8c80ce7b1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1177,7 +1177,7 @@ class SimpleDH(object): err = proc.stderr.read() if err: raise RuntimeError(_('OpenSSL error: %s') % err) - return proc.stdout.read() + return proc.stdout.read().strip('\n') def encrypt(self, text): return self._run_ssl(text, 'enc') -- cgit From 0acbf6d77f02ca0fa3a11e29a55bbb617c33a816 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 24 May 2011 22:13:59 +0000 Subject: DHSimple's decrypt needs to append \n when writing to stdin --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8c80ce7b1..45b04351d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1171,7 +1171,7 @@ class SimpleDH(object): shared = self._shared cmd = base_cmd % locals() proc = _runproc(cmd) - proc.stdin.write(text) + proc.stdin.write(text + '\n') proc.stdin.close() proc.wait() err = proc.stderr.read() -- cgit From e0aa1369d8050f023fee1e60b276d44a6298feb9 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Tue, 24 May 2011 21:09:43 -0700 Subject: instead of the API spawning a greenthread to wait for a host to be picked, the instance to boot, etc for setting the admin password... let's push the admin password down to the scheduler so that compute can just take care of setting the password as a part of the build process. --- nova/virt/xenapi/vmops.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 45b04351d..a16c6a0d8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -202,6 +202,13 @@ class VMOps(object): for path, contents in instance.injected_files: LOG.debug(_("Injecting file path: '%s'") % path) self.inject_file(instance, path, contents) + + def _set_admin_password(): + admin_password = instance.admin_password + if admin_password: + LOG.debug(_("Setting admin password")) + self.set_admin_password(instance, admin_password) + # NOTE(armando): Do we really need to do this in virt? # NOTE(tr3buchet): not sure but wherever we do it, we need to call # reset_network afterwards @@ -214,6 +221,7 @@ class VMOps(object): LOG.debug(_('Instance %s: booted'), instance_name) timer.stop() _inject_files() + _set_admin_password() return True except Exception, exc: LOG.warn(exc) -- cgit From 7387af3ab5a310f7c427f0257e531871f62f398d Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 25 May 2011 14:57:52 +0000 Subject: Changed the exception type to not require an instance ID --- nova/virt/xenapi/vmops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 45b04351d..aaf5585b1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -253,7 +253,8 @@ class VMOps(object): instance_name = instance_or_vm.name vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance_obj.id) + raise exception.NotFound(_("No opaque_ref could be determined " + "for '%s'.") % instance_or_vm) return vm_ref def _acquire_bootlock(self, vm): -- cgit From ed582a8b86f81140affd88805ba9989b591577cd Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 25 May 2011 17:01:20 +0000 Subject: change install_ref.admin_password to instance_ref.admin_pass to match the DB --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a16c6a0d8..e2d453d21 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -204,7 +204,7 @@ class VMOps(object): self.inject_file(instance, path, contents) def _set_admin_password(): - admin_password = instance.admin_password + admin_password = instance.admin_pass if admin_password: LOG.debug(_("Setting admin password")) self.set_admin_password(instance, admin_password) -- cgit From b933f90faecaddf7281455f4824577b586e07f0c Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 25 May 2011 17:55:51 +0000 Subject: updating admin_pass moved down to compute where the password is actually reset. only update if it succeeds. --- nova/virt/xenapi/vmops.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c9396cffe..be6ef48ea 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -466,6 +466,9 @@ class VMOps(object): # Successful return code from password is '0' if resp_dict['returncode'] != '0': raise RuntimeError(resp_dict['message']) + db.instance_update(context.get_admin_context(), + instance['id'], + dict(admin_pass=new_pass)) return resp_dict['message'] def inject_file(self, instance, path, contents): -- cgit