def collect(self, module=None, collected_facts=None): platform_facts = {} # platform.system() can be Linux, Darwin, Java, or Windows platform_facts['system'] = platform.system() platform_facts['kernel'] = platform.release() platform_facts['machine'] = platform.machine() platform_facts['python_version'] = platform.python_version() platform_facts['fqdn'] = socket.getfqdn() platform_facts['hostname'] = platform.node().split('.')[0] platform_facts['nodename'] = platform.node() platform_facts['domain'] = '.'.join(platform_facts['fqdn'].split('.')[1:]) arch_bits = platform.architecture()[0] platform_facts['userspace_bits'] = arch_bits.replace('bit', '') if platform_facts['machine'] == 'x86_64': platform_facts['architecture'] = platform_facts['machine'] if platform_facts['userspace_bits'] == '64': platform_facts['userspace_architecture'] = 'x86_64' elif platform_facts['userspace_bits'] == '32': platform_facts['userspace_architecture'] = 'i386' elif solaris_i86_re.search(platform_facts['machine']): platform_facts['architecture'] = 'i386' if platform_facts['userspace_bits'] == '64': platform_facts['userspace_architecture'] = 'x86_64' elif platform_facts['userspace_bits'] == '32': platform_facts['userspace_architecture'] = 'i386' else: platform_facts['architecture'] = platform_facts['machine'] if platform_facts['system'] == 'AIX': # Attempt to use getconf to figure out architecture # fall back to bootinfo if needed getconf_bin = module.get_bin_path('getconf') if getconf_bin: rc, out, err = module.run_command([getconf_bin, 'MACHINE_ARCHITECTURE']) data = out.splitlines() platform_facts['architecture'] = data[0] else: bootinfo_bin = module.get_bin_path('bootinfo') rc, out, err = module.run_command([bootinfo_bin, '-p']) data = out.splitlines() platform_facts['architecture'] = data[0] elif platform_facts['system'] == 'OpenBSD': platform_facts['architecture'] = platform.uname()[5] machine_id = get_file_content("/var/lib/dbus/machine-id") or get_file_content("/etc/machine-id") if machine_id: machine_id = machine_id.splitlines()[0] platform_facts["machine_id"] = machine_id return platform_facts
def get_distribution_SunOS(self): sunos_facts = {} # print('platform.release: %s' % distribution_release) data = get_file_content('/etc/release').splitlines()[0] # print('get_file_content: data=%s' % data) if 'Solaris' in data: 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 return sunos_facts uname_v = get_uname_version(self.module) distribution_version = None # print('uname_v: %s' % uname_v) 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') # print('sunos_facts: %s' % sunos_facts) 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): # 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_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 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) 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 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_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 main(): module_args = oci_utils.get_taggable_arg_spec(supports_create=True, supports_wait=True) module_args.update( dict(instance_id=dict(type='str', required=False), public_key=dict(type='str', required=False), instance_console_connection_id=dict(type='str', required=False, aliases=['id']), state=dict(type='str', required=False, default='present', choices=['absent', 'present']))) module = AnsibleModule( argument_spec=module_args, supports_check_mode=False, mutually_exclusive=['instance_console_connection_id', 'instance_id'], required_if=[('state', 'absent', ['instance_console_connection_id']), ('state', 'present', ['instance_id', 'public_key'])]) if not HAS_OCI_PY_SDK: module.fail_json(msg='oci python sdk required for this module.') compute_client = oci_utils.create_service_client(module, ComputeClient) state = module.params['state'] if state == 'absent': result = delete_instance_console_connection(compute_client, module) else: # Replace value of `public_key` key by content of file pointed to by the `public_key` param module.params["public_key"] = get_file_content( module.params['public_key']) instance_id = module.params['instance_id'] kwargs_list = { 'instance_id': instance_id, 'compartment_id': _get_compartment_of_instance(compute_client, instance_id) } result = oci_utils.check_and_create_resource( resource_type='instance_console_connection', create_fn=create_instance_console_connection, kwargs_create={ 'compute_client': compute_client, 'module': module }, list_fn=compute_client.list_instance_console_connections, kwargs_list=kwargs_list, module=module, model=CreateInstanceConsoleConnectionDetails(), exclude_attributes=None) module.exit_json(**result)
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 get_permanent_hostname(self): if not os.path.isfile(self.FILE): return '' try: return get_file_content(self.FILE, default='', strip=True) except Exception as e: self.module.fail_json(msg="failed to read hostname: %s" % to_native(e), exception=traceback.format_exc())
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 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" 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 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 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 _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 main(): module = AnsibleModule( argument_spec=dict( test=dict(type='str', default='strip'), touch_file=dict(type='str', default='/dev/null'), line_sep_file=dict(type='str', default='/dev/null'), line_sep_sep=dict(type='str', default='\n'), ) ) test = module.params['test'] facts = {} if test == 'strip': etc_passwd = get_file_content('/etc/passwd') etc_passwd_unstripped = get_file_content('/etc/passwd', strip=False) facts['etc_passwd_newlines'] = etc_passwd.count('\n') facts['etc_passwd_newlines_unstripped'] = etc_passwd_unstripped.count('\n') elif test == 'default': path = module.params['touch_file'] facts['touch_default'] = get_file_content(path, default='i am a default') elif test == 'line_sep': path = module.params['line_sep_file'] sep = module.params['line_sep_sep'] facts['line_sep'] = get_file_lines(path, line_sep=sep) elif test == 'invalid_mountpoint': facts['invalid_mountpoint'] = get_mount_size('/doesnotexist') result = { 'changed': False, 'ansible_facts': facts, } module.exit_json(**result)
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('^VERSION_ID="?([0-9]+\.?[0-9]*)"?', line) if distribution_version: suse_facts['distribution_version'] = distribution_version.group(1) if 'open' in data.lower(): release = re.search('^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('^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 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_cpu_facts(self): cpu_facts = {} cpu_facts['processor'] = [] rc, out, err = self.module.run_command("/sbin/sysctl -n hw.ncpu") cpu_facts['processor_count'] = out.strip() dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT) if not dmesg_boot: rc, dmesg_boot, err = self.module.run_command("/sbin/dmesg") 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_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 set_permanent_hostname(self, name): try: lines = [] found = False content = get_file_content(self.NETWORK_FILE, strip=False) or "" for line in content.splitlines(True): line = to_native(line) if line.strip().startswith('HOSTNAME'): lines.append("HOSTNAME=%s\n" % name) found = True else: lines.append(line) if not found: lines.append("HOSTNAME=%s\n" % name) with open(self.NETWORK_FILE, 'w+') as f: f.writelines(lines) except Exception as e: self.module.fail_json( msg="failed to update hostname: %s" % to_native(e), exception=traceback.format_exc())
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): """ 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.7f000001 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 """ 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'): aixcmd = '/usr/sbin/lsattr -E -l iscsi0 | grep initiator_name' aixret = subprocess.check_output(aixcmd, shell=True) if aixret[0].isalpha(): iscsi_facts['iscsi_iqn'] = aixret.split()[1].rstrip() return iscsi_facts
def collect(self, module=None, collected_facts=None): cmdline_facts = {} data = get_file_content('/proc/cmdline') if not data: return cmdline_facts cmdline_facts['cmdline'] = {} try: for piece in shlex.split(data): item = piece.split('=', 1) if len(item) == 1: cmdline_facts['cmdline'][item[0]] = True else: cmdline_facts['cmdline'][item[0]] = item[1] except ValueError: pass return cmdline_facts
def create_certificates(certificate_details_dict): if certificate_details_dict is None: return None result_certificates = dict() attributes = ['ca_certificate', 'private_key', 'public_certificate'] for key, value in six.iteritems(certificate_details_dict): certificate_details = CertificateDetails() certificate_name = value.get('certificate_name') if certificate_name is None: raise ClientError( Exception( "certificate_name is mandatory and must not be empty for certificate creation" )) certificate_details.certificate_name = certificate_name certificate_details.passphrase = value.get('passphrase', None) for attribute in attributes: if value.get(attribute) is not None: certificate_details.__setattr__( attribute, get_file_content(value.get(attribute, None))) result_certificates.update({key: certificate_details}) return result_certificates
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() size_total, size_available = get_mount_size(fields[1]) mount_facts['mounts'].append({ 'mount': fields[1], 'device': fields[0], 'fstype': fields[2], 'options': fields[3], 'size_total': size_total, 'size_available': size_available }) return mount_facts
def collect(self, module=None, collected_facts=None): ssh_pub_key_facts = {} keytypes = ('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 type_ in keytypes: factname = 'ssh_host_key_%s_public' % type_ 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, type_) keydata = get_file_content(key_filename) if keydata is not None: ssh_pub_key_facts[factname] = keydata.split()[1] 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 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): """ 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.7f000001 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 """ 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'): aixcmd = '/usr/sbin/lsattr -E -l iscsi0 | grep initiator_name' aixret = subprocess.check_output(aixcmd, shell=True) if aixret[0].isalpha(): iscsi_facts['iscsi_iqn'] = aixret.split()[1].rstrip() return iscsi_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_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') size_total, size_available = get_mount_size(fields[1]) mount_facts['mounts'].append({ 'mount': fields[1], 'device': fields[0], 'fstype': fields[2], 'options': fields[3], 'time': fields[4], 'size_total': size_total, 'size_available': size_available }) return mount_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('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('system', '') or collected_facts.get('system') in ['Bitrig', 'DragonFly']: # FIXME: we might want to break out to individual BSDs or 'rc' service_mgr_name = 'bsdinit' elif collected_facts.get('system') == 'AIX': service_mgr_name = 'src' elif collected_facts.get('system') == 'SunOS': service_mgr_name = 'smf' elif collected_facts.get('distribution') == 'OpenWrt': service_mgr_name = 'openwrt_init' elif collected_facts.get('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_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 in ['vendor', 'model']: d[key] = get_file_content(sysdir + "/device/" + key) 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
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 -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.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" ] 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_file_content(self, path): return get_file_content(path)
def _get_proc_cmdline(self): return get_file_content('/proc/cmdline')
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 self.is_systemd_managed_offline(module=module): service_mgr_name = 'systemd' 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_virtual_facts(self): virtual_facts = {} # We want to maintain compatibility with the old "virtualization_type" # and "virtualization_role" entries, so we need to track if we found # them. We won't return them until the end, but if we found them early, # we should avoid updating them again. found_virt = False # But as we go along, we also want to track virt tech the new way. host_tech = set() guest_tech = set() # 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): guest_tech.add('docker') if not found_virt: virtual_facts['virtualization_type'] = 'docker' virtual_facts['virtualization_role'] = 'guest' found_virt = True if re.search('/lxc/', line) or re.search('/machine.slice/machine-lxc', line): guest_tech.add('lxc') if not found_virt: virtual_facts['virtualization_type'] = 'lxc' virtual_facts['virtualization_role'] = 'guest' found_virt = True if re.search('/system.slice/containerd.service', line): guest_tech.add('containerd') if not found_virt: virtual_facts['virtualization_type'] = 'containerd' virtual_facts['virtualization_role'] = 'guest' found_virt = True # 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): guest_tech.add('lxc') if not found_virt: virtual_facts['virtualization_type'] = 'lxc' virtual_facts['virtualization_role'] = 'guest' found_virt = True if re.search('container=podman', line): guest_tech.add('podman') if not found_virt: virtual_facts['virtualization_type'] = 'podman' virtual_facts['virtualization_role'] = 'guest' found_virt = True if re.search('^container=.', line): guest_tech.add('container') if not found_virt: virtual_facts['virtualization_type'] = 'container' virtual_facts['virtualization_role'] = 'guest' found_virt = True if os.path.exists('/proc/vz') and not os.path.exists('/proc/lve'): virtual_facts['virtualization_type'] = 'openvz' if os.path.exists('/proc/bc'): host_tech.add('openvz') if not found_virt: virtual_facts['virtualization_role'] = 'host' else: guest_tech.add('openvz') if not found_virt: virtual_facts['virtualization_role'] = 'guest' found_virt = True systemd_container = get_file_content('/run/systemd/container') if systemd_container: guest_tech.add(systemd_container) if not found_virt: virtual_facts['virtualization_type'] = systemd_container virtual_facts['virtualization_role'] = 'guest' found_virt = True # ensure 'container' guest_tech is appropriately set if guest_tech.intersection(set(['docker', 'lxc', 'podman', 'openvz', 'containerd'])) or systemd_container: guest_tech.add('container') if os.path.exists("/proc/xen"): is_xen_host = False try: for line in get_file_lines('/proc/xen/capabilities'): if "control_d" in line: is_xen_host = True except IOError: pass if is_xen_host: host_tech.add('xen') if not found_virt: virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'host' else: if not found_virt: virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'guest' found_virt = True # assume guest for this block if not found_virt: virtual_facts['virtualization_role'] = 'guest' product_name = get_file_content('/sys/devices/virtual/dmi/id/product_name') sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor') product_family = get_file_content('/sys/devices/virtual/dmi/id/product_family') if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV'): guest_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' found_virt = True if sys_vendor == 'oVirt': guest_tech.add('oVirt') if not found_virt: virtual_facts['virtualization_type'] = 'oVirt' found_virt = True if sys_vendor == 'Red Hat': if product_family == 'RHV': guest_tech.add('RHV') if not found_virt: virtual_facts['virtualization_type'] = 'RHV' found_virt = True elif product_family == 'RHEV Hypervisor': guest_tech.add('RHEV') if not found_virt: virtual_facts['virtualization_type'] = 'RHEV' found_virt = True if product_name in ('VMware Virtual Platform', 'VMware7,1'): guest_tech.add('VMware') if not found_virt: virtual_facts['virtualization_type'] = 'VMware' found_virt = True if product_name in ('OpenStack Compute', 'OpenStack Nova'): guest_tech.add('openstack') if not found_virt: virtual_facts['virtualization_type'] = 'openstack' found_virt = True bios_vendor = get_file_content('/sys/devices/virtual/dmi/id/bios_vendor') if bios_vendor == 'Xen': guest_tech.add('xen') if not found_virt: virtual_facts['virtualization_type'] = 'xen' found_virt = True if bios_vendor == 'innotek GmbH': guest_tech.add('virtualbox') if not found_virt: virtual_facts['virtualization_type'] = 'virtualbox' found_virt = True if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner'): guest_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' found_virt = True KVM_SYS_VENDORS = ('QEMU', 'Amazon EC2', 'DigitalOcean', 'Google', 'Scaleway', 'Nutanix') if sys_vendor in KVM_SYS_VENDORS: guest_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' found_virt = True if sys_vendor == 'KubeVirt': guest_tech.add('KubeVirt') if not found_virt: virtual_facts['virtualization_type'] = 'KubeVirt' found_virt = True # FIXME: This does also match hyperv if sys_vendor == 'Microsoft Corporation': guest_tech.add('VirtualPC') if not found_virt: virtual_facts['virtualization_type'] = 'VirtualPC' found_virt = True if sys_vendor == 'Parallels Software International Inc.': guest_tech.add('parallels') if not found_virt: virtual_facts['virtualization_type'] = 'parallels' found_virt = True if sys_vendor == 'OpenStack Foundation': guest_tech.add('openstack') if not found_virt: virtual_facts['virtualization_type'] = 'openstack' found_virt = True # unassume guest if not found_virt: 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): if not found_virt: virtual_facts['virtualization_type'] = 'linux_vserver' if re.match(r'^VxID:\s+0', line): host_tech.add('linux_vserver') if not found_virt: virtual_facts['virtualization_role'] = 'host' else: guest_tech.add('linux_vserver') if not found_virt: virtual_facts['virtualization_role'] = 'guest' found_virt = True if os.path.exists('/proc/cpuinfo'): for line in get_file_lines('/proc/cpuinfo'): if re.match('^model name.*QEMU Virtual CPU', line): guest_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*User Mode Linux', line): guest_tech.add('uml') if not found_virt: virtual_facts['virtualization_type'] = 'uml' elif re.match('^model name.*UML', line): guest_tech.add('uml') if not found_virt: virtual_facts['virtualization_type'] = 'uml' elif re.match('^machine.*CHRP IBM pSeries .emulated by qemu.', line): guest_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*PowerVM Lx86', line): guest_tech.add('powervm_lx86') if not found_virt: virtual_facts['virtualization_type'] = 'powervm_lx86' elif re.match('^vendor_id.*IBM/S390', line): guest_tech.add('PR/SM') if not found_virt: 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': tech = data[1].strip() guest_tech.add(tech) if not found_virt: virtual_facts['virtualization_type'] = tech else: guest_tech.add('ibm_systemz') if not found_virt: virtual_facts['virtualization_type'] = 'ibm_systemz' else: continue if virtual_facts['virtualization_type'] == 'PR/SM': if not found_virt: virtual_facts['virtualization_role'] = 'LPAR' else: if not found_virt: virtual_facts['virtualization_role'] = 'guest' if not found_virt: found_virt = True # 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: host_tech.add('kvm') if not found_virt: 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 in ('vdsm', 'vdsmd'): # We add both kvm and RHEV to host_tech in this case. # It's accurate. RHEV uses KVM. host_tech.add('RHEV') if not found_virt: virtual_facts['virtualization_type'] = 'RHEV' break except Exception: pass found_virt = True if 'vboxdrv' in modules: host_tech.add('virtualbox') if not found_virt: virtual_facts['virtualization_type'] = 'virtualbox' virtual_facts['virtualization_role'] = 'host' found_virt = True if 'virtio' in modules: host_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' found_virt = True # 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'): guest_tech.add('VMware') if not found_virt: virtual_facts['virtualization_type'] = 'VMware' virtual_facts['virtualization_role'] = 'guest' found_virt = True if 'BHYVE' in out: guest_tech.add('bhyve') if not found_virt: virtual_facts['virtualization_type'] = 'bhyve' virtual_facts['virtualization_role'] = 'guest' found_virt = True if os.path.exists('/dev/kvm'): host_tech.add('kvm') if not found_virt: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'host' found_virt = True # If none of the above matches, return 'NA' for virtualization_type # and virtualization_role. This allows for proper grouping. if not found_virt: virtual_facts['virtualization_type'] = 'NA' virtual_facts['virtualization_role'] = 'NA' found_virt = True virtual_facts['virtualization_tech_guest'] = guest_tech virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts
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_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 -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.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"] 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_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', 'KVM Server', '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 in ('vdsm', 'vdsmd'): 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 os.path.exists('/dev/kvm'): virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'host' 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 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 in ['vendor', 'model']: d[key] = get_file_content(sysdir + "/device/" + key) 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
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'): if re.search('container=lxc', line): virtual_facts['virtualization_type'] = 'lxc' 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 product_name = get_file_content('/sys/devices/virtual/dmi/id/product_name') if product_name in ['KVM', 'Bochs']: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if product_name == 'RHEV Hypervisor': virtual_facts['virtualization_type'] = 'RHEV' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if product_name in ['VMware Virtual Platform', 'VMware7,1']: virtual_facts['virtualization_type'] = 'VMware' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if product_name in ['OpenStack Compute', 'OpenStack Nova']: virtual_facts['virtualization_type'] = 'openstack' virtual_facts['virtualization_role'] = 'guest' return virtual_facts bios_vendor = get_file_content('/sys/devices/virtual/dmi/id/bios_vendor') if bios_vendor == 'Xen': virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if bios_vendor == 'innotek GmbH': virtual_facts['virtualization_type'] = 'virtualbox' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if bios_vendor == 'Amazon EC2': virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor') # FIXME: This does also match hyperv if sys_vendor == 'Microsoft Corporation': virtual_facts['virtualization_type'] = 'VirtualPC' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if sys_vendor == 'Parallels Software International Inc.': virtual_facts['virtualization_type'] = 'parallels' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if sys_vendor == 'QEMU': virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if sys_vendor == 'oVirt': virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if sys_vendor == 'OpenStack Foundation': virtual_facts['virtualization_type'] = 'openstack' virtual_facts['virtualization_role'] = 'guest' return virtual_facts if sys_vendor == 'Amazon EC2': virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' return virtual_facts 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: 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: if open(f).read().rstrip() == 'vdsm': virtual_facts['virtualization_type'] = 'RHEV' break except Exception: pass else: virtual_facts['virtualization_type'] = 'kvm' else: virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'host' 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 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: macadress 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') args = [ip_path, 'addr', 'show', 'secondary', device] rc, secondary_data, stderr = self.module.run_command(args, errors='surrogate_then_replace') parse_ip_output(primary_data) 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