Comment 14 for bug 1187244

Revision history for this message
Racha Ben Ali (racha-ben-ali) wrote :

Using AWS, I am able to successfully attach a second ENI (an Elastic Network Interface, i.e. a vNIC) to the same VM instance in the same subnet.
There are probably very few use cases where you could optionally want to have two or more vNICs in same L2 for same VM. The network admin is start running out of the limited number of 4K VLANs, so the Neutron NEtwork quota is setup to one Neutron Network per tenant. You need a VM in multiple subnets, you will be forced to attach multiple vNICs for that VM on same L2 (but different or even same Neutron Subnet). This is currently imporssible with latest Neutron client in Nova.
Other use case I guess, is that L2 loops are not a problem because you control what is provisioned inside netowork infra service VM (in contrast with user VMs bridging multiple vNIC in different L2 in same/multiple VMs). Or you;re using an external Openflow controller to distinguish between virtual networks on top of the same single physical network VLAN enabled for Openflow....

I added a patch below to enable this. I guess if we feel that it is not allowed in most cases, then we can only opt-in to enable it using a flag in the configuration file.

The patch to be applied to ./nova/nova/network/neutronv2/api.py is below.
It applies to booting VMs with multiple vNICs on same network with both --nic net-id <netid1> --nic net-id <netid1>, … (i.e. ports to be created on the same network) and --nic port-id <portid1> --nic port-id <portid2>, … (i.e. ports already created on the same network)

--- ./nova/network/neutronv2/api.py 2014-01-04 01:51:34.207006405 +0000
+++ ../nova_multi_nic_per_network_per_VM/nova/network/neutronv2/api.py 2014-01-05 23:46:35.451006405 +0000
@@ -219,6 +219,7 @@ class API(base.Base):
         ports = {}
         fixed_ips = {}
         net_ids = []
+ nets=[]
         if requested_networks:
             for network_id, fixed_ip, port_id in requested_networks:
                 if port_id:
@@ -236,15 +237,17 @@ class API(base.Base):
                             # discard rather than popping.
                             available_macs.discard(port['mac_address'])
                     network_id = port['network_id']
- ports[network_id] = port
+ if network_id in ports.keys():
+ ports[network_id].append(port)
+ else:
+ ports[network_id] = [ port ]
                 elif fixed_ip and network_id:
                     fixed_ips[network_id] = fixed_ip
                 if network_id:
                     net_ids.append(network_id)
-
- nets = self._get_available_networks(context, instance['project_id'],
- net_ids)
-
+ for net_id in net_ids:
+ nets += self._get_available_networks(context, instance['project_id'], net_id)
         if not nets:
             LOG.warn(_("No network configured!"), instance=instance)
             return network_model.NetworkInfo([])
@@ -302,17 +305,18 @@ class API(base.Base):
             port_req_body = {'port': {'device_id': instance['uuid'],
                                       'device_owner': zone}}
             try:
- port = ports.get(network_id)
- self._populate_neutron_extension_values(instance,
+ ports_in_network = ports.get(network_id)
+ self._populate_neutron_extension_values(instance,
                                                         port_req_body)
- # Requires admin creds to set port bindings
- port_client = (neutron if not
+ # Requires admin creds to set port bindings
+ port_client = (neutron if not
                                self._has_port_binding_extension() else
                                neutronv2.get_client(context, admin=True))
- if port:
- port_client.update_port(port['id'], port_req_body)
- touched_port_ids.append(port['id'])
- else:
+ if ports_in_network:
+ for port in ports_in_network:
+ port_client.update_port(port['id'], port_req_body)
+ touched_port_ids.append(port['id'])
+ else:
                     created_port_ids.append(self._create_port(
                             port_client, instance, network_id,
                             port_req_body, fixed_ips.get(network_id),
@@ -554,10 +558,11 @@ class API(base.Base):
                     net_id = port['network_id']
                 else:
                     ports_needed_per_instance += 1
-
- if net_id in net_ids:
- raise exception.NetworkDuplicated(network_id=net_id)
- net_ids.append(net_id)
+ if net_id not in net_ids:
+ net_ids.append(net_id)

             # Now check to see if all requested networks exist
             nets = self._get_available_networks(context,