summaryrefslogtreecommitdiff
path: root/azurelinuxagent/pa
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/pa')
-rw-r--r--azurelinuxagent/pa/__init__.py17
-rw-r--r--azurelinuxagent/pa/deprovision/__init__.py20
-rw-r--r--azurelinuxagent/pa/deprovision/coreos.py34
-rw-r--r--azurelinuxagent/pa/deprovision/default.py131
-rw-r--r--azurelinuxagent/pa/deprovision/factory.py36
-rw-r--r--azurelinuxagent/pa/deprovision/ubuntu.py47
-rw-r--r--azurelinuxagent/pa/provision/__init__.py18
-rw-r--r--azurelinuxagent/pa/provision/default.py196
-rw-r--r--azurelinuxagent/pa/provision/factory.py32
-rw-r--r--azurelinuxagent/pa/provision/ubuntu.py98
-rw-r--r--azurelinuxagent/pa/rdma/__init__.py18
-rw-r--r--azurelinuxagent/pa/rdma/centos.py203
-rw-r--r--azurelinuxagent/pa/rdma/factory.py41
-rw-r--r--azurelinuxagent/pa/rdma/suse.py130
14 files changed, 1021 insertions, 0 deletions
diff --git a/azurelinuxagent/pa/__init__.py b/azurelinuxagent/pa/__init__.py
new file mode 100644
index 0000000..1ea2f38
--- /dev/null
+++ b/azurelinuxagent/pa/__init__.py
@@ -0,0 +1,17 @@
+# 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/pa/deprovision/__init__.py b/azurelinuxagent/pa/deprovision/__init__.py
new file mode 100644
index 0000000..de77168
--- /dev/null
+++ b/azurelinuxagent/pa/deprovision/__init__.py
@@ -0,0 +1,20 @@
+# 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.pa.deprovision.factory import get_deprovision_handler
+
+__all__ = ["get_deprovision_handler"]
diff --git a/azurelinuxagent/pa/deprovision/coreos.py b/azurelinuxagent/pa/deprovision/coreos.py
new file mode 100644
index 0000000..079a913
--- /dev/null
+++ b/azurelinuxagent/pa/deprovision/coreos.py
@@ -0,0 +1,34 @@
+# Microsoft Azure Linux Agent
+#
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+import azurelinuxagent.common.utils.fileutil as fileutil
+from azurelinuxagent.pa.deprovision.default import DeprovisionHandler, \
+ DeprovisionAction
+
+class CoreOSDeprovisionHandler(DeprovisionHandler):
+ def __init__(self):
+ super(CoreOSDeprovisionHandler, self).__init__()
+
+ 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/pa/deprovision/default.py b/azurelinuxagent/pa/deprovision/default.py
new file mode 100644
index 0000000..b570c31
--- /dev/null
+++ b/azurelinuxagent/pa/deprovision/default.py
@@ -0,0 +1,131 @@
+# Microsoft Azure Linux Agent
+#
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+import azurelinuxagent.common.conf as conf
+from azurelinuxagent.common.exception import ProtocolError
+from azurelinuxagent.common.future import read_input
+import azurelinuxagent.common.utils.fileutil as fileutil
+import azurelinuxagent.common.utils.shellutil as shellutil
+from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.protocol import get_protocol_util
+
+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):
+ self.osutil = get_osutil()
+ self.protocol_util = get_protocol_util()
+
+ 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.osutil.del_root_password))
+
+ def del_user(self, warnings, actions):
+
+ try:
+ ovfenv = self.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.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.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))
+
+ # For Freebsd
+ actions.append(DeprovisionAction(fileutil.rm_files, ["/var/db/dhclient.leases.hn0"]))
+
+ 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.osutil.set_hostname,
+ localhost))
+ actions.append(DeprovisionAction(self.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/pa/deprovision/factory.py b/azurelinuxagent/pa/deprovision/factory.py
new file mode 100644
index 0000000..dd01633
--- /dev/null
+++ b/azurelinuxagent/pa/deprovision/factory.py
@@ -0,0 +1,36 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+import azurelinuxagent.common.logger as logger
+from azurelinuxagent.common.utils.textutil import Version
+from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \
+ DISTRO_FULL_NAME
+
+from .default import DeprovisionHandler
+from .coreos import CoreOSDeprovisionHandler
+from .ubuntu import UbuntuDeprovisionHandler
+
+def get_deprovision_handler(distro_name=DISTRO_NAME,
+ distro_version=DISTRO_VERSION,
+ distro_full_name=DISTRO_FULL_NAME):
+ if distro_name == "ubuntu":
+ return UbuntuDeprovisionHandler()
+ if distro_name == "coreos":
+ return CoreOSDeprovisionHandler()
+
+ return DeprovisionHandler()
+
diff --git a/azurelinuxagent/pa/deprovision/ubuntu.py b/azurelinuxagent/pa/deprovision/ubuntu.py
new file mode 100644
index 0000000..14f90de
--- /dev/null
+++ b/azurelinuxagent/pa/deprovision/ubuntu.py
@@ -0,0 +1,47 @@
+# 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.common.logger as logger
+import azurelinuxagent.common.utils.fileutil as fileutil
+from azurelinuxagent.pa.deprovision.default 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):
+ super(UbuntuDeprovisionHandler, self).__init__()
+
+ 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/pa/provision/__init__.py b/azurelinuxagent/pa/provision/__init__.py
new file mode 100644
index 0000000..05f75ae
--- /dev/null
+++ b/azurelinuxagent/pa/provision/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+from azurelinuxagent.pa.provision.factory import get_provision_handler
diff --git a/azurelinuxagent/pa/provision/default.py b/azurelinuxagent/pa/provision/default.py
new file mode 100644
index 0000000..b07c147
--- /dev/null
+++ b/azurelinuxagent/pa/provision/default.py
@@ -0,0 +1,196 @@
+# 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.common.logger as logger
+from azurelinuxagent.common.future import ustr
+import azurelinuxagent.common.conf as conf
+from azurelinuxagent.common.event import add_event, WALAEventOperation
+from azurelinuxagent.common.exception import ProvisionError, ProtocolError, \
+ OSUtilError
+from azurelinuxagent.common.protocol.restapi import ProvisionStatus
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.fileutil as fileutil
+from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.protocol import get_protocol_util
+
+CUSTOM_DATA_FILE="CustomData"
+
+class ProvisionHandler(object):
+
+ def __init__(self):
+ self.osutil = get_osutil()
+ self.protocol_util = get_protocol_util()
+
+ 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.protocol_util.copy_ovf_env()
+ except ProtocolError as e:
+ self.report_event("Failed to copy ovf-env.xml: {0}".format(e))
+ return
+
+ self.protocol_util.get_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.osutil.set_hostname(ovfenv.hostname)
+
+ logger.info("Publish host name.")
+ self.osutil.publish_hostname(ovfenv.hostname)
+
+ self.config_user_account(ovfenv)
+
+ self.save_customdata(ovfenv)
+
+ if conf.get_delete_root_password():
+ self.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.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.osutil.chpasswd(ovfenv.username, ovfenv.user_password,
+ crypt_id=crypt_id, salt_len=salt_len)
+
+ logger.info("Configure sudoer")
+ self.osutil.conf_sudoer(ovfenv.username, nopasswd=ovfenv.user_password is None)
+
+ logger.info("Configure sshd")
+ self.osutil.conf_sshd(ovfenv.disable_ssh_password_auth)
+
+ #Disable selinux temporary
+ sel = self.osutil.is_selinux_enforcing()
+ if sel:
+ self.osutil.set_selinux_enforce(0)
+
+ self.deploy_ssh_pubkeys(ovfenv)
+ self.deploy_ssh_keypairs(ovfenv)
+
+ if sel:
+ self.osutil.set_selinux_enforce(1)
+
+ self.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.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.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.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.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.protocol_util.get_protocol()
+ protocol.report_provision_status(status)
+ except ProtocolError as e:
+ self.report_event(ustr(e))
+
diff --git a/azurelinuxagent/pa/provision/factory.py b/azurelinuxagent/pa/provision/factory.py
new file mode 100644
index 0000000..9bbe35c
--- /dev/null
+++ b/azurelinuxagent/pa/provision/factory.py
@@ -0,0 +1,32 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+import azurelinuxagent.common.logger as logger
+from azurelinuxagent.common.utils.textutil import Version
+from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \
+ DISTRO_FULL_NAME
+from .default import ProvisionHandler
+from .ubuntu import UbuntuProvisionHandler
+
+def get_provision_handler(distro_name=DISTRO_NAME,
+ distro_version=DISTRO_VERSION,
+ distro_full_name=DISTRO_FULL_NAME):
+ if distro_name == "ubuntu":
+ return UbuntuProvisionHandler()
+
+ return ProvisionHandler()
+
diff --git a/azurelinuxagent/pa/provision/ubuntu.py b/azurelinuxagent/pa/provision/ubuntu.py
new file mode 100644
index 0000000..c334f23
--- /dev/null
+++ b/azurelinuxagent/pa/provision/ubuntu.py
@@ -0,0 +1,98 @@
+# 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.common.logger as logger
+from azurelinuxagent.common.future import ustr
+import azurelinuxagent.common.conf as conf
+import azurelinuxagent.common.protocol.ovfenv as ovfenv
+from azurelinuxagent.common.event import add_event, WALAEventOperation
+from azurelinuxagent.common.exception import ProvisionError, ProtocolError
+import azurelinuxagent.common.utils.shellutil as shellutil
+import azurelinuxagent.common.utils.fileutil as fileutil
+from azurelinuxagent.pa.provision.default import ProvisionHandler
+
+"""
+On ubuntu image, provision could be disabled.
+"""
+class UbuntuProvisionHandler(ProvisionHandler):
+ def __init__(self):
+ super(UbuntuProvisionHandler, self).__init__()
+
+ 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.protocol_util.get_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.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
+ """
+ keypair_type = conf.get_ssh_host_keypair_type()
+ path = '/etc/ssh/ssh_host_{0}_key.pub'.format(keypair_type)
+ for retry in range(0, max_retry):
+ if os.path.isfile(path):
+ return self.get_ssh_host_key_thumbprint(keypair_type)
+ if retry < max_retry - 1:
+ logger.info("Wait for ssh host key be generated: {0}", path)
+ time.sleep(5)
+ raise ProvisionError("ssh host key is not generated.")
diff --git a/azurelinuxagent/pa/rdma/__init__.py b/azurelinuxagent/pa/rdma/__init__.py
new file mode 100644
index 0000000..dff0ba4
--- /dev/null
+++ b/azurelinuxagent/pa/rdma/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2016 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.pa.rdma.factory import get_rdma_handler
diff --git a/azurelinuxagent/pa/rdma/centos.py b/azurelinuxagent/pa/rdma/centos.py
new file mode 100644
index 0000000..c527e1b
--- /dev/null
+++ b/azurelinuxagent/pa/rdma/centos.py
@@ -0,0 +1,203 @@
+# 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 glob
+import os
+import re
+import azurelinuxagent.common.logger as logger
+import azurelinuxagent.common.utils.shellutil as shellutil
+from azurelinuxagent.common.rdma import RDMAHandler
+
+
+class CentOSRDMAHandler(RDMAHandler):
+ rdma_user_mode_package_name = 'microsoft-hyper-v-rdma'
+ rdma_kernel_mode_package_name = 'kmod-microsoft-hyper-v-rdma'
+ rdma_wrapper_package_name = 'msft-rdma-drivers'
+
+ hyper_v_package_name = "hypervkvpd"
+ hyper_v_package_name_new = "microsoft-hyper-v"
+
+ version_major = None
+ version_minor = None
+
+ def __init__(self, distro_version):
+ v = distro_version.split('.')
+ if len(v) < 2:
+ raise Exception('Unexpected centos version: %s' % distro_version)
+ self.version_major, self.version_minor = v[0], v[1]
+
+ def install_driver(self):
+ """
+ Install the KVP daemon and the appropriate RDMA driver package for the
+ RDMA firmware.
+ """
+ fw_version = RDMAHandler.get_rdma_version()
+ if not fw_version:
+ raise Exception('Cannot determine RDMA firmware version')
+ logger.info("RDMA: found firmware version: {0}".format(fw_version))
+ fw_version = self.get_int_rdma_version(fw_version)
+ installed_pkg = self.get_rdma_package_info()
+ if installed_pkg:
+ logger.info(
+ 'RDMA: driver package present: {0}'.format(installed_pkg))
+ if self.is_rdma_package_up_to_date(installed_pkg, fw_version):
+ logger.info('RDMA: driver package is up-to-date')
+ return
+ else:
+ logger.info('RDMA: driver package needs updating')
+ self.update_rdma_package(fw_version)
+ else:
+ logger.info('RDMA: driver package is NOT installed')
+ self.update_rdma_package(fw_version)
+
+ def is_rdma_package_up_to_date(self, pkg, fw_version):
+ # Example match (pkg name, -, followed by 3 segments, fw_version and -):
+ # - pkg=microsoft-hyper-v-rdma-4.1.0.142-20160323.x86_64
+ # - fw_version=142
+ pattern = '{0}-\d\.\d\.\d\.({1})-'.format(
+ self.rdma_user_mode_package_name, fw_version)
+ return re.match(pattern, pkg)
+
+ @staticmethod
+ def get_int_rdma_version(version):
+ s = version.split('.')
+ if len(s) == 0:
+ raise Exception('Unexpected RDMA firmware version: "%s"' % version)
+ return s[0]
+
+ def get_rdma_package_info(self):
+ """
+ Returns the installed rdma package name or None
+ """
+ ret, output = shellutil.run_get_output(
+ 'rpm -q %s' % self.rdma_user_mode_package_name, chk_err=False)
+ if ret != 0:
+ return None
+ return output
+
+ def update_rdma_package(self, fw_version):
+ logger.info("RDMA: updating RDMA packages")
+ self.refresh_repos()
+ self.force_install_package(self.rdma_wrapper_package_name)
+ self.install_rdma_drivers(fw_version)
+
+ def force_install_package(self, pkg_name):
+ """
+ Attempts to remove existing package and installs the package
+ """
+ logger.info('RDMA: Force installing package: %s' % pkg_name)
+ if self.uninstall_package(pkg_name) != 0:
+ logger.info('RDMA: Erasing package failed but will continue')
+ if self.install_package(pkg_name) != 0:
+ raise Exception('Failed to install package "{0}"'.format(pkg_name))
+ logger.info('RDMA: installation completed: %s' % pkg_name)
+
+ @staticmethod
+ def uninstall_package(pkg_name):
+ return shellutil.run('yum erase -y -q {0}'.format(pkg_name))
+
+ @staticmethod
+ def install_package(pkg_name):
+ return shellutil.run('yum install -y -q {0}'.format(pkg_name))
+
+ def refresh_repos(self):
+ logger.info("RDMA: refreshing yum repos")
+ if shellutil.run('yum clean all') != 0:
+ raise Exception('Cleaning yum repositories failed')
+ if shellutil.run('yum updateinfo') != 0:
+ raise Exception('Failed to act on yum repo update information')
+ logger.info("RDMA: repositories refreshed")
+
+ def install_rdma_drivers(self, fw_version):
+ """
+ Installs the drivers from /opt/rdma/rhel[Major][Minor] directory,
+ particularly the microsoft-hyper-v-rdma-* kmod-* and (no debuginfo or
+ src). Tries to uninstall them first.
+ """
+ pkg_dir = '/opt/microsoft/rdma/rhel{0}{1}'.format(
+ self.version_major, self.version_minor)
+ logger.info('RDMA: pkgs dir: {0}'.format(pkg_dir))
+ if not os.path.isdir(pkg_dir):
+ raise Exception('RDMA packages directory %s is missing' % pkg_dir)
+
+ pkgs = os.listdir(pkg_dir)
+ logger.info('RDMA: found %d files in package directory' % len(pkgs))
+
+ # Uninstal KVP daemon first (if exists)
+ self.uninstall_kvp_driver_package_if_exists()
+
+ # Install kernel mode driver (kmod-microsoft-hyper-v-rdma-*)
+ kmod_pkg = self.get_file_by_pattern(
+ pkgs, "%s-\d\.\d\.\d\.+(%s)-\d{8}\.x86_64.rpm" % (self.rdma_kernel_mode_package_name, fw_version))
+ if not kmod_pkg:
+ raise Exception("RDMA kernel mode package not found")
+ kmod_pkg_path = os.path.join(pkg_dir, kmod_pkg)
+ self.uninstall_pkg_and_install_from(
+ 'kernel mode', self.rdma_kernel_mode_package_name, kmod_pkg_path)
+
+ # Install user mode driver (microsoft-hyper-v-rdma-*)
+ umod_pkg = self.get_file_by_pattern(
+ pkgs, "%s-\d\.\d\.\d\.+(%s)-\d{8}\.x86_64.rpm" % (self.rdma_user_mode_package_name, fw_version))
+ if not umod_pkg:
+ raise Exception("RDMA user mode package not found")
+ umod_pkg_path = os.path.join(pkg_dir, umod_pkg)
+ self.uninstall_pkg_and_install_from(
+ 'user mode', self.rdma_user_mode_package_name, umod_pkg_path)
+
+ logger.info("RDMA: driver packages installed")
+ self.load_driver_module()
+ if not self.is_driver_loaded():
+ logger.info("RDMA: driver module is not loaded; reboot required")
+ self.reboot_system()
+ else:
+ logger.info("RDMA: kernel module is loaded")
+
+ @staticmethod
+ def get_file_by_pattern(list, pattern):
+ for l in list:
+ if re.match(pattern, l):
+ return l
+ return None
+
+ def uninstall_pkg_and_install_from(self, pkg_type, pkg_name, pkg_path):
+ logger.info(
+ "RDMA: Processing {0} driver: {1}".format(pkg_type, pkg_path))
+ logger.info("RDMA: Try to uninstall existing version: %s" % pkg_name)
+ if self.uninstall_package(pkg_name) == 0:
+ logger.info("RDMA: Successfully uninstaled %s" % pkg_name)
+ logger.info(
+ "RDMA: Installing {0} package from {1}".format(pkg_type, pkg_path))
+ if self.install_package(pkg_path) != 0:
+ raise Exception(
+ "Failed to install RDMA {0} package".format(pkg_type))
+
+ def uninstall_kvp_driver_package_if_exists(self):
+ kvp_pkgs = [self.hyper_v_package_name,
+ self.hyper_v_package_name_new]
+
+ for kvp_pkg in kvp_pkgs:
+ if shellutil.run("rpm -q %s" % kvp_pkg, chk_err=False) != 0:
+ logger.info(
+ "RDMA: kvp package %s does not exist, skipping" % kvp_pkg)
+ else:
+ logger.info('RDMA: erasing kvp package "%s"' % kvp_pkg)
+ if shellutil.run("yum erase -q -y %s" % kvp_pkg, chk_err=False) == 0:
+ logger.info("RDMA: successfully erased package")
+ else:
+ logger.error("RDMA: failed to erase package")
diff --git a/azurelinuxagent/pa/rdma/factory.py b/azurelinuxagent/pa/rdma/factory.py
new file mode 100644
index 0000000..535b3d3
--- /dev/null
+++ b/azurelinuxagent/pa/rdma/factory.py
@@ -0,0 +1,41 @@
+# Copyright 2016 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+import azurelinuxagent.common.logger as logger
+
+from azurelinuxagent.common.version import DISTRO_FULL_NAME, DISTRO_VERSION
+from azurelinuxagent.common.rdma import RDMAHandler
+from .suse import SUSERDMAHandler
+from .centos import CentOSRDMAHandler
+
+
+def get_rdma_handler(
+ distro_full_name=DISTRO_FULL_NAME,
+ distro_version=DISTRO_VERSION
+):
+ """Return the handler object for RDMA driver handling"""
+ if (
+ distro_full_name == 'SUSE Linux Enterprise Server' and
+ int(distro_version) > 11
+ ):
+ return SUSERDMAHandler()
+
+ if distro_full_name == 'CentOS Linux' or distro_full_name == 'CentOS':
+ return CentOSRDMAHandler(distro_version)
+
+ logger.info("No RDMA handler exists for distro='{0}' version='{1}'", distro_full_name, distro_version)
+ return RDMAHandler()
diff --git a/azurelinuxagent/pa/rdma/suse.py b/azurelinuxagent/pa/rdma/suse.py
new file mode 100644
index 0000000..f0d8d0f
--- /dev/null
+++ b/azurelinuxagent/pa/rdma/suse.py
@@ -0,0 +1,130 @@
+# 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 glob
+import os
+import azurelinuxagent.common.logger as logger
+import azurelinuxagent.common.utils.shellutil as shellutil
+from azurelinuxagent.common.rdma import RDMAHandler
+
+
+class SUSERDMAHandler(RDMAHandler):
+
+ def install_driver(self):
+ """Install the appropriate driver package for the RDMA firmware"""
+
+ fw_version = RDMAHandler.get_rdma_version()
+ if not fw_version:
+ error_msg = 'RDMA: Could not determine firmware version. '
+ error_msg += 'Therefore, no driver will be installed.'
+ logger.error(error_msg)
+ return
+ zypper_install = 'zypper -n in %s'
+ zypper_remove = 'zypper -n rm %s'
+ zypper_search = 'zypper se -s %s'
+ package_name = 'msft-rdma-kmp-default'
+ cmd = zypper_search % package_name
+ status, repo_package_info = shellutil.run_get_output(cmd)
+ driver_package_versions = []
+ driver_package_installed = False
+ for entry in repo_package_info.split('\n'):
+ if package_name in entry:
+ sections = entry.split('|')
+ if len(sections) < 4:
+ error_msg = 'RDMA: Unexpected output from"%s": "%s"'
+ logger.error(error_msg % (cmd, entry))
+ continue
+ installed = sections[0].strip()
+ version = sections[3].strip()
+ driver_package_versions.append(version)
+ if fw_version in version and installed == 'i':
+ info_msg = 'RDMA: Matching driver package "%s-%s" '
+ info_msg += 'is already installed, nothing to do.'
+ logger.info(info_msg % (package_name, version))
+ return True
+ if installed == 'i':
+ driver_package_installed = True
+
+ # If we get here the driver package is installed but the
+ # version doesn't match or no package is installed
+ requires_reboot = False
+ if driver_package_installed:
+ # Unloading the particular driver with rmmod does not work
+ # We have to reboot after the new driver is installed
+ if self.is_driver_loaded():
+ info_msg = 'RDMA: Currently loaded driver does not match the '
+ info_msg += 'firmware implementation, reboot will be required.'
+ logger.info(info_msg)
+ requires_reboot = True
+ logger.info("RDMA: removing package %s" % package_name)
+ cmd = zypper_remove % package_name
+ shellutil.run(cmd)
+ logger.info("RDMA: removed package %s" % package_name)
+
+ logger.info("RDMA: looking for fw version %s in packages" % fw_version)
+ for entry in driver_package_versions:
+ if not fw_version in version:
+ logger.info("Package '%s' is not a match." % entry)
+ else:
+ logger.info("Package '%s' is a match. Installing." % entry)
+ complete_name = '%s-%s' % (package_name, version)
+ cmd = zypper_install % complete_name
+ result = shellutil.run(cmd)
+ if result:
+ error_msg = 'RDMA: Failed install of package "%s" '
+ error_msg += 'from available repositories.'
+ logger.error(error_msg % complete_name)
+ msg = 'RDMA: Successfully installed "%s" from '
+ msg += 'configured repositories'
+ logger.info(msg % complete_name)
+ self.load_driver_module()
+ if requires_reboot:
+ self.reboot_system()
+ return True
+ else:
+ logger.info("RDMA: No suitable match in repos. Trying local.")
+ local_packages = glob.glob('/opt/microsoft/rdma/*.rpm')
+ for local_package in local_packages:
+ logger.info("Examining: %s" % local_package)
+ if local_package.endswith('.src.rpm'):
+ continue
+ if (
+ package_name in local_package and
+ fw_version in local_package
+ ):
+ logger.info("RDMA: Installing: %s" % local_package)
+ cmd = zypper_install % local_package
+ result = shellutil.run(cmd)
+ if result:
+ error_msg = 'RDMA: Failed install of package "%s" '
+ error_msg += 'from local package cache'
+ logger.error(error_msg % local_package)
+ break
+ msg = 'RDMA: Successfully installed "%s" from '
+ msg += 'local package cache'
+ logger.info(msg % (local_package))
+ self.load_driver_module()
+ if requires_reboot:
+ self.reboot_system()
+ return True
+ else:
+ error_msg = 'Unable to find driver package that matches '
+ error_msg += 'RDMA firmware version "%s"' % fw_version
+ logger.error(error_msg)
+ return