Merge lp:~sateesh-chodapuneedi/nova/esx-multi-nic into lp:~hudson-openstack/nova/milestone-proposed

Proposed by Vish Ishaya
Status: Merged
Approved by: Vish Ishaya
Approved revision: 1527
Merged at revision: 1166
Proposed branch: lp:~sateesh-chodapuneedi/nova/esx-multi-nic
Merge into: lp:~hudson-openstack/nova/milestone-proposed
Diff against target: 440 lines (+127/-112)
6 files modified
nova/tests/vmwareapi/stubs.py (+0/-2)
nova/virt/vmwareapi/fake.py (+1/-1)
nova/virt/vmwareapi/vif.py (+11/-16)
nova/virt/vmwareapi/vm_util.py (+12/-16)
nova/virt/vmwareapi/vmops.py (+63/-47)
tools/esx/guest_tool.py (+40/-30)
To merge this branch: bzr merge lp:~sateesh-chodapuneedi/nova/esx-multi-nic
Reviewer Review Type Date Requested Status
OpenStack release team Pending
Review via email: mp+75368@code.launchpad.net

Description of the change

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.
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.

This branch resolves the following bugs:
  Bug #831497 in OpenStack Compute (nova): "Instance spawn operation fails on ESXi compute node"
  https://bugs.launchpad.net/nova/+bug/831497
  Bug #839383 in OpenStack Compute (nova): "ESX(i) VIFs and mac addresses"
  https://bugs.launchpad.net/nova/+bug/839383

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/tests/vmwareapi/stubs.py'
2--- nova/tests/vmwareapi/stubs.py 2011-07-27 00:40:50 +0000
3+++ nova/tests/vmwareapi/stubs.py 2011-09-14 15:30:11 +0000
4@@ -47,7 +47,5 @@
5 stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
6 stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
7 fake_get_vim_object)
8- stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
9- fake_get_vim_object)
10 stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object",
11 fake_is_vim_object)
12
13=== modified file 'nova/virt/vmwareapi/fake.py'
14--- nova/virt/vmwareapi/fake.py 2011-09-12 15:00:09 +0000
15+++ nova/virt/vmwareapi/fake.py 2011-09-14 15:30:11 +0000
16@@ -409,7 +409,7 @@
17
18 def fake_get_network(*args, **kwargs):
19 """Fake get network."""
20- return [{'type': 'fake'}]
21+ return {'type': 'fake'}
22
23
24 def fake_fetch_image(context, image, instance, **kwargs):
25
26=== modified file 'nova/virt/vmwareapi/vif.py'
27--- nova/virt/vmwareapi/vif.py 2011-08-03 18:49:14 +0000
28+++ nova/virt/vmwareapi/vif.py 2011-09-14 15:30:11 +0000
29@@ -17,42 +17,35 @@
30
31 """VIF drivers for VMWare."""
32
33-from nova import db
34 from nova import exception
35 from nova import flags
36 from nova import log as logging
37-from nova import utils
38 from nova.virt.vif import VIFDriver
39-from nova.virt.vmwareapi_conn import VMWareAPISession
40 from nova.virt.vmwareapi import network_utils
41
42
43 LOG = logging.getLogger("nova.virt.vmwareapi.vif")
44
45 FLAGS = flags.FLAGS
46+FLAGS['vmwareapi_vlan_interface'].SetDefault('vmnic0')
47
48
49 class VMWareVlanBridgeDriver(VIFDriver):
50 """VIF Driver to setup bridge/VLAN networking using VMWare API."""
51
52 def plug(self, instance, network, mapping):
53+ """Plug the VIF to specified instance using information passed.
54+ Currently we are plugging the VIF(s) during instance creation itself.
55+ We can use this method when we add support to add additional NIC to
56+ an existing instance."""
57+ pass
58+
59+ def ensure_vlan_bridge(self, session, network):
60 """Create a vlan and bridge unless they already exist."""
61 vlan_num = network['vlan']
62 bridge = network['bridge']
63- bridge_interface = network['bridge_interface']
64+ vlan_interface = FLAGS.vmwareapi_vlan_interface
65
66- # Open vmwareapi session
67- host_ip = FLAGS.vmwareapi_host_ip
68- host_username = FLAGS.vmwareapi_host_username
69- host_password = FLAGS.vmwareapi_host_password
70- if not host_ip or host_username is None or host_password is None:
71- raise Exception(_('Must specify vmwareapi_host_ip, '
72- 'vmwareapi_host_username '
73- 'and vmwareapi_host_password to use '
74- 'connection_type=vmwareapi'))
75- session = VMWareAPISession(host_ip, host_username, host_password,
76- FLAGS.vmwareapi_api_retry_count)
77- vlan_interface = bridge_interface
78 # Check if the vlan_interface physical network adapter exists on the
79 # host.
80 if not network_utils.check_if_vlan_interface_exists(session,
81@@ -92,4 +85,6 @@
82 pgroup=pg_vlanid)
83
84 def unplug(self, instance, network, mapping):
85+ """Cleanup operations like deleting port group if no instance
86+ is associated with it."""
87 pass
88
89=== modified file 'nova/virt/vmwareapi/vm_util.py'
90--- nova/virt/vmwareapi/vm_util.py 2011-07-27 00:40:50 +0000
91+++ nova/virt/vmwareapi/vm_util.py 2011-09-14 15:30:11 +0000
92@@ -39,8 +39,7 @@
93
94
95 def get_vm_create_spec(client_factory, instance, data_store_name,
96- network_name="vmnet0",
97- os_type="otherGuest", network_ref=None):
98+ vif_infos, os_type="otherGuest"):
99 """Builds the VM Create spec."""
100 config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
101 config_spec.name = instance.name
102@@ -61,14 +60,12 @@
103 config_spec.numCPUs = int(instance.vcpus)
104 config_spec.memoryMB = int(instance.memory_mb)
105
106- mac_address = None
107- if instance['mac_addresses']:
108- mac_address = instance['mac_addresses'][0]['address']
109-
110- nic_spec = create_network_spec(client_factory,
111- network_name, mac_address)
112-
113- device_config_spec = [nic_spec]
114+ vif_spec_list = []
115+ for vif_info in vif_infos:
116+ vif_spec = create_network_spec(client_factory, vif_info)
117+ vif_spec_list.append(vif_spec)
118+
119+ device_config_spec = vif_spec_list
120
121 config_spec.deviceChange = device_config_spec
122 return config_spec
123@@ -93,8 +90,7 @@
124 return virtual_device_config
125
126
127-def create_network_spec(client_factory, network_name, mac_address,
128- network_ref=None):
129+def create_network_spec(client_factory, vif_info):
130 """
131 Builds a config spec for the addition of a new network
132 adapter to the VM.
133@@ -109,6 +105,9 @@
134 # NOTE(asomya): Only works on ESXi if the portgroup binding is set to
135 # ephemeral. Invalid configuration if set to static and the NIC does
136 # not come up on boot if set to dynamic.
137+ network_ref = vif_info['network_ref']
138+ network_name = vif_info['network_name']
139+ mac_address = vif_info['mac_address']
140 backing = None
141 if (network_ref and
142 network_ref['type'] == "DistributedVirtualPortgroup"):
143@@ -295,11 +294,8 @@
144 return config_spec
145
146
147-def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask,
148- gateway, broadcast, dns):
149+def get_machine_id_change_spec(client_factory, machine_id_str):
150 """Builds the machine id change config spec."""
151- machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask,
152- gateway, broadcast, dns)
153 virtual_machine_config_spec = \
154 client_factory.create('ns0:VirtualMachineConfigSpec')
155
156
157=== modified file 'nova/virt/vmwareapi/vmops.py'
158--- nova/virt/vmwareapi/vmops.py 2011-09-12 15:00:09 +0000
159+++ nova/virt/vmwareapi/vmops.py 2011-09-14 15:30:11 +0000
160@@ -27,7 +27,6 @@
161 import uuid
162
163 from nova import context as nova_context
164-from nova import db
165 from nova import exception
166 from nova import flags
167 from nova import log as logging
168@@ -111,22 +110,6 @@
169 client_factory = self._session._get_vim().client.factory
170 service_content = self._session._get_vim().get_service_content()
171
172- network = db.network_get_by_instance(nova_context.get_admin_context(),
173- instance['id'])
174-
175- net_name = network['bridge']
176-
177- def _check_if_network_bridge_exists():
178- network_ref = \
179- network_utils.get_network_with_the_name(self._session,
180- net_name)
181- if network_ref is None:
182- raise exception.NetworkNotFoundForBridge(bridge=net_name)
183- return network_ref
184-
185- self.plug_vifs(instance, network_info)
186- network_obj = _check_if_network_bridge_exists()
187-
188 def _get_datastore_ref():
189 """Get the datastore list and choose the first local storage."""
190 data_stores = self._session._call_method(vim_util, "get_objects",
191@@ -182,11 +165,36 @@
192
193 vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors()
194
195+ def _check_if_network_bridge_exists(network_name):
196+ network_ref = \
197+ network_utils.get_network_with_the_name(self._session,
198+ network_name)
199+ if network_ref is None:
200+ raise exception.NetworkNotFoundForBridge(bridge=network_name)
201+ return network_ref
202+
203+ def _get_vif_infos():
204+ vif_infos = []
205+ for (network, mapping) in network_info:
206+ mac_address = mapping['mac']
207+ network_name = network['bridge']
208+ if mapping.get('should_create_vlan'):
209+ network_ref = self._vif_driver.ensure_vlan_bridge(
210+ self._session, network)
211+ else:
212+ network_ref = _check_if_network_bridge_exists(network_name)
213+ vif_infos.append({'network_name': network_name,
214+ 'mac_address': mac_address,
215+ 'network_ref': network_ref,
216+ })
217+ return vif_infos
218+
219+ vif_infos = _get_vif_infos()
220+
221 # Get the create vm config spec
222 config_spec = vm_util.get_vm_create_spec(
223 client_factory, instance,
224- data_store_name, net_name, os_type,
225- network_obj)
226+ data_store_name, vif_infos, os_type)
227
228 def _execute_create_vm():
229 """Create VM on ESX host."""
230@@ -204,8 +212,10 @@
231
232 _execute_create_vm()
233
234- # Set the machine id for the VM for setting the IP
235- self._set_machine_id(client_factory, instance)
236+ # Set the machine.id parameter of the instance to inject
237+ # the NIC configuration inside the VM
238+ if FLAGS.flat_injected:
239+ self._set_machine_id(client_factory, instance, network_info)
240
241 # Naming the VM files in correspondence with the VM instance name
242 # The flat vmdk file name
243@@ -718,39 +728,45 @@
244 """Return link to instance's ajax console."""
245 return 'http://fakeajaxconsole/fake_url'
246
247- def _set_machine_id(self, client_factory, instance):
248- """
249- Set the machine id of the VM for guest tools to pick up and change
250- the IP.
251- """
252- admin_context = nova_context.get_admin_context()
253+ def _set_machine_id(self, client_factory, instance, network_info):
254+ """
255+ Set the machine id of the VM for guest tools to pick up and reconfigure
256+ the network interfaces.
257+ """
258 vm_ref = self._get_vm_ref_from_the_name(instance.name)
259 if vm_ref is None:
260 raise exception.InstanceNotFound(instance_id=instance.id)
261- network = db.network_get_by_instance(nova_context.get_admin_context(),
262- instance['id'])
263- mac_address = None
264- if instance['mac_addresses']:
265- mac_address = instance['mac_addresses'][0]['address']
266-
267- net_mask = network["netmask"]
268- gateway = network["gateway"]
269- broadcast = network["broadcast"]
270- # TODO(vish): add support for dns2
271- dns = network["dns1"]
272-
273- addresses = db.instance_get_fixed_addresses(admin_context,
274- instance['id'])
275- ip_addr = addresses[0] if addresses else None
276+
277+ machine_id_str = ''
278+ for (network, info) in network_info:
279+ # TODO(vish): add support for dns2
280+ # TODO(sateesh): add support for injection of ipv6 configuration
281+ ip_v4 = ip_v6 = None
282+ if 'ips' in info and len(info['ips']) > 0:
283+ ip_v4 = info['ips'][0]
284+ if 'ip6s' in info and len(info['ip6s']) > 0:
285+ ip_v6 = info['ip6s'][0]
286+ if len(info['dns']) > 0:
287+ dns = info['dns'][0]
288+ else:
289+ dns = ''
290+
291+ interface_str = "%s;%s;%s;%s;%s;%s" % \
292+ (info['mac'],
293+ ip_v4 and ip_v4['ip'] or '',
294+ ip_v4 and ip_v4['netmask'] or '',
295+ info['gateway'],
296+ info['broadcast'],
297+ dns)
298+ machine_id_str = machine_id_str + interface_str + '#'
299
300 machine_id_change_spec = \
301- vm_util.get_machine_id_change_spec(client_factory, mac_address,
302- ip_addr, net_mask, gateway,
303- broadcast, dns)
304+ vm_util.get_machine_id_change_spec(client_factory, machine_id_str)
305+
306 LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id "
307 "with ip - %(ip_addr)s") %
308 ({'name': instance.name,
309- 'ip_addr': ip_addr}))
310+ 'ip_addr': ip_v4['ip']}))
311 reconfig_task = self._session._call_method(self._session._get_vim(),
312 "ReconfigVM_Task", vm_ref,
313 spec=machine_id_change_spec)
314@@ -758,7 +774,7 @@
315 LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id "
316 "with ip - %(ip_addr)s") %
317 ({'name': instance.name,
318- 'ip_addr': ip_addr}))
319+ 'ip_addr': ip_v4['ip']}))
320
321 def _get_datacenter_name_and_ref(self):
322 """Get the datacenter name and the reference."""
323
324=== modified file 'tools/esx/guest_tool.py'
325--- tools/esx/guest_tool.py 2011-06-27 18:41:07 +0000
326+++ tools/esx/guest_tool.py 2011-09-14 15:30:11 +0000
327@@ -81,28 +81,34 @@
328
329 def _parse_network_details(machine_id):
330 """
331- Parse the machine.id field to get MAC, IP, Netmask and Gateway fields
332- machine.id is of the form MAC;IP;Netmask;Gateway;Broadcast;DNS1,DNS2
333- where ';' is the separator.
334+ Parse the machine_id to get MAC, IP, Netmask and Gateway fields per NIC.
335+ machine_id is of the form ('NIC_record#NIC_record#', '')
336+ Each of the NIC will have record NIC_record in the form
337+ 'MAC;IP;Netmask;Gateway;Broadcast;DNS' where ';' is field separator.
338+ Each record is separated by '#' from next record.
339 """
340+ logging.debug(_("Received machine_id from vmtools : %s") % machine_id[0])
341 network_details = []
342 if machine_id[1].strip() == "1":
343 pass
344 else:
345- network_info_list = machine_id[0].split(';')
346- assert len(network_info_list) % 6 == 0
347- no_grps = len(network_info_list) / 6
348- i = 0
349- while i < no_grps:
350- k = i * 6
351- network_details.append((
352- network_info_list[k].strip().lower(),
353- network_info_list[k + 1].strip(),
354- network_info_list[k + 2].strip(),
355- network_info_list[k + 3].strip(),
356- network_info_list[k + 4].strip(),
357- network_info_list[k + 5].strip().split(',')))
358- i += 1
359+ for machine_id_str in machine_id[0].split('#'):
360+ network_info_list = machine_id_str.split(';')
361+ if len(network_info_list) % 6 != 0:
362+ break
363+ no_grps = len(network_info_list) / 6
364+ i = 0
365+ while i < no_grps:
366+ k = i * 6
367+ network_details.append((
368+ network_info_list[k].strip().lower(),
369+ network_info_list[k + 1].strip(),
370+ network_info_list[k + 2].strip(),
371+ network_info_list[k + 3].strip(),
372+ network_info_list[k + 4].strip(),
373+ network_info_list[k + 5].strip().split(',')))
374+ i += 1
375+ logging.debug(_("NIC information from vmtools : %s") % network_details)
376 return network_details
377
378
379@@ -279,6 +285,7 @@
380
381
382 def _set_rhel_networking(network_details=None):
383+ """Set IPv4 network settings for RHEL distros."""
384 network_details = network_details or []
385 all_dns_servers = []
386 for network_detail in network_details:
387@@ -320,31 +327,33 @@
388
389
390 def _set_ubuntu_networking(network_details=None):
391+ """Set IPv4 network settings for Ubuntu."""
392 network_details = network_details or []
393- """ Set IPv4 network settings for Ubuntu """
394 all_dns_servers = []
395- for network_detail in network_details:
396+ interface_file_name = '/etc/network/interfaces'
397+ # Remove file
398+ os.remove(interface_file_name)
399+ # Touch file
400+ _execute(['touch', interface_file_name])
401+ interface_file = open(interface_file_name, 'w')
402+ for device, network_detail in enumerate(network_details):
403 mac_address, ip_address, subnet_mask, gateway, broadcast,\
404 dns_servers = network_detail
405 all_dns_servers.extend(dns_servers)
406 adapter_name, current_ip_address = \
407 _get_linux_adapter_name_and_ip_address(mac_address)
408
409- if adapter_name and not ip_address == current_ip_address:
410- interface_file_name = \
411- '/etc/network/interfaces'
412- # Remove file
413- os.remove(interface_file_name)
414- # Touch file
415- _execute(['touch', interface_file_name])
416- interface_file = open(interface_file_name, 'w')
417+ if adapter_name:
418 interface_file.write('\nauto %s' % adapter_name)
419 interface_file.write('\niface %s inet static' % adapter_name)
420 interface_file.write('\nbroadcast %s' % broadcast)
421 interface_file.write('\ngateway %s' % gateway)
422 interface_file.write('\nnetmask %s' % subnet_mask)
423- interface_file.write('\naddress %s' % ip_address)
424- interface_file.close()
425+ interface_file.write('\naddress %s\n' % ip_address)
426+ logging.debug(_("Successfully configured NIC %d with "
427+ "NIC info %s") % (device, network_detail))
428+ interface_file.close()
429+
430 if all_dns_servers:
431 dns_file_name = "/etc/resolv.conf"
432 os.remove(dns_file_name)
433@@ -355,7 +364,8 @@
434 for dns_server in unique_entries:
435 dns_file.write("\nnameserver %s" % dns_server)
436 dns_file.close()
437- print "\nRestarting networking....\n"
438+
439+ logging.debug(_("Restarting networking....\n"))
440 _execute(['/etc/init.d/networking', 'restart'])
441
442

Subscribers

People subscribed via source and target branches