summaryrefslogtreecommitdiff
path: root/azurelinuxagent/pa/rdma
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/pa/rdma')
-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
4 files changed, 392 insertions, 0 deletions
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