diff options
author | Daniel Watkins <daniel.watkins@canonical.com> | 2016-09-13 16:11:47 +0100 |
---|---|---|
committer | usd-importer <ubuntu-server@lists.ubuntu.com> | 2016-09-14 10:39:12 +0000 |
commit | 5009a9d0f3606fc08a80ec0d59076d8dc48d2f25 (patch) | |
tree | ad67eef74c5208178950db6ee28195e2137fa713 /azurelinuxagent/common/osutil | |
parent | 0f7cef5b52162d1ebb31a738bd8fc9febe1fbda6 (diff) | |
download | vyos-walinuxagent-5009a9d0f3606fc08a80ec0d59076d8dc48d2f25.tar.gz vyos-walinuxagent-5009a9d0f3606fc08a80ec0d59076d8dc48d2f25.zip |
Import patches-unapplied version 2.1.5-0ubuntu1 to ubuntu/yakkety-proposed
Imported using git-ubuntu import.
Changelog parent: 0f7cef5b52162d1ebb31a738bd8fc9febe1fbda6
New changelog entries:
* New upstream release (LP: #1603581)
- d/patches/disable-auto-update.patch:
- The new version introduces auto-updating of the agent to its latest
version via an internal mechanism; disable this
- d/patches/fix_shebangs.patch:
- Dropped in favour of the dh_python3 --shebang option.
- Refreshed d/patches/disable_udev_overrides.patch
Diffstat (limited to 'azurelinuxagent/common/osutil')
-rw-r--r-- | azurelinuxagent/common/osutil/__init__.py | 18 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/coreos.py | 92 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/debian.py | 47 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/default.py | 792 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/factory.py | 69 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/freebsd.py | 198 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/redhat.py | 122 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/suse.py | 108 | ||||
-rw-r--r-- | azurelinuxagent/common/osutil/ubuntu.py | 66 |
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' |