summaryrefslogtreecommitdiff
path: root/azurelinuxagent/common
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/common')
-rw-r--r--azurelinuxagent/common/event.py70
-rw-r--r--azurelinuxagent/common/future.py4
-rw-r--r--azurelinuxagent/common/logger.py35
-rw-r--r--azurelinuxagent/common/osutil/bigip.py51
-rw-r--r--azurelinuxagent/common/osutil/default.py13
-rw-r--r--azurelinuxagent/common/osutil/factory.py4
-rw-r--r--azurelinuxagent/common/osutil/freebsd.py12
-rw-r--r--azurelinuxagent/common/osutil/gaia.py69
-rw-r--r--azurelinuxagent/common/osutil/openbsd.py345
-rw-r--r--azurelinuxagent/common/protocol/hostplugin.py37
-rw-r--r--azurelinuxagent/common/protocol/metadata.py6
-rw-r--r--azurelinuxagent/common/protocol/util.py5
-rw-r--r--azurelinuxagent/common/protocol/wire.py21
-rw-r--r--azurelinuxagent/common/version.py10
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()