Multi-NIC support for vmwareapi virt driver in nova.

Does injection of Multi-NIC information to instances with Operating system flavors Ubuntu, Windows and RHEL.
vmwareapi virt driver now relies on calls to network manager instead of nova db calls for network configuration information of instance.
Ensure if port group is properly associated with vlan_interface specified in case of VLAN networking for instances.
Re-oranized VMWareVlanBridgeDriver and added session parmeter to methods to use existing session. Also removed session creation code as session comes as argument.
Added check for flat_inject flag before attempting an inject operation.
Removed stale code from vmwareapi stubs.
Also updated some comments to be more meaningful.
Did pep8 and pylint checks. Tried to improve pylint score for newly added lines of code.
This commit is contained in:
sateesh 2011-09-05 12:51:07 +05:30
parent 78a63bcad5
commit 6930c62a02
6 changed files with 122 additions and 107 deletions

View File

@ -45,8 +45,6 @@ def set_stubs(stubs):
stubs.Set(vmware_images, 'get_vmdk_size_and_properties',
fake.fake_get_vmdk_size_and_properties)
stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
fake_get_vim_object)
stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
fake_get_vim_object)
stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object",

View File

@ -409,7 +409,7 @@ def fake_plug_vifs(*args, **kwargs):
def fake_get_network(*args, **kwargs):
"""Fake get network."""
return [{'type': 'fake'}]
return {'type': 'fake'}
def fake_fetch_image(image, instance, **kwargs):

View File

@ -17,42 +17,35 @@
"""VIF drivers for VMWare."""
from nova import db
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
from nova.virt.vif import VIFDriver
from nova.virt.vmwareapi_conn import VMWareAPISession
from nova.virt.vmwareapi import network_utils
LOG = logging.getLogger("nova.virt.vmwareapi.vif")
FLAGS = flags.FLAGS
FLAGS['vmwareapi_vlan_interface'].SetDefault('vmnic0')
class VMWareVlanBridgeDriver(VIFDriver):
"""VIF Driver to setup bridge/VLAN networking using VMWare API."""
def plug(self, instance, network, mapping):
"""Plug the VIF to specified instance using information passed.
Currently we are plugging the VIF(s) during instance creation itself.
We can use this method when we add support to add additional NIC to
an existing instance."""
pass
def ensure_vlan_bridge(self, session, network):
"""Create a vlan and bridge unless they already exist."""
vlan_num = network['vlan']
bridge = network['bridge']
bridge_interface = network['bridge_interface']
vlan_interface = FLAGS.vmwareapi_vlan_interface
# Open vmwareapi session
host_ip = FLAGS.vmwareapi_host_ip
host_username = FLAGS.vmwareapi_host_username
host_password = FLAGS.vmwareapi_host_password
if not host_ip or host_username is None or host_password is None:
raise Exception(_('Must specify vmwareapi_host_ip, '
'vmwareapi_host_username '
'and vmwareapi_host_password to use '
'connection_type=vmwareapi'))
session = VMWareAPISession(host_ip, host_username, host_password,
FLAGS.vmwareapi_api_retry_count)
vlan_interface = bridge_interface
# Check if the vlan_interface physical network adapter exists on the
# host.
if not network_utils.check_if_vlan_interface_exists(session,
@ -92,4 +85,6 @@ class VMWareVlanBridgeDriver(VIFDriver):
pgroup=pg_vlanid)
def unplug(self, instance, network, mapping):
"""Cleanup operations like deleting port group if no instance
is associated with it."""
pass

View File

@ -39,8 +39,7 @@ def split_datastore_path(datastore_path):
def get_vm_create_spec(client_factory, instance, data_store_name,
network_name="vmnet0",
os_type="otherGuest", network_ref=None):
vif_infos, os_type="otherGuest"):
"""Builds the VM Create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
config_spec.name = instance.name
@ -61,14 +60,12 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
config_spec.numCPUs = int(instance.vcpus)
config_spec.memoryMB = int(instance.memory_mb)
mac_address = None
if instance['mac_addresses']:
mac_address = instance['mac_addresses'][0]['address']
vif_spec_list = []
for vif_info in vif_infos:
vif_spec = create_network_spec(client_factory, vif_info)
vif_spec_list.append(vif_spec)
nic_spec = create_network_spec(client_factory,
network_name, mac_address)
device_config_spec = [nic_spec]
device_config_spec = vif_spec_list
config_spec.deviceChange = device_config_spec
return config_spec
@ -93,8 +90,7 @@ def create_controller_spec(client_factory, key):
return virtual_device_config
def create_network_spec(client_factory, network_name, mac_address,
network_ref=None):
def create_network_spec(client_factory, vif_info):
"""
Builds a config spec for the addition of a new network
adapter to the VM.
@ -109,6 +105,9 @@ def create_network_spec(client_factory, network_name, mac_address,
# NOTE(asomya): Only works on ESXi if the portgroup binding is set to
# ephemeral. Invalid configuration if set to static and the NIC does
# not come up on boot if set to dynamic.
network_ref = vif_info['network_ref']
network_name = vif_info['network_name']
mac_address = vif_info['mac_address']
backing = None
if (network_ref and
network_ref['type'] == "DistributedVirtualPortgroup"):
@ -295,11 +294,8 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name):
return config_spec
def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask,
gateway, broadcast, dns):
def get_machine_id_change_spec(client_factory, machine_id_str):
"""Builds the machine id change config spec."""
machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask,
gateway, broadcast, dns)
virtual_machine_config_spec = \
client_factory.create('ns0:VirtualMachineConfigSpec')

View File

@ -27,7 +27,6 @@ import urllib2
import uuid
from nova import context as nova_context
from nova import db
from nova import exception
from nova import flags
from nova import log as logging
@ -111,22 +110,6 @@ class VMWareVMOps(object):
client_factory = self._session._get_vim().client.factory
service_content = self._session._get_vim().get_service_content()
network = db.network_get_by_instance(nova_context.get_admin_context(),
instance['id'])
net_name = network['bridge']
def _check_if_network_bridge_exists():
network_ref = \
network_utils.get_network_with_the_name(self._session,
net_name)
if network_ref is None:
raise exception.NetworkNotFoundForBridge(bridge=net_name)
return network_ref
self.plug_vifs(instance, network_info)
network_obj = _check_if_network_bridge_exists()
def _get_datastore_ref():
"""Get the datastore list and choose the first local storage."""
data_stores = self._session._call_method(vim_util, "get_objects",
@ -182,11 +165,36 @@ class VMWareVMOps(object):
vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors()
def _check_if_network_bridge_exists(network_name):
network_ref = \
network_utils.get_network_with_the_name(self._session,
network_name)
if network_ref is None:
raise exception.NetworkNotFoundForBridge(bridge=network_name)
return network_ref
def _get_vif_infos():
vif_infos = []
for (network, mapping) in network_info:
mac_address = mapping['mac']
network_name = network['bridge']
if mapping.get('should_create_vlan'):
network_ref = self._vif_driver.ensure_vlan_bridge(
self._session, network)
else:
network_ref = _check_if_network_bridge_exists(network_name)
vif_infos.append({'network_name': network_name,
'mac_address': mac_address,
'network_ref': network_ref,
})
return vif_infos
vif_infos = _get_vif_infos()
# Get the create vm config spec
config_spec = vm_util.get_vm_create_spec(
client_factory, instance,
data_store_name, net_name, os_type,
network_obj)
data_store_name, vif_infos, os_type)
def _execute_create_vm():
"""Create VM on ESX host."""
@ -204,8 +212,10 @@ class VMWareVMOps(object):
_execute_create_vm()
# Set the machine id for the VM for setting the IP
self._set_machine_id(client_factory, instance)
# Set the machine.id parameter of the instance to inject
# the NIC configuration inside the VM
if FLAGS.flat_injected:
self._set_machine_id(client_factory, instance, network_info)
# Naming the VM files in correspondence with the VM instance name
# The flat vmdk file name
@ -716,39 +726,45 @@ class VMWareVMOps(object):
"""Return link to instance's ajax console."""
return 'http://fakeajaxconsole/fake_url'
def _set_machine_id(self, client_factory, instance):
def _set_machine_id(self, client_factory, instance, network_info):
"""
Set the machine id of the VM for guest tools to pick up and change
the IP.
Set the machine id of the VM for guest tools to pick up and reconfigure
the network interfaces.
"""
admin_context = nova_context.get_admin_context()
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.InstanceNotFound(instance_id=instance.id)
network = db.network_get_by_instance(nova_context.get_admin_context(),
instance['id'])
mac_address = None
if instance['mac_addresses']:
mac_address = instance['mac_addresses'][0]['address']
net_mask = network["netmask"]
gateway = network["gateway"]
broadcast = network["broadcast"]
machine_id_str = ''
for (network, info) in network_info:
# TODO(vish): add support for dns2
dns = network["dns1"]
# TODO(sateesh): add support for injection of ipv6 configuration
ip_v4 = ip_v6 = None
if 'ips' in info and len(info['ips']) > 0:
ip_v4 = info['ips'][0]
if 'ip6s' in info and len(info['ip6s']) > 0:
ip_v6 = info['ip6s'][0]
if len(info['dns']) > 0:
dns = info['dns'][0]
else:
dns = ''
addresses = db.instance_get_fixed_addresses(admin_context,
instance['id'])
ip_addr = addresses[0] if addresses else None
interface_str = "%s;%s;%s;%s;%s;%s" % \
(info['mac'],
ip_v4 and ip_v4['ip'] or '',
ip_v4 and ip_v4['netmask'] or '',
info['gateway'],
info['broadcast'],
dns)
machine_id_str = machine_id_str + interface_str + '#'
machine_id_change_spec = \
vm_util.get_machine_id_change_spec(client_factory, mac_address,
ip_addr, net_mask, gateway,
broadcast, dns)
vm_util.get_machine_id_change_spec(client_factory, machine_id_str)
LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id "
"with ip - %(ip_addr)s") %
({'name': instance.name,
'ip_addr': ip_addr}))
'ip_addr': ip_v4['ip']}))
reconfig_task = self._session._call_method(self._session._get_vim(),
"ReconfigVM_Task", vm_ref,
spec=machine_id_change_spec)
@ -756,7 +772,7 @@ class VMWareVMOps(object):
LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id "
"with ip - %(ip_addr)s") %
({'name': instance.name,
'ip_addr': ip_addr}))
'ip_addr': ip_v4['ip']}))
def _get_datacenter_name_and_ref(self):
"""Get the datacenter name and the reference."""

View File

@ -81,16 +81,21 @@ def _bytes2int(bytes):
def _parse_network_details(machine_id):
"""
Parse the machine.id field to get MAC, IP, Netmask and Gateway fields
machine.id is of the form MAC;IP;Netmask;Gateway;Broadcast;DNS1,DNS2
where ';' is the separator.
Parse the machine_id to get MAC, IP, Netmask and Gateway fields per NIC.
machine_id is of the form ('NIC_record#NIC_record#', '')
Each of the NIC will have record NIC_record in the form
'MAC;IP;Netmask;Gateway;Broadcast;DNS' where ';' is field separator.
Each record is separated by '#' from next record.
"""
logging.debug(_("Received machine_id from vmtools : %s") % machine_id[0])
network_details = []
if machine_id[1].strip() == "1":
pass
else:
network_info_list = machine_id[0].split(';')
assert len(network_info_list) % 6 == 0
for machine_id_str in machine_id[0].split('#'):
network_info_list = machine_id_str.split(';')
if len(network_info_list) % 6 != 0:
break
no_grps = len(network_info_list) / 6
i = 0
while i < no_grps:
@ -103,6 +108,7 @@ def _parse_network_details(machine_id):
network_info_list[k + 4].strip(),
network_info_list[k + 5].strip().split(',')))
i += 1
logging.debug(_("NIC information from vmtools : %s") % network_details)
return network_details
@ -279,6 +285,7 @@ def _filter_duplicates(all_entries):
def _set_rhel_networking(network_details=None):
"""Set IPv4 network settings for RHEL distros."""
network_details = network_details or []
all_dns_servers = []
for network_detail in network_details:
@ -320,31 +327,33 @@ def _set_rhel_networking(network_details=None):
def _set_ubuntu_networking(network_details=None):
"""Set IPv4 network settings for Ubuntu."""
network_details = network_details or []
""" Set IPv4 network settings for Ubuntu """
all_dns_servers = []
for network_detail in network_details:
interface_file_name = '/etc/network/interfaces'
# Remove file
os.remove(interface_file_name)
# Touch file
_execute(['touch', interface_file_name])
interface_file = open(interface_file_name, 'w')
for device, network_detail in enumerate(network_details):
mac_address, ip_address, subnet_mask, gateway, broadcast,\
dns_servers = network_detail
all_dns_servers.extend(dns_servers)
adapter_name, current_ip_address = \
_get_linux_adapter_name_and_ip_address(mac_address)
if adapter_name and not ip_address == current_ip_address:
interface_file_name = \
'/etc/network/interfaces'
# Remove file
os.remove(interface_file_name)
# Touch file
_execute(['touch', interface_file_name])
interface_file = open(interface_file_name, 'w')
if adapter_name:
interface_file.write('\nauto %s' % adapter_name)
interface_file.write('\niface %s inet static' % adapter_name)
interface_file.write('\nbroadcast %s' % broadcast)
interface_file.write('\ngateway %s' % gateway)
interface_file.write('\nnetmask %s' % subnet_mask)
interface_file.write('\naddress %s' % ip_address)
interface_file.write('\naddress %s\n' % ip_address)
logging.debug(_("Successfully configured NIC %d with "
"NIC info %s") % (device, network_detail))
interface_file.close()
if all_dns_servers:
dns_file_name = "/etc/resolv.conf"
os.remove(dns_file_name)
@ -355,7 +364,8 @@ def _set_ubuntu_networking(network_details=None):
for dns_server in unique_entries:
dns_file.write("\nnameserver %s" % dns_server)
dns_file.close()
print "\nRestarting networking....\n"
logging.debug(_("Restarting networking....\n"))
_execute(['/etc/init.d/networking', 'restart'])