summaryrefslogtreecommitdiff
path: root/azurelinuxagent/common/osutil
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/common/osutil')
-rw-r--r--azurelinuxagent/common/osutil/__init__.py18
-rw-r--r--azurelinuxagent/common/osutil/coreos.py92
-rw-r--r--azurelinuxagent/common/osutil/debian.py47
-rw-r--r--azurelinuxagent/common/osutil/default.py792
-rw-r--r--azurelinuxagent/common/osutil/factory.py69
-rw-r--r--azurelinuxagent/common/osutil/freebsd.py198
-rw-r--r--azurelinuxagent/common/osutil/redhat.py122
-rw-r--r--azurelinuxagent/common/osutil/suse.py108
-rw-r--r--azurelinuxagent/common/osutil/ubuntu.py66
9 files changed, 1512 insertions, 0 deletions
diff --git a/azurelinuxagent/common/osutil/__init__.py b/azurelinuxagent/common/osutil/__init__.py
new file mode 100644
index 0000000..3b5ba3b
--- /dev/null
+++ b/azurelinuxagent/common/osutil/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2014 Microsoft Corporation
+#
+# 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+
+#
+
+from azurelinuxagent.common.osutil.factory import get_osutil
diff --git a/azurelinuxagent/common/osutil/coreos.py b/azurelinuxagent/common/osutil/coreos.py
new file mode 100644
index 0000000..e26fd97
--- /dev/null
+++ b/azurelinuxagent/common/osutil/coreos.py
@@ -0,0 +1,92 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 pwd
+import shutil
+import socket
+import array
+import struct
+import fcntl
+import time
+import base64
+import azurelinuxagent.common.logger as logger
+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 CoreOSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(CoreOSUtil, self).__init__()
+ self.agent_conf_file_path = '/usr/share/oem/waagent.conf'
+ self.waagent_path='/usr/share/oem/bin/waagent'
+ self.python_path='/usr/share/oem/python/bin'
+ if 'PATH' in os.environ:
+ path = "{0}:{1}".format(os.environ['PATH'], self.python_path)
+ else:
+ path = self.python_path
+ os.environ['PATH'] = path
+
+ if 'PYTHONPATH' in os.environ:
+ py_path = os.environ['PYTHONPATH']
+ py_path = "{0}:{1}".format(py_path, self.waagent_path)
+ else:
+ py_path = self.waagent_path
+ os.environ['PYTHONPATH'] = py_path
+
+ def is_sys_user(self, username):
+ #User 'core' is not a sysuser
+ if username == 'core':
+ return False
+ return super(CoreOSUtil, self).is_sys_user(username)
+
+ def is_dhcp_enabled(self):
+ return True
+
+ def start_network(self) :
+ return shellutil.run("systemctl start systemd-networkd", chk_err=False)
+
+ def restart_if(self, iface):
+ shellutil.run("systemctl restart systemd-networkd")
+
+ def restart_ssh_service(self):
+ # SSH is socket activated on CoreOS. No need to restart it.
+ pass
+
+ def stop_dhcp_service(self):
+ return shellutil.run("systemctl stop systemd-networkd", chk_err=False)
+
+ def start_dhcp_service(self):
+ return shellutil.run("systemctl start systemd-networkd", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("systemctl start wagent", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("systemctl stop wagent", chk_err=False)
+
+ def get_dhcp_pid(self):
+ ret= shellutil.run_get_output("pidof systemd-networkd")
+ return ret[1] if ret[0] == 0 else None
+
+ def conf_sshd(self, disable_password):
+ #In CoreOS, /etc/sshd_config is mount readonly. Skip the setting
+ pass
+
diff --git a/azurelinuxagent/common/osutil/debian.py b/azurelinuxagent/common/osutil/debian.py
new file mode 100644
index 0000000..f455572
--- /dev/null
+++ b/azurelinuxagent/common/osutil/debian.py
@@ -0,0 +1,47 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 pwd
+import shutil
+import socket
+import array
+import struct
+import fcntl
+import time
+import base64
+import azurelinuxagent.common.logger as logger
+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 DebianOSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(DebianOSUtil, self).__init__()
+
+ def restart_ssh_service(self):
+ return shellutil.run("service sshd restart", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("service azurelinuxagent stop", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("service azurelinuxagent start", chk_err=False)
+
diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py
new file mode 100644
index 0000000..c243c85
--- /dev/null
+++ b/azurelinuxagent/common/osutil/default.py
@@ -0,0 +1,792 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 shutil
+import socket
+import array
+import struct
+import time
+import pwd
+import fcntl
+import base64
+import glob
+import datetime
+import azurelinuxagent.common.logger as logger
+import azurelinuxagent.common.conf as conf
+from azurelinuxagent.common.exception import OSUtilError
+from azurelinuxagent.common.future import ustr
+import azurelinuxagent.common.utils.fileutil as fileutil
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.textutil as textutil
+from azurelinuxagent.common.utils.cryptutil import CryptUtil
+
+__RULES_FILES__ = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
+ "/etc/udev/rules.d/70-persistent-net.rules" ]
+
+"""
+Define distro specific behavior. OSUtil class defines default behavior
+for all distros. Each concrete distro classes could overwrite default behavior
+if needed.
+"""
+
+class DefaultOSUtil(object):
+
+ def __init__(self):
+ self.agent_conf_file_path = '/etc/waagent.conf'
+ self.selinux=None
+
+ def get_agent_conf_file_path(self):
+ return self.agent_conf_file_path
+
+ def get_userentry(self, username):
+ try:
+ return pwd.getpwnam(username)
+ except KeyError:
+ return None
+
+ def is_sys_user(self, username):
+ """
+ Check whether use is a system user.
+ If reset sys user is allowed in conf, return False
+ Otherwise, check whether UID is less than UID_MIN
+ """
+ if conf.get_allow_reset_sys_user():
+ return False
+
+ userentry = self.get_userentry(username)
+ uidmin = None
+ try:
+ uidmin_def = fileutil.get_line_startingwith("UID_MIN",
+ "/etc/login.defs")
+ if uidmin_def is not None:
+ uidmin = int(uidmin_def.split()[1])
+ except IOError as e:
+ pass
+ if uidmin == None:
+ uidmin = 100
+ if userentry != None and userentry[2] < uidmin:
+ return True
+ else:
+ return False
+
+ def useradd(self, username, expiration=None):
+ """
+ Create user account with 'username'
+ """
+ userentry = self.get_userentry(username)
+ if userentry is not None:
+ logger.info("User {0} already exists, skip useradd", username)
+ return
+
+ if expiration is not None:
+ cmd = "useradd -m {0} -e {1}".format(username, expiration)
+ else:
+ cmd = "useradd -m {0}".format(username)
+ retcode, out = shellutil.run_get_output(cmd)
+ if retcode != 0:
+ raise OSUtilError(("Failed to create user account:{0}, "
+ "retcode:{1}, "
+ "output:{2}").format(username, retcode, out))
+
+ 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))
+ passwd_hash = textutil.gen_password_hash(password, crypt_id, salt_len)
+ 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 conf_sudoer(self, username, nopasswd=False, remove=False):
+ sudoers_dir = conf.get_sudoers_dir()
+ sudoers_wagent = os.path.join(sudoers_dir, 'waagent')
+
+ if not remove:
+ # for older distros create sudoers.d
+ if not os.path.isdir(sudoers_dir):
+ sudoers_file = os.path.join(sudoers_dir, '../sudoers')
+ # create the sudoers.d directory
+ os.mkdir(sudoers_dir)
+ # add the include of sudoers.d to the /etc/sudoers
+ sudoers = '\n#includedir ' + sudoers_dir + '\n'
+ fileutil.append_file(sudoers_file, sudoers)
+ sudoer = None
+ if nopasswd:
+ sudoer = "{0} ALL=(ALL) NOPASSWD: ALL\n".format(username)
+ else:
+ sudoer = "{0} ALL=(ALL) ALL\n".format(username)
+ fileutil.append_file(sudoers_wagent, sudoer)
+ fileutil.chmod(sudoers_wagent, 0o440)
+ else:
+ #Remove user from sudoers
+ if os.path.isfile(sudoers_wagent):
+ try:
+ content = fileutil.read_file(sudoers_wagent)
+ sudoers = content.split("\n")
+ sudoers = [x for x in sudoers if username not in x]
+ fileutil.write_file(sudoers_wagent, "\n".join(sudoers))
+ except IOError as e:
+ raise OSUtilError("Failed to remove sudoer: {0}".format(e))
+
+ def del_root_password(self):
+ try:
+ passwd_file_path = conf.get_passwd_file_path()
+ passwd_content = fileutil.read_file(passwd_file_path)
+ passwd = passwd_content.split('\n')
+ new_passwd = [x for x in passwd if not x.startswith("root:")]
+ new_passwd.insert(0, "root:*LOCK*:14600::::::")
+ fileutil.write_file(passwd_file_path, "\n".join(new_passwd))
+ except IOError as e:
+ raise OSUtilError("Failed to delete root password:{0}".format(e))
+
+ def _norm_path(self, filepath):
+ home = conf.get_home_dir()
+ # Expand HOME variable if present in path
+ path = os.path.normpath(filepath.replace("$HOME", home))
+ return path
+
+ def deploy_ssh_keypair(self, username, keypair):
+ """
+ Deploy id_rsa and id_rsa.pub
+ """
+ path, thumbprint = keypair
+ path = self._norm_path(path)
+ dir_path = os.path.dirname(path)
+ fileutil.mkdir(dir_path, mode=0o700, owner=username)
+ lib_dir = conf.get_lib_dir()
+ prv_path = os.path.join(lib_dir, thumbprint + '.prv')
+ if not os.path.isfile(prv_path):
+ raise OSUtilError("Can't find {0}.prv".format(thumbprint))
+ shutil.copyfile(prv_path, path)
+ pub_path = path + '.pub'
+ crytputil = CryptUtil(conf.get_openssl_cmd())
+ pub = crytputil.get_pubkey_from_prv(prv_path)
+ fileutil.write_file(pub_path, pub)
+ self.set_selinux_context(pub_path, 'unconfined_u:object_r:ssh_home_t:s0')
+ self.set_selinux_context(path, 'unconfined_u:object_r:ssh_home_t:s0')
+ os.chmod(path, 0o644)
+ os.chmod(pub_path, 0o600)
+
+ def openssl_to_openssh(self, input_file, output_file):
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ cryptutil.crt_to_ssh(input_file, output_file)
+
+ def deploy_ssh_pubkey(self, username, pubkey):
+ """
+ Deploy authorized_key
+ """
+ path, thumbprint, value = pubkey
+ if path is None:
+ raise OSUtilError("Public key path is None")
+
+ crytputil = CryptUtil(conf.get_openssl_cmd())
+
+ path = self._norm_path(path)
+ dir_path = os.path.dirname(path)
+ fileutil.mkdir(dir_path, mode=0o700, owner=username)
+ if value is not None:
+ if not value.startswith("ssh-"):
+ raise OSUtilError("Bad public key: {0}".format(value))
+ fileutil.write_file(path, value)
+ elif thumbprint is not None:
+ lib_dir = conf.get_lib_dir()
+ crt_path = os.path.join(lib_dir, thumbprint + '.crt')
+ if not os.path.isfile(crt_path):
+ raise OSUtilError("Can't find {0}.crt".format(thumbprint))
+ pub_path = os.path.join(lib_dir, thumbprint + '.pub')
+ pub = crytputil.get_pubkey_from_crt(crt_path)
+ fileutil.write_file(pub_path, pub)
+ self.set_selinux_context(pub_path,
+ 'unconfined_u:object_r:ssh_home_t:s0')
+ self.openssl_to_openssh(pub_path, path)
+ fileutil.chmod(pub_path, 0o600)
+ else:
+ raise OSUtilError("SSH public key Fingerprint and Value are None")
+
+ self.set_selinux_context(path, 'unconfined_u:object_r:ssh_home_t:s0')
+ fileutil.chowner(path, username)
+ fileutil.chmod(path, 0o644)
+
+ def is_selinux_system(self):
+ """
+ Checks and sets self.selinux = True if SELinux is available on system.
+ """
+ if self.selinux == None:
+ if shellutil.run("which getenforce", chk_err=False) == 0:
+ self.selinux = True
+ else:
+ self.selinux = False
+ return self.selinux
+
+ def is_selinux_enforcing(self):
+ """
+ Calls shell command 'getenforce' and returns True if 'Enforcing'.
+ """
+ if self.is_selinux_system():
+ output = shellutil.run_get_output("getenforce")[1]
+ return output.startswith("Enforcing")
+ else:
+ return False
+
+ def set_selinux_enforce(self, state):
+ """
+ Calls shell command 'setenforce' with 'state'
+ and returns resulting exit code.
+ """
+ if self.is_selinux_system():
+ if state: s = '1'
+ else: s='0'
+ return shellutil.run("setenforce "+s)
+
+ def set_selinux_context(self, path, con):
+ """
+ Calls shell 'chcon' with 'path' and 'con' context.
+ Returns exit result.
+ """
+ if self.is_selinux_system():
+ if not os.path.exists(path):
+ logger.error("Path does not exist: {0}".format(path))
+ return 1
+ return shellutil.run('chcon ' + con + ' ' + path)
+
+ def conf_sshd(self, disable_password):
+ option = "no" if disable_password else "yes"
+ conf_file_path = conf.get_sshd_conf_file_path()
+ conf_file = fileutil.read_file(conf_file_path).split("\n")
+ textutil.set_ssh_config(conf_file, "PasswordAuthentication", option)
+ textutil.set_ssh_config(conf_file, "ChallengeResponseAuthentication", option)
+ textutil.set_ssh_config(conf_file, "ClientAliveInterval", "180")
+ fileutil.write_file(conf_file_path, "\n".join(conf_file))
+ logger.info("{0} SSH password-based authentication methods."
+ .format("Disabled" if disable_password else "Enabled"))
+ logger.info("Configured SSH client probing to keep connections alive.")
+
+
+ def get_dvd_device(self, dev_dir='/dev'):
+ pattern=r'(sr[0-9]|hd[c-z]|cdrom[0-9]|cd[0-9])'
+ 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):
+ if dvd_device is None:
+ dvd_device = self.get_dvd_device()
+ if mount_point is None:
+ mount_point = conf.get_dvd_mount_point()
+ mountlist = shellutil.run_get_output("mount")[1]
+ existing = self.get_mount_point(mountlist, dvd_device)
+ if existing is not None: #Already mounted
+ logger.info("{0} is already mounted at {1}", dvd_device, existing)
+ return
+ 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,iso9660",
+ chk_err=chk_err)
+ if retcode == 0:
+ logger.info("Successfully mounted dvd")
+ return
+ if retry < max_retry - 1:
+ logger.warn("Mount dvd failed: retry={0}, ret={1}", retry,
+ retcode)
+ time.sleep(5)
+ if chk_err:
+ raise OSUtilError("Failed to mount dvd.")
+
+ def umount_dvd(self, chk_err=True, mount_point=None):
+ if mount_point is None:
+ mount_point = conf.get_dvd_mount_point()
+ retcode = self.umount(mount_point, chk_err=chk_err)
+ if chk_err and retcode != 0:
+ raise OSUtilError("Failed to umount dvd.")
+
+ def eject_dvd(self, chk_err=True):
+ dvd = self.get_dvd_device()
+ retcode = shellutil.run("eject {0}".format(dvd))
+ if chk_err and retcode != 0:
+ raise OSUtilError("Failed to eject dvd: ret={0}".format(retcode))
+
+ def try_load_atapiix_mod(self):
+ try:
+ self.load_atapiix_mod()
+ except Exception as e:
+ logger.warn("Could not load ATAPI driver: {0}".format(e))
+
+ def load_atapiix_mod(self):
+ if self.is_atapiix_mod_loaded():
+ return
+ ret, kern_version = shellutil.run_get_output("uname -r")
+ if ret != 0:
+ raise Exception("Failed to call uname -r")
+ mod_path = os.path.join('/lib/modules',
+ kern_version.strip('\n'),
+ 'kernel/drivers/ata/ata_piix.ko')
+ if not os.path.isfile(mod_path):
+ raise Exception("Can't find module file:{0}".format(mod_path))
+
+ ret, output = shellutil.run_get_output("insmod " + mod_path)
+ if ret != 0:
+ raise Exception("Error calling insmod for ATAPI CD-ROM driver")
+ if not self.is_atapiix_mod_loaded(max_retry=3):
+ raise Exception("Failed to load ATAPI CD-ROM driver")
+
+ def is_atapiix_mod_loaded(self, max_retry=1):
+ for retry in range(0, max_retry):
+ ret = shellutil.run("lsmod | grep ata_piix", chk_err=False)
+ if ret == 0:
+ logger.info("Module driver for ATAPI CD-ROM is already present.")
+ return True
+ if retry < max_retry - 1:
+ time.sleep(1)
+ return False
+
+ def mount(self, dvd, mount_point, option="", chk_err=True):
+ cmd = "mount {0} {1} {2}".format(option, dvd, mount_point)
+ return shellutil.run_get_output(cmd, chk_err)[0]
+
+ def umount(self, mount_point, chk_err=True):
+ return shellutil.run("umount {0}".format(mount_point), chk_err=chk_err)
+
+ def allow_dhcp_broadcast(self):
+ #Open DHCP port if iptables is enabled.
+ # We supress error logging on error.
+ shellutil.run("iptables -D INPUT -p udp --dport 68 -j ACCEPT",
+ chk_err=False)
+ shellutil.run("iptables -I INPUT -p udp --dport 68 -j ACCEPT",
+ chk_err=False)
+
+
+ def remove_rules_files(self, rules_files=__RULES_FILES__):
+ lib_dir = conf.get_lib_dir()
+ for src in rules_files:
+ file_name = fileutil.base_name(src)
+ dest = os.path.join(lib_dir, file_name)
+ if os.path.isfile(dest):
+ os.remove(dest)
+ if os.path.isfile(src):
+ logger.warn("Move rules file {0} to {1}", file_name, dest)
+ shutil.move(src, dest)
+
+ def restore_rules_files(self, rules_files=__RULES_FILES__):
+ lib_dir = conf.get_lib_dir()
+ for dest in rules_files:
+ filename = fileutil.base_name(dest)
+ src = os.path.join(lib_dir, filename)
+ if os.path.isfile(dest):
+ continue
+ if os.path.isfile(src):
+ logger.warn("Move rules file {0} to {1}", filename, dest)
+ shutil.move(src, dest)
+
+ def get_mac_addr(self):
+ """
+ Convienience function, returns mac addr bound to
+ first non-loopback interface.
+ """
+ ifname=''
+ while len(ifname) < 2 :
+ ifname=self.get_first_if()[0]
+ addr = self.get_if_mac(ifname)
+ return textutil.hexstr_to_bytearray(addr)
+
+ def get_if_mac(self, ifname):
+ """
+ Return the mac-address bound to the socket.
+ """
+ sock = socket.socket(socket.AF_INET,
+ socket.SOCK_DGRAM,
+ socket.IPPROTO_UDP)
+ param = struct.pack('256s', (ifname[:15]+('\0'*241)).encode('latin-1'))
+ info = fcntl.ioctl(sock.fileno(), 0x8927, param)
+ return ''.join(['%02X' % textutil.str_to_ord(char) for char in info[18:24]])
+
+ def get_first_if(self):
+ """
+ Return the interface name, and ip addr of the
+ first active non-loopback interface.
+ """
+ iface=''
+ expected=16 # how many devices should I expect...
+ struct_size=40 # for 64bit the size is 40 bytes
+ sock = socket.socket(socket.AF_INET,
+ socket.SOCK_DGRAM,
+ socket.IPPROTO_UDP)
+ buff=array.array('B', b'\0' * (expected * struct_size))
+ param = struct.pack('iL',
+ expected*struct_size,
+ buff.buffer_info()[0])
+ ret = fcntl.ioctl(sock.fileno(), 0x8912, param)
+ retsize=(struct.unpack('iL', ret)[0])
+ if retsize == (expected * struct_size):
+ logger.warn(('SIOCGIFCONF returned more than {0} up '
+ 'network interfaces.'), expected)
+ sock = buff.tostring()
+ primary = bytearray(self.get_primary_interface(), encoding='utf-8')
+ for i in range(0, struct_size * expected, struct_size):
+ iface=sock[i:i+16].split(b'\0', 1)[0]
+ if len(iface) == 0 or self.is_loopback(iface) or iface != primary:
+ # test the next one
+ logger.info('interface [{0}] skipped'.format(iface))
+ continue
+ else:
+ # use this one
+ logger.info('interface [{0}] selected'.format(iface))
+ break
+
+ return iface.decode('latin-1'), socket.inet_ntoa(sock[i+20:i+24])
+
+ def get_primary_interface(self):
+ """
+ Get the name of the primary interface, which is the one with the
+ default route attached to it; if there are multiple default routes,
+ the primary has the lowest Metric.
+ :return: the interface which has the default route
+ """
+ # from linux/route.h
+ RTF_GATEWAY = 0x02
+ DEFAULT_DEST = "00000000"
+
+ hdr_iface = "Iface"
+ hdr_dest = "Destination"
+ hdr_flags = "Flags"
+ hdr_metric = "Metric"
+
+ idx_iface = -1
+ idx_dest = -1
+ idx_flags = -1
+ idx_metric = -1
+ primary = None
+ primary_metric = None
+
+ logger.info("examine /proc/net/route for primary interface")
+ with open('/proc/net/route') as routing_table:
+ idx = 0
+ for header in filter(lambda h: len(h) > 0, routing_table.readline().strip(" \n").split("\t")):
+ if header == hdr_iface:
+ idx_iface = idx
+ elif header == hdr_dest:
+ idx_dest = idx
+ elif header == hdr_flags:
+ idx_flags = idx
+ elif header == hdr_metric:
+ idx_metric = idx
+ idx = idx + 1
+ for entry in routing_table.readlines():
+ route = entry.strip(" \n").split("\t")
+ if route[idx_dest] == DEFAULT_DEST and int(route[idx_flags]) & RTF_GATEWAY == RTF_GATEWAY:
+ metric = int(route[idx_metric])
+ iface = route[idx_iface]
+ if primary is None or metric < primary_metric:
+ primary = iface
+ primary_metric = metric
+
+ if primary is None:
+ primary = ''
+
+ logger.info('primary interface is [{0}]'.format(primary))
+ return primary
+
+
+ def is_primary_interface(self, ifname):
+ """
+ Indicate whether the specified interface is the primary.
+ :param ifname: the name of the interface - eth0, lo, etc.
+ :return: True if this interface binds the default route
+ """
+ return self.get_primary_interface() == ifname
+
+
+ def is_loopback(self, ifname):
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ result = fcntl.ioctl(s.fileno(), 0x8913, struct.pack('256s', ifname[:15]))
+ flags, = struct.unpack('H', result[16:18])
+ isloopback = flags & 8 == 8
+ logger.info('interface [{0}] has flags [{1}], is loopback [{2}]'.format(ifname, flags, isloopback))
+ return isloopback
+
+ def get_dhcp_lease_endpoint(self):
+ """
+ OS specific, this should return the decoded endpoint of
+ the wireserver from option 245 in the dhcp leases file
+ if it exists on disk.
+ :return: The endpoint if available, or None
+ """
+ return None
+
+ @staticmethod
+ def get_endpoint_from_leases_path(pathglob):
+ """
+ Try to discover and decode the wireserver endpoint in the
+ specified dhcp leases path.
+ :param pathglob: The path containing dhcp lease files
+ :return: The endpoint if available, otherwise None
+ """
+ endpoint = None
+
+ HEADER_LEASE = "lease"
+ HEADER_OPTION = "option unknown-245"
+ HEADER_DNS = "option domain-name-servers"
+ HEADER_EXPIRE = "expire"
+ FOOTER_LEASE = "}"
+ FORMAT_DATETIME = "%Y/%m/%d %H:%M:%S"
+
+ 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_DNS in line:
+ cached_endpoint = line.replace(HEADER_DNS, '').strip(" ;")
+ elif HEADER_OPTION in line:
+ has_option_245 = True
+ 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:
+ 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 is_missing_default_route(self):
+ routes = shellutil.run_get_output("route -n")[1]
+ for route in routes.split("\n"):
+ if route.startswith("0.0.0.0 ") or route.startswith("default "):
+ return False
+ return True
+
+ def get_if_name(self):
+ return self.get_first_if()[0]
+
+ def get_ip4_addr(self):
+ return self.get_first_if()[1]
+
+ def set_route_for_dhcp_broadcast(self, ifname):
+ return shellutil.run("route add 255.255.255.255 dev {0}".format(ifname),
+ chk_err=False)
+
+ def remove_route_for_dhcp_broadcast(self, ifname):
+ shellutil.run("route del 255.255.255.255 dev {0}".format(ifname),
+ chk_err=False)
+
+ def is_dhcp_enabled(self):
+ return False
+
+ def stop_dhcp_service(self):
+ pass
+
+ def start_dhcp_service(self):
+ pass
+
+ def start_network(self):
+ pass
+
+ def start_agent_service(self):
+ pass
+
+ def stop_agent_service(self):
+ pass
+
+ def register_agent_service(self):
+ pass
+
+ def unregister_agent_service(self):
+ pass
+
+ def restart_ssh_service(self):
+ pass
+
+ def route_add(self, net, mask, gateway):
+ """
+ Add specified route using /sbin/route add -net.
+ """
+ cmd = ("/sbin/route add -net "
+ "{0} netmask {1} gw {2}").format(net, mask, gateway)
+ return shellutil.run(cmd, chk_err=False)
+
+ def get_dhcp_pid(self):
+ ret= shellutil.run_get_output("pidof dhclient")
+ return ret[1] if ret[0] == 0 else None
+
+ def set_hostname(self, hostname):
+ fileutil.write_file('/etc/hostname', hostname)
+ shellutil.run("hostname {0}".format(hostname), chk_err=False)
+
+ def set_dhcp_hostname(self, hostname):
+ autosend = r'^[^#]*?send\s*host-name.*?(<hostname>|gethostname[(,)])'
+ dhclient_files = ['/etc/dhcp/dhclient.conf', '/etc/dhcp3/dhclient.conf', '/etc/dhclient.conf']
+ for conf_file in dhclient_files:
+ if not os.path.isfile(conf_file):
+ continue
+ if fileutil.findstr_in_file(conf_file, autosend):
+ #Return if auto send host-name is configured
+ return
+ fileutil.update_conf_file(conf_file,
+ 'send host-name',
+ 'send host-name "{0}";'.format(hostname))
+
+ def restart_if(self, ifname, retries=3, wait=5):
+ retry_limit=retries+1
+ for attempt in range(1, retry_limit):
+ return_code=shellutil.run("ifdown {0} && ifup {0}".format(ifname))
+ if return_code == 0:
+ return
+ logger.warn("failed to restart {0}: return code {1}".format(ifname, return_code))
+ if attempt < retry_limit:
+ logger.info("retrying in {0} seconds".format(wait))
+ time.sleep(wait)
+ else:
+ logger.warn("exceeded restart retries")
+
+ def publish_hostname(self, hostname):
+ self.set_dhcp_hostname(hostname)
+ ifname = self.get_if_name()
+ self.restart_if(ifname)
+
+ def set_scsi_disks_timeout(self, timeout):
+ for dev in os.listdir("/sys/block"):
+ if dev.startswith('sd'):
+ self.set_block_device_timeout(dev, timeout)
+
+ def set_block_device_timeout(self, dev, timeout):
+ if dev is not None and timeout is not None:
+ file_path = "/sys/block/{0}/device/timeout".format(dev)
+ content = fileutil.read_file(file_path)
+ original = content.splitlines()[0].rstrip()
+ if original != timeout:
+ fileutil.write_file(file_path, timeout)
+ logger.info("Set block dev timeout: {0} with timeout: {1}",
+ dev, timeout)
+
+ def get_mount_point(self, mountlist, device):
+ """
+ Example of mountlist:
+ /dev/sda1 on / type ext4 (rw)
+ proc on /proc type proc (rw)
+ sysfs on /sys type sysfs (rw)
+ devpts on /dev/pts type devpts (rw,gid=5,mode=620)
+ tmpfs on /dev/shm type tmpfs
+ (rw,rootcontext="system_u:object_r:tmpfs_t:s0")
+ none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
+ /dev/sdb1 on /mnt/resource type ext4 (rw)
+ """
+ if (mountlist and device):
+ for entry in mountlist.split('\n'):
+ if(re.search(device, entry)):
+ tokens = entry.split()
+ #Return the 3rd column of this line
+ return tokens[2] if len(tokens) > 2 else None
+ return None
+
+ def device_for_ide_port(self, port_id):
+ """
+ Return device name attached to ide port 'n'.
+ """
+ if port_id > 3:
+ return None
+ g0 = "00000000"
+ if port_id > 1:
+ g0 = "00000001"
+ port_id = port_id - 2
+ device = None
+ path = "/sys/bus/vmbus/devices/"
+ for vmbus in os.listdir(path):
+ deviceid = fileutil.read_file(os.path.join(path, vmbus, "device_id"))
+ guid = deviceid.lstrip('{').split('-')
+ if guid[0] == g0 and guid[1] == "000" + ustr(port_id):
+ for root, dirs, files in os.walk(path + vmbus):
+ if root.endswith("/block"):
+ device = dirs[0]
+ break
+ else : #older distros
+ for d in dirs:
+ if ':' in d and "block" == d.split(':')[0]:
+ device = d.split(':')[1]
+ break
+ break
+ return device
+
+ 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 -f -r " + username)
+ self.conf_sudoer(username, remove=True)
+
+ def decode_customdata(self, data):
+ return base64.b64decode(data)
+
+ def get_total_mem(self):
+ cmd = "grep MemTotal /proc/meminfo |awk '{print $2}'"
+ ret = shellutil.run_get_output(cmd)
+ if ret[0] == 0:
+ return int(ret[1])/1024
+ else:
+ raise OSUtilError("Failed to get total memory: {0}".format(ret[1]))
+
+ def get_processor_cores(self):
+ ret = shellutil.run_get_output("grep 'processor.*:' /proc/cpuinfo |wc -l")
+ if ret[0] == 0:
+ return int(ret[1])
+ else:
+ raise OSUtilError("Failed to get processor cores")
+
+ 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
new file mode 100644
index 0000000..5e8ae6e
--- /dev/null
+++ b/azurelinuxagent/common/osutil/factory.py
@@ -0,0 +1,69 @@
+# Copyright 2014 Microsoft Corporation
+#
+# 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 azurelinuxagent.common.logger as logger
+from azurelinuxagent.common.utils.textutil import Version
+from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \
+ DISTRO_FULL_NAME
+
+from .default import DefaultOSUtil
+from .coreos import CoreOSUtil
+from .debian import DebianOSUtil
+from .freebsd import FreeBSDOSUtil
+from .redhat import RedhatOSUtil, Redhat6xOSUtil
+from .suse import SUSEOSUtil, SUSE11OSUtil
+from .ubuntu import UbuntuOSUtil, Ubuntu12OSUtil, Ubuntu14OSUtil, \
+ UbuntuSnappyOSUtil
+
+def get_osutil(distro_name=DISTRO_NAME, distro_version=DISTRO_VERSION,
+ distro_full_name=DISTRO_FULL_NAME):
+ if distro_name == "ubuntu":
+ if Version(distro_version) == Version("12.04") or \
+ Version(distro_version) == Version("12.10"):
+ return Ubuntu12OSUtil()
+ elif Version(distro_version) == Version("14.04") or \
+ Version(distro_version) == Version("14.10"):
+ return Ubuntu14OSUtil()
+ elif distro_full_name == "Snappy Ubuntu Core":
+ return UbuntuSnappyOSUtil()
+ else:
+ return UbuntuOSUtil()
+ if distro_name == "coreos":
+ return CoreOSUtil()
+ if distro_name == "suse":
+ if distro_full_name=='SUSE Linux Enterprise Server' and \
+ Version(distro_version) < Version('12') or \
+ distro_full_name == 'openSUSE' and \
+ Version(distro_version) < Version('13.2'):
+ return SUSE11OSUtil()
+ else:
+ return SUSEOSUtil()
+ elif distro_name == "debian":
+ return DebianOSUtil()
+ elif distro_name == "redhat" or distro_name == "centos" or \
+ distro_name == "oracle":
+ if Version(distro_version) < Version("7"):
+ return Redhat6xOSUtil()
+ else:
+ return RedhatOSUtil()
+ elif distro_name == "freebsd":
+ return FreeBSDOSUtil()
+ else:
+ logger.warn("Unable to load distro implemetation for {0}.", distro_name)
+ logger.warn("Use default distro implemetation instead.")
+ return DefaultOSUtil()
+
diff --git a/azurelinuxagent/common/osutil/freebsd.py b/azurelinuxagent/common/osutil/freebsd.py
new file mode 100644
index 0000000..ddf8db6
--- /dev/null
+++ b/azurelinuxagent/common/osutil/freebsd.py
@@ -0,0 +1,198 @@
+# Microsoft Azure Linux Agent
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 azurelinuxagent.common.utils.fileutil as fileutil
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.textutil as textutil
+import azurelinuxagent.common.logger as logger
+from azurelinuxagent.common.exception import OSUtilError
+from azurelinuxagent.common.osutil.default import DefaultOSUtil
+
+
+class FreeBSDOSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(FreeBSDOSUtil, self).__init__()
+ self._scsi_disks_timeout_set = False
+
+ def set_hostname(self, hostname):
+ rc_file_path = '/etc/rc.conf'
+ conf_file = fileutil.read_file(rc_file_path).split("\n")
+ textutil.set_ini_config(conf_file, "hostname", hostname)
+ fileutil.write_file(rc_file_path, "\n".join(conf_file))
+ shellutil.run("hostname {0}".format(hostname), chk_err=False)
+
+ def restart_ssh_service(self):
+ return shellutil.run('service sshd restart', chk_err=False)
+
+ def useradd(self, username, expiration=None):
+ """
+ Create user account with 'username'
+ """
+ userentry = self.get_userentry(username)
+ if userentry is not None:
+ logger.warn("User {0} already exists, skip useradd", username)
+ return
+
+ if expiration is not None:
+ cmd = "pw useradd {0} -e {1} -m".format(username, expiration)
+ else:
+ cmd = "pw useradd {0} -m".format(username)
+ retcode, out = shellutil.run_get_output(cmd)
+ if retcode != 0:
+ raise OSUtilError(("Failed to create user account:{0}, "
+ "retcode:{1}, "
+ "output:{2}").format(username, retcode, out))
+
+ 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/utx.active')
+ shellutil.run('rmuser -y ' + username)
+ self.conf_sudoer(username, remove=True)
+
+ 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))
+ passwd_hash = textutil.gen_password_hash(password, crypt_id, salt_len)
+ cmd = "echo '{0}'|pw usermod {1} -H 0 ".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):
+ err = shellutil.run('pw mod user root -w no')
+ if err:
+ raise OSUtilError("Failed to delete root password: Failed to update password database.")
+
+ 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):
+ """
+ For FreeBSD, the default broadcast goes to current default gw, not a all-ones broadcast address, need to
+ specify the route manually to get it work in a VNET environment.
+ SEE ALSO: man ip(4) IP_ONESBCAST,
+ """
+ return True
+
+ def is_dhcp_enabled(self):
+ return True
+
+ def start_dhcp_service(self):
+ shellutil.run("/etc/rc.d/dhclient start {0}".format(self.get_if_name()), chk_err=False)
+
+ 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 = shellutil.run_get_output("pgrep -n dhclient")
+ return ret[1] if ret[0] == 0 else None
+
+ def eject_dvd(self, chk_err=True):
+ dvd = self.get_dvd_device()
+ retcode = shellutil.run("cdcontrol -f {0} eject".format(dvd))
+ if chk_err and retcode != 0:
+ raise OSUtilError("Failed to eject dvd: ret={0}".format(retcode))
+
+ def restart_if(self, ifname):
+ # Restart dhclient only to publish hostname
+ shellutil.run("/etc/rc.d/dhclient restart {0}".format(ifname), chk_err=False)
+
+ def get_total_mem(self):
+ cmd = "sysctl hw.physmem |awk '{print $2}'"
+ ret, output = shellutil.run_get_output(cmd)
+ 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 hw.ncpu |awk '{print $2}'")
+ 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):
+ if self._scsi_disks_timeout_set:
+ return
+
+ ret, output = shellutil.run_get_output('sysctl kern.cam.da.default_timeout={0}'.format(timeout))
+ if ret:
+ raise OSUtilError("Failed set SCSI disks timeout: {0}".format(output))
+ self._scsi_disks_timeout_set = True
+
+ def check_pid_alive(self, pid):
+ return shellutil.run('ps -p {0}'.format(pid), chk_err=False) == 0
+
+ @staticmethod
+ def _get_net_info():
+ """
+ There is no SIOCGIFCONF
+ on freeBSD - 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 = ''
+
+ err, output = shellutil.run_get_output('ifconfig -l ether', chk_err=False)
+ if err:
+ 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]
+
+ err, output = shellutil.run_get_output('ifconfig ' + iface, chk_err=False)
+ if err:
+ 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('ether ') != -1:
+ mac = line.split()[1]
+ logger.verbose("Interface info: ({0},{1},{2})", iface, inet, mac)
+
+ return iface, inet, mac
diff --git a/azurelinuxagent/common/osutil/redhat.py b/azurelinuxagent/common/osutil/redhat.py
new file mode 100644
index 0000000..03084b6
--- /dev/null
+++ b/azurelinuxagent/common/osutil/redhat.py
@@ -0,0 +1,122 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 pwd
+import shutil
+import socket
+import array
+import struct
+import fcntl
+import time
+import base64
+import azurelinuxagent.common.conf as conf
+import azurelinuxagent.common.logger as logger
+from azurelinuxagent.common.future import ustr, bytebuffer
+from azurelinuxagent.common.exception import OSUtilError, CryptError
+import azurelinuxagent.common.utils.fileutil as fileutil
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.textutil as textutil
+from azurelinuxagent.common.utils.cryptutil import CryptUtil
+from azurelinuxagent.common.osutil.default import DefaultOSUtil
+
+class Redhat6xOSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(Redhat6xOSUtil, self).__init__()
+
+ def start_network(self):
+ return shellutil.run("/sbin/service networking start", chk_err=False)
+
+ def restart_ssh_service(self):
+ return shellutil.run("/sbin/service sshd condrestart", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("/sbin/service waagent stop", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("/sbin/service waagent start", chk_err=False)
+
+ def register_agent_service(self):
+ return shellutil.run("chkconfig --add waagent", chk_err=False)
+
+ def unregister_agent_service(self):
+ return shellutil.run("chkconfig --del waagent", chk_err=False)
+
+ def openssl_to_openssh(self, input_file, output_file):
+ pubkey = fileutil.read_file(input_file)
+ try:
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ ssh_rsa_pubkey = cryptutil.asn1_to_ssh(pubkey)
+ except CryptError as e:
+ raise OSUtilError(ustr(e))
+ fileutil.write_file(output_file, ssh_rsa_pubkey)
+
+ #Override
+ def get_dhcp_pid(self):
+ ret= shellutil.run_get_output("pidof dhclient")
+ return ret[1] if ret[0] == 0 else None
+
+ def set_hostname(self, hostname):
+ """
+ Set /etc/sysconfig/network
+ """
+ fileutil.update_conf_file('/etc/sysconfig/network',
+ 'HOSTNAME',
+ 'HOSTNAME={0}'.format(hostname))
+ shellutil.run("hostname {0}".format(hostname), chk_err=False)
+
+ def set_dhcp_hostname(self, hostname):
+ ifname = self.get_if_name()
+ filepath = "/etc/sysconfig/network-scripts/ifcfg-{0}".format(ifname)
+ fileutil.update_conf_file(filepath, 'DHCP_HOSTNAME',
+ 'DHCP_HOSTNAME={0}'.format(hostname))
+
+ def get_dhcp_lease_endpoint(self):
+ return self.get_endpoint_from_leases_path('/var/lib/dhclient/dhclient-*.leases')
+
+class RedhatOSUtil(Redhat6xOSUtil):
+ def __init__(self):
+ super(RedhatOSUtil, self).__init__()
+
+ def set_hostname(self, hostname):
+ """
+ Set /etc/hostname
+ Unlike redhat 6.x, redhat 7.x will set hostname to /etc/hostname
+ """
+ DefaultOSUtil.set_hostname(self, hostname)
+
+ def publish_hostname(self, hostname):
+ """
+ Restart NetworkManager first before publishing hostname
+ """
+ shellutil.run("service NetworkManager restart")
+ super(RedhatOSUtil, self).publish_hostname(hostname)
+
+ def register_agent_service(self):
+ return shellutil.run("systemctl enable waagent", chk_err=False)
+
+ def unregister_agent_service(self):
+ return shellutil.run("systemctl disable waagent", chk_err=False)
+
+ def openssl_to_openssh(self, input_file, output_file):
+ DefaultOSUtil.openssl_to_openssh(self, input_file, output_file)
+
+ def get_dhcp_lease_endpoint(self):
+ # centos7 has this weird naming with double hyphen like /var/lib/dhclient/dhclient--eth0.lease
+ return self.get_endpoint_from_leases_path('/var/lib/dhclient/dhclient-*.lease')
diff --git a/azurelinuxagent/common/osutil/suse.py b/azurelinuxagent/common/osutil/suse.py
new file mode 100644
index 0000000..f0ed0c0
--- /dev/null
+++ b/azurelinuxagent/common/osutil/suse.py
@@ -0,0 +1,108 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 pwd
+import shutil
+import socket
+import array
+import struct
+import fcntl
+import time
+import azurelinuxagent.common.logger as logger
+import azurelinuxagent.common.utils.fileutil as fileutil
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.textutil as textutil
+from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME
+from azurelinuxagent.common.osutil.default import DefaultOSUtil
+
+class SUSE11OSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(SUSE11OSUtil, self).__init__()
+ self.dhclient_name='dhcpcd'
+
+ def set_hostname(self, hostname):
+ fileutil.write_file('/etc/HOSTNAME', hostname)
+ shellutil.run("hostname {0}".format(hostname), chk_err=False)
+
+ def get_dhcp_pid(self):
+ ret= shellutil.run_get_output("pidof {0}".format(self.dhclient_name))
+ return ret[1] if ret[0] == 0 else None
+
+ def is_dhcp_enabled(self):
+ return True
+
+ def stop_dhcp_service(self):
+ cmd = "/sbin/service {0} stop".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_dhcp_service(self):
+ cmd = "/sbin/service {0} start".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_network(self) :
+ return shellutil.run("/sbin/service start network", chk_err=False)
+
+ def restart_ssh_service(self):
+ return shellutil.run("/sbin/service sshd restart", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("/sbin/service waagent stop", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("/sbin/service waagent start", chk_err=False)
+
+ def register_agent_service(self):
+ return shellutil.run("/sbin/insserv waagent", chk_err=False)
+
+ def unregister_agent_service(self):
+ return shellutil.run("/sbin/insserv -r waagent", chk_err=False)
+
+class SUSEOSUtil(SUSE11OSUtil):
+ def __init__(self):
+ super(SUSEOSUtil, self).__init__()
+ self.dhclient_name = 'wickedd-dhcp4'
+
+ def stop_dhcp_service(self):
+ cmd = "systemctl stop {0}".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_dhcp_service(self):
+ cmd = "systemctl start {0}".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_network(self) :
+ return shellutil.run("systemctl start network", chk_err=False)
+
+ def restart_ssh_service(self):
+ return shellutil.run("systemctl restart sshd", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("systemctl stop waagent", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("systemctl start waagent", chk_err=False)
+
+ def register_agent_service(self):
+ return shellutil.run("systemctl enable waagent", chk_err=False)
+
+ def unregister_agent_service(self):
+ return shellutil.run("systemctl disable waagent", chk_err=False)
+
+
diff --git a/azurelinuxagent/common/osutil/ubuntu.py b/azurelinuxagent/common/osutil/ubuntu.py
new file mode 100644
index 0000000..4032cf4
--- /dev/null
+++ b/azurelinuxagent/common/osutil/ubuntu.py
@@ -0,0 +1,66 @@
+#
+# Copyright 2014 Microsoft Corporation
+#
+# 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 azurelinuxagent.common.utils.shellutil as shellutil
+from azurelinuxagent.common.osutil.default import DefaultOSUtil
+
+class Ubuntu14OSUtil(DefaultOSUtil):
+ def __init__(self):
+ super(Ubuntu14OSUtil, self).__init__()
+
+ def start_network(self):
+ return shellutil.run("service networking start", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("service walinuxagent stop", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("service walinuxagent start", chk_err=False)
+
+ def remove_rules_files(self, rules_files=""):
+ pass
+
+ def restore_rules_files(self, rules_files=""):
+ pass
+
+ def get_dhcp_lease_endpoint(self):
+ return self.get_endpoint_from_leases_path('/var/lib/dhcp/dhclient.*.leases')
+
+class Ubuntu12OSUtil(Ubuntu14OSUtil):
+ def __init__(self):
+ super(Ubuntu12OSUtil, self).__init__()
+
+ #Override
+ def get_dhcp_pid(self):
+ ret= shellutil.run_get_output("pidof dhclient3")
+ return ret[1] if ret[0] == 0 else None
+
+class UbuntuOSUtil(Ubuntu14OSUtil):
+ def __init__(self):
+ super(UbuntuOSUtil, self).__init__()
+
+ def register_agent_service(self):
+ return shellutil.run("systemctl unmask walinuxagent", chk_err=False)
+
+ def unregister_agent_service(self):
+ return shellutil.run("systemctl mask walinuxagent", chk_err=False)
+
+class UbuntuSnappyOSUtil(Ubuntu14OSUtil):
+ def __init__(self):
+ super(UbuntuSnappyOSUtil, self).__init__()
+ self.conf_file_path = '/apps/walinuxagent/current/waagent.conf'