#!/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_snmp_target_host
short_description: Manages SNMP target host configuration on HUAWEI CloudEngine switches.
description:
- Manages SNMP target host configurations on HUAWEI CloudEngine switches.
author:
- wangdezhuang (@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:
version:
description:
- Version(s) Supported by SNMP Engine.
choices: ['none', 'v1', 'v2c', 'v3', 'v1v2c', 'v1v3', 'v2cv3', 'all']
connect_port:
description:
- Udp port used by SNMP agent to connect the Network management.
host_name:
description:
- Unique name to identify target host entry.
address:
description:
- Network Address.
notify_type:
description:
- To configure notify type as trap or inform.
choices: ['trap','inform']
vpn_name:
description:
- VPN instance Name.
recv_port:
description:
- UDP Port number used by network management to receive alarm messages.
security_model:
description:
- Security Model.
choices: ['v1','v2c', 'v3']
security_name:
description:
- Security Name.
security_name_v3:
description:
- Security Name V3.
security_level:
description:
- Security level indicating whether to use authentication and encryption.
choices: ['noAuthNoPriv','authentication', 'privacy']
is_public_net:
description:
- To enable or disable Public Net-manager for target Host.
default: no_use
choices: ['no_use','true','false']
interface_name:
description:
- Name of the interface to send the trap message.
'''
EXAMPLES = '''
- name: CloudEngine snmp target host test
hosts: cloudengine
connection: local
gather_facts: no
vars:
cli:
host: "{{ inventory_hostname }}"
port: "{{ ansible_ssh_port }}"
username: "{{ username }}"
password: "{{ password }}"
transport: cli
tasks:
- name: "Config SNMP version"
ce_snmp_target_host:
state: present
version: v2cv3
provider: "{{ cli }}"
- name: "Config SNMP target host"
ce_snmp_target_host:
state: present
host_name: test1
address: 1.1.1.1
notify_type: trap
vpn_name: js
security_model: v2c
security_name: wdz
provider: "{{ cli }}"
'''
RETURN = '''
changed:
description: check to see if a change was made on the device
returned: always
type: bool
sample: true
proposed:
description: k/v pairs of parameters passed into module
returned: always
type: dict
sample: {"address": "10.135.182.158", "host_name": "test2",
"notify_type": "trap", "security_level": "authentication",
"security_model": "v3", "security_name_v3": "wdz",
"state": "present", "vpn_name": "js"}
existing:
description: k/v pairs of existing aaa server
returned: always
type: dict
sample: {}
end_state:
description: k/v pairs of aaa params after module execution
returned: always
type: dict
sample: {"target host info": [{"address": "10.135.182.158", "domain": "snmpUDPDomain",
"nmsName": "test2", "notifyType": "trap",
"securityLevel": "authentication", "securityModel": "v3",
"securityNameV3": "wdz", "vpnInstanceName": "js"}]}
updates:
description: command sent to the device
returned: always
type: list
sample: ["snmp-agent target-host host-name test2 trap address udp-domain 10.135.182.158 vpn-instance js params securityname wdz v3 authentication"]
'''
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, load_config, check_ip_addr
# get snmp version
CE_GET_SNMP_VERSION = """
"""
# merge snmp version
CE_MERGE_SNMP_VERSION = """
%s
"""
# get snmp target host
CE_GET_SNMP_TARGET_HOST_HEADER = """
"""
CE_GET_SNMP_TARGET_HOST_TAIL = """
"""
# merge snmp target host
CE_MERGE_SNMP_TARGET_HOST_HEADER = """
%s
"""
CE_MERGE_SNMP_TARGET_HOST_TAIL = """
"""
# create snmp target host
CE_CREATE_SNMP_TARGET_HOST_HEADER = """
%s
"""
CE_CREATE_SNMP_TARGET_HOST_TAIL = """
"""
# delete snmp target host
CE_DELETE_SNMP_TARGET_HOST_HEADER = """
%s
"""
CE_DELETE_SNMP_TARGET_HOST_TAIL = """
"""
# get snmp listen port
CE_GET_SNMP_PORT = """
"""
# merge snmp listen port
CE_MERGE_SNMP_PORT = """
%s
"""
INTERFACE_TYPE = ['ethernet', 'eth-trunk', 'tunnel', 'null', 'loopback',
'vlanif', '100ge', '40ge', 'mtunnel', '10ge', 'ge', 'meth', 'vbdif', 'nve']
class SnmpTargetHost(object):
""" Manages SNMP target host configuration """
def __init__(self, **kwargs):
""" Class init """
# module
argument_spec = kwargs["argument_spec"]
self.spec = argument_spec
required_together = [("address", "notify_type"), ("address", "notify_type")]
required_if = [
["security_model", "v1", ["security_name"]],
["security_model", "v2c", ["security_name"]],
["security_model", "v3", ["security_name_v3"]]
]
self.module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
required_if=required_if,
supports_check_mode=True
)
# module args
self.state = self.module.params['state']
self.version = self.module.params['version']
self.connect_port = self.module.params['connect_port']
self.host_name = self.module.params['host_name']
self.domain = "snmpUDPDomain"
self.address = self.module.params['address']
self.notify_type = self.module.params['notify_type']
self.vpn_name = self.module.params['vpn_name']
self.recv_port = self.module.params['recv_port']
self.security_model = self.module.params['security_model']
self.security_name = self.module.params['security_name']
self.security_name_v3 = self.module.params['security_name_v3']
self.security_level = self.module.params['security_level']
self.is_public_net = self.module.params['is_public_net']
self.interface_name = self.module.params['interface_name']
# config
self.cur_cli_cfg = dict()
self.cur_netconf_cfg = dict()
self.end_netconf_cfg = dict()
# state
self.changed = False
self.updates_cmd = list()
self.results = dict()
self.proposed = dict()
self.existing = dict()
self.end_state = dict()
def netconf_get_config(self, conf_str):
""" Get configure by netconf """
xml_str = get_nc_config(self.module, conf_str)
return xml_str
def netconf_set_config(self, conf_str):
""" Set configure by netconf """
xml_str = set_nc_config(self.module, conf_str)
return xml_str
def check_cli_args(self):
""" Check invalid cli args """
if self.connect_port:
if int(self.connect_port) != 161 and (int(self.connect_port) > 65535 or int(self.connect_port) < 1025):
self.module.fail_json(
msg='Error: The value of connect_port %s is out of [161, 1025 - 65535].' % self.connect_port)
def check_netconf_args(self, result):
""" Check invalid netconf args """
need_cfg = True
same_flag = True
delete_flag = False
result["target_host_info"] = []
if self.host_name:
if len(self.host_name) > 32 or len(self.host_name) < 1:
self.module.fail_json(
msg='Error: The len of host_name is out of [1 - 32].')
if self.vpn_name and self.is_public_net != 'no_use':
if self.is_public_net == "true":
self.module.fail_json(
msg='Error: Do not support vpn_name and is_public_net at the same time.')
conf_str = CE_GET_SNMP_TARGET_HOST_HEADER
if self.domain:
conf_str += ""
if self.address:
if not check_ip_addr(ipaddr=self.address):
self.module.fail_json(
msg='Error: The host address [%s] is invalid.' % self.address)
conf_str += "
"
if self.notify_type:
conf_str += ""
if self.vpn_name:
if len(self.vpn_name) > 31 or len(self.vpn_name) < 1:
self.module.fail_json(
msg='Error: The len of vpn_name is out of [1 - 31].')
conf_str += ""
if self.recv_port:
if int(self.recv_port) > 65535 or int(self.recv_port) < 0:
self.module.fail_json(
msg='Error: The value of recv_port is out of [0 - 65535].')
conf_str += ""
if self.security_model:
conf_str += ""
if self.security_name:
if len(self.security_name) > 32 or len(self.security_name) < 1:
self.module.fail_json(
msg='Error: The len of security_name is out of [1 - 32].')
conf_str += ""
if self.security_name_v3:
if len(self.security_name_v3) > 32 or len(self.security_name_v3) < 1:
self.module.fail_json(
msg='Error: The len of security_name_v3 is out of [1 - 32].')
conf_str += ""
if self.security_level:
conf_str += ""
if self.is_public_net != 'no_use':
conf_str += ""
if self.interface_name:
if len(self.interface_name) > 63 or len(self.interface_name) < 1:
self.module.fail_json(
msg='Error: The len of interface_name is out of [1 - 63].')
find_flag = False
for item in INTERFACE_TYPE:
if item in self.interface_name.lower():
find_flag = True
break
if not find_flag:
self.module.fail_json(
msg='Error: Please input full name of interface_name.')
conf_str += ""
conf_str += CE_GET_SNMP_TARGET_HOST_TAIL
recv_xml = self.netconf_get_config(conf_str=conf_str)
if "" in recv_xml:
if self.state == "present":
same_flag = False
else:
delete_flag = False
else:
xml_str = recv_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)
target_host_info = root.findall(
"snmp/targetHosts/targetHost")
if target_host_info:
for tmp in target_host_info:
tmp_dict = dict()
for site in tmp:
if site.tag in ["nmsName", "domain", "address", "notifyType", "vpnInstanceName",
"portNumber", "securityModel", "securityName", "securityNameV3",
"securityLevel", "isPublicNet", "interface-name"]:
tmp_dict[site.tag] = site.text
result["target_host_info"].append(tmp_dict)
if result["target_host_info"]:
for tmp in result["target_host_info"]:
same_flag = True
if "nmsName" in tmp.keys():
if tmp["nmsName"] != self.host_name:
same_flag = False
else:
delete_flag = True
if "domain" in tmp.keys():
if tmp["domain"] != self.domain:
same_flag = False
if "address" in tmp.keys():
if tmp["address"] != self.address:
same_flag = False
if "notifyType" in tmp.keys():
if tmp["notifyType"] != self.notify_type:
same_flag = False
if "vpnInstanceName" in tmp.keys():
if tmp["vpnInstanceName"] != self.vpn_name:
same_flag = False
if "portNumber" in tmp.keys():
if tmp["portNumber"] != self.recv_port:
same_flag = False
if "securityModel" in tmp.keys():
if tmp["securityModel"] != self.security_model:
same_flag = False
if "securityName" in tmp.keys():
if tmp["securityName"] != self.security_name:
same_flag = False
if "securityNameV3" in tmp.keys():
if tmp["securityNameV3"] != self.security_name_v3:
same_flag = False
if "securityLevel" in tmp.keys():
if tmp["securityLevel"] != self.security_level:
same_flag = False
if "isPublicNet" in tmp.keys():
if tmp["isPublicNet"] != self.is_public_net:
same_flag = False
if "interface-name" in tmp.keys():
if tmp.get("interface-name") is not None:
if tmp["interface-name"].lower() != self.interface_name.lower():
same_flag = False
else:
same_flag = False
if same_flag:
break
if self.state == "present":
need_cfg = True
if same_flag:
need_cfg = False
else:
need_cfg = False
if delete_flag:
need_cfg = True
result["need_cfg"] = need_cfg
def cli_load_config(self, commands):
""" Load configure by cli """
if not self.module.check_mode:
load_config(self.module, commands)
def get_snmp_version(self):
""" Get snmp version """
version = None
conf_str = CE_GET_SNMP_VERSION
recv_xml = self.netconf_get_config(conf_str=conf_str)
if "" in recv_xml:
pass
else:
xml_str = recv_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)
version_info = root.find("snmp/engine")
if version_info:
for site in version_info:
if site.tag in ["version"]:
version = site.text
return version
def xml_get_connect_port(self):
""" Get connect port by xml """
tmp_cfg = None
conf_str = CE_GET_SNMP_PORT
recv_xml = self.netconf_get_config(conf_str=conf_str)
if "" in recv_xml:
pass
else:
xml_str = recv_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)
snmp_port_info = root.findall("snmp/systemCfg/snmpListenPort")
if snmp_port_info:
tmp_cfg = snmp_port_info[0].text
return tmp_cfg
def get_proposed(self):
""" Get proposed state """
self.proposed["state"] = self.state
if self.version:
self.proposed["version"] = self.version
if self.connect_port:
self.proposed["connect_port"] = self.connect_port
if self.host_name:
self.proposed["host_name"] = self.host_name
if self.address:
self.proposed["address"] = self.address
if self.notify_type:
self.proposed["notify_type"] = self.notify_type
if self.vpn_name:
self.proposed["vpn_name"] = self.vpn_name
if self.recv_port:
self.proposed["recv_port"] = self.recv_port
if self.security_model:
self.proposed["security_model"] = self.security_model
if self.security_name:
self.proposed["security_name"] = "******"
if self.security_name_v3:
self.proposed["security_name_v3"] = self.security_name_v3
if self.security_level:
self.proposed["security_level"] = self.security_level
if self.is_public_net != 'no_use':
self.proposed["is_public_net"] = self.is_public_net
if self.interface_name:
self.proposed["interface_name"] = self.interface_name
def get_existing(self):
""" Get existing state """
if self.version:
version = self.get_snmp_version()
if version:
self.cur_cli_cfg["version"] = version
self.existing["version"] = version
if self.connect_port:
tmp_cfg = self.xml_get_connect_port()
if tmp_cfg:
self.cur_cli_cfg["connect port"] = tmp_cfg
self.existing["connect port"] = tmp_cfg
if self.host_name:
self.existing["target host info"] = self.cur_netconf_cfg[
"target_host_info"]
def get_end_state(self):
""" Get end state """
if self.version:
version = self.get_snmp_version()
if version:
self.end_state["version"] = version
if self.connect_port:
tmp_cfg = self.xml_get_connect_port()
if tmp_cfg:
self.end_state["connect port"] = tmp_cfg
if self.host_name:
self.end_state["target host info"] = self.end_netconf_cfg[
"target_host_info"]
if self.existing == self.end_state:
self.changed = False
self.updates_cmd = list()
def config_version_cli(self):
""" Config version by cli """
if "disable" in self.cur_cli_cfg["version"]:
cmd = "snmp-agent sys-info version %s" % self.version
self.updates_cmd.append(cmd)
cmds = list()
cmds.append(cmd)
self.cli_load_config(cmds)
self.changed = True
else:
if self.version != self.cur_cli_cfg["version"]:
cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[
"version"]
self.updates_cmd.append(cmd)
cmd = "snmp-agent sys-info version %s" % self.version
self.updates_cmd.append(cmd)
cmds = list()
cmds.append(cmd)
self.cli_load_config(cmds)
self.changed = True
def undo_config_version_cli(self):
""" Undo config version by cli """
if "disable" in self.cur_cli_cfg["version"]:
pass
else:
cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[
"version"]
cmds = list()
cmds.append(cmd)
self.updates_cmd.append(cmd)
self.cli_load_config(cmds)
self.changed = True
def config_connect_port_xml(self):
""" Config connect port by xml """
if "connect port" in self.cur_cli_cfg.keys():
if self.cur_cli_cfg["connect port"] == self.connect_port:
pass
else:
cmd = "snmp-agent udp-port %s" % self.connect_port
cmds = list()
cmds.append(cmd)
self.updates_cmd.append(cmd)
conf_str = CE_MERGE_SNMP_PORT % self.connect_port
self.netconf_set_config(conf_str=conf_str)
self.changed = True
else:
cmd = "snmp-agent udp-port %s" % self.connect_port
cmds = list()
cmds.append(cmd)
self.updates_cmd.append(cmd)
conf_str = CE_MERGE_SNMP_PORT % self.connect_port
self.netconf_set_config(conf_str=conf_str)
self.changed = True
def undo_config_connect_port_cli(self):
""" Undo config connect port by cli """
if "connect port" in self.cur_cli_cfg.keys():
if not self.cur_cli_cfg["connect port"]:
pass
else:
cmd = "undo snmp-agent udp-port"
cmds = list()
cmds.append(cmd)
self.updates_cmd.append(cmd)
connect_port = "161"
conf_str = CE_MERGE_SNMP_PORT % connect_port
self.netconf_set_config(conf_str=conf_str)
self.changed = True
def merge_snmp_target_host(self):
""" Merge snmp target host operation """
conf_str = CE_MERGE_SNMP_TARGET_HOST_HEADER % self.host_name
if self.domain:
conf_str += "%s" % self.domain
if self.address:
conf_str += "%s" % self.address
if self.notify_type:
conf_str += "%s" % self.notify_type
if self.vpn_name:
conf_str += "%s" % self.vpn_name
if self.recv_port:
conf_str += "%s" % self.recv_port
if self.security_model:
conf_str += "%s" % self.security_model
if self.security_name:
conf_str += "%s" % self.security_name
if self.security_name_v3:
conf_str += "%s" % self.security_name_v3
if self.security_level:
conf_str += "%s" % self.security_level
if self.is_public_net != 'no_use':
conf_str += "%s" % self.is_public_net
if self.interface_name:
conf_str += "%s" % self.interface_name
conf_str += CE_MERGE_SNMP_TARGET_HOST_TAIL
recv_xml = self.netconf_set_config(conf_str=conf_str)
if "" not in recv_xml:
self.module.fail_json(msg='Error: Merge snmp target host failed.')
cmd = "snmp-agent target-host host-name %s " % self.host_name
cmd += "%s " % self.notify_type
cmd += "address udp-domain %s " % self.address
if self.recv_port:
cmd += "udp-port %s " % self.recv_port
if self.interface_name:
cmd += "source %s " % self.interface_name
if self.vpn_name:
cmd += "vpn-instance %s " % self.vpn_name
if self.is_public_net == "true":
cmd += "public-net "
if self.security_model in ["v1", "v2c"] and self.security_name:
cmd += "params securityname %s %s " % (
"******", self.security_model)
if self.security_model == "v3" and self.security_name_v3:
cmd += "params securityname %s %s " % (
self.security_name_v3, self.security_model)
if self.security_level and self.security_level in ["authentication", "privacy"]:
cmd += "%s" % self.security_level
self.changed = True
self.updates_cmd.append(cmd)
def delete_snmp_target_host(self):
""" Delete snmp target host operation """
conf_str = CE_DELETE_SNMP_TARGET_HOST_HEADER % self.host_name
if self.domain:
conf_str += "%s" % self.domain
if self.address:
conf_str += "%s" % self.address
if self.notify_type:
conf_str += "%s" % self.notify_type
if self.vpn_name:
conf_str += "%s" % self.vpn_name
if self.recv_port:
conf_str += "%s" % self.recv_port
if self.security_model:
conf_str += "%s" % self.security_model
if self.security_name:
conf_str += "%s" % self.security_name
if self.security_name_v3:
conf_str += "%s" % self.security_name_v3
if self.security_level:
conf_str += "%s" % self.security_level
if self.is_public_net != 'no_use':
conf_str += "%s" % self.is_public_net
if self.interface_name:
conf_str += "%s" % self.interface_name
conf_str += CE_DELETE_SNMP_TARGET_HOST_TAIL
recv_xml = self.netconf_set_config(conf_str=conf_str)
if "" not in recv_xml:
self.module.fail_json(msg='Error: Delete snmp target host failed.')
if not self.address:
cmd = "undo snmp-agent target-host host-name %s " % self.host_name
else:
if self.notify_type == "trap":
cmd = "undo snmp-agent target-host trap address udp-domain %s " % self.address
else:
cmd = "undo snmp-agent target-host inform address udp-domain %s " % self.address
if self.recv_port:
cmd += "udp-port %s " % self.recv_port
if self.interface_name:
cmd += "source %s " % self.interface_name
if self.vpn_name:
cmd += "vpn-instance %s " % self.vpn_name
if self.is_public_net == "true":
cmd += "public-net "
if self.security_model in ["v1", "v2c"] and self.security_name:
cmd += "params securityname %s" % "******"
if self.security_model == "v3" and self.security_name_v3:
cmd += "params securityname %s" % self.security_name_v3
self.changed = True
self.updates_cmd.append(cmd)
def merge_snmp_version(self):
""" Merge snmp version operation """
conf_str = CE_MERGE_SNMP_VERSION % self.version
recv_xml = self.netconf_set_config(conf_str=conf_str)
if "" not in recv_xml:
self.module.fail_json(msg='Error: Merge snmp version failed.')
if self.version == "none":
cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[
"version"]
self.updates_cmd.append(cmd)
elif self.version == "v1v2c":
cmd = "snmp-agent sys-info version v1"
self.updates_cmd.append(cmd)
cmd = "snmp-agent sys-info version v2c"
self.updates_cmd.append(cmd)
elif self.version == "v1v3":
cmd = "snmp-agent sys-info version v1"
self.updates_cmd.append(cmd)
cmd = "snmp-agent sys-info version v3"
self.updates_cmd.append(cmd)
elif self.version == "v2cv3":
cmd = "snmp-agent sys-info version v2c"
self.updates_cmd.append(cmd)
cmd = "snmp-agent sys-info version v3"
self.updates_cmd.append(cmd)
else:
cmd = "snmp-agent sys-info version %s" % self.version
self.updates_cmd.append(cmd)
self.changed = True
def work(self):
""" Main work function """
self.check_cli_args()
self.check_netconf_args(self.cur_netconf_cfg)
self.get_proposed()
self.get_existing()
if self.state == "present":
if self.version:
if self.version != self.cur_cli_cfg["version"]:
self.merge_snmp_version()
if self.connect_port:
self.config_connect_port_xml()
if self.cur_netconf_cfg["need_cfg"]:
self.merge_snmp_target_host()
else:
if self.connect_port:
self.undo_config_connect_port_cli()
if self.cur_netconf_cfg["need_cfg"]:
self.delete_snmp_target_host()
self.check_netconf_args(self.end_netconf_cfg)
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
self.results['updates'] = self.updates_cmd
self.module.exit_json(**self.results)
def main():
""" Module main """
argument_spec = dict(
state=dict(choices=['present', 'absent'], default='present'),
version=dict(choices=['none', 'v1', 'v2c', 'v3',
'v1v2c', 'v1v3', 'v2cv3', 'all']),
connect_port=dict(type='str'),
host_name=dict(type='str'),
address=dict(type='str'),
notify_type=dict(choices=['trap', 'inform']),
vpn_name=dict(type='str'),
recv_port=dict(type='str'),
security_model=dict(choices=['v1', 'v2c', 'v3']),
security_name=dict(type='str', no_log=True),
security_name_v3=dict(type='str'),
security_level=dict(
choices=['noAuthNoPriv', 'authentication', 'privacy']),
is_public_net=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']),
interface_name=dict(type='str')
)
argument_spec.update(ce_argument_spec)
module = SnmpTargetHost(argument_spec=argument_spec)
module.work()
if __name__ == '__main__':
main()