]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
In Arista ML2 driver Reconfigure VLAN on VM migration
authorShashank Hegde <shashank@arista.com>
Fri, 3 Jul 2015 01:16:56 +0000 (18:16 -0700)
committerSukhdev Kapur <sukhdev@aristanetworks.com>
Wed, 8 Jul 2015 17:36:34 +0000 (17:36 +0000)
Whenever a VM moves from one compute node to the other, the VLAN on the old
switch interface was not removed and the VLAN was not being provisioned on the
new switch interface. With this patch, the VLANs are provisioned correctly.

Closes-Bug: #1471050
Change-Id: I658ef87ffd82119f41beda9ba019e29a62dc96ff

neutron/plugins/ml2/drivers/arista/mechanism_arista.py
neutron/tests/unit/plugins/ml2/drivers/arista/test_mechanism_arista.py

index b82bf1af23cb54a86e63e07ac16271acdb1388a8..9d746bfb6570fe0e24774540e9bff396cda79b4d 100644 (file)
@@ -267,6 +267,22 @@ class AristaDriver(driver_api.MechanismDriver):
         orig_port = context.original
         if new_port['name'] != orig_port['name']:
             LOG.info(_LI('Port name changed to %s'), new_port['name'])
+        new_port = context.current
+        device_id = new_port['device_id']
+        device_owner = new_port['device_owner']
+        host = context.host
+
+        # device_id and device_owner are set on VM boot
+        is_vm_boot = device_id and device_owner
+        if host and host != orig_port['binding:host_id'] and is_vm_boot:
+            port_id = new_port['id']
+            network_id = new_port['network_id']
+            tenant_id = new_port['tenant_id']
+            if not tenant_id:
+                tenant_id = context._plugin_context.tenant_id
+            with self.eos_sync_lock:
+                db_lib.update_vm_host(device_id, host, port_id,
+                                      network_id, tenant_id)
 
     def update_port_postcommit(self, context):
         """Update the name of a given port in EOS.
@@ -276,9 +292,6 @@ class AristaDriver(driver_api.MechanismDriver):
         """
         port = context.current
         orig_port = context.original
-        if port['name'] == orig_port['name']:
-            # nothing to do
-            return
 
         device_id = port['device_id']
         device_owner = port['device_owner']
@@ -310,6 +323,12 @@ class AristaDriver(driver_api.MechanismDriver):
                 )
                 if vm_provisioned and net_provisioned:
                     try:
+                        orig_host = orig_port['binding:host_id']
+                        if host != orig_host:
+                            # The port moved to a different host. So delete the
+                            # old port on the old host before creating a new
+                            # port on the new host.
+                            self._delete_port(port, orig_host, tenant_id)
                         self.rpc.plug_port_into_network(device_id,
                                                         hostname,
                                                         port_id,
@@ -348,30 +367,42 @@ class AristaDriver(driver_api.MechanismDriver):
         from appropriate network.
         """
         port = context.current
-        device_id = port['device_id']
         host = context.host
-        port_id = port['id']
-        network_id = port['network_id']
         tenant_id = port['tenant_id']
         if not tenant_id:
             tenant_id = context._plugin_context.tenant_id
+
+        with self.eos_sync_lock:
+            self._delete_port(port, host, tenant_id)
+
+    def _delete_port(self, port, host, tenant_id):
+        """Deletes the port from EOS.
+
+        param port: Port which is to be deleted
+        param host: The host on which the port existed
+        param tenant_id: The tenant to which the port belongs to. Some times
+                         the tenant id in the port dict is not present (as in
+                         the case of HA router).
+        """
+        device_id = port['device_id']
+        port_id = port['id']
+        network_id = port['network_id']
         device_owner = port['device_owner']
 
         try:
-            with self.eos_sync_lock:
-                hostname = self._host_name(host)
-                if device_owner == n_const.DEVICE_OWNER_DHCP:
-                    self.rpc.unplug_dhcp_port_from_network(device_id,
-                                                           hostname,
-                                                           port_id,
-                                                           network_id,
-                                                           tenant_id)
-                else:
-                    self.rpc.unplug_host_from_network(device_id,
-                                                      hostname,
-                                                      port_id,
-                                                      network_id,
-                                                      tenant_id)
+            hostname = self._host_name(host)
+            if device_owner == n_const.DEVICE_OWNER_DHCP:
+                self.rpc.unplug_dhcp_port_from_network(device_id,
+                                                       hostname,
+                                                       port_id,
+                                                       network_id,
+                                                       tenant_id)
+            else:
+                self.rpc.unplug_host_from_network(device_id,
+                                                  hostname,
+                                                  port_id,
+                                                  network_id,
+                                                  tenant_id)
             # if necessary, delete tenant as well.
             self.delete_tenant(tenant_id)
         except arista_exc.AristaRpcError:
index d743d6aa4833dc00bf34f9cb49560cca79083b31..57d5071214879c2cb22ba3da81030bcbf9737d80 100644 (file)
@@ -254,6 +254,84 @@ class AristaDriverTestCase(testlib_api.SqlTestCase):
 
         mechanism_arista.db_lib.assert_has_calls(expected_calls)
 
+    def test_update_port_precommit(self):
+        tenant_id = 'ten-1'
+        network_id = 'net1-id'
+        segmentation_id = 1001
+        vm_id = 'vm1'
+
+        network_context = self._get_network_context(tenant_id,
+                                                    network_id,
+                                                    segmentation_id,
+                                                    False)
+
+        port_context = self._get_port_context(tenant_id,
+                                              network_id,
+                                              vm_id,
+                                              network_context)
+        host_id = port_context.current['binding:host_id']
+        port_context.original['binding:host_id'] = 'ubuntu0'
+        port_id = port_context.current['id']
+        self.drv.update_port_precommit(port_context)
+
+        expected_calls = [
+            mock.call.update_vm_host(vm_id, host_id, port_id,
+                                     network_id, tenant_id)
+        ]
+
+        mechanism_arista.db_lib.assert_has_calls(expected_calls)
+
+    def test_update_port_postcommit(self):
+        tenant_id = 'ten-1'
+        network_id = 'net1-id'
+        segmentation_id = 1001
+        vm_id = 'vm1'
+
+        network_context = self._get_network_context(tenant_id,
+                                                    network_id,
+                                                    segmentation_id,
+                                                    False)
+        port_context = self._get_port_context(tenant_id,
+                                              network_id,
+                                              vm_id,
+                                              network_context)
+
+        mechanism_arista.db_lib.is_vm_provisioned.return_value = True
+        mechanism_arista.db_lib.is_network_provisioned.return_value = True
+        mechanism_arista.db_lib.get_shared_network_owner_id.return_value = 1
+        mechanism_arista.db_lib.get_segmentation_id.return_value = 1001
+        mechanism_arista.db_lib.num_nets_provisioned.return_value = 1
+        mechanism_arista.db_lib.num_vms_provisioned.return_value = 1
+
+        port = port_context.current
+        device_id = port['device_id']
+        device_owner = port['device_owner']
+        host_id = port['binding:host_id']
+        orig_host_id = 'ubuntu0'
+        port_context.original['binding:host_id'] = orig_host_id
+        port_id = port['id']
+        port_name = port['name']
+
+        self.drv.update_port_postcommit(port_context)
+
+        expected_calls = [
+            mock.call.NeutronNets(),
+            mock.call.get_segmentation_id(tenant_id, network_id),
+            mock.call.is_vm_provisioned(device_id, host_id, port_id,
+                                        network_id, tenant_id),
+            mock.call.is_network_provisioned(tenant_id, network_id,
+                                             segmentation_id),
+            mock.call.unplug_host_from_network(device_id, orig_host_id,
+                                               port_id, network_id, tenant_id),
+            mock.call.num_nets_provisioned(tenant_id),
+            mock.call.num_vms_provisioned(tenant_id),
+            mock.call.plug_port_into_network(device_id, host_id, port_id,
+                                             network_id, tenant_id,
+                                             port_name, device_owner)
+        ]
+
+        mechanism_arista.db_lib.assert_has_calls(expected_calls)
+
     def _get_network_context(self, tenant_id, net_id, seg_id, shared):
         network = {'id': net_id,
                    'tenant_id': tenant_id,
@@ -271,7 +349,7 @@ class AristaDriverTestCase(testlib_api.SqlTestCase):
                 'id': 101,
                 'network_id': net_id
                 }
-        return FakePortContext(port, port, network)
+        return FakePortContext(port, dict(port), network)
 
 
 class fake_keystone_info_class(object):