def _patch(self, category, patchlist): if self.exists_stop_flag(): self.log_and_syslog( logging.INFO, "Installing patches (Category:" + category + ") is stopped/canceled") return False, list() if not patchlist: self.log_and_syslog(logging.INFO, "No packages are available for update.") return False, list() self.log_and_syslog( logging.INFO, "Start to install " + str(len(patchlist)) + " patches (Category:" + category + ")") self.log_and_syslog(logging.INFO, "Patch list: " + ' '.join(patchlist)) pkg_failed = [] for pkg_name in patchlist: if pkg_name == 'walinuxagent': continue current_patch_time = time.time() if current_patch_time - start_patch_time > self.install_duration: msg = "Patching time exceeded. The pending package will be patched in the next cycle" self.log_and_syslog(logging.WARNING, msg) return True, pkg_failed retcode = self.patch_package(pkg_name) if retcode != 0: self.log_and_syslog(logging.ERROR, "Failed to patch the package:" + pkg_name) pkg_failed.append(' '.join([pkg_name, category])) continue self.patched.append(pkg_name) self.log_and_syslog(logging.INFO, "Package " + pkg_name + " is patched.") waagent.AppendFileContents(self.package_patched_path, pkg_name + ' ' + category + '\n') return False, pkg_failed
def _download(self, category): self.hutil.log_and_syslog( logging.INFO, "Start to check&download patches (Category:" + category + ")") retcode, downloadlist = self.check(category) if retcode > 0: msg = "Failed to check valid upgrades" self.hutil.log_and_syslog(logging.ERROR, msg) self.hutil.do_exit(1, 'Enable', 'error', '0', msg) if 'walinuxagent' in downloadlist: downloadlist.remove('walinuxagent') if not downloadlist: self.hutil.log_and_syslog(logging.INFO, "No packages are available for update.") return self.hutil.log_and_syslog( logging.INFO, "There are " + str(len(downloadlist)) + " packages to upgrade.") self.hutil.log_and_syslog(logging.INFO, "Download list: " + ' '.join(downloadlist)) for pkg_name in downloadlist: if pkg_name in self.downloaded: continue retcode = self.download_package(pkg_name) if retcode != 0: self.hutil.log_and_syslog( logging.ERROR, "Failed to download the package: " + pkg_name) continue self.downloaded.append(pkg_name) self.hutil.log_and_syslog( logging.INFO, "Package " + pkg_name + " is downloaded.") waagent.AppendFileContents(self.package_downloaded_path, pkg_name + ' ' + category + '\n')
def _patch(self, category, patchlist): if self.exists_stop_flag(): self.hutil.log("Installing patches (Category:" + category + ") is stopped/canceled") return if not patchlist: self.hutil.log("No packages are available for update.") return self.hutil.log("Start to install " + str(len(patchlist)) + " patches (Category:" + category + ")") self.hutil.log("Patch list: " + ' '.join(patchlist)) for pkg_name in patchlist: current_patch_time = time.time() if current_patch_time - start_patch_time > self.install_duration: self.hutil.log( "Patching time exceeded. The pending package will be \ patched in the next cycle") break retcode = self.patch_package(pkg_name) if retcode != 0: self.hutil.error("Failed to patch the package:" + pkg_name) continue self.patched.append(pkg_name) self.hutil.log("Package " + pkg_name + " is patched.") waagent.AppendFileContents(self.package_patched_path, pkg_name + ' ' + category + '\n')
def retry_download(self): retry_count = 0 max_retry_count = 12 self.log_and_syslog( logging.INFO, "Retry queue: {0}".format(" ".join([ pkg_name for pkg_name, category in self.download_retry_queue ]))) while self.download_retry_queue: pkg_name, category = self.download_retry_queue[0] self.download_retry_queue = self.download_retry_queue[1:] retcode = self.download_package(pkg_name) if retcode == 0: self.downloaded.append(pkg_name) self.log_and_syslog(logging.INFO, "Package " + pkg_name + " is downloaded.") waagent.AppendFileContents(self.package_downloaded_path, pkg_name + ' ' + category + '\n') else: self.log_and_syslog( logging.ERROR, "Failed to download the package: " + pkg_name) self.log_and_syslog( logging.INFO, "Put {0} back into a retry queue".format(pkg_name)) self.download_retry_queue.append((pkg_name, category)) retry_count = retry_count + 1 if retry_count > max_retry_count: err_msg = ("Failed to download after {0} retries, " "retry queue: {1}").format( max_retry_count, " ".join([ pkg_name for pkg_name, category in self.download_retry_queue ])) self.log_and_syslog(logging.ERROR, err_msg) waagent.AddExtensionEvent( name=self.hutil.get_name(), op=waagent.WALAEventOperation.Download, isSuccess=False, version=Version, message=err_msg) break k = retry_count if (retry_count < 10) else 10 interval = int(random.uniform(0, 2**k)) self.log_and_syslog( logging.INFO, ("Sleep {0}s before " "the next retry, current retry_count = {1}").format( interval, retry_count)) time.sleep(interval)
def _download(self, category): self.hutil.log("Start to check&download patches (Category:" + category + ")") retcode, downloadlist = self.check(category) if retcode > 0: self.hutil.error("Failed to check valid upgrades") sys.exit(1) if not downloadlist: self.hutil.log("No packages are available for update.") return self.hutil.log("There are " + str(len(downloadlist)) + " packages to upgrade.") self.hutil.log("Download list: " + ' '.join(downloadlist)) for pkg_name in downloadlist: if pkg_name in self.downloaded: continue retcode = self.download_package(pkg_name) if retcode != 0: self.hutil.error("Failed to download the package: " + pkg_name) continue self.downloaded.append(pkg_name) self.hutil.log("Package " + pkg_name + " is downloaded.") waagent.AppendFileContents(self.package_downloaded_path, pkg_name + ' ' + category + '\n')
def patch_one_off(self): """ Called when startTime is empty string, which means a on-demand patch. """ self.provide_vm_status_test(StatusTest["Oneoff"]) if not self.check_vm_idle(StatusTest["Oneoff"]): return global start_patch_time start_patch_time = time.time() self.hutil.log_and_syslog(logging.INFO, "Going to patch one-off") waagent.SetFileContents(self.package_downloaded_path, '') waagent.SetFileContents(self.package_patched_path, '') # Record the open deleted files before patching self.open_deleted_files_before = self.check_open_deleted_files() pkg_failed = [] is_time_out = [False, False] retcode, patchlist_required = self.check(self.category_required) if retcode > 0: msg = "Failed to check valid upgrades" self.hutil.log_and_syslog(logging.ERROR, msg) self.hutil.do_exit(1, 'Enable', 'error', '0', msg) if not patchlist_required: self.hutil.log_and_syslog( logging.INFO, "No packages are available for update. (Category:" + self.category_required + ")") else: is_time_out[0], failed = self._patch(self.category_required, patchlist_required) pkg_failed.extend(failed) if self.category == self.category_all: if not self.exists_stop_flag(): if not is_time_out[0]: retcode, patchlist_other = self.check(self.category_all) if retcode > 0: msg = "Failed to check valid upgrades" self.hutil.log_and_syslog(logging.ERROR, msg) self.hutil.do_exit(1, 'Enable', 'error', '0', msg) patchlist_other = [ pkg for pkg in patchlist_other if pkg not in patchlist_required ] if len(patchlist_other) == 0: self.hutil.log_and_syslog( logging.INFO, "No packages are available for update. (Category:" + self.category_all + ")") else: self.hutil.log_and_syslog( logging.INFO, "Going to sleep for " + str(self.gap_between_stage) + "s") time.sleep(self.gap_between_stage) self.hutil.log_and_syslog( logging.INFO, "Going to patch one-off (Category:" + self.category_all + ")") is_time_out[1], failed = self._patch( self.category_all, patchlist_other) pkg_failed.extend(failed) else: self.hutil.log_and_syslog( logging.INFO, "Installing patches (Category:" + self.category_all + ") is stopped/canceled") if is_time_out[0] or is_time_out[1]: waagent.AddExtensionEvent(name=self.hutil.get_name(), op="Oneoff Patch", isSuccess=False, version=Version, message="Patching time out") shutil.copy2(self.package_patched_path, self.package_downloaded_path) for pkg in pkg_failed: waagent.AppendFileContents(self.package_downloaded_path, pkg + '\n') self.open_deleted_files_after = self.check_open_deleted_files() self.delete_stop_flag() #self.report() if StatusTest["Oneoff"]["Healthy"]: is_healthy = StatusTest["Oneoff"]["Healthy"]() msg = "Checking the VM is healthy after patching: " + str( is_healthy) self.hutil.log_and_syslog(logging.INFO, msg) waagent.AddExtensionEvent(name=self.hutil.get_name(), op="Check healthy", isSuccess=is_healthy, version=Version, message=msg) if self.patched is not None and len(self.patched) > 0: self.reboot_if_required()
def patch(self): # Read the latest configuration for scheduled task settings = json.loads( waagent.GetFileContents(self.scheduled_configs_file)) self.parse_settings(settings) if not self.check_vm_idle(StatusTest["Scheduled"]): return if self.exists_stop_flag(): self.hutil.log_and_syslog( logging.INFO, "Installing patches is stopped/canceled") self.delete_stop_flag() return # Record the scheduled time waagent.AppendFileContents( self.history_scheduled, time.strftime("%Y-%m-%d %a", time.localtime()) + '\n') # Record the open deleted files before patching self.open_deleted_files_before = self.check_open_deleted_files() retcode = self.stop_download() if retcode == 0: self.hutil.log_and_syslog( logging.WARNING, "Download time exceeded. The pending package will be downloaded in the next cycle" ) waagent.AddExtensionEvent(name=self.hutil.get_name(), op=waagent.WALAEventOperation.Download, isSuccess=False, version=Version, message="Downloading time out") global start_patch_time start_patch_time = time.time() pkg_failed = [] is_time_out = [False, False] patchlist = self.get_pkg_to_patch(self.category_required) is_time_out[0], failed = self._patch(self.category_required, patchlist) pkg_failed.extend(failed) if not self.exists_stop_flag(): if not is_time_out[0]: patchlist = self.get_pkg_to_patch(self.category_all) if len(patchlist) == 0: self.hutil.log_and_syslog( logging.INFO, "No packages are available for update. (Category:" + self.category_all + ")") else: self.hutil.log_and_syslog( logging.INFO, "Going to sleep for " + str(self.gap_between_stage) + "s") time.sleep(self.gap_between_stage) is_time_out[1], failed = self._patch( self.category_all, patchlist) pkg_failed.extend(failed) else: msg = "Installing patches (Category:" + self.category_all + ") is stopped/canceled" self.hutil.log_and_syslog(logging.INFO, msg) if is_time_out[0] or is_time_out[1]: msg = "Patching time out" self.hutil.log_and_syslog(logging.WARNING, msg) waagent.AddExtensionEvent(name=self.hutil.get_name(), op="Patch", isSuccess=False, version=Version, message=msg) self.open_deleted_files_after = self.check_open_deleted_files() self.delete_stop_flag() #self.report() if StatusTest["Scheduled"]["Healthy"]: is_healthy = StatusTest["Scheduled"]["Healthy"]() msg = "Checking the VM is healthy after patching: " + str( is_healthy) self.hutil.log_and_syslog(logging.INFO, msg) waagent.AddExtensionEvent(name=self.hutil.get_name(), op="Check healthy", isSuccess=is_healthy, version=Version, message=msg) if self.patched is not None and len(self.patched) > 0: self.reboot_if_required()
def _save_other_sudoers(sudoers): sudoersFile = '/etc/sudoers.d/waagent' if sudoers is None: return waagent.AppendFileContents(sudoersFile, "\n".join(sudoers)) os.chmod("/etc/sudoers.d/waagent", 0440)
def _set_user_account_pub_key(protect_settings, hutil): ovf_xml = waagent.GetFileContents('/var/lib/waagent/ovf-env.xml') ovf_env = waagent.OvfEnv().Parse(ovf_xml) # user name must be provided if set ssh key or password if not protect_settings or not protect_settings.has_key('username'): return user_name = protect_settings['username'] user_pass = protect_settings.get('password') cert_txt = protect_settings.get('ssh_key') no_convert = False if (not (user_pass) and not (cert_txt) and not (ovf_env.SshPublicKeys)): raise Exception("No password or ssh_key is specified.") #Reset user account and password, password could be empty sudoers = _get_other_sudoers(user_name) error_string = waagent.MyDistro.CreateAccount(user_name, user_pass, None, None) _save_other_sudoers(sudoers) if error_string != None: waagent.AddExtensionEvent( name=hutil.get_name(), op=waagent.WALAEventOperation.Enable, isSuccess=False, message="(02101)Failed to create the account or set the password.") raise Exception("Failed to create the account or set the password: "******"Succeeded in create the account or set the password.") #Allow password authentication if user_pass is provided if user_pass is not None: _allow_password_auth() #Reset ssh key with the new public key passed in or reuse old public key. if cert_txt or len(ovf_env.SshPublicKeys) > 0: if cert_txt and cert_txt.strip().lower().startswith("ssh-rsa"): no_convert = True try: pub_path = os.path.join('/home/', user_name, '.ssh', 'authorized_keys') ovf_env.UserName = user_name if (no_convert): if (cert_txt): pub_path = ovf_env.PrepareDir(pub_path) final_cert_txt = cert_txt if (not cert_txt.endswith("\n")): final_cert_txt = final_cert_txt + "\n" waagent.AppendFileContents(pub_path, final_cert_txt) waagent.MyDistro.setSelinuxContext( pub_path, 'unconfined_u:object_r:ssh_home_t:s0') waagent.ChangeOwner(pub_path, user_name) hutil.log("Succeeded in resetting ssh_key.") else: waagent.AddExtensionEvent( name=hutil.get_name(), op=waagent.WALAEventOperation.Enable, isSuccess=False, message= "(02100)Failed to reset ssh key because the cert content is empty." ) else: if cert_txt: _save_cert_str_as_file(cert_txt, 'temp.crt') else: for pkey in ovf_env.SshPublicKeys: if pkey[1]: shutil.copy( os.path.join(waagent.LibDir, pkey[0] + '.crt'), os.path.join(os.getcwd(), 'temp.crt')) break pub_path = ovf_env.PrepareDir(pub_path) retcode = waagent.Run( waagent.Openssl + " x509 -in temp.crt -noout -pubkey > temp.pub") if retcode > 0: raise Exception("Failed to generate public key file.") waagent.MyDistro.sshDeployPublicKey('temp.pub', pub_path) waagent.MyDistro.setSelinuxContext( pub_path, 'unconfined_u:object_r:ssh_home_t:s0') waagent.ChangeOwner(pub_path, user_name) os.remove('temp.pub') os.remove('temp.crt') hutil.log("Succeeded in resetting ssh_key.") except Exception as e: hutil.log(str(e)) waagent.AddExtensionEvent( name=hutil.get_name(), op=waagent.WALAEventOperation.Enable, isSuccess=False, message="(02100)Failed to reset ssh key.")