1# Copyright (C) 2019 Red Hat, Inc. 2# 3# This program 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# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. 15""" 16The junos_vlans class 17It is in this file where the current configuration (as dict) 18is compared to the provided configuration (as dict) and the command set 19necessary to bring the current configuration to it's desired end-state is 20created 21""" 22from __future__ import absolute_import, division, print_function 23__metaclass__ = type 24 25 26from ansible.module_utils.network.common.cfg.base import ConfigBase 27from ansible.module_utils.network.common.utils import to_list 28from ansible.module_utils.network.junos.facts.facts import Facts 29from ansible.module_utils.network.junos.junos import (locked_config, 30 load_config, 31 commit_configuration, 32 discard_changes, 33 tostring) 34from ansible.module_utils.network.common.netconf import (build_root_xml_node, 35 build_child_xml_node) 36 37 38class Vlans(ConfigBase): 39 """ 40 The junos_vlans class 41 """ 42 43 gather_subset = [ 44 '!all', 45 '!min', 46 ] 47 48 gather_network_resources = [ 49 'vlans', 50 ] 51 52 def __init__(self, module): 53 super(Vlans, self).__init__(module) 54 55 def get_vlans_facts(self): 56 """ Get the 'facts' (the current configuration) 57 58 :rtype: A dictionary 59 :returns: The current configuration as a dictionary 60 """ 61 facts, _warnings = Facts(self._module).get_facts( 62 self.gather_subset, self.gather_network_resources) 63 vlans_facts = facts['ansible_network_resources'].get('vlans') 64 if not vlans_facts: 65 return [] 66 return vlans_facts 67 68 def execute_module(self): 69 """ Execute the module 70 71 :rtype: A dictionary 72 :returns: The result from module execution 73 """ 74 result = {'changed': False} 75 warnings = list() 76 77 existing_vlans_facts = self.get_vlans_facts() 78 config_xmls = self.set_config(existing_vlans_facts) 79 80 with locked_config(self._module): 81 for config_xml in to_list(config_xmls): 82 diff = load_config(self._module, config_xml, warnings) 83 84 commit = not self._module.check_mode 85 if diff: 86 if commit: 87 commit_configuration(self._module) 88 else: 89 discard_changes(self._module) 90 result['changed'] = True 91 92 if self._module._diff: 93 result['diff'] = {'prepared': diff} 94 95 result['commands'] = config_xmls 96 97 changed_vlans_facts = self.get_vlans_facts() 98 99 result['before'] = existing_vlans_facts 100 if result['changed']: 101 result['after'] = changed_vlans_facts 102 103 result['warnings'] = warnings 104 return result 105 106 def set_config(self, existing_vlans_facts): 107 """ Collect the configuration from the args passed to the module, 108 collect the current configuration (as a dict from facts) 109 110 :rtype: A list 111 :returns: the commands necessary to migrate the current configuration 112 to the desired configuration 113 """ 114 want = self._module.params['config'] 115 have = existing_vlans_facts 116 resp = self.set_state(want, have) 117 return to_list(resp) 118 119 def set_state(self, want, have): 120 """ Select the appropriate function based on the state provided 121 122 :param want: the desired configuration as a dictionary 123 :param have: the current configuration as a dictionary 124 :rtype: A list 125 :returns: the commands necessary to migrate the current configuration 126 to the desired configuration 127 """ 128 root = build_root_xml_node('vlans') 129 state = self._module.params['state'] 130 if state == 'overridden': 131 config_xmls = self._state_overridden(want, have) 132 elif state == 'deleted': 133 config_xmls = self._state_deleted(want, have) 134 elif state == 'merged': 135 config_xmls = self._state_merged(want, have) 136 elif state == 'replaced': 137 config_xmls = self._state_replaced(want, have) 138 139 for xml in config_xmls: 140 root.append(xml) 141 142 return tostring(root) 143 144 def _state_replaced(self, want, have): 145 """ The command generator when state is replaced 146 147 :rtype: A list 148 :returns: the xml necessary to migrate the current configuration 149 to the desired configuration 150 """ 151 intf_xml = [] 152 intf_xml.extend(self._state_deleted(want, have)) 153 intf_xml.extend(self._state_merged(want, have)) 154 return intf_xml 155 156 def _state_overridden(self, want, have): 157 """ The command generator when state is overridden 158 159 :rtype: A list 160 :returns: the xml necessary to migrate the current configuration 161 to the desired configuration 162 """ 163 intf_xml = [] 164 intf_xml.extend(self._state_deleted(have, have)) 165 intf_xml.extend(self._state_merged(want, have)) 166 return intf_xml 167 168 def _state_merged(self, want, have): 169 """ The command generator when state is merged 170 171 :rtype: A list 172 :returns: the xml necessary to merge the provided into 173 the current configuration 174 """ 175 intf_xml = [] 176 177 for config in want: 178 vlan_name = str(config['name']) 179 vlan_id = str(config['vlan_id']) 180 vlan_description = config.get('description') 181 vlan_root = build_root_xml_node('vlan') 182 build_child_xml_node(vlan_root, 'name', vlan_name) 183 build_child_xml_node(vlan_root, 'vlan-id', vlan_id) 184 if vlan_description: 185 build_child_xml_node(vlan_root, 'description', 186 vlan_description) 187 intf_xml.append(vlan_root) 188 return intf_xml 189 190 def _state_deleted(self, want, have): 191 """ The command generator when state is deleted 192 193 :rtype: A list 194 :returns: the xml necessary to remove the current configuration 195 of the provided objects 196 """ 197 intf_xml = [] 198 199 if not want: 200 want = have 201 202 for config in want: 203 vlan_name = config['name'] 204 vlan_root = build_root_xml_node('vlan') 205 vlan_root.attrib.update({'delete': 'delete'}) 206 build_child_xml_node(vlan_root, 'name', vlan_name) 207 intf_xml.append(vlan_root) 208 return intf_xml 209