Fuel plugins: Apply patchset to support neutron security group
Change-Id: Ic20774f23ebd4d08c7f3102029058a7cc977d029
This commit is contained in:
parent
7fbd82239e
commit
5178863ce7
|
@ -8,6 +8,7 @@ import re
|
|||
from socket import inet_ntoa
|
||||
from struct import pack
|
||||
import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
|
||||
|
@ -373,6 +374,24 @@ def enable_linux_bridge(himn, username, password):
|
|||
ssh(himn, username, password, 'rm -f /etc/modprobe.d/blacklist-bridge')
|
||||
|
||||
|
||||
def patch_compute_xenapi():
|
||||
"""replace folder xenapi to add patches which are not merged to upstream"""
|
||||
# TODO(huanxie): need to confirm the overall patchset list
|
||||
patchset_dir = sys.path[0]
|
||||
patchfile_list = ['%s/patchset/vif-plug.patch' % patchset_dir,
|
||||
'%s/patchset/nova-neutron-race-condition.patch' % patchset_dir,
|
||||
'%s/patchset/ovs-interim-bridge.patch' % patchset_dir,
|
||||
'%s/patchset/neutron-security-group.patch' % patchset_dir]
|
||||
for patch_file in patchfile_list:
|
||||
execute('patch', '-d', DIST_PACKAGES_DIR, '-p1', '-i', patch_file)
|
||||
|
||||
|
||||
def patch_neutron_ovs_agent():
|
||||
patchset_dir = sys.path[0]
|
||||
patch_file = '%s/patchset/neutron-rootwrap-xen-dom0.patch' % patchset_dir
|
||||
execute('patch', '-d', '/usr/', '-p1', '-i', patch_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
install_xenapi_sdk()
|
||||
astute = get_astute(ASTUTE_PATH)
|
||||
|
@ -397,6 +416,7 @@ if __name__ == '__main__':
|
|||
forward_port('br-mgmt', himn_eth, HIMN_IP, '80')
|
||||
|
||||
create_novacompute_conf(HIMN_IP, username, password, public_ip)
|
||||
patch_compute_xenapi()
|
||||
restart_services('nova-compute')
|
||||
|
||||
install_logrotate_script(HIMN_IP, username, password)
|
||||
|
@ -406,4 +426,5 @@ if __name__ == '__main__':
|
|||
br_mappings = find_bridge_mappings(astute, HIMN_IP,
|
||||
username, password)
|
||||
modify_neutron_ovs_agent_conf(INT_BRIDGE, br_mappings)
|
||||
patch_neutron_ovs_agent()
|
||||
restart_services('neutron-plugin-openvswitch-agent')
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
diff --git a/bin/neutron-rootwrap-xen-dom0 b/bin/neutron-rootwrap-xen-dom0
|
||||
index b4e2e31..1d73883 100755
|
||||
--- a/bin/neutron-rootwrap-xen-dom0
|
||||
+++ b/bin/neutron-rootwrap-xen-dom0
|
||||
@@ -113,11 +113,14 @@ def run_command(url, username, password, user_args, cmd_input):
|
||||
try:
|
||||
session = XenAPI.Session(url)
|
||||
session.login_with_password(username, password)
|
||||
- host = session.xenapi.session.get_this_host(session.handle)
|
||||
- result = session.xenapi.host.call_plugin(
|
||||
- host, 'netwrap', 'run_command',
|
||||
- {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)})
|
||||
- return json.loads(result)
|
||||
+ try:
|
||||
+ host = session.xenapi.session.get_this_host(session.handle)
|
||||
+ result = session.xenapi.host.call_plugin(
|
||||
+ host, 'netwrap', 'run_command',
|
||||
+ {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)})
|
||||
+ return json.loads(result)
|
||||
+ finally:
|
||||
+ session.xenapi.session.logout()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
sys.exit(RC_XENAPI_ERROR)
|
|
@ -0,0 +1,417 @@
|
|||
diff --git a/nova/tests/unit/virt/xenapi/test_vif.py b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
index 65e3070..a314746 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
@@ -22,6 +22,8 @@ from nova.network import model
|
||||
from nova.tests.unit.virt.xenapi import stubs
|
||||
from nova.virt.xenapi import network_utils
|
||||
from nova.virt.xenapi import vif
|
||||
+from nova.virt.xenapi import vm_utils
|
||||
+from oslo_serialization import jsonutils
|
||||
|
||||
fake_vif = {
|
||||
'created_at': None,
|
||||
@@ -194,69 +196,144 @@ class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase):
|
||||
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||
self.assertTrue(mock_create_vif_interim_network.called)
|
||||
|
||||
- @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
- return_value="fake_net_name")
|
||||
- @patch.object(vif.XenAPIOpenVswitchDriver, '_get_patch_port_pair_names',
|
||||
- return_value=("fake_port_name1", "fake_port_name2"))
|
||||
- @patch.object(network_utils, 'find_network_with_name_label',
|
||||
- return_value='fake_network')
|
||||
+ @patch.object(vm_utils, 'lookup', return_value='fake_vm_ref')
|
||||
@patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='fake_vif_ref')
|
||||
- def test_unplug(self, mock_get_vif_ref,
|
||||
+ @patch.object(network_utils, 'find_network_with_name_label',
|
||||
+ return_value='fake_network')
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, '_device_exists',
|
||||
+ return_value=True)
|
||||
+ def test_unplug(self, mock_lookup, mock_get_vif_ref,
|
||||
mock_find_network_with_name_label,
|
||||
- mock_get_patch_port_pair_names,
|
||||
- mock_get_vif_interim_net_name):
|
||||
+ mock_device_exists):
|
||||
instance = {'name': "fake_instance"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
self.ovs_driver.unplug(instance, fake_vif, vm_ref)
|
||||
|
||||
- del_p1 = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
- '"del-port", "fake_bridge", "fake_port_name1"]'
|
||||
- del_p2 = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
- '"del-port", "xapi1", "fake_port_name2"]'
|
||||
- del_br = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
- '"del-br", "fake_bridge"]'
|
||||
+ # interim bridge
|
||||
+ patch_port1 = ("vif%s" % fake_vif['id'])[:14]
|
||||
+ del_p1 = {'cmd': jsonutils.dumps(
|
||||
+ ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||
+ 'fake_bridge', patch_port1])}
|
||||
+ del_br = {'cmd': jsonutils.dumps(
|
||||
+ ['ovs-vsctl', '--', '--if-exists', 'del-br',
|
||||
+ 'fake_bridge'])}
|
||||
+
|
||||
+ # linux bridge
|
||||
+ qbr_name = ("qbr%s" % fake_vif['id'])[:14]
|
||||
+ tap_name = ("tap%s" % fake_vif['id'])[:14]
|
||||
+ qvb_name = ("qvb%s" % fake_vif['id'])[:14]
|
||||
+ del_tap = {'cmd': jsonutils.dumps(
|
||||
+ ['brctl', 'delif', qbr_name, tap_name])}
|
||||
+ del_link_tap = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'delete', tap_name])}
|
||||
+ del_qvb = {'cmd': jsonutils.dumps(
|
||||
+ ['brctl', 'delif', qbr_name, qvb_name])}
|
||||
+ del_link_qvb = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'delete', qvb_name])}
|
||||
+ down_link_qbr = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qbr_name, 'down'])}
|
||||
+ del_qbr = {'cmd': jsonutils.dumps(['brctl', 'delbr', qbr_name])}
|
||||
|
||||
expected_calls = [call.call_xenapi('VIF.destroy', 'fake_vif_ref'),
|
||||
call.call_xenapi('network.get_VIFs', 'fake_network'),
|
||||
call.call_xenapi('network.get_bridge', 'fake_network'),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': del_p1}),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': del_p2}),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_p1),
|
||||
call.call_xenapi('network.destroy', 'fake_network'),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': del_br})]
|
||||
+ call.call_plugin('xenhost', 'network_config', del_br),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_tap),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_link_tap),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_qvb),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_link_qvb),
|
||||
+ call.call_plugin('xenhost', 'network_config', down_link_qbr),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_qbr)]
|
||||
self._session.assert_has_calls(expected_calls)
|
||||
|
||||
- @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
- return_value="fake_net_name")
|
||||
- @patch.object(vif.XenAPIOpenVswitchDriver, '_get_patch_port_pair_names',
|
||||
- return_value=("fake_port_name1", "fake_port_name2"))
|
||||
- def test_post_start_actions(self, mock_get_patch_port_pair_names,
|
||||
- mock_get_vif_interim_net_name):
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, '_device_exists',
|
||||
+ return_value=False)
|
||||
+ def test_post_start_actions(self, mock_device_exists):
|
||||
vif_ref = "fake_vif_ref"
|
||||
instance = {'name': 'fake_instance_name'}
|
||||
self.ovs_driver.post_start_actions(instance, vif_ref)
|
||||
|
||||
- add_p1 = '["ovs-vsctl", "--", ' \
|
||||
- '"--if-exists", "del-port", "fake_port_name1", "--", ' \
|
||||
- '"add-port", "fake_bridge", "fake_port_name1", "--", ' \
|
||||
- '"set", "interface", "fake_port_name1", ' \
|
||||
- '"type=patch", "options:peer=fake_port_name2"]'
|
||||
- add_p2 = '["ovs-vsctl", "--", ' \
|
||||
- '"--if-exists", "del-port", "fake_port_name2", "--", ' \
|
||||
- '"add-port", "xapi1", "fake_port_name2", "--", ' \
|
||||
- '"set", "interface", "fake_port_name2", ' \
|
||||
- '"type=patch", "options:peer=fake_port_name1"]'
|
||||
- ext_map = '["ovs-vsctl", "set", "Interface", "fake_port_name2", ' \
|
||||
- '"external-ids:attached-mac=00:00:00:00:00:00", ' \
|
||||
- '"external-ids:iface-id=fake-nicira-iface-id", ' \
|
||||
- '"external-ids:iface-status=active", ' \
|
||||
- '"external-ids:xs-vif-uuid=fake_uuid"]'
|
||||
+ # create linux bridge
|
||||
+ qbr_name = ("qbr%s" % fake_vif['id'])[:14]
|
||||
+ add_qbr = {'cmd': jsonutils.dumps(['brctl', 'addbr', qbr_name])}
|
||||
+ set_fd = {'cmd': jsonutils.dumps(['brctl', 'setfd', qbr_name, '0'])}
|
||||
+ off_stp = {'cmd': jsonutils.dumps(['brctl', 'stp', qbr_name, 'off'])}
|
||||
+ up_qbr_link = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qbr_name, 'up'])}
|
||||
+
|
||||
+ # create veth pair, qvb/qvo
|
||||
+ qvb_name = ("qvb%s" % fake_vif['id'])[:14]
|
||||
+ qvo_name = ("qvo%s" % fake_vif['id'])[:14]
|
||||
+ qvb_qvo_veth_pair = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'add', qvb_name, 'type', 'veth',
|
||||
+ 'peer', 'name', qvo_name])}
|
||||
+ qvb_link_up = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qvb_name, 'up'])}
|
||||
+ qvb_promisc_on = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qvb_name, 'promisc', 'on'])}
|
||||
+ qvo_link_up = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qvo_name, 'up'])}
|
||||
+ qvo_promisc_on = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', qvo_name, 'promisc', 'on'])}
|
||||
+
|
||||
+ # add qvb/qvo port
|
||||
+ add_qvb = {'cmd': jsonutils.dumps(
|
||||
+ ['brctl', 'addif', qbr_name, qvb_name])}
|
||||
+ del_add_qvo = {'cmd': jsonutils.dumps(
|
||||
+ ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||
+ qvo_name, '--', 'add-port', 'xapi1', qvo_name])}
|
||||
+ ext_map = {'cmd': jsonutils.dumps(
|
||||
+ ["ovs-vsctl", "set", "Interface", qvo_name,
|
||||
+ "external-ids:attached-mac=%s" % fake_vif['address'],
|
||||
+ "external-ids:iface-id=%s" % fake_vif['id'],
|
||||
+ "external-ids:iface-status=active",
|
||||
+ "external-ids:xs-vif-uuid=%s" % fake_vif['uuid']])}
|
||||
+
|
||||
+ # create veth tap/patch port
|
||||
+ patch_port1 = ("vif%s" % fake_vif['id'])[:14]
|
||||
+ tap_port = ("tap%s" % fake_vif['id'])[:14]
|
||||
+ tap_patch_veth_pair = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'add', tap_port, 'type',
|
||||
+ 'veth', 'peer', 'name', patch_port1])}
|
||||
+ up_tap_link = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', tap_port, 'up'])}
|
||||
+ tap_promisc_on = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', tap_port, 'promisc', 'on'])}
|
||||
+ up_patch_link = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', patch_port1, 'up'])}
|
||||
+ patch_promisc_on = {'cmd': jsonutils.dumps(
|
||||
+ ['ip', 'link', 'set', patch_port1, 'promisc', 'on'])}
|
||||
+ add_tap = {'cmd': jsonutils.dumps(
|
||||
+ ['brctl', 'addif', qbr_name, tap_port])}
|
||||
+ add_patch = {'cmd': jsonutils.dumps(
|
||||
+ ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||
+ patch_port1, '--', 'add-port', 'fake_bridge',
|
||||
+ patch_port1])}
|
||||
|
||||
expected_calls = [call.call_xenapi('VIF.get_record', vif_ref),
|
||||
call.call_xenapi('network.get_bridge', 'fake_network'),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': add_p1}),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': add_p2}),
|
||||
- call.call_plugin('xenhost', 'network_config', {'cmd': ext_map})]
|
||||
+ call.call_plugin('xenhost', 'network_config', add_qbr),
|
||||
+ call.call_plugin('xenhost', 'network_config', set_fd),
|
||||
+ call.call_plugin('xenhost', 'network_config', off_stp),
|
||||
+ call.call_plugin('xenhost', 'network_config', up_qbr_link),
|
||||
+ call.call_plugin('xenhost', 'network_config', qvb_qvo_veth_pair),
|
||||
+ call.call_plugin('xenhost', 'network_config', qvb_link_up),
|
||||
+ call.call_plugin('xenhost', 'network_config', qvb_promisc_on),
|
||||
+ call.call_plugin('xenhost', 'network_config', qvo_link_up),
|
||||
+ call.call_plugin('xenhost', 'network_config', qvo_promisc_on),
|
||||
+ call.call_plugin('xenhost', 'network_config', add_qvb),
|
||||
+ call.call_plugin('xenhost', 'network_config', del_add_qvo),
|
||||
+ call.call_plugin('xenhost', 'network_config', ext_map),
|
||||
+ call.call_plugin('xenhost', 'network_config', tap_patch_veth_pair),
|
||||
+ call.call_plugin('xenhost', 'network_config', up_tap_link),
|
||||
+ call.call_plugin('xenhost', 'network_config', tap_promisc_on),
|
||||
+ call.call_plugin('xenhost', 'network_config', up_patch_link),
|
||||
+ call.call_plugin('xenhost', 'network_config', patch_promisc_on),
|
||||
+ call.call_plugin('xenhost', 'network_config', add_tap),
|
||||
+ call.call_plugin('xenhost', 'network_config', add_patch)]
|
||||
self._session.assert_has_calls(expected_calls)
|
||||
|
||||
@patch.object(network_utils, 'find_network_with_name_label',
|
||||
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
|
||||
index 5c1ac29..bae0a84 100644
|
||||
--- a/nova/virt/xenapi/vif.py
|
||||
+++ b/nova/virt/xenapi/vif.py
|
||||
@@ -230,11 +230,11 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
|
||||
def unplug(self, instance, vif, vm_ref):
|
||||
"""unplug vif:
|
||||
- 1. unplug and destroy vif.
|
||||
- 2. delete the patch port pair between the integration bridge and
|
||||
- the interim network.
|
||||
- 3. destroy the interim network
|
||||
- 4. delete the OVS bridge service for the interim network
|
||||
+ 1. delete the patch port pair between the integration bridge and
|
||||
+ the qbr linux bridge(if exist) and the interim network.
|
||||
+ 2. destroy the interim network
|
||||
+ 3. delete the OVS bridge service for the interim network
|
||||
+ 4. delete linux bridge qbr and related ports if exist
|
||||
"""
|
||||
try:
|
||||
super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
||||
@@ -255,27 +255,124 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
{'vif_id': vif['id']})
|
||||
bridge_name = self._session.call_xenapi('network.get_bridge',
|
||||
network)
|
||||
- patch_port1, patch_port2 = self._get_patch_port_pair_names(
|
||||
- vif['id'])
|
||||
- # delete the patch port pair
|
||||
+ patch_port1, tap_name = self._get_patch_port_pair_names(vif['id'])
|
||||
+
|
||||
self._del_ovs_port(bridge_name, patch_port1)
|
||||
- self._del_ovs_port(CONF.xenserver.ovs_integration_bridge,
|
||||
- patch_port2)
|
||||
|
||||
- LOG.debug('destroying network: network=%(network)s,'
|
||||
- 'bridge=%(br)s',
|
||||
+ LOG.debug('destroying network: network=%(network)s, bridge=%(br)s',
|
||||
{'network': network, 'br': bridge_name})
|
||||
self._session.call_xenapi('network.destroy', network)
|
||||
# delete bridge if it still exists.
|
||||
# As there is patch port existing on this bridge when destroying
|
||||
- # the VM vif (which happens when shutdown the VM), the bridge
|
||||
- # won't be destroyed automatically by XAPI. So let's destroy it
|
||||
- # at here.
|
||||
+ # VM vif (which happens when shutdown the VM), the bridge won't be
|
||||
+ # destroyed automatically by XAPI. So let's destroy it at here.
|
||||
self._del_ovs_br(bridge_name)
|
||||
+
|
||||
+ qbr_name = self._get_qbr_name(vif['id'])
|
||||
+ qvb_name, qvo_name = self._get_veth_pair_names(vif['id'])
|
||||
+ if self._device_exists(qbr_name):
|
||||
+ # delete tap port, qvb port and qbr
|
||||
+ LOG.debug(
|
||||
+ "destroy linux bridge %(qbr)s when unplug vif %(vif)s",
|
||||
+ {'qbr': qbr_name, 'vif': vif['id']})
|
||||
+ self._delete_linux_port(qbr_name, tap_name)
|
||||
+ self._delete_linux_port(qbr_name, qvb_name)
|
||||
+ self._delete_linux_bridge(qbr_name)
|
||||
+ self._del_ovs_port(CONF.xenserver.ovs_integration_bridge, qvo_name)
|
||||
except Exception as e:
|
||||
LOG.debug("Fail to unplug vif %(vif)s, exception:%(exception)s",
|
||||
{'vif': vif, 'exception': e}, instance=instance)
|
||||
|
||||
+ def _get_qbr_name(self, iface_id):
|
||||
+ return ("qbr" + iface_id)[:network_model.NIC_NAME_LEN]
|
||||
+
|
||||
+ def _get_veth_pair_names(self, iface_id):
|
||||
+ return (("qvb%s" % iface_id)[:network_model.NIC_NAME_LEN],
|
||||
+ ("qvo%s" % iface_id)[:network_model.NIC_NAME_LEN])
|
||||
+
|
||||
+ def _device_exists(self, device):
|
||||
+ """Check if ethernet device exists."""
|
||||
+ try:
|
||||
+ self._exec_dom0_cmd(['ip', 'link', 'show', device])
|
||||
+ return True
|
||||
+ except Exception:
|
||||
+ # Swallow exception from plugin, since this indicates the device
|
||||
+ # doesn't exist
|
||||
+ return False
|
||||
+
|
||||
+ def _delete_net_dev(self, dev):
|
||||
+ """Delete a network device only if it exists."""
|
||||
+ if self._device_exists(dev):
|
||||
+ LOG.debug("delete network device '%s'", dev)
|
||||
+ cmd_args = ['ip', 'link', 'delete', dev]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _create_veth_pair(self, dev1_name, dev2_name):
|
||||
+ """Create a pair of veth devices with the specified names,
|
||||
+ deleting any previous devices with those names.
|
||||
+ """
|
||||
+ for dev in [dev1_name, dev2_name]:
|
||||
+ self._delete_net_dev(dev)
|
||||
+ LOG.debug("Create veth pair, port1:%(qvb)s, port2:%(qvo)s",
|
||||
+ {'qvb': dev1_name, 'qvo': dev2_name})
|
||||
+ cmd_args = ['ip', 'link', 'add', dev1_name, 'type', 'veth', 'peer',
|
||||
+ 'name', dev2_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ for dev in [dev1_name, dev2_name]:
|
||||
+ cmd_args = ['ip', 'link', 'set', dev, 'up']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['ip', 'link', 'set', dev, 'promisc', 'on']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _create_linux_bridge(self, vif_rec):
|
||||
+ """create a qbr linux bridge for neutron security group
|
||||
+ """
|
||||
+ iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||
+ linux_br_name = self._get_qbr_name(iface_id)
|
||||
+ if not self._device_exists(linux_br_name):
|
||||
+ LOG.debug("Create linux bridge %s", linux_br_name)
|
||||
+ cmd_args = ['brctl', 'addbr', linux_br_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['brctl', 'setfd', linux_br_name, '0']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['brctl', 'stp', linux_br_name, 'off']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['ip', 'link', 'set', linux_br_name, 'up']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ qvb_name, qvo_name = self._get_veth_pair_names(iface_id)
|
||||
+ if not self._device_exists(qvo_name):
|
||||
+ self._create_veth_pair(qvb_name, qvo_name)
|
||||
+ cmd_args = ['brctl', 'addif', linux_br_name, qvb_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port', qvo_name,
|
||||
+ '--', 'add-port',
|
||||
+ CONF.xenserver.ovs_integration_bridge, qvo_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ self._map_external_ids_with_vif(qvo_name, vif_rec)
|
||||
+ return linux_br_name
|
||||
+
|
||||
+ def _delete_linux_port(self, qbr_name, port_name):
|
||||
+ try:
|
||||
+ # delete port in linux bridge
|
||||
+ cmd_args = ['brctl', 'delif', qbr_name, port_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ self._delete_net_dev(port_name)
|
||||
+ except Exception:
|
||||
+ LOG.debug("Fail to delete linux port %(port_name)s on bridge"
|
||||
+ "%(qbr_name)s",
|
||||
+ {'port_name': port_name, 'qbr_name': qbr_name})
|
||||
+
|
||||
+ def _delete_linux_bridge(self, qbr_name):
|
||||
+ try:
|
||||
+ # delete linux bridge qbrxxx
|
||||
+ cmd_args = ['ip', 'link', 'set', qbr_name, 'down']
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['brctl', 'delbr', qbr_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ except Exception:
|
||||
+ LOG.debug("Fail to delete linux bridge %s", qbr_name)
|
||||
+
|
||||
def post_start_actions(self, instance, vif_ref):
|
||||
"""Do needed actions post vif start:
|
||||
plug the interim ovs bridge to the integration bridge;
|
||||
@@ -287,10 +384,10 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
bridge_name = self._session.call_xenapi('network.get_bridge',
|
||||
network_ref)
|
||||
iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||
- patch_port1, patch_port2 = self._get_patch_port_pair_names(iface_id)
|
||||
+ patch_port1, tap_name = self._get_patch_port_pair_names(iface_id)
|
||||
LOG.debug('plug_ovs_bridge: port1=%(port1)s, port2=%(port2)s,'
|
||||
'network_ref=%(network_ref)s, bridge_name=%(bridge_name)s',
|
||||
- {'port1': patch_port1, 'port2': patch_port2,
|
||||
+ {'port1': patch_port1, 'port2': tap_name,
|
||||
'network_ref': network_ref,
|
||||
'bridge_name': bridge_name})
|
||||
if bridge_name is None:
|
||||
@@ -298,10 +395,15 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
"VM:%(vm_name)s"),
|
||||
{'vif_ref': vif_ref, 'vm_name': instance['name']})
|
||||
|
||||
- self._add_patch_port(bridge_name, patch_port1, patch_port2)
|
||||
- self._add_patch_port(CONF.xenserver.ovs_integration_bridge,
|
||||
- patch_port2, patch_port1)
|
||||
- self._map_external_ids_with_vif(patch_port2, vif_rec)
|
||||
+ # Create Linux bridge qbrXXX
|
||||
+ linux_br_name = self._create_linux_bridge(vif_rec)
|
||||
+ LOG.debug("create veth pair for interim bridge and linux bridge")
|
||||
+ self._create_veth_pair(tap_name, patch_port1)
|
||||
+ cmd_args = ['brctl', 'addif', linux_br_name, tap_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+ cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port', patch_port1,
|
||||
+ '--', 'add-port', bridge_name, patch_port1]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
|
||||
def get_vif_interim_net_name(self, vif):
|
||||
return ("net-" + vif['id'])[:network_model.NIC_NAME_LEN]
|
||||
@@ -326,8 +428,8 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
return network_ref
|
||||
|
||||
def _get_patch_port_pair_names(self, iface_id):
|
||||
- return (("pp1-%s" % iface_id)[:network_model.NIC_NAME_LEN],
|
||||
- ("pp2-%s" % iface_id)[:network_model.NIC_NAME_LEN])
|
||||
+ return (("vif%s" % iface_id)[:network_model.NIC_NAME_LEN],
|
||||
+ ("tap%s" % iface_id)[:network_model.NIC_NAME_LEN])
|
||||
|
||||
def _add_patch_port(self, bridge_name, port_name, peer_port_name):
|
||||
cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port', port_name,
|
||||
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
index 4bf85ac..80f263f 100755
|
||||
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
@@ -214,9 +214,11 @@ def iptables_config(session, args):
|
||||
|
||||
|
||||
def network_config(session, args):
|
||||
- # function to config OVS bridge
|
||||
+ # function to config OVS bridge and Linux bridge
|
||||
ALLOWED_CMDS = [
|
||||
'ovs-vsctl',
|
||||
+ 'brctl',
|
||||
+ 'ip'
|
||||
]
|
||||
cmd = json.loads(args.get('cmd'))
|
||||
if cmd is None or cmd == []:
|
|
@ -0,0 +1,368 @@
|
|||
diff --git a/nova/tests/unit/virt/xenapi/test_vmops.py b/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
index 58c64c0..33bec73 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
@@ -28,6 +28,7 @@ from nova import test
|
||||
from nova.tests.unit import fake_flavor
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit.virt.xenapi import stubs
|
||||
+from nova import utils
|
||||
from nova.virt import fake
|
||||
from nova.virt.xenapi import agent as xenapi_agent
|
||||
from nova.virt.xenapi.client import session as xenapi_session
|
||||
@@ -315,10 +316,11 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
self.mox.StubOutWithMock(self.vmops.firewall_driver,
|
||||
'apply_instance_filter')
|
||||
self.mox.StubOutWithMock(self.vmops, '_update_last_dom_id')
|
||||
+ self.mox.StubOutWithMock(self.vmops._session, 'call_xenapi')
|
||||
|
||||
def _test_spawn(self, name_label_param=None, block_device_info_param=None,
|
||||
rescue=False, include_root_vdi=True, throw_exception=None,
|
||||
- attach_pci_dev=False):
|
||||
+ attach_pci_dev=False, neutron_exception=False):
|
||||
self._stub_out_common()
|
||||
|
||||
instance = {"name": "dummy", "uuid": "fake_uuid"}
|
||||
@@ -411,38 +413,55 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
step += 1
|
||||
self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
|
||||
- self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
- self.vmops.firewall_driver.setup_basic_filtering(instance,
|
||||
- network_info).AndRaise(NotImplementedError)
|
||||
- self.vmops.firewall_driver.prepare_instance_filter(instance,
|
||||
- network_info)
|
||||
- step += 1
|
||||
- self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
-
|
||||
- if rescue:
|
||||
- self.vmops._attach_orig_disks(instance, vm_ref)
|
||||
+ if neutron_exception:
|
||||
+ events = [('network-vif-plugged', 1)]
|
||||
+ self.vmops._get_neutron_events(network_info,
|
||||
+ True, True).AndReturn(events)
|
||||
+ self.mox.StubOutWithMock(self.vmops, '_neutron_failed_callback')
|
||||
+ self.mox.StubOutWithMock(self.vmops._virtapi,
|
||||
+ 'wait_for_instance_event')
|
||||
+ self.vmops._virtapi.wait_for_instance_event(instance, events,
|
||||
+ deadline=300,
|
||||
+ error_callback=self.vmops._neutron_failed_callback).\
|
||||
+ AndRaise(exception.VirtualInterfaceCreateException)
|
||||
+ else:
|
||||
+ self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
+ self.vmops.firewall_driver.setup_basic_filtering(instance,
|
||||
+ network_info).AndRaise(NotImplementedError)
|
||||
+ self.vmops.firewall_driver.prepare_instance_filter(instance,
|
||||
+ network_info)
|
||||
step += 1
|
||||
- self.vmops._update_instance_progress(context, instance, step,
|
||||
- steps)
|
||||
- self.vmops._start(instance, vm_ref)
|
||||
- self.vmops._wait_for_instance_to_start(instance, vm_ref)
|
||||
- self.vmops._update_last_dom_id(vm_ref)
|
||||
- step += 1
|
||||
- self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
-
|
||||
- self.vmops._configure_new_instance_with_agent(instance, vm_ref,
|
||||
- injected_files, admin_password)
|
||||
- self.vmops._remove_hostname(instance, vm_ref)
|
||||
- step += 1
|
||||
- self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
+ self.vmops._update_instance_progress(context, instance,
|
||||
+ step, steps)
|
||||
+
|
||||
+ if rescue:
|
||||
+ self.vmops._attach_orig_disks(instance, vm_ref)
|
||||
+ step += 1
|
||||
+ self.vmops._update_instance_progress(context, instance, step,
|
||||
+ steps)
|
||||
+ start_pause = True
|
||||
+ self.vmops._start(instance, vm_ref, start_pause=start_pause)
|
||||
+ step += 1
|
||||
+ self.vmops._update_instance_progress(context, instance,
|
||||
+ step, steps)
|
||||
+ self.vmops.firewall_driver.apply_instance_filter(instance,
|
||||
+ network_info)
|
||||
+ step += 1
|
||||
+ self.vmops._update_instance_progress(context, instance,
|
||||
+ step, steps)
|
||||
+ self.vmops._session.call_xenapi('VM.unpause', vm_ref)
|
||||
+ self.vmops._wait_for_instance_to_start(instance, vm_ref)
|
||||
+ self.vmops._update_last_dom_id(vm_ref)
|
||||
+ self.vmops._configure_new_instance_with_agent(instance, vm_ref,
|
||||
+ injected_files, admin_password)
|
||||
+ self.vmops._remove_hostname(instance, vm_ref)
|
||||
+ step += 1
|
||||
+ last_call = self.vmops._update_instance_progress(context, instance,
|
||||
+ step, steps)
|
||||
|
||||
- self.vmops.firewall_driver.apply_instance_filter(instance,
|
||||
- network_info)
|
||||
- step += 1
|
||||
- last_call = self.vmops._update_instance_progress(context, instance,
|
||||
- step, steps)
|
||||
if throw_exception:
|
||||
last_call.AndRaise(throw_exception)
|
||||
+ if throw_exception or neutron_exception:
|
||||
self.vmops._destroy(instance, vm_ref, network_info=network_info)
|
||||
vm_utils.destroy_kernel_ramdisk(self.vmops._session, instance,
|
||||
kernel_file, ramdisk_file)
|
||||
@@ -469,11 +488,25 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
self.assertRaises(test.TestingException, self._test_spawn,
|
||||
throw_exception=test.TestingException())
|
||||
|
||||
+ def test_spawn_with_neutron(self):
|
||||
+ self.mox.StubOutWithMock(self.vmops, '_get_neutron_events')
|
||||
+ events = [('network-vif-plugged', 1)]
|
||||
+ network_info = "net_info"
|
||||
+ self.vmops._get_neutron_events(network_info,
|
||||
+ True, True).AndReturn(events)
|
||||
+ self.mox.StubOutWithMock(self.vmops,
|
||||
+ '_neutron_failed_callback')
|
||||
+ self._test_spawn()
|
||||
+
|
||||
+ def test_spawn_with_neutron_exception(self):
|
||||
+ self.mox.StubOutWithMock(self.vmops, '_get_neutron_events')
|
||||
+ self.assertRaises(exception.VirtualInterfaceCreateException,
|
||||
+ self._test_spawn, neutron_exception=True)
|
||||
+
|
||||
def _test_finish_migration(self, power_on=True, resize_instance=True,
|
||||
throw_exception=None, booted_from_volume=False):
|
||||
self._stub_out_common()
|
||||
self.mox.StubOutWithMock(volumeops.VolumeOps, "connect_volume")
|
||||
- self.mox.StubOutWithMock(self.vmops._session, 'call_xenapi')
|
||||
self.mox.StubOutWithMock(vm_utils, "import_all_migrated_disks")
|
||||
self.mox.StubOutWithMock(self.vmops, "_attach_mapped_block_devices")
|
||||
|
||||
@@ -541,12 +574,14 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
network_info)
|
||||
|
||||
if power_on:
|
||||
- self.vmops._start(instance, vm_ref)
|
||||
- self.vmops._wait_for_instance_to_start(instance, vm_ref)
|
||||
- self.vmops._update_last_dom_id(vm_ref)
|
||||
+ self.vmops._start(instance, vm_ref, start_pause=True)
|
||||
|
||||
self.vmops.firewall_driver.apply_instance_filter(instance,
|
||||
network_info)
|
||||
+ if power_on:
|
||||
+ self.vmops._session.call_xenapi('VM.unpause', vm_ref)
|
||||
+ self.vmops._wait_for_instance_to_start(instance, vm_ref)
|
||||
+ self.vmops._update_last_dom_id(vm_ref)
|
||||
|
||||
last_call = self.vmops._update_instance_progress(context, instance,
|
||||
step=5, total_steps=5)
|
||||
@@ -704,6 +739,57 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
self.vmops._configure_new_instance_with_agent(instance, vm_ref,
|
||||
None, None)
|
||||
|
||||
+ @mock.patch.object(utils, 'is_neutron', return_value=True)
|
||||
+ def test_get_neutron_event(self, mock_is_neutron):
|
||||
+ network_info = [{"active": False, "id": 1},
|
||||
+ {"active": True, "id": 2},
|
||||
+ {"active": False, "id": 3},
|
||||
+ {"id": 4}]
|
||||
+ power_on = True
|
||||
+ first_boot = True
|
||||
+ events = self.vmops._get_neutron_events(network_info,
|
||||
+ power_on, first_boot)
|
||||
+ self.assertEqual("network-vif-plugged", events[0][0])
|
||||
+ self.assertEqual(1, events[0][1])
|
||||
+ self.assertEqual("network-vif-plugged", events[1][0])
|
||||
+ self.assertEqual(3, events[1][1])
|
||||
+
|
||||
+ @mock.patch.object(utils, 'is_neutron', return_value=False)
|
||||
+ def test_get_neutron_event_not_neutron_network(self, mock_is_neutron):
|
||||
+ network_info = [{"active": False, "id": 1},
|
||||
+ {"active": True, "id": 2},
|
||||
+ {"active": False, "id": 3},
|
||||
+ {"id": 4}]
|
||||
+ power_on = True
|
||||
+ first_boot = True
|
||||
+ events = self.vmops._get_neutron_events(network_info,
|
||||
+ power_on, first_boot)
|
||||
+ self.assertEqual([], events)
|
||||
+
|
||||
+ @mock.patch.object(utils, 'is_neutron', return_value=True)
|
||||
+ def test_get_neutron_event_power_off(self, mock_is_neutron):
|
||||
+ network_info = [{"active": False, "id": 1},
|
||||
+ {"active": True, "id": 2},
|
||||
+ {"active": False, "id": 3},
|
||||
+ {"id": 4}]
|
||||
+ power_on = False
|
||||
+ first_boot = True
|
||||
+ events = self.vmops._get_neutron_events(network_info,
|
||||
+ power_on, first_boot)
|
||||
+ self.assertEqual([], events)
|
||||
+
|
||||
+ @mock.patch.object(utils, 'is_neutron', return_value=True)
|
||||
+ def test_get_neutron_event_not_first_boot(self, mock_is_neutron):
|
||||
+ network_info = [{"active": False, "id": 1},
|
||||
+ {"active": True, "id": 2},
|
||||
+ {"active": False, "id": 3},
|
||||
+ {"id": 4}]
|
||||
+ power_on = True
|
||||
+ first_boot = False
|
||||
+ events = self.vmops._get_neutron_events(network_info,
|
||||
+ power_on, first_boot)
|
||||
+ self.assertEqual([], events)
|
||||
+
|
||||
|
||||
class DestroyTestCase(VMOpsTestBase):
|
||||
def setUp(self):
|
||||
diff --git a/nova/tests/unit/virt/xenapi/test_xenapi.py b/nova/tests/unit/virt/xenapi/test_xenapi.py
|
||||
index 28b50ac..4847cfc 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/test_xenapi.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_xenapi.py
|
||||
@@ -329,6 +329,12 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
|
||||
virtual_size)
|
||||
self.stubs.Set(vm_utils, '_safe_copy_vdi', fake_safe_copy_vdi)
|
||||
|
||||
+ def fake_update_instance_with_power_on(self,
|
||||
+ vm_ref, instance, power_on):
|
||||
+ self._update_last_dom_id(vm_ref)
|
||||
+ self.stubs.Set(vmops.VMOps, '_update_instance_with_power_on',
|
||||
+ fake_update_instance_with_power_on)
|
||||
+
|
||||
def tearDown(self):
|
||||
fake_image.FakeImageService_reset()
|
||||
super(XenAPIVMTestCase, self).tearDown()
|
||||
@@ -1675,6 +1681,19 @@ class XenAPIMigrateInstance(stubs.XenAPITestBase):
|
||||
self.stubs.Set(vmops.VMOps, '_inject_instance_metadata',
|
||||
fake_inject_instance_metadata)
|
||||
|
||||
+ def fake_update_instance_with_power_on(self,
|
||||
+ vm_ref, instance, power_on):
|
||||
+ pass
|
||||
+ self.stubs.Set(vmops.VMOps, '_update_instance_with_power_on',
|
||||
+ fake_update_instance_with_power_on)
|
||||
+
|
||||
+ def _create_instance(self, **kw):
|
||||
+ values = self.instance_values.copy()
|
||||
+ values.update(kw)
|
||||
+ instance = objects.Instance(context=self.context, **values)
|
||||
+ instance.create()
|
||||
+ return instance
|
||||
+
|
||||
def test_migrate_disk_and_power_off(self):
|
||||
instance = db.instance_create(self.context, self.instance_values)
|
||||
xenapi_fake.create_vm(instance['name'], 'Running')
|
||||
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
|
||||
index 65a41a3..1fbc15a 100644
|
||||
--- a/nova/virt/xenapi/vmops.py
|
||||
+++ b/nova/virt/xenapi/vmops.py
|
||||
@@ -22,6 +22,7 @@ import functools
|
||||
import time
|
||||
import zlib
|
||||
|
||||
+import eventlet
|
||||
from eventlet import greenthread
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
@@ -321,7 +322,8 @@ class VMOps(object):
|
||||
rescue=False, power_on=power_on, resize=resize_instance,
|
||||
completed_callback=completed_callback)
|
||||
|
||||
- def _start(self, instance, vm_ref=None, bad_volumes_callback=None):
|
||||
+ def _start(self, instance, vm_ref=None, bad_volumes_callback=None,
|
||||
+ start_pause=False):
|
||||
"""Power on a VM instance."""
|
||||
vm_ref = vm_ref or self._get_vm_opaque_ref(instance)
|
||||
LOG.debug("Starting instance", instance=instance)
|
||||
@@ -339,7 +341,7 @@ class VMOps(object):
|
||||
|
||||
self._session.call_xenapi('VM.start_on', vm_ref,
|
||||
self._session.host_ref,
|
||||
- False, False)
|
||||
+ start_pause, False)
|
||||
|
||||
# Allow higher-layers a chance to detach bad-volumes as well (in order
|
||||
# to cleanup BDM entries and detach in Cinder)
|
||||
@@ -548,14 +550,13 @@ class VMOps(object):
|
||||
self._prepare_instance_filter(instance, network_info)
|
||||
|
||||
@step
|
||||
- def boot_instance_step(undo_mgr, vm_ref):
|
||||
+ def start_paused_instance_step(undo_mgr, vm_ref):
|
||||
if power_on:
|
||||
- self._start(instance, vm_ref)
|
||||
- self._wait_for_instance_to_start(instance, vm_ref)
|
||||
- self._update_last_dom_id(vm_ref)
|
||||
+ self._start(instance, vm_ref, start_pause=True)
|
||||
|
||||
@step
|
||||
- def configure_booted_instance_step(undo_mgr, vm_ref):
|
||||
+ def boot_and_configure_instance_step(undo_mgr, vm_ref):
|
||||
+ self._update_instance_with_power_on(vm_ref, instance, power_on)
|
||||
if first_boot:
|
||||
self._configure_new_instance_with_agent(instance, vm_ref,
|
||||
injected_files, admin_password)
|
||||
@@ -583,22 +584,61 @@ class VMOps(object):
|
||||
attach_devices_step(undo_mgr, vm_ref, vdis, disk_image_type)
|
||||
|
||||
inject_instance_data_step(undo_mgr, vm_ref, vdis)
|
||||
- setup_network_step(undo_mgr, vm_ref)
|
||||
|
||||
- if rescue:
|
||||
- attach_orig_disks_step(undo_mgr, vm_ref)
|
||||
-
|
||||
- boot_instance_step(undo_mgr, vm_ref)
|
||||
+ # if use neutron, prepare waiting event from neutron
|
||||
+ timeout = CONF.vif_plugging_timeout
|
||||
+ events = self._get_neutron_events(network_info,
|
||||
+ power_on, first_boot)
|
||||
+ try:
|
||||
+ with self._virtapi.wait_for_instance_event(
|
||||
+ instance, events, deadline=timeout,
|
||||
+ error_callback=self._neutron_failed_callback):
|
||||
+ LOG.debug("wait for instance event:%s", events)
|
||||
+ setup_network_step(undo_mgr, vm_ref)
|
||||
+ if rescue:
|
||||
+ attach_orig_disks_step(undo_mgr, vm_ref)
|
||||
+ start_paused_instance_step(undo_mgr, vm_ref)
|
||||
+ except eventlet.timeout.Timeout:
|
||||
+ self._handle_neutron_event_timeout(instance, undo_mgr)
|
||||
|
||||
- configure_booted_instance_step(undo_mgr, vm_ref)
|
||||
apply_security_group_filters_step(undo_mgr)
|
||||
-
|
||||
+ boot_and_configure_instance_step(undo_mgr, vm_ref)
|
||||
if completed_callback:
|
||||
completed_callback()
|
||||
except Exception:
|
||||
msg = _("Failed to spawn, rolling back")
|
||||
undo_mgr.rollback_and_reraise(msg=msg, instance=instance)
|
||||
|
||||
+ def _handle_neutron_event_timeout(self, instance, undo_mgr):
|
||||
+ # We didn't get callback from Neutron within given time
|
||||
+ LOG.warn(_LW('Timeout waiting for vif plugging callback'),
|
||||
+ instance=instance)
|
||||
+ if CONF.vif_plugging_is_fatal:
|
||||
+ raise exception.VirtualInterfaceCreateException()
|
||||
+
|
||||
+ def _update_instance_with_power_on(self, vm_ref, instance, power_on):
|
||||
+ if power_on:
|
||||
+ LOG.debug("Update instance when power on", instance=instance)
|
||||
+ self._session.VM.unpause(vm_ref)
|
||||
+ self._wait_for_instance_to_start(instance, vm_ref)
|
||||
+ self._update_last_dom_id(vm_ref)
|
||||
+
|
||||
+ def _neutron_failed_callback(self, event_name, instance):
|
||||
+ LOG.warn(_LW('Neutron Reported failure on event %(event)s'),
|
||||
+ {'event': event_name}, instance=instance)
|
||||
+ if CONF.vif_plugging_is_fatal:
|
||||
+ raise exception.VirtualInterfaceCreateException()
|
||||
+
|
||||
+ def _get_neutron_events(self, network_info, power_on, first_boot):
|
||||
+ # Only get network-vif-plugged events with VIF's status is not active.
|
||||
+ # With VIF whose status is active, neutron may not notify such event.
|
||||
+ timeout = CONF.vif_plugging_timeout
|
||||
+ if (utils.is_neutron() and power_on and timeout and first_boot):
|
||||
+ return [('network-vif-plugged', vif['id'])
|
||||
+ for vif in network_info if vif.get('active', True) is False]
|
||||
+ else:
|
||||
+ return []
|
||||
+
|
||||
def _attach_orig_disks(self, instance, vm_ref):
|
||||
orig_vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
orig_vdi_refs = self._find_vdi_refs(orig_vm_ref,
|
|
@ -0,0 +1,715 @@
|
|||
diff --git a/nova/tests/unit/virt/xenapi/stubs.py b/nova/tests/unit/virt/xenapi/stubs.py
|
||||
index 5d515b1..f3a49b2 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/stubs.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/stubs.py
|
||||
@@ -29,6 +29,13 @@ from nova.virt.xenapi import vm_utils
|
||||
from nova.virt.xenapi import vmops
|
||||
|
||||
|
||||
+def stubout_create_vifs(stubs, vmops):
|
||||
+ def fake_create_vifs(*args):
|
||||
+ return ['fake_vif_ref']
|
||||
+
|
||||
+ stubs.Set(vmops, '_create_vifs', fake_create_vifs)
|
||||
+
|
||||
+
|
||||
def stubout_firewall_driver(stubs, conn):
|
||||
|
||||
def fake_none(self, *args):
|
||||
diff --git a/nova/tests/unit/virt/xenapi/test_vif.py b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
index a41e506..65e3070 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
@@ -13,7 +13,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
-import mock
|
||||
+from mock import call
|
||||
+from mock import Mock
|
||||
+from mock import patch
|
||||
|
||||
from nova import exception
|
||||
from nova.network import model
|
||||
@@ -30,7 +32,7 @@ fake_vif = {
|
||||
'address': '00:00:00:00:00:00',
|
||||
'network_id': 123,
|
||||
'instance_uuid': 'fake-uuid',
|
||||
- 'uuid': 'fake-uuid-2',
|
||||
+ 'uuid': 'fake_uuid',
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +44,7 @@ def fake_call_xenapi(method, *args):
|
||||
return {'uuid': fake_vif['uuid'],
|
||||
'MAC': fake_vif['address'],
|
||||
'network': 'fake_network',
|
||||
- 'other_config': {'nicira-iface-id': fake_vif['id']}
|
||||
+ 'other_config': {'nicira-iface-id': 'fake-nicira-iface-id'}
|
||||
}
|
||||
else:
|
||||
raise exception.Exception("Failed get vif record")
|
||||
@@ -58,14 +60,27 @@ def fake_call_xenapi(method, *args):
|
||||
return "fake_vif_ref"
|
||||
else:
|
||||
raise exception.Exception("VIF existed")
|
||||
+ if method == "network.create":
|
||||
+ return "fake_network_ref"
|
||||
+ if method == "network.get_bridge":
|
||||
+ return "fake_bridge"
|
||||
+ if method == "network.get_VIFs":
|
||||
+ return
|
||||
+ if method == "network.destroy":
|
||||
+ return
|
||||
return "Unexpected call_xenapi: %s.%s" % (method, args)
|
||||
|
||||
|
||||
+def fake_call_plugin(plugin, method, *args):
|
||||
+ return
|
||||
+
|
||||
+
|
||||
class XenVIFDriverTestBase(stubs.XenAPITestBaseNoDB):
|
||||
def setUp(self):
|
||||
super(XenVIFDriverTestBase, self).setUp()
|
||||
- self._session = mock.Mock()
|
||||
+ self._session = Mock()
|
||||
self._session.call_xenapi.side_effect = fake_call_xenapi
|
||||
+ self._session.call_plugin.side_effect = fake_call_plugin
|
||||
|
||||
|
||||
class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
@@ -79,8 +94,8 @@ class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
ret_vif_ref = self.base_driver._get_vif_ref(fake_vif, vm_ref)
|
||||
self.assertEqual(vif_ref, ret_vif_ref)
|
||||
|
||||
- expected = [mock.call('VM.get_VIFs', vm_ref),
|
||||
- mock.call('VIF.get_record', vif_ref)]
|
||||
+ expected = [call('VM.get_VIFs', vm_ref),
|
||||
+ call('VIF.get_record', vif_ref)]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
|
||||
def test_get_vif_ref_none_and_exception(self):
|
||||
@@ -89,9 +104,9 @@ class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
ret_vif_ref = self.base_driver._get_vif_ref(vif, vm_ref)
|
||||
self.assertIsNone(ret_vif_ref)
|
||||
|
||||
- expected = [mock.call('VM.get_VIFs', vm_ref),
|
||||
- mock.call('VIF.get_record', 'fake_vif_ref'),
|
||||
- mock.call('VIF.get_record', 'fake_vif_ref_A2')]
|
||||
+ expected = [call('VM.get_VIFs', vm_ref),
|
||||
+ call('VIF.get_record', 'fake_vif_ref'),
|
||||
+ call('VIF.get_record', 'fake_vif_ref_A2')]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
|
||||
def test_create_vif(self):
|
||||
@@ -100,7 +115,7 @@ class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
ret_vif_ref = self.base_driver._create_vif(fake_vif, vif_rec, vm_ref)
|
||||
self.assertEqual("fake_vif_ref", ret_vif_ref)
|
||||
|
||||
- expected = [mock.call('VIF.create', vif_rec)]
|
||||
+ expected = [call('VIF.create', vif_rec)]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
|
||||
def test_create_vif_exception(self):
|
||||
@@ -108,16 +123,16 @@ class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
self.base_driver._create_vif,
|
||||
"fake_vif", "missing_vif_rec", "fake_vm_ref")
|
||||
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ @patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='fake_vif_ref')
|
||||
def test_unplug(self, mock_get_vif_ref):
|
||||
instance = {'name': "fake_instance"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
self.base_driver.unplug(instance, fake_vif, vm_ref)
|
||||
- expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
+ expected = [call('VIF.destroy', 'fake_vif_ref')]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ @patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='missing_vif_ref')
|
||||
def test_unplug_exception(self, mock_get_vif_ref):
|
||||
instance = "fake_instance"
|
||||
@@ -132,9 +147,9 @@ class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
|
||||
super(XenAPIBridgeDriverTestCase, self).setUp()
|
||||
self.bridge_driver = vif.XenAPIBridgeDriver(self._session)
|
||||
|
||||
- @mock.patch.object(vif.XenAPIBridgeDriver, '_ensure_vlan_bridge',
|
||||
+ @patch.object(vif.XenAPIBridgeDriver, '_ensure_vlan_bridge',
|
||||
return_value='fake_network_ref')
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
+ @patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
return_value='fake_vif_ref')
|
||||
def test_plug_create_vlan(self, mock_create_vif, mock_ensure_vlan_bridge):
|
||||
instance = {'name': "fake_instance_name"}
|
||||
@@ -149,14 +164,14 @@ class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
|
||||
ret_vif_ref = self.bridge_driver.plug(instance, vif, vm_ref, device)
|
||||
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ @patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='fake_vif_ref')
|
||||
def test_unplug(self, mock_get_vif_ref):
|
||||
instance = {'name': "fake_instance"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
self.bridge_driver.unplug(instance, fake_vif, vm_ref)
|
||||
|
||||
- expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
+ expected = [call('VIF.destroy', 'fake_vif_ref')]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
|
||||
|
||||
@@ -165,25 +180,106 @@ class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase):
|
||||
super(XenAPIOpenVswitchDriverTestCase, self).setUp()
|
||||
self.ovs_driver = vif.XenAPIOpenVswitchDriver(self._session)
|
||||
|
||||
- @mock.patch.object(network_utils, 'find_network_with_bridge',
|
||||
- return_value='fake_network_ref')
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
+ @patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
return_value='fake_vif_ref')
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref', return_value=None)
|
||||
- def test_plug(self, mock_get_vif_ref, mock_create_vif,
|
||||
- mock_find_network_with_bridge):
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, 'create_vif_interim_network')
|
||||
+ @patch.object(vif.XenVIFDriver, '_get_vif_ref', return_value=None)
|
||||
+ def test_plug(self, mock_get_vif_ref,
|
||||
+ mock_create_vif_interim_network,
|
||||
+ mock_create_vif):
|
||||
instance = {'name': "fake_instance_name"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
device = 1
|
||||
ret_vif_ref = self.ovs_driver.plug(instance, fake_vif, vm_ref, device)
|
||||
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||
+ self.assertTrue(mock_create_vif_interim_network.called)
|
||||
|
||||
- @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
+ return_value="fake_net_name")
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, '_get_patch_port_pair_names',
|
||||
+ return_value=("fake_port_name1", "fake_port_name2"))
|
||||
+ @patch.object(network_utils, 'find_network_with_name_label',
|
||||
+ return_value='fake_network')
|
||||
+ @patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='fake_vif_ref')
|
||||
- def test_unplug(self, mock_get_vif_ref):
|
||||
+ def test_unplug(self, mock_get_vif_ref,
|
||||
+ mock_find_network_with_name_label,
|
||||
+ mock_get_patch_port_pair_names,
|
||||
+ mock_get_vif_interim_net_name):
|
||||
instance = {'name': "fake_instance"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
self.ovs_driver.unplug(instance, fake_vif, vm_ref)
|
||||
|
||||
- expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
- self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+ del_p1 = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
+ '"del-port", "fake_bridge", "fake_port_name1"]'
|
||||
+ del_p2 = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
+ '"del-port", "xapi1", "fake_port_name2"]'
|
||||
+ del_br = '["ovs-vsctl", "--", "--if-exists", ' \
|
||||
+ '"del-br", "fake_bridge"]'
|
||||
+
|
||||
+ expected_calls = [call.call_xenapi('VIF.destroy', 'fake_vif_ref'),
|
||||
+ call.call_xenapi('network.get_VIFs', 'fake_network'),
|
||||
+ call.call_xenapi('network.get_bridge', 'fake_network'),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': del_p1}),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': del_p2}),
|
||||
+ call.call_xenapi('network.destroy', 'fake_network'),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': del_br})]
|
||||
+ self._session.assert_has_calls(expected_calls)
|
||||
+
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
+ return_value="fake_net_name")
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, '_get_patch_port_pair_names',
|
||||
+ return_value=("fake_port_name1", "fake_port_name2"))
|
||||
+ def test_post_start_actions(self, mock_get_patch_port_pair_names,
|
||||
+ mock_get_vif_interim_net_name):
|
||||
+ vif_ref = "fake_vif_ref"
|
||||
+ instance = {'name': 'fake_instance_name'}
|
||||
+ self.ovs_driver.post_start_actions(instance, vif_ref)
|
||||
+
|
||||
+ add_p1 = '["ovs-vsctl", "--", ' \
|
||||
+ '"--if-exists", "del-port", "fake_port_name1", "--", ' \
|
||||
+ '"add-port", "fake_bridge", "fake_port_name1", "--", ' \
|
||||
+ '"set", "interface", "fake_port_name1", ' \
|
||||
+ '"type=patch", "options:peer=fake_port_name2"]'
|
||||
+ add_p2 = '["ovs-vsctl", "--", ' \
|
||||
+ '"--if-exists", "del-port", "fake_port_name2", "--", ' \
|
||||
+ '"add-port", "xapi1", "fake_port_name2", "--", ' \
|
||||
+ '"set", "interface", "fake_port_name2", ' \
|
||||
+ '"type=patch", "options:peer=fake_port_name1"]'
|
||||
+ ext_map = '["ovs-vsctl", "set", "Interface", "fake_port_name2", ' \
|
||||
+ '"external-ids:attached-mac=00:00:00:00:00:00", ' \
|
||||
+ '"external-ids:iface-id=fake-nicira-iface-id", ' \
|
||||
+ '"external-ids:iface-status=active", ' \
|
||||
+ '"external-ids:xs-vif-uuid=fake_uuid"]'
|
||||
+
|
||||
+ expected_calls = [call.call_xenapi('VIF.get_record', vif_ref),
|
||||
+ call.call_xenapi('network.get_bridge', 'fake_network'),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': add_p1}),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': add_p2}),
|
||||
+ call.call_plugin('xenhost', 'network_config', {'cmd': ext_map})]
|
||||
+ self._session.assert_has_calls(expected_calls)
|
||||
+
|
||||
+ @patch.object(network_utils, 'find_network_with_name_label',
|
||||
+ return_value="fake_network_ref")
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
+ return_value="fake_net_name")
|
||||
+ def test_create_vif_interim_network_exist(self,
|
||||
+ mock_get_vif_interim_net_name,
|
||||
+ mock_find_network_with_name_label):
|
||||
+ self.ovs_driver.create_vif_interim_network(fake_vif)
|
||||
+ self._session.call_xenapi.assert_not_called()
|
||||
+
|
||||
+ @patch.object(network_utils, 'find_network_with_name_label',
|
||||
+ return_value=None)
|
||||
+ @patch.object(vif.XenAPIOpenVswitchDriver, 'get_vif_interim_net_name',
|
||||
+ return_value="fake_net_name")
|
||||
+ def test_create_vif_interim_network_new(self,
|
||||
+ mock_get_vif_interim_net_name,
|
||||
+ mock_find_network_with_name_label):
|
||||
+ network_rec = {'name_label': "fake_net_name",
|
||||
+ 'name_description': "interim network for vif",
|
||||
+ 'other_config': {}}
|
||||
+ network_ref = self.ovs_driver.create_vif_interim_network(fake_vif)
|
||||
+ self._session.call_xenapi.assert_called_once_with('network.create',
|
||||
+ network_rec)
|
||||
+ self.assertEqual(network_ref, 'fake_network_ref')
|
||||
diff --git a/nova/tests/unit/virt/xenapi/test_vmops.py b/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
index 33bec73..c6f1499 100644
|
||||
--- a/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_vmops.py
|
||||
@@ -50,6 +50,7 @@ class VMOpsTestBase(stubs.XenAPITestBaseNoDB):
|
||||
self._session = xenapi_session.XenAPISession('test_url', 'root',
|
||||
'test_pass')
|
||||
self.vmops = vmops.VMOps(self._session, fake.FakeVirtAPI())
|
||||
+ stubs.stubout_create_vifs(self.stubs, self.vmops)
|
||||
|
||||
def create_vm(self, name, state="Running"):
|
||||
vm_ref = xenapi_fake.create_vm(name, state)
|
||||
@@ -303,7 +304,6 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
self.mox.StubOutWithMock(self.vmops, '_inject_instance_metadata')
|
||||
self.mox.StubOutWithMock(self.vmops, '_inject_auto_disk_config')
|
||||
self.mox.StubOutWithMock(self.vmops, '_file_inject_vm_settings')
|
||||
- self.mox.StubOutWithMock(self.vmops, '_create_vifs')
|
||||
self.mox.StubOutWithMock(self.vmops.firewall_driver,
|
||||
'setup_basic_filtering')
|
||||
self.mox.StubOutWithMock(self.vmops.firewall_driver,
|
||||
@@ -401,6 +401,9 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
"0/0000:00:00.0")
|
||||
else:
|
||||
pci_manager.get_instance_pci_devs(instance).AndReturn([])
|
||||
+
|
||||
+ self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
+
|
||||
step += 1
|
||||
self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
|
||||
@@ -425,15 +428,6 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
error_callback=self.vmops._neutron_failed_callback).\
|
||||
AndRaise(exception.VirtualInterfaceCreateException)
|
||||
else:
|
||||
- self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
- self.vmops.firewall_driver.setup_basic_filtering(instance,
|
||||
- network_info).AndRaise(NotImplementedError)
|
||||
- self.vmops.firewall_driver.prepare_instance_filter(instance,
|
||||
- network_info)
|
||||
- step += 1
|
||||
- self.vmops._update_instance_progress(context, instance,
|
||||
- step, steps)
|
||||
-
|
||||
if rescue:
|
||||
self.vmops._attach_orig_disks(instance, vm_ref)
|
||||
step += 1
|
||||
@@ -444,6 +438,22 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
step += 1
|
||||
self.vmops._update_instance_progress(context, instance,
|
||||
step, steps)
|
||||
+
|
||||
+ self.vmops.firewall_driver.setup_basic_filtering(instance,
|
||||
+ network_info).AndRaise(NotImplementedError)
|
||||
+ self.vmops.firewall_driver.prepare_instance_filter(instance,
|
||||
+ network_info)
|
||||
+ step += 1
|
||||
+ self.vmops._update_instance_progress(context, instance, step,
|
||||
+ steps)
|
||||
+
|
||||
+ self.vmops._configure_new_instance_with_agent(instance, vm_ref,
|
||||
+ injected_files, admin_password)
|
||||
+ self.vmops._remove_hostname(instance, vm_ref)
|
||||
+ step += 1
|
||||
+ self.vmops._update_instance_progress(context, instance,
|
||||
+ step, steps)
|
||||
+
|
||||
self.vmops.firewall_driver.apply_instance_filter(instance,
|
||||
network_info)
|
||||
step += 1
|
||||
@@ -560,6 +570,7 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
vdis, di_type, network_info, False, None, None)
|
||||
self.vmops._attach_mapped_block_devices(instance, block_device_info)
|
||||
pci_manager.get_instance_pci_devs(instance).AndReturn([])
|
||||
+ self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
|
||||
self.vmops._inject_instance_metadata(instance, vm_ref)
|
||||
self.vmops._inject_auto_disk_config(instance, vm_ref)
|
||||
@@ -567,15 +578,14 @@ class SpawnTestCase(VMOpsTestBase):
|
||||
network_info)
|
||||
self.vmops.inject_network_info(instance, network_info, vm_ref)
|
||||
|
||||
- self.vmops._create_vifs(instance, vm_ref, network_info)
|
||||
+ if power_on:
|
||||
+ self.vmops._start(instance, vm_ref, start_pause=True)
|
||||
+
|
||||
self.vmops.firewall_driver.setup_basic_filtering(instance,
|
||||
network_info).AndRaise(NotImplementedError)
|
||||
self.vmops.firewall_driver.prepare_instance_filter(instance,
|
||||
network_info)
|
||||
|
||||
- if power_on:
|
||||
- self.vmops._start(instance, vm_ref, start_pause=True)
|
||||
-
|
||||
self.vmops.firewall_driver.apply_instance_filter(instance,
|
||||
network_info)
|
||||
if power_on:
|
||||
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
|
||||
index 5c7a350..5c1ac29 100644
|
||||
--- a/nova/virt/xenapi/vif.py
|
||||
+++ b/nova/virt/xenapi/vif.py
|
||||
@@ -19,13 +19,17 @@
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
+from oslo_serialization import jsonutils
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.i18n import _LW
|
||||
+from nova.network import model as network_model
|
||||
from nova.virt.xenapi import network_utils
|
||||
from nova.virt.xenapi import vm_utils
|
||||
|
||||
+LOG = logging.getLogger(__name__)
|
||||
+
|
||||
|
||||
xenapi_ovs_integration_bridge_opt = cfg.StrOpt('ovs_integration_bridge',
|
||||
default='xapi1',
|
||||
@@ -63,8 +67,6 @@ class XenVIFDriver(object):
|
||||
raise exception.NovaException(
|
||||
reason=_("Failed to create vif %s") % vif)
|
||||
|
||||
- LOG.debug("create vif %(vif)s for vm %(vm_ref)s successfully",
|
||||
- {'vif': vif, 'vm_ref': vm_ref})
|
||||
return vif_ref
|
||||
|
||||
def unplug(self, instance, vif, vm_ref):
|
||||
@@ -185,11 +187,18 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
||||
def unplug(self, instance, vif, vm_ref):
|
||||
super(XenAPIBridgeDriver, self).unplug(instance, vif, vm_ref)
|
||||
|
||||
+ def post_start_actions(self, instance, vif_ref):
|
||||
+ """no further actions needed for this driver type"""
|
||||
+ pass
|
||||
+
|
||||
|
||||
class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
"""VIF driver for Open vSwitch with XenAPI."""
|
||||
|
||||
def plug(self, instance, vif, vm_ref=None, device=None):
|
||||
+ """create an interim network for this vif; and build
|
||||
+ the vif_rec which will be used by xapi to create VM vif
|
||||
+ """
|
||||
if not vm_ref:
|
||||
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
|
||||
@@ -203,10 +212,9 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
if not device:
|
||||
device = 0
|
||||
|
||||
- # with OVS model, always plug into an OVS integration bridge
|
||||
- # that is already created
|
||||
- network_ref = network_utils.find_network_with_bridge(
|
||||
- self._session, CONF.xenserver.ovs_integration_bridge)
|
||||
+ # create an interim network which has a bridge directly connected
|
||||
+ # to instance
|
||||
+ network_ref = self.create_vif_interim_network(vif)
|
||||
vif_rec = {}
|
||||
vif_rec['device'] = str(device)
|
||||
vif_rec['network'] = network_ref
|
||||
@@ -221,4 +229,138 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
return self._create_vif(vif, vif_rec, vm_ref)
|
||||
|
||||
def unplug(self, instance, vif, vm_ref):
|
||||
- super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
||||
+ """unplug vif:
|
||||
+ 1. unplug and destroy vif.
|
||||
+ 2. delete the patch port pair between the integration bridge and
|
||||
+ the interim network.
|
||||
+ 3. destroy the interim network
|
||||
+ 4. delete the OVS bridge service for the interim network
|
||||
+ """
|
||||
+ try:
|
||||
+ super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
||||
+
|
||||
+ net_name = self.get_vif_interim_net_name(vif)
|
||||
+ network = network_utils.find_network_with_name_label(
|
||||
+ self._session, net_name)
|
||||
+ if network is None:
|
||||
+ return
|
||||
+ vifs = self._session.call_xenapi('network.get_VIFs', network)
|
||||
+ if vifs:
|
||||
+ # only remove the interim network when it's empty.
|
||||
+ # for resize/migrate on local host, vifs on both of the
|
||||
+ # source and target VM will be connected to the same
|
||||
+ # interim network.
|
||||
+ return
|
||||
+ LOG.debug('destroying patch port pair for vif: vif_id=%(vif_id)s',
|
||||
+ {'vif_id': vif['id']})
|
||||
+ bridge_name = self._session.call_xenapi('network.get_bridge',
|
||||
+ network)
|
||||
+ patch_port1, patch_port2 = self._get_patch_port_pair_names(
|
||||
+ vif['id'])
|
||||
+ # delete the patch port pair
|
||||
+ self._del_ovs_port(bridge_name, patch_port1)
|
||||
+ self._del_ovs_port(CONF.xenserver.ovs_integration_bridge,
|
||||
+ patch_port2)
|
||||
+
|
||||
+ LOG.debug('destroying network: network=%(network)s,'
|
||||
+ 'bridge=%(br)s',
|
||||
+ {'network': network, 'br': bridge_name})
|
||||
+ self._session.call_xenapi('network.destroy', network)
|
||||
+ # delete bridge if it still exists.
|
||||
+ # As there is patch port existing on this bridge when destroying
|
||||
+ # the VM vif (which happens when shutdown the VM), the bridge
|
||||
+ # won't be destroyed automatically by XAPI. So let's destroy it
|
||||
+ # at here.
|
||||
+ self._del_ovs_br(bridge_name)
|
||||
+ except Exception as e:
|
||||
+ LOG.debug("Fail to unplug vif %(vif)s, exception:%(exception)s",
|
||||
+ {'vif': vif, 'exception': e}, instance=instance)
|
||||
+
|
||||
+ def post_start_actions(self, instance, vif_ref):
|
||||
+ """Do needed actions post vif start:
|
||||
+ plug the interim ovs bridge to the integration bridge;
|
||||
+ set external_ids to the int-br port which will service
|
||||
+ for this vif.
|
||||
+ """
|
||||
+ vif_rec = self._session.call_xenapi('VIF.get_record', vif_ref)
|
||||
+ network_ref = vif_rec['network']
|
||||
+ bridge_name = self._session.call_xenapi('network.get_bridge',
|
||||
+ network_ref)
|
||||
+ iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||
+ patch_port1, patch_port2 = self._get_patch_port_pair_names(iface_id)
|
||||
+ LOG.debug('plug_ovs_bridge: port1=%(port1)s, port2=%(port2)s,'
|
||||
+ 'network_ref=%(network_ref)s, bridge_name=%(bridge_name)s',
|
||||
+ {'port1': patch_port1, 'port2': patch_port2,
|
||||
+ 'network_ref': network_ref,
|
||||
+ 'bridge_name': bridge_name})
|
||||
+ if bridge_name is None:
|
||||
+ raise Exception(_("Can't find bridge for vif_ref:%(vif_ref)s of"
|
||||
+ "VM:%(vm_name)s"),
|
||||
+ {'vif_ref': vif_ref, 'vm_name': instance['name']})
|
||||
+
|
||||
+ self._add_patch_port(bridge_name, patch_port1, patch_port2)
|
||||
+ self._add_patch_port(CONF.xenserver.ovs_integration_bridge,
|
||||
+ patch_port2, patch_port1)
|
||||
+ self._map_external_ids_with_vif(patch_port2, vif_rec)
|
||||
+
|
||||
+ def get_vif_interim_net_name(self, vif):
|
||||
+ return ("net-" + vif['id'])[:network_model.NIC_NAME_LEN]
|
||||
+
|
||||
+ def create_vif_interim_network(self, vif):
|
||||
+ net_name = self.get_vif_interim_net_name(vif)
|
||||
+ network_rec = {'name_label': net_name,
|
||||
+ 'name_description': "interim network for vif",
|
||||
+ 'other_config': {}}
|
||||
+ network_ref = network_utils.find_network_with_name_label(
|
||||
+ self._session, net_name)
|
||||
+ if network_ref:
|
||||
+ # already exist, just return
|
||||
+ # in some scenarios: e..g resize/migrate, it won't create new
|
||||
+ # interim network.
|
||||
+ return network_ref
|
||||
+ network_ref = self._session.call_xenapi('network.create', network_rec)
|
||||
+ if network_ref is None:
|
||||
+ raise Exception(_("Failed to create the interim netowrk for vif -"
|
||||
+ "%(vif_id)s"),
|
||||
+ {'vif_id': vif['id']})
|
||||
+ return network_ref
|
||||
+
|
||||
+ def _get_patch_port_pair_names(self, iface_id):
|
||||
+ return (("pp1-%s" % iface_id)[:network_model.NIC_NAME_LEN],
|
||||
+ ("pp2-%s" % iface_id)[:network_model.NIC_NAME_LEN])
|
||||
+
|
||||
+ def _add_patch_port(self, bridge_name, port_name, peer_port_name):
|
||||
+ cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port', port_name,
|
||||
+ '--', 'add-port', bridge_name, port_name,
|
||||
+ '--', 'set', 'interface', port_name,
|
||||
+ 'type=patch', 'options:peer=%s' % peer_port_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _del_ovs_port(self, bridge_name, port_name):
|
||||
+ cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||
+ bridge_name, port_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _del_ovs_br(self, bridge_name):
|
||||
+ cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-br', bridge_name]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _map_external_ids_with_vif(self, interface, vif_rec):
|
||||
+ '''set external ids on the integration bridge vif
|
||||
+ '''
|
||||
+ mac = vif_rec['MAC']
|
||||
+ iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||
+ vif_uuid = vif_rec['uuid']
|
||||
+ cmd_args = ['ovs-vsctl', 'set', 'Interface', interface,
|
||||
+ 'external-ids:attached-mac=%s' % mac,
|
||||
+ 'external-ids:iface-id=%s' % iface_id,
|
||||
+ 'external-ids:iface-status=active',
|
||||
+ 'external-ids:xs-vif-uuid=%s' % vif_uuid,
|
||||
+ ]
|
||||
+ self._exec_dom0_cmd(cmd_args)
|
||||
+
|
||||
+ def _exec_dom0_cmd(self, cmd_args):
|
||||
+ args = {
|
||||
+ 'cmd': jsonutils.dumps(cmd_args),
|
||||
+ }
|
||||
+ self._session.call_plugin('xenhost', 'network_config', args)
|
||||
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
|
||||
index c8fdc52..0a48a3f 100644
|
||||
--- a/nova/virt/xenapi/vm_utils.py
|
||||
+++ b/nova/virt/xenapi/vm_utils.py
|
||||
@@ -1686,6 +1686,15 @@ def lookup_vm_vdis(session, vm_ref):
|
||||
return vdi_refs
|
||||
|
||||
|
||||
+def lookup_vm_vifs(session, vm_ref):
|
||||
+ """Look for the VIFs that are attached to the VM."""
|
||||
+ try:
|
||||
+ vif_refs = session.call_xenapi("VM.get_VIFs", vm_ref)
|
||||
+ except session.XenAPI.Failure:
|
||||
+ LOG.exception(_LE('"Look for the VIFs failed'))
|
||||
+ return vif_refs
|
||||
+
|
||||
+
|
||||
def lookup(session, name_label, check_rescue=False):
|
||||
"""Look the instance up and return it if available.
|
||||
:param:check_rescue: if True will return the 'name'-rescue vm if it
|
||||
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
|
||||
index 1fbc15a..0e4509f 100644
|
||||
--- a/nova/virt/xenapi/vmops.py
|
||||
+++ b/nova/virt/xenapi/vmops.py
|
||||
@@ -348,6 +348,15 @@ class VMOps(object):
|
||||
if bad_volumes_callback and bad_devices:
|
||||
bad_volumes_callback(bad_devices)
|
||||
|
||||
+ # Do some post start operations which needed to be done after start.
|
||||
+ self._post_start_actions(instance)
|
||||
+
|
||||
+ def _post_start_actions(self, instance):
|
||||
+ vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
+ vif_refs = vm_utils.lookup_vm_vifs(self._session, vm_ref)
|
||||
+ for vif_ref in vif_refs:
|
||||
+ self.vif_driver.post_start_actions(instance, vif_ref)
|
||||
+
|
||||
def _get_vdis_for_instance(self, context, instance, name_label,
|
||||
image_meta, image_type, block_device_info):
|
||||
"""Create or connect to all virtual disks for this instance."""
|
||||
@@ -513,9 +522,11 @@ class VMOps(object):
|
||||
return vm_ref
|
||||
|
||||
@step
|
||||
- def attach_devices_step(undo_mgr, vm_ref, vdis, disk_image_type):
|
||||
+ def attach_devices_step(undo_mgr, instance, vm_ref, vdis,
|
||||
+ disk_image_type, network_info):
|
||||
attach_disks(undo_mgr, vm_ref, vdis, disk_image_type)
|
||||
attach_pci_devices(undo_mgr, vm_ref)
|
||||
+ self._create_vifs(instance, vm_ref, network_info)
|
||||
|
||||
if rescue:
|
||||
# NOTE(johannes): Attach disks from original VM to rescue VM now,
|
||||
@@ -581,7 +592,8 @@ class VMOps(object):
|
||||
|
||||
vm_ref = create_vm_record_step(undo_mgr, disk_image_type,
|
||||
kernel_file, ramdisk_file)
|
||||
- attach_devices_step(undo_mgr, vm_ref, vdis, disk_image_type)
|
||||
+ attach_devices_step(undo_mgr, instance, vm_ref, vdis,
|
||||
+ disk_image_type, network_info)
|
||||
|
||||
inject_instance_data_step(undo_mgr, vm_ref, vdis)
|
||||
|
||||
@@ -1930,13 +1942,20 @@ class VMOps(object):
|
||||
"""Creates vifs for an instance."""
|
||||
|
||||
LOG.debug("Creating vifs", instance=instance)
|
||||
+ vif_refs = []
|
||||
|
||||
# this function raises if vm_ref is not a vm_opaque_ref
|
||||
self._session.call_xenapi("VM.get_domid", vm_ref)
|
||||
|
||||
for device, vif in enumerate(network_info):
|
||||
LOG.debug('Create VIF %s', vif, instance=instance)
|
||||
- self.vif_driver.plug(instance, vif, vm_ref=vm_ref, device=device)
|
||||
+ vif_ref = self.vif_driver.plug(instance, vif,
|
||||
+ vm_ref=vm_ref, device=device)
|
||||
+ vif_refs.append(vif_ref)
|
||||
+
|
||||
+ LOG.debug('Created the vif_refs: %(vifs)s for VM name: %(name)s',
|
||||
+ {'vifs': vif_refs, 'name': instance['name']},
|
||||
+ instance=instance)
|
||||
|
||||
def plug_vifs(self, instance, network_info):
|
||||
"""Set up VIF networking on the host."""
|
||||
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version
|
||||
index b67c84b..8674d0a 100755
|
||||
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version
|
||||
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version
|
||||
@@ -28,7 +28,13 @@ import utils
|
||||
# 1.0 - Initial version.
|
||||
# 1.1 - New call to check GC status
|
||||
# 1.2 - Added support for pci passthrough devices
|
||||
+<<<<<<< HEAD
|
||||
PLUGIN_VERSION = "1.2"
|
||||
+=======
|
||||
+# 1.3 - Add vhd2 functions for doing glance operations by url
|
||||
+# 1.4 - Added function for network configuration on ovs bridge
|
||||
+PLUGIN_VERSION = "1.4"
|
||||
+>>>>>>> 52d0d42... xenapi: OVS agent updates the wrong port when using XenServer + Neutron
|
||||
|
||||
def get_version(session):
|
||||
return PLUGIN_VERSION
|
||||
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
index 0e95e33..4bf85ac 100755
|
||||
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
|
||||
@@ -213,6 +213,22 @@ def iptables_config(session, args):
|
||||
raise pluginlib.PluginError(_("Invalid iptables command"))
|
||||
|
||||
|
||||
+def network_config(session, args):
|
||||
+ # function to config OVS bridge
|
||||
+ ALLOWED_CMDS = [
|
||||
+ 'ovs-vsctl',
|
||||
+ ]
|
||||
+ cmd = json.loads(args.get('cmd'))
|
||||
+ if cmd is None or cmd == []:
|
||||
+ msg = _("empty command is supplied")
|
||||
+ raise pluginlib.PluginError(msg)
|
||||
+ if cmd[0] not in ALLOWED_CMDS:
|
||||
+ msg = _("Dom0 execution of '%s' is not permitted") % cmd[0]
|
||||
+ raise pluginlib.PluginError(msg)
|
||||
+ result = _run_command(cmd, json.loads(args.get('cmd_input', 'null')))
|
||||
+ return json.dumps(result)
|
||||
+
|
||||
+
|
||||
def _power_action(action, arg_dict):
|
||||
# Host must be disabled first
|
||||
host_uuid = arg_dict['host_uuid']
|
||||
@@ -457,4 +473,5 @@ if __name__ == "__main__":
|
||||
"get_config": get_config,
|
||||
"set_config": set_config,
|
||||
"iptables_config": iptables_config,
|
||||
+ "network_config": network_config,
|
||||
"host_uptime": host_uptime})
|
|
@ -0,0 +1,376 @@
|
|||
diff --git a/nova/tests/unit/virt/xenapi/test_vif.py b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
new file mode 100644
|
||||
index 0000000..a41e506
|
||||
--- /dev/null
|
||||
+++ b/nova/tests/unit/virt/xenapi/test_vif.py
|
||||
@@ -0,0 +1,189 @@
|
||||
+# Copyright 2013 OpenStack Foundation
|
||||
+# All Rights Reserved.
|
||||
+#
|
||||
+# 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 nova import exception
|
||||
+from nova.network import model
|
||||
+from nova.tests.unit.virt.xenapi import stubs
|
||||
+from nova.virt.xenapi import network_utils
|
||||
+from nova.virt.xenapi import vif
|
||||
+
|
||||
+fake_vif = {
|
||||
+ 'created_at': None,
|
||||
+ 'updated_at': None,
|
||||
+ 'deleted_at': None,
|
||||
+ 'deleted': 0,
|
||||
+ 'id': '123456789123',
|
||||
+ 'address': '00:00:00:00:00:00',
|
||||
+ 'network_id': 123,
|
||||
+ 'instance_uuid': 'fake-uuid',
|
||||
+ 'uuid': 'fake-uuid-2',
|
||||
+}
|
||||
+
|
||||
+
|
||||
+def fake_call_xenapi(method, *args):
|
||||
+ if method == "VM.get_VIFs":
|
||||
+ return ["fake_vif_ref", "fake_vif_ref_A2"]
|
||||
+ if method == "VIF.get_record":
|
||||
+ if args[0] == "fake_vif_ref":
|
||||
+ return {'uuid': fake_vif['uuid'],
|
||||
+ 'MAC': fake_vif['address'],
|
||||
+ 'network': 'fake_network',
|
||||
+ 'other_config': {'nicira-iface-id': fake_vif['id']}
|
||||
+ }
|
||||
+ else:
|
||||
+ raise exception.Exception("Failed get vif record")
|
||||
+ if method == "VIF.unplug":
|
||||
+ return
|
||||
+ if method == "VIF.destroy":
|
||||
+ if args[0] == "fake_vif_ref":
|
||||
+ return
|
||||
+ else:
|
||||
+ raise exception.Exception("unplug vif failed")
|
||||
+ if method == "VIF.create":
|
||||
+ if args[0] == "fake_vif_rec":
|
||||
+ return "fake_vif_ref"
|
||||
+ else:
|
||||
+ raise exception.Exception("VIF existed")
|
||||
+ return "Unexpected call_xenapi: %s.%s" % (method, args)
|
||||
+
|
||||
+
|
||||
+class XenVIFDriverTestBase(stubs.XenAPITestBaseNoDB):
|
||||
+ def setUp(self):
|
||||
+ super(XenVIFDriverTestBase, self).setUp()
|
||||
+ self._session = mock.Mock()
|
||||
+ self._session.call_xenapi.side_effect = fake_call_xenapi
|
||||
+
|
||||
+
|
||||
+class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||
+ def setUp(self):
|
||||
+ super(XenVIFDriverTestCase, self).setUp()
|
||||
+ self.base_driver = vif.XenVIFDriver(self._session)
|
||||
+
|
||||
+ def test_get_vif_ref(self):
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ vif_ref = 'fake_vif_ref'
|
||||
+ ret_vif_ref = self.base_driver._get_vif_ref(fake_vif, vm_ref)
|
||||
+ self.assertEqual(vif_ref, ret_vif_ref)
|
||||
+
|
||||
+ expected = [mock.call('VM.get_VIFs', vm_ref),
|
||||
+ mock.call('VIF.get_record', vif_ref)]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+
|
||||
+ def test_get_vif_ref_none_and_exception(self):
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ vif = {'address': "no_match_vif_address"}
|
||||
+ ret_vif_ref = self.base_driver._get_vif_ref(vif, vm_ref)
|
||||
+ self.assertIsNone(ret_vif_ref)
|
||||
+
|
||||
+ expected = [mock.call('VM.get_VIFs', vm_ref),
|
||||
+ mock.call('VIF.get_record', 'fake_vif_ref'),
|
||||
+ mock.call('VIF.get_record', 'fake_vif_ref_A2')]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+
|
||||
+ def test_create_vif(self):
|
||||
+ vif_rec = "fake_vif_rec"
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ ret_vif_ref = self.base_driver._create_vif(fake_vif, vif_rec, vm_ref)
|
||||
+ self.assertEqual("fake_vif_ref", ret_vif_ref)
|
||||
+
|
||||
+ expected = [mock.call('VIF.create', vif_rec)]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+
|
||||
+ def test_create_vif_exception(self):
|
||||
+ self.assertRaises(exception.NovaException,
|
||||
+ self.base_driver._create_vif,
|
||||
+ "fake_vif", "missing_vif_rec", "fake_vm_ref")
|
||||
+
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ return_value='fake_vif_ref')
|
||||
+ def test_unplug(self, mock_get_vif_ref):
|
||||
+ instance = {'name': "fake_instance"}
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ self.base_driver.unplug(instance, fake_vif, vm_ref)
|
||||
+ expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ return_value='missing_vif_ref')
|
||||
+ def test_unplug_exception(self, mock_get_vif_ref):
|
||||
+ instance = "fake_instance"
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ self.assertRaises(exception.NovaException,
|
||||
+ self.base_driver.unplug,
|
||||
+ instance, fake_vif, vm_ref)
|
||||
+
|
||||
+
|
||||
+class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
|
||||
+ def setUp(self):
|
||||
+ super(XenAPIBridgeDriverTestCase, self).setUp()
|
||||
+ self.bridge_driver = vif.XenAPIBridgeDriver(self._session)
|
||||
+
|
||||
+ @mock.patch.object(vif.XenAPIBridgeDriver, '_ensure_vlan_bridge',
|
||||
+ return_value='fake_network_ref')
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
+ return_value='fake_vif_ref')
|
||||
+ def test_plug_create_vlan(self, mock_create_vif, mock_ensure_vlan_bridge):
|
||||
+ instance = {'name': "fake_instance_name"}
|
||||
+ network = model.Network()
|
||||
+ network._set_meta({'should_create_vlan': True})
|
||||
+ vif = model.VIF()
|
||||
+ vif._set_meta({'rxtx_cap': 1})
|
||||
+ vif['network'] = network
|
||||
+ vif['address'] = "fake_address"
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ device = 1
|
||||
+ ret_vif_ref = self.bridge_driver.plug(instance, vif, vm_ref, device)
|
||||
+ self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||
+
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ return_value='fake_vif_ref')
|
||||
+ def test_unplug(self, mock_get_vif_ref):
|
||||
+ instance = {'name': "fake_instance"}
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ self.bridge_driver.unplug(instance, fake_vif, vm_ref)
|
||||
+
|
||||
+ expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
+
|
||||
+
|
||||
+class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase):
|
||||
+ def setUp(self):
|
||||
+ super(XenAPIOpenVswitchDriverTestCase, self).setUp()
|
||||
+ self.ovs_driver = vif.XenAPIOpenVswitchDriver(self._session)
|
||||
+
|
||||
+ @mock.patch.object(network_utils, 'find_network_with_bridge',
|
||||
+ return_value='fake_network_ref')
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
||||
+ return_value='fake_vif_ref')
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref', return_value=None)
|
||||
+ def test_plug(self, mock_get_vif_ref, mock_create_vif,
|
||||
+ mock_find_network_with_bridge):
|
||||
+ instance = {'name': "fake_instance_name"}
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ device = 1
|
||||
+ ret_vif_ref = self.ovs_driver.plug(instance, fake_vif, vm_ref, device)
|
||||
+ self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||
+
|
||||
+ @mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
+ return_value='fake_vif_ref')
|
||||
+ def test_unplug(self, mock_get_vif_ref):
|
||||
+ instance = {'name': "fake_instance"}
|
||||
+ vm_ref = "fake_vm_ref"
|
||||
+ self.ovs_driver.unplug(instance, fake_vif, vm_ref)
|
||||
+
|
||||
+ expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
+ self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
|
||||
index 6b3218c..5c7a350 100644
|
||||
--- a/nova/virt/xenapi/vif.py
|
||||
+++ b/nova/virt/xenapi/vif.py
|
||||
@@ -18,8 +18,11 @@
|
||||
"""VIF drivers for XenAPI."""
|
||||
|
||||
from oslo_config import cfg
|
||||
+from oslo_log import log as logging
|
||||
|
||||
+from nova import exception
|
||||
from nova.i18n import _
|
||||
+from nova.i18n import _LW
|
||||
from nova.virt.xenapi import network_utils
|
||||
from nova.virt.xenapi import vm_utils
|
||||
|
||||
@@ -31,11 +34,56 @@ xenapi_ovs_integration_bridge_opt = cfg.StrOpt('ovs_integration_bridge',
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opt(xenapi_ovs_integration_bridge_opt, 'xenserver')
|
||||
|
||||
+LOG = logging.getLogger(__name__)
|
||||
+
|
||||
|
||||
class XenVIFDriver(object):
|
||||
def __init__(self, xenapi_session):
|
||||
self._session = xenapi_session
|
||||
|
||||
+ def _get_vif_ref(self, vif, vm_ref):
|
||||
+ vif_refs = self._session.call_xenapi("VM.get_VIFs", vm_ref)
|
||||
+ for vif_ref in vif_refs:
|
||||
+ try:
|
||||
+ vif_rec = self._session.call_xenapi('VIF.get_record', vif_ref)
|
||||
+ if vif_rec['MAC'] == vif['address']:
|
||||
+ return vif_ref
|
||||
+ except Exception:
|
||||
+ # When got exception here, maybe the vif is removed during the
|
||||
+ # loop, ignore this vif and continue
|
||||
+ continue
|
||||
+ return None
|
||||
+
|
||||
+ def _create_vif(self, vif, vif_rec, vm_ref):
|
||||
+ try:
|
||||
+ vif_ref = self._session.call_xenapi('VIF.create', vif_rec)
|
||||
+ except Exception as e:
|
||||
+ LOG.warn(_LW("Failed to create vif, exception:%(exception)s, "
|
||||
+ "vif:%(vif)s"), {'exception': e, 'vif': vif})
|
||||
+ raise exception.NovaException(
|
||||
+ reason=_("Failed to create vif %s") % vif)
|
||||
+
|
||||
+ LOG.debug("create vif %(vif)s for vm %(vm_ref)s successfully",
|
||||
+ {'vif': vif, 'vm_ref': vm_ref})
|
||||
+ return vif_ref
|
||||
+
|
||||
+ def unplug(self, instance, vif, vm_ref):
|
||||
+ try:
|
||||
+ LOG.debug("unplug vif, vif:%(vif)s, vm_ref:%(vm_ref)s",
|
||||
+ {'vif': vif, 'vm_ref': vm_ref}, instance=instance)
|
||||
+ vif_ref = self._get_vif_ref(vif, vm_ref)
|
||||
+ if not vif_ref:
|
||||
+ LOG.debug("vif didn't exist, no need to unplug vif %s",
|
||||
+ vif, instance=instance)
|
||||
+ return
|
||||
+ self._session.call_xenapi('VIF.destroy', vif_ref)
|
||||
+ except Exception as e:
|
||||
+ LOG.warn(
|
||||
+ _LW("Fail to unplug vif:%(vif)s, exception:%(exception)s"),
|
||||
+ {'vif': vif, 'exception': e}, instance=instance)
|
||||
+ raise exception.NovaException(
|
||||
+ reason=_("Failed to unplug vif %s") % vif)
|
||||
+
|
||||
|
||||
class XenAPIBridgeDriver(XenVIFDriver):
|
||||
"""VIF Driver for XenAPI that uses XenAPI to create Networks."""
|
||||
@@ -43,6 +91,14 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
||||
def plug(self, instance, vif, vm_ref=None, device=None):
|
||||
if not vm_ref:
|
||||
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
+
|
||||
+ # if VIF already exists, return this vif_ref directly
|
||||
+ vif_ref = self._get_vif_ref(vif, vm_ref)
|
||||
+ if vif_ref:
|
||||
+ LOG.debug("VIF %s already exists when plug vif",
|
||||
+ vif_ref, instance=instance)
|
||||
+ return vif_ref
|
||||
+
|
||||
if not device:
|
||||
device = 0
|
||||
|
||||
@@ -65,7 +121,7 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
||||
else:
|
||||
vif_rec['qos_algorithm_type'] = ''
|
||||
vif_rec['qos_algorithm_params'] = {}
|
||||
- return vif_rec
|
||||
+ return self._create_vif(vif, vif_rec, vm_ref)
|
||||
|
||||
def _ensure_vlan_bridge(self, network):
|
||||
"""Ensure that a VLAN bridge exists."""
|
||||
@@ -126,8 +182,8 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
||||
|
||||
return network_ref
|
||||
|
||||
- def unplug(self, instance, vif):
|
||||
- pass
|
||||
+ def unplug(self, instance, vif, vm_ref):
|
||||
+ super(XenAPIBridgeDriver, self).unplug(instance, vif, vm_ref)
|
||||
|
||||
|
||||
class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
@@ -137,6 +193,13 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
if not vm_ref:
|
||||
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
|
||||
+ # if VIF already exists, return this vif_ref directly
|
||||
+ vif_ref = self._get_vif_ref(vif, vm_ref)
|
||||
+ if vif_ref:
|
||||
+ LOG.debug("VIF %s already exists when plug vif",
|
||||
+ vif_ref, instance=instance)
|
||||
+ return vif_ref
|
||||
+
|
||||
if not device:
|
||||
device = 0
|
||||
|
||||
@@ -155,7 +218,7 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
# OVS on the hypervisor monitors this key and uses it to
|
||||
# set the iface-id attribute
|
||||
vif_rec['other_config'] = {'nicira-iface-id': vif['id']}
|
||||
- return vif_rec
|
||||
+ return self._create_vif(vif, vif_rec, vm_ref)
|
||||
|
||||
- def unplug(self, instance, vif):
|
||||
- pass
|
||||
+ def unplug(self, instance, vif, vm_ref):
|
||||
+ super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
||||
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
|
||||
index 294a2aa..65a41a3 100644
|
||||
--- a/nova/virt/xenapi/vmops.py
|
||||
+++ b/nova/virt/xenapi/vmops.py
|
||||
@@ -1576,11 +1576,10 @@ class VMOps(object):
|
||||
self._destroy_vdis(instance, vm_ref)
|
||||
self._destroy_kernel_ramdisk(instance, vm_ref)
|
||||
|
||||
- vm_utils.destroy_vm(self._session, instance, vm_ref)
|
||||
-
|
||||
- self.unplug_vifs(instance, network_info)
|
||||
+ self.unplug_vifs(instance, network_info, vm_ref)
|
||||
self.firewall_driver.unfilter_instance(
|
||||
instance, network_info=network_info)
|
||||
+ vm_utils.destroy_vm(self._session, instance, vm_ref)
|
||||
|
||||
def pause(self, instance):
|
||||
"""Pause VM instance."""
|
||||
@@ -1896,25 +1895,18 @@ class VMOps(object):
|
||||
self._session.call_xenapi("VM.get_domid", vm_ref)
|
||||
|
||||
for device, vif in enumerate(network_info):
|
||||
- vif_rec = self.vif_driver.plug(instance, vif,
|
||||
- vm_ref=vm_ref, device=device)
|
||||
- network_ref = vif_rec['network']
|
||||
- LOG.debug('Creating VIF for network %s',
|
||||
- network_ref, instance=instance)
|
||||
- vif_ref = self._session.call_xenapi('VIF.create', vif_rec)
|
||||
- LOG.debug('Created VIF %(vif_ref)s, network %(network_ref)s',
|
||||
- {'vif_ref': vif_ref, 'network_ref': network_ref},
|
||||
- instance=instance)
|
||||
+ LOG.debug('Create VIF %s', vif, instance=instance)
|
||||
+ self.vif_driver.plug(instance, vif, vm_ref=vm_ref, device=device)
|
||||
|
||||
def plug_vifs(self, instance, network_info):
|
||||
"""Set up VIF networking on the host."""
|
||||
for device, vif in enumerate(network_info):
|
||||
self.vif_driver.plug(instance, vif, device=device)
|
||||
|
||||
- def unplug_vifs(self, instance, network_info):
|
||||
+ def unplug_vifs(self, instance, network_info, vm_ref):
|
||||
if network_info:
|
||||
for vif in network_info:
|
||||
- self.vif_driver.unplug(instance, vif)
|
||||
+ self.vif_driver.unplug(instance, vif, vm_ref)
|
||||
|
||||
def reset_network(self, instance, rescue=False):
|
||||
"""Calls resetnetwork method in agent."""
|
Loading…
Reference in New Issue