#!/usr/bin/python # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import (absolute_import, division, print_function) __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = ''' --- module: ce_sflow short_description: Manages sFlow configuration on HUAWEI CloudEngine switches. description: - Configure Sampled Flow (sFlow) to monitor traffic on an interface in real time, detect abnormal traffic, and locate the source of attack traffic, ensuring stable running of the network. author: QijunPan (@QijunPan) notes: - This module requires the netconf system service be enabled on the remote device being managed. - Recommended connection is C(netconf). - This module also works with C(local) connections for legacy playbooks. options: agent_ip: description: - Specifies the IPv4/IPv6 address of an sFlow agent. source_ip: description: - Specifies the source IPv4/IPv6 address of sFlow packets. collector_id: description: - Specifies the ID of an sFlow collector. This ID is used when you specify the collector in subsequent sFlow configuration. choices: ['1', '2'] collector_ip: description: - Specifies the IPv4/IPv6 address of the sFlow collector. collector_ip_vpn: description: - Specifies the name of a VPN instance. The value is a string of 1 to 31 case-sensitive characters, spaces not supported. When double quotation marks are used around the string, spaces are allowed in the string. The value C(_public_) is reserved and cannot be used as the VPN instance name. collector_datagram_size: description: - Specifies the maximum length of sFlow packets sent from an sFlow agent to an sFlow collector. The value is an integer, in bytes. It ranges from 1024 to 8100. The default value is 1400. collector_udp_port: description: - Specifies the UDP destination port number of sFlow packets. The value is an integer that ranges from 1 to 65535. The default value is 6343. collector_meth: description: - Configures the device to send sFlow packets through service interfaces, enhancing the sFlow packet forwarding capability. The enhanced parameter is optional. No matter whether you configure the enhanced mode, the switch determines to send sFlow packets through service cards or management port based on the routing information on the collector. When the value is meth, the device forwards sFlow packets at the control plane. When the value is enhanced, the device forwards sFlow packets at the forwarding plane to enhance the sFlow packet forwarding capacity. choices: ['meth', 'enhanced'] collector_description: description: - Specifies the description of an sFlow collector. The value is a string of 1 to 255 case-sensitive characters without spaces. sflow_interface: description: - Full name of interface for Flow Sampling or Counter. It must be a physical interface, Eth-Trunk, or Layer 2 subinterface. sample_collector: description: - Indicates the ID list of the collector. sample_rate: description: - Specifies the flow sampling rate in the format 1/rate. The value is an integer and ranges from 1 to 4294967295. The default value is 8192. sample_length: description: - Specifies the maximum length of sampled packets. The value is an integer and ranges from 18 to 512, in bytes. The default value is 128. sample_direction: description: - Enables flow sampling in the inbound or outbound direction. choices: ['inbound', 'outbound', 'both'] counter_collector: description: - Indicates the ID list of the counter collector. counter_interval: description: - Indicates the counter sampling interval. The value is an integer that ranges from 10 to 4294967295, in seconds. The default value is 20. export_route: description: - Configures the sFlow packets sent by the switch not to carry routing information. choices: ['enable', 'disable'] rate_limit: description: - Specifies the rate of sFlow packets sent from a card to the control plane. The value is an integer that ranges from 100 to 1500, in pps. type: str rate_limit_slot: description: - Specifies the slot where the rate of output sFlow packets is limited. If this parameter is not specified, the rate of sFlow packets sent from all cards to the control plane is limited. The value is an integer or a string of characters. type: str forward_enp_slot: description: - Enable the Embedded Network Processor (ENP) chip function. The switch uses the ENP chip to perform sFlow sampling, and the maximum sFlow sampling interval is 65535. If you set the sampling interval to be larger than 65535, the switch automatically restores it to 65535. The value is an integer or 'all'. type: str state: description: - Determines whether the config should be present or not on the device. default: present choices: ['present', 'absent'] ''' EXAMPLES = ''' --- - name: sflow module test hosts: ce128 connection: local gather_facts: no vars: cli: host: "{{ inventory_hostname }}" port: "{{ ansible_ssh_port }}" username: "{{ username }}" password: "{{ password }}" transport: cli tasks: - name: Configuring sFlow Agent ce_sflow: agent_ip: 6.6.6.6 provider: '{{ cli }}' - name: Configuring sFlow Collector ce_sflow: collector_id: 1 collector_ip: 7.7.7.7 collector_ip_vpn: vpn1 collector_description: Collector1 provider: '{{ cli }}' - name: Configure flow sampling. ce_sflow: sflow_interface: 10GE2/0/2 sample_collector: 1 sample_direction: inbound provider: '{{ cli }}' - name: Configure counter sampling. ce_sflow: sflow_interface: 10GE2/0/2 counter_collector: 1 counter_interval: 1000 provider: '{{ cli }}' ''' RETURN = ''' proposed: description: k/v pairs of parameters passed into module returned: verbose mode type: dict sample: {"agent_ip": "6.6.6.6", "state": "present"} existing: description: k/v pairs of existing configuration returned: verbose mode type: dict sample: {"agent": {}} end_state: description: k/v pairs of configuration after module execution returned: verbose mode type: dict sample: {"agent": {"family": "ipv4", "ipv4Addr": "1.2.3.4", "ipv6Addr": null}} updates: description: commands sent to the device returned: always type: list sample: ["sflow agent ip 6.6.6.6"] changed: description: check to see if a change was made on the device returned: always type: bool sample: true ''' import re from xml.etree import ElementTree from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr CE_NC_GET_SFLOW = """ %s %s """ def is_config_exist(cmp_cfg, test_cfg): """is configuration exist?""" if not cmp_cfg or not test_cfg: return False return bool(test_cfg in cmp_cfg) def is_valid_ip_vpn(vpname): """check ip vpn""" if not vpname: return False if vpname == "_public_": return False if len(vpname) < 1 or len(vpname) > 31: return False return True def get_ip_version(address): """get ip version fast""" if not address: return None if address.count(':') >= 2 and address.count(":") <= 7: return "ipv6" elif address.count('.') == 3: return "ipv4" else: return None def get_interface_type(interface): """get the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" if interface is None: return None if interface.upper().startswith('GE'): iftype = 'ge' elif interface.upper().startswith('10GE'): iftype = '10ge' elif interface.upper().startswith('25GE'): iftype = '25ge' elif interface.upper().startswith('4X10GE'): iftype = '4x10ge' elif interface.upper().startswith('40GE'): iftype = '40ge' elif interface.upper().startswith('100GE'): iftype = '100ge' elif interface.upper().startswith('VLANIF'): iftype = 'vlanif' elif interface.upper().startswith('LOOPBACK'): iftype = 'loopback' elif interface.upper().startswith('METH'): iftype = 'meth' elif interface.upper().startswith('ETH-TRUNK'): iftype = 'eth-trunk' elif interface.upper().startswith('VBDIF'): iftype = 'vbdif' elif interface.upper().startswith('NVE'): iftype = 'nve' elif interface.upper().startswith('TUNNEL'): iftype = 'tunnel' elif interface.upper().startswith('ETHERNET'): iftype = 'ethernet' elif interface.upper().startswith('FCOE-PORT'): iftype = 'fcoe-port' elif interface.upper().startswith('FABRIC-PORT'): iftype = 'fabric-port' elif interface.upper().startswith('STACK-PORT'): iftype = 'stack-port' elif interface.upper().startswith('NULL'): iftype = 'null' else: return None return iftype.lower() class Sflow(object): """Manages sFlow""" def __init__(self, argument_spec): self.spec = argument_spec self.module = None self.__init_module__() # module input info self.agent_ip = self.module.params['agent_ip'] self.agent_version = None self.source_ip = self.module.params['source_ip'] self.source_version = None self.export_route = self.module.params['export_route'] self.rate_limit = self.module.params['rate_limit'] self.rate_limit_slot = self.module.params['rate_limit_slot'] self.forward_enp_slot = self.module.params['forward_enp_slot'] self.collector_id = self.module.params['collector_id'] self.collector_ip = self.module.params['collector_ip'] self.collector_version = None self.collector_ip_vpn = self.module.params['collector_ip_vpn'] self.collector_datagram_size = self.module.params['collector_datagram_size'] self.collector_udp_port = self.module.params['collector_udp_port'] self.collector_meth = self.module.params['collector_meth'] self.collector_description = self.module.params['collector_description'] self.sflow_interface = self.module.params['sflow_interface'] self.sample_collector = self.module.params['sample_collector'] or list() self.sample_rate = self.module.params['sample_rate'] self.sample_length = self.module.params['sample_length'] self.sample_direction = self.module.params['sample_direction'] self.counter_collector = self.module.params['counter_collector'] or list() self.counter_interval = self.module.params['counter_interval'] self.state = self.module.params['state'] # state self.config = "" # current config self.sflow_dict = dict() self.changed = False self.updates_cmd = list() self.commands = list() self.results = dict() self.proposed = dict() self.existing = dict() self.end_state = dict() def __init_module__(self): """init module""" required_together = [("collector_id", "collector_ip")] self.module = AnsibleModule( argument_spec=self.spec, required_together=required_together, supports_check_mode=True) def check_response(self, con_obj, xml_name): """Check if response message is already succeed""" xml_str = con_obj.xml if "" not in xml_str: self.module.fail_json(msg='Error: %s failed.' % xml_name) def netconf_set_config(self, xml_str, xml_name): """netconf set config""" rcv_xml = set_nc_config(self.module, xml_str) if "" not in rcv_xml: self.module.fail_json(msg='Error: %s failed.' % xml_name) def get_sflow_dict(self): """ sflow config dict""" sflow_dict = dict(source=list(), agent=dict(), collector=list(), sampling=dict(), counter=dict(), export=dict()) conf_str = CE_NC_GET_SFLOW % ( self.sflow_interface, self.sflow_interface) if not self.collector_meth: conf_str = conf_str.replace("", "") rcv_xml = get_nc_config(self.module, conf_str) if "" in rcv_xml: return sflow_dict xml_str = rcv_xml.replace('\r', '').replace('\n', '').\ replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ replace('xmlns="http://www.huawei.com/netconf/vrp"', "") root = ElementTree.fromstring(xml_str) # get source info srcs = root.findall("sflow/sources/source") if srcs: for src in srcs: attrs = dict() for attr in src: if attr.tag in ["family", "ipv4Addr", "ipv6Addr"]: attrs[attr.tag] = attr.text sflow_dict["source"].append(attrs) # get agent info agent = root.find("sflow/agents/agent") if agent: for attr in agent: if attr.tag in ["family", "ipv4Addr", "ipv6Addr"]: sflow_dict["agent"][attr.tag] = attr.text # get collector info collectors = root.findall("sflow/collectors/collector") if collectors: for collector in collectors: attrs = dict() for attr in collector: if attr.tag in ["collectorID", "family", "ipv4Addr", "ipv6Addr", "vrfName", "datagramSize", "port", "description", "meth"]: attrs[attr.tag] = attr.text sflow_dict["collector"].append(attrs) # get sampling info sample = root.find("sflow/samplings/sampling") if sample: for attr in sample: if attr.tag in ["ifName", "collectorID", "direction", "length", "rate"]: sflow_dict["sampling"][attr.tag] = attr.text # get counter info counter = root.find("sflow/counters/counter") if counter: for attr in counter: if attr.tag in ["ifName", "collectorID", "interval"]: sflow_dict["counter"][attr.tag] = attr.text # get export info export = root.find("sflow/exports/export") if export: for attr in export: if attr.tag == "ExportRoute": sflow_dict["export"][attr.tag] = attr.text return sflow_dict def config_agent(self): """configures sFlow agent""" xml_str = '' if not self.agent_ip: return xml_str self.agent_version = get_ip_version(self.agent_ip) if not self.agent_version: self.module.fail_json(msg="Error: agent_ip is invalid.") if self.state == "present": if self.agent_ip != self.sflow_dict["agent"].get("ipv4Addr") \ and self.agent_ip != self.sflow_dict["agent"].get("ipv6Addr"): xml_str += '' xml_str += '%s' % self.agent_version if self.agent_version == "ipv4": xml_str += '%s' % self.agent_ip self.updates_cmd.append("sflow agent ip %s" % self.agent_ip) else: xml_str += '%s' % self.agent_ip self.updates_cmd.append("sflow agent ipv6 %s" % self.agent_ip) xml_str += '' else: if self.agent_ip == self.sflow_dict["agent"].get("ipv4Addr") \ or self.agent_ip == self.sflow_dict["agent"].get("ipv6Addr"): xml_str += '' self.updates_cmd.append("undo sflow agent") return xml_str def config_source(self): """configures the source IP address for sFlow packets""" xml_str = '' if not self.source_ip: return xml_str self.source_version = get_ip_version(self.source_ip) if not self.source_version: self.module.fail_json(msg="Error: source_ip is invalid.") src_dict = dict() for src in self.sflow_dict["source"]: if src.get("family") == self.source_version: src_dict = src break if self.state == "present": if self.source_ip != src_dict.get("ipv4Addr") \ and self.source_ip != src_dict.get("ipv6Addr"): xml_str += '' xml_str += '%s' % self.source_version if self.source_version == "ipv4": xml_str += '%s' % self.source_ip self.updates_cmd.append("sflow source ip %s" % self.source_ip) else: xml_str += '%s' % self.source_ip self.updates_cmd.append( "sflow source ipv6 %s" % self.source_ip) xml_str += '' else: if self.source_ip == src_dict.get("ipv4Addr"): xml_str += 'ipv4' self.updates_cmd.append("undo sflow source ip %s" % self.source_ip) elif self.source_ip == src_dict.get("ipv6Addr"): xml_str += 'ipv6' self.updates_cmd.append("undo sflow source ipv6 %s" % self.source_ip) return xml_str def config_collector(self): """creates an sFlow collector and sets or modifies optional parameters for the sFlow collector""" xml_str = '' if not self.collector_id: return xml_str if self.state == "present" and not self.collector_ip: return xml_str if self.collector_ip: self.collector_version = get_ip_version(self.collector_ip) if not self.collector_version: self.module.fail_json(msg="Error: collector_ip is invalid.") # get collector dict exist_dict = dict() for collector in self.sflow_dict["collector"]: if collector.get("collectorID") == self.collector_id: exist_dict = collector break change = False if self.state == "present": if not exist_dict: change = True elif self.collector_version != exist_dict.get("family"): change = True elif self.collector_version == "ipv4" and self.collector_ip != exist_dict.get("ipv4Addr"): change = True elif self.collector_version == "ipv6" and self.collector_ip != exist_dict.get("ipv6Addr"): change = True elif self.collector_ip_vpn and self.collector_ip_vpn != exist_dict.get("vrfName"): change = True elif not self.collector_ip_vpn and exist_dict.get("vrfName") != "_public_": change = True elif self.collector_udp_port and self.collector_udp_port != exist_dict.get("port"): change = True elif not self.collector_udp_port and exist_dict.get("port") != "6343": change = True elif self.collector_datagram_size and self.collector_datagram_size != exist_dict.get("datagramSize"): change = True elif not self.collector_datagram_size and exist_dict.get("datagramSize") != "1400": change = True elif self.collector_meth and self.collector_meth != exist_dict.get("meth"): change = True elif not self.collector_meth and exist_dict.get("meth") and exist_dict.get("meth") != "meth": change = True elif self.collector_description and self.collector_description != exist_dict.get("description"): change = True elif not self.collector_description and exist_dict.get("description"): change = True else: pass else: # absent # collector not exist if not exist_dict: return xml_str if self.collector_version and self.collector_version != exist_dict.get("family"): return xml_str if self.collector_version == "ipv4" and self.collector_ip != exist_dict.get("ipv4Addr"): return xml_str if self.collector_version == "ipv6" and self.collector_ip != exist_dict.get("ipv6Addr"): return xml_str if self.collector_ip_vpn and self.collector_ip_vpn != exist_dict.get("vrfName"): return xml_str if self.collector_udp_port and self.collector_udp_port != exist_dict.get("port"): return xml_str if self.collector_datagram_size and self.collector_datagram_size != exist_dict.get("datagramSize"): return xml_str if self.collector_meth and self.collector_meth != exist_dict.get("meth"): return xml_str if self.collector_description and self.collector_description != exist_dict.get("description"): return xml_str change = True if not change: return xml_str # update or delete if self.state == "absent": xml_str += '%s' % self.collector_id self.updates_cmd.append("undo collector %s" % self.collector_id) else: xml_str += '%s' % self.collector_id cmd = "sflow collector %s" % self.collector_id xml_str += '%s' % self.collector_version if self.collector_version == "ipv4": cmd += " ip %s" % self.collector_ip xml_str += '%s' % self.collector_ip else: cmd += " ipv6 %s" % self.collector_ip xml_str += '%s' % self.collector_ip if self.collector_ip_vpn: cmd += " vpn-instance %s" % self.collector_ip_vpn xml_str += '%s' % self.collector_ip_vpn if self.collector_datagram_size: cmd += " length %s" % self.collector_datagram_size xml_str += '%s' % self.collector_datagram_size if self.collector_udp_port: cmd += " udp-port %s" % self.collector_udp_port xml_str += '%s' % self.collector_udp_port if self.collector_description: cmd += " description %s" % self.collector_description xml_str += '%s' % self.collector_description else: xml_str += '' if self.collector_meth: if self.collector_meth == "enhanced": cmd += " enhanced" xml_str += '%s' % self.collector_meth self.updates_cmd.append(cmd) xml_str += "" return xml_str def config_sampling(self): """configure sflow sampling on an interface""" xml_str = '' if not self.sflow_interface: return xml_str if not self.sflow_dict["sampling"] and self.state == "absent": return xml_str self.updates_cmd.append("interface %s" % self.sflow_interface) if self.state == "present": xml_str += '%s' % self.sflow_interface else: xml_str += '%s' % self.sflow_interface # sample_collector if self.sample_collector: if self.sflow_dict["sampling"].get("collectorID") \ and self.sflow_dict["sampling"].get("collectorID") != "invalid": existing = self.sflow_dict["sampling"].get("collectorID").split(',') else: existing = list() if self.state == "present": diff = list(set(self.sample_collector) - set(existing)) if diff: self.updates_cmd.append( "sflow sampling collector %s" % ' '.join(diff)) new_set = list(self.sample_collector + existing) xml_str += '%s' % ','.join(list(set(new_set))) else: same = list(set(self.sample_collector) & set(existing)) if same: self.updates_cmd.append( "undo sflow sampling collector %s" % ' '.join(same)) xml_str += '%s' % ','.join(list(set(same))) # sample_rate if self.sample_rate: exist = bool(self.sample_rate == self.sflow_dict["sampling"].get("rate")) if self.state == "present" and not exist: self.updates_cmd.append( "sflow sampling rate %s" % self.sample_rate) xml_str += '%s' % self.sample_rate elif self.state == "absent" and exist: self.updates_cmd.append( "undo sflow sampling rate %s" % self.sample_rate) xml_str += '%s' % self.sample_rate # sample_length if self.sample_length: exist = bool(self.sample_length == self.sflow_dict["sampling"].get("length")) if self.state == "present" and not exist: self.updates_cmd.append( "sflow sampling length %s" % self.sample_length) xml_str += '%s' % self.sample_length elif self.state == "absent" and exist: self.updates_cmd.append( "undo sflow sampling length %s" % self.sample_length) xml_str += '%s' % self.sample_length # sample_direction if self.sample_direction: direction = list() if self.sample_direction == "both": direction = ["inbound", "outbound"] else: direction.append(self.sample_direction) existing = list() if self.sflow_dict["sampling"].get("direction"): if self.sflow_dict["sampling"].get("direction") == "both": existing = ["inbound", "outbound"] else: existing.append( self.sflow_dict["sampling"].get("direction")) if self.state == "present": diff = list(set(direction) - set(existing)) if diff: new_set = list(set(direction + existing)) self.updates_cmd.append( "sflow sampling %s" % ' '.join(diff)) if len(new_set) > 1: new_dir = "both" else: new_dir = new_set[0] xml_str += '%s' % new_dir else: same = list(set(existing) & set(direction)) if same: self.updates_cmd.append("undo sflow sampling %s" % ' '.join(same)) if len(same) > 1: del_dir = "both" else: del_dir = same[0] xml_str += '%s' % del_dir if xml_str.endswith(""): self.updates_cmd.pop() return "" xml_str += '' return xml_str def config_counter(self): """configures sflow counter on an interface""" xml_str = '' if not self.sflow_interface: return xml_str if not self.sflow_dict["counter"] and self.state == "absent": return xml_str self.updates_cmd.append("interface %s" % self.sflow_interface) if self.state == "present": xml_str += '%s' % self.sflow_interface else: xml_str += '%s' % self.sflow_interface # counter_collector if self.counter_collector: if self.sflow_dict["counter"].get("collectorID") \ and self.sflow_dict["counter"].get("collectorID") != "invalid": existing = self.sflow_dict["counter"].get("collectorID").split(',') else: existing = list() if self.state == "present": diff = list(set(self.counter_collector) - set(existing)) if diff: self.updates_cmd.append("sflow counter collector %s" % ' '.join(diff)) new_set = list(self.counter_collector + existing) xml_str += '%s' % ','.join(list(set(new_set))) else: same = list(set(self.counter_collector) & set(existing)) if same: self.updates_cmd.append( "undo sflow counter collector %s" % ' '.join(same)) xml_str += '%s' % ','.join(list(set(same))) # counter_interval if self.counter_interval: exist = bool(self.counter_interval == self.sflow_dict["counter"].get("interval")) if self.state == "present" and not exist: self.updates_cmd.append( "sflow counter interval %s" % self.counter_interval) xml_str += '%s' % self.counter_interval elif self.state == "absent" and exist: self.updates_cmd.append( "undo sflow counter interval %s" % self.counter_interval) xml_str += '%s' % self.counter_interval if xml_str.endswith(""): self.updates_cmd.pop() return "" xml_str += '' return xml_str def config_export(self): """configure sflow export""" xml_str = '' if not self.export_route: return xml_str if self.export_route == "enable": if self.sflow_dict["export"] and self.sflow_dict["export"].get("ExportRoute") == "disable": xml_str = 'disable' self.updates_cmd.append("undo sflow export extended-route-data disable") else: # disable if not self.sflow_dict["export"] or self.sflow_dict["export"].get("ExportRoute") != "disable": xml_str = 'disable' self.updates_cmd.append("sflow export extended-route-data disable") return xml_str def netconf_load_config(self, xml_str): """load sflow config by netconf""" if not xml_str: return xml_cfg = """ %s """ % xml_str self.netconf_set_config(xml_cfg, "SET_SFLOW") self.changed = True def check_params(self): """Check all input params""" # check agent_ip if self.agent_ip: self.agent_ip = self.agent_ip.upper() if not check_ip_addr(self.agent_ip): self.module.fail_json(msg="Error: agent_ip is invalid.") # check source_ip if self.source_ip: self.source_ip = self.source_ip.upper() if not check_ip_addr(self.source_ip): self.module.fail_json(msg="Error: source_ip is invalid.") # check collector if self.collector_id: # check collector_ip and collector_ip_vpn if self.collector_ip: self.collector_ip = self.collector_ip.upper() if not check_ip_addr(self.collector_ip): self.module.fail_json( msg="Error: collector_ip is invalid.") if self.collector_ip_vpn and not is_valid_ip_vpn(self.collector_ip_vpn): self.module.fail_json( msg="Error: collector_ip_vpn is invalid.") # check collector_datagram_size ranges from 1024 to 8100 if self.collector_datagram_size: if not self.collector_datagram_size.isdigit(): self.module.fail_json( msg="Error: collector_datagram_size is not digit.") if int(self.collector_datagram_size) < 1024 or int(self.collector_datagram_size) > 8100: self.module.fail_json( msg="Error: collector_datagram_size is not ranges from 1024 to 8100.") # check collector_udp_port ranges from 1 to 65535 if self.collector_udp_port: if not self.collector_udp_port.isdigit(): self.module.fail_json( msg="Error: collector_udp_port is not digit.") if int(self.collector_udp_port) < 1 or int(self.collector_udp_port) > 65535: self.module.fail_json( msg="Error: collector_udp_port is not ranges from 1 to 65535.") # check collector_description 1 to 255 case-sensitive characters if self.collector_description: if self.collector_description.count(" "): self.module.fail_json( msg="Error: collector_description should without spaces.") if len(self.collector_description) < 1 or len(self.collector_description) > 255: self.module.fail_json( msg="Error: collector_description is not ranges from 1 to 255.") # check sflow_interface if self.sflow_interface: intf_type = get_interface_type(self.sflow_interface) if not intf_type: self.module.fail_json(msg="Error: intf_type is invalid.") if intf_type not in ['ge', '10ge', '25ge', '4x10ge', '40ge', '100ge', 'eth-trunk']: self.module.fail_json( msg="Error: interface %s is not support sFlow." % self.sflow_interface) # check sample_collector if self.sample_collector: self.sample_collector.sort() if self.sample_collector not in [["1"], ["2"], ["1", "2"]]: self.module.fail_json( msg="Error: sample_collector is invalid.") # check sample_rate ranges from 1 to 4294967295 if self.sample_rate: if not self.sample_rate.isdigit(): self.module.fail_json( msg="Error: sample_rate is not digit.") if int(self.sample_rate) < 1 or int(self.sample_rate) > 4294967295: self.module.fail_json( msg="Error: sample_rate is not ranges from 1 to 4294967295.") # check sample_length ranges from 18 to 512 if self.sample_length: if not self.sample_length.isdigit(): self.module.fail_json( msg="Error: sample_rate is not digit.") if int(self.sample_length) < 18 or int(self.sample_length) > 512: self.module.fail_json( msg="Error: sample_length is not ranges from 18 to 512.") # check counter_collector if self.counter_collector: self.counter_collector.sort() if self.counter_collector not in [["1"], ["2"], ["1", "2"]]: self.module.fail_json( msg="Error: counter_collector is invalid.") # counter_interval ranges from 10 to 4294967295 if self.counter_interval: if not self.counter_interval.isdigit(): self.module.fail_json( msg="Error: counter_interval is not digit.") if int(self.counter_interval) < 10 or int(self.counter_interval) > 4294967295: self.module.fail_json( msg="Error: sample_length is not ranges from 10 to 4294967295.") if self.rate_limit or self.rate_limit_slot or self.forward_enp_slot: self.module.fail_json(msg="Error: The following parameters cannot be configured" "because XML mode is not supported:rate_limit,rate_limit_slot,forward_enp_slot.") def get_proposed(self): """get proposed info""" # base config if self.agent_ip: self.proposed["agent_ip"] = self.agent_ip if self.source_ip: self.proposed["source_ip"] = self.source_ip if self.export_route: self.proposed["export_route"] = self.export_route if self.rate_limit: self.proposed["rate_limit"] = self.rate_limit self.proposed["rate_limit_slot"] = self.rate_limit_slot if self.forward_enp_slot: self.proposed["forward_enp_slot"] = self.forward_enp_slot if self.collector_id: self.proposed["collector_id"] = self.collector_id if self.collector_ip: self.proposed["collector_ip"] = self.collector_ip self.proposed["collector_ip_vpn"] = self.collector_ip_vpn if self.collector_datagram_size: self.proposed[ "collector_datagram_size"] = self.collector_datagram_size if self.collector_udp_port: self.proposed["collector_udp_port"] = self.collector_udp_port if self.collector_meth: self.proposed["collector_meth"] = self.collector_meth if self.collector_description: self.proposed[ "collector_description"] = self.collector_description # sample and counter config if self.sflow_interface: self.proposed["sflow_interface"] = self.sflow_interface if self.sample_collector: self.proposed["sample_collector"] = self.sample_collector if self.sample_rate: self.proposed["sample_rate"] = self.sample_rate if self.sample_length: self.proposed["sample_length"] = self.sample_length if self.sample_direction: self.proposed["sample_direction"] = self.sample_direction if self.counter_collector: self.proposed["counter_collector"] = self.counter_collector if self.counter_interval: self.proposed["counter_interval"] = self.counter_interval self.proposed["state"] = self.state def get_existing(self): """get existing info""" if not self.sflow_dict: return if self.agent_ip: self.existing["agent"] = self.sflow_dict["agent"] if self.source_ip: self.existing["source"] = self.sflow_dict["source"] if self.collector_id: self.existing["collector"] = self.sflow_dict["collector"] if self.export_route: self.existing["export"] = self.sflow_dict["export"] if self.sflow_interface: self.existing["sampling"] = self.sflow_dict["sampling"] self.existing["counter"] = self.sflow_dict["counter"] def get_end_state(self): """get end state info""" sflow_dict = self.get_sflow_dict() if not sflow_dict: return if self.agent_ip: self.end_state["agent"] = sflow_dict["agent"] if self.source_ip: self.end_state["source"] = sflow_dict["source"] if self.collector_id: self.end_state["collector"] = sflow_dict["collector"] if self.export_route: self.end_state["export"] = sflow_dict["export"] if self.sflow_interface: self.end_state["sampling"] = sflow_dict["sampling"] self.end_state["counter"] = sflow_dict["counter"] if self.existing == self.end_state: self.changed = False def work(self): """worker""" self.check_params() self.sflow_dict = self.get_sflow_dict() self.get_existing() self.get_proposed() # deal present or absent xml_str = '' if self.export_route: xml_str += self.config_export() if self.agent_ip: xml_str += self.config_agent() if self.source_ip: xml_str += self.config_source() if self.state == "present": if self.collector_id and self.collector_ip: xml_str += self.config_collector() if self.sflow_interface: xml_str += self.config_sampling() xml_str += self.config_counter() else: if self.sflow_interface: xml_str += self.config_sampling() xml_str += self.config_counter() if self.collector_id: xml_str += self.config_collector() if xml_str: self.netconf_load_config(xml_str) self.changed = True self.get_end_state() self.results['changed'] = self.changed self.results['proposed'] = self.proposed self.results['existing'] = self.existing self.results['end_state'] = self.end_state if self.changed: self.results['updates'] = self.updates_cmd else: self.results['updates'] = list() self.module.exit_json(**self.results) def main(): """Module main""" argument_spec = dict( agent_ip=dict(required=False, type='str'), source_ip=dict(required=False, type='str'), export_route=dict(required=False, type='str', choices=['enable', 'disable']), rate_limit=dict(required=False, removed_in_version=2.13, type='str'), rate_limit_slot=dict(required=False, removed_in_version=2.13, type='str'), forward_enp_slot=dict(required=False, removed_in_version=2.13, type='str'), collector_id=dict(required=False, type='str', choices=['1', '2']), collector_ip=dict(required=False, type='str'), collector_ip_vpn=dict(required=False, type='str'), collector_datagram_size=dict(required=False, type='str'), collector_udp_port=dict(required=False, type='str'), collector_meth=dict(required=False, type='str', choices=['meth', 'enhanced']), collector_description=dict(required=False, type='str'), sflow_interface=dict(required=False, type='str'), sample_collector=dict(required=False, type='list'), sample_rate=dict(required=False, type='str'), sample_length=dict(required=False, type='str'), sample_direction=dict(required=False, type='str', choices=['inbound', 'outbound', 'both']), counter_collector=dict(required=False, type='list'), counter_interval=dict(required=False, type='str'), state=dict(required=False, default='present', choices=['present', 'absent']) ) argument_spec.update(ce_argument_spec) module = Sflow(argument_spec) module.work() if __name__ == '__main__': main()