186 |
- |
1 |
"""
|
|
|
2 |
Copyright 2008-2019 VMware, Inc. All rights reserved. -- VMware Confidential
|
|
|
3 |
|
|
|
4 |
VMware VMX component installer.
|
|
|
5 |
"""
|
|
|
6 |
|
|
|
7 |
GCONF_DEFAULTS = 'xml:readwrite:/etc/gconf/gconf.xml.defaults'
|
|
|
8 |
DEST = LIBDIR/'vmware'
|
|
|
9 |
CONFIG = DEST/'setup/vmware-config'
|
|
|
10 |
CUPSLIBDIR = LIBDIR/'cups'
|
|
|
11 |
SETTINGS = \
|
|
|
12 |
{'libdir': DEST,
|
|
|
13 |
'bindir': BINDIR,
|
|
|
14 |
'initscriptdir': INITSCRIPTDIR,
|
|
|
15 |
'gksu.rootMethod': 'sudo' if 'SUDO_USER' in ENV else 'su',
|
|
|
16 |
'NETWORKING': 'yes',
|
|
|
17 |
'VMBLOCK_CONFED': 'yes',
|
|
|
18 |
'VMCI_CONFED': 'yes',
|
|
|
19 |
'VSOCK_CONFED': 'yes',
|
|
|
20 |
'authd.fullpath': SBINDIR/'vmware-authd',
|
|
|
21 |
}
|
|
|
22 |
|
|
|
23 |
vmwareSentinel = '# Automatically generated by the VMware Installer - DO NOT REMOVE\n'
|
|
|
24 |
|
|
|
25 |
MIN_NESTED_TOOLS_VERSION = '8.9.0'
|
|
|
26 |
MIN_EQUIVALENT_KERNEL_VERSION = (3, 9)
|
|
|
27 |
# Present requirement is a RHEL6-like environment.
|
|
|
28 |
MIN_GLIBC_VERSION = (2, 12)
|
|
|
29 |
MIN_KERNEL_VERSION = (2, 6, 32)
|
|
|
30 |
|
|
|
31 |
# Player and Workstation both depend on some configuration living
|
|
|
32 |
# in /etc/vmware
|
|
|
33 |
ETCDIR = Destination('/etc/vmware')
|
|
|
34 |
|
|
|
35 |
class VMX(Installer):
|
|
|
36 |
def InitializeQuestions(self, old, new, upgrade):
|
|
|
37 |
self.AddQuestion('ClosePrograms', key='ClosePrograms', text='',
|
|
|
38 |
required=True, default='Yes', level='REQUIRED')
|
|
|
39 |
|
|
|
40 |
# Check whether we're in a VM or on real hardware. If we're in a VM, verify that the
|
|
|
41 |
# proper version of Tools is already installed.
|
|
|
42 |
self.inVM = False
|
|
|
43 |
self.checkVMTempFile = self.GetFilePath('extra/checkvm')
|
|
|
44 |
# Get the directory above our installer and write checkvm to it so we can run it
|
|
|
45 |
tmpdir = path(ENV['VMWARE_INSTALLER']).dirname().dirname()
|
|
|
46 |
self.checkvmBin = tmpdir/'checkvm'
|
|
|
47 |
path(self.checkVMTempFile).copy(self.checkvmBin)
|
|
|
48 |
self.checkvmBin.chmod(0o755)
|
|
|
49 |
path(self.checkVMTempFile).remove()
|
|
|
50 |
|
|
|
51 |
# Check the environment for forceInstallInVM. This check is here in InitializeQuestions
|
|
|
52 |
# because it is run before InitializeInstall, where this variable is also used.
|
|
|
53 |
self.forceInstallInVM = False
|
|
|
54 |
try:
|
|
|
55 |
txt = ENV['VMWARE_FORCE_INSTALL_IN_VM']
|
|
|
56 |
if txt == 'yes':
|
|
|
57 |
self.forceInstallInVM = True
|
|
|
58 |
except KeyError:
|
|
|
59 |
pass # Okay if it hasn't been set
|
|
|
60 |
|
|
|
61 |
ret = self.RunCommand(self.checkvmBin, ignoreErrors=True)
|
|
|
62 |
if ret.retCode == 0:
|
|
|
63 |
# We are running in a virtual machine, don't install VSock and VMCI
|
|
|
64 |
log.Info('Running inside a virtual machine!')
|
|
|
65 |
self.inVM = True
|
|
|
66 |
if self.forceInstallInVM:
|
|
|
67 |
log.Info('But forcing install as if we are not.')
|
|
|
68 |
self.inVM = False
|
|
|
69 |
|
|
|
70 |
if self.inVM:
|
|
|
71 |
# Now we need to check if Tools is installed. If they are, we need to see if
|
|
|
72 |
# they are a recent enough version to make this work. If they aren't, then
|
|
|
73 |
# notify the user that they are out of luck and must upgrade to a newer version
|
|
|
74 |
# of Tools.
|
|
|
75 |
minVersion = MIN_NESTED_TOOLS_VERSION
|
|
|
76 |
toolsVersion = self.getToolsVersion()
|
|
|
77 |
if not self.forceInstallInVM: # Only check if they haven't been overridden
|
|
|
78 |
if not toolsVersion:
|
|
|
79 |
# vmci & vsock is upstreamed since kernel 3.9, and kernel 3.9 doesn't
|
|
|
80 |
# need vmblock. So, in a distro with kernel later than 3.9, even if
|
|
|
81 |
# there's no entire vmware tools, driver version is also acceptable.
|
|
|
82 |
kernelVersion = self.getHostKernelVersion()
|
|
|
83 |
if self.isVersionOkay(kernelVersion, MIN_EQUIVALENT_KERNEL_VERSION):
|
|
|
84 |
log.Info('Kernel version is acceptable.')
|
|
|
85 |
else:
|
|
|
86 |
log.Info('Kernel version is too old. Related modules will be configured.')
|
|
|
87 |
msg = 'You are installing inside a virtual machine. It is' \
|
|
|
88 |
' recommended that you should install at least version' \
|
|
|
89 |
' %s of VMware Tools before installing this product.' \
|
|
|
90 |
' This product will work without VMware Tools, but if' \
|
|
|
91 |
' you choose to install VMware Tools after installing' \
|
|
|
92 |
' this product, the two products may not work together.' \
|
|
|
93 |
% minVersion
|
|
|
94 |
log.Warn(msg)
|
|
|
95 |
self.UserMessage ('Warning: %s' % msg)
|
|
|
96 |
self.inVM = False
|
|
|
97 |
elif not self.isToolsVersionOkay(toolsVersion, minVersion):
|
|
|
98 |
self.UserMessage('You must have at least version %s of VMware' % minVersion + \
|
|
|
99 |
' Tools installed to install this product inside a virtual machine.' + \
|
|
|
100 |
' Your version is %s. Please update VMware Tools.' % toolsVersion)
|
|
|
101 |
raise Exception('Tools version %s is too low to install inside a VM.')
|
|
|
102 |
elif ret.retCode == 1:
|
|
|
103 |
# We are on normal hardware.
|
|
|
104 |
log.Info('Running on a real machine!')
|
|
|
105 |
self.inVM = False
|
|
|
106 |
else:
|
|
|
107 |
log.Warn('Something went wrong detecting whether we are in a VM or not. Assuming we are not.')
|
|
|
108 |
self.inVM = False
|
|
|
109 |
|
|
|
110 |
|
|
|
111 |
def InitializeInstall(self, old, new, upgrade):
|
|
|
112 |
if self._checkXenPresence():
|
|
|
113 |
log.Warn('This system is running a Xen kernel. You cannot run VMs under the Xen kernel.')
|
|
|
114 |
|
|
|
115 |
if path('/dev/kvm').exists():
|
|
|
116 |
log.Warn('This system has KVM enabled. You cannot run VMs with KVM enabled.')
|
|
|
117 |
|
|
|
118 |
self.AddTarget('File', 'bin/*', BINDIR)
|
|
|
119 |
self.AddTarget('File', 'sbin/*', SBINDIR)
|
|
|
120 |
self.AddTarget('File', 'lib/*', DEST)
|
|
|
121 |
self.AddTarget('File', 'roms/*', DEST/'roms')
|
|
|
122 |
self.AddTarget('File', 'etc/init.d/vmware', SYSCONFDIR/'init.d/vmware')
|
|
|
123 |
|
|
|
124 |
# Install the proper modules.xml file depending on whether we're installing on a real
|
|
|
125 |
# machine or inside a VM
|
|
|
126 |
if not self.inVM or self.forceInstallInVM:
|
|
|
127 |
self.AddTarget('File', 'extra/modules.xml', DEST/'modules/modules.xml')
|
|
|
128 |
else:
|
|
|
129 |
# Use the nested VM modules.xml file and mark modules that Tools provides as unconfigured
|
|
|
130 |
# so Modconfig doesn't try to rebuild them and the init script doesn't try to manage them
|
|
|
131 |
self.AddTarget('File', 'extra/modules.nestedVM.xml', DEST/'modules/modules.xml')
|
|
|
132 |
SETTINGS['VMCI_CONFED'] = 'no'
|
|
|
133 |
SETTINGS['VSOCK_CONFED'] = 'no'
|
|
|
134 |
SETTINGS['VMBLOCK_CONFED'] = 'no'
|
|
|
135 |
|
|
|
136 |
# Symlink all binaries to appLoader.
|
|
|
137 |
for i in ('vmware-modconfig', 'vmware-modconfig-console',
|
|
|
138 |
'vmware-gksu', 'vmware-vmblock-fuse'):
|
|
|
139 |
self.AddTarget('Link', DEST/'bin/appLoader', DEST/'bin'/i)
|
|
|
140 |
|
|
|
141 |
self.SetPermission(DEST/'bin/*', BINARY)
|
|
|
142 |
self.SetPermission(SYSCONFDIR/'init.d/vmware', BINARY)
|
|
|
143 |
self.SetPermission(DEST/'bin/vmware-vmx*', SETUID)
|
|
|
144 |
self.SetPermission(DEST/'lib/libvmware-gksu.so/gksu-run-helper', BINARY)
|
|
|
145 |
self.SetPermission(SBINDIR/'vmware-authd', SETUID)
|
|
|
146 |
|
|
|
147 |
# modprobe.d script. Only install this on systems where modprobe.d
|
|
|
148 |
# exists.
|
|
|
149 |
if path('/etc/modprobe.d').exists():
|
|
|
150 |
self.AddTarget('File', 'etc/modprobe.d/modprobe-vmware-fuse.conf',
|
|
|
151 |
'/etc/modprobe.d/vmware-fuse.conf')
|
|
|
152 |
|
|
|
153 |
# Add a link for internationalization directory
|
|
|
154 |
self.AddTarget('Link', DEST/'icu', SYSCONFDIR/'vmware/icu')
|
|
|
155 |
|
|
|
156 |
def PreTransactionUninstall(self, old, new, upgrade):
|
|
|
157 |
self.AddQuestion('ClosePrograms', key='ClosePrograms', text='',
|
|
|
158 |
required=True, default='Yes', level='REQUIRED')
|
|
|
159 |
|
|
|
160 |
def PreInstall(self, old, new, upgrade):
|
|
|
161 |
# Remove all modules in case some were left behind somehow. For
|
|
|
162 |
# example, the installation database could have been blown away.
|
|
|
163 |
ret, kvers, _ = self.RunCommand('uname', '-r')
|
|
|
164 |
kvers = kvers.strip()
|
|
|
165 |
modules = ('vmmon', 'vmnet')
|
|
|
166 |
if not self.inVM:
|
|
|
167 |
modules = modules + ('vmblock', 'vmci', 'vsock')
|
|
|
168 |
base = path('/lib/modules/%s/misc' % kvers)
|
|
|
169 |
|
|
|
170 |
for module in modules:
|
|
|
171 |
for ext in ('o', 'ko'):
|
|
|
172 |
mod = '%s.%s' % (module, ext)
|
|
|
173 |
(base/mod).remove(ignore_errors=True)
|
|
|
174 |
|
|
|
175 |
def PreUninstall(self, old, new, upgrade):
|
|
|
176 |
script = INITSCRIPTDIR/'vmware'
|
|
|
177 |
|
|
|
178 |
# Stop and deconfigure services
|
|
|
179 |
if ENV.get('VMWARE_SKIP_SERVICES'):
|
|
|
180 |
log.Info('Skipping stopping services')
|
|
|
181 |
elif INITSCRIPTDIR and self._scriptRunnable(script) and self.RunCommand(script, 'stop', ignoreErrors=True).retCode != 0:
|
|
|
182 |
log.Error('Unable to stop VMware services')
|
|
|
183 |
|
|
|
184 |
# Deconfigure services
|
|
|
185 |
inits = self.LoadInclude('initscript')
|
|
|
186 |
inits.DeconfigureService('vmware')
|
|
|
187 |
|
|
|
188 |
# This must be the last thing done since the actions above may
|
|
|
189 |
# depend on it.
|
|
|
190 |
for key in list(SETTINGS.keys()):
|
|
|
191 |
self.RunCommand(CONFIG, '-d', key)
|
|
|
192 |
|
|
|
193 |
# Remove prelink appLoader exclusion.
|
|
|
194 |
self._configurePrelink(False)
|
|
|
195 |
|
|
|
196 |
def PostUninstall(self, old, new, upgrade):
|
|
|
197 |
# Modules have been removed during uninstall. We need to be sure to
|
|
|
198 |
# run depmod to pick up that change. Don't fail if for some reason
|
|
|
199 |
# depmod can't be executed though.
|
|
|
200 |
self.RunCommand('depmod', '-a', ignoreErrors=True)
|
|
|
201 |
|
|
|
202 |
def PostInstall(self, old, new, upgrade):
|
|
|
203 |
for key, val in list(SETTINGS.items()):
|
|
|
204 |
self.RunCommand(CONFIG, '-s', key, val)
|
|
|
205 |
|
|
|
206 |
bootstrap = ETCDIR/'bootstrap'
|
|
|
207 |
# Create the bootstrap file.
|
|
|
208 |
bootstrap.unlink(ignore_errors=True)
|
|
|
209 |
# Fill it.
|
|
|
210 |
for i in ('PREFIX', 'BINDIR', 'SBINDIR', 'LIBDIR', 'DATADIR',
|
|
|
211 |
'SYSCONFDIR', 'DOCDIR', 'MANDIR', 'INCLUDEDIR', 'INITDIR',
|
|
|
212 |
'INITSCRIPTDIR'):
|
|
|
213 |
bootstrap.write_text('%s="%s"\n' % (i, globals()[i]), append=True)
|
|
|
214 |
# Register it.
|
|
|
215 |
# XXX: Skip registration for this change. Allow the installer to handle
|
|
|
216 |
# deletion. A later change will correct this.
|
|
|
217 |
# self.RegisterFile(bootstrap, fileType='ConfigFile')
|
|
|
218 |
|
|
|
219 |
# Configure Gtk+.
|
|
|
220 |
# @todo: make it its own component
|
|
|
221 |
libconf = DEST/'libconf'
|
|
|
222 |
replace = libconf/'etc/gtk-3.0/gdk-pixbuf.loaders'
|
|
|
223 |
template = '@@LIBCONF_DIR@@'
|
|
|
224 |
|
|
|
225 |
self.RunCommand('sed', '-e', 's,%s,%s,g' % (template, libconf),
|
|
|
226 |
'-i', replace)
|
|
|
227 |
|
|
|
228 |
# Add prelink appLoader exclusion.
|
|
|
229 |
self._configurePrelink(True)
|
|
|
230 |
|
|
|
231 |
# Set up virtual networking
|
|
|
232 |
self._configureNetworking(old, new)
|
|
|
233 |
|
|
|
234 |
# Set up the vmware service script
|
|
|
235 |
inits = self.LoadInclude('initscript')
|
|
|
236 |
inits.ConfigureService('vmware',
|
|
|
237 |
'This service starts and stops VMware services',
|
|
|
238 |
'$network $syslog', # Start
|
|
|
239 |
'$network $syslog', # Stop
|
|
|
240 |
'',
|
|
|
241 |
'',
|
|
|
242 |
19,
|
|
|
243 |
8)
|
|
|
244 |
|
|
|
245 |
# Make sure to start services
|
|
|
246 |
script = INITSCRIPTDIR/'vmware'
|
|
|
247 |
if INITSCRIPTDIR and script.exists():
|
|
|
248 |
self.RunCommand(script, 'stop', ignoreErrors=True)
|
|
|
249 |
self.RunCommand(script, 'start')
|
|
|
250 |
|
|
|
251 |
# If no INITDIR was given, notify the user that the vmware service must
|
|
|
252 |
# be manually set up
|
|
|
253 |
if not INITDIR:
|
|
|
254 |
self.UserMessage('No rc*.d style init script directories were given to the installer. '
|
|
|
255 |
'You must manually add the necessary links to ensure that the vmware '
|
|
|
256 |
'service at %s is automatically started and stopped on startup and shutdown.' % str(INITSCRIPTDIR/'vmware'))
|
|
|
257 |
|
|
|
258 |
def _scriptRunnable(self, script):
|
|
|
259 |
""" Returns True if the script exists and is in a runnable state """
|
|
|
260 |
return script.isexe() and script.isfile() and self.RunCommand(script, 'validate').retCode == 100
|
|
|
261 |
|
|
|
262 |
def _AddLineToFile(self, fil, text, addToEnd=True):
|
|
|
263 |
"""
|
|
|
264 |
Add a line/consecutive lines to a file, surrounded by the VMware Sentinel.
|
|
|
265 |
### This method only adds a single line to a file ###
|
|
|
266 |
### You cannot make more than one modification per file ###
|
|
|
267 |
|
|
|
268 |
@param fil: The file name. Either a string or path object
|
|
|
269 |
@param text: The text to add. This function appends a \n
|
|
|
270 |
@param addToEnd: Add to the end of the file? If false, to the beginning.
|
|
|
271 |
|
|
|
272 |
@return: True on successful modification, False if the file did not exist.
|
|
|
273 |
"""
|
|
|
274 |
fil = path(fil) # Make sure it's a path object
|
|
|
275 |
if fil.exists():
|
|
|
276 |
txt = fil.text()
|
|
|
277 |
# Modify the text
|
|
|
278 |
if addToEnd:
|
|
|
279 |
txt = ''.join([txt, vmwareSentinel, text, '\n', vmwareSentinel])
|
|
|
280 |
else:
|
|
|
281 |
txt = ''.join([vmwareSentinel, text, '\n', vmwareSentinel, txt])
|
|
|
282 |
# Write it back to the file
|
|
|
283 |
fil.write_text(txt)
|
|
|
284 |
return True
|
|
|
285 |
else:
|
|
|
286 |
log.Info('Attempted to modify file %s, does not exist.' % fil)
|
|
|
287 |
return False
|
|
|
288 |
|
|
|
289 |
def _RemoveLineFromFile(self, fil):
|
|
|
290 |
"""
|
|
|
291 |
Remove a line bracketed by the VMware Sentinel
|
|
|
292 |
|
|
|
293 |
@param fil: The file name. Either a string or path object
|
|
|
294 |
|
|
|
295 |
@return: True on successful modification, False if the file did not exist.
|
|
|
296 |
"""
|
|
|
297 |
fil = path(fil) # Make sure it's a path object
|
|
|
298 |
if fil.exists():
|
|
|
299 |
txt = fil.text()
|
|
|
300 |
m = re.sub(vmwareSentinel + '.*\n' + vmwareSentinel,
|
|
|
301 |
'', txt, re.DOTALL)
|
|
|
302 |
fil.write_text(m)
|
|
|
303 |
return True
|
|
|
304 |
else:
|
|
|
305 |
log.Info('Attempted to modify file %s, does not exist.' % fil)
|
|
|
306 |
return False
|
|
|
307 |
|
|
|
308 |
def _configurePrelink(self, enable):
|
|
|
309 |
"""
|
|
|
310 |
Configures prelinking by adding appLoader exclusion.
|
|
|
311 |
|
|
|
312 |
@param enable: True if to add exclusion, False if to remove it.
|
|
|
313 |
"""
|
|
|
314 |
prelink = path('/etc/prelink.conf')
|
|
|
315 |
|
|
|
316 |
if prelink.exists():
|
|
|
317 |
# XXX: It would probably be good to refactor this into some
|
|
|
318 |
# sort of helper function that can add and remove lines from
|
|
|
319 |
# files.
|
|
|
320 |
skipPrelink = [ '# appLoader will segfault if prelinked.',
|
|
|
321 |
'-b %s' % (DEST/'bin/appLoader') ]
|
|
|
322 |
|
|
|
323 |
lines = prelink.lines(encoding='utf-8', retain=False)
|
|
|
324 |
|
|
|
325 |
# Strip whitespace from lines so that trailing whitespace
|
|
|
326 |
# won't impact matching lines.
|
|
|
327 |
lines = [line.strip() for line in lines]
|
|
|
328 |
|
|
|
329 |
if enable:
|
|
|
330 |
if skipPrelink[-1] not in lines:
|
|
|
331 |
lines += skipPrelink
|
|
|
332 |
log.Info('Added appLoader prelink exclusion to %s.', prelink)
|
|
|
333 |
else:
|
|
|
334 |
log.Warn('appLoader skip prelinking already present.')
|
|
|
335 |
else:
|
|
|
336 |
found = False
|
|
|
337 |
|
|
|
338 |
for line in skipPrelink:
|
|
|
339 |
if line in lines:
|
|
|
340 |
found = True
|
|
|
341 |
lines.remove(line)
|
|
|
342 |
|
|
|
343 |
if found:
|
|
|
344 |
log.Info('Removed appLoader prelink exclusion from %s.', prelink)
|
|
|
345 |
|
|
|
346 |
# XXX: This can technically fail. Actually, there are a
|
|
|
347 |
# whole host of things that can fail in this function. On
|
|
|
348 |
# one hand we would like to catch errors and correct them
|
|
|
349 |
# while being fairly resilient at installation time.
|
|
|
350 |
#
|
|
|
351 |
# One option might be to have a @failok decoration for things
|
|
|
352 |
# that can fail loudly for internal builds but do not cause
|
|
|
353 |
# installations to fail in release builds.
|
|
|
354 |
prelink.write_lines(lines, encoding='utf-8')
|
|
|
355 |
else:
|
|
|
356 |
log.Info('Prelink not present, skipping configuration.')
|
|
|
357 |
|
|
|
358 |
def _validate_cpu_flags(self, reqFlags):
|
|
|
359 |
"""
|
|
|
360 |
Checks to ensure that every CPU on the given machine has all the
|
|
|
361 |
flags passed in the reqFlags list.
|
|
|
362 |
@param: A list of the flags that must be present.
|
|
|
363 |
@returns: True on all necessary flags present, False othewise.
|
|
|
364 |
"""
|
|
|
365 |
flagSec = False
|
|
|
366 |
cpuInfoFile = '/proc/cpuinfo'
|
|
|
367 |
try:
|
|
|
368 |
cpuInfo = open(cpuInfoFile, mode='r')
|
|
|
369 |
for line in cpuInfo:
|
|
|
370 |
if line.startswith('flags'):
|
|
|
371 |
flagSec = True
|
|
|
372 |
flagsFound = line.split(' ')
|
|
|
373 |
for flag in reqFlags:
|
|
|
374 |
if flag not in flagsFound:
|
|
|
375 |
return False
|
|
|
376 |
return flagSec
|
|
|
377 |
except IOError:
|
|
|
378 |
raise Exception('Could not open %s' % (cpuInfoFile))
|
|
|
379 |
finally:
|
|
|
380 |
if cpuInfo:
|
|
|
381 |
cpuInfo.close()
|
|
|
382 |
|
|
|
383 |
def PreTransactionInstall(self, old, new, upgrade):
|
|
|
384 |
if ENV.get('VMWARE_SKIP_VERSION_CHECKS'):
|
|
|
385 |
return # skip all checks
|
|
|
386 |
|
|
|
387 |
# CPU flags check
|
|
|
388 |
# TODO: also check for vt / ept / (AMD equivalent)
|
|
|
389 |
reqFlags = ['lm']
|
|
|
390 |
cpuOk = self._validate_cpu_flags(reqFlags)
|
|
|
391 |
kernelOk = self.isVersionOkay(self.getHostKernelVersion(),
|
|
|
392 |
MIN_KERNEL_VERSION)
|
|
|
393 |
glibcOk = self.isVersionOkay(self.getHostGlibcVersion(),
|
|
|
394 |
MIN_GLIBC_VERSION)
|
|
|
395 |
if not cpuOk:
|
|
|
396 |
self.UserMessage('One or more of your processors does not have the '
|
|
|
397 |
'necessary 64bit extensions to run VMware '
|
|
|
398 |
'virtual machines.')
|
|
|
399 |
if not kernelOk:
|
|
|
400 |
self.UserMessage('The Linux kernel version on this system is '
|
|
|
401 |
'insufficient for running VMware virtual machines. '
|
|
|
402 |
'Linux kernel version %s or later is required.'
|
|
|
403 |
% str(MIN_KERNEL_VERSION))
|
|
|
404 |
if not glibcOk:
|
|
|
405 |
self.UserMessage('The glibc library version on this system is '
|
|
|
406 |
'insufficient for running VMware virtual machines. '
|
|
|
407 |
'Glibc library version %s or later is required.'
|
|
|
408 |
% str(MIN_GLIBC_VERSION))
|
|
|
409 |
if not cpuOk or not glibcOk or not kernelOk:
|
|
|
410 |
raise Exception('Host not supported by this product.')
|
|
|
411 |
|
|
|
412 |
def PostTransactionInstall(self, old, new, upgrade):
|
|
|
413 |
# Build modules with modconfig. This can't happen during the other install
|
|
|
414 |
# phases because VMIS has locked the database and modconfig invokes another
|
|
|
415 |
# version of VMIS to register the compiled modules.
|
|
|
416 |
if ENV.get('VMWARE_SKIP_MODULES'):
|
|
|
417 |
log.Info('Skipping kernel module installation')
|
|
|
418 |
else:
|
|
|
419 |
# run depmod -a before modconfig in case we are upgrading from a version
|
|
|
420 |
# that did not properly do this after uninstalling modules. modconfig
|
|
|
421 |
# uses modules.dep to determine upstream status of modules, therefore
|
|
|
422 |
# it might get confused if modules.dep is not up-to-date.
|
|
|
423 |
self.RunCommand('depmod', '-a', ignoreErrors=True)
|
|
|
424 |
ret = self.RunCommand(BINDIR/'vmware-modconfig', '--console', '--install-all')
|
|
|
425 |
if ret.retCode == 0:
|
|
|
426 |
log.Info('Successfully installed kernel modules')
|
|
|
427 |
else:
|
|
|
428 |
log.Info('Unable to install kernel modules')
|
|
|
429 |
log.Info('stdout: %s' % ret.stdout)
|
|
|
430 |
log.Info('stderr: %s' % ret.stderr)
|
|
|
431 |
|
|
|
432 |
def _checkXenPresence(self):
|
|
|
433 |
"""
|
|
|
434 |
Checks whether this install is within a Xen domain,
|
|
|
435 |
that is running on a Xen kernel. Returns True if so.
|
|
|
436 |
"""
|
|
|
437 |
xenTest = path('/proc/xen/capabilities')
|
|
|
438 |
return xenTest.exists() and len(xenTest.text())
|
|
|
439 |
|
|
|
440 |
def _escape(self, string):
|
|
|
441 |
""" Escapes a string for use in a shell context """
|
|
|
442 |
# XXX: Borrowed from util/shell.py: Escape. Break that into a component-side
|
|
|
443 |
# include file and remove this method once that's done.
|
|
|
444 |
return "'%s'" % string.replace("'", '"\'"')
|
|
|
445 |
|
|
|
446 |
def _configureNetworking(self, old, new):
|
|
|
447 |
if 'VMWARE_SKIP_NETWORKING' in ENV:
|
|
|
448 |
return
|
|
|
449 |
|
|
|
450 |
vnetlib = BINDIR/'vmware-networks'
|
|
|
451 |
|
|
|
452 |
backup = ENV.get('VMWARE_RESTORE_NETWORKING')
|
|
|
453 |
backup = backup and path(backup)
|
|
|
454 |
|
|
|
455 |
locations = ENV.get('VMWARE_MIGRATE_NETWORKING')
|
|
|
456 |
locations = locations and path(locations)
|
|
|
457 |
|
|
|
458 |
if backup and backup.owner == 'root':
|
|
|
459 |
log.Info('Restoring previous network settings from %s', backup)
|
|
|
460 |
backup.copy('/etc/vmware/networking')
|
|
|
461 |
backup.remove()
|
|
|
462 |
old = 1
|
|
|
463 |
elif locations and locations.owner == 'root' and not old:
|
|
|
464 |
log.Info('Migrating old network settings from %s', locations)
|
|
|
465 |
if self.RunCommand(vnetlib, '--migrate-network-settings', locations, ignoreErrors=True).retCode == 0:
|
|
|
466 |
locations.remove()
|
|
|
467 |
return True
|
|
|
468 |
else:
|
|
|
469 |
log.Info('Migrating network settings failed, forcing new network settings')
|
|
|
470 |
old = 0
|
|
|
471 |
new = 1
|
|
|
472 |
|
|
|
473 |
# old might be None so make it 0 for the purposes of vnetlib.
|
|
|
474 |
old = old or 0
|
|
|
475 |
|
|
|
476 |
return self.RunCommand(vnetlib, '--postinstall', '%s,%s,%s' % ('vmware-player', old, new),
|
|
|
477 |
ignoreErrors=True).retCode == 0
|
|
|
478 |
|
|
|
479 |
def getToolsVersion(self):
|
|
|
480 |
toolboxCmd = path('/usr/bin/vmware-toolbox-cmd')
|
|
|
481 |
toolbox = path('/usr/bin/vmware-toolbox')
|
|
|
482 |
ret = None
|
|
|
483 |
if toolboxCmd:
|
|
|
484 |
ret = self.RunCommand(toolboxCmd, '--version', ignoreErrors=True)
|
|
|
485 |
elif toolbox:
|
|
|
486 |
ret = self.RunCommand(toolbox, '--version', ignoreErrors=True)
|
|
|
487 |
else:
|
|
|
488 |
# Cannot find Tools version...
|
|
|
489 |
return None
|
|
|
490 |
|
|
|
491 |
if ret and ret.retCode == 0:
|
|
|
492 |
toolsVersion = ret.stdout
|
|
|
493 |
# Parse it
|
|
|
494 |
toolsVersion = re.findall('\d+\.\d+\.\d+', toolsVersion)
|
|
|
495 |
if toolsVersion:
|
|
|
496 |
toolsVersion = toolsVersion[0]
|
|
|
497 |
log.Info('Found Tools version %s' % toolsVersion)
|
|
|
498 |
return toolsVersion
|
|
|
499 |
else:
|
|
|
500 |
# Something went wrong in the process... No Tools version found...
|
|
|
501 |
return None
|
|
|
502 |
else:
|
|
|
503 |
return None
|
|
|
504 |
|
|
|
505 |
def isToolsVersionOkay(self, version, minVersion):
|
|
|
506 |
if not version:
|
|
|
507 |
return False
|
|
|
508 |
versions = self.LoadInclude('versions')
|
|
|
509 |
if versions.CompareVersionString(version, minVersion) >= 0:
|
|
|
510 |
log.Info('Tools version is compatible.')
|
|
|
511 |
return True
|
|
|
512 |
else:
|
|
|
513 |
log.Info('Tools version is too old.')
|
|
|
514 |
return False
|
|
|
515 |
|
|
|
516 |
def getHostGlibcVersion(self):
|
|
|
517 |
"""Return host's glibc version as a tuple."""
|
|
|
518 |
try:
|
|
|
519 |
import subprocess
|
|
|
520 |
out = subprocess.check_output(["ldd", "--version"]).decode()
|
|
|
521 |
brand = out.split('\n', 1)[0]
|
|
|
522 |
except subprocess.CalledProcessError:
|
|
|
523 |
log.Info('Unable to query glibc version.')
|
|
|
524 |
return None
|
|
|
525 |
# brand = full ldd brand string, e.g. 'ldd (Distro GLIBC N.NN) 2.17'
|
|
|
526 |
log.Info('Glibc brand string ' + brand)
|
|
|
527 |
try:
|
|
|
528 |
# versionStr = glibc version, e.g. '2.17'
|
|
|
529 |
versionStr = brand.split(' ')[-1]
|
|
|
530 |
glibcVersion = tuple([int(x) for x in versionStr.split(".")])
|
|
|
531 |
log.Info('Found glibc version ' + str(glibcVersion))
|
|
|
532 |
return glibcVersion
|
|
|
533 |
except Exception as e:
|
|
|
534 |
log.Info('Unable to parse glibc version: ' + str(e))
|
|
|
535 |
return None
|
|
|
536 |
|
|
|
537 |
def getHostKernelVersion(self):
|
|
|
538 |
"""Return host's kernel version as a tuple."""
|
|
|
539 |
import platform
|
|
|
540 |
# releaseStr = full kernel version, e.g. '3.10.0-327.el7.x86_64'
|
|
|
541 |
releaseStr = platform.release()
|
|
|
542 |
log.Info('Kernel release string ' + releaseStr)
|
|
|
543 |
# versionStr = kernel numeric version, e.g. '3.10.0'
|
|
|
544 |
try:
|
|
|
545 |
versionStr = releaseStr.split("-")[0]
|
|
|
546 |
kernelVersion = tuple([int(x) for x in versionStr.split(".")])
|
|
|
547 |
log.Info('Found Linux kernel version ' + str(kernelVersion))
|
|
|
548 |
return kernelVersion
|
|
|
549 |
except Exception as e:
|
|
|
550 |
log.Info('Unable to parse kernel version: ' + str(e))
|
|
|
551 |
return None
|
|
|
552 |
|
|
|
553 |
def isVersionOkay(self, version, minVersion):
|
|
|
554 |
if not version:
|
|
|
555 |
return True # Do not error out on parse failure
|
|
|
556 |
elif version < minVersion:
|
|
|
557 |
return False
|
|
|
558 |
else:
|
|
|
559 |
return True
|