def collect(self, module=None, collected_facts=None): """ Currently NVMe is only supported in some Linux distributions. If NVMe is configured on the host then a file will have been created during the NVMe driver installation. This file holds the unique NQN of the host. Example of contents of /etc/nvme/hostnqn: # cat /etc/nvme/hostnqn nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d """ nvme_facts = {} nvme_facts['hostnqn'] = "" if sys.platform.startswith('linux'): for line in get_file_content('/etc/nvme/hostnqn', '').splitlines(): if line.startswith('#') or line.startswith( ';') or line.strip() == '': continue if line.startswith('nqn.'): nvme_facts['hostnqn'] = line break return nvme_facts
def get_uptime_facts(self): uptime_facts = {} uptime_file_content = get_file_content('/proc/uptime') if uptime_file_content: uptime_seconds_string = uptime_file_content.split(' ')[0] uptime_facts['uptime_seconds'] = int(float(uptime_seconds_string)) return uptime_facts
def collect(self, module=None, collected_facts=None): # NOTE: this is populated even if it is not set fips_facts = {} fips_facts['fips'] = False data = get_file_content('/proc/sys/crypto/fips_enabled') if data and data == '1': fips_facts['fips'] = True return fips_facts
def get_distribution_SunOS(self): sunos_facts = {} data = get_file_content('/etc/release').splitlines()[0] if 'Solaris' in data: # for solaris 10 uname_r will contain 5.10, for solaris 11 it will have 5.11 uname_r = get_uname(self.module, flags=['-r']) ora_prefix = '' if 'Oracle Solaris' in data: data = data.replace('Oracle ', '') ora_prefix = 'Oracle ' sunos_facts['distribution'] = data.split()[0] sunos_facts['distribution_version'] = data.split()[1] sunos_facts['distribution_release'] = ora_prefix + data sunos_facts['distribution_major_version'] = uname_r.split('.')[1].rstrip() return sunos_facts uname_v = get_uname(self.module, flags=['-v']) distribution_version = None if 'SmartOS' in data: sunos_facts['distribution'] = 'SmartOS' if _file_exists('/etc/product'): product_data = dict([l.split(': ', 1) for l in get_file_content('/etc/product').splitlines() if ': ' in l]) if 'Image' in product_data: distribution_version = product_data.get('Image').split()[-1] elif 'OpenIndiana' in data: sunos_facts['distribution'] = 'OpenIndiana' elif 'OmniOS' in data: sunos_facts['distribution'] = 'OmniOS' distribution_version = data.split()[-1] elif uname_v is not None and 'NexentaOS_' in uname_v: sunos_facts['distribution'] = 'Nexenta' distribution_version = data.split()[-1].lstrip('v') if sunos_facts.get('distribution', '') in ('SmartOS', 'OpenIndiana', 'OmniOS', 'Nexenta'): sunos_facts['distribution_release'] = data.strip() if distribution_version is not None: sunos_facts['distribution_version'] = distribution_version elif uname_v is not None: sunos_facts['distribution_version'] = uname_v.splitlines()[0].strip() return sunos_facts return sunos_facts
def collect(self, module=None, collected_facts=None): local_facts = {} local_facts['local'] = {} if not module: return local_facts fact_path = module.params.get('fact_path', None) if not fact_path or not os.path.exists(fact_path): return local_facts local = {} for fn in sorted(glob.glob(fact_path + '/*.fact')): # where it will sit under local facts fact_base = os.path.basename(fn).replace('.fact', '') if stat.S_IXUSR & os.stat(fn)[stat.ST_MODE]: # run it # try to read it as json first # if that fails read it with ConfigParser # if that fails, skip it try: rc, out, err = module.run_command(fn) except UnicodeError: fact = 'error loading fact - output of running %s was not utf-8' % fn local[fact_base] = fact local_facts['local'] = local module.warn(fact) return local_facts else: out = get_file_content(fn, default='') # load raw json fact = 'loading %s' % fact_base try: fact = json.loads(out) except ValueError: # load raw ini cp = configparser.ConfigParser() try: cp.readfp(StringIO(out)) except configparser.Error: fact = "error loading fact - please check content" module.warn(fact) else: fact = {} for sect in cp.sections(): if sect not in fact: fact[sect] = {} for opt in cp.options(sect): val = cp.get(sect, opt) fact[sect][opt] = val local[fact_base] = fact local_facts['local'] = local return local_facts
def parse_distribution_file_SUSE(self, name, data, path, collected_facts): suse_facts = {} if 'suse' not in data.lower(): return False, suse_facts # TODO: remove if tested without this if path == '/etc/os-release': for line in data.splitlines(): distribution = re.search("^NAME=(.*)", line) if distribution: suse_facts['distribution'] = distribution.group(1).strip('"') # example pattern are 13.04 13.0 13 distribution_version = re.search(r'^VERSION_ID="?([0-9]+\.?[0-9]*)"?', line) if distribution_version: suse_facts['distribution_version'] = distribution_version.group(1) suse_facts['distribution_major_version'] = distribution_version.group(1).split('.')[0] if 'open' in data.lower(): release = re.search(r'^VERSION_ID="?[0-9]+\.?([0-9]*)"?', line) if release: suse_facts['distribution_release'] = release.groups()[0] elif 'enterprise' in data.lower() and 'VERSION_ID' in line: # SLES doesn't got funny release names release = re.search(r'^VERSION_ID="?[0-9]+\.?([0-9]*)"?', line) if release.group(1): release = release.group(1) else: release = "0" # no minor number, so it is the first release suse_facts['distribution_release'] = release # Starting with SLES4SAP12 SP3 NAME reports 'SLES' instead of 'SLES_SAP' # According to SuSe Support (SR101182877871) we should use the CPE_NAME to detect SLES4SAP if re.search("^CPE_NAME=.*sles_sap.*$", line): suse_facts['distribution'] = 'SLES_SAP' elif path == '/etc/SuSE-release': if 'open' in data.lower(): data = data.splitlines() distdata = get_file_content(path).splitlines()[0] suse_facts['distribution'] = distdata.split()[0] for line in data: release = re.search('CODENAME *= *([^\n]+)', line) if release: suse_facts['distribution_release'] = release.groups()[0].strip() elif 'enterprise' in data.lower(): lines = data.splitlines() distribution = lines[0].split()[0] if "Server" in data: suse_facts['distribution'] = "SLES" elif "Desktop" in data: suse_facts['distribution'] = "SLED" for line in lines: release = re.search('PATCHLEVEL = ([0-9]+)', line) # SLES doesn't got funny release names if release: suse_facts['distribution_release'] = release.group(1) suse_facts['distribution_version'] = collected_facts['distribution_version'] + '.' + release.group(1) return True, suse_facts
def get_holders(self, block_dev_dict, sysdir): block_dev_dict['holders'] = [] if os.path.isdir(sysdir + "/holders"): for folder in os.listdir(sysdir + "/holders"): if not folder.startswith("dm-"): continue name = get_file_content(sysdir + "/holders/" + folder + "/dm/name") if name: block_dev_dict['holders'].append(name) else: block_dev_dict['holders'].append(folder)
def _mtab_entries(self): mtab_file = '/etc/mtab' if not os.path.exists(mtab_file): mtab_file = '/proc/mounts' mtab = get_file_content(mtab_file, '') mtab_entries = [] for line in mtab.splitlines(): fields = line.split() if len(fields) < 4: continue mtab_entries.append(fields) return mtab_entries
def get_mount_facts(self): mount_facts = {} mount_facts['mounts'] = [] fstab = get_file_content('/etc/fstab') if fstab: for line in fstab.splitlines(): if line.startswith('#') or line.strip() == '': continue fields = re.sub(r'\s+', ' ', line).split() mount_statvfs_info = get_mount_size(fields[1]) mount_info = {'mount': fields[1], 'device': fields[0], 'fstype': fields[2], 'options': fields[3]} mount_info.update(mount_statvfs_info) mount_facts['mounts'].append(mount_info) return mount_facts
def collect(self, module=None, collected_facts=None): dns_facts = {} # TODO: flatten dns_facts['dns'] = {} for line in get_file_content('/etc/resolv.conf', '').splitlines(): if line.startswith('#') or line.startswith( ';') or line.strip() == '': continue tokens = line.split() if len(tokens) == 0: continue if tokens[0] == 'nameserver': if 'nameservers' not in dns_facts['dns']: dns_facts['dns']['nameservers'] = [] for nameserver in tokens[1:]: dns_facts['dns']['nameservers'].append(nameserver) elif tokens[0] == 'domain': if len(tokens) > 1: dns_facts['dns']['domain'] = tokens[1] elif tokens[0] == 'search': dns_facts['dns']['search'] = [] for suffix in tokens[1:]: dns_facts['dns']['search'].append(suffix) elif tokens[0] == 'sortlist': dns_facts['dns']['sortlist'] = [] for address in tokens[1:]: dns_facts['dns']['sortlist'].append(address) elif tokens[0] == 'options': dns_facts['dns']['options'] = {} if len(tokens) > 1: for option in tokens[1:]: option_tokens = option.split(':', 1) if len(option_tokens) == 0: continue val = len( option_tokens) == 2 and option_tokens[1] or True dns_facts['dns']['options'][option_tokens[0]] = val return dns_facts
def collect(self, module=None, collected_facts=None): ssh_pub_key_facts = {} algos = ('dsa', 'rsa', 'ecdsa', 'ed25519') # list of directories to check for ssh keys # used in the order listed here, the first one with keys is used keydirs = ['/etc/ssh', '/etc/openssh', '/etc'] for keydir in keydirs: for algo in algos: factname = 'ssh_host_key_%s_public' % algo if factname in ssh_pub_key_facts: # a previous keydir was already successful, stop looking # for keys return ssh_pub_key_facts key_filename = '%s/ssh_host_%s_key.pub' % (keydir, algo) keydata = get_file_content(key_filename) if keydata is not None: (keytype, key) = keydata.split()[0:2] ssh_pub_key_facts[factname] = key ssh_pub_key_facts[factname + '_keytype'] = keytype return ssh_pub_key_facts
def get_mount_facts(self): mount_facts = {} mount_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.splitlines(): fields = line.split('\t') mount_statvfs_info = get_mount_size(fields[1]) mount_info = { 'mount': fields[1], 'device': fields[0], 'fstype': fields[2], 'options': fields[3], 'time': fields[4] } mount_info.update(mount_statvfs_info) mount_facts['mounts'].append(mount_info) return mount_facts
def get_cpu_facts(self): cpu_facts = {} cpu_facts['processor'] = [] sysctl = self.module.get_bin_path('sysctl') if sysctl: rc, out, err = self.module.run_command("%s -n hw.ncpu" % sysctl, check_rc=False) cpu_facts['processor_count'] = out.strip() dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT) if not dmesg_boot: try: rc, dmesg_boot, err = self.module.run_command(self.module.get_bin_path("dmesg"), check_rc=False) except Exception: dmesg_boot = '' for line in dmesg_boot.splitlines(): if 'CPU:' in line: cpu = re.sub(r'CPU:\s+', r"", line) cpu_facts['processor'].append(cpu.strip()) if 'Logical CPUs per core' in line: cpu_facts['processor_cores'] = line.split()[4] return cpu_facts
def get_virtual_facts(self): virtual_facts = {} # Set empty values as default virtual_facts['virtualization_type'] = '' virtual_facts['virtualization_role'] = '' virtual_product_facts = self.detect_virt_product('hw.product') virtual_facts.update(virtual_product_facts) if virtual_facts['virtualization_type'] == '': virtual_vendor_facts = self.detect_virt_vendor('hw.vendor') virtual_facts.update(virtual_vendor_facts) # Check the dmesg if vmm(4) attached, indicating the host is # capable of virtualization. dmesg_boot = get_file_content(OpenBSDVirtual.DMESG_BOOT) for line in dmesg_boot.splitlines(): match = re.match('^vmm0 at mainbus0: (SVM/RVI|VMX/EPT)$', line) if match: virtual_facts['virtualization_type'] = 'vmm' virtual_facts['virtualization_role'] = 'host' return virtual_facts
def collect(self, module=None, collected_facts=None): facts_dict = {} if not module: return facts_dict collected_facts = collected_facts or {} service_mgr_name = None # TODO: detect more custom init setups like bootscripts, dmd, s6, Epoch, etc # also other OSs other than linux might need to check across several possible candidates # Mapping of proc_1 values to more useful names proc_1_map = { 'procd': 'openwrt_init', 'runit-init': 'runit', 'svscan': 'svc', 'openrc-init': 'openrc', } # try various forms of querying pid 1 proc_1 = get_file_content('/proc/1/comm') if proc_1 is None: # FIXME: return code isnt checked # FIXME: if stdout is empty string, odd things # FIXME: other code seems to think we could get proc_1 == None past this point rc, proc_1, err = module.run_command("ps -p 1 -o comm|tail -n 1", use_unsafe_shell=True) # If the output of the command starts with what looks like a PID, then the 'ps' command # probably didn't work the way we wanted, probably because it's busybox if re.match(r' *[0-9]+ ', proc_1): proc_1 = None # The ps command above may return "COMMAND" if the user cannot read /proc, e.g. with grsecurity if proc_1 == "COMMAND\n": proc_1 = None # FIXME: empty string proc_1 staus empty string if proc_1 is not None: proc_1 = os.path.basename(proc_1) proc_1 = to_native(proc_1) proc_1 = proc_1.strip() if proc_1 is not None and (proc_1 == 'init' or proc_1.endswith('sh')): # many systems return init, so this cannot be trusted, if it ends in 'sh' it probalby is a shell in a container proc_1 = None # if not init/None it should be an identifiable or custom init, so we are done! if proc_1 is not None: # Lookup proc_1 value in map and use proc_1 value itself if no match # FIXME: empty string still falls through service_mgr_name = proc_1_map.get(proc_1, proc_1) # FIXME: replace with a system->service_mgr_name map? # start with the easy ones elif collected_facts.get('ansible_distribution', None) == 'MacOSX': # FIXME: find way to query executable, version matching is not ideal if LooseVersion(platform.mac_ver()[0]) >= LooseVersion('10.4'): service_mgr_name = 'launchd' else: service_mgr_name = 'systemstarter' elif 'BSD' in collected_facts.get( 'ansible_system', '') or collected_facts.get('ansible_system') in [ 'Bitrig', 'DragonFly' ]: # FIXME: we might want to break out to individual BSDs or 'rc' service_mgr_name = 'bsdinit' elif collected_facts.get('ansible_system') == 'AIX': service_mgr_name = 'src' elif collected_facts.get('ansible_system') == 'SunOS': service_mgr_name = 'smf' elif collected_facts.get('ansible_distribution') == 'OpenWrt': service_mgr_name = 'openwrt_init' elif collected_facts.get('ansible_system') == 'Linux': # FIXME: mv is_systemd_managed if self.is_systemd_managed(module=module): service_mgr_name = 'systemd' elif module.get_bin_path('initctl') and os.path.exists( "/etc/init/"): service_mgr_name = 'upstart' elif os.path.exists('/sbin/openrc'): service_mgr_name = 'openrc' elif os.path.exists('/etc/init.d/'): service_mgr_name = 'sysvinit' if not service_mgr_name: # if we cannot detect, fallback to generic 'service' service_mgr_name = 'service' facts_dict['service_mgr'] = service_mgr_name return facts_dict
def get_interfaces_info(self, ip_path, default_ipv4, default_ipv6): interfaces = {} ips = dict( all_ipv4_addresses=[], all_ipv6_addresses=[], ) # FIXME: maybe split into smaller methods? # FIXME: this is pretty much a constructor for path in glob.glob('/sys/class/net/*'): if not os.path.isdir(path): continue device = os.path.basename(path) interfaces[device] = {'device': device} if os.path.exists(os.path.join(path, 'address')): macaddress = get_file_content(os.path.join(path, 'address'), default='') if macaddress and macaddress != '00:00:00:00:00:00': interfaces[device]['macaddress'] = macaddress if os.path.exists(os.path.join(path, 'mtu')): interfaces[device]['mtu'] = int( get_file_content(os.path.join(path, 'mtu'))) if os.path.exists(os.path.join(path, 'operstate')): interfaces[device]['active'] = get_file_content( os.path.join(path, 'operstate')) != 'down' if os.path.exists(os.path.join(path, 'device', 'driver', 'module')): interfaces[device]['module'] = os.path.basename( os.path.realpath( os.path.join(path, 'device', 'driver', 'module'))) if os.path.exists(os.path.join(path, 'type')): _type = get_file_content(os.path.join(path, 'type')) interfaces[device]['type'] = self.INTERFACE_TYPE.get( _type, 'unknown') if os.path.exists(os.path.join(path, 'bridge')): interfaces[device]['type'] = 'bridge' interfaces[device]['interfaces'] = [ os.path.basename(b) for b in glob.glob(os.path.join(path, 'brif', '*')) ] if os.path.exists(os.path.join(path, 'bridge', 'bridge_id')): interfaces[device]['id'] = get_file_content(os.path.join( path, 'bridge', 'bridge_id'), default='') if os.path.exists(os.path.join(path, 'bridge', 'stp_state')): interfaces[device]['stp'] = get_file_content( os.path.join(path, 'bridge', 'stp_state')) == '1' if os.path.exists(os.path.join(path, 'bonding')): interfaces[device]['type'] = 'bonding' interfaces[device]['slaves'] = get_file_content( os.path.join(path, 'bonding', 'slaves'), default='').split() interfaces[device]['mode'] = get_file_content( os.path.join(path, 'bonding', 'mode'), default='').split()[0] interfaces[device]['miimon'] = get_file_content( os.path.join(path, 'bonding', 'miimon'), default='').split()[0] interfaces[device]['lacp_rate'] = get_file_content( os.path.join(path, 'bonding', 'lacp_rate'), default='').split()[0] primary = get_file_content( os.path.join(path, 'bonding', 'primary')) if primary: interfaces[device]['primary'] = primary path = os.path.join(path, 'bonding', 'all_slaves_active') if os.path.exists(path): interfaces[device][ 'all_slaves_active'] = get_file_content( path) == '1' if os.path.exists(os.path.join(path, 'bonding_slave')): interfaces[device]['perm_macaddress'] = get_file_content( os.path.join(path, 'bonding_slave', 'perm_hwaddr'), default='') if os.path.exists(os.path.join(path, 'device')): interfaces[device]['pciid'] = os.path.basename( os.readlink(os.path.join(path, 'device'))) if os.path.exists(os.path.join(path, 'speed')): speed = get_file_content(os.path.join(path, 'speed')) if speed is not None: interfaces[device]['speed'] = int(speed) # Check whether an interface is in promiscuous mode if os.path.exists(os.path.join(path, 'flags')): promisc_mode = False # The second byte indicates whether the interface is in promiscuous mode. # 1 = promisc # 0 = no promisc data = int(get_file_content(os.path.join(path, 'flags')), 16) promisc_mode = (data & 0x0100 > 0) interfaces[device]['promisc'] = promisc_mode # TODO: determine if this needs to be in a nested scope/closure def parse_ip_output(output, secondary=False): for line in output.splitlines(): if not line: continue words = line.split() broadcast = '' if words[0] == 'inet': if '/' in words[1]: address, netmask_length = words[1].split('/') if len(words) > 3: broadcast = words[3] else: # pointopoint interfaces do not have a prefix address = words[1] netmask_length = "32" address_bin = struct.unpack( '!L', socket.inet_aton(address))[0] netmask_bin = (1 << 32) - ( 1 << 32 >> int(netmask_length)) netmask = socket.inet_ntoa( struct.pack('!L', netmask_bin)) network = socket.inet_ntoa( struct.pack('!L', address_bin & netmask_bin)) iface = words[-1] # NOTE: device is ref to outside scope # NOTE: interfaces is also ref to outside scope if iface != device: interfaces[iface] = {} if not secondary and "ipv4" not in interfaces[iface]: interfaces[iface]['ipv4'] = { 'address': address, 'broadcast': broadcast, 'netmask': netmask, 'network': network } else: if "ipv4_secondaries" not in interfaces[iface]: interfaces[iface]["ipv4_secondaries"] = [] interfaces[iface]["ipv4_secondaries"].append({ 'address': address, 'broadcast': broadcast, 'netmask': netmask, 'network': network, }) # add this secondary IP to the main device if secondary: if "ipv4_secondaries" not in interfaces[device]: interfaces[device]["ipv4_secondaries"] = [] if device != iface: interfaces[device]["ipv4_secondaries"].append({ 'address': address, 'broadcast': broadcast, 'netmask': netmask, 'network': network, }) # NOTE: default_ipv4 is ref to outside scope # If this is the default address, update default_ipv4 if 'address' in default_ipv4 and default_ipv4[ 'address'] == address: default_ipv4['broadcast'] = broadcast default_ipv4['netmask'] = netmask default_ipv4['network'] = network # NOTE: macaddress is ref from outside scope default_ipv4['macaddress'] = macaddress default_ipv4['mtu'] = interfaces[device]['mtu'] default_ipv4['type'] = interfaces[device].get( "type", "unknown") default_ipv4['alias'] = words[-1] if not address.startswith('127.'): ips['all_ipv4_addresses'].append(address) elif words[0] == 'inet6': if 'peer' == words[2]: address = words[1] _, prefix = words[3].split('/') scope = words[5] else: address, prefix = words[1].split('/') scope = words[3] if 'ipv6' not in interfaces[device]: interfaces[device]['ipv6'] = [] interfaces[device]['ipv6'].append({ 'address': address, 'prefix': prefix, 'scope': scope }) # If this is the default address, update default_ipv6 if 'address' in default_ipv6 and default_ipv6[ 'address'] == address: default_ipv6['prefix'] = prefix default_ipv6['scope'] = scope default_ipv6['macaddress'] = macaddress default_ipv6['mtu'] = interfaces[device]['mtu'] default_ipv6['type'] = interfaces[device].get( "type", "unknown") if not address == '::1': ips['all_ipv6_addresses'].append(address) ip_path = self.module.get_bin_path("ip") args = [ip_path, 'addr', 'show', 'primary', device] rc, primary_data, stderr = self.module.run_command( args, errors='surrogate_then_replace') if rc == 0: parse_ip_output(primary_data) else: # possibly busybox, fallback to running without the "primary" arg # https://github.com/ansible/ansible/issues/50871 args = [ip_path, 'addr', 'show', device] rc, data, stderr = self.module.run_command( args, errors='surrogate_then_replace') if rc == 0: parse_ip_output(data) args = [ip_path, 'addr', 'show', 'secondary', device] rc, secondary_data, stderr = self.module.run_command( args, errors='surrogate_then_replace') if rc == 0: parse_ip_output(secondary_data, secondary=True) interfaces[device].update(self.get_ethtool_data(device)) # replace : by _ in interface name since they are hard to use in template new_interfaces = {} # i is a dict key (string) not an index int for i in interfaces: if ':' in i: new_interfaces[i.replace(':', '_')] = interfaces[i] else: new_interfaces[i] = interfaces[i] return new_interfaces, ips
def _get_file_content(self, path): return get_file_content(path)
def _get_proc_cmdline(self): return get_file_content('/proc/cmdline')
def get_virtual_facts(self): virtual_facts = {} # lxc/docker if os.path.exists('/proc/1/cgroup'): for line in get_file_lines('/proc/1/cgroup'): if re.search(r'/docker(/|-[0-9a-f]+\.scope)', line): virtual_facts['virtualization_type'] = 'docker' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if re.search('/lxc/', line) or re.search( '/machine.slice/machine-lxc', line): virtual_facts['virtualization_type'] = 'lxc' virtual_facts['virtualization_role'] = 'guest' return virtual_facts # lxc does not always appear in cgroups anymore but sets 'container=lxc' environment var, requires root privs if os.path.exists('/proc/1/environ'): for line in get_file_lines('/proc/1/environ', line_sep='\x00'): if re.search('container=lxc', line): virtual_facts['virtualization_type'] = 'lxc' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if re.search('container=podman', line): virtual_facts['virtualization_type'] = 'podman' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if re.search('^container=.', line): virtual_facts['virtualization_type'] = 'container' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if os.path.exists('/proc/vz') and not os.path.exists('/proc/lve'): virtual_facts['virtualization_type'] = 'openvz' if os.path.exists('/proc/bc'): virtual_facts['virtualization_role'] = 'host' else: virtual_facts['virtualization_role'] = 'guest' return virtual_facts systemd_container = get_file_content('/run/systemd/container') if systemd_container: virtual_facts['virtualization_type'] = systemd_container virtual_facts['virtualization_role'] = 'guest' return virtual_facts if os.path.exists("/proc/xen"): virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'guest' try: for line in get_file_lines('/proc/xen/capabilities'): if "control_d" in line: virtual_facts['virtualization_role'] = 'host' except IOError: pass return virtual_facts # assume guest for this block virtual_facts['virtualization_role'] = 'guest' product_name = get_file_content( '/sys/devices/virtual/dmi/id/product_name') if product_name in ('KVM', 'Bochs', 'AHV'): virtual_facts['virtualization_type'] = 'kvm' return virtual_facts if product_name == 'RHEV Hypervisor': virtual_facts['virtualization_type'] = 'RHEV' return virtual_facts if product_name in ('VMware Virtual Platform', 'VMware7,1'): virtual_facts['virtualization_type'] = 'VMware' return virtual_facts if product_name in ('OpenStack Compute', 'OpenStack Nova'): virtual_facts['virtualization_type'] = 'openstack' return virtual_facts bios_vendor = get_file_content( '/sys/devices/virtual/dmi/id/bios_vendor') if bios_vendor == 'Xen': virtual_facts['virtualization_type'] = 'xen' return virtual_facts if bios_vendor == 'innotek GmbH': virtual_facts['virtualization_type'] = 'virtualbox' return virtual_facts if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner'): virtual_facts['virtualization_type'] = 'kvm' return virtual_facts sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor') KVM_SYS_VENDORS = ('QEMU', 'oVirt', 'Amazon EC2', 'DigitalOcean', 'Google', 'Scaleway', 'Nutanix') if sys_vendor in KVM_SYS_VENDORS: virtual_facts['virtualization_type'] = 'kvm' return virtual_facts # FIXME: This does also match hyperv if sys_vendor == 'Microsoft Corporation': virtual_facts['virtualization_type'] = 'VirtualPC' return virtual_facts if sys_vendor == 'Parallels Software International Inc.': virtual_facts['virtualization_type'] = 'parallels' return virtual_facts if sys_vendor == 'OpenStack Foundation': virtual_facts['virtualization_type'] = 'openstack' return virtual_facts # unassume guest del virtual_facts['virtualization_role'] if os.path.exists('/proc/self/status'): for line in get_file_lines('/proc/self/status'): if re.match(r'^VxID:\s+\d+', line): virtual_facts['virtualization_type'] = 'linux_vserver' if re.match(r'^VxID:\s+0', line): virtual_facts['virtualization_role'] = 'host' else: virtual_facts['virtualization_role'] = 'guest' return virtual_facts if os.path.exists('/proc/cpuinfo'): for line in get_file_lines('/proc/cpuinfo'): if re.match('^model name.*QEMU Virtual CPU', line): virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*User Mode Linux', line): virtual_facts['virtualization_type'] = 'uml' elif re.match('^model name.*UML', line): virtual_facts['virtualization_type'] = 'uml' elif re.match('^machine.*CHRP IBM pSeries .emulated by qemu.', line): virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*PowerVM Lx86', line): virtual_facts['virtualization_type'] = 'powervm_lx86' elif re.match('^vendor_id.*IBM/S390', line): virtual_facts['virtualization_type'] = 'PR/SM' lscpu = self.module.get_bin_path('lscpu') if lscpu: rc, out, err = self.module.run_command(["lscpu"]) if rc == 0: for line in out.splitlines(): data = line.split(":", 1) key = data[0].strip() if key == 'Hypervisor': virtual_facts[ 'virtualization_type'] = data[1].strip( ) else: virtual_facts['virtualization_type'] = 'ibm_systemz' else: continue if virtual_facts['virtualization_type'] == 'PR/SM': virtual_facts['virtualization_role'] = 'LPAR' else: virtual_facts['virtualization_role'] = 'guest' return virtual_facts # Beware that we can have both kvm and virtualbox running on a single system if os.path.exists("/proc/modules") and os.access( '/proc/modules', os.R_OK): modules = [] for line in get_file_lines("/proc/modules"): data = line.split(" ", 1) modules.append(data[0]) if 'kvm' in modules: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'host' if os.path.isdir('/rhev/'): # Check whether this is a RHEV hypervisor (is vdsm running ?) for f in glob.glob('/proc/[0-9]*/comm'): try: with open(f) as virt_fh: comm_content = virt_fh.read().rstrip() if comm_content == 'vdsm': virtual_facts['virtualization_type'] = 'RHEV' break except Exception: pass return virtual_facts if 'vboxdrv' in modules: virtual_facts['virtualization_type'] = 'virtualbox' virtual_facts['virtualization_role'] = 'host' return virtual_facts if 'virtio' in modules: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts # In older Linux Kernel versions, /sys filesystem is not available # dmidecode is the safest option to parse virtualization related values dmi_bin = self.module.get_bin_path('dmidecode') # We still want to continue even if dmidecode is not available if dmi_bin is not None: (rc, out, err) = self.module.run_command( '%s -s system-product-name' % dmi_bin) if rc == 0: # Strip out commented lines (specific dmidecode output) vendor_name = ''.join([ line.strip() for line in out.splitlines() if not line.startswith('#') ]) if vendor_name.startswith('VMware'): virtual_facts['virtualization_type'] = 'VMware' virtual_facts['virtualization_role'] = 'guest' return virtual_facts # If none of the above matches, return 'NA' for virtualization_type # and virtualization_role. This allows for proper grouping. virtual_facts['virtualization_type'] = 'NA' virtual_facts['virtualization_role'] = 'NA' return virtual_facts
def collect(self, module=None, collected_facts=None): """ Example of contents of /etc/iscsi/initiatorname.iscsi: ## DO NOT EDIT OR REMOVE THIS FILE! ## If you remove this file, the iSCSI daemon will not start. ## If you change the InitiatorName, existing access control lists ## may reject this initiator. The InitiatorName must be unique ## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames. InitiatorName=iqn.1993-08.org.debian:01:44a42c8ddb8b Example of output from the AIX lsattr command: # lsattr -E -l iscsi0 disc_filename /etc/iscsi/targets Configuration file False disc_policy file Discovery Policy True initiator_name iqn.localhost.hostid.7f000002 iSCSI Initiator Name True isns_srvnames auto iSNS Servers IP Addresses True isns_srvports iSNS Servers Port Numbers True max_targets 16 Maximum Targets Allowed True num_cmd_elems 200 Maximum number of commands to queue to driver True Example of output from the HP-UX iscsiutil command: #iscsiutil -l Initiator Name : iqn.1986-03.com.hp:mcel_VMhost3.1f355cf6-e2db-11e0-a999-b44c0aef5537 Initiator Alias : Authentication Method : None CHAP Method : CHAP_UNI Initiator CHAP Name : CHAP Secret : NAS Hostname : NAS Secret : Radius Server Hostname : Header Digest : None, CRC32C (default) Data Digest : None, CRC32C (default) SLP Scope list for iSLPD : """ iscsi_facts = {} iscsi_facts['iscsi_iqn'] = "" if sys.platform.startswith('linux') or sys.platform.startswith('sunos'): for line in get_file_content('/etc/iscsi/initiatorname.iscsi', '').splitlines(): if line.startswith('#') or line.startswith(';') or line.strip() == '': continue if line.startswith('InitiatorName='): iscsi_facts['iscsi_iqn'] = line.split('=', 1)[1] break elif sys.platform.startswith('aix'): cmd = get_bin_path('lsattr') if cmd: cmd += " -E -l iscsi0" rc, out, err = module.run_command(cmd) if rc == 0 and out: line = self.findstr(out, 'initiator_name') iscsi_facts['iscsi_iqn'] = line.split()[1].rstrip() elif sys.platform.startswith('hp-ux'): # try to find it in the default PATH and opt_dirs cmd = get_bin_path('iscsiutil', opt_dirs=['/opt/iscsi/bin']) if cmd: cmd += " -l" rc, out, err = module.run_command(cmd) if out: line = self.findstr(out, 'Initiator Name') iscsi_facts['iscsi_iqn'] = line.split(":", 1)[1].rstrip() return iscsi_facts
def get_dmi_facts(self): ''' learn dmi facts from system Try /sys first for dmi related facts. If that is not available, fall back to dmidecode executable ''' dmi_facts = {} if os.path.exists('/sys/devices/virtual/dmi/id/product_name'): # Use kernel DMI info, if available # DMI SPEC -- https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop", "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower", "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station", "All In One", "Sub Notebook", "Space-saving", "Lunch Box", "Main Server Chassis", "Expansion Chassis", "Sub Chassis", "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis", "Rack Mount Chassis", "Sealed-case PC", "Multi-system", "CompactPCI", "AdvancedTCA", "Blade", "Blade Enclosure", "Tablet", "Convertible", "Detachable", "IoT Gateway", "Embedded PC", "Mini PC", "Stick PC" ] DMI_DICT = { 'bios_date': '/sys/devices/virtual/dmi/id/bios_date', 'bios_version': '/sys/devices/virtual/dmi/id/bios_version', 'form_factor': '/sys/devices/virtual/dmi/id/chassis_type', 'product_name': '/sys/devices/virtual/dmi/id/product_name', 'product_serial': '/sys/devices/virtual/dmi/id/product_serial', 'product_uuid': '/sys/devices/virtual/dmi/id/product_uuid', 'product_version': '/sys/devices/virtual/dmi/id/product_version', 'system_vendor': '/sys/devices/virtual/dmi/id/sys_vendor' } for (key, path) in DMI_DICT.items(): data = get_file_content(path) if data is not None: if key == 'form_factor': try: dmi_facts['form_factor'] = FORM_FACTOR[int(data)] except IndexError: dmi_facts['form_factor'] = 'unknown (%s)' % data else: dmi_facts[key] = data else: dmi_facts[key] = 'NA' else: # Fall back to using dmidecode, if available dmi_bin = self.module.get_bin_path('dmidecode') DMI_DICT = { 'bios_date': 'bios-release-date', 'bios_version': 'bios-version', 'form_factor': 'chassis-type', 'product_name': 'system-product-name', 'product_serial': 'system-serial-number', 'product_uuid': 'system-uuid', 'product_version': 'system-version', 'system_vendor': 'system-manufacturer' } for (k, v) in DMI_DICT.items(): if dmi_bin is not None: (rc, out, err) = self.module.run_command('%s -s %s' % (dmi_bin, v)) if rc == 0: # Strip out commented lines (specific dmidecode output) thisvalue = ''.join([ line for line in out.splitlines() if not line.startswith('#') ]) try: json.dumps(thisvalue) except UnicodeDecodeError: thisvalue = "NA" dmi_facts[k] = thisvalue else: dmi_facts[k] = 'NA' else: dmi_facts[k] = 'NA' return dmi_facts
def get_device_facts(self): device_facts = {} device_facts['devices'] = {} lspci = self.module.get_bin_path('lspci') if lspci: rc, pcidata, err = self.module.run_command( [lspci, '-D'], errors='surrogate_then_replace') else: pcidata = None try: block_devs = os.listdir("/sys/block") except OSError: return device_facts devs_wwn = {} try: devs_by_id = os.listdir("/dev/disk/by-id") except OSError: pass else: for link_name in devs_by_id: if link_name.startswith("wwn-"): try: wwn_link = os.readlink( os.path.join("/dev/disk/by-id", link_name)) except OSError: continue devs_wwn[os.path.basename(wwn_link)] = link_name[4:] links = self.get_all_device_links() device_facts['device_links'] = links for block in block_devs: virtual = 1 sysfs_no_links = 0 try: path = os.readlink(os.path.join("/sys/block/", block)) except OSError: e = sys.exc_info()[1] if e.errno == errno.EINVAL: path = block sysfs_no_links = 1 else: continue sysdir = os.path.join("/sys/block", path) if sysfs_no_links == 1: for folder in os.listdir(sysdir): if "device" in folder: virtual = 0 break d = {} d['virtual'] = virtual d['links'] = {} for (link_type, link_values) in iteritems(links): d['links'][link_type] = link_values.get(block, []) diskname = os.path.basename(sysdir) for key in ['vendor', 'model', 'sas_address', 'sas_device_handle']: d[key] = get_file_content(sysdir + "/device/" + key) sg_inq = self.module.get_bin_path('sg_inq') if sg_inq: device = "/dev/%s" % (block) rc, drivedata, err = self.module.run_command([sg_inq, device]) if rc == 0: serial = re.search(r"Unit serial number:\s+(\w+)", drivedata) if serial: d['serial'] = serial.group(1) for key, test in [ ('removable', '/removable'), ('support_discard', '/queue/discard_granularity'), ]: d[key] = get_file_content(sysdir + test) if diskname in devs_wwn: d['wwn'] = devs_wwn[diskname] d['partitions'] = {} for folder in os.listdir(sysdir): m = re.search("(" + diskname + r"[p]?\d+)", folder) if m: part = {} partname = m.group(1) part_sysdir = sysdir + "/" + partname part['links'] = {} for (link_type, link_values) in iteritems(links): part['links'][link_type] = link_values.get( partname, []) part['start'] = get_file_content(part_sysdir + "/start", 0) part['sectors'] = get_file_content(part_sysdir + "/size", 0) part['sectorsize'] = get_file_content( part_sysdir + "/queue/logical_block_size") if not part['sectorsize']: part['sectorsize'] = get_file_content( part_sysdir + "/queue/hw_sector_size", 512) part['size'] = bytes_to_human( (float(part['sectors']) * 512.0)) part['uuid'] = get_partition_uuid(partname) self.get_holders(part, part_sysdir) d['partitions'][partname] = part d['rotational'] = get_file_content(sysdir + "/queue/rotational") d['scheduler_mode'] = "" scheduler = get_file_content(sysdir + "/queue/scheduler") if scheduler is not None: m = re.match(r".*?(\[(.*)\])", scheduler) if m: d['scheduler_mode'] = m.group(2) d['sectors'] = get_file_content(sysdir + "/size") if not d['sectors']: d['sectors'] = 0 d['sectorsize'] = get_file_content(sysdir + "/queue/logical_block_size") if not d['sectorsize']: d['sectorsize'] = get_file_content( sysdir + "/queue/hw_sector_size", 512) d['size'] = bytes_to_human(float(d['sectors']) * 512.0) d['host'] = "" # domains are numbered (0 to ffff), bus (0 to ff), slot (0 to 1f), and function (0 to 7). m = re.match(r".+/([a-f0-9]{4}:[a-f0-9]{2}:[0|1][a-f0-9]\.[0-7])/", sysdir) if m and pcidata: pciid = m.group(1) did = re.escape(pciid) m = re.search("^" + did + r"\s(.*)$", pcidata, re.MULTILINE) if m: d['host'] = m.group(1) self.get_holders(d, sysdir) device_facts['devices'][diskname] = d return device_facts