From 2a6a01e1227ada38d3677a4a04edfc33414f29e4 Mon Sep 17 00:00:00 2001 From: Michael Scherer Date: Sat, 18 Apr 2015 22:33:55 -0400 Subject: [PATCH] Port some changes that occured on v1 get_facts modules to v2 - 5c64956a7 Set distribution, release, and version for NetBSD - ec01e071d adjusted for the possibility of lsblk not existing for fact gathering - d4eddabb2 Patch for bug #10485 - ansible_distribution fact populates as 'RedHat' on Oracle Linux systems - 7813ffd71 Adding uptime_seconds fact for linux and darwin platforms - 29cca0191 Adding oVirt recognition for oVirt guests. - d0197195e Handle /etc/os-release files with 'Raspbian' in them - 58a5f8dfa Pulls machine id in ansible facts - 1968f9969 Wrong OS_FAMILY declaration for openSUSE - 5dec45e24 Fix wrong distribution facts on SLES/openSUSE and a few others --- v2/ansible/module_utils/facts.py | 199 ++++++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 29 deletions(-) diff --git a/v2/ansible/module_utils/facts.py b/v2/ansible/module_utils/facts.py index 5844c4f678..5d70df4294 100644 --- a/v2/ansible/module_utils/facts.py +++ b/v2/ansible/module_utils/facts.py @@ -87,7 +87,8 @@ class Facts(object): _I386RE = re.compile(r'i([3456]86|86pc)') # For the most part, we assume that platform.dist() will tell the truth. # This is the fallback to handle unknowns or exceptions - OSDIST_LIST = ( ('/etc/redhat-release', 'RedHat'), + OSDIST_LIST = ( ('/etc/oracle-release', 'Oracle Linux'), + ('/etc/redhat-release', 'RedHat'), ('/etc/vmware-release', 'VMwareESX'), ('/etc/openwrt_release', 'OpenWrt'), ('/etc/system-release', 'OtherLinux'), @@ -170,9 +171,14 @@ class Facts(object): if self.facts['system'] == 'Linux': self.get_distribution_facts() elif self.facts['system'] == 'AIX': - rc, out, err = module.run_command("/usr/sbin/bootinfo -p") - data = out.split('\n') - self.facts['architecture'] = data[0] + try: + rc, out, err = module.run_command("/usr/sbin/bootinfo -p") + data = out.split('\n') + self.facts['architecture'] = data[0] + except: + self.facts['architecture'] = 'Not Available' + elif self.facts['system'] == 'OpenBSD': + self.facts['architecture'] = platform.uname()[5] def get_local_facts(self): @@ -229,8 +235,8 @@ class Facts(object): RedHat = 'RedHat', Fedora = 'RedHat', CentOS = 'RedHat', Scientific = 'RedHat', SLC = 'RedHat', Ascendos = 'RedHat', CloudLinux = 'RedHat', PSBM = 'RedHat', OracleLinux = 'RedHat', OVS = 'RedHat', OEL = 'RedHat', Amazon = 'RedHat', - XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', SLES = 'Suse', - SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo', + XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', Raspbian = 'Debian', SLES = 'Suse', + SLED = 'Suse', openSUSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo', Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake', Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris', SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin', @@ -261,6 +267,10 @@ class Facts(object): self.facts['distribution'] = 'FreeBSD' self.facts['distribution_release'] = platform.release() self.facts['distribution_version'] = platform.version() + elif self.facts['system'] == 'NetBSD': + self.facts['distribution'] = 'NetBSD' + self.facts['distribution_release'] = platform.release() + self.facts['distribution_version'] = platform.version() elif self.facts['system'] == 'OpenBSD': self.facts['distribution'] = 'OpenBSD' self.facts['distribution_release'] = platform.release() @@ -284,6 +294,13 @@ class Facts(object): # Once we determine the value is one of these distros # we trust the values are always correct break + elif name == 'Oracle Linux': + data = get_file_content(path) + if 'Oracle Linux' in data: + self.facts['distribution'] = name + else: + self.facts['distribution'] = data.split()[0] + break elif name == 'RedHat': data = get_file_content(path) if 'Red Hat' in data: @@ -354,24 +371,49 @@ class Facts(object): data = get_file_content(path) if 'suse' in data.lower(): if path == '/etc/os-release': - release = re.search("PRETTY_NAME=[^(]+ \(?([^)]+?)\)", data) - distdata = get_file_content(path).split('\n')[0] - self.facts['distribution'] = distdata.split('=')[1] - if release: - self.facts['distribution_release'] = release.groups()[0] - break + for line in data.splitlines(): + distribution = re.search("^NAME=(.*)", line) + if distribution: + self.facts['distribution'] = distribution.group(1).strip('"') + distribution_version = re.search('^VERSION_ID="?([0-9]+\.?[0-9]*)"?', line) # example pattern are 13.04 13.0 13 + if distribution_version: + self.facts['distribution_version'] = distribution_version.group(1) + if 'open' in data.lower(): + release = re.search("^PRETTY_NAME=[^(]+ \(?([^)]+?)\)", line) + if release: + self.facts['distribution_release'] = release.groups()[0] + elif 'enterprise' in data.lower(): + release = re.search('^VERSION_ID="?[0-9]+\.?([0-9]*)"?', line) # SLES doesn't got funny release names + if release: + release = release.group(1) + else: + release = "0" # no minor number, so it is the first release + self.facts['distribution_release'] = release + break elif path == '/etc/SuSE-release': - data = data.splitlines() - distdata = get_file_content(path).split('\n')[0] - self.facts['distribution'] = distdata.split()[0] - for line in data: - release = re.search('CODENAME *= *([^\n]+)', line) - if release: - self.facts['distribution_release'] = release.groups()[0].strip() - break + if 'open' in data.lower(): + data = data.splitlines() + distdata = get_file_content(path).split('\n')[0] + self.facts['distribution'] = distdata.split()[0] + for line in data: + release = re.search('CODENAME *= *([^\n]+)', line) + if release: + self.facts['distribution_release'] = release.groups()[0].strip() + elif 'enterprise' in data.lower(): + lines = data.splitlines() + distribution = lines[0].split()[0] + if "Server" in data: + self.facts['distribution'] = "SLES" + elif "Desktop" in data: + self.facts['distribution'] = "SLED" + for line in lines: + release = re.search('PATCHLEVEL = ([0-9]+)', line) # SLES doesn't got funny release names + if release: + self.facts['distribution_release'] = release.group(1) + self.facts['distribution_version'] = self.facts['distribution_version'] + '.' + release.group(1) elif name == 'Debian': data = get_file_content(path) - if 'Debian' in data: + if 'Debian' in data or 'Raspbian' in data: release = re.search("PRETTY_NAME=[^(]+ \(?([^)]+?)\)", data) if release: self.facts['distribution_release'] = release.groups()[0] @@ -403,7 +445,10 @@ class Facts(object): self.facts['distribution_release'] = release.group(1).strip('"') else: self.facts['distribution'] = name - + machine_id = get_file_content("/var/lib/dbus/machine-id") or get_file_content("/etc/machine-id") + if machine_id: + machine_id = machine_id.split('\n')[0] + self.facts["machine_id"] = machine_id self.facts['os_family'] = self.facts['distribution'] if self.facts['distribution'] in OS_FAMILY: self.facts['os_family'] = OS_FAMILY[self.facts['distribution']] @@ -462,7 +507,7 @@ class Facts(object): if rc == 0: self.facts['lsb'] = {} for line in out.split('\n'): - if len(line) < 1: + if len(line) < 1 or ':' not in line: continue value = line.split(':', 1)[1].strip() if 'LSB Version:' in line: @@ -635,6 +680,7 @@ class LinuxHardware(Hardware): self.get_memory_facts() self.get_dmi_facts() self.get_device_facts() + self.get_uptime_facts() try: self.get_mount_facts() except TimeoutError: @@ -855,13 +901,14 @@ class LinuxHardware(Hardware): size_available = statvfs_result.f_bsize * (statvfs_result.f_bavail) except OSError, e: continue - lsblkPath = module.get_bin_path("lsblk") - rc, out, err = module.run_command("%s -ln --output UUID %s" % (lsblkPath, fields[0]), use_unsafe_shell=True) - if rc == 0: - uuid = out.strip() - else: - uuid = 'NA' + uuid = 'NA' + lsblkPath = module.get_bin_path("lsblk") + if lsblkPath: + rc, out, err = module.run_command("%s -ln --output UUID %s" % (lsblkPath, fields[0]), use_unsafe_shell=True) + + if rc == 0: + uuid = out.strip() self.facts['mounts'].append( {'mount': fields[1], @@ -973,6 +1020,9 @@ class LinuxHardware(Hardware): self.facts['devices'][diskname] = d + def get_uptime_facts(self): + uptime_seconds_string = get_file_content('/proc/uptime').split(' ')[0] + self.facts['uptime_seconds'] = int(float(uptime_seconds_string)) class SunOSHardware(Hardware): """ @@ -987,6 +1037,10 @@ class SunOSHardware(Hardware): def populate(self): self.get_cpu_facts() self.get_memory_facts() + try: + self.get_mount_facts() + except TimeoutError: + pass return self.facts def get_cpu_facts(self): @@ -1047,6 +1101,17 @@ class SunOSHardware(Hardware): self.facts['swap_allocated_mb'] = allocated / 1024 self.facts['swap_reserved_mb'] = reserved / 1024 + @timeout(10) + def get_mount_facts(self): + self.facts['mounts'] = [] + # For a detailed format description see mnttab(4) + # special mount_point fstype options time + fstab = get_file_content('/etc/mnttab') + if fstab: + for line in fstab.split('\n'): + fields = line.rstrip('\n').split('\t') + self.facts['mounts'].append({'mount': fields[1], 'device': fields[0], 'fstype' : fields[2], 'options': fields[3], 'time': fields[4]}) + class OpenBSDHardware(Hardware): """ OpenBSD-specific subclass of Hardware. Defines memory, CPU and device facts: @@ -1071,6 +1136,7 @@ class OpenBSDHardware(Hardware): self.get_memory_facts() self.get_processor_facts() self.get_device_facts() + self.get_mount_facts() return self.facts def get_sysctl(self): @@ -1083,6 +1149,19 @@ class OpenBSDHardware(Hardware): sysctl[key] = value.strip() return sysctl + @timeout(10) + def get_mount_facts(self): + self.facts['mounts'] = [] + fstab = get_file_content('/etc/fstab') + if fstab: + for line in fstab.split('\n'): + if line.startswith('#') or line.strip() == '': + continue + fields = re.sub(r'\s+',' ',line.rstrip('\n')).split() + if fields[1] == 'none' or fields[3] == 'xx': + continue + self.facts['mounts'].append({'mount': fields[1], 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]}) + def get_memory_facts(self): # Get free memory. vmstat output looks like: # procs memory page disks traps cpu @@ -2133,7 +2212,40 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): self.parse_inet6_line(words, current_if, ips) else: self.parse_unknown_line(words, current_if, ips) + uname_path = module.get_bin_path('uname') + if uname_path: + rc, out, err = module.run_command([uname_path, '-W']) + # don't bother with wpars it does not work + # zero means not in wpar + if out.split()[0] == '0': + if current_if['macaddress'] == 'unknown' and re.match('^en', current_if['device']): + entstat_path = module.get_bin_path('entstat') + if entstat_path: + rc, out, err = module.run_command([entstat_path, current_if['device'] ]) + if rc != 0: + break + for line in out.split('\n'): + if not line: + pass + buff = re.match('^Hardware Address: (.*)', line) + if buff: + current_if['macaddress'] = buff.group(1) + buff = re.match('^Device Type:', line) + if buff and re.match('.*Ethernet', line): + current_if['type'] = 'ether' + # device must have mtu attribute in ODM + if 'mtu' not in current_if: + lsattr_path = module.get_bin_path('lsattr') + if lsattr_path: + rc, out, err = module.run_command([lsattr_path,'-El', current_if['device'] ]) + if rc != 0: + break + for line in out.split('\n'): + if line: + words = line.split() + if words[0] == 'mtu': + current_if['mtu'] = words[1] return interfaces, ips # AIX 'ifconfig -a' does not inform about MTU, so remove current_if['mtu'] here @@ -2360,6 +2472,11 @@ class LinuxVirtual(Virtual): self.facts['virtualization_role'] = 'guest' return + if sys_vendor == 'oVirt': + self.facts['virtualization_type'] = 'kvm' + self.facts['virtualization_role'] = 'guest' + return + if os.path.exists('/proc/self/status'): for line in get_file_lines('/proc/self/status'): if re.match('^VxID: \d+', line): @@ -2514,6 +2631,30 @@ class SunOSVirtual(Virtual): if 'VirtualBox' in line: self.facts['virtualization_type'] = 'virtualbox' self.facts['virtualization_role'] = 'guest' + # Detect domaining on Sparc hardware + if os.path.exists("/usr/sbin/virtinfo"): + # The output of virtinfo is different whether we are on a machine with logical + # domains ('LDoms') on a T-series or domains ('Domains') on a M-series. Try LDoms first. + rc, out, err = module.run_command("/usr/sbin/virtinfo -p") + # The output contains multiple lines with different keys like this: + # DOMAINROLE|impl=LDoms|control=false|io=false|service=false|root=false + # The output may also be not formated and the returncode is set to 0 regardless of the error condition: + # virtinfo can only be run from the global zone + try: + for line in out.split('\n'): + fields = line.split('|') + if( fields[0] == 'DOMAINROLE' and fields[1] == 'impl=LDoms' ): + self.facts['virtualization_type'] = 'ldom' + self.facts['virtualization_role'] = 'guest' + hostfeatures = [] + for field in fields[2:]: + arg = field.split('=') + if( arg[1] == 'true' ): + hostfeatures.append(arg[0]) + if( len(hostfeatures) > 0 ): + self.facts['virtualization_role'] = 'host (' + ','.join(hostfeatures) + ')' + except ValueError, e: + pass def get_file_content(path, default=None, strip=True): data = default