in VMBackup/main/WaagentLib.py [0:0]
def Parse(self, xmlText):
"""
Write configuration to file ExtensionsConfig.xml.
Log plugin specific activity to /var/log/azure/<Publisher>.<PluginName>/<Version>/CommandExecution.log.
If state is enabled:
if the plugin is installed:
if the new plugin's version is higher
if DisallowMajorVersionUpgrade is false or if true, the version is a minor version do upgrade:
download the new archive
do the updateCommand.
disable the old plugin and remove
enable the new plugin
if the new plugin's version is the same or lower:
create the new .settings file from the configuration received
do the enableCommand
if the plugin is not installed:
download/unpack archive and call the installCommand/Enable
if state is disabled:
call disableCommand
if state is uninstall:
call uninstallCommand
remove old plugin directory.
"""
self.reinitialize()
self.Util = Util()
dom = xml.dom.minidom.parseString(xmlText)
LogIfVerbose(xmlText)
self.plugin_log_dir = '/var/log/azure'
if not os.path.exists(self.plugin_log_dir):
os.mkdir(self.plugin_log_dir)
try:
self.Extensions = dom.getElementsByTagName("Extensions")
pg = dom.getElementsByTagName("Plugins")
if len(pg) > 0:
self.Plugins = pg[0].getElementsByTagName("Plugin")
else:
self.Plugins = []
incarnation = self.Extensions[0].getAttribute("goalStateIncarnation")
SetFileContents('ExtensionsConfig.' + incarnation + '.xml', xmlText)
except Exception as e:
Error('ERROR: Error parsing ExtensionsConfig: {0}.'.format(e))
return None
for p in self.Plugins:
if len(p.getAttribute("location")) < 1: # this plugin is inside the PluginSettings
continue
p.setAttribute('restricted', 'false')
previous_version = None
version = p.getAttribute("version")
name = p.getAttribute("name")
plog_dir = self.plugin_log_dir + '/' + name + '/' + version
if not os.path.exists(plog_dir):
os.makedirs(plog_dir)
p.plugin_log = plog_dir + '/CommandExecution.log'
handler = name + '-' + version
if p.getAttribute("isJson") != 'true':
Error("Plugin " + name + " version: " + version + " is not a JSON Extension. Skipping.")
continue
Log("Found Plugin: " + name + ' version: ' + version)
if p.getAttribute("state") == 'disabled' or p.getAttribute("state") == 'uninstall':
# disable
zip_dir = LibDir + "/" + name + '-' + version
mfile = None
for root, dirs, files in os.walk(zip_dir):
for f in files:
if f in ('HandlerManifest.json'):
mfile = os.path.join(root, f)
if mfile != None:
break
if mfile == None:
Error('HandlerManifest.json not found.')
continue
manifest = GetFileContents(mfile)
p.setAttribute('manifestdata', manifest)
if self.launchCommand(p.plugin_log, name, version, 'disableCommand') == None:
self.SetHandlerState(handler, 'Enabled')
Error('Unable to disable ' + name)
SimpleLog(p.plugin_log, 'ERROR: Unable to disable ' + name)
else:
self.SetHandlerState(handler, 'Disabled')
Log(name + ' is disabled')
SimpleLog(p.plugin_log, name + ' is disabled')
# uninstall if needed
if p.getAttribute("state") == 'uninstall':
if self.launchCommand(p.plugin_log, name, version, 'uninstallCommand') == None:
self.SetHandlerState(handler, 'Installed')
Error('Unable to uninstall ' + name)
SimpleLog(p.plugin_log, 'Unable to uninstall ' + name)
else:
self.SetHandlerState(handler, 'NotInstalled')
Log(name + ' uninstallCommand completed .')
# remove the plugin
Run('rm -rf ' + LibDir + '/' + name + '-' + version + '*')
Log(name + '-' + version + ' extension files deleted.')
SimpleLog(p.plugin_log, name + '-' + version + ' extension files deleted.')
continue
# state is enabled
# if the same plugin exists and the version is newer or
# does not exist then download and unzip the new plugin
plg_dir = None
latest_version_installed = LooseVersion("0.0")
for item in os.listdir(LibDir):
itemPath = os.path.join(LibDir, item)
if os.path.isdir(itemPath) and name in item:
try:
# Split plugin dir name with '-' to get intalled plugin name and version
sperator = item.rfind('-')
if sperator < 0:
continue
installed_plg_name = item[0:sperator]
installed_plg_version = LooseVersion(item[sperator + 1:])
# Check installed plugin name and compare installed version to get the latest version installed
if installed_plg_name == name and installed_plg_version > latest_version_installed:
plg_dir = itemPath
previous_version = str(installed_plg_version)
latest_version_installed = installed_plg_version
except Exception as e:
Warn("Invalid plugin dir name: {0} {1}".format(item, e))
continue
if plg_dir == None or LooseVersion(version) > LooseVersion(previous_version):
location = p.getAttribute("location")
Log("Downloading plugin manifest: " + name + " from " + location)
SimpleLog(p.plugin_log, "Downloading plugin manifest: " + name + " from " + location)
self.Util.Endpoint = location.split('/')[2]
Log("Plugin server is: " + self.Util.Endpoint)
SimpleLog(p.plugin_log, "Plugin server is: " + self.Util.Endpoint)
manifest = self.Util.HttpGetWithoutHeaders(location, chkProxy=True)
if manifest == None:
Error(
"Unable to download plugin manifest" + name + " from primary location. Attempting with failover location.")
SimpleLog(p.plugin_log,
"Unable to download plugin manifest" + name + " from primary location. Attempting with failover location.")
failoverlocation = p.getAttribute("failoverlocation")
self.Util.Endpoint = failoverlocation.split('/')[2]
Log("Plugin failover server is: " + self.Util.Endpoint)
SimpleLog(p.plugin_log, "Plugin failover server is: " + self.Util.Endpoint)
manifest = self.Util.HttpGetWithoutHeaders(failoverlocation, chkProxy=True)
# if failoverlocation also fail what to do then?
if manifest == None:
AddExtensionEvent(name, WALAEventOperation.Download, False, 0, version,
"Download mainfest fail " + failoverlocation)
Log("Plugin manifest " + name + " downloading failed from failover location.")
SimpleLog(p.plugin_log, "Plugin manifest " + name + " downloading failed from failover location.")
filepath = LibDir + "/" + name + '.' + incarnation + '.manifest'
if os.path.splitext(location)[-1] == '.xml': # if this is an xml file we may have a BOM
if ord(manifest[0]) > 128 and ord(manifest[1]) > 128 and ord(manifest[2]) > 128:
manifest = manifest[3:]
SetFileContents(filepath, manifest)
# Get the bundle url from the manifest
p.setAttribute('manifestdata', manifest)
man_dom = xml.dom.minidom.parseString(manifest)
bundle_uri = ""
for mp in man_dom.getElementsByTagName("Plugin"):
if GetNodeTextData(mp.getElementsByTagName("Version")[0]) == version:
bundle_uri = GetNodeTextData(mp.getElementsByTagName("Uri")[0])
break
if len(mp.getElementsByTagName("DisallowMajorVersionUpgrade")):
if GetNodeTextData(mp.getElementsByTagName("DisallowMajorVersionUpgrade")[
0]) == 'true' and previous_version != None and previous_version.split('.')[
0] != version.split('.')[0]:
Log('DisallowMajorVersionUpgrade is true, this major version is restricted from upgrade.')
SimpleLog(p.plugin_log,
'DisallowMajorVersionUpgrade is true, this major version is restricted from upgrade.')
p.setAttribute('restricted', 'true')
continue
if len(bundle_uri) < 1:
Error("Unable to fetch Bundle URI from manifest for " + name + " v " + version)
SimpleLog(p.plugin_log, "Unable to fetch Bundle URI from manifest for " + name + " v " + version)
continue
Log("Bundle URI = " + bundle_uri)
SimpleLog(p.plugin_log, "Bundle URI = " + bundle_uri)
# Download the zipfile archive and save as '.zip'
bundle = self.Util.HttpGetWithoutHeaders(bundle_uri, chkProxy=True)
if bundle == None:
AddExtensionEvent(name, WALAEventOperation.Download, True, 0, version,
"Download zip fail " + bundle_uri)
Error("Unable to download plugin bundle" + bundle_uri)
SimpleLog(p.plugin_log, "Unable to download plugin bundle" + bundle_uri)
continue
AddExtensionEvent(name, WALAEventOperation.Download, True, 0, version, "Download Success")
b = bytearray(bundle)
filepath = LibDir + "/" + os.path.basename(bundle_uri) + '.zip'
SetFileContents(filepath, b)
Log("Plugin bundle" + bundle_uri + "downloaded successfully length = " + str(len(bundle)))
SimpleLog(p.plugin_log,
"Plugin bundle" + bundle_uri + "downloaded successfully length = " + str(len(bundle)))
# unpack the archive
z = zipfile.ZipFile(filepath)
zip_dir = LibDir + "/" + name + '-' + version
z.extractall(zip_dir)
Log('Extracted ' + bundle_uri + ' to ' + zip_dir)
SimpleLog(p.plugin_log, 'Extracted ' + bundle_uri + ' to ' + zip_dir)
# zip no file perms in .zip so set all the scripts to +x
Run("find " + zip_dir + " -type f | xargs chmod u+x ")
# write out the base64 config data so the plugin can process it.
mfile = None
for root, dirs, files in os.walk(zip_dir):
for f in files:
if f in ('HandlerManifest.json'):
mfile = os.path.join(root, f)
if mfile != None:
break
if mfile == None:
Error('HandlerManifest.json not found.')
SimpleLog(p.plugin_log, 'HandlerManifest.json not found.')
continue
manifest = GetFileContents(mfile)
p.setAttribute('manifestdata', manifest)
# create the status and config dirs
Run('mkdir -p ' + root + '/status')
Run('mkdir -p ' + root + '/config')
# write out the configuration data to goalStateIncarnation.settings file in the config path.
config = ''
seqNo = '0'
if len(dom.getElementsByTagName("PluginSettings")) != 0:
pslist = dom.getElementsByTagName("PluginSettings")[0].getElementsByTagName("Plugin")
for ps in pslist:
if name == ps.getAttribute("name") and version == ps.getAttribute("version"):
Log("Found RuntimeSettings for " + name + " V " + version)
SimpleLog(p.plugin_log, "Found RuntimeSettings for " + name + " V " + version)
config = GetNodeTextData(ps.getElementsByTagName("RuntimeSettings")[0])
seqNo = ps.getElementsByTagName("RuntimeSettings")[0].getAttribute("seqNo")
break
if config == '':
Log("No RuntimeSettings for " + name + " V " + version)
SimpleLog(p.plugin_log, "No RuntimeSettings for " + name + " V " + version)
SetFileContents(root + "/config/" + seqNo + ".settings", config)
# create HandlerEnvironment.json
handler_env = '[{ "name": "' + name + '", "seqNo": "' + seqNo + '", "version": 1.0, "handlerEnvironment": { "logFolder": "' + os.path.dirname(
p.plugin_log) + '", "configFolder": "' + root + '/config", "statusFolder": "' + root + '/status", "heartbeatFile": "' + root + '/heartbeat.log"}}]'
SetFileContents(root + '/HandlerEnvironment.json', handler_env)
self.SetHandlerState(handler, 'NotInstalled')
cmd = ''
getcmd = 'installCommand'
if plg_dir != None and previous_version != None and LooseVersion(version) > LooseVersion(
previous_version):
previous_handler = name + '-' + previous_version
if self.GetHandlerState(previous_handler) != 'NotInstalled':
getcmd = 'updateCommand'
# disable the old plugin if it exists
if self.launchCommand(p.plugin_log, name, previous_version, 'disableCommand') == None:
self.SetHandlerState(previous_handler, 'Enabled')
Error('Unable to disable old plugin ' + name + ' version ' + previous_version)
SimpleLog(p.plugin_log,
'Unable to disable old plugin ' + name + ' version ' + previous_version)
else:
self.SetHandlerState(previous_handler, 'Disabled')
Log(name + ' version ' + previous_version + ' is disabled')
SimpleLog(p.plugin_log, name + ' version ' + previous_version + ' is disabled')
try:
Log("Copy status file from old plugin dir to new")
old_plg_dir = plg_dir
new_plg_dir = os.path.join(LibDir, "{0}-{1}".format(name, version))
old_ext_status_dir = os.path.join(old_plg_dir, "status")
new_ext_status_dir = os.path.join(new_plg_dir, "status")
if os.path.isdir(old_ext_status_dir):
for status_file in os.listdir(old_ext_status_dir):
status_file_path = os.path.join(old_ext_status_dir, status_file)
if os.path.isfile(status_file_path):
shutil.copy2(status_file_path, new_ext_status_dir)
mrseq_file = os.path.join(old_plg_dir, "mrseq")
if os.path.isfile(mrseq_file):
shutil.copy(mrseq_file, new_plg_dir)
except Exception as e:
Error("Failed to copy status file.")
isupgradeSuccess = True
if getcmd == 'updateCommand':
if self.launchCommand(p.plugin_log, name, version, getcmd, previous_version) == None:
Error('Update failed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Update failed for ' + name + '-' + version)
isupgradeSuccess = False
else:
Log('Update complete' + name + '-' + version)
SimpleLog(p.plugin_log, 'Update complete' + name + '-' + version)
# if we updated - call unistall for the old plugin
if self.launchCommand(p.plugin_log, name, previous_version, 'uninstallCommand') == None:
self.SetHandlerState(previous_handler, 'Installed')
Error('Uninstall failed for ' + name + '-' + previous_version)
SimpleLog(p.plugin_log, 'Uninstall failed for ' + name + '-' + previous_version)
isupgradeSuccess = False
else:
self.SetHandlerState(previous_handler, 'NotInstalled')
Log('Uninstall complete' + previous_handler)
SimpleLog(p.plugin_log, 'Uninstall complete' + name + '-' + previous_version)
try:
# rm old plugin dir
if os.path.isdir(plg_dir):
shutil.rmtree(plg_dir)
Log(name + '-' + previous_version + ' extension files deleted.')
SimpleLog(p.plugin_log, name + '-' + previous_version + ' extension files deleted.')
except Exception as e:
Error("Failed to remove old plugin directory")
AddExtensionEvent(name, WALAEventOperation.Upgrade, isupgradeSuccess, 0, previous_version)
else: # run install
if self.launchCommand(p.plugin_log, name, version, getcmd) == None:
self.SetHandlerState(handler, 'NotInstalled')
Error('Installation failed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Installation failed for ' + name + '-' + version)
else:
self.SetHandlerState(handler, 'Installed')
Log('Installation completed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Installation completed for ' + name + '-' + version)
# end if plg_dir == none or version > = prev
# change incarnation of settings file so it knows how to name status...
zip_dir = LibDir + "/" + name + '-' + version
mfile = None
for root, dirs, files in os.walk(zip_dir):
for f in files:
if f in ('HandlerManifest.json'):
mfile = os.path.join(root, f)
if mfile != None:
break
if mfile == None:
Error('HandlerManifest.json not found.')
SimpleLog(p.plugin_log, 'HandlerManifest.json not found.')
continue
manifest = GetFileContents(mfile)
p.setAttribute('manifestdata', manifest)
config = ''
seqNo = '0'
if len(dom.getElementsByTagName("PluginSettings")) != 0:
try:
pslist = dom.getElementsByTagName("PluginSettings")[0].getElementsByTagName("Plugin")
except:
Error('Error parsing ExtensionsConfig.')
SimpleLog(p.plugin_log, 'Error parsing ExtensionsConfig.')
continue
for ps in pslist:
if name == ps.getAttribute("name") and version == ps.getAttribute("version"):
Log("Found RuntimeSettings for " + name + " V " + version)
SimpleLog(p.plugin_log, "Found RuntimeSettings for " + name + " V " + version)
config = GetNodeTextData(ps.getElementsByTagName("RuntimeSettings")[0])
seqNo = ps.getElementsByTagName("RuntimeSettings")[0].getAttribute("seqNo")
break
if config == '':
Error("No RuntimeSettings for " + name + " V " + version)
SimpleLog(p.plugin_log, "No RuntimeSettings for " + name + " V " + version)
SetFileContents(root + "/config/" + seqNo + ".settings", config)
# state is still enable
if (self.GetHandlerState(handler) == 'NotInstalled'): # run install first if true
if self.launchCommand(p.plugin_log, name, version, 'installCommand') == None:
self.SetHandlerState(handler, 'NotInstalled')
Error('Installation failed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Installation failed for ' + name + '-' + version)
else:
self.SetHandlerState(handler, 'Installed')
Log('Installation completed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Installation completed for ' + name + '-' + version)
if (self.GetHandlerState(handler) != 'NotInstalled'):
if self.launchCommand(p.plugin_log, name, version, 'enableCommand') == None:
self.SetHandlerState(handler, 'Installed')
Error('Enable failed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Enable failed for ' + name + '-' + version)
else:
self.SetHandlerState(handler, 'Enabled')
Log('Enable completed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Enable completed for ' + name + '-' + version)
# this plugin processing is complete
Log('Processing completed for ' + name + '-' + version)
SimpleLog(p.plugin_log, 'Processing completed for ' + name + '-' + version)
# end plugin processing loop
Log('Finished processing ExtensionsConfig.xml')
try:
SimpleLog(p.plugin_log, 'Finished processing ExtensionsConfig.xml')
except:
pass
return self