1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright (c) 2011 Citrix Systems, Inc.
4
# Copyright 2011 OpenStack LLC.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
19
Guest tools for ESX to set up network in the guest.
20
On Windows we require pyWin32 installed on Python.
34
gettext.install('nova', unicode=1)
36
PLATFORM_WIN = 'win32'
37
PLATFORM_LINUX = 'linux2'
40
NO_MACHINE_ID = 'No machine id'
43
FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
44
if sys.platform == PLATFORM_WIN:
45
LOG_DIR = os.path.join(os.environ.get('ALLUSERSPROFILE'), 'openstack')
46
elif sys.platform == PLATFORM_LINUX:
47
LOG_DIR = '/var/log/openstack'
50
if not os.path.exists(LOG_DIR):
52
LOG_FILENAME = os.path.join(LOG_DIR, 'openstack-guest-tools.log')
53
logging.basicConfig(filename=LOG_FILENAME, format=FORMAT)
55
if sys.hexversion < 0x3000000:
56
_byte = ord # 2.x chr to integer
58
_byte = int # 3.x byte to integer
61
class ProcessExecutionError:
62
"""Process Execution Error Class."""
64
def __init__(self, exit_code, stdout, stderr, cmd):
65
self.exit_code = exit_code
71
return str(self.exit_code)
74
def _bytes2int(bytes):
75
"""Convert bytes to int."""
78
intgr = (intgr << 8) + _byte(byt)
82
def _parse_network_details(machine_id):
84
Parse the machine_id to get MAC, IP, Netmask and Gateway fields per NIC.
85
machine_id is of the form ('NIC_record#NIC_record#', '')
86
Each of the NIC will have record NIC_record in the form
87
'MAC;IP;Netmask;Gateway;Broadcast;DNS' where ';' is field separator.
88
Each record is separated by '#' from next record.
90
logging.debug(_("Received machine_id from vmtools : %s") % machine_id[0])
92
if machine_id[1].strip() == "1":
95
for machine_id_str in machine_id[0].split('#'):
96
network_info_list = machine_id_str.split(';')
97
if len(network_info_list) % 6 != 0:
99
no_grps = len(network_info_list) / 6
103
network_details.append((
104
network_info_list[k].strip().lower(),
105
network_info_list[k + 1].strip(),
106
network_info_list[k + 2].strip(),
107
network_info_list[k + 3].strip(),
108
network_info_list[k + 4].strip(),
109
network_info_list[k + 5].strip().split(',')))
111
logging.debug(_("NIC information from vmtools : %s") % network_details)
112
return network_details
115
def _get_windows_network_adapters():
116
"""Get the list of windows network adapters."""
117
import win32com.client
118
wbem_locator = win32com.client.Dispatch('WbemScripting.SWbemLocator')
119
wbem_service = wbem_locator.ConnectServer('.', 'root\cimv2')
120
wbem_network_adapters = wbem_service.InstancesOf('Win32_NetworkAdapter')
121
network_adapters = []
122
for wbem_network_adapter in wbem_network_adapters:
123
if wbem_network_adapter.NetConnectionStatus == 2 or \
124
wbem_network_adapter.NetConnectionStatus == 7:
125
adapter_name = wbem_network_adapter.NetConnectionID
126
mac_address = wbem_network_adapter.MacAddress.lower()
127
wbem_network_adapter_config = \
128
wbem_network_adapter.associators_(
129
'Win32_NetworkAdapterSetting',
130
'Win32_NetworkAdapterConfiguration')[0]
133
if wbem_network_adapter_config.IPEnabled:
134
ip_address = wbem_network_adapter_config.IPAddress[0]
135
subnet_mask = wbem_network_adapter_config.IPSubnet[0]
136
#wbem_network_adapter_config.DefaultIPGateway[0]
137
network_adapters.append({'name': adapter_name,
138
'mac-address': mac_address,
139
'ip-address': ip_address,
140
'subnet-mask': subnet_mask})
141
return network_adapters
144
def _get_linux_network_adapters():
145
"""Get the list of Linux network adapters."""
148
arch = platform.architecture()[0]
149
if arch == ARCH_32_BIT:
152
elif arch == ARCH_64_BIT:
156
raise OSError(_("Unknown architecture: %s") % arch)
157
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
158
names = array.array('B', '\0' * max_bytes)
159
outbytes = struct.unpack('iL', fcntl.ioctl(
162
struct.pack('iL', max_bytes, names.buffer_info()[0])))[0]
164
[names.tostring()[n_counter:n_counter + offset1].split('\0', 1)[0]
165
for n_counter in xrange(0, outbytes, offset2)]
166
network_adapters = []
167
for adapter_name in adapter_names:
168
ip_address = socket.inet_ntoa(fcntl.ioctl(
171
struct.pack('256s', adapter_name))[20:24])
172
subnet_mask = socket.inet_ntoa(fcntl.ioctl(
175
struct.pack('256s', adapter_name))[20:24])
176
raw_mac_address = '%012x' % _bytes2int(fcntl.ioctl(
179
struct.pack('256s', adapter_name))[18:24])
180
mac_address = ":".join([raw_mac_address[m_counter:m_counter + 2]
181
for m_counter in range(0, len(raw_mac_address), 2)]).lower()
182
network_adapters.append({'name': adapter_name,
183
'mac-address': mac_address,
184
'ip-address': ip_address,
185
'subnet-mask': subnet_mask})
186
return network_adapters
189
def _get_adapter_name_and_ip_address(network_adapters, mac_address):
190
"""Get the adapter name based on the MAC address."""
193
for network_adapter in network_adapters:
194
if network_adapter['mac-address'] == mac_address.lower():
195
adapter_name = network_adapter['name']
196
ip_address = network_adapter['ip-address']
198
return adapter_name, ip_address
201
def _get_win_adapter_name_and_ip_address(mac_address):
202
"""Get Windows network adapter name."""
203
network_adapters = _get_windows_network_adapters()
204
return _get_adapter_name_and_ip_address(network_adapters, mac_address)
207
def _get_linux_adapter_name_and_ip_address(mac_address):
208
"""Get Linux network adapter name."""
209
network_adapters = _get_linux_network_adapters()
210
return _get_adapter_name_and_ip_address(network_adapters, mac_address)
213
def _execute(cmd_list, process_input=None, check_exit_code=True):
214
"""Executes the command with the list of arguments specified."""
215
cmd = ' '.join(cmd_list)
216
logging.debug(_("Executing command: '%s'") % cmd)
217
env = os.environ.copy()
218
obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
219
stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
221
if process_input is not None:
222
result = obj.communicate(process_input)
224
result = obj.communicate()
227
logging.debug(_("Result was %s") % obj.returncode)
228
if check_exit_code and obj.returncode != 0:
229
(stdout, stderr) = result
230
raise ProcessExecutionError(exit_code=obj.returncode,
238
def _windows_set_networking():
239
"""Set IP address for the windows VM."""
240
program_files = os.environ.get('PROGRAMFILES')
241
program_files_x86 = os.environ.get('PROGRAMFILES(X86)')
242
vmware_tools_bin = None
243
if os.path.exists(os.path.join(program_files, 'VMware', 'VMware Tools',
245
vmware_tools_bin = os.path.join(program_files, 'VMware',
246
'VMware Tools', 'vmtoolsd.exe')
247
elif os.path.exists(os.path.join(program_files, 'VMware', 'VMware Tools',
248
'VMwareService.exe')):
249
vmware_tools_bin = os.path.join(program_files, 'VMware',
250
'VMware Tools', 'VMwareService.exe')
251
elif program_files_x86 and os.path.exists(os.path.join(program_files_x86,
252
'VMware', 'VMware Tools',
253
'VMwareService.exe')):
254
vmware_tools_bin = os.path.join(program_files_x86, 'VMware',
255
'VMware Tools', 'VMwareService.exe')
257
cmd = ['"' + vmware_tools_bin + '"', '--cmd', 'machine.id.get']
258
for network_detail in _parse_network_details(_execute(cmd,
259
check_exit_code=False)):
260
mac_address, ip_address, subnet_mask, gateway, broadcast,\
261
dns_servers = network_detail
262
adapter_name, current_ip_address = \
263
_get_win_adapter_name_and_ip_address(mac_address)
264
if adapter_name and not ip_address == current_ip_address:
265
cmd = ['netsh', 'interface', 'ip', 'set', 'address',
266
'name="%s"' % adapter_name, 'source=static', ip_address,
267
subnet_mask, gateway, '1']
269
# Windows doesn't let you manually set the broadcast address
270
for dns_server in dns_servers:
272
cmd = ['netsh', 'interface', 'ip', 'add', 'dns',
273
'name="%s"' % adapter_name, dns_server]
276
logging.warn(_("VMware Tools is not installed"))
279
def _filter_duplicates(all_entries):
281
for entry in all_entries:
282
if entry and entry not in final_list:
283
final_list.append(entry)
287
def _set_rhel_networking(network_details=None):
288
"""Set IPv4 network settings for RHEL distros."""
289
network_details = network_details or []
291
for network_detail in network_details:
292
mac_address, ip_address, subnet_mask, gateway, broadcast,\
293
dns_servers = network_detail
294
all_dns_servers.extend(dns_servers)
295
adapter_name, current_ip_address = \
296
_get_linux_adapter_name_and_ip_address(mac_address)
297
if adapter_name and not ip_address == current_ip_address:
298
interface_file_name = \
299
'/etc/sysconfig/network-scripts/ifcfg-%s' % adapter_name
301
os.remove(interface_file_name)
303
_execute(['touch', interface_file_name])
304
interface_file = open(interface_file_name, 'w')
305
interface_file.write('\nDEVICE=%s' % adapter_name)
306
interface_file.write('\nUSERCTL=yes')
307
interface_file.write('\nONBOOT=yes')
308
interface_file.write('\nBOOTPROTO=static')
309
interface_file.write('\nBROADCAST=%s' % broadcast)
310
interface_file.write('\nNETWORK=')
311
interface_file.write('\nGATEWAY=%s' % gateway)
312
interface_file.write('\nNETMASK=%s' % subnet_mask)
313
interface_file.write('\nIPADDR=%s' % ip_address)
314
interface_file.write('\nMACADDR=%s' % mac_address)
315
interface_file.close()
317
dns_file_name = "/etc/resolv.conf"
318
os.remove(dns_file_name)
319
_execute(['touch', dns_file_name])
320
dns_file = open(dns_file_name, 'w')
321
dns_file.write("; generated by OpenStack guest tools")
322
unique_entries = _filter_duplicates(all_dns_servers)
323
for dns_server in unique_entries:
324
dns_file.write("\nnameserver %s" % dns_server)
326
_execute(['/sbin/service', 'network', 'restart'])
329
def _set_ubuntu_networking(network_details=None):
330
"""Set IPv4 network settings for Ubuntu."""
331
network_details = network_details or []
333
interface_file_name = '/etc/network/interfaces'
335
os.remove(interface_file_name)
337
_execute(['touch', interface_file_name])
338
interface_file = open(interface_file_name, 'w')
339
for device, network_detail in enumerate(network_details):
340
mac_address, ip_address, subnet_mask, gateway, broadcast,\
341
dns_servers = network_detail
342
all_dns_servers.extend(dns_servers)
343
adapter_name, current_ip_address = \
344
_get_linux_adapter_name_and_ip_address(mac_address)
347
interface_file.write('\nauto %s' % adapter_name)
348
interface_file.write('\niface %s inet static' % adapter_name)
349
interface_file.write('\nbroadcast %s' % broadcast)
350
interface_file.write('\ngateway %s' % gateway)
351
interface_file.write('\nnetmask %s' % subnet_mask)
352
interface_file.write('\naddress %s\n' % ip_address)
353
logging.debug(_("Successfully configured NIC %d with "
354
"NIC info %s") % (device, network_detail))
355
interface_file.close()
358
dns_file_name = "/etc/resolv.conf"
359
os.remove(dns_file_name)
360
_execute(['touch', dns_file_name])
361
dns_file = open(dns_file_name, 'w')
362
dns_file.write("; generated by OpenStack guest tools")
363
unique_entries = _filter_duplicates(all_dns_servers)
364
for dns_server in unique_entries:
365
dns_file.write("\nnameserver %s" % dns_server)
368
logging.debug(_("Restarting networking....\n"))
369
_execute(['/etc/init.d/networking', 'restart'])
372
def _linux_set_networking():
373
"""Set IP address for the Linux VM."""
374
vmware_tools_bin = None
375
if os.path.exists('/usr/sbin/vmtoolsd'):
376
vmware_tools_bin = '/usr/sbin/vmtoolsd'
377
elif os.path.exists('/usr/bin/vmtoolsd'):
378
vmware_tools_bin = '/usr/bin/vmtoolsd'
379
elif os.path.exists('/usr/sbin/vmware-guestd'):
380
vmware_tools_bin = '/usr/sbin/vmware-guestd'
381
elif os.path.exists('/usr/bin/vmware-guestd'):
382
vmware_tools_bin = '/usr/bin/vmware-guestd'
384
cmd = [vmware_tools_bin, '--cmd', 'machine.id.get']
385
network_details = _parse_network_details(_execute(cmd,
386
check_exit_code=False))
387
# TODO(sateesh): For other distros like suse, debian, BSD, etc.
388
if(platform.dist()[0] == 'Ubuntu'):
389
_set_ubuntu_networking(network_details)
390
elif (platform.dist()[0] == 'redhat'):
391
_set_rhel_networking(network_details)
393
logging.warn(_("Distro '%s' not supported") % platform.dist()[0])
395
logging.warn(_("VMware Tools is not installed"))
397
if __name__ == '__main__':
398
pltfrm = sys.platform
399
if pltfrm == PLATFORM_WIN:
400
_windows_set_networking()
401
elif pltfrm == PLATFORM_LINUX:
402
_linux_set_networking()
404
raise NotImplementedError(_("Platform not implemented: '%s'") % pltfrm)