diff options
author | Ćukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com> | 2017-07-03 13:44:00 +0200 |
---|---|---|
committer | usd-importer <ubuntu-server@lists.ubuntu.com> | 2017-07-03 12:23:41 +0000 |
commit | 43bdf9debe5377216aed0086bff2aad864f6ba82 (patch) | |
tree | 2db24e946e7b5b18c302c9d0a41c3e3ac5d52d5e /azurelinuxagent/common | |
parent | 4fb0b5a09b26135ade285844da5d7dfe582a8d4c (diff) | |
download | vyos-walinuxagent-43bdf9debe5377216aed0086bff2aad864f6ba82.tar.gz vyos-walinuxagent-43bdf9debe5377216aed0086bff2aad864f6ba82.zip |
Import patches-unapplied version 2.2.14-0ubuntu1 to ubuntu/artful-proposed
Imported using git-ubuntu import.
Changelog parent: 4fb0b5a09b26135ade285844da5d7dfe582a8d4c
New changelog entries:
* New upstream release (LP: #1701350).
* debian/copyright:
- Refreshed copyright content.
Diffstat (limited to 'azurelinuxagent/common')
-rw-r--r-- | azurelinuxagent/common/event.py | 70 | ||||
-rw-r--r-- | azurelinuxagent/common/future.py | 4 | ||||
-rw-r--r-- | azurelinuxagent/common/logger.py | 35 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/bigip.py | 51 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/default.py | 13 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/factory.py | 4 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/freebsd.py | 12 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/gaia.py | 69 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/openbsd.py | 345 | ||||
-rw-r--r-- | azurelinuxagent/common/protocol/hostplugin.py | 37 | ||||
-rw-r--r-- | azurelinuxagent/common/protocol/metadata.py | 6 | ||||
-rw-r--r-- | azurelinuxagent/common/protocol/util.py | 5 | ||||
-rw-r--r-- | azurelinuxagent/common/protocol/wire.py | 21 | ||||
-rw-r--r-- | azurelinuxagent/common/version.py | 10 |
14 files changed, 555 insertions, 127 deletions
diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py index 116478b..723b8bf 100644 --- a/azurelinuxagent/common/event.py +++ b/azurelinuxagent/common/event.py @@ -25,7 +25,7 @@ import datetime import threading import platform -from datetime import datetime +from datetime import datetime, timedelta import azurelinuxagent.common.logger as logger @@ -39,6 +39,7 @@ from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ DISTRO_CODE_NAME, AGENT_VERSION, \ CURRENT_AGENT, CURRENT_VERSION +_EVENT_MSG = "Event: name={0}, op={1}, message={2}" class WALAEventOperation: ActivateResourceDisk = "ActivateResourceDisk" @@ -47,6 +48,7 @@ class WALAEventOperation: Enable = "Enable" HealthCheck = "HealthCheck" HeartBeat = "HeartBeat" + HostPlugin = "HostPlugin" Install = "Install" InitializeHostPlugin = "InitializeHostPlugin" ProcessGoalState = "ProcessGoalState" @@ -58,10 +60,19 @@ class WALAEventOperation: Upgrade = "Upgrade" Update = "Update" +def _log_event(name, op, message, is_success=True): + global _EVENT_MSG + + if not is_success: + logger.error(_EVENT_MSG, name, op, message) + else: + logger.info(_EVENT_MSG, name, op, message) + class EventLogger(object): def __init__(self): self.event_dir = None + self.periodic_events = {} def save_event(self, data): if self.event_dir is None: @@ -92,9 +103,33 @@ class EventLogger(object): except IOError as e: raise EventError("Failed to write events to file:{0}", e) + def reset_periodic(self): + self.periodic_messages = {} + + def is_period_elapsed(self, delta, h): + return h not in self.periodic_messages or \ + (self.periodic_messages[h] + delta) <= datetime.now() + + def add_periodic(self, + delta, name, op="", is_success=True, duration=0, + version=CURRENT_VERSION, message="", evt_type="", + is_internal=False, log_event=True, force=False): + + h = hash(name+op+ustr(is_success)+message) + + if force or self.is_period_elapsed(delta, h): + self.add_event(name, + op=op, is_success=is_success, duration=duration, + version=version, message=message, evt_type=evt_type, + is_internal=is_internal, log_event=log_event) + self.periodic_messages[h] = datetime.now() + def add_event(self, name, op="", is_success=True, duration=0, version=CURRENT_VERSION, - message="", evt_type="", is_internal=False): + message="", evt_type="", is_internal=False, log_event=True): + if not is_success or log_event: + _log_event(name, op, message, is_success=is_success) + event = TelemetryEvent(1, "69B669B9-4AF8-4C50-BDC4-6006FA76E975") event.parameters.append(TelemetryEventParam('Name', name)) event.parameters.append(TelemetryEventParam('Version', str(version))) @@ -129,21 +164,40 @@ def report_event(op, is_success=True, message=''): message=message, op=op) +def report_periodic(delta, op, is_success=True, message=''): + from azurelinuxagent.common.version import AGENT_NAME, CURRENT_VERSION + add_periodic(delta, AGENT_NAME, + version=CURRENT_VERSION, + is_success=is_success, + message=message, + op=op) def add_event(name, op="", is_success=True, duration=0, version=CURRENT_VERSION, message="", evt_type="", is_internal=False, log_event=True, reporter=__event_logger__): - if log_event or not is_success: - log = logger.info if is_success else logger.error - log("Event: name={0}, op={1}, message={2}", name, op, message) + if reporter.event_dir is None: + logger.warn("Event reporter is not initialized.") + _log_event(name, op, message, is_success=is_success) + return + + reporter.add_event( + name, op=op, is_success=is_success, duration=duration, + version=str(version), message=message, evt_type=evt_type, + is_internal=is_internal, log_event=log_event) +def add_periodic( + delta, name, op="", is_success=True, duration=0, version=CURRENT_VERSION, + message="", evt_type="", is_internal=False, log_event=True, force=False, + reporter=__event_logger__): if reporter.event_dir is None: logger.warn("Event reporter is not initialized.") + _log_event(name, op, message, is_success=is_success) return - reporter.add_event(name, op=op, is_success=is_success, duration=duration, - version=str(version), message=message, evt_type=evt_type, - is_internal=is_internal) + reporter.add_periodic( + delta, name, op=op, is_success=is_success, duration=duration, + version=str(version), message=message, evt_type=evt_type, + is_internal=is_internal, log_event=log_event, force=force) def init_event_logger(event_dir, reporter=__event_logger__): reporter.event_dir = event_dir diff --git a/azurelinuxagent/common/future.py b/azurelinuxagent/common/future.py index 8509732..8d5b70b 100644 --- a/azurelinuxagent/common/future.py +++ b/azurelinuxagent/common/future.py @@ -13,8 +13,6 @@ if sys.version_info[0]== 3: bytebuffer = memoryview - read_input = input - elif sys.version_info[0] == 2: import httplib as httpclient from urlparse import urlparse @@ -24,8 +22,6 @@ elif sys.version_info[0] == 2: bytebuffer = buffer - read_input = raw_input - else: raise ImportError("Unknown python version:{0}".format(sys.version_info)) diff --git a/azurelinuxagent/common/logger.py b/azurelinuxagent/common/logger.py index c1eb18f..bfdc73a 100644 --- a/azurelinuxagent/common/logger.py +++ b/azurelinuxagent/common/logger.py @@ -20,7 +20,13 @@ Log utils import os import sys from azurelinuxagent.common.future import ustr -from datetime import datetime +from datetime import datetime, timedelta + +EVERY_DAY = timedelta(days=1) +EVERY_HALF_DAY = timedelta(hours=12) +EVERY_HOUR = timedelta(hours=1) +EVERY_HALF_HOUR = timedelta(minutes=30) +EVERY_FIFTEEN_MINUTES = timedelta(minutes=15) class Logger(object): """ @@ -28,10 +34,23 @@ class Logger(object): """ def __init__(self, logger=None, prefix=None): self.appenders = [] - if logger is not None: - self.appenders.extend(logger.appenders) + self.logger = self if logger is None else logger + self.periodic_messages = {} self.prefix = prefix + def reset_periodic(self): + self.logger.periodic_messages = {} + + def is_period_elapsed(self, delta, h): + return h not in self.logger.periodic_messages or \ + (self.logger.periodic_messages[h] + delta) <= datetime.now() + + def periodic(self, delta, msg_format, *args): + h = hash(msg_format) + if self.is_period_elapsed(delta, h): + self.info(msg_format, *args) + self.logger.periodic_messages[h] = datetime.now() + def verbose(self, msg_format, *args): self.log(LogLevel.VERBOSE, msg_format, *args) @@ -62,8 +81,12 @@ class Logger(object): log_item = ustr(log_item.encode('ascii', "backslashreplace"), encoding="ascii") + for appender in self.appenders: appender.write(level, log_item) + if self.logger != self: + for appender in self.logger.appenders: + appender.write(level, log_item) def add_appender(self, appender_type, level, path): appender = _create_logger_appender(appender_type, level, path) @@ -129,6 +152,12 @@ class AppenderType(object): def add_logger_appender(appender_type, level=LogLevel.INFO, path=None): DEFAULT_LOGGER.add_appender(appender_type, level, path) +def reset_periodic(): + DEFAULT_LOGGER.reset_periodic() + +def periodic(delta, msg_format, *args): + DEFAULT_LOGGER.periodic(delta, msg_format, *args) + def verbose(msg_format, *args): DEFAULT_LOGGER.verbose(msg_format, *args) diff --git a/azurelinuxagent/common/osutil/bigip.py b/azurelinuxagent/common/osutil/bigip.py index fea7aff..8f6570f 100644 --- a/azurelinuxagent/common/osutil/bigip.py +++ b/azurelinuxagent/common/osutil/bigip.py @@ -258,57 +258,6 @@ class BigIpOSUtil(DefaultOSUtil): """ logger.warn("Eject is not supported on this platform") - def set_admin_access_to_ip(self, dest_ip): - """Sets admin access to an IP address - - This method is primarily used to limit which user account is allowed to - communicate with the Azure(Stack) metadata service. This service is at - the address 169.254.169.254 and includes information about the device - that "normal" users should not be allowed to see. - - We cannot use this iptables command that comes with the default class - because we do not ship the 'ipt_owner' iptables extension with BIG-IP. - - This should not be a problem though as the only people who should have - access to BIG-IP are people who are root anyways. Our system is not - a "general purpose" user system. So for those reasons I am dropping - that requirement from our implementation. - - :param dest_ip: The IP address that you want to allow admin access for - """ - self._set_accept_admin_access_to_ip(dest_ip) - self._set_drop_admin_access_to_ip(dest_ip) - - def _set_accept_admin_access_to_ip(self, dest_ip): - """Sets the "accept" IP Tables rules - - I broke this out to a separate function so that I could more easily - test it in the tests/common/osutil/test_default.py code - - :param dest_ip: - :return: - """ - # This allows root to access dest_ip - rm_old = "iptables -D OUTPUT -d {0} -j ACCEPT" - rule = "iptables -A OUTPUT -d {0} -j ACCEPT" - shellutil.run(rm_old.format(dest_ip), chk_err=False) - shellutil.run(rule.format(dest_ip)) - - def _set_drop_admin_access_to_ip(self, dest_ip): - """Sets the "drop" IP Tables rules - - I broke this out to a separate function so that I could more easily - test it in the tests/common/osutil/test_default.py code - - :param dest_ip: - :return: - """ - # This blocks all other users to access dest_ip - rm_old = "iptables -D OUTPUT -d {0} -j DROP" - rule = "iptables -A OUTPUT -d {0} -j DROP" - shellutil.run(rm_old.format(dest_ip), chk_err=False) - shellutil.run(rule.format(dest_ip)) - def get_first_if(self): """Return the interface name, and ip addr of the management interface. diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py index 20dc1f3..58c0ef8 100644 --- a/azurelinuxagent/common/osutil/default.py +++ b/azurelinuxagent/common/osutil/default.py @@ -841,18 +841,5 @@ class DefaultOSUtil(object): def get_processor_cores(self): return multiprocessing.cpu_count() - def set_admin_access_to_ip(self, dest_ip): - #This allows root to access dest_ip - rm_old= "iptables -D OUTPUT -d {0} -j ACCEPT -m owner --uid-owner 0" - rule = "iptables -A OUTPUT -d {0} -j ACCEPT -m owner --uid-owner 0" - shellutil.run(rm_old.format(dest_ip), chk_err=False) - shellutil.run(rule.format(dest_ip)) - - #This blocks all other users to access dest_ip - rm_old = "iptables -D OUTPUT -d {0} -j DROP" - rule = "iptables -A OUTPUT -d {0} -j DROP" - shellutil.run(rm_old.format(dest_ip), chk_err=False) - shellutil.run(rule.format(dest_ip)) - def check_pid_alive(self, pid): return pid is not None and os.path.isdir(os.path.join('/proc', pid)) diff --git a/azurelinuxagent/common/osutil/factory.py b/azurelinuxagent/common/osutil/factory.py index 3447651..2be90ab 100644 --- a/azurelinuxagent/common/osutil/factory.py +++ b/azurelinuxagent/common/osutil/factory.py @@ -24,6 +24,7 @@ from .clearlinux import ClearLinuxUtil from .coreos import CoreOSUtil from .debian import DebianOSUtil from .freebsd import FreeBSDOSUtil +from .openbsd import OpenBSDOSUtil from .redhat import RedhatOSUtil, Redhat6xOSUtil from .suse import SUSEOSUtil, SUSE11OSUtil from .ubuntu import UbuntuOSUtil, Ubuntu12OSUtil, Ubuntu14OSUtil, UbuntuSnappyOSUtil @@ -87,6 +88,9 @@ def get_osutil(distro_name=DISTRO_NAME, elif distro_name == "freebsd": return FreeBSDOSUtil() + elif distro_name == "openbsd": + return OpenBSDOSUtil() + elif distro_name == "bigip": return BigIpOSUtil() diff --git a/azurelinuxagent/common/osutil/freebsd.py b/azurelinuxagent/common/osutil/freebsd.py index 0f465a9..39d1760 100644 --- a/azurelinuxagent/common/osutil/freebsd.py +++ b/azurelinuxagent/common/osutil/freebsd.py @@ -229,17 +229,21 @@ class FreeBSDOSUtil(DefaultOSUtil): err, output = shellutil.run_get_output(cmd_search_blkvsc) if err == 0: output = output.rstrip() - cmd_search_dev="camcontrol devlist | grep {0} | awk -F \( '{{print $2}}'|awk -F , '{{print $1}}'".format(output) + cmd_search_dev="camcontrol devlist | grep {0} | awk -F \( '{{print $2}}'|sed -e 's/.*(//'| sed -e 's/).*//'".format(output) err, output = shellutil.run_get_output(cmd_search_dev) if err == 0: - return output.rstrip() + for possible in output.rstrip().split(','): + if not possible.startswith('pass'): + return possible cmd_search_storvsc = "camcontrol devlist -b | grep storvsc{0} | awk '{{print $1}}'".format(output) err, output = shellutil.run_get_output(cmd_search_storvsc) if err == 0: output = output.rstrip() - cmd_search_dev="camcontrol devlist | grep {0} | awk -F \( '{{print $2}}'|awk -F , '{{print $1}}'".format(output) + cmd_search_dev="camcontrol devlist | grep {0} | awk -F \( '{{print $2}}'|sed -e 's/.*(//'| sed -e 's/).*//'".format(output) err, output = shellutil.run_get_output(cmd_search_dev) if err == 0: - return output.rstrip() + for possible in output.rstrip().split(','): + if not possible.startswith('pass'): + return possible return None diff --git a/azurelinuxagent/common/osutil/gaia.py b/azurelinuxagent/common/osutil/gaia.py index a1069d3..6a87b6b 100644 --- a/azurelinuxagent/common/osutil/gaia.py +++ b/azurelinuxagent/common/osutil/gaia.py @@ -16,15 +16,20 @@ # Requires Python 2.4+ and Openssl 1.0+ # +import base64 import socket import struct import time -import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf from azurelinuxagent.common.exception import OSUtilError +from azurelinuxagent.common.future import ustr, bytebuffer +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.osutil.default import DefaultOSUtil +from azurelinuxagent.common.utils.cryptutil import CryptUtil +import azurelinuxagent.common.utils.fileutil as fileutil import azurelinuxagent.common.utils.shellutil as shellutil import azurelinuxagent.common.utils.textutil as textutil -from azurelinuxagent.common.osutil.default import DefaultOSUtil class GaiaOSUtil(DefaultOSUtil): @@ -64,12 +69,11 @@ class GaiaOSUtil(DefaultOSUtil): if ret != 0: raise OSUtilError("Failed to delete root password") - def _replace_user(path, username): + def _replace_user(self, path, username): + if path.startswith('$HOME'): + path = '/home' + path[5:] parts = path.split('/') - for i in xrange(len(parts)): - if parts[i] == '$HOME': - parts[i + 1] = username - break + parts[2] = username return '/'.join(parts) def deploy_ssh_keypair(self, username, keypair): @@ -80,13 +84,57 @@ class GaiaOSUtil(DefaultOSUtil): super(GaiaOSUtil, self).deploy_ssh_keypair( username, (path, thumbprint)) + def openssl_to_openssh(self, input_file, output_file): + cryptutil = CryptUtil(conf.get_openssl_cmd()) + ret, out = shellutil.run_get_output( + conf.get_openssl_cmd() + + " rsa -pubin -noout -text -in '" + input_file + "'") + if ret != 0: + raise OSUtilError('openssl failed with {0}'.format(ret)) + + modulus = [] + exponent = [] + buf = None + for line in out.split('\n'): + if line.startswith('Modulus:'): + buf = modulus + buf.append(line) + continue + if line.startswith('Exponent:'): + buf = exponent + buf.append(line) + continue + if buf and line: + buf.append(line.strip().replace(':', '')) + + def text_to_num(buf): + if len(buf) == 1: + return int(buf[0].split()[1]) + return long(''.join(buf[1:]), 16) + + n = text_to_num(modulus) + e = text_to_num(exponent) + + keydata = bytearray() + keydata.extend(struct.pack('>I', len('ssh-rsa'))) + keydata.extend(b'ssh-rsa') + keydata.extend(struct.pack('>I', len(cryptutil.num_to_bytes(e)))) + keydata.extend(cryptutil.num_to_bytes(e)) + keydata.extend(struct.pack('>I', len(cryptutil.num_to_bytes(n)) + 1)) + keydata.extend(b'\0') + keydata.extend(cryptutil.num_to_bytes(n)) + keydata_base64 = base64.b64encode(bytebuffer(keydata)) + fileutil.write_file(output_file, + ustr(b'ssh-rsa ' + keydata_base64 + b'\n', + encoding='utf-8')) + def deploy_ssh_pubkey(self, username, pubkey): logger.info('deploy_ssh_pubkey') username = 'admin' path, thumbprint, value = pubkey path = self._replace_user(path, username) super(GaiaOSUtil, self).deploy_ssh_pubkey( - 'admin', (path, thumbprint, value)) + username, (path, thumbprint, value)) def eject_dvd(self, chk_err=True): logger.warn('eject is not supported on GAiA') @@ -114,7 +162,7 @@ class GaiaOSUtil(DefaultOSUtil): def restart_ssh_service(self): return shellutil.run('/sbin/service sshd condrestart', chk_err=False) - def _address_to_string(addr): + def _address_to_string(self, addr): return socket.inet_ntoa(struct.pack("!I", addr)) def _get_prefix(self, mask): @@ -146,6 +194,3 @@ class GaiaOSUtil(DefaultOSUtil): def del_account(self, username): logger.warn('del_account is ignored on GAiA') - - def set_admin_access_to_ip(self, dest_ip): - logger.warn('set_admin_access_to_ip is ignored on GAiA') diff --git a/azurelinuxagent/common/osutil/openbsd.py b/azurelinuxagent/common/osutil/openbsd.py new file mode 100644 index 0000000..9bfe6de --- /dev/null +++ b/azurelinuxagent/common/osutil/openbsd.py @@ -0,0 +1,345 @@ +# Microsoft Azure Linux Agent +# +# Copyright 2014 Microsoft Corporation +# Copyright 2017 Reyk Floeter <reyk@openbsd.org> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and OpenSSL 1.0+ + +import os +import re +import time +import glob +import datetime + +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf + +from azurelinuxagent.common.exception import OSUtilError +from azurelinuxagent.common.osutil.default import DefaultOSUtil + +UUID_PATTERN = re.compile( + r'^\s*[A-F0-9]{8}(?:\-[A-F0-9]{4}){3}\-[A-F0-9]{12}\s*$', + re.IGNORECASE) + +class OpenBSDOSUtil(DefaultOSUtil): + def __init__(self): + super(OpenBSDOSUtil, self).__init__() + self._scsi_disks_timeout_set = False + + def get_instance_id(self): + ret, output = shellutil.run_get_output("sysctl -n hw.uuid") + if ret != 0 or UUID_PATTERN.match(output) is None: + return "" + return output.strip() + + def set_hostname(self, hostname): + fileutil.write_file("/etc/myname", "{}\n".format(hostname)) + shellutil.run("hostname {0}".format(hostname), chk_err=False) + + def restart_ssh_service(self): + return shellutil.run('rcctl restart sshd', chk_err=False) + + def start_agent_service(self): + return shellutil.run('rcctl start waagent', chk_err=False) + + def stop_agent_service(self): + return shellutil.run('rcctl stop waagent', chk_err=False) + + def register_agent_service(self): + shellutil.run('chmod 0555 /etc/rc.d/waagent', chk_err=False) + return shellutil.run('rcctl enable waagent', chk_err=False) + + def unregister_agent_service(self): + return shellutil.run('rcctl disable waagent', chk_err=False) + + def del_account(self, username): + if self.is_sys_user(username): + logger.error("{0} is a system user. Will not delete it.", + username) + shellutil.run("> /var/run/utmp") + shellutil.run("userdel -r " + username) + self.conf_sudoer(username, remove=True) + + def conf_sudoer(self, username, nopasswd=False, remove=False): + doas_conf = "/etc/doas.conf" + doas = None + if not remove: + if not os.path.isfile(doas_conf): + # always allow root to become root + doas = "permit keepenv nopass root\n" + fileutil.append_file(doas_conf, doas) + if nopasswd: + doas = "permit keepenv nopass {0}\n".format(username) + else: + doas = "permit keepenv persist {0}\n".format(username) + fileutil.append_file(doas_conf, doas) + fileutil.chmod(doas_conf, 0o644) + else: + # Remove user from doas.conf + if os.path.isfile(doas_conf): + try: + content = fileutil.read_file(doas_conf) + doas = content.split("\n") + doas = [x for x in doas if username not in x] + fileutil.write_file(doas_conf, "\n".join(doas)) + except IOError as err: + raise OSUtilError("Failed to remove sudoer: " + "{0}".format(err)) + + def chpasswd(self, username, password, crypt_id=6, salt_len=10): + if self.is_sys_user(username): + raise OSUtilError(("User {0} is a system user. " + "Will not set passwd.").format(username)) + cmd = "echo -n {0}|encrypt".format(password) + ret, output = shellutil.run_get_output(cmd, log_cmd=False) + if ret != 0: + raise OSUtilError(("Failed to encrypt password for {0}: {1}" + "").format(username, output)) + passwd_hash = output.strip() + cmd = "usermod -p '{0}' {1}".format(passwd_hash, username) + ret, output = shellutil.run_get_output(cmd, log_cmd=False) + if ret != 0: + raise OSUtilError(("Failed to set password for {0}: {1}" + "").format(username, output)) + + def del_root_password(self): + ret, output = shellutil.run_get_output('usermod -p "*" root') + if ret: + raise OSUtilError("Failed to delete root password: " + "{0}".format(output)) + + def get_if_mac(self, ifname): + data = self._get_net_info() + if data[0] == ifname: + return data[2].replace(':', '').upper() + return None + + def get_first_if(self): + return self._get_net_info()[:2] + + def route_add(self, net, mask, gateway): + cmd = 'route add {0} {1} {2}'.format(net, gateway, mask) + return shellutil.run(cmd, chk_err=False) + + def is_missing_default_route(self): + ret = shellutil.run("route -n get default", chk_err=False) + if ret == 0: + return False + return True + + def is_dhcp_enabled(self): + pass + + def start_dhcp_service(self): + pass + + def stop_dhcp_service(self): + pass + + def get_dhcp_lease_endpoint(self): + """ + OpenBSD has a sligthly different lease file format. + """ + endpoint = None + pathglob = '/var/db/dhclient.leases.{}'.format(self.get_first_if()[0]) + + HEADER_LEASE = "lease" + HEADER_OPTION = "option option-245" + HEADER_EXPIRE = "expire" + FOOTER_LEASE = "}" + FORMAT_DATETIME = "%Y/%m/%d %H:%M:%S %Z" + + logger.info("looking for leases in path [{0}]".format(pathglob)) + for lease_file in glob.glob(pathglob): + leases = open(lease_file).read() + if HEADER_OPTION in leases: + cached_endpoint = None + has_option_245 = False + expired = True # assume expired + for line in leases.splitlines(): + if line.startswith(HEADER_LEASE): + cached_endpoint = None + has_option_245 = False + expired = True + elif HEADER_OPTION in line: + try: + ipaddr = line.split(" ")[-1].strip(";").split(":") + cached_endpoint = \ + ".".join(str(int(d, 16)) for d in ipaddr) + has_option_245 = True + except ValueError: + logger.error("could not parse '{0}'".format(line)) + elif HEADER_EXPIRE in line: + if "never" in line: + expired = False + else: + try: + expire_string = line.split( + " ", 4)[-1].strip(";") + expire_date = datetime.datetime.strptime( + expire_string, FORMAT_DATETIME) + if expire_date > datetime.datetime.utcnow(): + expired = False + except ValueError: + logger.error("could not parse expiry token " + "'{0}'".format(line)) + elif FOOTER_LEASE in line: + logger.info("dhcp entry:{0}, 245:{1}, expired: {2}" + .format(cached_endpoint, has_option_245, expired)) + if not expired and cached_endpoint is not None and has_option_245: + endpoint = cached_endpoint + logger.info("found endpoint [{0}]".format(endpoint)) + # we want to return the last valid entry, so + # keep searching + if endpoint is not None: + logger.info("cached endpoint found [{0}]".format(endpoint)) + else: + logger.info("cached endpoint not found") + return endpoint + + def allow_dhcp_broadcast(self): + pass + + def set_route_for_dhcp_broadcast(self, ifname): + return shellutil.run("route add 255.255.255.255 -iface " + "{0}".format(ifname), chk_err=False) + + def remove_route_for_dhcp_broadcast(self, ifname): + shellutil.run("route delete 255.255.255.255 -iface " + "{0}".format(ifname), chk_err=False) + + def get_dhcp_pid(self): + ret, output = shellutil.run_get_output("pgrep -n dhclient", + chk_err=False) + return output if ret == 0 else None + + def get_dvd_device(self, dev_dir='/dev'): + pattern = r'cd[0-9]c' + for dvd in [re.match(pattern, dev) for dev in os.listdir(dev_dir)]: + if dvd is not None: + return "/dev/{0}".format(dvd.group(0)) + raise OSUtilError("Failed to get DVD device") + + def mount_dvd(self, + max_retry=6, + chk_err=True, + dvd_device=None, + mount_point=None, + sleep_time=5): + if dvd_device is None: + dvd_device = self.get_dvd_device() + if mount_point is None: + mount_point = conf.get_dvd_mount_point() + if not os.path.isdir(mount_point): + os.makedirs(mount_point) + + for retry in range(0, max_retry): + retcode = self.mount(dvd_device, mount_point, option="-o ro -t udf", + chk_err=chk_err) + if retcode == 0: + logger.info("Successfully mounted DVD") + return + if retry < max_retry - 1: + mountlist = shellutil.run_get_output("/sbin/mount")[1] + existing = self.get_mount_point(mountlist, dvd_device) + if existing is not None: + logger.info("{0} is mounted at {1}", dvd_device, existing) + return + logger.warn("Mount DVD failed: retry={0}, ret={1}", retry, + retcode) + time.sleep(sleep_time) + if chk_err: + raise OSUtilError("Failed to mount DVD.") + + def eject_dvd(self, chk_err=True): + dvd = self.get_dvd_device() + retcode = shellutil.run("cdio eject {0}".format(dvd)) + if chk_err and retcode != 0: + raise OSUtilError("Failed to eject DVD: ret={0}".format(retcode)) + + def restart_if(self, ifname, retries=3, wait=5): + # Restart dhclient only to publish hostname + shellutil.run("/sbin/dhclient {0}".format(ifname), chk_err=False) + + def get_total_mem(self): + ret, output = shellutil.run_get_output("sysctl -n hw.physmem") + if ret: + raise OSUtilError("Failed to get total memory: {0}".format(output)) + try: + return int(output)/1024/1024 + except ValueError: + raise OSUtilError("Failed to get total memory: {0}".format(output)) + + def get_processor_cores(self): + ret, output = shellutil.run_get_output("sysctl -n hw.ncpu") + if ret: + raise OSUtilError("Failed to get processor cores.") + + try: + return int(output) + except ValueError: + raise OSUtilError("Failed to get total memory: {0}".format(output)) + + def set_scsi_disks_timeout(self, timeout): + pass + + def check_pid_alive(self, pid): + if not pid: + return + return shellutil.run('ps -p {0}'.format(pid), chk_err=False) == 0 + + @staticmethod + def _get_net_info(): + """ + There is no SIOCGIFCONF + on OpenBSD - just parse ifconfig. + Returns strings: iface, inet4_addr, and mac + or 'None,None,None' if unable to parse. + We will sleep and retry as the network must be up. + """ + iface = '' + inet = '' + mac = '' + + ret, output = shellutil.run_get_output( + 'ifconfig hvn | grep -E "^hvn.:" | sed "s/:.*//g"', chk_err=False) + if ret: + raise OSUtilError("Can't find ether interface:{0}".format(output)) + ifaces = output.split() + if not ifaces: + raise OSUtilError("Can't find ether interface.") + iface = ifaces[0] + + ret, output = shellutil.run_get_output( + 'ifconfig ' + iface, chk_err=False) + if ret: + raise OSUtilError("Can't get info for interface:{0}".format(iface)) + + for line in output.split('\n'): + if line.find('inet ') != -1: + inet = line.split()[1] + elif line.find('lladdr ') != -1: + mac = line.split()[1] + logger.verbose("Interface info: ({0},{1},{2})", iface, inet, mac) + + return iface, inet, mac + + def device_for_ide_port(self, port_id): + """ + Return device name attached to ide port 'n'. + """ + return "wd{0}".format(port_id) diff --git a/azurelinuxagent/common/protocol/hostplugin.py b/azurelinuxagent/common/protocol/hostplugin.py index 464fd35..9af8a97 100644 --- a/azurelinuxagent/common/protocol/hostplugin.py +++ b/azurelinuxagent/common/protocol/hostplugin.py @@ -70,7 +70,7 @@ class HostPluginProtocol(object): if not self.is_initialized: self.api_versions = self.get_api_versions() self.is_available = API_VERSION in self.api_versions - self.is_initialized = True + self.is_initialized = self.is_available from azurelinuxagent.common.event import WALAEventOperation, report_event report_event(WALAEventOperation.InitializeHostPlugin, is_success=self.is_available) @@ -143,7 +143,9 @@ class HostPluginProtocol(object): headers = {"x-ms-vmagentlog-deploymentid": self.deployment_id, "x-ms-vmagentlog-containerid": self.container_id} - logger.info("HostGAPlugin: Put VM log to [{0}]".format(url)) + logger.periodic( + logger.EVERY_FIFTEEN_MINUTES, + "HostGAPlugin: Put VM log to [{0}]".format(url)) try: response = restutil.http_put(url, content, headers) if response.status != httpclient.OK: @@ -175,7 +177,7 @@ class HostPluginProtocol(object): self._put_page_blob_status(sas_url, status_blob) if not HostPluginProtocol.is_default_channel(): - logger.info("HostGAPlugin: Setting host plugin as default channel") + logger.verbose("HostGAPlugin: Setting host plugin as default channel") HostPluginProtocol.set_default_channel(True) except Exception as e: message = "HostGAPlugin: Exception Put VM status: {0}, {1}".format(e, traceback.format_exc()) @@ -288,12 +290,23 @@ class HostPluginProtocol(object): @staticmethod def read_response_error(response): - if response is None: - return '' - body = remove_bom(response.read()) - if PY_VERSION_MAJOR < 3 and body is not None: - body = ustr(body, encoding='utf-8') - return "{0}, {1}, {2}".format( - response.status, - response.reason, - body) + result = '' + if response is not None: + try: + body = remove_bom(response.read()) + result = "[{0}: {1}] {2}".format(response.status, + response.reason, + body) + + # this result string is passed upstream to several methods + # which do a raise HttpError() or a format() of some kind; + # as a result it cannot have any unicode characters + if PY_VERSION_MAJOR < 3: + result = ustr(result, encoding='ascii', errors='ignore') + else: + result = result\ + .encode(encoding='ascii', errors='ignore')\ + .decode(encoding='ascii', errors='ignore') + except Exception: + logger.warn(traceback.format_exc()) + return result diff --git a/azurelinuxagent/common/protocol/metadata.py b/azurelinuxagent/common/protocol/metadata.py index c50b3dd..b0b6f67 100644 --- a/azurelinuxagent/common/protocol/metadata.py +++ b/azurelinuxagent/common/protocol/metadata.py @@ -113,7 +113,7 @@ class MetadataProtocol(Protocol): except HttpError as e: raise ProtocolError(ustr(e)) if resp.status != httpclient.CREATED: - raise ProtocolError("{0} - POST: {1}".format(resp.status, url)) + logger.warn("{0} for POST {1}".format(resp.status, url)) def _get_trans_cert(self): trans_crt_file = os.path.join(conf.get_lib_dir(), @@ -236,14 +236,14 @@ class MetadataProtocol(Protocol): return ext_list, etag def get_ext_handler_pkgs(self, ext_handler): - logger.info("Get extension handler packages") + logger.verbose("Get extension handler packages") pkg_list = ExtHandlerPackageList() manifest = None for version_uri in ext_handler.versionUris: try: manifest, etag = self._get_data(version_uri.uri) - logger.info("Successfully downloaded manifest") + logger.verbose("Successfully downloaded manifest") break except ProtocolError as e: logger.warn("Failed to fetch manifest: {0}", e) diff --git a/azurelinuxagent/common/protocol/util.py b/azurelinuxagent/common/protocol/util.py index 0ba03ec..bb3500a 100644 --- a/azurelinuxagent/common/protocol/util.py +++ b/azurelinuxagent/common/protocol/util.py @@ -162,12 +162,7 @@ class ProtocolUtil(object): def _detect_metadata_protocol(self): protocol = MetadataProtocol() protocol.detect() - - # only allow root access METADATA_ENDPOINT - self.osutil.set_admin_access_to_ip(METADATA_ENDPOINT) - self.save_protocol("MetadataProtocol") - return protocol def _detect_protocol(self, protocols): diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py index 936be8c..d731e11 100644 --- a/azurelinuxagent/common/protocol/wire.py +++ b/azurelinuxagent/common/protocol/wire.py @@ -597,9 +597,9 @@ class WireClient(object): Call storage service, handle SERVICE_UNAVAILABLE(503) """ - # force the chk_proxy arg to True, since all calls to storage should - # use a configured proxy - kwargs['chk_proxy'] = True + # Default to use the configured HTTP proxy + if not 'chk_proxy' in kwargs or kwargs['chk_proxy'] is None: + kwargs['chk_proxy'] = True for retry in range(0, 3): resp = http_req(*args, **kwargs) @@ -626,7 +626,7 @@ class WireClient(object): logger.verbose("Manifest could not be downloaded, falling back to host plugin") host = self.get_host_plugin() uri, headers = host.get_artifact_request(version.uri) - response = self.fetch(uri, headers) + response = self.fetch(uri, headers, chk_proxy=False) if not response: host = self.get_host_plugin(force_update=True) logger.info("Retry fetch in {0} seconds", @@ -642,14 +642,15 @@ class WireClient(object): return response raise ProtocolError("Failed to fetch manifest from all sources") - def fetch(self, uri, headers=None): + def fetch(self, uri, headers=None, chk_proxy=None): logger.verbose("Fetch [{0}] with headers [{1}]", uri, headers) return_value = None try: resp = self.call_storage_service( restutil.http_get, uri, - headers) + headers, + chk_proxy=chk_proxy) if resp.status == httpclient.OK: return_value = self.decode_config(resp.read()) else: @@ -831,7 +832,7 @@ class WireClient(object): if not blob_type in ["BlockBlob", "PageBlob"]: blob_type = "BlockBlob" - logger.info("Status Blob type is unspecified " + logger.verbose("Status Blob type is unspecified " "-- assuming it is a BlockBlob") try: @@ -998,17 +999,17 @@ class WireClient(object): artifacts_profile = None if self.has_artifacts_profile_blob(): blob = self.ext_conf.artifacts_profile_blob - logger.info("Getting the artifacts profile") + logger.verbose("Getting the artifacts profile") profile = self.fetch(blob) if profile is None: logger.warn("Download failed, falling back to host plugin") host = self.get_host_plugin() uri, headers = host.get_artifact_request(blob) - profile = self.decode_config(self.fetch(uri, headers)) + profile = self.decode_config(self.fetch(uri, headers, chk_proxy=False)) if not textutil.is_str_none_or_whitespace(profile): - logger.info("Artifacts profile downloaded successfully") + logger.verbose("Artifacts profile downloaded successfully") artifacts_profile = InVMArtifactsProfile(profile) return artifacts_profile diff --git a/azurelinuxagent/common/version.py b/azurelinuxagent/common/version.py index dc3592b..d1d4c62 100644 --- a/azurelinuxagent/common/version.py +++ b/azurelinuxagent/common/version.py @@ -77,6 +77,9 @@ def get_distro(): if 'FreeBSD' in platform.system(): release = re.sub('\-.*\Z', '', ustr(platform.release())) osinfo = ['freebsd', release, '', 'freebsd'] + elif 'OpenBSD' in platform.system(): + release = re.sub('\-.*\Z', '', ustr(platform.release())) + osinfo = ['openbsd', release, '', 'openbsd'] elif 'linux_distribution' in dir(platform): supported = platform._supported_dists + ('alpine',) osinfo = list(platform.linux_distribution(full_distribution_name=0, @@ -110,7 +113,7 @@ def get_distro(): AGENT_NAME = "WALinuxAgent" AGENT_LONG_NAME = "Azure Linux Agent" -AGENT_VERSION = '2.2.12' +AGENT_VERSION = '2.2.14' AGENT_LONG_VERSION = "{0}-{1}".format(AGENT_NAME, AGENT_VERSION) AGENT_DESCRIPTION = """ The Azure Linux Agent supports the provisioning and running of Linux @@ -160,7 +163,10 @@ CURRENT_AGENT, CURRENT_VERSION = set_current_agent() def set_goal_state_agent(): agent = None - pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] + if os.path.isdir("/proc"): + pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] + else: + pids = [] for pid in pids: try: pname = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read() |