Skip to content

Commit

Permalink
Merge pull request #168 from aristanetworks/features/cv-containers-de…
Browse files Browse the repository at this point in the history
…lete-configlets
  • Loading branch information
titom73 authored Apr 22, 2020
2 parents ee3577c + 7bf702c commit 480f256
Show file tree
Hide file tree
Showing 4 changed files with 404 additions and 54 deletions.
108 changes: 95 additions & 13 deletions ansible_collections/arista/cvp/plugins/module_utils/cv_api2018.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@
# limitations under the License.
#

# pylint: disable=redefined-builtin
# pylint: disable=too-many-public-methods
# pylint: disable=too-many-lines
# pylint: disable=invalid-name
# pylint: disable=C0103


from __future__ import absolute_import, division, print_function
__metaclass__ = type

''' Class containing calls to CVP RESTful API.
'''
import os
# This import is for proper file IO handling support for both Python 2 and 3
# pylint: disable=redefined-builtin
from io import open

from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpApiError
Expand Down Expand Up @@ -63,8 +69,6 @@ class CvpApi(object):
failed and no session could be established to a CVP node. Destroy
the class and re-instantiate.
'''
# pylint: disable=too-many-public-methods
# pylint: disable=too-many-lines

def __init__(self, clnt, request_timeout=30):
''' Initialize the class.
Expand Down Expand Up @@ -145,7 +149,7 @@ def get_net_element_info_by_device_id(self, device_id):
try:
element_info = self.clnt.get('/provisioning/getNetElementInfoById.do?netElementId=%s'
% qplus(device_id), timeout=self.request_timeout)
except CvpApiError as e:
except CvpApiError as e: # pylint: disable=invalid-name
# Catch an invalid task_id error and return None
if 'errorMessage' in str(e):
self.log.debug('Device with id %s could not be found' % device_id)
Expand Down Expand Up @@ -306,7 +310,7 @@ def update_configlets_on_device(self, app_name, device, add_configlets, del_conf
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Device %s : %s' % (device['fqdn'], e))
raise Exception("update_configlets_on_device:%s" % e)
configlets = []
Expand All @@ -319,7 +323,7 @@ def update_configlets_on_device(self, app_name, device, add_configlets, del_conf
tasks = {}
return tasks

def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_imageBundle, create_task=True):
def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_imageBundle, create_task=True): # pylint: disable=invalid-name
''' Remove the image bundle from the specified container.
Args:
Expand Down Expand Up @@ -376,7 +380,7 @@ def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_im
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
# pylint: disable=unreachable
raise Exception("update_imageBundle_on_device:%s" % e)
self.log.debug('Device %s : %s' % (device['fqdn'], e))
Expand Down Expand Up @@ -431,19 +435,19 @@ def reset_device(self, app_name, device, create_task=True):
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except CvpApiError as e:
except CvpApiError as e: # pylint: disable=invalid-name
if any(txt in str(e) for txt in ['Data already exists', 'undefined container']):
self.log.debug('Device %s already in container Undefined'
% device['fqdn'])
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Reset Device %s : %s' % (device['fqdn'], e))
raise Exception("reset_device:%s" % e)
if create_task:
url = '/provisioning/v2/saveTopology.do'
tasks = self.clnt.post(url, data=[], timeout=self.request_timeout)
return tasks

def provision_device(self, app_name, device, container, configlets, imageBundle, create_task=True):
def provision_device(self, app_name, device, container, configlets, imageBundle, create_task=True): # pylint: disable=invalid-name
'''Move a device from the undefined container to a target container.
Optionally apply device-specific configlets and an imageBundle.
Expand Down Expand Up @@ -476,7 +480,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
self.move_device_to_container('%s:provision_device' % app_name, device, container,
create_task=False)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - move %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-move_to_container:%s" % e)
# Don't save configlet action if there is an image bundle to add
Expand All @@ -489,7 +493,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
created_tasks = self.update_configlets_on_device(app_name, device, configlets, [],
configlet_task)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - configlets %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-update_configlets:%s" % e)
# If configlet action created tasks then don't action imageBundles
Expand All @@ -498,7 +502,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
created_tasks = self.update_imageBundle_on_device(app_name, device, imageBundle, {},
create_task)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - imageBundle %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-update_imageBundle:%s" % e)
return created_tasks
Expand Down Expand Up @@ -579,6 +583,84 @@ def apply_configlets_to_container(self, app_name, container, new_configlets, cre
else:
return data

# pylint: disable=too-many-locals
# pylint: disable=invalid-name
def remove_configlets_from_container(self, app_name, container,
del_configlets, create_task=True):
''' Remove the configlets from the container.
Args:
app_name (str): The application name to use in info field.
container (dict): The container dict
del_configlets (list): List of configlet name and key pairs
create_task (bool): Determines whether or not to execute a save
and create the tasks (if any)
Returns:
response (dict): A dict that contains a status and a list of
task ids created (if any).
Ex: {u'data': {u'status': u'success', u'taskIds': [u'35']}}
'''
self.log.debug(
'remove_configlets_from_container: container: %s names: %s' %
(container, del_configlets))

# Get all the configlets assigned to the device.
configlets = self.get_configlets_by_container_id(container['key'])

# Get a list of the names and keys of the configlets. Do not add
# configlets that are on the delete list.
keep_names = []
keep_keys = []
for configlet in configlets['configletList']:
key = configlet['key']
if next((ent for ent in del_configlets if ent['key'] == key),
None) is None:
keep_names.append(configlet['name'])
keep_keys.append(key)

# Remove the names and keys of the configlets to keep and build a
# list of the configlets to remove.
del_names = []
del_keys = []
for entry in del_configlets:
del_names.append(entry['name'])
del_keys.append(entry['key'])

info = '%s Configlet Remove: from Container %s' % (app_name,
container['name'])
info_preview = '<b>Configlet Remove:</b> from Container' + container[
'name']
data = {'data': [{'info': info,
'infoPreview': info_preview,
'note': '',
'action': 'associate',
'nodeType': 'configlet',
'nodeId': '',
'configletList': keep_keys,
'configletNamesList': keep_names,
'ignoreConfigletNamesList': del_names,
'ignoreConfigletList': del_keys,
'configletBuilderList': [],
'configletBuilderNamesList': [],
'ignoreConfigletBuilderList': [],
'ignoreConfigletBuilderNamesList': [],
'toId': container['key'],
'toIdType': 'container',
'fromId': '',
'nodeName': '',
'fromName': '',
'toName': container['name'],
'nodeIpAddress': '',
'nodeTargetIpAddress': '',
'childTasks': [],
'parentTask': ''}]}
self.log.debug(
'remove_configlets_from_container: saveTopology data:\n%s'
% data['data'])
self._add_temp_action(data)
if create_task:
return self._save_topology_v2([])
return data

# ~Configlet Related API Calls

def get_configlet_by_name(self, name):
Expand Down
98 changes: 88 additions & 10 deletions ansible_collections/arista/cvp/plugins/module_utils/cv_api2019.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def get_net_element_info_by_device_id(self, device_id):
try:
element_info = self.clnt.get('/provisioning/getNetElementInfoById.do?netElementId=%s'
% qplus(device_id), timeout=self.request_timeout)
except CvpApiError as e:
except CvpApiError as e: # pylint: disable=invalid-name
# Catch an invalid task_id error and return None
if 'errorMessage' in str(e):
self.log.debug('Device with id %s could not be found' % device_id)
Expand Down Expand Up @@ -309,7 +309,7 @@ def update_configlets_on_device(self, app_name, device, add_configlets, del_conf
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Device %s : %s' % (device['fqdn'], e))
raise Exception("update_configlets_on_device:%s" % e)
configlets = []
Expand All @@ -322,7 +322,7 @@ def update_configlets_on_device(self, app_name, device, add_configlets, del_conf
tasks = {}
return tasks

def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_imageBundle, create_task=True):
def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_imageBundle, create_task=True): # pylint: disable=invalid-name
''' Remove the image bundle from the specified container.
Args:
Expand Down Expand Up @@ -379,7 +379,7 @@ def update_imageBundle_on_device(self, app_name, device, add_imageBundle, del_im
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
# pylint: disable=unreachable
raise Exception("update_imageBundle_on_device:%s" % e)
self.log.debug('Device %s : %s' % (device['fqdn'], e))
Expand Down Expand Up @@ -434,19 +434,19 @@ def reset_device(self, app_name, device, create_task=True):
'format=topology&queryParam=&nodeId=root')
try:
self.clnt.post(url, data=data, timeout=self.request_timeout)
except CvpApiError as e:
except CvpApiError as e: # pylint: disable=invalid-name
if any(txt in str(e) for txt in ['Data already exists', 'undefined container']):
self.log.debug('Device %s already in container Undefined'
% device['fqdn'])
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Reset Device %s : %s' % (device['fqdn'], e))
raise Exception("reset_device:%s" % e)
if create_task:
url = '/provisioning/v2/saveTopology.do'
tasks = self.clnt.post(url, data=[], timeout=self.request_timeout)
return tasks

def provision_device(self, app_name, device, container, configlets, imageBundle, create_task=True):
def provision_device(self, app_name, device, container, configlets, imageBundle, create_task=True): # pylint: disable=invalid-name
'''Move a device from the undefined container to a target container.
Optionally apply device-specific configlets and an imageBundle.
Expand Down Expand Up @@ -479,7 +479,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
self.move_device_to_container('%s:provision_device' % app_name, device, container,
create_task=False)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - move %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-move_to_container:%s" % e)
# Don't save configlet action if there is an image bundle to add
Expand All @@ -492,7 +492,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
created_tasks = self.update_configlets_on_device(app_name, device, configlets, [],
configlet_task)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - configlets %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-update_configlets:%s" % e)
# If configlet action created tasks then don't action imageBundles
Expand All @@ -501,7 +501,7 @@ def provision_device(self, app_name, device, container, configlets, imageBundle,
try:
created_tasks = self.update_imageBundle_on_device(app_name, device, imageBundle, {},
create_task)
except Exception as e:
except Exception as e: # pylint: disable=invalid-name
self.log.debug('Provision Device - imageBundle %s : %s' % (device['fqdn'], e))
raise Exception("provsion_device-update_imageBundle:%s" % e)
return created_tasks
Expand Down Expand Up @@ -582,6 +582,84 @@ def apply_configlets_to_container(self, app_name, container, new_configlets, cre
else:
return data

# pylint: disable=too-many-locals
# pylint: disable=invalid-name
def remove_configlets_from_container(self, app_name, container,
del_configlets, create_task=True):
''' Remove the configlets from the container.
Args:
app_name (str): The application name to use in info field.
container (dict): The container dict
del_configlets (list): List of configlet name and key pairs
create_task (bool): Determines whether or not to execute a save
and create the tasks (if any)
Returns:
response (dict): A dict that contains a status and a list of
task ids created (if any).
Ex: {u'data': {u'status': u'success', u'taskIds': [u'35']}}
'''
self.log.debug(
'remove_configlets_from_container: container: %s names: %s' %
(container, del_configlets))

# Get all the configlets assigned to the device.
configlets = self.get_configlets_by_container_id(container['key'])

# Get a list of the names and keys of the configlets. Do not add
# configlets that are on the delete list.
keep_names = []
keep_keys = []
for configlet in configlets['configletList']:
key = configlet['key']
if next((ent for ent in del_configlets if ent['key'] == key),
None) is None:
keep_names.append(configlet['name'])
keep_keys.append(key)

# Remove the names and keys of the configlets to keep and build a
# list of the configlets to remove.
del_names = []
del_keys = []
for entry in del_configlets:
del_names.append(entry['name'])
del_keys.append(entry['key'])

info = '%s Configlet Remove: from Container %s' % (app_name,
container['name'])
info_preview = '<b>Configlet Remove:</b> from Container' + container[
'name']
data = {'data': [{'info': info,
'infoPreview': info_preview,
'note': '',
'action': 'associate',
'nodeType': 'configlet',
'nodeId': '',
'configletList': keep_keys,
'configletNamesList': keep_names,
'ignoreConfigletNamesList': del_names,
'ignoreConfigletList': del_keys,
'configletBuilderList': [],
'configletBuilderNamesList': [],
'ignoreConfigletBuilderList': [],
'ignoreConfigletBuilderNamesList': [],
'toId': container['key'],
'toIdType': 'container',
'fromId': '',
'nodeName': '',
'fromName': '',
'toName': container['name'],
'nodeIpAddress': '',
'nodeTargetIpAddress': '',
'childTasks': [],
'parentTask': ''}]}
self.log.debug(
'remove_configlets_from_container: saveTopology data:\n%s'
% data['data'])
self._add_temp_action(data)
if create_task:
return self._save_topology_v2([])
return data

# ~Configlet Related API Calls

def get_configlet_by_name(self, name):
Expand Down
Loading

0 comments on commit 480f256

Please sign in to comment.