Adding the ability to use pre-created ports in Network scenarios

It's possible to launch an instance with virtual interfaces
either by specifying the network the interface should be attached
to or the port.
There are cases (e.g. SR-IOV) where it's only possible to launch
an instance with a pre-created port.
Enabling the creation of  a port with known vnic_types (Currently
'normal','direct' and 'macvtap') and launching an instance with this port
attached.

Nova support Neutron SR-IOV ports https://wiki.openstack.org/wiki/Nova-neutron-sriov

Change-Id: Id5cbd50d285ac0c7e8099c30151ace9d99f02020
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 0eeb738..bc4198f 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -779,6 +779,10 @@
 # value)
 #dns_servers = 8.8.8.8,8.8.4.4
 
+# vnic_type to use when Launching instances with pre-configured ports.
+# Supported ports are: ['normal','direct','macvtap'] (string value)
+#port_vnic_type = <None>
+
 
 [network-feature-enabled]
 
diff --git a/tempest/config.py b/tempest/config.py
index c60434a..4588b20 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -455,7 +455,13 @@
     cfg.ListOpt('dns_servers',
                 default=["8.8.8.8", "8.8.4.4"],
                 help="List of dns servers which should be used"
-                     " for subnet creation")
+                     " for subnet creation"),
+    cfg.StrOpt('port_vnic_type',
+               choices=[None, 'normal', 'direct', 'macvtap'],
+               help="vnic_type to use when Launching instances"
+                    " with pre-configured ports."
+                    " Supported ports are:"
+                    " ['normal','direct','macvtap']"),
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b1f5cce..2acee96 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -639,14 +639,15 @@
         self.addCleanup(self.delete_wrapper, subnet.delete)
         return subnet
 
-    def _create_port(self, network, client=None, namestart='port-quotatest'):
+    def _create_port(self, network_id, client=None, namestart='port-quotatest',
+                     **kwargs):
         if not client:
             client = self.network_client
         name = data_utils.rand_name(namestart)
         result = client.create_port(
             name=name,
-            network_id=network.id,
-            tenant_id=network.tenant_id)
+            network_id=network_id,
+            **kwargs)
         self.assertIsNotNone(result, 'Unable to allocate port')
         port = net_resources.DeletablePort(client=client,
                                            **result['port'])
@@ -1047,6 +1048,66 @@
             subnet.add_to_router(router.id)
         return network, subnet, router
 
+    def create_server(self, name=None, image=None, flavor=None,
+                      wait_on_boot=True, wait_on_delete=True,
+                      create_kwargs=None):
+        vnic_type = CONF.network.port_vnic_type
+
+        # If vnic_type is configured create port for
+        # every network
+        if vnic_type:
+            ports = []
+            networks = []
+            create_port_body = {'binding:vnic_type': vnic_type,
+                                'namestart': 'port-smoke'}
+            if create_kwargs:
+                net_client = create_kwargs.get("network_client",
+                                               self.network_client)
+
+                # Convert security group names to security group ids
+                # to pass to create_port
+                if create_kwargs.get('security_groups'):
+                    security_groups = net_client.list_security_groups().get(
+                        'security_groups')
+                    sec_dict = dict([(s['name'], s['id'])
+                                    for s in security_groups])
+
+                    sec_groups_names = [s['name'] for s in create_kwargs[
+                        'security_groups']]
+                    security_groups_ids = [sec_dict[s]
+                                           for s in sec_groups_names]
+
+                    if security_groups_ids:
+                        create_port_body[
+                            'security_groups'] = security_groups_ids
+                networks = create_kwargs.get('networks')
+            else:
+                net_client = self.network_client
+            # If there are no networks passed to us we look up
+            # for the tenant's private networks and create a port
+            # if there is only one private network. The same behaviour
+            # as we would expect when passing the call to the clients
+            # with no networks
+            if not networks:
+                networks = net_client.list_networks(filters={
+                    'router:external': False})
+                self.assertEqual(1, len(networks),
+                                 "There is more than one"
+                                 " network for the tenant")
+            for net in networks:
+                net_id = net['uuid']
+                port = self._create_port(network_id=net_id,
+                                         client=net_client,
+                                         **create_port_body)
+                ports.append({'port': port.id})
+            if ports:
+                create_kwargs['networks'] = ports
+
+        return super(NetworkScenarioTest, self).create_server(
+            name=name, image=image, flavor=flavor,
+            wait_on_boot=wait_on_boot, wait_on_delete=wait_on_delete,
+            create_kwargs=create_kwargs)
+
 
 # power/provision states as of icehouse
 class BaremetalPowerStates(object):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index fc174cd..ae326c4 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -381,6 +381,9 @@
 
     @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
                           'NIC hotplug not available')
+    @testtools.skipIf(CONF.network.port_vnic_type in ['direct', 'macvtap'],
+                      'NIC hotplug not supported for '
+                      'vnic_type direct or macvtap')
     @test.attr(type='smoke')
     @test.services('compute', 'network')
     def test_hotplug_nic(self):
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 83739fd..55ab18f 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -238,7 +238,8 @@
             ],
             'key_name': tenant.keypair['name'],
             'security_groups': security_groups_names,
-            'tenant_id': tenant.creds.tenant_id
+            'tenant_id': tenant.creds.tenant_id,
+            'network_client': tenant.manager.network_client
         }
         server = self.create_server(name=name, create_kwargs=create_kwargs)
         self.assertEqual(