Skip to content

Commit

Permalink
[DM] discover device workflow related files
Browse files Browse the repository at this point in the history
Change-Id: I291449b36dd2187fee0c2bde406885fa0d4cba37
Closes-Bug: #1751952
  • Loading branch information
sjeevaraj committed Mar 6, 2018
1 parent 8edc122 commit 298a9bb
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 60 deletions.
1 change: 1 addition & 0 deletions src/config/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ subdirs=[
'uve',
'vnc_openstack',
'contrail_issu',
'fabric-ansible',
]

CfgmEnv.SConscript(dirs=subdirs, exports='CfgmEnv', duplicate = 0)
Expand Down
30 changes: 27 additions & 3 deletions src/config/fabric-ansible/ansible-playbooks/discover_device.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
- name: discover_device
- name: Gather fabric details and discover device
hosts: localhost
gather_facts: no
connection: local
vars:
auth_token: "{{ playbook_input.auth_token }}"
api_server_host: "localhost"
output: {"status":"", "message":"", "results":""}
host_prefix: []

pre_tasks:
- set_fact:
ipaddress: []
credentials: [ { "username": "root", "password": "pasword" } ]
fabric_fq_name: "{{ playbook_input.input.fabric_fq_name }}"
when: playbook_input.input.fabric_fq_name is defined

- set_fact:
fabric_uuid: "{{ playbook_input.input.fabric_uuid }}"
when: playbook_input.input.fabric_uuid is defined

- set_fact:
tag_fq_name: "namespace=management_ip"

- set_fact:
api_server_host: "{{ playbook_input.api_server_host }}"
when: playbook_input.api_server_host is defined

roles:
- fabric_namespace_read
- probe_device
- check_credentials

post_tasks:
- name: set output parameter
set_fact:
output: "{{ output|combine({'status':'SUCCESS','message':'Successfully discovered devices', 'results': '{{ results }}' })}}"

- debug: var=output
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,27 @@

DOCUMENTATION = '''
---
This is to map the given SNMP OID with the vendor and family and check for
supoorted device family.
oid_to_vendor_family:
oid: The snmp OID from snmp_facts module return patameter.
ansible_facts.ansible_sysobjectid.
host: IP address
hostname: hostname of the IP/host from snmp_facts module return parameter.
ansible_facts.ansible_sysname.
'''

EXAMPLES = '''
oid_to_vendor_family:
oid: "1.3.6.1.4.1.2636.1.1.1.2.29"
host: "10.155.67.7"
hostname: "cloudcpe"
'''

RETURN = '''
Returns three parameters. The vendor,family and product for a given
host is returned back to the caller.
'''

oid_mapping = {
Expand All @@ -28,16 +45,29 @@
"1.3.6.1.4.1.2636.1.1.1.2.29": {"vendor": "juniper",
"family": "juniper-mx",
"product": "mx240"},
"1.3.6.1.4.1.2636.1.1.1.2.11": {"vendor": "juniper",
"family": "juniper-mx",
"product": "m10i"},
}
_output = {'job_log_message': '', 'oid_mapping': {}}


def find_vendor_family(module):

mapped_value = {}

if module.params['oid'] in oid_mapping:
mapped_value['host'] = module.params['host']
mapped_value['hostname'] = module.params['hostname']
mapped_value.update(oid_mapping[module.params['oid']])
_output['job_log_message'] += "\nTask: OID MAPPING: " + \
"vendor and product for the host: " + \
mapped_value['host'] + " is " + str(mapped_value)
else:
_output['job_log_message'] += "\nTask: OID MAPPING: " + \
"device with oid " + \
module.params['oid'] + " NOT supported"

return mapped_value


Expand All @@ -50,12 +80,12 @@ def main():
),
supports_check_mode=True
)

mapped_value = find_vendor_family(module)

output = {}
output['oid_mapping'] = mapped_value
_output['oid_mapping'] = mapped_value

module.exit_json(**output)
module.exit_json(**_output)


if __name__ == '__main__':
Expand Down
40 changes: 28 additions & 12 deletions src/config/fabric-ansible/ansible-playbooks/library/ping_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,42 @@
IP addresses defined by subnet
"""

__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule
import subprocess
import ipaddress
import socket
__metaclass__ = type


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

DOCUMENTATION = '''
---
For a given subnet or a list of IPs/hosts, check if they are reachable through
ICMP ping.
ping_sweep:
subnets: List of IP Prefixes
hosts: List of Ip addresses / hostnames
'''

EXAMPLES = '''
ping_sweep:
subnets: ["10.155.67.0/29","10.155.72.0/30"]
OR
ping_sweep:
hosts: ["10.155.68.2", "10.102.65.4", "10.157.3.2"]
'''

RETURN = '''
Return two lists, rechable hosts and unrechable hosts.
'''

result = {}
_result = {}


def check_ping(module):
Expand All @@ -42,18 +58,18 @@ def check_ping(module):
ip_net = ipaddress.ip_network(unicode(subnet))
all_hosts.extend(list(ip_net.hosts()))
except ValueError:
result['failure'] = "Subnet is not valid " + subnet
module.exit_json(**result)
_result['failure'] = "Subnet is not valid " + subnet
module.exit_json(**_result)

if module.params['hosts']:
for host in module.params['hosts']:
try:
ipaddr = socket.gethostbyname(host)
all_hosts.append(ipaddr)
except (ValueError, Exception) as e:
result['failure'] = "Host not valid " + \
_result['failure'] = "Host not valid " + \
host + "Failed with exception " + str(e)
module.exit_json(**result)
module.exit_json(**_result)

for ip in all_hosts:
ping_output = subprocess.Popen(['ping', '-c', '3', str(ip)],
Expand All @@ -79,12 +95,12 @@ def main():

reachable, unreachable = check_ping(module)

result['reachable_hosts'] = reachable
result['unreachable_hosts'] = unreachable
result['job_log_message'] = "Task: PING SWEEP: ping sweep completed." + \
"Reachable hosts are : " + ','.join(reachable)
_result['reachable_hosts'] = reachable
_result['unreachable_hosts'] = unreachable
_result['job_log_message'] = "Task: PING SWEEP: ping sweep completed." + \
"Reachable hosts are : " + ','.join(reachable)

module.exit_json(**result)
module.exit_json(**_result)


if __name__ == '__main__':
Expand Down
136 changes: 118 additions & 18 deletions src/config/fabric-ansible/ansible-playbooks/library/ssh_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,149 @@
"""
This file contains implementation of checking for successful SSH connections
"""
from ansible.module_utils.basic import AnsibleModule
import paramiko


__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule
import paramiko

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

DOCUMENTATION = '''
---
Check for ssh connectivity with a given list of credentials against all
rechables hosts.
If vendor and device family are specified, those credentials take precedence
for a given host matching the specifics.
ssh_connection:
hosts: List of rechable hosts with vendor and family info
credentials: List of credentials as given in the fabric object
'''

EXAMPLES = '''
ssh_connection:
hosts:[{
"family": "juniper-mx",
"host": "10.155.67.1",
"hostname": "jtme-m10-01",
"product": "m10i",
"vendor": "juniper"
},
{
"family": "juniper-mx",
"host": "10.155.67.2",
"hostname": "jtme-m10-02",
"product": "m10i",
"vendor": "juniper"
}]
credentials: [{
"credential": {
"password": "Embe1mpls",
"username": "root"
},
"device_family": "qfx",
"vendor": "Juniper"
}, {
"credential": {
"password": "Embe1mpls",
"username": "root"
},
"device_family": null,
"vendor": "Juniper"
}]
'''

RETURN = '''
List of hostnames long with their valid credentials
'''

_result = {'job_log_message': '', 'oid_mapping': {}}


def ssh_connect(ssh_conn, username, password, hostname):
successful_cred = {}
try:
ssh_conn.connect(
username=username,
password=password,
hostname=hostname)
successful_cred.update(
{'hostname': hostname, 'username': username, 'password': password})
_result['job_log_message'] += "\nTask: CREDENTIAL CHECK : " + \
"Credentials worked for host: " + hostname
except(paramiko.BadHostKeyException,
paramiko.AuthenticationException,
paramiko.SSHException,
Exception) as e:
_result['job_log_message'] += "\nTask: CREDENTIAL CHECK: Host: " + \
hostname + " hit the exception " + str(e)
pass

return successful_cred


def ssh_check(module):
hosts = module.params['hosts']
credentials = module.params['credentials']

remove_null = []
successful_connections = []

ssh_conn = paramiko.SSHClient()
ssh_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())

index = 0

"""
check if credentials dict has both username and password defined.
If neither avaiable, remove the entire entry from the list.
Cannot check ssh connectivity with just the username/password.
"""
for outer_dict in credentials[index:]:
for inner_dict in outer_dict.values():
if isinstance(inner_dict, dict):
if inner_dict['username'] and inner_dict['password']:
index += 1
break
else:
credentials.remove(outer_dict)
index = index
break

"""
In a list of dict for credentials, if a dict value is None
remove the key from the dict. Only keys with values are retained.
"""
for single_dict in credentials:
remove_null.append(
dict([(dkey, ddata)
for dkey, ddata in single_dict.iteritems() if(ddata)]))
"""
Sorting based on number of keys in a dict. Max to min sorting done here
resulting list would have dict with max keys as first entry
and min as the last
"""
sorted_len = sorted(remove_null, key=len, reverse=True)

for host in hosts:
for cred in credentials:
try:
ssh_conn.connect(username=cred['username'],
password=cred['password'], hostname=host)
successful_connections.append({'hostname': host,
'username': cred['username'],
'password': cred['password']})
break
except(paramiko.BadHostKeyException,
paramiko.AuthenticationException,
paramiko.SSHException, Exception) as e:
for cred in sorted_len:
if (('device_family' in cred and 'vendor' not in cred) or
(('vendor' in cred) and
((cred['vendor'].lower() != host['vendor']) or
('device_family' in cred and cred['device_family'] not in
host['family'])))):
continue
else:
valid_cred = ssh_connect(
ssh_conn,
cred['credential']['username'],
cred['credential']['password'],
host['host'])
if valid_cred:
successful_connections.append(valid_cred)
break

return successful_connections

Expand All @@ -65,9 +166,8 @@ def main():

successful_connections = ssh_check(module)

result = {}
result['ssh_success'] = successful_connections
module.exit_json(**result)
_result['ssh_success'] = successful_connections
module.exit_json(**_result)


if __name__ == '__main__':
Expand Down

0 comments on commit 298a9bb

Please sign in to comment.