Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apcon modules #57079

Open
wants to merge 4 commits into
base: devel
from
Open

Apcon modules #57079

Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

No changes.
@@ -0,0 +1,157 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by
# Ansible still belong to the author of the module, and may assign their own
# license to the complete work.
#
# Copyright (C) 2018 APCON, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Contains utility methods
# APCON Networking

import time
import socket
import re
from distutils.cmd import Command
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.network.common.utils import to_list, EntityCollection
from ansible.module_utils.connection import Connection, exec_command
from ansible.module_utils.connection import ConnectionError

_DEVICE_CONFIGS = {}
_CONNECTION = None

apconos_provider_spec = {
'host': dict(),
'port': dict(type='int'),
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']),
no_log=True),
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']),
type='path'),
'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']),
type='bool'),
'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']),
no_log=True),
'timeout': dict(type='int'),
'context': dict(),
'passwords': dict()
}

apconos_argument_spec = {
'host': dict(),
'port': dict(type='int'),
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']),
no_log=True),
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']),
type='path'),
'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']),
type='bool'),
'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']),
no_log=True),
'timeout': dict(type='int'),
'context': dict(),
'passwords': dict(),
'provider': dict(type='dict', options=apconos_provider_spec),
}

command_spec = {
'command': dict(key=True),
'prompt': dict(),
'answer': dict(),
'check_all': dict()
}


def get_provider_argspec():
return apconos_provider_spec


def check_args(module, warnings):
pass


def get_connection(module):
global _CONNECTION
if _CONNECTION:
return _CONNECTION
_CONNECTION = Connection(module._socket_path)

return _CONNECTION


def get_config(module, flags=None):
flags = [] if flags is None else flags

cmd = ' '.join(flags).strip()

try:
return _DEVICE_CONFIGS[cmd]
except KeyError:
conn = get_connection(module)
out = conn.get(cmd)
cfg = to_text(out, errors='surrogate_then_replace').strip()
_DEVICE_CONFIGS[cmd] = cfg
return cfg


def run_commands(module, commands, check_rc=True):
connection = get_connection(module)
transform = EntityCollection(module, command_spec)
commands = transform(commands)

responses = list()

for cmd in commands:
out = connection.get(**cmd)
responses.append(to_text(out, errors='surrogate_then_replace'))

return responses


def load_config(module, config):
try:
conn = get_connection(module)
conn.get('enable')
conn.edit_config(config)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc))


def get_defaults_flag(module):
rc, out, err = exec_command(module, 'display running-config ?')
out = to_text(out, errors='surrogate_then_replace')

commands = set()
for line in out.splitlines():
if line:
commands.add(line.strip().split()[0])

if 'all' in commands:
return 'all'
else:
return 'full'
No changes.
@@ -0,0 +1,190 @@
#!/usr/bin/python
#
# Copyright (C) 2018 Apcon.
#
# GNU General Public License v3.0+
#
# This program 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.
#
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# Module to execute apconos Commands on Apcon Switches.
# Apcon Networking

from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = """
---
module: apconos_cert
version_added: "2.9.0"

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

It could also be 2.9 as no new modules are added on minor releases.

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 30, 2019

Author

I'll fix it!
Thanks!

author: "David Lee (@davidlee-ap)"
short_description: install ssl ipv4 certificate on apcon network devices

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

Capitalize properly

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 30, 2019

Author

I'll fix it. Thanks!

description:
- import and install ssl ipv4 certificate with specifying remote filename

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

Capitalize properly

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 30, 2019

Author

I'll fix it.
Thanks!

notes:
- tested against apcon iis+ii
options:
command:
description:
- currently it is not being used in apconos_cert module.

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

Why is it here then?

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 30, 2019

Author

Sanity test couldn't pass without it. It might be implemented in the future.

This comment has been minimized.

Copy link
@mattclay

mattclay May 30, 2019

Member

If it's not used by the module, remove it from the module spec instead of adding it to the docs.

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 31, 2019

Author

I'll remove it.
Thanks!

provider:
description:
- please use connection:network_cli.
ipaddress:
description:
- specify a remote ip address at which tftp server resides.
filename:
description:
- specify a remote file name which resides on a remote tftp server.
wait_for:
description:
- list of conditions to evaluate against the output of the
command. The task will wait for each condition to be true
before moving forward. If the conditional is not true
within the configured number of retries, the task fails.
See examples.
match:
description:
- The I(match) argument is used in conjunction with the
I(wait_for) argument to specify the match policy. Valid
values are C(all) or C(any). If the value is set to C(all)
then all conditionals in the wait_for must be satisfied. If
the value is set to C(any) then only one of the values must be
satisfied.
default: all
choices: ['any', 'all']
retries:
description:
- Specifies the number of retries a command should by tried
before it is considered failed. The command is run on the
target device every retry and evaluated against the
I(wait_for) conditions.
default: 10
interval:
description:
- Configures the interval in seconds to wait between retries
of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before
trying the command again.
default: 1
"""

EXAMPLES = """
- name: Install SSL Certificate
apconos_cert:
ipaddress: 10.0.0.100
filename: remoteSSLCert.pem
"""

RETURN = """
"""

import re
import time

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.parsing import Conditional
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.apconos.apconos import load_config, run_commands
from ansible.module_utils.network.apconos.apconos import apconos_argument_spec, check_args
from ansible.module_utils.six import string_types


def to_lines(stdout):
for item in stdout:
if isinstance(item, string_types):
item = str(item).split('\n')
yield item


def construct_update_command(module):
"""construct update command
"""
command = module.params['command']
ipaddress = module.params['ipaddress']
filename = module.params['filename']
command[0] = 'tftp put ssl ipv4 ' + ipaddress[0] + ' ' + filename[0] + time.strftime("%d_%b+%H:%M:%S", time.gmtime())
command.append('tftp get ssl ipv4 ' + ipaddress[0] + ' ' + filename[0])

return command


def main():
""" main entry point for module execution
"""
spec = dict(
wait_for=dict(type='list'),
match=dict(default='all', choices=['all', 'any']),

retries=dict(default=10, type='int'),
interval=dict(default=1, type='int'),

ipaddress=dict(type='list'),

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

Why would this be a list instead of a str? Also, other modules normally use camel case. I'll defer to a core developer to see what they think about this convention.

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 30, 2019

Author

str should be OK, but it is supposed to be a list for the application.

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

What do you mean? How is this interfacing and what’s the application?

This comment has been minimized.

Copy link
@mattclay

mattclay May 30, 2019

Member

The module is only using ipaddress[0], so this shouldn't be a list.

This comment has been minimized.

Copy link
@davidlee-ap

davidlee-ap May 31, 2019

Author

The module is only using ipaddress[0], so this shouldn't be a list.

I'll fix it.
Thanks!

filename=dict(type='list'),
command=dict(type='list'))

spec.update(apconos_argument_spec)

module = AnsibleModule(argument_spec=spec, supports_check_mode=True)

This comment has been minimized.

Copy link
@kbreit

kbreit May 30, 2019

Contributor

Do you have anywhere in your code that's supporting check mode?

result = {'changed': False}

wait_for = module.params['wait_for'] or list()
conditionals = [Conditional(c) for c in wait_for]

retries = module.params['retries']
interval = module.params['interval']
match = module.params['match']

while retries > 0:
responses = run_commands(module, construct_update_command(module))

for item in list(conditionals):
if item(responses):
if match == 'any':
conditionals = list()
break
conditionals.remove(item)

if not conditionals:
break

time.sleep(interval)
retries -= 1

if conditionals:
failed_conditions = [item.raw for item in conditionals]
msg = 'One or more conditional statements have not been satisfied'
module.fail_json(msg=msg, failed_conditions=failed_conditions)

for item in responses:
if len(item) == 0:
result.update({
'changed': True,
'stdout': responses,
'stdout_lines': list(to_lines(responses))
})
elif 'ERROR' in item:
result.update({
'failed': True,
'stdout': responses,
'stdout_lines': list(to_lines(responses))
})
else:
result.update({
'stdout': item,
'stdout_lines': list(to_lines(responses))
})

module.exit_json(**result)


if __name__ == '__main__':
main()
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.