diff --git a/salt/modules/network.py b/salt/modules/network.py index 9e80139c4b..f104dc7228 100644 --- a/salt/modules/network.py +++ b/salt/modules/network.py @@ -336,6 +336,53 @@ def _netstat_sunos(): return ret +def _netstat_aix(): + ''' + Return netstat information for SunOS flavors + ''' + ret = [] + ## AIX 6.1 - 7.2, appears to ignore addr_family field contents + ## for addr_family in ('inet', 'inet6'): + for addr_family in ('inet',): + # Lookup connections + cmd = 'netstat -n -a -f {0} | tail -n +3'.format(addr_family) + out = __salt__['cmd.run'](cmd, python_shell=True) + for line in out.splitlines(): + comps = line.split() + if len(comps) < 5: + continue + + proto_seen = None + tcp_flag = True + if 'tcp' == comps[0] or 'tcp4' == comps[0]: + proto_seen = 'tcp' + elif 'tcp6' == comps[0]: + proto_seen = 'tcp6' + elif 'udp' == comps[0] or 'udp4' == comps[0]: + proto_seen = 'udp' + tcp_flag = False + elif 'udp6' == comps[0]: + proto_seen = 'udp6' + tcp_flag = False + + if tcp_flag: + if len(comps) >= 6: + ret.append({ + 'proto': proto_seen, + 'recv-q': comps[1], + 'send-q': comps[2], + 'local-address': comps[3], + 'remote-address': comps[4], + 'state': comps[5]}) + else: + if len(comps) >= 5: + ret.append({ + 'proto': proto_seen, + 'local-address': comps[3], + 'remote-address': comps[4]}) + return ret + + def _netstat_route_linux(): ''' Return netstat routing information for Linux distros @@ -506,6 +553,36 @@ def _netstat_route_sunos(): return ret +def _netstat_route_aix(): + ''' + Return netstat routing information for AIX + ''' + ret = [] + cmd = 'netstat -f inet -rn | tail -n +5' + out = __salt__['cmd.run'](cmd, python_shell=True) + for line in out.splitlines(): + comps = line.split() + ret.append({ + 'addr_family': 'inet', + 'destination': comps[0], + 'gateway': comps[1], + 'netmask': '', + 'flags': comps[2], + 'interface': comps[5] if len(comps) >= 6 else ''}) + cmd = 'netstat -f inet6 -rn | tail -n +5' + out = __salt__['cmd.run'](cmd, python_shell=True) + for line in out.splitlines(): + comps = line.split() + ret.append({ + 'addr_family': 'inet6', + 'destination': comps[0], + 'gateway': comps[1], + 'netmask': '', + 'flags': comps[2], + 'interface': comps[5] if len(comps) >= 6 else ''}) + return ret + + def netstat(): ''' Return information on open ports and states @@ -520,6 +597,9 @@ def netstat(): .. versionchanged:: 2015.8.0 Added support for SunOS + .. versionchanged:: 2016.11.4 + Added support for AIX + CLI Example: .. code-block:: bash @@ -532,6 +612,8 @@ def netstat(): return _netstat_bsd() elif __grains__['kernel'] == 'SunOS': return _netstat_sunos() + elif __grains__['kernel'] == 'AIX': + return _netstat_aix() raise CommandExecutionError('Not yet supported on this platform') @@ -566,6 +648,22 @@ def active_tcp(): 'remote_port': '.'.join(connection['remote-address'].split('.')[-1:]) } return ret + elif __grains__['kernel'] == 'AIX': + # lets use netstat to mimic linux as close as possible + ret = {} + for connection in _netstat_aix(): + ## TBD need to deliver AIX output in consumable fashion + if not connection['proto'].startswith('tcp'): + continue + if connection['state'] != 'ESTABLISHED': + continue + ret[len(ret)+1] = { + 'local_addr': '.'.join(connection['local-address'].split('.')[:-1]), + 'local_port': '.'.join(connection['local-address'].split('.')[-1:]), + 'remote_addr': '.'.join(connection['remote-address'].split('.')[:-1]), + 'remote_port': '.'.join(connection['remote-address'].split('.')[-1:]) + } + return ret else: return {} @@ -577,6 +675,9 @@ def traceroute(host): .. versionchanged:: 2015.8.0 Added support for SunOS + .. versionchanged:: 2016.11.4 + Added support for AIX + CLI Example: .. code-block:: bash @@ -593,7 +694,7 @@ def traceroute(host): out = __salt__['cmd.run'](cmd) # Parse version of traceroute - if salt.utils.is_sunos(): + if salt.utils.is_sunos() or salt.utils.is_aix(): traceroute_version = [0, 0, 0] else: cmd2 = 'traceroute --version' @@ -626,8 +727,21 @@ def traceroute(host): if line.startswith('traceroute'): continue + if salt.utils.is_aix(): + if line.startswith('trying to get source for'): + continue + + if line.startswith('source should be'): + continue + + if line.startswith('outgoing MTU'): + continue + + if line.startswith('fragmentation required'): + continue + if 'Darwin' in str(traceroute_version[1]) or 'FreeBSD' in str(traceroute_version[1]) or \ - __grains__['kernel'] == 'SunOS': + __grains__['kernel'] == 'SunOS' or __grains__['kernel'] == 'AIX': try: traceline = re.findall(r'\s*(\d*)\s+(.*)\s+\((.*)\)\s+(.*)$', line)[0] except IndexError: @@ -729,8 +843,15 @@ def arp(): if comps[0] == 'Host' or comps[1] == '(incomplete)': continue ret[comps[1]] = comps[0] + elif __grains__['kernel'] == 'AIX': + if comps[0] == 'bucket': + continue + if comps[0] == 'There': + continue + ret[comps[3]] = comps[1].strip('(').strip(')') else: ret[comps[3]] = comps[1].strip('(').strip(')') + return ret @@ -1298,6 +1419,9 @@ def routes(family=None): .. versionchanged:: 2015.8.0 Added support for SunOS (Solaris 10, Illumos, SmartOS) + .. versionchanged:: 2016.11.4 + Added support for AIX + CLI Example: .. code-block:: bash @@ -1317,6 +1441,8 @@ def routes(family=None): routes_ = _netstat_route_netbsd() elif __grains__['os'] in ['OpenBSD']: routes_ = _netstat_route_openbsd() + elif __grains__['os'] in ['AIX']: + routes_ = _netstat_route_aix() else: raise CommandExecutionError('Not yet supported on this platform') @@ -1334,6 +1460,9 @@ def default_route(family=None): .. versionchanged:: 2015.8.0 Added support for SunOS (Solaris 10, Illumos, SmartOS) + .. versionchanged:: 2016.11.4 + Added support for AIX + CLI Example: .. code-block:: bash @@ -1350,7 +1479,7 @@ def default_route(family=None): default_route['inet'] = ['0.0.0.0', 'default'] default_route['inet6'] = ['::/0', 'default'] elif __grains__['os'] in ['FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS', 'Darwin'] or \ - __grains__['kernel'] == 'SunOS': + __grains__['kernel'] == 'SunOS' or __grains__['kernel'] == 'AIX': default_route['inet'] = ['default'] default_route['inet6'] = ['default'] else: @@ -1381,6 +1510,9 @@ def get_route(ip): Added support for SunOS (Solaris 10, Illumos, SmartOS) Added support for OpenBSD + .. versionchanged:: 2016.11.4 + Added support for AIX + CLI Example:: salt '*' network.get_route 10.10.10.10 @@ -1466,6 +1598,39 @@ def get_route(ip): return ret + if __grains__['kernel'] == 'AIX': + # root@la68pp002_pub:~# route -n get 172.29.149.95 + # route to: 172.29.149.95 + #destination: 172.29.149.95 + # gateway: 127.0.0.1 + # interface: lo0 + #interf addr: 127.0.0.1 + # flags: + #recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire + # 0 0 0 0 0 0 0 -68642 + cmd = 'route -n get {0}'.format(ip) + out = __salt__['cmd.run'](cmd, python_shell=False) + + ret = { + 'destination': ip, + 'gateway': None, + 'interface': None, + 'source': None + } + + for line in out.splitlines(): + line = line.split(':') + if 'route to' in line[0]: + ret['destination'] = line[1].strip() + if 'gateway' in line[0]: + ret['gateway'] = line[1].strip() + if 'interface' in line[0]: + ret['interface'] = line[1].strip() + if 'interf addr' in line[0]: + ret['source'] = line[1].strip() + + return ret + else: raise CommandExecutionError('Not yet supported on this platform') diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py index 59d4a9989c..df5f2a7f0b 100644 --- a/salt/utils/__init__.py +++ b/salt/utils/__init__.py @@ -1782,6 +1782,14 @@ def is_openbsd(): return sys.platform.startswith('openbsd') +@real_memoize +def is_aix(): + ''' + Simple function to return if host is AIX or not + ''' + return sys.platform.startswith('aix') + + def is_fcntl_available(check_sunos=False): ''' Simple function to check if the `fcntl` module is available or not. diff --git a/salt/utils/network.py b/salt/utils/network.py index f68e3a4bbc..caac7ef6dc 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -660,10 +660,39 @@ def _get_iface_info(iface): return None, error_msg +def _hw_addr_aix(iface): + ''' + Return the hardware address (a.k.a. MAC address) for a given interface on AIX + MAC address not available in through interfaces + ''' + cmd = subprocess.Popen( + 'entstat -d {0} | grep \'Hardware Address\''.format(iface), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT).communicate()[0] + + if cmd: + comps = cmd.split(' ') + if len(comps) == 3: + mac_addr = comps[2].strip('\'').strip() + return mac_addr + + error_msg = ('Interface "{0}" either not available or does not contain a hardware address'.format(iface)) + log.error(error_msg) + return error_msg + + def hw_addr(iface): ''' Return the hardware address (a.k.a. MAC address) for a given interface + + .. versionchanged:: 2016.11.4 + Added support for AIX + ''' + if salt.utils.is_aix(): + return _hw_addr_aix + iface_info, error = _get_iface_info(iface) if error is False: @@ -714,8 +743,10 @@ def _subnets(proto='inet', interfaces_=None): if proto == 'inet': subnet = 'netmask' + dflt_cidr = 32 elif proto == 'inet6': subnet = 'prefixlen' + dflt_cidr = 128 else: log.error('Invalid proto {0} calling subnets()'.format(proto)) return @@ -725,7 +756,10 @@ def _subnets(proto='inet', interfaces_=None): addrs.extend([addr for addr in ip_info.get('secondary', []) if addr.get('type') == proto]) for intf in addrs: - intf = ipaddress.ip_interface('{0}/{1}'.format(intf['address'], intf[subnet])) + if subnet in intf: + intf = ipaddress.ip_interface('{0}/{1}'.format(intf['address'], intf[subnet])) + else: + intf = ipaddress.ip_interface('{0}/{1}'.format(intf['address'], dflt_cidr)) if not intf.is_loopback: ret.add(intf.network) return [str(net) for net in sorted(ret)]