1# This file is part of Ansible
2#
3# Ansible is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation, either version 3 of the License, or
6# (at your option) any later version.
7#
8# Ansible is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
15
16from __future__ import (absolute_import, division, print_function)
17__metaclass__ = type
18
19import re
20
21from ansible.module_utils.facts.network.base import NetworkCollector
22from ansible.module_utils.facts.network.generic_bsd import GenericBsdIfconfigNetwork
23
24
25class SunOSNetwork(GenericBsdIfconfigNetwork):
26    """
27    This is the SunOS Network Class.
28    It uses the GenericBsdIfconfigNetwork.
29
30    Solaris can have different FLAGS and MTU for IPv4 and IPv6 on the same interface
31    so these facts have been moved inside the 'ipv4' and 'ipv6' lists.
32    """
33    platform = 'SunOS'
34
35    # Solaris 'ifconfig -a' will print interfaces twice, once for IPv4 and again for IPv6.
36    # MTU and FLAGS also may differ between IPv4 and IPv6 on the same interface.
37    # 'parse_interface_line()' checks for previously seen interfaces before defining
38    # 'current_if' so that IPv6 facts don't clobber IPv4 facts (or vice versa).
39    def get_interfaces_info(self, ifconfig_path):
40        interfaces = {}
41        current_if = {}
42        ips = dict(
43            all_ipv4_addresses=[],
44            all_ipv6_addresses=[],
45        )
46        rc, out, err = self.module.run_command([ifconfig_path, '-a'])
47
48        for line in out.splitlines():
49
50            if line:
51                words = line.split()
52
53                if re.match(r'^\S', line) and len(words) > 3:
54                    current_if = self.parse_interface_line(words, current_if, interfaces)
55                    interfaces[current_if['device']] = current_if
56                elif words[0].startswith('options='):
57                    self.parse_options_line(words, current_if, ips)
58                elif words[0] == 'nd6':
59                    self.parse_nd6_line(words, current_if, ips)
60                elif words[0] == 'ether':
61                    self.parse_ether_line(words, current_if, ips)
62                elif words[0] == 'media:':
63                    self.parse_media_line(words, current_if, ips)
64                elif words[0] == 'status:':
65                    self.parse_status_line(words, current_if, ips)
66                elif words[0] == 'lladdr':
67                    self.parse_lladdr_line(words, current_if, ips)
68                elif words[0] == 'inet':
69                    self.parse_inet_line(words, current_if, ips)
70                elif words[0] == 'inet6':
71                    self.parse_inet6_line(words, current_if, ips)
72                else:
73                    self.parse_unknown_line(words, current_if, ips)
74
75        # 'parse_interface_line' and 'parse_inet*_line' leave two dicts in the
76        # ipv4/ipv6 lists which is ugly and hard to read.
77        # This quick hack merges the dictionaries. Purely cosmetic.
78        for iface in interfaces:
79            for v in 'ipv4', 'ipv6':
80                combined_facts = {}
81                for facts in interfaces[iface][v]:
82                    combined_facts.update(facts)
83                if len(combined_facts.keys()) > 0:
84                    interfaces[iface][v] = [combined_facts]
85
86        return interfaces, ips
87
88    def parse_interface_line(self, words, current_if, interfaces):
89        device = words[0][0:-1]
90        if device not in interfaces:
91            current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
92        else:
93            current_if = interfaces[device]
94        flags = self.get_options(words[1])
95        v = 'ipv4'
96        if 'IPv6' in flags:
97            v = 'ipv6'
98        if 'LOOPBACK' in flags:
99            current_if['type'] = 'loopback'
100        current_if[v].append({'flags': flags, 'mtu': words[3]})
101        current_if['macaddress'] = 'unknown'    # will be overwritten later
102        return current_if
103
104    # Solaris displays single digit octets in MAC addresses e.g. 0:1:2:d:e:f
105    # Add leading zero to each octet where needed.
106    def parse_ether_line(self, words, current_if, ips):
107        macaddress = ''
108        for octet in words[1].split(':'):
109            octet = ('0' + octet)[-2:None]
110            macaddress += (octet + ':')
111        current_if['macaddress'] = macaddress[0:-1]
112
113
114class SunOSNetworkCollector(NetworkCollector):
115    _fact_class = SunOSNetwork
116    _platform = 'SunOS'
117