libvirt: introduce a Guest to wrap around virDomain
Introduces object Guest to wrap around virDomain object and provide a higher level API. With the introduction of this new class also update _create_domain logic and update tests. Next patches will clean code in drivers to use this new implementation. Note about Copyrights, they have been copied from libvirt/driver.py and libvirt/test_driver.py since several parts of the code from those files will be moved in guests.py and test_guest.py Change-Id: I2d193ae44a4ac48ad863d237172cf2c1da05e3e9
This commit is contained in:
parent
93cbba96fa
commit
5ced65f873
|
@ -68,6 +68,9 @@ class NUMAServersTest(ServersTestBase):
|
|||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'nova.virt.libvirt.host.libvirt',
|
||||
fakelibvirt))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'nova.virt.libvirt.guest.libvirt',
|
||||
fakelibvirt))
|
||||
self.useFixture(fakelibvirt.FakeLibvirtFixture())
|
||||
|
||||
def _setup_compute_service(self):
|
||||
|
|
|
@ -86,6 +86,7 @@ from nova.virt.libvirt import blockinfo
|
|||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import driver as libvirt_driver
|
||||
from nova.virt.libvirt import firewall
|
||||
from nova.virt.libvirt import guest as libvirt_guest
|
||||
from nova.virt.libvirt import host
|
||||
from nova.virt.libvirt import imagebackend
|
||||
from nova.virt.libvirt import lvm
|
||||
|
@ -95,6 +96,7 @@ from nova.virt.libvirt import volume as volume_drivers
|
|||
|
||||
libvirt_driver.libvirt = fakelibvirt
|
||||
host.libvirt = fakelibvirt
|
||||
libvirt_guest.libvirt = fakelibvirt
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -602,7 +604,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
|
||||
return objects.Service(**service_ref)
|
||||
|
||||
def _get_launch_flags(self, drvr, network_info, power_on=True,
|
||||
def _get_pause_flag(self, drvr, network_info, power_on=True,
|
||||
vifs_already_plugged=False):
|
||||
timeout = CONF.vif_plugging_timeout
|
||||
|
||||
|
@ -613,9 +615,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
power_on and timeout):
|
||||
events = drvr._get_neutron_events(network_info)
|
||||
|
||||
launch_flags = events and fakelibvirt.VIR_DOMAIN_START_PAUSED or 0
|
||||
|
||||
return launch_flags
|
||||
return bool(events)
|
||||
|
||||
def test_public_api_signatures(self):
|
||||
baseinst = driver.ComputeDriver(None)
|
||||
|
@ -9957,29 +9957,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
dom_mock.ID.assert_called_once_with()
|
||||
mock_get_domain.assert_called_once_with(instance)
|
||||
|
||||
@mock.patch.object(encodeutils, 'safe_decode')
|
||||
def test_create_domain(self, mock_safe_decode):
|
||||
def test_create_domain(self):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
mock_domain = mock.MagicMock()
|
||||
mock_instance = mock.MagicMock()
|
||||
|
||||
domain = drvr._create_domain(domain=mock_domain,
|
||||
instance=mock_instance)
|
||||
domain = drvr._create_domain(domain=mock_domain)
|
||||
|
||||
self.assertEqual(mock_domain, domain)
|
||||
mock_domain.createWithFlags.assert_has_calls([mock.call(0)])
|
||||
# There is a global in oslo.log which calls encodeutils.safe_decode
|
||||
# which could be getting called from any number of places, so we need
|
||||
# to just assert that safe_decode was called at least twice in
|
||||
# _create_domain with the errors='ignore' kwarg.
|
||||
safe_decode_ignore_errors_calls = 0
|
||||
for call in mock_safe_decode.call_args_list:
|
||||
# call is a tuple where 0 is positional args and 1 is a kwargs dict
|
||||
if call[1].get('errors') == 'ignore':
|
||||
safe_decode_ignore_errors_calls += 1
|
||||
|
||||
self.assertTrue(safe_decode_ignore_errors_calls >= 2,
|
||||
'safe_decode should have been called at least twice')
|
||||
|
||||
@mock.patch('nova.virt.disk.api.clean_lxc_namespace')
|
||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.get_info')
|
||||
|
@ -10154,13 +10139,13 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
|
||||
self.log_error_called = False
|
||||
|
||||
def fake_error(msg, *args):
|
||||
def fake_error(msg, *args, **kwargs):
|
||||
self.log_error_called = True
|
||||
self.assertIn(fake_xml, msg % args)
|
||||
self.assertIn('safe decoded', msg % args)
|
||||
|
||||
self.stubs.Set(encodeutils, 'safe_decode', fake_safe_decode)
|
||||
self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error)
|
||||
self.stubs.Set(nova.virt.libvirt.guest.LOG, 'error', fake_error)
|
||||
|
||||
self.create_fake_libvirt_mock(defineXML=fake_defineXML)
|
||||
self.mox.ReplayAll()
|
||||
|
@ -10182,12 +10167,12 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
|
||||
self.log_error_called = False
|
||||
|
||||
def fake_error(msg, *args):
|
||||
def fake_error(msg, *args, **kwargs):
|
||||
self.log_error_called = True
|
||||
self.assertIn(fake_xml, msg % args)
|
||||
|
||||
self.stubs.Set(fake_domain, 'createWithFlags', fake_createWithFlags)
|
||||
self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error)
|
||||
self.stubs.Set(nova.virt.libvirt.guest.LOG, 'error', fake_error)
|
||||
|
||||
self.create_fake_libvirt_mock()
|
||||
self.mox.ReplayAll()
|
||||
|
@ -10204,21 +10189,27 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
fake_xml = "<test>this is a test</test>"
|
||||
fake_domain = FakeVirtDomain(fake_xml)
|
||||
|
||||
def fake_enable_hairpin(launch_flags):
|
||||
def fake_execute(*args, **kwargs):
|
||||
raise processutils.ProcessExecutionError('error')
|
||||
|
||||
def fake_get_interfaces(*args):
|
||||
return ["dev"]
|
||||
|
||||
self.log_error_called = False
|
||||
|
||||
def fake_error(msg, *args):
|
||||
def fake_error(msg, *args, **kwargs):
|
||||
self.log_error_called = True
|
||||
self.assertIn(fake_xml, msg % args)
|
||||
|
||||
self.stubs.Set(nova.virt.libvirt.driver.LOG, 'error', fake_error)
|
||||
self.stubs.Set(nova.virt.libvirt.guest.LOG, 'error', fake_error)
|
||||
|
||||
self.create_fake_libvirt_mock()
|
||||
self.mox.ReplayAll()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
self.stubs.Set(drvr, '_enable_hairpin', fake_enable_hairpin)
|
||||
self.stubs.Set(nova.utils, 'execute', fake_execute)
|
||||
self.stubs.Set(
|
||||
nova.virt.libvirt.guest.Guest, 'get_interfaces',
|
||||
fake_get_interfaces)
|
||||
|
||||
self.assertRaises(processutils.ProcessExecutionError,
|
||||
drvr._create_domain,
|
||||
|
@ -10584,7 +10575,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
_handler, cleanup, firewall_driver, create, plug_vifs):
|
||||
domain = drvr._create_domain_and_network(self.context, 'xml',
|
||||
instance, None, None)
|
||||
self.assertEqual(0, create.call_args_list[0][1]['launch_flags'])
|
||||
self.assertEqual(0, create.call_args_list[0][1]['pause'])
|
||||
self.assertEqual(0, domain.resume.call_count)
|
||||
|
||||
def _test_create_with_network_events(self, neutron_failure=None,
|
||||
|
@ -10628,10 +10619,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
power_on=power_on)
|
||||
plug_vifs.assert_called_with(instance, vifs)
|
||||
|
||||
flag = self._get_launch_flags(drvr, vifs, power_on=power_on)
|
||||
self.assertEqual(flag,
|
||||
create.call_args_list[0][1]['launch_flags'])
|
||||
if flag:
|
||||
pause = self._get_pause_flag(drvr, vifs, power_on=power_on)
|
||||
self.assertEqual(pause,
|
||||
create.call_args_list[0][1]['pause'])
|
||||
if pause:
|
||||
domain.resume.assert_called_once_with()
|
||||
if neutron_failure and CONF.vif_plugging_is_fatal:
|
||||
cleanup.assert_called_once_with(self.context,
|
||||
|
@ -10762,10 +10753,9 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
network_info)
|
||||
prepare_instance_filter.assert_called_once_with(instance,
|
||||
network_info)
|
||||
flags = self._get_launch_flags(drvr, network_info)
|
||||
create_domain.assert_called_once_with(fake_xml, instance=instance,
|
||||
launch_flags=flags,
|
||||
power_on=True)
|
||||
pause = self._get_pause_flag(drvr, network_info)
|
||||
create_domain.assert_called_once_with(
|
||||
fake_xml, pause=pause, power_on=True)
|
||||
self.assertEqual(mock_dom, domain)
|
||||
|
||||
def test_get_guest_storage_config(self):
|
||||
|
@ -11787,7 +11777,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||
self.assertEqual(powered_on, power_on)
|
||||
self.assertTrue(vifs_already_plugged)
|
||||
|
||||
def fake_enable_hairpin(instance):
|
||||
def fake_enable_hairpin():
|
||||
pass
|
||||
|
||||
def fake_execute(*args, **kwargs):
|
||||
|
@ -11811,7 +11801,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||
fake_create_image)
|
||||
self.stubs.Set(self.drvr, '_create_domain_and_network',
|
||||
fake_create_domain_and_network)
|
||||
self.stubs.Set(self.drvr, '_enable_hairpin',
|
||||
self.stubs.Set(nova.virt.libvirt.guest.Guest, 'enable_hairpin',
|
||||
fake_enable_hairpin)
|
||||
self.stubs.Set(utils, 'execute', fake_execute)
|
||||
fw = base_firewall.NoopFirewallDriver()
|
||||
|
@ -11867,7 +11857,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||
self.assertTrue(vifs_already_plugged)
|
||||
return mock.MagicMock()
|
||||
|
||||
def fake_enable_hairpin(instance):
|
||||
def fake_enable_hairpin():
|
||||
pass
|
||||
|
||||
def fake_get_info(instance):
|
||||
|
@ -11888,7 +11878,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||
self.stubs.Set(self.drvr, 'firewall_driver', fw)
|
||||
self.stubs.Set(self.drvr, '_create_domain_and_network',
|
||||
fake_create_domain)
|
||||
self.stubs.Set(self.drvr, '_enable_hairpin',
|
||||
self.stubs.Set(nova.virt.libvirt.guest.Guest, 'enable_hairpin',
|
||||
fake_enable_hairpin)
|
||||
self.stubs.Set(self.drvr, 'get_info',
|
||||
fake_get_info)
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
# Copyright 2010 OpenStack Foundation
|
||||
# Copyright 2012 University Of Minho
|
||||
# Copyright 2014-2015 Red Hat, 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.
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import encodeutils
|
||||
|
||||
from nova import context
|
||||
from nova import test
|
||||
from nova.tests.unit.virt.libvirt import fakelibvirt
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import guest as libvirt_guest
|
||||
from nova.virt.libvirt import host
|
||||
|
||||
|
||||
host.libvirt = fakelibvirt
|
||||
libvirt_guest.libvirt = fakelibvirt
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class GuestTestCase(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GuestTestCase, self).setUp()
|
||||
|
||||
self.useFixture(fakelibvirt.FakeLibvirtFixture())
|
||||
self.host = host.Host("qemu:///system")
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def test_repr(self):
|
||||
domain = mock.MagicMock()
|
||||
domain.ID.return_value = 99
|
||||
domain.UUIDString.return_value = "UUID"
|
||||
domain.name.return_value = "foo"
|
||||
|
||||
guest = libvirt_guest.Guest(domain)
|
||||
self.assertEqual("<Guest 99 foo UUID>", repr(guest))
|
||||
|
||||
@mock.patch.object(fakelibvirt.Connection, 'defineXML')
|
||||
def test_create(self, mock_define):
|
||||
libvirt_guest.Guest.create("xml", self.host)
|
||||
mock_define.assert_called_once_with("xml")
|
||||
|
||||
@mock.patch.object(fakelibvirt.Connection, 'defineXML')
|
||||
def test_create_exception(self, mock_define):
|
||||
mock_define.side_effect = test.TestingException
|
||||
self.assertRaises(test.TestingException,
|
||||
libvirt_guest.Guest.create,
|
||||
"foo", self.host)
|
||||
|
||||
def test_launch(self):
|
||||
domain = mock.MagicMock()
|
||||
|
||||
guest = libvirt_guest.Guest(domain)
|
||||
guest.launch()
|
||||
|
||||
domain.createWithFlags.assert_called_once_with(0)
|
||||
|
||||
def test_launch_and_pause(self):
|
||||
domain = mock.MagicMock()
|
||||
|
||||
guest = libvirt_guest.Guest(domain)
|
||||
guest.launch(pause=True)
|
||||
|
||||
domain.createWithFlags.assert_called_once_with(
|
||||
fakelibvirt.VIR_DOMAIN_START_PAUSED)
|
||||
|
||||
@mock.patch.object(encodeutils, 'safe_decode')
|
||||
def test_launch_exception(self, mock_safe_decode):
|
||||
domain = mock.MagicMock()
|
||||
domain.createWithFlags.side_effect = test.TestingException
|
||||
mock_safe_decode.return_value = "</xml>"
|
||||
|
||||
guest = libvirt_guest.Guest(domain)
|
||||
self.assertRaises(test.TestingException, guest.launch)
|
||||
self.assertEqual(1, mock_safe_decode.called)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(libvirt_guest.Guest, 'get_interfaces')
|
||||
def test_enable_hairpin(self, mock_get_interfaces, mock_execute):
|
||||
mock_get_interfaces.return_value = ["vnet0", "vnet1"]
|
||||
|
||||
guest = libvirt_guest.Guest(mock.MagicMock())
|
||||
guest.enable_hairpin()
|
||||
mock_execute.assert_has_calls([
|
||||
mock.call(
|
||||
'tee', '/sys/class/net/vnet0/brport/hairpin_mode',
|
||||
run_as_root=True, process_input='1', check_exit_code=[0, 1]),
|
||||
mock.call(
|
||||
'tee', '/sys/class/net/vnet1/brport/hairpin_mode',
|
||||
run_as_root=True, process_input='1', check_exit_code=[0, 1])])
|
||||
|
||||
@mock.patch.object(encodeutils, 'safe_decode')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(libvirt_guest.Guest, 'get_interfaces')
|
||||
def test_enable_hairpin_exception(self, mock_get_interfaces,
|
||||
mock_execute, mock_safe_decode):
|
||||
mock_get_interfaces.return_value = ["foo"]
|
||||
mock_execute.side_effect = test.TestingException('oops')
|
||||
|
||||
guest = libvirt_guest.Guest(mock.MagicMock())
|
||||
self.assertRaises(test.TestingException, guest.enable_hairpin)
|
||||
self.assertEqual(1, mock_safe_decode.called)
|
||||
|
||||
def test_get_interfaces(self):
|
||||
dom = mock.MagicMock()
|
||||
dom.XMLDesc.return_value = """
|
||||
<domain>
|
||||
<devices>
|
||||
<interface type="network">
|
||||
<target dev="vnet0"/>
|
||||
</interface>
|
||||
<interface type="network">
|
||||
<target dev="vnet1"/>
|
||||
</interface>
|
||||
</devices>
|
||||
</domain>"""
|
||||
guest = libvirt_guest.Guest(dom)
|
||||
self.assertEqual(["vnet0", "vnet1"], guest.get_interfaces())
|
||||
|
||||
def test_get_interfaces_exception(self):
|
||||
dom = mock.MagicMock()
|
||||
dom.XMLDesc.return_value = "<bad xml>"
|
||||
guest = libvirt_guest.Guest(dom)
|
||||
self.assertEqual([], guest.get_interfaces())
|
|
@ -47,7 +47,6 @@ from oslo_concurrency import processutils
|
|||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import strutils
|
||||
|
@ -93,6 +92,7 @@ from nova.virt.libvirt import blockinfo
|
|||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import dmcrypt
|
||||
from nova.virt.libvirt import firewall as libvirt_firewall
|
||||
from nova.virt.libvirt import guest as libvirt_guest
|
||||
from nova.virt.libvirt import host
|
||||
from nova.virt.libvirt import imagebackend
|
||||
from nova.virt.libvirt import imagecache
|
||||
|
@ -1403,8 +1403,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
if state == power_state.RUNNING:
|
||||
new_dom = self._create_domain(domain=virt_dom)
|
||||
elif state == power_state.PAUSED:
|
||||
new_dom = self._create_domain(domain=virt_dom,
|
||||
launch_flags=libvirt.VIR_DOMAIN_START_PAUSED)
|
||||
new_dom = self._create_domain(
|
||||
domain=virt_dom, pause=True)
|
||||
if new_dom is not None:
|
||||
self._attach_pci_devices(new_dom,
|
||||
pci_manager.get_instance_pci_devs(instance))
|
||||
|
@ -2353,15 +2353,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
def poll_rebooting_instances(self, timeout, instances):
|
||||
pass
|
||||
|
||||
def _enable_hairpin(self, xml):
|
||||
interfaces = self._get_interfaces(xml)
|
||||
for interface in interfaces:
|
||||
utils.execute('tee',
|
||||
'/sys/class/net/%s/brport/hairpin_mode' % interface,
|
||||
process_input='1',
|
||||
run_as_root=True,
|
||||
check_exit_code=[0, 1])
|
||||
|
||||
# NOTE(ilyaalekseyev): Implementation like in multinics
|
||||
# for xenapi(tr3buchet)
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
|
@ -4330,36 +4321,25 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
self._create_domain_cleanup_lxc(instance)
|
||||
|
||||
def _create_domain(self, xml=None, domain=None,
|
||||
instance=None, launch_flags=0, power_on=True):
|
||||
power_on=True, pause=False):
|
||||
"""Create a domain.
|
||||
|
||||
Either domain or xml must be passed in. If both are passed, then
|
||||
the domain definition is overwritten from the xml.
|
||||
"""
|
||||
err = None
|
||||
try:
|
||||
if xml:
|
||||
err = (_LE('Error defining a domain with XML: %s') %
|
||||
encodeutils.safe_decode(xml, errors='ignore'))
|
||||
domain = self._host.write_instance_config(xml)
|
||||
if xml:
|
||||
guest = libvirt_guest.Guest.create(xml, self._host)
|
||||
else:
|
||||
guest = libvirt_guest.Guest(domain)
|
||||
|
||||
if power_on:
|
||||
err = _LE('Error launching a defined domain with XML: %s') \
|
||||
% encodeutils.safe_decode(domain.XMLDesc(0),
|
||||
errors='ignore')
|
||||
domain.createWithFlags(launch_flags)
|
||||
if power_on or pause:
|
||||
guest.launch(pause=pause)
|
||||
|
||||
if not utils.is_neutron():
|
||||
err = _LE('Error enabling hairpin mode with XML: %s') \
|
||||
% encodeutils.safe_decode(domain.XMLDesc(0),
|
||||
errors='ignore')
|
||||
self._enable_hairpin(domain.XMLDesc(0))
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if err:
|
||||
LOG.error(err)
|
||||
if not utils.is_neutron():
|
||||
guest.enable_hairpin()
|
||||
|
||||
return domain
|
||||
# TODO(sahid): This method should return the Guest object
|
||||
return guest._domain
|
||||
|
||||
def _neutron_failed_callback(self, event_name, instance):
|
||||
LOG.error(_LE('Neutron Reported failure on event '
|
||||
|
@ -4410,7 +4390,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
else:
|
||||
events = []
|
||||
|
||||
launch_flags = events and libvirt.VIR_DOMAIN_START_PAUSED or 0
|
||||
pause = bool(events)
|
||||
domain = None
|
||||
try:
|
||||
with self.virtapi.wait_for_instance_event(
|
||||
|
@ -4424,9 +4404,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
with self._lxc_disk_handler(instance, image_meta,
|
||||
block_device_info, disk_info):
|
||||
domain = self._create_domain(
|
||||
xml, instance=instance,
|
||||
launch_flags=launch_flags,
|
||||
power_on=power_on)
|
||||
xml, pause=pause, power_on=power_on)
|
||||
|
||||
self.firewall_driver.apply_instance_filter(instance,
|
||||
network_info)
|
||||
|
@ -4450,7 +4428,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
raise exception.VirtualInterfaceCreateException()
|
||||
|
||||
# Resume only if domain has been paused
|
||||
if launch_flags & libvirt.VIR_DOMAIN_START_PAUSED:
|
||||
if pause:
|
||||
domain.resume()
|
||||
return domain
|
||||
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
# 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.
|
||||
# Copyright (c) 2011 Piston Cloud Computing, Inc
|
||||
# Copyright (c) 2012 University Of Minho
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright (c) 2015 Red Hat, 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.
|
||||
|
||||
"""
|
||||
Manages information about the guest.
|
||||
|
||||
This class encapsulates libvirt domain provides certain
|
||||
higher level APIs around the raw libvirt API. These APIs are
|
||||
then used by all the other libvirt related classes
|
||||
"""
|
||||
|
||||
from lxml import etree
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
|
||||
from nova.i18n import _LE
|
||||
from nova import utils
|
||||
|
||||
libvirt = None
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Guest(object):
|
||||
|
||||
def __init__(self, domain):
|
||||
|
||||
global libvirt
|
||||
if libvirt is None:
|
||||
libvirt = importutils.import_module('libvirt')
|
||||
|
||||
self._domain = domain
|
||||
|
||||
def __repr__(self):
|
||||
return "<Guest %(id)d %(name)s %(uuid)s>" % {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'uuid': self.uuid
|
||||
}
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._domain.ID()
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
return self._domain.UUIDString()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._domain.name()
|
||||
|
||||
@property
|
||||
def _encoded_xml(self):
|
||||
return encodeutils.safe_decode(self._domain.XMLDesc(0))
|
||||
|
||||
@classmethod
|
||||
def create(cls, xml, host):
|
||||
"""Create a new Guest
|
||||
|
||||
:param xml: XML definition of the domain to create
|
||||
:param host: host.Host connection to define the guest on
|
||||
|
||||
:returns guest.Guest: Guest ready to be launched
|
||||
"""
|
||||
try:
|
||||
# TODO(sahid): Host.write_instance_config should return
|
||||
# an instance of Guest
|
||||
domain = host.write_instance_config(xml)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Error defining a domain with XML: %s') %
|
||||
encodeutils.safe_decode(xml))
|
||||
return cls(domain)
|
||||
|
||||
def launch(self, pause=False):
|
||||
"""Starts a created guest.
|
||||
|
||||
:param pause: Indicates whether to start and pause the guest
|
||||
"""
|
||||
flags = pause and libvirt.VIR_DOMAIN_START_PAUSED or 0
|
||||
try:
|
||||
return self._domain.createWithFlags(flags)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Error launching a defined domain '
|
||||
'with XML: %s') %
|
||||
self._encoded_xml, errors='ignore')
|
||||
|
||||
def enable_hairpin(self):
|
||||
"""Enables hairpin mode for this guest."""
|
||||
interfaces = self.get_interfaces()
|
||||
try:
|
||||
for interface in interfaces:
|
||||
utils.execute(
|
||||
'tee',
|
||||
'/sys/class/net/%s/brport/hairpin_mode' % interface,
|
||||
process_input='1',
|
||||
run_as_root=True,
|
||||
check_exit_code=[0, 1])
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Error enabling hairpin mode with XML: %s') %
|
||||
self._encoded_xml, errors='ignore')
|
||||
|
||||
def get_interfaces(self):
|
||||
"""Returns a list of all network interfaces for this domain."""
|
||||
doc = None
|
||||
|
||||
try:
|
||||
doc = etree.fromstring(self._encoded_xml)
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
interfaces = []
|
||||
|
||||
nodes = doc.findall('./devices/interface/target')
|
||||
for target in nodes:
|
||||
interfaces.append(target.get('dev'))
|
||||
|
||||
return interfaces
|
Loading…
Reference in New Issue