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,
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 multi_nic_ per_network_ per_VM/ nova/network/ neutronv2/ api.py 2014-01-05 23:46:35.451006405 +0000
# discard rather than popping.
available_ macs.discard( port['mac_ address' ])
network_ id = port['network_id'] id].append( port)
elif fixed_ip and network_id:
fixed_ips[ network_ id] = fixed_ip
net_ids. append( network_ id) available_ networks( context, instance[ 'project_ id'], available_ networks( context, instance[ 'project_ id'], net_id)
LOG. warn(_( "No network configured!"), instance=instance) model.NetworkIn fo([])
port_ req_body = {'port': {'device_id': instance['uuid'],
'device_ owner': zone}} network_ id) neutron_ extension_ values( instance, network_ id) neutron_ extension_ values( instance,
port_req_ body)
self. _has_port_ binding_ extension( ) else
neutronv2. get_client( context, admin=True)) update_ port(port[ 'id'], port_req_body) port_ids. append( port['id' ]) update_ port(port[ 'id'], port_req_body) port_ids. append( port['id' ])
created_ port_ids. append( self._create_ port(
port_client, instance, network_id,
port_req_ body, fixed_ips. get(network_ id),
net_id = port['network_id']
else:
ports_needed_ per_instance += 1 NetworkDuplicat ed(network_ id=net_ id) append( net_id) append( net_id)
+++ ../nova_
@@ -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):
- ports[network_id] = port
+ if network_id in ports.keys():
+ ports[network_
+ else:
+ ports[network_id] = [ port ]
if network_id:
-
- nets = self._get_
- net_ids)
-
+ for net_id in net_ids:
+ nets += self._get_
if not nets:
return network_
@@ -302,17 +305,18 @@ class API(base.Base):
try:
- port = ports.get(
- self._populate_
+ ports_in_network = ports.get(
+ self._populate_
- # Requires admin creds to set port bindings
- port_client = (neutron if not
+ # Requires admin creds to set port bindings
+ port_client = (neutron if not
- if port:
- port_client.
- touched_
- else:
+ if ports_in_network:
+ for port in ports_in_network:
+ port_client.
+ touched_
+ else:
@@ -554,10 +558,11 @@ class API(base.Base):
-
- if net_id in net_ids:
- raise exception.
- net_ids.
+ if net_id not in net_ids:
+ net_ids.
# Now check to see if all requested networks exist available_ networks( context,
nets = self._get_