193
193
'label': network['label'],
194
194
'gateway': network['gateway'],
195
'broadcast': network['broadcast'],
195
196
'mac': instance['mac_address'],
197
'rxtx_cap': flavor['rxtx_cap'],
196
198
'dns': [network['dns']],
197
199
'ips': [ip_dict(ip) for ip in network_ips]}
311
312
def destroy(self, instance, cleanup=True):
312
313
instance_name = instance['name']
314
# TODO(justinsb): Refactor all lookupByName calls for error-handling
316
virt_dom = self._conn.lookupByName(instance_name)
317
except libvirt.libvirtError as e:
318
errcode = e.get_error_code()
319
if errcode == libvirt.VIR_ERR_NO_DOMAIN:
322
LOG.warning(_("Error from libvirt during lookup of "
323
"%(instance_name)s. Code=%(errcode)s "
316
virt_dom = self._lookup_by_name(instance_name)
317
except exception.NotFound:
328
320
# If the instance is already terminated, we're still happy
329
321
# Otherwise, destroy it
364
# We'll save this for when we do shutdown,
365
# instead of destroy - but destroy returns immediately
366
timer = utils.LoopingCall(f=None)
356
def _wait_for_destroy():
357
"""Called at an interval until the VM is gone."""
358
instance_name = instance['name']
370
state = self.get_info(instance['name'])['state']
371
db.instance_set_state(context.get_admin_context(),
372
instance['id'], state)
373
if state == power_state.SHUTOFF:
375
except Exception as ex:
376
msg = _("Error encountered when destroying instance '%(id)s': "
377
"%(ex)s") % {"id": instance["id"], "ex": ex}
379
db.instance_set_state(context.get_admin_context(),
361
state = self.get_info(instance_name)['state']
362
except exception.NotFound:
363
msg = _("Instance %s destroyed successfully.") % instance_name
365
raise utils.LoopingCallDone
367
timer = utils.LoopingCall(_wait_for_destroy)
368
timer.start(interval=0.5, now=True)
384
370
self.firewall_driver.unfilter_instance(instance)
401
387
@exception.wrap_exception
402
388
def attach_volume(self, instance_name, device_path, mountpoint):
403
virt_dom = self._conn.lookupByName(instance_name)
389
virt_dom = self._lookup_by_name(instance_name)
404
390
mount_device = mountpoint.rpartition("/")[2]
405
391
if device_path.startswith('/dev/'):
406
392
xml = """<disk type='block'>
437
423
if child.prop('dev') == device:
441
427
ctx.xpathFreeContext()
445
431
@exception.wrap_exception
446
432
def detach_volume(self, instance_name, mountpoint):
447
virt_dom = self._conn.lookupByName(instance_name)
433
virt_dom = self._lookup_by_name(instance_name)
448
434
mount_device = mountpoint.rpartition("/")[2]
449
435
xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device)
451
raise exception.NotFound(_("No disk at %s") % mount_device)
437
raise exception.DiskNotFound(location=mount_device)
452
438
virt_dom.detachDevice(xml)
454
440
@exception.wrap_exception
469
455
metadata = {'disk_format': base['disk_format'],
470
456
'container_format': base['container_format'],
471
457
'is_public': False,
458
'name': '%s.%s' % (base['name'], image_id),
472
459
'properties': {'architecture': base['architecture'],
473
'name': '%s.%s' % (base['name'], image_id),
474
460
'kernel_id': instance['kernel_id'],
475
461
'image_location': 'snapshot',
476
462
'image_state': 'available',
497
483
# Export the snapshot to a raw image
498
484
temp_dir = tempfile.mkdtemp()
499
485
out_path = os.path.join(temp_dir, snapshot_name)
500
qemu_img_cmd = '%s convert -f qcow2 -O raw -s %s %s %s' % (
505
utils.execute(qemu_img_cmd)
486
qemu_img_cmd = (FLAGS.qemu_img,
496
utils.execute(*qemu_img_cmd)
507
498
# Upload that image to the image service
508
499
with open(out_path) as image_file:
517
508
@exception.wrap_exception
518
509
def reboot(self, instance):
510
"""Reboot a virtual machine, given an instance reference.
512
This method actually destroys and re-creates the domain to ensure the
513
reboot happens, as the guest OS cannot ignore this action.
516
virt_dom = self._conn.lookupByName(instance['name'])
517
# NOTE(itoumsn): Use XML delived from the running instance
518
# instead of using to_xml(instance). This is almost the ultimate
520
xml = virt_dom.XMLDesc(0)
521
# NOTE(itoumsn): self.shutdown() and wait instead of self.destroy() is
522
# better because we cannot ensure flushing dirty buffers
523
# in the guest OS. But, in case of KVM, shutdown() does not work...
519
524
self.destroy(instance, False)
520
xml = self.to_xml(instance)
521
525
self.firewall_driver.setup_basic_filtering(instance)
522
526
self.firewall_driver.prepare_instance_filter(instance)
523
527
self._create_new_domain(xml)
524
528
self.firewall_driver.apply_instance_filter(instance)
526
timer = utils.LoopingCall(f=None)
528
530
def _wait_for_reboot():
531
"""Called at an interval until the VM is running again."""
532
instance_name = instance['name']
530
state = self.get_info(instance['name'])['state']
531
db.instance_set_state(context.get_admin_context(),
532
instance['id'], state)
533
if state == power_state.RUNNING:
534
LOG.debug(_('instance %s: rebooted'), instance['name'])
536
except Exception, exn:
537
LOG.exception(_('_wait_for_reboot failed: %s'), exn)
538
db.instance_set_state(context.get_admin_context(),
540
power_state.SHUTDOWN)
543
timer.f = _wait_for_reboot
535
state = self.get_info(instance_name)['state']
536
except exception.NotFound:
537
msg = _("During reboot, %s disappeared.") % instance_name
539
raise utils.LoopingCallDone
541
if state == power_state.RUNNING:
542
msg = _("Instance %s rebooted successfully.") % instance_name
544
raise utils.LoopingCallDone
546
timer = utils.LoopingCall(_wait_for_reboot)
544
547
return timer.start(interval=0.5, now=True)
546
549
@exception.wrap_exception
560
563
raise exception.ApiError("resume not supported for libvirt")
562
565
@exception.wrap_exception
563
def rescue(self, instance, callback=None):
566
def rescue(self, instance):
567
"""Loads a VM using rescue images.
569
A rescue is normally performed when something goes wrong with the
570
primary images and data needs to be corrected/recovered. Rescuing
571
should not edit or over-ride the original image, only allow for
564
575
self.destroy(instance, False)
566
577
xml = self.to_xml(instance, rescue=True)
570
581
self._create_image(instance, xml, '.rescue', rescue_images)
571
582
self._create_new_domain(xml)
573
timer = utils.LoopingCall(f=None)
575
584
def _wait_for_rescue():
585
"""Called at an interval until the VM is running again."""
586
instance_name = instance['name']
577
state = self.get_info(instance['name'])['state']
578
db.instance_set_state(None, instance['id'], state)
579
if state == power_state.RUNNING:
580
LOG.debug(_('instance %s: rescued'), instance['name'])
582
except Exception, exn:
583
LOG.exception(_('_wait_for_rescue failed: %s'), exn)
584
db.instance_set_state(None,
586
power_state.SHUTDOWN)
589
timer.f = _wait_for_rescue
589
state = self.get_info(instance_name)['state']
590
except exception.NotFound:
591
msg = _("During reboot, %s disappeared.") % instance_name
593
raise utils.LoopingCallDone
595
if state == power_state.RUNNING:
596
msg = _("Instance %s rescued successfully.") % instance_name
598
raise utils.LoopingCallDone
600
timer = utils.LoopingCall(_wait_for_rescue)
590
601
return timer.start(interval=0.5, now=True)
592
603
@exception.wrap_exception
593
def unrescue(self, instance, callback=None):
594
# NOTE(vish): Because reboot destroys and recreates an instance using
595
# the normal xml file, we can just call reboot here
604
def unrescue(self, instance):
605
"""Reboot the VM which is being rescued back into primary images.
607
Because reboot destroys and re-creates instances, unresue should
596
611
self.reboot(instance)
598
613
@exception.wrap_exception
603
618
# for xenapi(tr3buchet)
604
619
@exception.wrap_exception
605
620
def spawn(self, instance, network_info=None):
606
xml = self.to_xml(instance, network_info)
607
db.instance_set_state(context.get_admin_context(),
621
xml = self.to_xml(instance, False, network_info)
611
622
self.firewall_driver.setup_basic_filtering(instance, network_info)
612
623
self.firewall_driver.prepare_instance_filter(instance, network_info)
613
self._create_image(instance, xml, network_info)
624
self._create_image(instance, xml, network_info=network_info)
614
625
domain = self._create_new_domain(xml)
615
626
LOG.debug(_("instance %s: is running"), instance['name'])
616
627
self.firewall_driver.apply_instance_filter(instance)
620
631
instance['name'])
621
632
domain.setAutostart(1)
623
timer = utils.LoopingCall(f=None)
625
634
def _wait_for_boot():
635
"""Called at an interval until the VM is running."""
636
instance_name = instance['name']
627
state = self.get_info(instance['name'])['state']
628
db.instance_set_state(context.get_admin_context(),
629
instance['id'], state)
630
if state == power_state.RUNNING:
631
LOG.debug(_('instance %s: booted'), instance['name'])
634
LOG.exception(_('instance %s: failed to boot'),
636
db.instance_set_state(context.get_admin_context(),
638
power_state.SHUTDOWN)
641
timer.f = _wait_for_boot
639
state = self.get_info(instance_name)['state']
640
except exception.NotFound:
641
msg = _("During reboot, %s disappeared.") % instance_name
643
raise utils.LoopingCallDone
645
if state == power_state.RUNNING:
646
msg = _("Instance %s spawned successfully.") % instance_name
648
raise utils.LoopingCallDone
650
timer = utils.LoopingCall(_wait_for_boot)
642
651
return timer.start(interval=0.5, now=True)
644
653
def _flush_xen_console(self, virsh_output):
729
738
@exception.wrap_exception
730
739
def get_vnc_console(self, instance):
731
740
def get_vnc_port_for_instance(instance_name):
732
virt_dom = self._conn.lookupByName(instance_name)
741
virt_dom = self._lookup_by_name(instance_name)
733
742
xml = virt_dom.XMLDesc(0)
734
743
# TODO: use etree instead of minidom
735
744
dom = minidom.parseString(xml)
951
960
mac_id = mapping['mac'].replace(':', '')
953
962
if FLAGS.allow_project_net_traffic:
963
template = "<parameter name=\"%s\"value=\"%s\" />\n"
964
net, mask = _get_net_and_mask(network['cidr'])
965
values = [("PROJNET", net), ("PROJMASK", mask)]
954
966
if FLAGS.use_ipv6:
955
net, mask = _get_net_and_mask(network['cidr'])
956
967
net_v6, prefixlen_v6 = _get_net_and_prefixlen(
957
968
network['cidr_v6'])
958
extra_params = ("<parameter name=\"PROJNET\" "
960
"<parameter name=\"PROJMASK\" "
962
"<parameter name=\"PROJNETV6\" "
964
"<parameter name=\"PROJMASKV6\" "
965
"value=\"%s\" />\n") % \
966
(net, mask, net_v6, prefixlen_v6)
968
net, mask = _get_net_and_mask(network['cidr'])
969
extra_params = ("<parameter name=\"PROJNET\" "
971
"<parameter name=\"PROJMASK\" "
972
"value=\"%s\" />\n") % \
969
values.extend([("PROJNETV6", net_v6),
970
("PROJMASKV6", prefixlen_v6)])
972
extra_params = "".join([template % value for value in values])
975
974
extra_params = "\n"
991
def to_xml(self, instance, rescue=False, network_info=None):
992
# TODO(termie): cache?
993
LOG.debug(_('instance %s: starting toXML method'), instance['name'])
990
def _prepare_xml_info(self, instance, rescue=False, network_info=None):
995
991
# TODO(adiantum) remove network_info creation code
996
992
# when multinics will be completed
997
993
if not network_info:
1001
997
for (network, mapping) in network_info:
1002
nics.append(self._get_nic_for_xml(network,
998
nics.append(self._get_nic_for_xml(network, mapping))
1004
999
# FIXME(vish): stick this in db
1005
1000
inst_type_id = instance['instance_type_id']
1006
1001
inst_type = instance_types.get_instance_type(inst_type_id)
1032
1027
xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk"
1034
1029
xml_info['disk'] = xml_info['basepath'] + "/disk"
1032
def to_xml(self, instance, rescue=False, network_info=None):
1033
# TODO(termie): cache?
1034
LOG.debug(_('instance %s: starting toXML method'), instance['name'])
1035
xml_info = self._prepare_xml_info(instance, rescue, network_info)
1036
1036
xml = str(Template(self.libvirt_xml, searchList=[xml_info]))
1037
LOG.debug(_('instance %s: finished toXML method'),
1037
LOG.debug(_('instance %s: finished toXML method'), instance['name'])
1040
def _lookup_by_name(self, instance_name):
1041
"""Retrieve libvirt domain object given an instance name.
1043
All libvirt error handling should be handled in this method and
1044
relevant nova exceptions should be raised in response.
1048
return self._conn.lookupByName(instance_name)
1049
except libvirt.libvirtError as ex:
1050
error_code = ex.get_error_code()
1051
if error_code == libvirt.VIR_ERR_NO_DOMAIN:
1052
msg = _("Instance %s not found") % instance_name
1053
raise exception.NotFound(msg)
1055
msg = _("Error from libvirt while looking up %(instance_name)s: "
1056
"[Error Code %(error_code)s] %(ex)s") % locals()
1057
raise exception.Error(msg)
1041
1059
def get_info(self, instance_name):
1042
# NOTE(justinsb): When libvirt isn't running / can't connect, we get:
1043
# libvir: Remote error : unable to connect to
1044
# '/var/run/libvirt/libvirt-sock', libvirtd may need to be started:
1045
# No such file or directory
1047
virt_dom = self._conn.lookupByName(instance_name)
1048
except libvirt.libvirtError as e:
1049
errcode = e.get_error_code()
1050
if errcode == libvirt.VIR_ERR_NO_DOMAIN:
1051
raise exception.NotFound(_("Instance %s not found")
1053
LOG.warning(_("Error from libvirt during lookup. "
1054
"Code=%(errcode)s Error=%(e)s") %
1060
"""Retrieve information from libvirt for a specific instance name.
1062
If a libvirt error is encountered during lookup, we might raise a
1063
NotFound exception or Error exception depending on how severe the
1067
virt_dom = self._lookup_by_name(instance_name)
1058
1068
(state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info()
1059
1069
return {'state': state,
1060
1070
'max_mem': max_mem,
1299
1309
xml = libxml2.parseDoc(xml)
1300
1310
nodes = xml.xpathEval('//host/cpu')
1301
1311
if len(nodes) != 1:
1302
raise exception.Invalid(_("Invalid xml. '<cpu>' must be 1,"
1303
"but %d\n") % len(nodes)
1312
reason = _("'<cpu>' must be 1, but %d\n") % len(nodes)
1313
reason += xml.serialize()
1314
raise exception.InvalidCPUInfo(reason=reason)
1306
1316
cpu_info = dict()
1348
1357
Note that this function takes an instance name, not an Instance, so
1349
1358
that it can be called by monitor.
1351
domain = self._conn.lookupByName(instance_name)
1360
domain = self._lookup_by_name(instance_name)
1352
1361
return domain.blockStats(disk)
1354
1363
def interface_stats(self, instance_name, interface):
1356
1365
Note that this function takes an instance name, not an Instance, so
1357
1366
that it can be called by monitor.
1359
domain = self._conn.lookupByName(instance_name)
1368
domain = self._lookup_by_name(instance_name)
1360
1369
return domain.interfaceStats(interface)
1362
1371
def get_console_pool_info(self, console_type):
1388
1397
service_ref = db.service_get_all_compute_by_host(ctxt, host)[0]
1389
1398
except exception.NotFound:
1390
raise exception.Invalid(_("Cannot update compute manager "
1391
"specific info, because no service "
1392
"record was found."))
1399
raise exception.ComputeServiceUnavailable(host=host)
1394
1401
# Updating host information
1395
1402
dic = {'vcpus': self.get_vcpu_total(),
1728
1735
logging.info('ensuring static filters')
1729
1736
self._ensure_static_filters()
1738
if instance['image_id'] == str(FLAGS.vpn_image_id):
1739
base_filter = 'nova-vpn'
1741
base_filter = 'nova-base'
1731
1743
for (network, mapping) in network_info:
1732
1744
nic_id = mapping['mac'].replace(':', '')
1733
1745
instance_filter_name = self._instance_filter_name(instance, nic_id)
1734
1746
self._define_filter(self._filter_container(instance_filter_name,
1737
1749
def _ensure_static_filters(self):
1738
1750
if self.static_filters_configured:
1743
1755
'no-ip-spoofing',
1744
1756
'no-arp-spoofing',
1745
1757
'allow-dhcp-server']))
1758
self._define_filter(self._filter_container('nova-vpn',
1759
['allow-dhcp-server']))
1746
1760
self._define_filter(self.nova_base_ipv4_filter)
1747
1761
self._define_filter(self.nova_base_ipv6_filter)
1748
1762
self._define_filter(self.nova_dhcp_filter)
1749
1763
self._define_filter(self.nova_ra_filter)
1750
self._define_filter(self.nova_vpn_filter)
1751
1764
if FLAGS.allow_project_net_traffic:
1752
1765
self._define_filter(self.nova_project_filter)
1753
1766
if FLAGS.use_ipv6:
1761
1774
''.join(["<filterref filter='%s'/>" % (f,) for f in filters]))
1764
nova_vpn_filter = '''<filter name='nova-vpn' chain='root'>
1765
<uuid>2086015e-cf03-11df-8c5d-080027c27973</uuid>
1766
<filterref filter='allow-dhcp-server'/>
1767
<filterref filter='nova-allow-dhcp-server'/>
1768
<filterref filter='nova-base-ipv4'/>
1769
<filterref filter='nova-base-ipv6'/>
1772
1777
def nova_base_ipv4_filter(self):
1773
1778
retval = "<filter name='nova-base-ipv4' chain='ipv4'>"
1774
1779
for protocol in ['tcp', 'udp', 'icmp']:
1846
1847
'nova-base-ipv6',
1847
1848
'nova-allow-dhcp-server']
1851
networks = [network for (network, _m) in network_info if
1852
network['gateway_v6']]
1855
instance_secgroup_filter_children.\
1856
append('nova-allow-ra-server')
1849
1858
for security_group in \
1850
1859
db.security_group_get_by_instance(ctxt, instance['id']):
1852
1861
self.refresh_security_group_rules(security_group['id'])
1854
instance_secgroup_filter_children += [('nova-secgroup-%s' %
1855
security_group['id'])]
1863
instance_secgroup_filter_children.append('nova-secgroup-%s' %
1864
security_group['id'])
1857
1866
self._define_filter(
1858
1867
self._filter_container(instance_secgroup_filter_name,
1859
1868
instance_secgroup_filter_children))
1861
for (network, mapping) in network_info:
1870
network_filters = self.\
1871
_create_network_filters(instance, network_info,
1872
instance_secgroup_filter_name)
1874
for (name, children) in network_filters:
1875
self._define_filters(name, children)
1877
def _create_network_filters(self, instance, network_info,
1878
instance_secgroup_filter_name):
1879
if instance['image_id'] == str(FLAGS.vpn_image_id):
1880
base_filter = 'nova-vpn'
1882
base_filter = 'nova-base'
1885
for (_n, mapping) in network_info:
1862
1886
nic_id = mapping['mac'].replace(':', '')
1863
1887
instance_filter_name = self._instance_filter_name(instance, nic_id)
1864
instance_filter_children = \
1865
[base_filter, instance_secgroup_filter_name]
1868
gateway_v6 = network['gateway_v6']
1871
instance_secgroup_filter_children += \
1872
['nova-allow-ra-server']
1888
instance_filter_children = [base_filter,
1889
instance_secgroup_filter_name]
1874
1891
if FLAGS.allow_project_net_traffic:
1875
instance_filter_children += ['nova-project']
1892
instance_filter_children.append('nova-project')
1876
1893
if FLAGS.use_ipv6:
1877
instance_filter_children += ['nova-project-v6']
1879
self._define_filter(
1880
self._filter_container(instance_filter_name,
1881
instance_filter_children))
1894
instance_filter_children.append('nova-project-v6')
1896
result.append((instance_filter_name, instance_filter_children))
1900
def _define_filters(self, filter_name, filter_children):
1901
self._define_filter(self._filter_container(filter_name,
1885
1904
def refresh_security_group_rules(self, security_group_id):
1886
1905
return self._define_filter(
1982
2001
self.add_filters_for_instance(instance, network_info)
1983
2002
self.iptables.apply()
1985
def add_filters_for_instance(self, instance, network_info=None):
1986
if not network_info:
1987
network_info = _get_network_info(instance)
1988
chain_name = self._instance_chain_name(instance)
1990
self.iptables.ipv4['filter'].add_chain(chain_name)
1992
ips_v4 = [ip['ip'] for (_, mapping) in network_info
1993
for ip in mapping['ips']]
1995
for ipv4_address in ips_v4:
1996
self.iptables.ipv4['filter'].add_rule('local',
1998
(ipv4_address, chain_name))
2001
self.iptables.ipv6['filter'].add_chain(chain_name)
2002
ips_v6 = [ip['ip'] for (_, mapping) in network_info
2003
for ip in mapping['ip6s']]
2005
for ipv6_address in ips_v6:
2006
self.iptables.ipv6['filter'].add_rule('local',
2011
ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info)
2004
def _create_filter(self, ips, chain_name):
2005
return ['-d %s -j $%s' % (ip, chain_name) for ip in ips]
2007
def _filters_for_instance(self, chain_name, network_info):
2008
ips_v4 = [ip['ip'] for (_n, mapping) in network_info
2009
for ip in mapping['ips']]
2010
ipv4_rules = self._create_filter(ips_v4, chain_name)
2012
ips_v6 = [ip['ip'] for (_n, mapping) in network_info
2013
for ip in mapping['ip6s']]
2015
ipv6_rules = self._create_filter(ips_v6, chain_name)
2016
return ipv4_rules, ipv6_rules
2018
def _add_filters(self, chain_name, ipv4_rules, ipv6_rules):
2013
2019
for rule in ipv4_rules:
2014
2020
self.iptables.ipv4['filter'].add_rule(chain_name, rule)
2017
2023
for rule in ipv6_rules:
2018
2024
self.iptables.ipv6['filter'].add_rule(chain_name, rule)
2026
def add_filters_for_instance(self, instance, network_info=None):
2027
chain_name = self._instance_chain_name(instance)
2029
self.iptables.ipv6['filter'].add_chain(chain_name)
2030
self.iptables.ipv4['filter'].add_chain(chain_name)
2031
ipv4_rules, ipv6_rules = self._filters_for_instance(chain_name,
2033
self._add_filters('local', ipv4_rules, ipv6_rules)
2034
ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info)
2035
self._add_filters(chain_name, ipv4_rules, ipv6_rules)
2020
2037
def remove_filters_for_instance(self, instance):
2021
2038
chain_name = self._instance_chain_name(instance)