import time
import copy
from jnpr.healthbot.swagger.models.playbook_schema import PlaybookSchema
from jnpr.healthbot.swagger.models.rule_schema_variable import RuleSchemaVariable
from jnpr.healthbot.modules import BaseModule
from jnpr.healthbot.modules.rules import Rule
from jnpr.healthbot.modules.devices import Device, DeviceGroup
from jnpr.healthbot.exception import SchemaError, NotFoundError
import logging
logger = logging.getLogger(__file__)
[docs]class Playbook(BaseModule):
[docs] def __init__(self, hbot):
"""
:param object hbot: :class:`jnpr.healthbot.HealthBotClient` client instance
"""
super().__init__(hbot)
[docs] def add(self, schema: PlaybookSchema = None, **kwargs):
"""
Add playbook
:param object schema: `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_
:param object kwargs: key values, which can be used to create
PlaybookSchema
Check `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_
for details about which all keys can be used
Example:
::
from jnpr.healthbot import HealthBotClient
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
hb.playbook.add(playbook_name="HbEZ-example",
rules = ['protocol.infra/check-task-momory-usage'])
# or
from jnpr.healthbot import HealthBotClient
from jnpr.healthbot import PlaybookSchema
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
pbs = PlaybookSchema(playbook_name="HbEZ-example",
rules = ['protocol.infra/check-task-momory-usage'])
hb.playbook.add(pbs)
Returns:
True if action successful
"""
if schema is None:
schema = PlaybookSchema(**kwargs)
payload = self.hbot._create_payload(schema)
payload_url = self.hbot.urlfor.playbook(payload['playbook-name'])
resp = self.api.get(payload_url + self.hbot.apiopt_candidate)
if resp.ok:
return True
response = self.api.post(payload_url, json=payload)
if response.status_code != 200:
logger.error(response.text)
response.raise_for_status()
return True
[docs] def delete(self, playbook_name: str):
"""
Delete playbook
:param str playbook_name: The playbook name to deleted
Example:
::
from jnpr.healthbot import HealthBotClient
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
hb.playbook.delete('linecard-kpis-playbook')
"""
payload = {'playbook-name': playbook_name}
rule_url = self.hbot.urlfor.playbook(playbook_name)
response = self.api.delete(rule_url, json=payload)
if response.status_code != 204:
logger.error(response.text)
response.raise_for_status()
return True
[docs] def get(self, playbook_name: str = None, uncommitted: bool = True):
"""
get playbook details
:param str playbook_name: Name of the playbook
:param bool uncommitted: True includes fetches uncommitted changes,
False restricts data set to only committed changes
Example:
::
from jnpr.healthbot import HealthBotClient
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
print(hb.playbook.get('linecard-kpis-playbook'))
# for all
print(hb.playbook.get())
"""
if playbook_name is not None:
playbook_url = self.hbot.urlfor.playbook(playbook_name)
if uncommitted:
playbook_url += self.hbot.apiopt_candidate
response = self.api.get(playbook_url)
if response.status_code != 200:
logger.error(response.text)
response.raise_for_status()
return self.hbot._create_schema(response, PlaybookSchema)
else:
playbook_list = []
playbooks_list_url = self.hbot.urlfor.playbooks()
if uncommitted:
playbooks_list_url += self.hbot.apiopt_candidate
resp = self.api.get(playbooks_list_url)
if resp.status_code == 404:
return False
existing_playbooks = resp.json()['playbook']
for playbook in existing_playbooks:
obj = self.hbot._create_schema(playbook, PlaybookSchema)
playbook_list.append(obj)
return playbook_list
[docs] def update(self, schema: PlaybookSchema = None, **kwargs):
"""
Update `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_ for
given playbook schema
Passing Schema invoke `put` and kwargs `post`
:param obj schema: `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_
:param object kwargs: key values, which can be used to create
PlaybookSchema
Check `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_
for details about which all keys can be used
Example:
::
from jnpr.healthbot import HealthBotClient
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
schemaObj = hb.playbook.get('xyz')
schemaObj.description = 'changed description'
hb.playbook.update(schemaObj)
:returns: True when OK
"""
if schema is None:
schema = PlaybookSchema(**kwargs)
call = self.api.post
else:
if not isinstance(schema, PlaybookSchema):
raise SchemaError(PlaybookSchema)
call = self.api.put
payload = self.hbot._create_payload(schema)
name = schema.playbook_name
playbook_url = self.hbot.urlfor.playbook(name)
response = call(playbook_url, json=payload)
if response.status_code != 200:
logger.error(response.text)
response.raise_for_status()
return True
[docs] def upload_playbook_file(self, filename):
"""
:param filename: File to be loaded
:return: return True of OK
"""
return self.hbot.upload_helper_file(filename)
class _RuleVariableClass(object):
def __init__(self, values):
super(_RuleVariableClass, self).__setattr__('_values', values)
super(_RuleVariableClass, self).__setattr__('_variable_value', [])
for item in values:
name = item['name'].replace('-', '_')
self.__dict__[name] = RuleSchemaVariable(**item).value
self.__dict__['_{}_schema'.format(
name)] = RuleSchemaVariable(**item)
def __setattr__(self, key, value):
if key in self.__dict__:
# To make sure value is as per rule/type defined
self.__dict__['_{}_schema'.format(key)].value = value
# value should be of string type in DeviceGroupVariableSchema
self._variable_value.append({'name': key.replace('_', '-'),
'value': str(value)})
def __repr__(self):
return str(self._values)
[docs]class PlayBookInstanceBuilder(Playbook):
[docs] def __init__(self, hbot, playbook: str, instance: str = None,
device_group_name: str = None):
"""
Help in building and applying playbook instance
:param hbot: HealthBOtClient instance
:param playbook: Playbook name for which instance need to be created
:param instance: Playbook instance name
:param device_group_name: Device group which will be associated with instance
Example:
::
from jnpr.healthbot import HealthBotClient
with HealthBotClient('xx.xxx.x.xx', 'xxxx', 'xxxx') as hb:
from jnpr.healthbot import PlayBookInstanceBuilder
pbb = PlayBookInstanceBuilder(hb, 'forwarding-table-summary', 'HbEZ-instance', 'Core')
variable = pbb.rule_variables["protocol.routesummary/check-fib-summary"]
variable.route_address_family = 'pqr'
variable.route_count_threshold = 100
# Apply variable to given device(s)
pbb.apply(device_ids=['vmx'])
#clear all the variable if you want to set it something else for group or other device(s)
pbb.clear()
variable = pbb.rule_variables["protocol.routesummary/check-fib-summary"]
variable.route_address_family = 'abc'
variable.route_count_threshold = 200
pbb.apply()
hb.commit()
"""
Playbook.__init__(self, hbot)
self.hbot = hbot
self.playbook = playbook
self.instance_id = instance
self.device_group_name = device_group_name
self._rule_variables = dict()
self._rule_variables = self.rule_variables
[docs] def apply(self, device_ids: list = None, commit: bool = False):
"""
Apply the playbook instance
:param device_ids: if the rule variables need to be associated for
given device id(s). Default to device group
:param commit: Pass true if need to commit the changes
Example:
::
from jnpr.healthbot import PlayBookInstanceBuilder
pbb = PlayBookInstanceBuilder(hb, 'forwarding-table-summary', 'HbEZ-instance', 'Core')
pbb.apply()
:return: True if all OK
"""
if self.device_group_name is None:
raise RuntimeError("PlayBookInstanceBuilder object device group attribute not provided")
device_instance = Device(self.hbot)
device_group_instance = DeviceGroup(self.hbot)
device_group = device_group_instance.get(self.device_group_name)
if device_group is None:
raise NotFoundError({'detail': 'No details for device group: {}'.format(
self.device_group_name), 'status': 404})
if device_ids is not None:
for device_id in device_ids:
if device_id not in device_group.devices:
raise RuntimeError("Given device id '{}' is not present".
format(device_id))
device = device_instance.get(device_id)
device.variable = self.device_variable
if not device_instance.update(device):
logger.error(
"Not able to update '{}' device id".format(device_id))
device_group_variable = []
for item in self.device_variable:
tmp = copy.copy(item)
tmp.pop('variable-value', None)
device_group_variable.append(tmp)
existing_variable = device_group.variable
if existing_variable is None:
device_group.variable = device_group_variable
else:
existing_variable.extend(device_group_variable)
device_group.variable = existing_variable
if not device_group_instance.update(device_group):
logger.error(
"Not able to update '{}' device group".format(device_group))
return False
else:
existing_variable = device_group.variable
if existing_variable is None:
device_group.variable = self.device_variable
else:
existing_variable.extend(self.device_variable)
device_group.variable = existing_variable
existing_playbooks = device_group.playbooks or []
existing_playbooks.append(self.playbook)
device_group.playbooks = existing_playbooks
if not device_group_instance.update(device_group):
logger.error(
"Not able to update '{}' device group".format(device_group))
return False
if commit:
self.hbot.commit()
return True
return True
[docs] def clear(self):
"""
Clear the old set values to rule variables
:return: None
"""
self._rule_variables = dict()
self._rule_variables = self.rule_variables
[docs] def delete(self):
"""
Delete playbook instance
Example:
::
from jnpr.healthbot import PlayBookInstanceBuilder
pbb = PlayBookInstanceBuilder(hb, 'forwarding-table-summary', 'HbEZ-instance', 'Core')
pbb.delete()
:return: True if success
"""
delete_playbook_from_group = True
if self.device_group_name is None:
raise RuntimeError("PlayBookInstanceBuilder object device group attribute not provided")
device_instance = Device(self.hbot)
device_group_instance = DeviceGroup(self.hbot)
device_group = device_group_instance.get(self.device_group_name)
if device_group is None:
raise NotFoundError({'detail': 'No details for device group: {}'.format(
self.device_group_name), 'status': 404})
for device_id in device_group.devices:
device = device_instance.get(device_id)
existing_variables = device.variable or []
if len(existing_variables) > 0:
changed = False
for variable in existing_variables:
if self.playbook == variable.get('playbook'):
if self.instance_id == variable.get('instance-id'):
device.variable.remove(variable)
changed = True
else:
delete_playbook_from_group = False
if changed and not device_instance.update(device):
logger.error(
"Not able to update '{}' device id".format(device_id))
return False
existing_variables = copy.copy(device_group.variable) or []
if len(existing_variables) > 0:
changed = False
for variable in existing_variables:
if self.playbook == variable.get('playbook'):
if self.instance_id == variable.get('instance-id'):
device_group.variable.remove(variable)
changed = True
else:
delete_playbook_from_group = False
if changed and not device_group_instance.update(device_group):
logger.error(
"Not able to update '{}' device id".format(device_id))
return False
if delete_playbook_from_group:
update_playbooks = device_group.playbooks or []
if self.playbook in update_playbooks:
update_playbooks.remove(self.playbook)
device_group.playbook = update_playbooks
if not device_group_instance.update(device_group):
logger.error(
"Not able to update '{}' device group".format(device_group))
return False
return True
@property
def playbook_schema(self):
return self._get_playbook_schema()
@playbook_schema.setter
def playbook_schema(self, value):
""" read-only property """
raise RuntimeError("playbook_schema is read-only!")
@property
def rules(self):
return self.playbook_schema.to_dict().get('rules', [])
@rules.setter
def rules(self, value):
""" read-only property """
raise RuntimeError("rules is read-only!")
@property
def rule_variables(self):
for i in self.rules:
if i not in self._rule_variables:
variable = Rule(self.hbot).get(*i.split('/')).variable
if variable is not None:
self._rule_variables[i] = _RuleVariableClass(variable)
else:
self._rule_variables[i] = None
return self._rule_variables
@rule_variables.setter
def rule_variables(self, value):
""" read-only property """
raise RuntimeError("rules_variables is read-only!")
@property
def device_variable(self):
variable = []
for key, val in self._rule_variables.items():
tmp = dict()
tmp['@'] = {'changed-seconds': int(time.time())}
tmp['instance-id'] = self.instance_id
tmp['rule'] = key
tmp['playbook'] = self.playbook
variable_value = val._variable_value if val is not None else []
tmp['variable-value'] = variable_value
variable.append(tmp)
return variable
def _get_playbook_schema(self):
"""
Get PlaybookSchema for given playbook name
:return: PlaybookSchema `PlaybookSchema <jnpr.healthbot.swagger.models.html#playbookschema>`_
"""
# get playbook schema first
try:
logger.debug("Getting details of playbook '{}'".format(
self.playbook))
return self.get(self.playbook)
except Exception as ex:
logger.error("Got exception '{}' while getting details of "
"playbook '{}'".format(ex, self.playbook_name))
raise ex