From 30be3a4a9ff85962344763b2098bf65afc0d345c Mon Sep 17 00:00:00 2001 From: Xu Yuandong Date: Mon, 28 Oct 2019 09:51:20 +0800 Subject: [PATCH] Revert "ce_sflow: update to fix bugs." (#64001) * Revert "ce_sflow: update to fix bugs. (#59349)" This reverts commit 77115247180e0dcb1178bed8654a7a6264bfa476. * Update ce_sflow.py --- .../modules/network/cloudengine/ce_sflow.py | 228 +++++++++++++++--- 1 file changed, 191 insertions(+), 37 deletions(-) diff --git a/lib/ansible/modules/network/cloudengine/ce_sflow.py b/lib/ansible/modules/network/cloudengine/ce_sflow.py index 7362e611d95..deaf9a4b81c 100644 --- a/lib/ansible/modules/network/cloudengine/ce_sflow.py +++ b/lib/ansible/modules/network/cloudengine/ce_sflow.py @@ -33,13 +33,13 @@ author: QijunPan (@QijunPan) options: agent_ip: description: - - Specifies the IPv4/IPv6 address of a sFlow agent. + - 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 a sFlow collector. This ID is used when you specify + - 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: @@ -72,7 +72,7 @@ options: choices: ['meth', 'enhanced'] collector_description: description: - - Specifies the description of a sFlow collector. + - Specifies the description of an sFlow collector. The value is a string of 1 to 255 case-sensitive characters without spaces. sflow_interface: description: @@ -104,6 +104,30 @@ options: 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. + version_added: "2.10" + 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. + version_added: "2.10" + 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'. + version_added: "2.10" + type: str state: description: - Determines whether the config should be present or not @@ -187,7 +211,8 @@ changed: import re from xml.etree import ElementTree from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr, to_string +from ansible.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr +from ansible.module_utils.network.cloudengine.ce import get_config, load_config CE_NC_GET_SFLOW = """ @@ -331,6 +356,33 @@ def get_interface_type(interface): return iftype.lower() +def get_rate_limit(config): + """get sflow management-plane export rate-limit info""" + + get = re.findall(r"sflow management-plane export rate-limit ([0-9]+) slot ([0-9]+)", config) + if not get: + get = re.findall(r"sflow management-plane export rate-limit ([0-9]+)", config) + if not get: + return None + else: + return dict(rate_limit=get[0]) + else: + limit = list() + for slot in get: + limit.append(dict(rate_limit=slot[0], slot_id=slot[1])) + return limit + + +def get_forward_enp(config): + """get assign forward enp sflow enable slot info""" + + get = re.findall(r"assign forward enp sflow enable slot (\S+)", config) + if not get: + return None + else: + return list(get) + + class Sflow(object): """Manages sFlow""" @@ -345,6 +397,9 @@ class Sflow(object): 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 @@ -394,11 +449,46 @@ class Sflow(object): if "" not in rcv_xml: self.module.fail_json(msg='Error: %s failed.' % xml_name) - def netconf_get_config(self, xml_str): - """netconf set config""" + def cli_load_config(self, commands): + """load config by cli""" + + if not self.module.check_mode: + load_config(self.module, commands) + + def get_current_config(self): + """get current configuration""" + + flags = list() + exp = "" + if self.rate_limit: + exp += "assign sflow management-plane export rate-limit %s" % self.rate_limit + if self.rate_limit_slot: + exp += " slot %s" % self.rate_limit_slot + exp += "$" + + if self.forward_enp_slot: + if exp: + exp += "|" + exp += "assign forward enp sflow enable slot %s$" % self.forward_enp_slot + + if exp: + exp = " | ignore-case include " + exp + flags.append(exp) + return get_config(self.module, flags) + else: + return "" + + def cli_add_command(self, command, undo=False): + """add command to self.update_cmd and self.commands""" + + if undo and command.lower() not in ["quit", "return"]: + cmd = "undo " + command + else: + cmd = command - if xml_str is not None: - return get_nc_config(self.module, xml_str) + self.commands.append(cmd) # set to device + if command.lower() not in ["quit", "return"]: + self.updates_cmd.append(cmd) # show updates result def get_sflow_dict(self): """ sflow config dict""" @@ -411,7 +501,7 @@ class Sflow(object): if not self.collector_meth: conf_str = conf_str.replace("", "") - rcv_xml = self.netconf_get_config(conf_str) + rcv_xml = get_nc_config(self.module, conf_str) if "" in rcv_xml: return sflow_dict @@ -422,7 +512,7 @@ class Sflow(object): root = ElementTree.fromstring(xml_str) # get source info - srcs = root.findall("sflow/sources/source") + srcs = root.findall("data/sflow/sources/source") if srcs: for src in srcs: attrs = dict() @@ -432,14 +522,14 @@ class Sflow(object): sflow_dict["source"].append(attrs) # get agent info - agent = root.find("sflow/agents/agent") + agent = root.find("data/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") + collectors = root.findall("data/sflow/collectors/collector") if collectors: for collector in collectors: attrs = dict() @@ -450,21 +540,21 @@ class Sflow(object): sflow_dict["collector"].append(attrs) # get sampling info - sample = root.find("sflow/samplings/sampling") + sample = root.find("data/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") + counter = root.find("data/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") + export = root.find("data/sflow/exports/export") if export: for attr in export: if attr.tag == "ExportRoute": @@ -497,15 +587,11 @@ class Sflow(object): xml_str += '' else: - flag = False - if self.agent_ip == self.sflow_dict["agent"].get("ipv4Addr"): - self.updates_cmd.append("undo sflow agent ip %s" % self.agent_ip) - flag = True - elif self.agent_ip == self.sflow_dict["agent"].get("ipv6Addr"): - self.updates_cmd.append("undo sflow agent ipv6 %s" % self.agent_ip) - flag = True - if flag is True: + 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): @@ -630,7 +716,7 @@ class Sflow(object): # update or delete if self.state == "absent": xml_str += '%s' % self.collector_id - self.updates_cmd.append("undo sflow collector %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 @@ -846,6 +932,29 @@ class Sflow(object): return xml_str + def config_assign(self): + """configure assign""" + + # assign sflow management-plane export rate-limit rate-limit [ slot slot-id ] + if self.rate_limit: + cmd = "assign sflow management-plane export rate-limit %s" % self.rate_limit + if self.rate_limit_slot: + cmd += " slot %s" % self.rate_limit_slot + exist = is_config_exist(self.config, cmd) + if self.state == "present" and not exist: + self.cli_add_command(cmd) + elif self.state == "absent" and exist: + self.cli_add_command(cmd, undo=True) + + # assign forward enp sflow enable slot { slot-id | all } + if self.forward_enp_slot: + cmd = "assign forward enp sflow enable slot %s" % self.forward_enp_slot + exist = is_config_exist(self.config, cmd) + if self.state == "present" and not exist: + self.cli_add_command(cmd) + elif self.state == "absent" and exist: + self.cli_add_command(cmd, undo=True) + def netconf_load_config(self, xml_str): """load sflow config by netconf""" @@ -926,12 +1035,11 @@ class Sflow(object): msg="Error: interface %s is not support sFlow." % self.sflow_interface) # check sample_collector - if 0 < len(self.sample_collector) < 3: - self.sample_collector = [str(i) for i in self.sample_collector] - for id in self.sample_collector: - if id not in ("1", "2"): - self.module.fail_json( - msg="Error: sample_collector is invalid.") + 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: @@ -952,12 +1060,11 @@ class Sflow(object): msg="Error: sample_length is not ranges from 18 to 512.") # check counter_collector - if 0 < len(self.counter_collector) < 3: - self.counter_collector = [str(i) for i in self.counter_collector] - for id in self.counter_collector: - if id not in ("1", "2"): - self.module.fail_json( - msg="Error: counter_collector is invalid.") + 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: @@ -968,6 +1075,24 @@ class Sflow(object): self.module.fail_json( msg="Error: sample_length is not ranges from 10 to 4294967295.") + # check rate_limit ranges from 100 to 1500 and check rate_limit_slot + if self.rate_limit: + if not self.rate_limit.isdigit(): + self.module.fail_json(msg="Error: rate_limit is not digit.") + if int(self.rate_limit) < 100 or int(self.rate_limit) > 1500: + self.module.fail_json( + msg="Error: rate_limit is not ranges from 100 to 1500.") + if self.rate_limit_slot and not self.rate_limit_slot.isdigit(): + self.module.fail_json( + msg="Error: rate_limit_slot is not digit.") + + # check forward_enp_slot + if self.forward_enp_slot: + self.forward_enp_slot.lower() + if not self.forward_enp_slot.isdigit() and self.forward_enp_slot != "all": + self.module.fail_json( + msg="Error: forward_enp_slot is invalid.") + def get_proposed(self): """get proposed info""" @@ -978,6 +1103,11 @@ class Sflow(object): 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: @@ -1015,6 +1145,13 @@ class Sflow(object): def get_existing(self): """get existing info""" + if self.config: + if self.rate_limit: + self.existing["rate_limit"] = get_rate_limit(self.config) + if self.forward_enp_slot: + self.existing["forward_enp_slot"] = get_forward_enp( + self.config) + if not self.sflow_dict: return @@ -1034,7 +1171,13 @@ class Sflow(object): def get_end_state(self): """get end state info""" - # else: + config = self.get_current_config() + if config: + if self.rate_limit: + self.end_state["rate_limit"] = get_rate_limit(config) + if self.forward_enp_slot: + self.end_state["forward_enp_slot"] = get_forward_enp(config) + sflow_dict = self.get_sflow_dict() if not sflow_dict: return @@ -1057,6 +1200,7 @@ class Sflow(object): self.check_params() self.sflow_dict = self.get_sflow_dict() + self.config = self.get_current_config() self.get_existing() self.get_proposed() @@ -1082,6 +1226,13 @@ class Sflow(object): if self.collector_id: xml_str += self.config_collector() + if self.rate_limit or self.forward_enp_slot: + self.config_assign() + + if self.commands: + self.cli_load_config(self.commands) + self.changed = True + if xml_str: self.netconf_load_config(xml_str) self.changed = True @@ -1107,6 +1258,9 @@ def main(): source_ip=dict(required=False, type='str'), export_route=dict(required=False, type='str', choices=['enable', 'disable']), + rate_limit=dict(required=False, type='str'), + rate_limit_slot=dict(required=False, type='str'), + forward_enp_slot=dict(required=False, 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'),