diff options
Diffstat (limited to 'azurelinuxagent/distro')
35 files changed, 0 insertions, 4006 deletions
diff --git a/azurelinuxagent/distro/__init__.py b/azurelinuxagent/distro/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/__init__.py +++ b/azurelinuxagent/distro/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/distro/coreos/__init__.py b/azurelinuxagent/distro/coreos/__init__.py deleted file mode 100644 index 8c1bbdb..0000000 --- a/azurelinuxagent/distro/coreos/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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+ -# diff --git a/azurelinuxagent/distro/coreos/deprovision.py b/azurelinuxagent/distro/coreos/deprovision.py deleted file mode 100644 index 9642579..0000000 --- a/azurelinuxagent/distro/coreos/deprovision.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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.utils.fileutil as fileutil -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction - -class CoreOSDeprovisionHandler(DeprovisionHandler): - def __init__(self, distro): - self.distro = distro - - def setup(self, deluser): - warnings, actions = super(CoreOSDeprovisionHandler, self).setup(deluser) - warnings.append("WARNING! /etc/machine-id will be removed.") - files_to_del = ['/etc/machine-id'] - actions.append(DeprovisionAction(fileutil.rm_files, files_to_del)) - return warnings, actions - diff --git a/azurelinuxagent/distro/coreos/distro.py b/azurelinuxagent/distro/coreos/distro.py deleted file mode 100644 index 04c7bff..0000000 --- a/azurelinuxagent/distro/coreos/distro.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.coreos.osutil import CoreOSUtil -from azurelinuxagent.distro.coreos.deprovision import CoreOSDeprovisionHandler - -class CoreOSDistro(DefaultDistro): - def __init__(self): - super(CoreOSDistro, self).__init__() - self.osutil = CoreOSUtil() - self.deprovision_handler = CoreOSDeprovisionHandler(self) - diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/distro/coreos/osutil.py deleted file mode 100644 index ffc83e3..0000000 --- a/azurelinuxagent/distro/coreos/osutil.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# 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.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil 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): - return shellutil.run("systemctl restart sshd", chk_err=False) - - 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 set_ssh_client_alive_interval(self): - #In CoreOS, /etc/sshd_config is mount readonly. Skip the setting - pass - - def conf_sshd(self, disable_password): - #In CoreOS, /etc/sshd_config is mount readonly. Skip the setting - pass - diff --git a/azurelinuxagent/distro/debian/__init__.py b/azurelinuxagent/distro/debian/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/debian/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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+ -# - diff --git a/azurelinuxagent/distro/debian/distro.py b/azurelinuxagent/distro/debian/distro.py deleted file mode 100644 index 01f4e3e..0000000 --- a/azurelinuxagent/distro/debian/distro.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.debian.osutil import DebianOSUtil - -class DebianDistro(DefaultDistro): - def __init__(self): - super(DebianDistro, self).__init__() - self.osutil = DebianOSUtil() - diff --git a/azurelinuxagent/distro/debian/loader.py b/azurelinuxagent/distro/debian/loader.py deleted file mode 100644 index cc0c06f..0000000 --- a/azurelinuxagent/distro/debian/loader.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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+ -# - - -def get_osutil(): - from azurelinuxagent.distro.debian.osutil import DebianOSUtil - return DebianOSUtil() - diff --git a/azurelinuxagent/distro/debian/osutil.py b/azurelinuxagent/distro/debian/osutil.py deleted file mode 100644 index a40c1de..0000000 --- a/azurelinuxagent/distro/debian/osutil.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# 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.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil 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/distro/default/__init__.py b/azurelinuxagent/distro/default/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/default/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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+ -# - diff --git a/azurelinuxagent/distro/default/daemon.py b/azurelinuxagent/distro/default/daemon.py deleted file mode 100644 index cf9eb16..0000000 --- a/azurelinuxagent/distro/default/daemon.py +++ /dev/null @@ -1,103 +0,0 @@ -# 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 os -import time -import sys -import traceback -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProtocolError -from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \ - DISTRO_NAME, DISTRO_VERSION, \ - DISTRO_FULL_NAME, PY_VERSION_MAJOR, \ - PY_VERSION_MINOR, PY_VERSION_MICRO -import azurelinuxagent.event as event -import azurelinuxagent.utils.fileutil as fileutil - - -class DaemonHandler(object): - def __init__(self, distro): - self.distro = distro - self.running = True - - - def run(self): - logger.info("{0} Version:{1}", AGENT_LONG_NAME, AGENT_VERSION) - logger.info("OS: {0} {1}", DISTRO_NAME, DISTRO_VERSION) - logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR, - PY_VERSION_MICRO) - - self.check_pid() - - while self.running: - try: - self.daemon() - except Exception as e: - err_msg = traceback.format_exc() - add_event("WALA", is_success=False, message=ustr(err_msg), - op=WALAEventOperation.UnhandledError) - logger.info("Sleep 15 seconds and restart daemon") - time.sleep(15) - - def check_pid(self): - """Check whether daemon is already running""" - pid = None - pid_file = conf.get_agent_pid_file_path() - if os.path.isfile(pid_file): - pid = fileutil.read_file(pid_file) - - if pid is not None and os.path.isdir(os.path.join("/proc", pid)): - logger.info("Daemon is already running: {0}", pid) - sys.exit(0) - - fileutil.write_file(pid_file, ustr(os.getpid())) - - def daemon(self): - logger.info("Run daemon") - #Create lib dir - if not os.path.isdir(conf.get_lib_dir()): - fileutil.mkdir(conf.get_lib_dir(), mode=0o700) - os.chdir(conf.get_lib_dir()) - - if conf.get_detect_scvmm_env(): - if self.distro.scvmm_handler.run(): - return - - self.distro.provision_handler.run() - - if conf.get_resourcedisk_format(): - self.distro.resource_disk_handler.run() - - try: - protocol = self.distro.protocol_util.detect_protocol() - except ProtocolError as e: - logger.error("Failed to detect protocol, exit", e) - return - - self.distro.event_handler.run() - self.distro.env_handler.run() - - while self.running: - #Handle extensions - self.distro.ext_handlers_handler.run() - time.sleep(25) - diff --git a/azurelinuxagent/distro/default/deprovision.py b/azurelinuxagent/distro/default/deprovision.py deleted file mode 100644 index 4db4cdc..0000000 --- a/azurelinuxagent/distro/default/deprovision.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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.conf as conf -from azurelinuxagent.exception import ProtocolError -from azurelinuxagent.future import read_input -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil - -class DeprovisionAction(object): - def __init__(self, func, args=[], kwargs={}): - self.func = func - self.args = args - self.kwargs = kwargs - - def invoke(self): - self.func(*self.args, **self.kwargs) - -class DeprovisionHandler(object): - def __init__(self, distro): - self.distro = distro - - def del_root_password(self, warnings, actions): - warnings.append("WARNING! root password will be disabled. " - "You will not be able to login as root.") - - actions.append(DeprovisionAction(self.distro.osutil.del_root_password)) - - def del_user(self, warnings, actions): - - try: - ovfenv = self.distro.protocol_util.get_ovf_env() - except ProtocolError: - warnings.append("WARNING! ovf-env.xml is not found.") - warnings.append("WARNING! Skip delete user.") - return - - username = ovfenv.username - warnings.append(("WARNING! {0} account and entire home directory " - "will be deleted.").format(username)) - actions.append(DeprovisionAction(self.distro.osutil.del_account, - [username])) - - - def regen_ssh_host_key(self, warnings, actions): - warnings.append("WARNING! All SSH host key pairs will be deleted.") - actions.append(DeprovisionAction(shellutil.run, - ['rm -f /etc/ssh/ssh_host_*key*'])) - - def stop_agent_service(self, warnings, actions): - warnings.append("WARNING! The waagent service will be stopped.") - actions.append(DeprovisionAction(self.distro.osutil.stop_agent_service)) - - def del_files(self, warnings, actions): - files_to_del = ['/root/.bash_history', '/var/log/waagent.log'] - actions.append(DeprovisionAction(fileutil.rm_files, files_to_del)) - - def del_dhcp_lease(self, warnings, actions): - warnings.append("WARNING! Cached DHCP leases will be deleted.") - dirs_to_del = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"] - actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del)) - - def del_lib_dir(self, warnings, actions): - dirs_to_del = [conf.get_lib_dir()] - actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del)) - - def reset_hostname(self, warnings, actions): - localhost = ["localhost.localdomain"] - actions.append(DeprovisionAction(self.distro.osutil.set_hostname, - localhost)) - actions.append(DeprovisionAction(self.distro.osutil.set_dhcp_hostname, - localhost)) - - def setup(self, deluser): - warnings = [] - actions = [] - - self.stop_agent_service(warnings, actions) - if conf.get_regenerate_ssh_host_key(): - self.regen_ssh_host_key(warnings, actions) - - self.del_dhcp_lease(warnings, actions) - self.reset_hostname(warnings, actions) - - if conf.get_delete_root_password(): - self.del_root_password(warnings, actions) - - self.del_lib_dir(warnings, actions) - self.del_files(warnings, actions) - - if deluser: - self.del_user(warnings, actions) - - return warnings, actions - - def run(self, force=False, deluser=False): - warnings, actions = self.setup(deluser) - for warning in warnings: - print(warning) - - if not force: - confirm = read_input("Do you want to proceed (y/n)") - if not confirm.lower().startswith('y'): - return - - for action in actions: - action.invoke() - - diff --git a/azurelinuxagent/distro/default/dhcp.py b/azurelinuxagent/distro/default/dhcp.py deleted file mode 100644 index fc439d2..0000000 --- a/azurelinuxagent/distro/default/dhcp.py +++ /dev/null @@ -1,318 +0,0 @@ -# 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 os -import socket -import array -import time -import threading -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.textutil import hex_dump, hex_dump2, hex_dump3, \ - compare_bytes, str_to_ord, \ - unpack_big_endian, \ - unpack_little_endian, \ - int_to_ip4_addr -from azurelinuxagent.exception import DhcpError - - -class DhcpHandler(object): - """ - Azure use DHCP option 245 to pass endpoint ip to VMs. - """ - def __init__(self, distro): - self.distro = distro - self.endpoint = None - self.gateway = None - self.routes = None - - def run(self): - """ - Send dhcp request - Configure default gateway and routes - Save wire server endpoint if found - """ - self.send_dhcp_req() - self.conf_routes() - - def wait_for_network(self): - """ - Wait for network stack to be initialized. - """ - ipv4 = self.distro.osutil.get_ip4_addr() - while ipv4 == '' or ipv4 == '0.0.0.0': - logger.info("Waiting for network.") - time.sleep(10) - logger.info("Try to start network interface.") - self.distro.osutil.start_network() - ipv4 = self.distro.osutil.get_ip4_addr() - - def conf_routes(self): - logger.info("Configure routes") - logger.info("Gateway:{0}", self.gateway) - logger.info("Routes:{0}", self.routes) - #Add default gateway - if self.gateway is not None: - self.distro.osutil.route_add(0 , 0, self.gateway) - if self.routes is not None: - for route in self.routes: - self.distro.osutil.route_add(route[0], route[1], route[2]) - - def _send_dhcp_req(self, request): - __waiting_duration__ = [0, 10, 30, 60, 60] - for duration in __waiting_duration__: - try: - self.distro.osutil.allow_dhcp_broadcast() - response = socket_send(request) - validate_dhcp_resp(request, response) - return response - except DhcpError as e: - logger.warn("Failed to send DHCP request: {0}", e) - time.sleep(duration) - return None - - def send_dhcp_req(self): - """ - Build dhcp request with mac addr - Configure route to allow dhcp traffic - Stop dhcp service if necessary - """ - logger.info("Send dhcp request") - mac_addr = self.distro.osutil.get_mac_addr() - req = build_dhcp_request(mac_addr) - - # Temporary allow broadcast for dhcp. Remove the route when done. - missing_default_route = self.distro.osutil.is_missing_default_route() - ifname = self.distro.osutil.get_if_name() - if missing_default_route: - self.distro.osutil.set_route_for_dhcp_broadcast(ifname) - - # In some distros, dhcp service needs to be shutdown before agent probe - # endpoint through dhcp. - if self.distro.osutil.is_dhcp_enabled(): - self.distro.osutil.stop_dhcp_service() - - resp = self._send_dhcp_req(req) - - if self.distro.osutil.is_dhcp_enabled(): - self.distro.osutil.start_dhcp_service() - - if missing_default_route: - self.distro.osutil.remove_route_for_dhcp_broadcast(ifname) - - if resp is None: - raise DhcpError("Failed to receive dhcp response.") - self.endpoint, self.gateway, self.routes = parse_dhcp_resp(resp) - -def validate_dhcp_resp(request, response): - bytes_recv = len(response) - if bytes_recv < 0xF6: - logger.error("HandleDhcpResponse: Too few bytes received:{0}", - bytes_recv) - return False - - logger.verb("BytesReceived:{0}", hex(bytes_recv)) - logger.verb("DHCP response:{0}", hex_dump(response, bytes_recv)) - - # check transactionId, cookie, MAC address cookie should never mismatch - # transactionId and MAC address may mismatch if we see a response - # meant from another machine - if not compare_bytes(request, response, 0xEC, 4): - logger.verb("Cookie not match:\nsend={0},\nreceive={1}", - hex_dump3(request, 0xEC, 4), - hex_dump3(response, 0xEC, 4)) - raise DhcpError("Cookie in dhcp respones doesn't match the request") - - if not compare_bytes(request, response, 4, 4): - logger.verb("TransactionID not match:\nsend={0},\nreceive={1}", - hex_dump3(request, 4, 4), - hex_dump3(response, 4, 4)) - raise DhcpError("TransactionID in dhcp respones " - "doesn't match the request") - - if not compare_bytes(request, response, 0x1C, 6): - logger.verb("Mac Address not match:\nsend={0},\nreceive={1}", - hex_dump3(request, 0x1C, 6), - hex_dump3(response, 0x1C, 6)) - raise DhcpError("Mac Addr in dhcp respones " - "doesn't match the request") - -def parse_route(response, option, i, length, bytes_recv): - # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx - logger.verb("Routes at offset: {0} with length:{1}", hex(i), hex(length)) - routes = [] - if length < 5: - logger.error("Data too small for option:{0}", option) - j = i + 2 - while j < (i + length + 2): - mask_len_bits = str_to_ord(response[j]) - mask_len_bytes = (((mask_len_bits + 7) & ~7) >> 3) - mask = 0xFFFFFFFF & (0xFFFFFFFF << (32 - mask_len_bits)) - j += 1 - net = unpack_big_endian(response, j, mask_len_bytes) - net <<= (32 - mask_len_bytes * 8) - net &= mask - j += mask_len_bytes - gateway = unpack_big_endian(response, j, 4) - j += 4 - routes.append((net, mask, gateway)) - if j != (i + length + 2): - logger.error("Unable to parse routes") - return routes - -def parse_ip_addr(response, option, i, length, bytes_recv): - if i + 5 < bytes_recv: - if length != 4: - logger.error("Endpoint or Default Gateway not 4 bytes") - return None - addr = unpack_big_endian(response, i + 2, 4) - ip_addr = int_to_ip4_addr(addr) - return ip_addr - else: - logger.error("Data too small for option:{0}", option) - return None - -def parse_dhcp_resp(response): - """ - Parse DHCP response: - Returns endpoint server or None on error. - """ - logger.verb("parse Dhcp Response") - bytes_recv = len(response) - endpoint = None - gateway = None - routes = None - - # Walk all the returned options, parsing out what we need, ignoring the - # others. We need the custom option 245 to find the the endpoint we talk to, - # as well as, to handle some Linux DHCP client incompatibilities, - # options 3 for default gateway and 249 for routes. And 255 is end. - - i = 0xF0 # offset to first option - while i < bytes_recv: - option = str_to_ord(response[i]) - length = 0 - if (i + 1) < bytes_recv: - length = str_to_ord(response[i + 1]) - logger.verb("DHCP option {0} at offset:{1} with length:{2}", - hex(option), hex(i), hex(length)) - if option == 255: - logger.verb("DHCP packet ended at offset:{0}", hex(i)) - break - elif option == 249: - routes = parse_route(response, option, i, length, bytes_recv) - elif option == 3: - gateway = parse_ip_addr(response, option, i, length, bytes_recv) - logger.verb("Default gateway:{0}, at {1}", gateway, hex(i)) - elif option == 245: - endpoint = parse_ip_addr(response, option, i, length, bytes_recv) - logger.verb("Azure wire protocol endpoint:{0}, at {1}", gateway, - hex(i)) - else: - logger.verb("Skipping DHCP option:{0} at {1} with length {2}", - hex(option), hex(i), hex(length)) - i += length + 2 - return endpoint, gateway, routes - -def socket_send(request): - sock = None - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, - socket.IPPROTO_UDP) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(("0.0.0.0", 68)) - sock.sendto(request, ("<broadcast>", 67)) - sock.settimeout(10) - logger.verb("Send DHCP request: Setting socket.timeout=10, " - "entering recv") - response = sock.recv(1024) - return response - except IOError as e: - raise DhcpError("{0}".format(e)) - finally: - if sock is not None: - sock.close() - -def build_dhcp_request(mac_addr): - """ - Build DHCP request string. - """ - # - # typedef struct _DHCP { - # UINT8 Opcode; /* op: BOOTREQUEST or BOOTREPLY */ - # UINT8 HardwareAddressType; /* htype: ethernet */ - # UINT8 HardwareAddressLength; /* hlen: 6 (48 bit mac address) */ - # UINT8 Hops; /* hops: 0 */ - # UINT8 TransactionID[4]; /* xid: random */ - # UINT8 Seconds[2]; /* secs: 0 */ - # UINT8 Flags[2]; /* flags: 0 or 0x8000 for broadcast */ - # UINT8 ClientIpAddress[4]; /* ciaddr: 0 */ - # UINT8 YourIpAddress[4]; /* yiaddr: 0 */ - # UINT8 ServerIpAddress[4]; /* siaddr: 0 */ - # UINT8 RelayAgentIpAddress[4]; /* giaddr: 0 */ - # UINT8 ClientHardwareAddress[16]; /* chaddr: 6 byte eth MAC address */ - # UINT8 ServerName[64]; /* sname: 0 */ - # UINT8 BootFileName[128]; /* file: 0 */ - # UINT8 MagicCookie[4]; /* 99 130 83 99 */ - # /* 0x63 0x82 0x53 0x63 */ - # /* options -- hard code ours */ - # - # UINT8 MessageTypeCode; /* 53 */ - # UINT8 MessageTypeLength; /* 1 */ - # UINT8 MessageType; /* 1 for DISCOVER */ - # UINT8 End; /* 255 */ - # } DHCP; - # - - # tuple of 244 zeros - # (struct.pack_into would be good here, but requires Python 2.5) - request = [0] * 244 - - trans_id = gen_trans_id() - - # Opcode = 1 - # HardwareAddressType = 1 (ethernet/MAC) - # HardwareAddressLength = 6 (ethernet/MAC/48 bits) - for a in range(0, 3): - request[a] = [1, 1, 6][a] - - # fill in transaction id (random number to ensure response matches request) - for a in range(0, 4): - request[4 + a] = str_to_ord(trans_id[a]) - - logger.verb("BuildDhcpRequest: transactionId:%s,%04X" % ( - hex_dump2(trans_id), - unpack_big_endian(request, 4, 4))) - - # fill in ClientHardwareAddress - for a in range(0, 6): - request[0x1C + a] = str_to_ord(mac_addr[a]) - - # DHCP Magic Cookie: 99, 130, 83, 99 - # MessageTypeCode = 53 DHCP Message Type - # MessageTypeLength = 1 - # MessageType = DHCPDISCOVER - # End = 255 DHCP_END - for a in range(0, 8): - request[0xEC + a] = [99, 130, 83, 99, 53, 1, 1, 255][a] - return array.array("B", request) - -def gen_trans_id(): - return os.urandom(4) diff --git a/azurelinuxagent/distro/default/distro.py b/azurelinuxagent/distro/default/distro.py deleted file mode 100644 index ca0d77e..0000000 --- a/azurelinuxagent/distro/default/distro.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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+ -# - -from azurelinuxagent.conf import ConfigurationProvider -from azurelinuxagent.distro.default.osutil import DefaultOSUtil -from azurelinuxagent.distro.default.daemon import DaemonHandler -from azurelinuxagent.distro.default.init import InitHandler -from azurelinuxagent.distro.default.monitor import MonitorHandler -from azurelinuxagent.distro.default.dhcp import DhcpHandler -from azurelinuxagent.distro.default.protocolUtil import ProtocolUtil -from azurelinuxagent.distro.default.scvmm import ScvmmHandler -from azurelinuxagent.distro.default.env import EnvHandler -from azurelinuxagent.distro.default.provision import ProvisionHandler -from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler -from azurelinuxagent.distro.default.extension import ExtHandlersHandler -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler - -class DefaultDistro(object): - """ - """ - def __init__(self): - self.osutil = DefaultOSUtil() - self.protocol_util = ProtocolUtil(self) - - self.init_handler = InitHandler(self) - self.daemon_handler = DaemonHandler(self) - self.event_handler = MonitorHandler(self) - self.dhcp_handler = DhcpHandler(self) - self.scvmm_handler = ScvmmHandler(self) - self.env_handler = EnvHandler(self) - self.provision_handler = ProvisionHandler(self) - self.resource_disk_handler = ResourceDiskHandler(self) - self.ext_handlers_handler = ExtHandlersHandler(self) - self.deprovision_handler = DeprovisionHandler(self) - diff --git a/azurelinuxagent/distro/default/env.py b/azurelinuxagent/distro/default/env.py deleted file mode 100644 index 7878cff..0000000 --- a/azurelinuxagent/distro/default/env.py +++ /dev/null @@ -1,104 +0,0 @@ -# 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 os -import socket -import threading -import time -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf - -class EnvHandler(object): - """ - Monitor changes to dhcp and hostname. - If dhcp clinet process re-start has occurred, reset routes, dhcp with fabric. - - Monitor scsi disk. - If new scsi disk found, set timeout - """ - def __init__(self, distro): - self.distro = distro - self.stopped = True - self.hostname = None - self.dhcpid = None - self.server_thread=None - - def run(self): - if not self.stopped: - logger.info("Stop existing env monitor service.") - self.stop() - - self.stopped = False - logger.info("Start env monitor service.") - self.distro.dhcp_handler.conf_routes() - self.hostname = socket.gethostname() - self.dhcpid = self.distro.osutil.get_dhcp_pid() - self.server_thread = threading.Thread(target = self.monitor) - self.server_thread.setDaemon(True) - self.server_thread.start() - - def monitor(self): - """ - Monitor dhcp client pid and hostname. - If dhcp clinet process re-start has occurred, reset routes. - """ - while not self.stopped: - self.distro.osutil.remove_rules_files() - timeout = conf.get_root_device_scsi_timeout() - if timeout is not None: - self.distro.osutil.set_scsi_disks_timeout(timeout) - if conf.get_monitor_hostname(): - self.handle_hostname_update() - self.handle_dhclient_restart() - time.sleep(5) - - def handle_hostname_update(self): - curr_hostname = socket.gethostname() - if curr_hostname != self.hostname: - logger.info("EnvMonitor: Detected host name change: {0} -> {1}", - self.hostname, curr_hostname) - self.distro.osutil.set_hostname(curr_hostname) - self.distro.osutil.publish_hostname(curr_hostname) - self.hostname = curr_hostname - - def handle_dhclient_restart(self): - if self.dhcpid is None: - logger.warn("Dhcp client is not running. ") - self.dhcpid = self.distro.osutil.get_dhcp_pid() - return - - #The dhcp process hasn't changed since last check - if os.path.isdir(os.path.join('/proc', self.dhcpid.strip())): - return - - newpid = self.distro.osutil.get_dhcp_pid() - if newpid is not None and newpid != self.dhcpid: - logger.info("EnvMonitor: Detected dhcp client restart. " - "Restoring routing table.") - self.distro.dhcp_handler.conf_routes() - self.dhcpid = newpid - - def stop(self): - """ - Stop server comminucation and join the thread to main thread. - """ - self.stopped = True - if self.server_thread is not None: - self.server_thread.join() - diff --git a/azurelinuxagent/distro/default/extension.py b/azurelinuxagent/distro/default/extension.py deleted file mode 100644 index 82cdfed..0000000 --- a/azurelinuxagent/distro/default/extension.py +++ /dev/null @@ -1,817 +0,0 @@ -# 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 os -import zipfile -import time -import json -import subprocess -import shutil -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ExtensionError, ProtocolError, HttpError -from azurelinuxagent.future import ustr -from azurelinuxagent.metadata import AGENT_VERSION -from azurelinuxagent.protocol.restapi import ExtHandlerStatus, ExtensionStatus, \ - ExtensionSubStatus, Extension, \ - VMStatus, ExtHandler, \ - get_properties, set_properties -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.restutil as restutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.textutil import Version - -#HandlerEnvironment.json schema version -HANDLER_ENVIRONMENT_VERSION = 1.0 - -VALID_EXTENSION_STATUS = ['transitioning', 'error', 'success', 'warning'] - -VALID_HANDLER_STATUS = ['Ready', 'NotReady', "Installing", "Unresponsive"] - -def validate_has_key(obj, key, fullname): - if key not in obj: - raise ExtensionError("Missing: {0}".format(fullname)) - -def validate_in_range(val, valid_range, name): - if val not in valid_range: - raise ExtensionError("Invalid {0}: {1}".format(name, val)) - -def parse_formatted_message(formatted_message): - if formatted_message is None: - return None - validate_has_key(formatted_message, 'lang', 'formattedMessage/lang') - validate_has_key(formatted_message, 'message', 'formattedMessage/message') - return formatted_message.get('message') - -def parse_ext_substatus(substatus): - #Check extension sub status format - validate_has_key(substatus, 'status', 'substatus/status') - validate_in_range(substatus['status'], VALID_EXTENSION_STATUS, - 'substatus/status') - status = ExtensionSubStatus() - status.name = substatus.get('name') - status.status = substatus.get('status') - status.code = substatus.get('code', 0) - formatted_message = substatus.get('formattedMessage') - status.message = parse_formatted_message(formatted_message) - return status - -def parse_ext_status(ext_status, data): - if data is None or len(data) is None: - return - #Currently, only the first status will be reported - data = data[0] - #Check extension status format - validate_has_key(data, 'status', 'status') - status_data = data['status'] - validate_has_key(status_data, 'status', 'status/status') - - validate_in_range(status_data['status'], VALID_EXTENSION_STATUS, - 'status/status') - - applied_time = status_data.get('configurationAppliedTime') - ext_status.configurationAppliedTime = applied_time - ext_status.operation = status_data.get('operation') - ext_status.status = status_data.get('status') - ext_status.code = status_data.get('code', 0) - formatted_message = status_data.get('formattedMessage') - ext_status.message = parse_formatted_message(formatted_message) - substatus_list = status_data.get('substatus') - if substatus_list is None: - return - for substatus in substatus_list: - ext_status.substatusList.append(parse_ext_substatus(substatus)) - -class ExtHandlerState(object): - NotInstalled = "NotInstalled" - Installed = "Installed" - Enabled = "Enabled" - -class ExtHandlersHandler(object): - def __init__(self, distro): - self.distro = distro - self.ext_handlers = None - self.last_etag = None - self.log_report = False - - def run(self): - ext_handlers, etag = None, None - try: - self.protocol = self.distro.protocol_util.get_protocol() - ext_handlers, etag = self.protocol.get_ext_handlers() - except ProtocolError as e: - add_event(name="WALA", is_success=False, message=ustr(e)) - return - - if self.last_etag is not None and self.last_etag == etag: - logger.verb("No change to ext handler config:{0}, skip", etag) - self.log_report = False - else: - logger.info("Handle new ext handler config") - self.log_report = True #Log status report success on new config - self.handle_ext_handlers(ext_handlers) - self.last_etag = etag - - self.report_ext_handlers_status(ext_handlers) - - def handle_ext_handlers(self, ext_handlers): - if ext_handlers.extHandlers is None or \ - len(ext_handlers.extHandlers) == 0: - logger.info("No ext handler config found") - return - - for ext_handler in ext_handlers.extHandlers: - #TODO handle install in sequence, enable in parallel - self.handle_ext_handler(ext_handler) - - def handle_ext_handler(self, ext_handler): - ext_handler_i = ExtHandlerInstance(ext_handler, self.protocol) - try: - state = ext_handler.properties.state - ext_handler_i.logger.info("Expected handler state: {0}", state) - if state == "enabled": - self.handle_enable(ext_handler_i) - elif state == u"disabled": - self.handle_disable(ext_handler_i) - elif state == u"uninstall": - self.handle_uninstall(ext_handler_i) - else: - message = u"Unknown ext handler state:{0}".format(state) - raise ExtensionError(message) - except ExtensionError as e: - ext_handler_i.set_handler_status(message=ustr(e), code=-1) - ext_handler_i.report_event(message=ustr(e), is_success=False) - - def handle_enable(self, ext_handler_i): - - ext_handler_i.decide_version() - - old_ext_handler_i = ext_handler_i.get_installed_ext_handler() - if old_ext_handler_i is not None and \ - old_ext_handler_i.version_gt(ext_handler_i): - raise ExtensionError(u"Downgrade not allowed") - - handler_state = ext_handler_i.get_handler_state() - ext_handler_i.logger.info("Current handler state is: {0}", handler_state) - if handler_state == ExtHandlerState.NotInstalled: - ext_handler_i.set_handler_state(ExtHandlerState.NotInstalled) - - ext_handler_i.download() - - ext_handler_i.update_settings() - - if old_ext_handler_i is None: - ext_handler_i.install() - elif ext_handler_i.version_gt(old_ext_handler_i): - old_ext_handler_i.disable() - ext_handler_i.copy_status_files(old_ext_handler_i) - ext_handler_i.update() - old_ext_handler_i.uninstall() - old_ext_handler_i.rm_ext_handler_dir() - ext_handler_i.update_with_install() - else: - ext_handler_i.update_settings() - - ext_handler_i.enable() - - def handle_disable(self, ext_handler_i): - handler_state = ext_handler_i.get_handler_state() - ext_handler_i.logger.info("Current handler state is: {0}", handler_state) - if handler_state == ExtHandlerState.Enabled: - ext_handler_i.disable() - - def handle_uninstall(self, ext_handler_i): - handler_state = ext_handler_i.get_handler_state() - ext_handler_i.logger.info("Current handler state is: {0}", handler_state) - if handler_state != ExtHandlerState.NotInstalled: - if handler_state == ExtHandlerState.Enabled: - ext_handler_i.disable() - ext_handler_i.uninstall() - ext_handler_i.rm_ext_handler_dir() - - def report_ext_handlers_status(self, ext_handlers): - """Go thru handler_state dir, collect and report status""" - vm_status = VMStatus() - vm_status.vmAgent.version = AGENT_VERSION - vm_status.vmAgent.status = "Ready" - vm_status.vmAgent.message = "Guest Agent is running" - - if ext_handlers is not None: - for ext_handler in ext_handlers.extHandlers: - try: - self.report_ext_handler_status(vm_status, ext_handler) - except ExtensionError as e: - add_event(name="WALA", is_success=False, message=ustr(e)) - - logger.verb("Report vm agent status") - - try: - self.protocol.report_vm_status(vm_status) - except ProtocolError as e: - message = "Failed to report vm agent status: {0}".format(e) - add_event(name="WALA", is_success=False, message=message) - - if self.log_report: - logger.info("Successfully reported vm agent status") - - - def report_ext_handler_status(self, vm_status, ext_handler): - ext_handler_i = ExtHandlerInstance(ext_handler, self.protocol) - - handler_status = ext_handler_i.get_handler_status() - if handler_status is None: - return - - handler_state = ext_handler_i.get_handler_state() - if handler_state != ExtHandlerState.NotInstalled: - try: - active_exts = ext_handler_i.report_ext_status() - handler_status.extensions.extend(active_exts) - except ExtensionError as e: - ext_handler_i.set_handler_status(message=ustr(e), code=-1) - - try: - heartbeat = ext_handler_i.collect_heartbeat() - if heartbeat is not None: - handler_status.status = heartbeat.get('status') - except ExtensionError as e: - ext_handler_i.set_handler_status(message=ustr(e), code=-1) - - vm_status.vmAgent.extensionHandlers.append(handler_status) - -class ExtHandlerInstance(object): - def __init__(self, ext_handler, protocol): - self.ext_handler = ext_handler - self.protocol = protocol - self.operation = None - self.pkg = None - - prefix = "[{0}]".format(self.get_full_name()) - self.logger = logger.Logger(logger.DEFAULT_LOGGER, prefix) - - try: - fileutil.mkdir(self.get_log_dir(), mode=0o744) - except IOError as e: - self.logger.error(u"Failed to create extension log dir: {0}", e) - - log_file = os.path.join(self.get_log_dir(), "CommandExecution.log") - self.logger.add_appender(logger.AppenderType.FILE, - logger.LogLevel.INFO, log_file) - - def decide_version(self): - """ - If auto-upgrade, get the largest public extension version under - the requested major version family of currently installed plugin version - - Else, get the highest hot-fix for requested version, - """ - self.logger.info("Decide which version to use") - try: - pkg_list = self.protocol.get_ext_handler_pkgs(self.ext_handler) - except ProtocolError as e: - raise ExtensionError("Failed to get ext handler pkgs", e) - - version = self.ext_handler.properties.version - update_policy = self.ext_handler.properties.upgradePolicy - - version_frag = version.split('.') - if len(version_frag) < 2: - raise ExtensionError("Wrong version format: {0}".format(version)) - - version_prefix = None - if update_policy is not None and update_policy == 'auto': - version_prefix = "{0}.".format(version_frag[0]) - else: - version_prefix = "{0}.{1}.".format(version_frag[0], version_frag[1]) - - packages = [x for x in pkg_list.versions \ - if x.version.startswith(version_prefix) or \ - x.version == version] - - packages = sorted(packages, key=lambda x: Version(x.version), - reverse=True) - - if len(packages) <= 0: - raise ExtensionError("Failed to find and valid extension package") - self.pkg = packages[0] - self.ext_handler.properties.version = packages[0].version - self.logger.info("Use version: {0}", self.pkg.version) - - def version_gt(self, other): - self_version = self.ext_handler.properties.version - other_version = other.ext_handler.properties.version - return Version(self_version) > Version(other_version) - - def get_installed_ext_handler(self): - lastest_version = None - ext_handler_name = self.ext_handler.name - - for dir_name in os.listdir(conf.get_lib_dir()): - path = os.path.join(conf.get_lib_dir(), dir_name) - if os.path.isdir(path) and dir_name.startswith(ext_handler_name): - seperator = dir_name.rfind('-') - if seperator < 0: - continue - installed_name = dir_name[0: seperator] - installed_version = dir_name[seperator + 1:] - if installed_name != ext_handler_name: - continue - if lastest_version is None or \ - Version(lastest_version) < Version(installed_version): - lastest_version = installed_version - - if lastest_version is None: - return None - - data = get_properties(self.ext_handler) - old_ext_handler = ExtHandler() - set_properties("ExtHandler", old_ext_handler, data) - old_ext_handler.properties.version = lastest_version - return ExtHandlerInstance(old_ext_handler, self.protocol) - - def copy_status_files(self, old_ext_handler_i): - self.logger.info("Copy status files from old plugin to new") - old_ext_dir = old_ext_handler_i.get_base_dir() - new_ext_dir = self.get_base_dir() - - old_ext_mrseq_file = os.path.join(old_ext_dir, "mrseq") - if os.path.isfile(old_ext_mrseq_file): - shutil.copy2(old_ext_mrseq_file, new_ext_dir) - - old_ext_status_dir = old_ext_handler_i.get_status_dir() - new_ext_status_dir = self.get_status_dir() - - if os.path.isdir(old_ext_status_dir): - for status_file in os.listdir(old_ext_status_dir): - status_file = os.path.join(old_ext_status_dir, status_file) - if os.path.isfile(status_file): - shutil.copy2(status_file, new_ext_status_dir) - - def set_operation(self, op): - self.operation = op - - def report_event(self, message="", is_success=True): - version = self.ext_handler.properties.version - add_event(name=self.ext_handler.name, version=version, message=message, - op=self.operation, is_success=is_success) - - def download(self): - self.logger.info("Download extension package") - self.set_operation(WALAEventOperation.Download) - if self.pkg is None: - raise ExtensionError("No package uri found") - - package = None - for uri in self.pkg.uris: - try: - package = self.protocol.download_ext_handler_pkg(uri.uri) - except ProtocolError as e: - logger.warn("Failed download extension: {0}", e) - - if package is None: - raise ExtensionError("Failed to download extension") - - self.logger.info("Unpack extension package") - pkg_file = os.path.join(conf.get_lib_dir(), - os.path.basename(uri.uri) + ".zip") - try: - fileutil.write_file(pkg_file, bytearray(package), asbin=True) - zipfile.ZipFile(pkg_file).extractall(self.get_base_dir()) - except IOError as e: - raise ExtensionError(u"Failed to write and unzip plugin", e) - - chmod = "find {0} -type f | xargs chmod u+x".format(self.get_base_dir()) - shellutil.run(chmod) - self.report_event(message="Download succeeded") - - self.logger.info("Initialize extension directory") - #Save HandlerManifest.json - man_file = fileutil.search_file(self.get_base_dir(), - 'HandlerManifest.json') - - if man_file is None: - raise ExtensionError("HandlerManifest.json not found") - - try: - man = fileutil.read_file(man_file, remove_bom=True) - fileutil.write_file(self.get_manifest_file(), man) - except IOError as e: - raise ExtensionError(u"Failed to save HandlerManifest.json", e) - - #Create status and config dir - try: - status_dir = self.get_status_dir() - fileutil.mkdir(status_dir, mode=0o700) - conf_dir = self.get_conf_dir() - fileutil.mkdir(conf_dir, mode=0o700) - except IOError as e: - raise ExtensionError(u"Failed to create status or config dir", e) - - #Save HandlerEnvironment.json - self.create_handler_env() - - def enable(self): - self.logger.info("Enable extension.") - self.set_operation(WALAEventOperation.Enable) - - man = self.load_manifest() - self.launch_command(man.get_enable_command()) - self.set_handler_state(ExtHandlerState.Enabled) - self.set_handler_status(status="Ready", message="Plugin enabled") - - def disable(self): - self.logger.info("Disable extension.") - self.set_operation(WALAEventOperation.Disable) - - man = self.load_manifest() - self.launch_command(man.get_disable_command(), timeout=900) - self.set_handler_state(ExtHandlerState.Installed) - self.set_handler_status(status="NotReady", message="Plugin disabled") - - def install(self): - self.logger.info("Install extension.") - self.set_operation(WALAEventOperation.Install) - - man = self.load_manifest() - self.launch_command(man.get_install_command(), timeout=900) - self.set_handler_state(ExtHandlerState.Installed) - - def uninstall(self): - self.logger.info("Uninstall extension.") - self.set_operation(WALAEventOperation.UnInstall) - - try: - man = self.load_manifest() - self.launch_command(man.get_uninstall_command()) - except ExtensionError as e: - self.report_event(message=ustr(e), is_success=False) - - def rm_ext_handler_dir(self): - try: - handler_state_dir = self.get_handler_state_dir() - if os.path.isdir(handler_state_dir): - self.logger.info("Remove ext handler dir: {0}", handler_state_dir) - shutil.rmtree(handler_state_dir) - base_dir = self.get_base_dir() - if os.path.isdir(base_dir): - self.logger.info("Remove ext handler dir: {0}", base_dir) - shutil.rmtree(base_dir) - except IOError as e: - message = "Failed to rm ext handler dir: {0}".format(e) - self.report_event(message=message, is_success=False) - - def update(self): - self.logger.info("Update extension.") - self.set_operation(WALAEventOperation.Update) - - man = self.load_manifest() - self.launch_command(man.get_update_command(), timeout=900) - - def update_with_install(self): - man = self.load_manifest() - if man.is_update_with_install(): - self.install() - else: - self.logger.info("UpdateWithInstall not set. " - "Skip install during upgrade.") - self.set_handler_state(ExtHandlerState.Installed) - - def get_largest_seq_no(self): - seq_no = -1 - conf_dir = self.get_conf_dir() - for item in os.listdir(conf_dir): - item_path = os.path.join(conf_dir, item) - if os.path.isfile(item_path): - try: - seperator = item.rfind(".") - if seperator > 0 and item[seperator + 1:] == 'settings': - curr_seq_no = int(item.split('.')[0]) - if curr_seq_no > seq_no: - seq_no = curr_seq_no - except Exception as e: - self.logger.verb("Failed to parse file name: {0}", item) - continue - return seq_no - - def collect_ext_status(self, ext): - self.logger.verb("Collect extension status") - - seq_no = self.get_largest_seq_no() - if seq_no == -1: - return None - - status_dir = self.get_status_dir() - ext_status_file = "{0}.status".format(seq_no) - ext_status_file = os.path.join(status_dir, ext_status_file) - - ext_status = ExtensionStatus(seq_no=seq_no) - try: - data_str = fileutil.read_file(ext_status_file) - data = json.loads(data_str) - parse_ext_status(ext_status, data) - except IOError as e: - ext_status.message = u"Failed to get status file {0}".format(e) - ext_status.code = -1 - ext_status.status = "error" - except ValueError as e: - ext_status.message = u"Malformed status file {0}".format(e) - ext_status.code = -1 - ext_status.status = "error" - - return ext_status - - def report_ext_status(self): - active_exts = [] - for ext in self.ext_handler.properties.extensions: - ext_status = self.collect_ext_status(ext) - if ext_status is None: - continue - try: - self.protocol.report_ext_status(self.ext_handler.name, ext.name, - ext_status) - active_exts.append(ext.name) - except ProtocolError as e: - self.logger.error(u"Failed to report extension status: {0}", e) - return active_exts - - def collect_heartbeat(self): - man = self.load_manifest() - if not man.is_report_heartbeat(): - return - heartbeat_file = os.path.join(conf.get_lib_dir(), - self.get_heartbeat_file()) - - self.logger.info("Collect heart beat") - if not os.path.isfile(heartbeat_file): - raise ExtensionError("Failed to get heart beat file") - if not self.is_responsive(heartbeat_file): - return { - "status": "Unresponsive", - "code": -1, - "message": "Extension heartbeat is not responsive" - } - try: - heartbeat_json = fileutil.read_file(heartbeat_file) - heartbeat = json.loads(heartbeat_json)[0]['heartbeat'] - except IOError as e: - raise ExtensionError("Failed to get heartbeat file:{0}".format(e)) - except ValueError as e: - raise ExtensionError("Malformed heartbeat file: {0}".format(e)) - return heartbeat - - def is_responsive(self, heartbeat_file): - last_update=int(time.time() - os.stat(heartbeat_file).st_mtime) - return last_update > 600 # not updated for more than 10 min - - def launch_command(self, cmd, timeout=300): - self.logger.info("Launch command:{0}", cmd) - base_dir = self.get_base_dir() - try: - devnull = open(os.devnull, 'w') - child = subprocess.Popen(base_dir + "/" + cmd, shell=True, - cwd=base_dir, stdout=devnull) - except Exception as e: - #TODO do not catch all exception - raise ExtensionError("Failed to launch: {0}, {1}".format(cmd, e)) - - retry = timeout / 5 - while retry > 0 and child.poll == None: - time.sleep(5) - retry -= 1 - if retry == 0: - os.kill(child.pid, 9) - raise ExtensionError("Timeout({0}): {1}".format(timeout, cmd)) - - ret = child.wait() - if ret == None or ret != 0: - raise ExtensionError("Non-zero exit code: {0}, {1}".format(ret, cmd)) - - self.report_event(message="Launch command succeeded: {0}".format(cmd)) - - def load_manifest(self): - man_file = self.get_manifest_file() - try: - data = json.loads(fileutil.read_file(man_file)) - except IOError as e: - raise ExtensionError('Failed to load manifest file.') - except ValueError as e: - raise ExtensionError('Malformed manifest file.') - - return HandlerManifest(data[0]) - - def update_settings_file(self, settings_file, settings): - settings_file = os.path.join(self.get_conf_dir(), settings_file) - try: - fileutil.write_file(settings_file, settings) - except IOError as e: - raise ExtensionError(u"Failed to update settings file", e) - - def update_settings(self): - if self.ext_handler.properties.extensions is None or \ - len(self.ext_handler.properties.extensions) == 0: - #This is the behavior of waagent 2.0.x - #The new agent has to be consistent with the old one. - self.logger.info("Extension has no settings, write empty 0.settings") - self.update_settings_file("0.settings", "") - return - - for ext in self.ext_handler.properties.extensions: - settings = { - 'publicSettings': ext.publicSettings, - 'protectedSettings': ext.protectedSettings, - 'protectedSettingsCertThumbprint': ext.certificateThumbprint - } - ext_settings = { - "runtimeSettings":[{ - "handlerSettings": settings - }] - } - settings_file = "{0}.settings".format(ext.sequenceNumber) - self.logger.info("Update settings file: {0}", settings_file) - self.update_settings_file(settings_file, json.dumps(ext_settings)) - - def create_handler_env(self): - env = [{ - "name": self.ext_handler.name, - "version" : HANDLER_ENVIRONMENT_VERSION, - "handlerEnvironment" : { - "logFolder" : self.get_log_dir(), - "configFolder" : self.get_conf_dir(), - "statusFolder" : self.get_status_dir(), - "heartbeatFile" : self.get_heartbeat_file() - } - }] - try: - fileutil.write_file(self.get_env_file(), json.dumps(env)) - except IOError as e: - raise ExtensionError(u"Failed to save handler environment", e) - - def get_handler_state_dir(self): - return os.path.join(conf.get_lib_dir(), "handler_state", - self.get_full_name()) - - def set_handler_state(self, handler_state): - state_dir = self.get_handler_state_dir() - if not os.path.exists(state_dir): - try: - fileutil.mkdir(state_dir, 0o700) - except IOError as e: - self.logger.error("Failed to create state dir: {0}", e) - - try: - state_file = os.path.join(state_dir, "state") - fileutil.write_file(state_file, handler_state) - except IOError as e: - self.logger.error("Failed to set state: {0}", e) - - def get_handler_state(self): - state_dir = self.get_handler_state_dir() - state_file = os.path.join(state_dir, "state") - if not os.path.isfile(state_file): - return ExtHandlerState.NotInstalled - - try: - return fileutil.read_file(state_file) - except IOError as e: - self.logger.error("Failed to get state: {0}", e) - return ExtHandlerState.NotInstalled - - def set_handler_status(self, status="NotReady", message="", - code=0): - state_dir = self.get_handler_state_dir() - if not os.path.exists(state_dir): - try: - fileutil.mkdir(state_dir, 0o700) - except IOError as e: - self.logger.error("Failed to create state dir: {0}", e) - - handler_status = ExtHandlerStatus() - handler_status.name = self.ext_handler.name - handler_status.version = self.ext_handler.properties.version - handler_status.message = message - handler_status.code = code - handler_status.status = status - status_file = os.path.join(state_dir, "status") - - try: - fileutil.write_file(status_file, - json.dumps(get_properties(handler_status))) - except (IOError, ValueError, ProtocolError) as e: - self.logger.error("Failed to save handler status: {0}", e) - - def get_handler_status(self): - state_dir = self.get_handler_state_dir() - status_file = os.path.join(state_dir, "status") - if not os.path.isfile(status_file): - return None - - try: - data = json.loads(fileutil.read_file(status_file)) - handler_status = ExtHandlerStatus() - set_properties("ExtHandlerStatus", handler_status, data) - return handler_status - except (IOError, ValueError) as e: - self.logger.error("Failed to get handler status: {0}", e) - - def get_full_name(self): - return "{0}-{1}".format(self.ext_handler.name, - self.ext_handler.properties.version) - - def get_base_dir(self): - return os.path.join(conf.get_lib_dir(), self.get_full_name()) - - def get_status_dir(self): - return os.path.join(self.get_base_dir(), "status") - - def get_conf_dir(self): - return os.path.join(self.get_base_dir(), 'config') - - def get_heartbeat_file(self): - return os.path.join(self.get_base_dir(), 'heartbeat.log') - - def get_manifest_file(self): - return os.path.join(self.get_base_dir(), 'HandlerManifest.json') - - def get_env_file(self): - return os.path.join(self.get_base_dir(), 'HandlerEnvironment.json') - - def get_log_dir(self): - return os.path.join(conf.get_ext_log_dir(), self.ext_handler.name, - self.ext_handler.properties.version) - -class HandlerEnvironment(object): - def __init__(self, data): - self.data = data - - def get_version(self): - return self.data["version"] - - def get_log_dir(self): - return self.data["handlerEnvironment"]["logFolder"] - - def get_conf_dir(self): - return self.data["handlerEnvironment"]["configFolder"] - - def get_status_dir(self): - return self.data["handlerEnvironment"]["statusFolder"] - - def get_heartbeat_file(self): - return self.data["handlerEnvironment"]["heartbeatFile"] - -class HandlerManifest(object): - def __init__(self, data): - if data is None or data['handlerManifest'] is None: - raise ExtensionError('Malformed manifest file.') - self.data = data - - def get_name(self): - return self.data["name"] - - def get_version(self): - return self.data["version"] - - def get_install_command(self): - return self.data['handlerManifest']["installCommand"] - - def get_uninstall_command(self): - return self.data['handlerManifest']["uninstallCommand"] - - def get_update_command(self): - return self.data['handlerManifest']["updateCommand"] - - def get_enable_command(self): - return self.data['handlerManifest']["enableCommand"] - - def get_disable_command(self): - return self.data['handlerManifest']["disableCommand"] - - def is_reboot_after_install(self): - """ - Deprecated - """ - return False - - def is_report_heartbeat(self): - return self.data['handlerManifest'].get('reportHeartbeat', False) - - def is_update_with_install(self): - update_mode = self.data['handlerManifest'].get('updateMode') - if update_mode is None: - return True - return update_mode.low() == "updatewithinstall" diff --git a/azurelinuxagent/distro/default/init.py b/azurelinuxagent/distro/default/init.py deleted file mode 100644 index c703e87..0000000 --- a/azurelinuxagent/distro/default/init.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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 os -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -import azurelinuxagent.event as event - - -class InitHandler(object): - def __init__(self, distro): - self.distro = distro - - def run(self, verbose): - #Init stdout log - level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO - logger.add_logger_appender(logger.AppenderType.STDOUT, level) - - #Init config - conf_file_path = self.distro.osutil.get_agent_conf_file_path() - conf.load_conf_from_file(conf_file_path) - - #Init log - verbose = verbose or conf.get_logs_verbose() - level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO - logger.add_logger_appender(logger.AppenderType.FILE, level, - path="/var/log/waagent.log") - logger.add_logger_appender(logger.AppenderType.CONSOLE, level, - path="/dev/console") - - #Init event reporter - event_dir = os.path.join(conf.get_lib_dir(), "events") - event.init_event_logger(event_dir) - event.enable_unhandled_err_dump("WALA") - - - diff --git a/azurelinuxagent/distro/default/monitor.py b/azurelinuxagent/distro/default/monitor.py deleted file mode 100644 index 3b26c9a..0000000 --- a/azurelinuxagent/distro/default/monitor.py +++ /dev/null @@ -1,182 +0,0 @@ -# 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 sys -import traceback -import atexit -import json -import time -import datetime -import threading -import platform -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -from azurelinuxagent.event import WALAEventOperation, add_event -from azurelinuxagent.exception import EventError, ProtocolError, OSUtilError -from azurelinuxagent.future import ustr -from azurelinuxagent.utils.textutil import parse_doc, findall, find, getattrib -from azurelinuxagent.protocol.restapi import TelemetryEventParam, \ - TelemetryEventList, \ - TelemetryEvent, \ - set_properties, get_properties -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ - DISTRO_CODE_NAME, AGENT_LONG_VERSION - - -def parse_event(data_str): - try: - return parse_json_event(data_str) - except ValueError: - return parse_xml_event(data_str) - -def parse_xml_param(param_node): - name = getattrib(param_node, "Name") - value_str = getattrib(param_node, "Value") - attr_type = getattrib(param_node, "T") - value = value_str - if attr_type == 'mt:uint64': - value = int(value_str) - elif attr_type == 'mt:bool': - value = bool(value_str) - elif attr_type == 'mt:float64': - value = float(value_str) - return TelemetryEventParam(name, value) - -def parse_xml_event(data_str): - try: - xml_doc = parse_doc(data_str) - event_id = getattrib(find(xml_doc, "Event"), 'id') - provider_id = getattrib(find(xml_doc, "Provider"), 'id') - event = TelemetryEvent(event_id, provider_id) - param_nodes = findall(xml_doc, 'Param') - for param_node in param_nodes: - event.parameters.append(parse_xml_param(param_node)) - return event - except Exception as e: - raise ValueError(ustr(e)) - -def parse_json_event(data_str): - data = json.loads(data_str) - event = TelemetryEvent() - set_properties("TelemetryEvent", event, data) - return event - - -class MonitorHandler(object): - def __init__(self, distro): - self.distro = distro - self.sysinfo = [] - - def run(self): - event_thread = threading.Thread(target = self.daemon) - event_thread.setDaemon(True) - event_thread.start() - - def init_sysinfo(self): - osversion = "{0}:{1}-{2}-{3}:{4}".format(platform.system(), - DISTRO_NAME, - DISTRO_VERSION, - DISTRO_CODE_NAME, - platform.release()) - - - self.sysinfo.append(TelemetryEventParam("OSVersion", osversion)) - self.sysinfo.append(TelemetryEventParam("GAVersion", AGENT_LONG_VERSION)) - - try: - ram = self.distro.osutil.get_total_mem() - processors = self.distro.osutil.get_processor_cores() - self.sysinfo.append(TelemetryEventParam("RAM", ram)) - self.sysinfo.append(TelemetryEventParam("Processors", processors)) - except OSUtilError as e: - logger.warn("Failed to get system info: {0}", e) - - try: - protocol = self.distro.protocol_util.get_protocol() - vminfo = protocol.get_vminfo() - self.sysinfo.append(TelemetryEventParam("VMName", - vminfo.vmName)) - self.sysinfo.append(TelemetryEventParam("TenantName", - vminfo.tenantName)) - self.sysinfo.append(TelemetryEventParam("RoleName", - vminfo.roleName)) - self.sysinfo.append(TelemetryEventParam("RoleInstanceName", - vminfo.roleInstanceName)) - self.sysinfo.append(TelemetryEventParam("ContainerId", - vminfo.containerId)) - except ProtocolError as e: - logger.warn("Failed to get system info: {0}", e) - - def collect_event(self, evt_file_name): - try: - logger.verb("Found event file: {0}", evt_file_name) - with open(evt_file_name, "rb") as evt_file: - #if fail to open or delete the file, throw exception - data_str = evt_file.read().decode("utf-8",'ignore') - logger.verb("Processed event file: {0}", evt_file_name) - os.remove(evt_file_name) - return data_str - except IOError as e: - msg = "Failed to process {0}, {1}".format(evt_file_name, e) - raise EventError(msg) - - def collect_and_send_events(self): - event_list = TelemetryEventList() - event_dir = os.path.join(conf.get_lib_dir(), "events") - event_files = os.listdir(event_dir) - for event_file in event_files: - if not event_file.endswith(".tld"): - continue - event_file_path = os.path.join(event_dir, event_file) - try: - data_str = self.collect_event(event_file_path) - except EventError as e: - logger.error("{0}", e) - continue - - try: - event = parse_event(data_str) - event.parameters.extend(self.sysinfo) - event_list.events.append(event) - except (ValueError, ProtocolError) as e: - logger.warn("Failed to decode event file: {0}", e) - continue - - if len(event_list.events) == 0: - return - - try: - protocol = self.distro.protocol_util.get_protocol() - protocol.report_event(event_list) - except ProtocolError as e: - logger.error("{0}", e) - - def daemon(self): - self.init_sysinfo() - last_heartbeat = datetime.datetime.min - period = datetime.timedelta(hours = 12) - while(True): - if (datetime.datetime.now()-last_heartbeat) > period: - last_heartbeat = datetime.datetime.now() - add_event(op=WALAEventOperation.HeartBeat, name="WALA", - is_success=True) - try: - self.collect_and_send_events() - except Exception as e: - logger.warn("Failed to send events: {0}", e) - time.sleep(60) diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/distro/default/osutil.py deleted file mode 100644 index 18ab2ba..0000000 --- a/azurelinuxagent/distro/default/osutil.py +++ /dev/null @@ -1,623 +0,0 @@ -# -# 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 azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -from azurelinuxagent.exception import OSUtilError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.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): - # for older distros create sudoers.d - if not os.path.isdir('/etc/sudoers.d/'): - # create the /etc/sudoers.d/ directory - os.mkdir('/etc/sudoers.d/') - # add the include of sudoers.d to the /etc/sudoers - sudoers = '\n' + '#includedir /etc/sudoers.d/\n' - fileutil.append_file('/etc/sudoers', sudoers) - sudoer = None - if nopasswd: - sudoer = "{0} ALL = (ALL) NOPASSWD\n".format(username) - else: - sudoer = "{0} ALL = (ALL) ALL\n".format(username) - fileutil.append_file('/etc/sudoers.d/waagent', sudoer) - fileutil.chmod('/etc/sudoers.d/waagent', 0o440) - - 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("Publich 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(): - return shellutil.run('chcon ' + con + ' ' + path) - - def set_ssh_client_alive_interval(self): - 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, "ClientAliveInterval", "180") - fileutil.write_file(conf_file_path, '\n'.join(conf_file)) - logger.info("Configured SSH client probing to keep connections alive.") - - 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) - fileutil.write_file(conf_file_path, "\n".join(conf_file)) - logger.info("Disabled SSH password-based authentication methods.") - - - def get_dvd_device(self, dev_dir='/dev'): - patten=r'(sr[0-9]|hd[c-z]|cdrom[0-9])' - for dvd in [re.match(patten, 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 = self.get_dvd_device() - mount_point = conf.get_dvd_mount_point() - mountlist = shellutil.run_get_output("mount")[1] - existing = self.get_mount_point(mountlist, dvd) - if existing is not None: #Already mounted - logger.info("{0} is already mounted at {1}", dvd, existing) - return - if not os.path.isdir(mount_point): - os.makedirs(mount_point) - - for retry in range(0, max_retry): - retcode = self.mount(dvd, mount_point, option="-o ro -t iso9660,udf", - 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 = 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 load_atappix_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(dvd, option, 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-loobback 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() - for i in range(0, struct_size * expected, struct_size): - iface=sock[i:i+16].split(b'\0', 1)[0] - if iface == b'lo': - continue - else: - break - return iface.decode('latin-1'), socket.inet_ntoa(sock[i+20:i+24]) - - 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'] - 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): - shellutil.run("ifdown {0} && ifup {1}".format(ifname, ifname)) - - 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) - #Remove user from suders - if os.path.isfile("/etc/suders.d/waagent"): - try: - content = fileutil.read_file("/etc/sudoers.d/waagent") - sudoers = content.split("\n") - sudoers = [x for x in sudoers if username not in x] - fileutil.write_file("/etc/sudoers.d/waagent", - "\n".join(sudoers)) - except IOError as e: - raise OSUtilError("Failed to remove sudoer: {0}".format(e)) - - 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 procerssor 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)) - diff --git a/azurelinuxagent/distro/default/protocolUtil.py b/azurelinuxagent/distro/default/protocolUtil.py deleted file mode 100644 index 34466cf..0000000 --- a/azurelinuxagent/distro/default/protocolUtil.py +++ /dev/null @@ -1,243 +0,0 @@ -# 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 os -import re -import shutil -import time -import threading -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import ProtocolError, OSUtilError, \ - ProtocolNotFoundError, DhcpError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.protocol.ovfenv import OvfEnv -from azurelinuxagent.protocol.wire import WireProtocol -from azurelinuxagent.protocol.metadata import MetadataProtocol, METADATA_ENDPOINT -import azurelinuxagent.utils.shellutil as shellutil - -OVF_FILE_NAME = "ovf-env.xml" - -#Tag file to indicate usage of metadata protocol -TAG_FILE_NAME = "useMetadataEndpoint.tag" - -PROTOCOL_FILE_NAME = "Protocol" - -#MAX retry times for protocol probing -MAX_RETRY = 360 - -PROBE_INTERVAL = 10 - -ENDPOINT_FILE_NAME = "WireServerEndpoint" - -class ProtocolUtil(object): - """ - ProtocolUtil handles initialization for protocol instance. 2 protocol types - are invoked, wire protocol and metadata protocols. - """ - def __init__(self, distro): - self.distro = distro - self.protocol = None - self.lock = threading.Lock() - - def copy_ovf_env(self): - """ - Copy ovf env file from dvd to hard disk. - Remove password before save it to the disk - """ - dvd_mount_point = conf.get_dvd_mount_point() - ovf_file_path_on_dvd = os.path.join(dvd_mount_point, OVF_FILE_NAME) - tag_file_path_on_dvd = os.path.join(dvd_mount_point, TAG_FILE_NAME) - try: - self.distro.osutil.mount_dvd() - ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True) - ovfenv = OvfEnv(ovfxml) - ovfxml = re.sub("<UserPassword>.*?<", "<UserPassword>*<", ovfxml) - ovf_file_path = os.path.join(conf.get_lib_dir(), OVF_FILE_NAME) - fileutil.write_file(ovf_file_path, ovfxml) - - if os.path.isfile(tag_file_path_on_dvd): - logger.info("Found {0} in provisioning ISO", TAG_FILE_NAME) - tag_file_path = os.path.join(conf.get_lib_dir(), TAG_FILE_NAME) - shutil.copyfile(tag_file_path_on_dvd, tag_file_path) - - except (OSUtilError, IOError) as e: - raise ProtocolError(ustr(e)) - - try: - self.distro.osutil.umount_dvd() - self.distro.osutil.eject_dvd() - except OSUtilError as e: - logger.warn(ustr(e)) - - return ovfenv - - def get_ovf_env(self): - """ - Load saved ovf-env.xml - """ - ovf_file_path = os.path.join(conf.get_lib_dir(), OVF_FILE_NAME) - if os.path.isfile(ovf_file_path): - xml_text = fileutil.read_file(ovf_file_path) - return OvfEnv(xml_text) - else: - raise ProtocolError("ovf-env.xml is missing.") - - def _get_wireserver_endpoint(self): - try: - file_path = os.path.join(conf.get_lib_dir(), ENDPOINT_FILE_NAME) - return fileutil.read_file(file_path) - except IOError as e: - raise OSUtilError(ustr(e)) - - def _set_wireserver_endpoint(self, endpoint): - try: - file_path = os.path.join(conf.get_lib_dir(), ENDPOINT_FILE_NAME) - fileutil.write_file(file_path, endpoint) - except IOError as e: - raise OSUtilError(ustr(e)) - - def _detect_wire_protocol(self): - endpoint = self.distro.dhcp_handler.endpoint - if endpoint is None: - logger.info("WireServer endpoint is not found. Rerun dhcp handler") - try: - self.distro.dhcp_handler.run() - except DhcpError as e: - raise ProtocolError(ustr(e)) - endpoint = self.distro.dhcp_handler.endpoint - - try: - protocol = WireProtocol(endpoint) - protocol.detect() - self._set_wireserver_endpoint(endpoint) - return protocol - except ProtocolError as e: - logger.info("WireServer is not responding. Reset endpoint") - self.distro.dhcp_handler.endpoint = None - raise e - - def _detect_metadata_protocol(self): - protocol = MetadataProtocol() - protocol.detect() - - #Only allow root access METADATA_ENDPOINT - self.distro.osutil.set_admin_access_to_ip(METADATA_ENDPOINT) - - return protocol - - def _detect_protocol(self, protocols): - """ - Probe protocol endpoints in turn. - """ - protocol_file_path = os.path.join(conf.get_lib_dir(), PROTOCOL_FILE_NAME) - if os.path.isfile(protocol_file_path): - os.remove(protocol_file_path) - for retry in range(0, MAX_RETRY): - for protocol in protocols: - try: - if protocol == "WireProtocol": - return self._detect_wire_protocol() - - if protocol == "MetadataProtocol": - return self._detect_metadata_protocol() - - except ProtocolError as e: - logger.info("Protocol endpoint not found: {0}, {1}", - protocol, e) - - if retry < MAX_RETRY -1: - logger.info("Retry detect protocols: retry={0}", retry) - time.sleep(PROBE_INTERVAL) - raise ProtocolNotFoundError("No protocol found.") - - def _get_protocol(self): - """ - Get protocol instance based on previous detecting result. - """ - protocol_file_path = os.path.join(conf.get_lib_dir(), - PROTOCOL_FILE_NAME) - if not os.path.isfile(protocol_file_path): - raise ProtocolError("No protocl found") - - protocol_name = fileutil.read_file(protocol_file_path) - if protocol_name == "WireProtocol": - endpoint = self._get_wireserver_endpoint() - return WireProtocol(endpoint) - elif protocol_name == "MetadataProtocol": - return MetadataProtocol() - else: - raise ProtocolNotFoundError(("Unknown protocol: {0}" - "").format(protocol_name)) - - def detect_protocol(self): - """ - Detect protocol by endpoints - - :returns: protocol instance - """ - logger.info("Detect protocol endpoints") - protocols = ["WireProtocol", "MetadataProtocol"] - self.lock.acquire() - try: - if self.protocol is None: - self.protocol = self._detect_protocol(protocols) - return self.protocol - finally: - self.lock.release() - - def detect_protocol_by_file(self): - """ - Detect protocol by tag file. - - If a file "useMetadataEndpoint.tag" is found on provision iso, - metedata protocol will be used. No need to probe for wire protocol - - :returns: protocol instance - """ - logger.info("Detect protocol by file") - self.lock.acquire() - try: - tag_file_path = os.path.join(conf.get_lib_dir(), TAG_FILE_NAME) - if self.protocol is None: - protocols = [] - if os.path.isfile(tag_file_path): - protocols.append("MetadataProtocol") - else: - protocols.append("WireProtocol") - self.protocol = self._detect_protocol(protocols) - finally: - self.lock.release() - return self.protocol - - def get_protocol(self): - """ - Get protocol instance based on previous detecting result. - - :returns protocol instance - """ - self.lock.acquire() - try: - if self.protocol is None: - self.protocol = self._get_protocol() - return self.protocol - finally: - self.lock.release() - return self.protocol - diff --git a/azurelinuxagent/distro/default/provision.py b/azurelinuxagent/distro/default/provision.py deleted file mode 100644 index 695b82a..0000000 --- a/azurelinuxagent/distro/default/provision.py +++ /dev/null @@ -1,191 +0,0 @@ -# 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+ -# - -""" -Provision handler -""" - -import os -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError, ProtocolError, OSUtilError -from azurelinuxagent.protocol.restapi import ProvisionStatus -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.fileutil as fileutil - -CUSTOM_DATA_FILE="CustomData" - -class ProvisionHandler(object): - - def __init__(self, distro): - self.distro = distro - - def run(self): - #If provision is not enabled, return - if not conf.get_provision_enabled(): - logger.info("Provisioning is disabled. Skip.") - return - - provisioned = os.path.join(conf.get_lib_dir(), "provisioned") - if os.path.isfile(provisioned): - return - - logger.info("Run provision handler.") - logger.info("Copy ovf-env.xml.") - try: - ovfenv = self.distro.protocol_util.copy_ovf_env() - except ProtocolError as e: - self.report_event("Failed to copy ovf-env.xml: {0}".format(e)) - return - - self.distro.protocol_util.detect_protocol_by_file() - - self.report_not_ready("Provisioning", "Starting") - - try: - logger.info("Start provisioning") - self.provision(ovfenv) - fileutil.write_file(provisioned, "") - thumbprint = self.reg_ssh_host_key() - logger.info("Finished provisioning") - except ProvisionError as e: - logger.error("Provision failed: {0}", e) - self.report_not_ready("ProvisioningFailed", ustr(e)) - self.report_event(ustr(e)) - return - - self.report_ready(thumbprint) - self.report_event("Provision succeed", is_success=True) - - def reg_ssh_host_key(self): - keypair_type = conf.get_ssh_host_keypair_type() - if conf.get_regenerate_ssh_host_key(): - shellutil.run("rm -f /etc/ssh/ssh_host_*key*") - shellutil.run(("ssh-keygen -N '' -t {0} -f /etc/ssh/ssh_host_{1}_key" - "").format(keypair_type, keypair_type)) - thumbprint = self.get_ssh_host_key_thumbprint(keypair_type) - return thumbprint - - def get_ssh_host_key_thumbprint(self, keypair_type): - cmd = "ssh-keygen -lf /etc/ssh/ssh_host_{0}_key.pub".format(keypair_type) - ret = shellutil.run_get_output(cmd) - if ret[0] == 0: - return ret[1].rstrip().split()[1].replace(':', '') - else: - raise ProvisionError(("Failed to generate ssh host key: " - "ret={0}, out= {1}").format(ret[0], ret[1])) - - def provision(self, ovfenv): - logger.info("Handle ovf-env.xml.") - try: - logger.info("Set host name.") - self.distro.osutil.set_hostname(ovfenv.hostname) - - logger.info("Publish host name.") - self.distro.osutil.publish_hostname(ovfenv.hostname) - - self.config_user_account(ovfenv) - - self.save_customdata(ovfenv) - - if conf.get_delete_root_password(): - self.distro.osutil.del_root_password() - - except OSUtilError as e: - raise ProvisionError("Failed to handle ovf-env.xml: {0}".format(e)) - - def config_user_account(self, ovfenv): - logger.info("Create user account if not exists") - self.distro.osutil.useradd(ovfenv.username) - - if ovfenv.user_password is not None: - logger.info("Set user password.") - crypt_id = conf.get_password_cryptid() - salt_len = conf.get_password_crypt_salt_len() - self.distro.osutil.chpasswd(ovfenv.username, ovfenv.user_password, - crypt_id=crypt_id, salt_len=salt_len) - - logger.info("Configure sudoer") - self.distro.osutil.conf_sudoer(ovfenv.username, ovfenv.user_password is None) - - logger.info("Configure sshd") - self.distro.osutil.conf_sshd(ovfenv.disable_ssh_password_auth) - - #Disable selinux temporary - sel = self.distro.osutil.is_selinux_enforcing() - if sel: - self.distro.osutil.set_selinux_enforce(0) - - self.deploy_ssh_pubkeys(ovfenv) - self.deploy_ssh_keypairs(ovfenv) - - if sel: - self.distro.osutil.set_selinux_enforce(1) - - self.distro.osutil.restart_ssh_service() - - def save_customdata(self, ovfenv): - customdata = ovfenv.customdata - if customdata is None: - return - - logger.info("Save custom data") - lib_dir = conf.get_lib_dir() - if conf.get_decode_customdata(): - customdata= self.distro.osutil.decode_customdata(customdata) - customdata_file = os.path.join(lib_dir, CUSTOM_DATA_FILE) - fileutil.write_file(customdata_file, customdata) - - if conf.get_execute_customdata(): - logger.info("Execute custom data") - os.chmod(customdata_file, 0o700) - shellutil.run(customdata_file) - - def deploy_ssh_pubkeys(self, ovfenv): - for pubkey in ovfenv.ssh_pubkeys: - logger.info("Deploy ssh public key.") - self.distro.osutil.deploy_ssh_pubkey(ovfenv.username, pubkey) - - def deploy_ssh_keypairs(self, ovfenv): - for keypair in ovfenv.ssh_keypairs: - logger.info("Deploy ssh key pairs.") - self.distro.osutil.deploy_ssh_keypair(ovfenv.username, keypair) - - def report_event(self, message, is_success=False): - add_event(name="WALA", message=message, is_success=is_success, - op=WALAEventOperation.Provision) - - def report_not_ready(self, sub_status, description): - status = ProvisionStatus(status="NotReady", subStatus=sub_status, - description=description) - try: - protocol = self.distro.protocol_util.get_protocol() - protocol.report_provision_status(status) - except ProtocolError as e: - self.report_event(ustr(e)) - - def report_ready(self, thumbprint=None): - status = ProvisionStatus(status="Ready") - status.properties.certificateThumbprint = thumbprint - try: - protocol = self.distro.protocol_util.get_protocol() - protocol.report_provision_status(status) - except ProtocolError as e: - self.report_event(ustr(e)) - diff --git a/azurelinuxagent/distro/default/resourceDisk.py b/azurelinuxagent/distro/default/resourceDisk.py deleted file mode 100644 index a6c5232..0000000 --- a/azurelinuxagent/distro/default/resourceDisk.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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 os -import re -import threading -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -from azurelinuxagent.event import add_event, WALAEventOperation -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.exception import ResourceDiskError - -DATALOSS_WARNING_FILE_NAME="DATALOSS_WARNING_README.txt" -DATA_LOSS_WARNING="""\ -WARNING: THIS IS A TEMPORARY DISK. - -Any data stored on this drive is SUBJECT TO LOSS and THERE IS NO WAY TO RECOVER IT. - -Please do not use this disk for storing any personal or application data. - -For additional details to please refer to the MSDN documentation at : http://msdn.microsoft.com/en-us/library/windowsazure/jj672979.aspx -""" - -class ResourceDiskHandler(object): - def __init__(self, distro): - self.distro = distro - - def start_activate_resource_disk(self): - disk_thread = threading.Thread(target = self.run) - disk_thread.start() - - def run(self): - mount_point = None - if conf.get_resourcedisk_format(): - mount_point = self.activate_resource_disk() - if mount_point is not None and \ - conf.get_resourcedisk_enable_swap(): - self.enable_swap(mount_point) - - def activate_resource_disk(self): - logger.info("Activate resource disk") - try: - mount_point = conf.get_resourcedisk_mountpoint() - fs = conf.get_resourcedisk_filesystem() - mount_point = self.mount_resource_disk(mount_point, fs) - warning_file = os.path.join(mount_point, DATALOSS_WARNING_FILE_NAME) - try: - fileutil.write_file(warning_file, DATA_LOSS_WARNING) - except IOError as e: - logger.warn("Failed to write data loss warnning:{0}", e) - return mount_point - except ResourceDiskError as e: - logger.error("Failed to mount resource disk {0}", e) - add_event(name="WALA", is_success=False, message=ustr(e), - op=WALAEventOperation.ActivateResourceDisk) - - def enable_swap(self, mount_point): - logger.info("Enable swap") - try: - size_mb = conf.get_resourcedisk_swap_size_mb() - self.create_swap_space(mount_point, size_mb) - except ResourceDiskError as e: - logger.error("Failed to enable swap {0}", e) - - def mount_resource_disk(self, mount_point, fs): - device = self.distro.osutil.device_for_ide_port(1) - if device is None: - raise ResourceDiskError("unable to detect disk topology") - - device = "/dev/" + device - mountlist = shellutil.run_get_output("mount")[1] - existing = self.distro.osutil.get_mount_point(mountlist, device) - - if(existing): - logger.info("Resource disk {0}1 is already mounted", device) - return existing - - fileutil.mkdir(mount_point, mode=0o755) - - logger.info("Detect GPT...") - partition = device + "1" - ret = shellutil.run_get_output("parted {0} print".format(device)) - if ret[0]: - raise ResourceDiskError("({0}) {1}".format(device, ret[1])) - - if "gpt" in ret[1]: - logger.info("GPT detected") - logger.info("Get GPT partitions") - parts = [x for x in ret[1].split("\n") if re.match("^\s*[0-9]+", x)] - logger.info("Found more than {0} GPT partitions.", len(parts)) - if len(parts) > 1: - logger.info("Remove old GPT partitions") - for i in range(1, len(parts) + 1): - logger.info("Remove partition: {0}", i) - shellutil.run("parted {0} rm {1}".format(device, i)) - - logger.info("Create a new GPT partition using entire disk space") - shellutil.run("parted {0} mkpart primary 0% 100%".format(device)) - - logger.info("Format partition: {0} with fstype {1}",partition,fs) - shellutil.run("mkfs." + fs + " " + partition + " -F") - else: - logger.info("GPT not detected") - logger.info("Check fstype") - ret = shellutil.run_get_output("sfdisk -q -c {0} 1".format(device)) - if ret[1].rstrip() == "7" and fs != "ntfs": - logger.info("The partition is formatted with ntfs") - logger.info("Format partition: {0} with fstype {1}",partition,fs) - shellutil.run("sfdisk -c {0} 1 83".format(device)) - shellutil.run("mkfs." + fs + " " + partition + " -F") - - logger.info("Mount resource disk") - ret = shellutil.run("mount {0} {1}".format(partition, mount_point), - chk_err=False) - if ret: - logger.warn("Failed to mount resource disk. Retry mounting") - shellutil.run("mkfs." + fs + " " + partition + " -F") - ret = shellutil.run("mount {0} {1}".format(partition, mount_point)) - if ret: - raise ResourceDiskError("({0}) {1}".format(partition, ret)) - - logger.info("Resource disk ({0}) is mounted at {1} with fstype {2}", - device, mount_point, fs) - return mount_point - - def create_swap_space(self, mount_point, size_mb): - size_kb = size_mb * 1024 - size = size_kb * 1024 - swapfile = os.path.join(mount_point, 'swapfile') - swaplist = shellutil.run_get_output("swapon -s")[1] - - if swapfile in swaplist and os.path.getsize(swapfile) == size: - logger.info("Swap already enabled") - return - - if os.path.isfile(swapfile) and os.path.getsize(swapfile) != size: - logger.info("Remove old swap file") - shellutil.run("swapoff -a", chk_err=False) - os.remove(swapfile) - - if not os.path.isfile(swapfile): - logger.info("Create swap file") - shellutil.run(("dd if=/dev/zero of={0} bs=1024 " - "count={1}").format(swapfile, size_kb)) - shellutil.run("mkswap {0}".format(swapfile)) - if shellutil.run("swapon {0}".format(swapfile)): - raise ResourceDiskError("{0}".format(swapfile)) - logger.info("Enabled {0}KB of swap at {1}".format(size_kb, swapfile)) - diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/distro/default/scvmm.py deleted file mode 100644 index 4d083b4..0000000 --- a/azurelinuxagent/distro/default/scvmm.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 os -import subprocess -import azurelinuxagent.logger as logger - -VMM_CONF_FILE_NAME = "linuxosconfiguration.xml" -VMM_STARTUP_SCRIPT_NAME= "install" - -class ScvmmHandler(object): - def __init__(self, distro): - self.distro = distro - - def detect_scvmm_env(self): - logger.info("Detecting Microsoft System Center VMM Environment") - self.distro.osutil.mount_dvd(max_retry=1, chk_err=False) - mount_point = self.distro.osutil.get_dvd_mount_point() - found = os.path.isfile(os.path.join(mount_point, VMM_CONF_FILE_NAME)) - if found: - self.start_scvmm_agent() - else: - self.distro.osutil.umount_dvd(chk_err=False) - return found - - def start_scvmm_agent(self): - logger.info("Starting Microsoft System Center VMM Initialization " - "Process") - mount_point = self.distro.osutil.get_dvd_mount_point() - startup_script = os.path.join(mount_point, VMM_STARTUP_SCRIPT_NAME) - subprocess.Popen(["/bin/bash", startup_script, "-p " + mount_point]) - diff --git a/azurelinuxagent/distro/loader.py b/azurelinuxagent/distro/loader.py deleted file mode 100644 index 74ea9e7..0000000 --- a/azurelinuxagent/distro/loader.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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.logger as logger -from azurelinuxagent.utils.textutil import Version -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ - DISTRO_FULL_NAME -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.ubuntu.distro import UbuntuDistro, \ - Ubuntu14Distro, \ - Ubuntu12Distro, \ - UbuntuSnappyDistro -from azurelinuxagent.distro.redhat.distro import RedhatDistro, Redhat6xDistro -from azurelinuxagent.distro.coreos.distro import CoreOSDistro -from azurelinuxagent.distro.suse.distro import SUSE11Distro, SUSEDistro -from azurelinuxagent.distro.debian.distro import DebianDistro - -def get_distro(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 Ubuntu12Distro() - elif Version(distro_version) == Version("14.04") or \ - Version(distro_version) == Version("14.10"): - return Ubuntu14Distro() - elif distro_full_name == "Snappy Ubuntu Core": - return UbuntuSnappyDistro() - else: - return UbuntuDistro() - if distro_name == "coreos": - return CoreOSDistro() - 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 SUSE11Distro() - else: - return SUSEDistro() - elif distro_name == "debian": - return DebianDistro() - elif distro_name == "redhat" or distro_name == "centos" or \ - distro_name == "oracle": - if Version(distro_version) < Version("7"): - return Redhat6xDistro() - else: - return RedhatDistro() - else: - logger.warn("Unable to load distro implemetation for {0}.", distro_name) - logger.warn("Use default distro implemetation instead.") - return DefaultDistro() - diff --git a/azurelinuxagent/distro/redhat/__init__.py b/azurelinuxagent/distro/redhat/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/redhat/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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+ -# - diff --git a/azurelinuxagent/distro/redhat/distro.py b/azurelinuxagent/distro/redhat/distro.py deleted file mode 100644 index 2f128d7..0000000 --- a/azurelinuxagent/distro/redhat/distro.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.redhat.osutil import RedhatOSUtil, Redhat6xOSUtil -from azurelinuxagent.distro.coreos.deprovision import CoreOSDeprovisionHandler - -class Redhat6xDistro(DefaultDistro): - def __init__(self): - super(Redhat6xDistro, self).__init__() - self.osutil = Redhat6xOSUtil() - -class RedhatDistro(DefaultDistro): - def __init__(self): - super(RedhatDistro, self).__init__() - self.osutil = RedhatOSUtil() diff --git a/azurelinuxagent/distro/redhat/osutil.py b/azurelinuxagent/distro/redhat/osutil.py deleted file mode 100644 index 7f769a5..0000000 --- a/azurelinuxagent/distro/redhat/osutil.py +++ /dev/null @@ -1,115 +0,0 @@ -# -# 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.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr, bytebuffer -from azurelinuxagent.exception import OSUtilError, CryptError -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.utils.cryptutil import CryptUtil -from azurelinuxagent.distro.default.osutil 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)) - -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) diff --git a/azurelinuxagent/distro/suse/__init__.py b/azurelinuxagent/distro/suse/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/suse/__init__.py +++ b/azurelinuxagent/distro/suse/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/distro/suse/distro.py b/azurelinuxagent/distro/suse/distro.py deleted file mode 100644 index 5b39369..0000000 --- a/azurelinuxagent/distro/suse/distro.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.suse.osutil import SUSE11OSUtil, SUSEOSUtil - -class SUSE11Distro(DefaultDistro): - def __init__(self): - super(SUSE11Distro, self).__init__() - self.osutil = SUSE11OSUtil() - -class SUSEDistro(DefaultDistro): - def __init__(self): - super(SUSEDistro, self).__init__() - self.osutil = SUSEOSUtil() - diff --git a/azurelinuxagent/distro/suse/osutil.py b/azurelinuxagent/distro/suse/osutil.py deleted file mode 100644 index 8d6f5bf..0000000 --- a/azurelinuxagent/distro/suse/osutil.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# 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.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME -from azurelinuxagent.distro.default.osutil 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/distro/ubuntu/__init__.py b/azurelinuxagent/distro/ubuntu/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/ubuntu/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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+ -# - diff --git a/azurelinuxagent/distro/ubuntu/deprovision.py b/azurelinuxagent/distro/ubuntu/deprovision.py deleted file mode 100644 index da6e834..0000000 --- a/azurelinuxagent/distro/ubuntu/deprovision.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 os -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction - -def del_resolv(): - if os.path.realpath('/etc/resolv.conf') != '/run/resolvconf/resolv.conf': - logger.info("resolvconf is not configured. Removing /etc/resolv.conf") - fileutil.rm_files('/etc/resolv.conf') - else: - logger.info("resolvconf is enabled; leaving /etc/resolv.conf intact") - fileutil.rm_files('/etc/resolvconf/resolv.conf.d/tail', - '/etc/resolvconf/resolv.conf.d/originial') - - -class UbuntuDeprovisionHandler(DeprovisionHandler): - def __init__(self, distro): - super(UbuntuDeprovisionHandler, self).__init__(distro) - - def setup(self, deluser): - warnings, actions = super(UbuntuDeprovisionHandler, self).setup(deluser) - warnings.append("WARNING! Nameserver configuration in " - "/etc/resolvconf/resolv.conf.d/{tail,originial} " - "will be deleted.") - actions.append(DeprovisionAction(del_resolv)) - return warnings, actions - diff --git a/azurelinuxagent/distro/ubuntu/distro.py b/azurelinuxagent/distro/ubuntu/distro.py deleted file mode 100644 index f380f6c..0000000 --- a/azurelinuxagent/distro/ubuntu/distro.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.ubuntu.osutil import Ubuntu14OSUtil, \ - Ubuntu12OSUtil, \ - UbuntuOSUtil, \ - UbuntuSnappyOSUtil - -from azurelinuxagent.distro.ubuntu.provision import UbuntuProvisionHandler -from azurelinuxagent.distro.ubuntu.deprovision import UbuntuDeprovisionHandler - -class UbuntuDistro(DefaultDistro): - def __init__(self): - super(UbuntuDistro, self).__init__() - self.osutil = UbuntuOSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class Ubuntu12Distro(DefaultDistro): - def __init__(self): - super(Ubuntu12Distro, self).__init__() - self.osutil = Ubuntu12OSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class Ubuntu14Distro(DefaultDistro): - def __init__(self): - super(Ubuntu14Distro, self).__init__() - self.osutil = Ubuntu14OSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class UbuntuSnappyDistro(DefaultDistro): - def __init__(self): - super(UbuntuSnappyDistro, self).__init__() - self.osutil = UbuntuSnappyOSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) diff --git a/azurelinuxagent/distro/ubuntu/osutil.py b/azurelinuxagent/distro/ubuntu/osutil.py deleted file mode 100644 index cc4b8ef..0000000 --- a/azurelinuxagent/distro/ubuntu/osutil.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# 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.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil 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) - -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' - - def remove_rules_files(self, rules_files=""): - pass - - def restore_rules_files(self, rules_files=""): - pass diff --git a/azurelinuxagent/distro/ubuntu/provision.py b/azurelinuxagent/distro/ubuntu/provision.py deleted file mode 100644 index 330e057..0000000 --- a/azurelinuxagent/distro/ubuntu/provision.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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 os -import time -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -import azurelinuxagent.protocol.ovfenv as ovfenv -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError, ProtocolError -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.distro.default.provision import ProvisionHandler - -""" -On ubuntu image, provision could be disabled. -""" -class UbuntuProvisionHandler(ProvisionHandler): - def __init__(self, distro): - self.distro = distro - - def run(self): - #If provision is enabled, run default provision handler - if conf.get_provision_enabled(): - super(UbuntuProvisionHandler, self).run() - return - - logger.info("run Ubuntu provision handler") - provisioned = os.path.join(conf.get_lib_dir(), "provisioned") - if os.path.isfile(provisioned): - return - - logger.info("Waiting cloud-init to copy ovf-env.xml.") - self.wait_for_ovfenv() - - protocol = self.distro.protocol_util.detect_protocol() - self.report_not_ready("Provisioning", "Starting") - logger.info("Sleep 15 seconds to prevent throttling") - time.sleep(15) #Sleep to prevent throttling - try: - logger.info("Wait for ssh host key to be generated.") - thumbprint = self.wait_for_ssh_host_key() - fileutil.write_file(provisioned, "") - logger.info("Finished provisioning") - - except ProvisionError as e: - logger.error("Provision failed: {0}", e) - self.report_not_ready("ProvisioningFailed", ustr(e)) - self.report_event(ustr(e)) - return - - self.report_ready(thumbprint) - self.report_event("Provision succeed", is_success=True) - - def wait_for_ovfenv(self, max_retry=60): - """ - Wait for cloud-init to copy ovf-env.xml file from provision ISO - """ - for retry in range(0, max_retry): - try: - self.distro.protocol_util.get_ovf_env() - return - except ProtocolError: - if retry < max_retry - 1: - logger.info("Wait for cloud-init to copy ovf-env.xml") - time.sleep(5) - raise ProvisionError("ovf-env.xml is not copied") - - def wait_for_ssh_host_key(self, max_retry=60): - """ - Wait for cloud-init to generate ssh host key - """ - kepair_type = conf.get_ssh_host_keypair_type() - path = '/etc/ssh/ssh_host_{0}_key'.format(kepair_type) - for retry in range(0, max_retry): - if os.path.isfile(path): - return self.get_ssh_host_key_thumbprint(kepair_type) - if retry < max_retry - 1: - logger.info("Wait for ssh host key be generated: {0}", path) - time.sleep(5) - raise ProvisionError("Ssh hsot key is not generated.") |