diff --git a/Api01.py b/Api01.py new file mode 100644 index 00000000..1fb027bc --- /dev/null +++ b/Api01.py @@ -0,0 +1,26 @@ +from typing import List +from idoit_scaleup.base import IDoitApiBase + +from idoit_scaleup import createApiCalls +from idoit_scaleup import consts + +import json + +config_file = 'api.json' + +# Test Abfrage der IP-Adressen + +OBJ_ID = 3518 + +f = open(config_file) +cfg = json.load(f) +idoit_apis = createApiCalls(cfg) +api = idoit_apis[consts.C__CATG__IP] +api.set_debug_mode() +#rtn=api.read_categories(OBJ_ID) +rtn=api.get_all() +print(json.dumps(rtn)) + + + + diff --git a/api.json b/api.json new file mode 100644 index 00000000..9aedcf39 --- /dev/null +++ b/api.json @@ -0,0 +1,6 @@ + { + "api_key": "c1ia5q", + "user": "admin", + "password" : "admin", + "jrpc_url": "https://demo.i-doit.com/src/jsonrpc.php" +} \ No newline at end of file diff --git a/idoit_scaleup/__init__.py b/idoit_scaleup/__init__.py new file mode 100755 index 00000000..dc678aec --- /dev/null +++ b/idoit_scaleup/__init__.py @@ -0,0 +1,90 @@ +from .object import IDoitObject +from . import consts +from pprint import pprint +from .cat_access import IDoitAccess +from .cat_application import IDoitApplication +from .cat_connector import IDoitConnector +from .cat_cpu import IDoitCpu +from .cat_ip import IDoitIP +from .cat_location import IDoitLocation +from .cat_memory import IDoitMemory +from .cat_network import IDoitNetwork +from .cat_networkport import IDoitNetworkPort +from .cat_network_log_port import IDoitNetworkLogicalPort +from .cat_power_consumer import IDoitPowerConsumer +#from .cat_racktables import Racktables +from .cat_vlan import IDoitVlan +from .category import IDoitCategory +from .conditional_read import IDoitConditionalRead +from .dialog import IDoitDialog +from .search import IDoitSearch +from .cat_storage_device import IDoitStorageDevice +from .api_log import IDoitApiLog +import sys +import inspect + + +def createApiCall(cfg, category): + current_module = sys.modules[__name__] + for name, obj in inspect.getmembers(current_module): + if inspect.isclass(obj): + if issubclass(obj, IDoitCategory): + try: + found = False + found = (obj.CATEGORY == category) + if found: + return obj(cfg) + except AttributeError: + pass + if category.startswith('C__OBJTYPE__'): + return IDoitObject(cfg, category) + if category.startswith('C__CATS__') or category.startswith('C__CATG__'): + return IDoitCategory(cfg, category) + return None + + +def createApiCalls(cfg): + rtn = {} + for varname in consts.__dict__.keys(): + if not (varname in rtn.keys()): + rtn[varname] = createApiCall(cfg, varname) + return rtn + + +def createApiDialogs(cfg, category, field): + if 'dialogs' not in cfg.keys(): + cfg['dialogs'] = {} + if category not in cfg['dialogs'].keys(): + cfg['dialogs'][category] = {} + if field not in cfg['dialogs'][category].keys(): + cfg['dialogs'][category][field] = IDoitDialog(cfg, category, field) + return cfg['dialogs'][category][field] + + +def search(cfg) -> IDoitSearch: + return IDoitSearch(cfg) + + +def conditional_read(cfg) -> IDoitConditionalRead: + return IDoitConditionalRead(cfg) + + +def get_all_classes(): + rtn = [] + current_module = sys.modules[__name__] + for name, obj in inspect.getmembers(current_module): + if inspect.isclass(obj): + rtn.append(name) + return rtn + + +def turn_on_api_logging(): + IDoitApiLog.instance().turn_on() + + +def turn_off_api_logging(): + IDoitApiLog.instance().turn_off() + + +def get_api_log(): + return IDoitApiLog.instance().get_api_log() diff --git a/idoit_scaleup/__pycache__/__init__.cpython-311.pyc b/idoit_scaleup/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..6178bc1f Binary files /dev/null and b/idoit_scaleup/__pycache__/__init__.cpython-311.pyc differ diff --git a/idoit_scaleup/__pycache__/__init__.cpython-312.pyc b/idoit_scaleup/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..964739fb Binary files /dev/null and b/idoit_scaleup/__pycache__/__init__.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/api_log.cpython-312.pyc b/idoit_scaleup/__pycache__/api_log.cpython-312.pyc new file mode 100644 index 00000000..b357445a Binary files /dev/null and b/idoit_scaleup/__pycache__/api_log.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/base.cpython-311.pyc b/idoit_scaleup/__pycache__/base.cpython-311.pyc new file mode 100644 index 00000000..d351706e Binary files /dev/null and b/idoit_scaleup/__pycache__/base.cpython-311.pyc differ diff --git a/idoit_scaleup/__pycache__/base.cpython-312.pyc b/idoit_scaleup/__pycache__/base.cpython-312.pyc new file mode 100644 index 00000000..d7204cdf Binary files /dev/null and b/idoit_scaleup/__pycache__/base.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_access.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_access.cpython-312.pyc new file mode 100644 index 00000000..d3c82b5f Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_access.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_application.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_application.cpython-312.pyc new file mode 100644 index 00000000..e14b5328 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_application.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_connector.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_connector.cpython-312.pyc new file mode 100644 index 00000000..d3b54498 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_connector.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_cpu.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_cpu.cpython-312.pyc new file mode 100644 index 00000000..c19a7963 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_cpu.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_ip.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_ip.cpython-312.pyc new file mode 100644 index 00000000..1b3f26e5 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_ip.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_location.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_location.cpython-312.pyc new file mode 100644 index 00000000..294d60f4 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_location.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_memory.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_memory.cpython-312.pyc new file mode 100644 index 00000000..553b2526 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_memory.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_network.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_network.cpython-312.pyc new file mode 100644 index 00000000..faed1e52 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_network.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_network_log_port.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_network_log_port.cpython-312.pyc new file mode 100644 index 00000000..f5ea47ab Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_network_log_port.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_networkport.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_networkport.cpython-312.pyc new file mode 100644 index 00000000..24a73392 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_networkport.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_power_consumer.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_power_consumer.cpython-312.pyc new file mode 100644 index 00000000..b0eb978e Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_power_consumer.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_racktables.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_racktables.cpython-312.pyc new file mode 100644 index 00000000..fc8e8ce4 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_racktables.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_storage_device.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_storage_device.cpython-312.pyc new file mode 100644 index 00000000..d0697f35 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_storage_device.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/cat_vlan.cpython-312.pyc b/idoit_scaleup/__pycache__/cat_vlan.cpython-312.pyc new file mode 100644 index 00000000..fce61790 Binary files /dev/null and b/idoit_scaleup/__pycache__/cat_vlan.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/category.cpython-312.pyc b/idoit_scaleup/__pycache__/category.cpython-312.pyc new file mode 100644 index 00000000..3ef0b738 Binary files /dev/null and b/idoit_scaleup/__pycache__/category.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/conditional_read.cpython-312.pyc b/idoit_scaleup/__pycache__/conditional_read.cpython-312.pyc new file mode 100644 index 00000000..3816cb69 Binary files /dev/null and b/idoit_scaleup/__pycache__/conditional_read.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/consts.cpython-312.pyc b/idoit_scaleup/__pycache__/consts.cpython-312.pyc new file mode 100644 index 00000000..1cc99229 Binary files /dev/null and b/idoit_scaleup/__pycache__/consts.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/dialog.cpython-312.pyc b/idoit_scaleup/__pycache__/dialog.cpython-312.pyc new file mode 100644 index 00000000..9019001f Binary files /dev/null and b/idoit_scaleup/__pycache__/dialog.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/object.cpython-311.pyc b/idoit_scaleup/__pycache__/object.cpython-311.pyc new file mode 100644 index 00000000..9cd6f01a Binary files /dev/null and b/idoit_scaleup/__pycache__/object.cpython-311.pyc differ diff --git a/idoit_scaleup/__pycache__/object.cpython-312.pyc b/idoit_scaleup/__pycache__/object.cpython-312.pyc new file mode 100644 index 00000000..c07037c0 Binary files /dev/null and b/idoit_scaleup/__pycache__/object.cpython-312.pyc differ diff --git a/idoit_scaleup/__pycache__/search.cpython-312.pyc b/idoit_scaleup/__pycache__/search.cpython-312.pyc new file mode 100644 index 00000000..9ef56091 Binary files /dev/null and b/idoit_scaleup/__pycache__/search.cpython-312.pyc differ diff --git a/idoit_scaleup/api_log.py b/idoit_scaleup/api_log.py new file mode 100755 index 00000000..24ffc1a8 --- /dev/null +++ b/idoit_scaleup/api_log.py @@ -0,0 +1,30 @@ + + +class IDoitApiLog: + _instance = None + + def __init__(self): + raise RuntimeError('Call instance() instead') + + @classmethod + def instance(cls): + if cls._instance is None: + cls._instance = cls.__new__(cls) + cls.log = [] + cls.do_log = False + # Put any initialization here. + return cls._instance + + def turn_on(cls): + cls.do_log = True + + def append_api_log(cls, url: str, payload, response): + if cls.do_log: + cls.log.append({ + 'url': url, + 'payload': payload, + 'response': response + }) + + def get_api_log(cls): + return cls.log diff --git a/idoit_scaleup/base.py b/idoit_scaleup/base.py new file mode 100755 index 00000000..fc166642 --- /dev/null +++ b/idoit_scaleup/base.py @@ -0,0 +1,54 @@ +import json +import requests +from pprint import pprint +from .api_log import IDoitApiLog +from json.decoder import JSONDecodeError + + +class IDoitApiBase: + def __init__(self, cfg): + self.cfg = cfg + self.debug = False + + def set_debug_mode(self, debug=True): + self.debug = debug + + def xml_rpc_call(self, method, params, debug=False): + headers = {'content-type': 'application/json'} + payload = { + "method": method, + "params": params, + "jsonrpc": "2.0", + "id": 1, + } + basic_auth = (self.cfg['user'], self.cfg['password']) + params['apikey'] = self.cfg['api_key'] + try: + response = requests.post( + self.cfg['jrpc_url'], + data=json.dumps(payload), + auth=basic_auth, + headers=headers + ).json() + except JSONDecodeError: + print('JSON DECODE ERROR') + print('Url') + print(self.cfg['jrpc_url']) + print('Payload') + payload['params']['apikey'] = 'xxx' + pprint(payload) + response = {} + + IDoitApiLog.instance().append_api_log( + self.cfg['jrpc_url'], payload, response) + + if self.debug or debug or 'error' in response.keys(): + print('Url') + print(self.cfg['jrpc_url']) + print('Payload') + payload['params']['apikey'] = 'xxx' + pprint(payload) + print('Resonse') + pprint(response) + + return response diff --git a/idoit_scaleup/cat_access.py b/idoit_scaleup/cat_access.py new file mode 100755 index 00000000..be7209f9 --- /dev/null +++ b/idoit_scaleup/cat_access.py @@ -0,0 +1,13 @@ +from .consts import C__CATG__ACCESS +from .category import IDoitCategory + + +class IDoitAccess(IDoitCategory): + + CATEGORY = C__CATG__ACCESS + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_primary(self, data): + return int(data['primary']['value']) diff --git a/idoit_scaleup/cat_application.py b/idoit_scaleup/cat_application.py new file mode 100755 index 00000000..d6364a67 --- /dev/null +++ b/idoit_scaleup/cat_application.py @@ -0,0 +1,42 @@ +from .consts import C__CATG__APPLICATION +from .category import IDoitCategory +from copy import deepcopy + +class IDoitApplication(IDoitCategory): + + CATEGORY = C__CATG__APPLICATION + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + + # FIXME Skip, must be implemented later + def convert_field_with_name_assigned_license(self, data): + return 0 + + # FIXME Skip, must be implemented later + def convert_field_with_name_assigned_database_schema(self, data): + return 0 + + # FIXME Skip, must be implemented later + def convert_field_with_name_assigned_it_service(self, data): + return 0 + + def convert_field_with_name_assigned_version(self, data): + return int(data['assigned_version']['ref_id']) + + def convert_field_with_name_bequest_nagios_services(self, data): + return int(data['bequest_nagios_services']['value']) +# def fix_obj(self, cdata): +# if ('assigned_version' in cdata.keys()) and (cdata['assigned_version'] is not None): +# cdata['assigned_version'] = str(cdata['assigned_version']) + +# def save_category(self, objId, data): +# cdata = deepcopy(data) +# self.fix_obj(cdata) +# return super().save_category(objId, cdata) + +# def update_category(self, objId, data): +# cdata = deepcopy(data) +# self.fix_obj(cdata) +# return super().update_category(objId, cdata) diff --git a/idoit_scaleup/cat_connector.py b/idoit_scaleup/cat_connector.py new file mode 100755 index 00000000..d9922f55 --- /dev/null +++ b/idoit_scaleup/cat_connector.py @@ -0,0 +1,31 @@ +from .consts import C__CATG__CONNECTOR +from .category import IDoitCategory +from pprint import pprint + + +class IDoitConnector(IDoitCategory): + + CATEGORY = C__CATG__CONNECTOR + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_assigned_category(self, data): + return data['assigned_category']['const'] + + def convert_field_with_name_assigned_connector(self, data): + return self.conv_array_field('assigned_connector', data, 'ref_id') + + def convert_field_with_name_cable_connection(self, data): + return self.conv_array_field('cable_connection', data, 'id') + + def convert_field_with_name_relation_direction(self, data): + return int(data['relation_direction']['id']) + + def convert_field_with_name_connector_sibling(self, data): + return int(data['connector_sibling']['id']) + + def save_category_if_changed(self, objId, data): + raise Exception( + 'Funktioniert nur wenn es nur eine Kategorie gibt, ' + + 'muss mit ID spezifiziert werden') diff --git a/idoit_scaleup/cat_cpu.py b/idoit_scaleup/cat_cpu.py new file mode 100755 index 00000000..860ab192 --- /dev/null +++ b/idoit_scaleup/cat_cpu.py @@ -0,0 +1,13 @@ +from .consts import C__CATG__CPU +from .category import IDoitCategory + + +class IDoitCpu(IDoitCategory): + + CATEGORY = C__CATG__CPU + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_frequency(self, data): + return float(data['frequency']['title']) diff --git a/idoit_scaleup/cat_ip.py b/idoit_scaleup/cat_ip.py new file mode 100755 index 00000000..4a065899 --- /dev/null +++ b/idoit_scaleup/cat_ip.py @@ -0,0 +1,41 @@ +from .consts import C__CATG__IP +from .category import IDoitCategory + + +class IDoitIP(IDoitCategory): + + CATEGORY = C__CATG__IP + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_primary(self, data): + if data['primary']['value']==None: + return False + return int(data['primary']['value']) + + def convert_field_with_name_active(self, data): + if data['active']['value']==None: + return False + return int(data['active']['value']) + + def convert_field_with_name_use_standard_gateway(self, data): + if data['use_standard_gateway']['value']==None: + return False + return int(data['use_standard_gateway']['value']) + + def convert_field_with_name_ipv4_address(self, data): + return data['ipv4_address']['ref_title'] + + def convert_field_with_name_ipv6_address(self, data): + return data['ipv6_address']['ref_title'] + + def convert_field_with_name_assigned_port(self,data): + return data['assigned_port']['title'] + + def convert_field_with_name_dns_domain(self, data): + rtn=[] + if 'dns_domain' in data and data['dns_domain'] is not None: + for ele in data['dns_domain']: + rtn.append(ele['title']) + return rtn diff --git a/idoit_scaleup/cat_location.py b/idoit_scaleup/cat_location.py new file mode 100755 index 00000000..f8ed7b00 --- /dev/null +++ b/idoit_scaleup/cat_location.py @@ -0,0 +1,42 @@ +from .consts import C__CATG__LOCATION +from .category import IDoitCategory +from pprint import pprint +from copy import deepcopy + + +class IDoitLocation(IDoitCategory): + + CATEGORY = C__CATG__LOCATION + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_pos(self, data): + return int(data['pos']['visually_from']) + + def convert_field_with_name_latitude(self, data): + if data['latitude'] == '': + return None + return float(data['latitude']) + + def convert_field_with_name_longitude(self, data): + if data['longitude'] == '': + return None + return float(data['longitude']) + + def fix_empty_position(self, data): + for pos_key in ['latitude', 'longitude']: + if not (pos_key in data): + data[pos_key] = None + elif data[pos_key] == '': + data[pos_key] = None + + def save_category(self, objId, data): + cdata = deepcopy(data) + self.fix_empty_position(cdata) + return super().save_category(objId, cdata) + + def update_category(self, objId, data): + cdata = deepcopy(data) + self.fix_empty_position(cdata) + return super().update_category(objId, cdata) diff --git a/idoit_scaleup/cat_memory.py b/idoit_scaleup/cat_memory.py new file mode 100755 index 00000000..e3c6df09 --- /dev/null +++ b/idoit_scaleup/cat_memory.py @@ -0,0 +1,13 @@ +from .consts import C__CATG__MEMORY +from .category import IDoitCategory + + +class IDoitMemory(IDoitCategory): + + CATEGORY = C__CATG__MEMORY + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_capacity(self, data): + return float(data['capacity']['title']) diff --git a/idoit_scaleup/cat_network.py b/idoit_scaleup/cat_network.py new file mode 100755 index 00000000..ba6bd08c --- /dev/null +++ b/idoit_scaleup/cat_network.py @@ -0,0 +1,40 @@ +from .consts import C__CATS__NET +from .category import IDoitCategory +from pprint import pprint +from copy import deepcopy +from ipaddress import IPv4Network +from ipaddress import IPv6Network + + +class IDoitNetwork(IDoitCategory): + + CATEGORY = C__CATS__NET + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_layer2_assignments(self, data): + rtn = [] + for ele in data['layer2_assignments']: + rtn.append(int(ele['id'])) + return rtn + + def fix_mask_and_range(self, data): + net_str=f"{data['address']}/{data['cidr_suffix']}" + if int(data['type'])==1: + net= IPv4Network(net_str) + elif int(data['type'])==1000: + net= IPv6Network(net_str) + data['netmask']= str(net.netmask) + data['range_from'] = str(net[0]) + data['range_to'] = str(net[-1]) + + def save_category(self, objId, data): + cdata = deepcopy(data) + self.fix_mask_and_range(cdata) + return super().save_category(objId, cdata) + + def update_category(self, objId, data): + cdata = deepcopy(data) + self.fix_mask_and_range(cdata) + return super().update_category(objId, cdata) diff --git a/idoit_scaleup/cat_network_log_port.py b/idoit_scaleup/cat_network_log_port.py new file mode 100755 index 00000000..7782acc0 --- /dev/null +++ b/idoit_scaleup/cat_network_log_port.py @@ -0,0 +1,64 @@ +from .consts import C__CATG__NETWORK_LOG_PORT, C__CATG__IP, C__CATG__NETWORK_PORT +from .category import IDoitCategory +from copy import deepcopy + + +class IDoitNetworkLogicalPort(IDoitCategory): + + CATEGORY = C__CATG__NETWORK_LOG_PORT + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_active(self, data): + return int(data['active']['value']) + + def convert_field_with_name_interface(self, data): + if len(data['interface']) == 0: + return None + return int(data['interface'][0]['id']) + + def convert_field_with_name_addresses(self, data): + return self.convert_list(data['addresses']) + + def convert_field_with_name_layer2_assignment(self, data): + if data['layer2_assignment'] == []: + return None + # else: + raise Exception('unknown conversion ', data) + + def convert_field_with_name_assigned_connector(self, data): + return self.conv_array_field('assigned_connector', data, 'ref_id') + + def convert_field_with_name_net(self, data): + return self.convert_list(data['net']) + + def save_category_if_changed(self, objId, data): + raise Exception( + 'Funktioniert nur wenn es nur eine Kategorie gibt, ' + + 'muss mit ID spezifiziert werden') + + def fix_obj(self, cdata): + if ('interface' in cdata.keys()) and (cdata['interface'] is not None): + cdata['interface'] = "%d_C__CATG__NETWORK_INTERFACE" % cdata['interface'] + if ('addresses' in cdata.keys()) and (cdata['addresses'] is not None): + rtn = [] + for ele in cdata['addresses']: + rtn.append(str(ele)) + cdata['addresses'] = rtn + + if ('ports' in cdata.keys()) and (cdata['ports'] is not None): + rtn = [] + for ele in cdata['ports']: + rtn.append("%d" % (ele)) + cdata['ports'] = rtn + + def save_category(self, objId, data): + cdata = deepcopy(data) + self.fix_obj(cdata) + return super().save_category(objId, cdata) + + def update_category(self, objId, data): + cdata = deepcopy(data) + self.fix_obj(cdata) + return super().update_category(objId, cdata) diff --git a/idoit_scaleup/cat_networkport.py b/idoit_scaleup/cat_networkport.py new file mode 100755 index 00000000..5cdb0846 --- /dev/null +++ b/idoit_scaleup/cat_networkport.py @@ -0,0 +1,69 @@ +from .consts import C__CATG__NETWORK_PORT +from .category import IDoitCategory +from copy import deepcopy + + +class IDoitNetworkPort(IDoitCategory): + + CATEGORY = C__CATG__NETWORK_PORT + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_active(self, data): + return int(data['active']['value']) + + def convert_field_with_name_interface(self, data): + if len(data['interface']) == 0: + return None + return int(data['interface'][0]['id']) + + def convert_field_with_name_speed(self, data): + return float(data['speed']['title']) + + def convert_field_with_name_cable(self, data): + return self.conv_array_field('cable', data, 'id') + + def convert_field_with_name_addresses(self, data): + return self.convert_list(data['addresses']) + + def convert_field_with_name_layer2_assignment(self, data): + if data['layer2_assignment'] == []: + return None + # else: + raise Exception('unknown conversion ', data) + + def convert_field_with_name_assigned_connector(self, data): + return self.conv_array_field('assigned_connector', data, 'ref_id') + + def convert_field_with_name_relation_direction(self, data): + return int(data['relation_direction']['id']) + + def save_category_if_changed(self, objId, data): + raise Exception( + 'Funktioniert nur wenn es nur eine Kategorie gibt, ' + + 'muss mit ID spezifiziert werden') + + def fix_obj(self, cdata): + #if ('interface' in cdata.keys()) and (cdata['interface'] is not None): + # cdata['interface'] = "%d_C__CATG__NETWORK_INTERFACE" % cdata['interface'] + + if ('addresses' in cdata.keys()) : + if cdata['addresses'] is None: + cdata['addresses'] = None + # FIXME Das geht im Moment nicht. siehe Idoit Bug #20429 (sanders) + else: + rtn = [] + for ele in cdata['addresses']: + rtn.append(str(ele)) + cdata['addresses'] = rtn + + def save_category(self, objId, data): + cdata = deepcopy(data) + self.fix_obj(cdata) + return super().save_category(objId, cdata) + + def update_category(self, objId, data): + cdata = deepcopy(data) + self.fix_obj(cdata) + return super().update_category(objId, cdata) diff --git a/idoit_scaleup/cat_power_consumer.py b/idoit_scaleup/cat_power_consumer.py new file mode 100755 index 00000000..a927a6de --- /dev/null +++ b/idoit_scaleup/cat_power_consumer.py @@ -0,0 +1,13 @@ +from .consts import C__CATG__POWER_CONSUMER +from .category import IDoitCategory + + +class IDoitPowerConsumer(IDoitCategory): + + CATEGORY = C__CATG__POWER_CONSUMER + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_active(self, data): + return int(data['active']['value']) diff --git a/idoit_scaleup/cat_racktables.py b/idoit_scaleup/cat_racktables.py new file mode 100755 index 00000000..315b74ba --- /dev/null +++ b/idoit_scaleup/cat_racktables.py @@ -0,0 +1,62 @@ +from .consts import C__CATG__CUSTOM_FIELDS_RACKTABLES +from pprint import pprint +from .category import IDoitCategory + + +class Racktables(IDoitCategory): + CATEGORY = C__CATG__CUSTOM_FIELDS_RACKTABLES + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + self.rt_link = '' + self.rt_id = '' + self.rt_type = '' + self.rt_content = '' + for field in self.fields: + title = self.fields[field]['title'] + if title == 'Racktables URL': + self.rt_link = field + if title == 'Racktables ID': + self.rt_id = field + if title == 'Racktables Object Type': + self.rt_type = field + if title == 'Racktables Inhalt': + self.rt_content = field + if ((self.rt_link == '') or + (self.rt_id == '') or + (self.rt_type == '') or + (self.rt_content == '')): + raise Exception('Object nicht deifinert') + + def save_category(self, objId, data): + mydata = {} + if 'id' in data.keys(): + mydata[self.rt_id] = data['id'] + if 'link' in data.keys(): + mydata[self.rt_link] = data['link'] + if 'type' in data.keys(): + mydata[self.rt_type] = data['type'] + if 'content' in data.keys(): + mydata[self.rt_content] = data['content'] + if 'description' in data.keys(): + mydata['description'] = data['description'] + return super().save_category(objId, mydata) + + def convert_incomming_category(self, data): + rtn = {} + if self.rt_id in data.keys(): + rtn['id'] = data[self.rt_id] + + if self.rt_link in data.keys(): + rtn['link'] = data[self.rt_link] + + if self.rt_type in data.keys(): + rtn['type'] = data[self.rt_type] + + if self.rt_content in data.keys(): + rtn['content'] = data[self.rt_content] + + if 'description' in data.keys(): + rtn['description'] = data['description'] + + rtn['_data'] = data + return rtn diff --git a/idoit_scaleup/cat_storage_device.py b/idoit_scaleup/cat_storage_device.py new file mode 100755 index 00000000..f5864f76 --- /dev/null +++ b/idoit_scaleup/cat_storage_device.py @@ -0,0 +1,16 @@ +from .consts import C__CATG__STORAGE_DEVICE +from .category import IDoitCategory + + +class IDoitStorageDevice(IDoitCategory): + + CATEGORY = C__CATG__STORAGE_DEVICE + def __init__(self, cfg): + + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_capacity(self, data): + return float(data['capacity']['title']) + + def convert_field_with_name_hotspare(self, data): + return int(data['hotspare']['value']) diff --git a/idoit_scaleup/cat_vlan.py b/idoit_scaleup/cat_vlan.py new file mode 100755 index 00000000..791d60dc --- /dev/null +++ b/idoit_scaleup/cat_vlan.py @@ -0,0 +1,20 @@ +from .consts import C__CATS__LAYER2_NET +from .category import IDoitCategory +from pprint import pprint + + +class IDoitVlan(IDoitCategory): + + CATEGORY = C__CATS__LAYER2_NET + + def __init__(self, cfg): + super().__init__(cfg, self.CATEGORY) + + def convert_field_with_name_standard(self, data): + return int(data['standard']['value']) + + def convert_field_with_name_layer3_assignments(self, data): + rtn = [] + for ele in data['layer3_assignments']: + rtn.append(int(ele['id'])) + return rtn diff --git a/idoit_scaleup/category.py b/idoit_scaleup/category.py new file mode 100755 index 00000000..ebee8c00 --- /dev/null +++ b/idoit_scaleup/category.py @@ -0,0 +1,252 @@ +from pprint import pprint +from .base import IDoitApiBase +from copy import deepcopy +import json + + +class IDoitCategory(IDoitApiBase): + def __init__(self, cfg, obj_type: str): + super().__init__(cfg) + self.obj_type = obj_type + params = { + 'category': obj_type + } + r = self.xml_rpc_call('cmdb.category_info', params) + self.fields = {} + for fieldname in r['result'].keys(): + default = None + if 'default' in r['result'][fieldname]['ui']: + default = r['result'][fieldname]['ui']['default'] or None + self.fields[fieldname] = { + 'data_type': r['result'][fieldname]['data']['type'], + 'info_type': r['result'][fieldname]['info']['type'], + 'ui_type': r['result'][fieldname]['ui']['type'], + 'default': default, + 'title': r['result'][fieldname]['info']['title'] + } + + def is_dialog_plus_field(self, fieldname): + return (self.fields[fieldname]['info_type'] == 'dialog_plus') + + def save_category(self, objId, data): + sdata = deepcopy(data) + if "_data" in sdata.keys(): + del (sdata["_data"]) + + params = { + "object": objId, + 'category': self.obj_type, + 'data': sdata} + if self.debug: + print('--Category Save---') + pprint(params) + print('--Category Save---') + return self.xml_rpc_call('cmdb.category.save', params) + + def update_category(self, objId, data): + sdata = deepcopy(data) + if "_data" in sdata.keys(): + del (sdata["_data"]) + if "id" in sdata.keys(): + sdata['category_id'] = sdata['id'] + del (sdata["id"]) + if sdata['category_id'] is None: + raise Exception('category_id is None') + params = { + "objID": objId, + 'category': self.obj_type, + 'data': sdata} + if self.debug: + print('--Category Update---') + pprint(params) + print('--Category Update---') + return self.xml_rpc_call('cmdb.category.update', params) + + def read_categories(self, objId): + params = { + "objID": objId, + 'category': self.obj_type, + } + r = self.xml_rpc_call('cmdb.category.read', params) + rtn = [] + for item in r['result']: + rtn.append(self.convert_incomming_category(item)) + return rtn + + def read_category_by_id(self, objId, id): + for cat in self.read_categories(objId): + if cat['id'] == id: + return cat + # not found + return None + + def delete_category(self, objId, id): + params = { + "objID": objId, + 'category': self.obj_type, + 'cateID': id + } + return self.xml_rpc_call('cmdb.category.delete', params) + + def read_category(self, objId): + r = self.read_categories(objId) + if len(r) == 0: + return None + assert (len(r) == 1) + return r[0] + + def conv_array_field(self, fieldname, data, ref_field): + if len(data[fieldname]) == 0: + return None + if type(data[fieldname]) is dict: + return int(data[fieldname][ref_field]) + if len(data[fieldname]) != 1: + raise Exception('Field "%s" has more than one entry %s' % + (fieldname, json.dumps(data))) + return int(data[fieldname][0][ref_field]) + + def convert_list(self, list): + rtn = [] + for ele in list: + rtn.append(int(ele['id'])) + if len(rtn) == 0: + return None + return rtn + + def convert_field(self, fieldname, data): + object_methods = [method_name for method_name in dir(self) + if callable(getattr(self, method_name))] + field = self.fields[fieldname] + if fieldname in data.keys(): + if data[fieldname] is None: + return None + field_method_name = 'convert_field_with_name_%s' % fieldname + if field_method_name in object_methods: + method = getattr(self, field_method_name) + return method(data) + try: + if field['ui_type'] == 'popup': + if field['data_type'] == 'int': + return int(data[fieldname]['id']) + if field['ui_type'] == 'text': + if field['data_type'] == 'int': + return int(data[fieldname]) + if field['data_type'] == 'double': + return float(data[fieldname]) + if field['data_type'] == 'float': + return float(data[fieldname]) + if field['data_type'] == 'link': + return data[fieldname] + if field['data_type'] == 'text': + return data[fieldname] + if field['ui_type'] == 'wysiwyg': + if field['data_type'] == 'text': + return data[fieldname] + if field['ui_type'] == 'textarea': + if field['data_type'] == 'text_area': + return data[fieldname] + if field['ui_type'] == 'dialog': + if field['data_type'] == 'int': + return int(data[fieldname]['id']) + if field['data_type'] == 'text': + return data[fieldname]['id'] + if field['ui_type'] == 'f_dialog_list': + if field['data_type'] == 'int': + return self.convert_list(data[fieldname]) + if field['ui_type'] == 'datetime': + if field['data_type'] == 'date_time': + try: + rtn=data[fieldname]['title'] + except: + rtn=data[fieldname] + return rtn + if field['ui_type'] == 'numeric': + if field['data_type'] == 'text': + return float(data[fieldname]) + except: + raise Exception('Wrong conversion ', + self.obj_type, fieldname, field, data) + raise Exception('Unknwown data_type/ ui_type', + self.obj_type, fieldname, field, data) + + def convert_incomming_category(self, data): + rtn = {} + for fieldname in self.fields.keys(): + rtn[fieldname] = self.convert_field(fieldname, data) + if 'id' in data.keys(): + rtn['id'] = int(data['id']) + rtn['_data'] = data + return rtn + + def partial_equal(self, obj1, obj2): + + if isinstance(obj1, str): + return (obj1 == obj2) + + if isinstance(obj1, int): + return (obj1 == obj2) + if obj1 is None and obj2 is None: + return True + if obj1 is None: + return False + if obj2 is None: + return False + + if isinstance(obj1, list): + if isinstance(obj2, list): + if len(obj1) != len(obj2): + return False + for ele in obj1: + if ele not in obj2: + return False + return True + else: + return False + + for key in obj1.keys(): + if key not in obj2: + return False + if not (self.partial_equal(obj1[key], obj2[key])): + return False + return True + + # Funktioniert nur wo es genau eine Kategory gibt + def save_category_if_changed(self, objId, data): + oldData = self.read_category(objId) + if not (self.partial_equal(data, oldData)): + print('save', objId, self.obj_type) + pprint(data) + print('old:') + pprint(oldData) + r = self.save_category(objId, data) + print(r) + + def update_categorys(self, objId, equals_attr, new_data_arr): + old_data_arr = self.read_categories(objId) + for old_data in old_data_arr: + old_found = False + for new_data in new_data_arr: + if old_data[equals_attr] == new_data[equals_attr]: + old_found = True + if not (self.partial_equal(new_data, old_data)): + print('save', objId, self.obj_type) + pprint(new_data) + print('old:') + pprint(old_data) + new_data['id'] = old_data['id'] + r = self.update_category(objId, new_data) + pprint(r) + if not old_found: + print('delete:', objId, self.obj_type) + pprint(old_data) + self.delete_category(objId, old_data['id']) + for new_data in new_data_arr: + new_found = False + for old_data in old_data_arr: + if old_data[equals_attr] == new_data[equals_attr]: + new_found = True + if not new_found: + print('save', objId, self.obj_type) + pprint(new_data) + r = self.save_category(objId, new_data) + pprint(r) diff --git a/idoit_scaleup/conditional_read.py b/idoit_scaleup/conditional_read.py new file mode 100755 index 00000000..895bbcc5 --- /dev/null +++ b/idoit_scaleup/conditional_read.py @@ -0,0 +1,29 @@ +from pprint import pprint +from .base import IDoitApiBase + + +class IDoitConditionalRead(IDoitApiBase): + + def __init__(self, cfg): + super().__init__(cfg) + self.clear_search_list() + + def clear_search_list(self): + self.search_list = [] + + def add_search_param(self, category: str, field: str, value: str, operator: str = None, compare: str = "=", ): + entry = { + 'property': "%s-%s" % (category, field), + 'comparison': compare, + 'value': value, + } + if operator: + entry['operator'] = operator + self.search_list.append(entry) + + def search(self): + params = { + 'conditions': self.search_list, + } + rtn = self.xml_rpc_call('cmdb.condition.read', params) + return rtn['result'] diff --git a/idoit_scaleup/consts.py b/idoit_scaleup/consts.py new file mode 100755 index 00000000..d4b6cc74 --- /dev/null +++ b/idoit_scaleup/consts.py @@ -0,0 +1,38 @@ +C__CATG__ACCESS = 'C__CATG__ACCESS' +C__CATG__APPLICATION = 'C__CATG__APPLICATION' +C__CATG__ADDRESS = 'C__CATG__ADDRESS' +C__CATG__CONNECTOR = 'C__CATG__CONNECTOR' +C__CATG__CPU = 'C__CATG__CPU' +#C__CATG__CUSTOM_FIELDS_RACKTABLES = 'C__CATG__CUSTOM_FIELDS_RACKTABLES' +C__CATG__FORMFACTOR = 'C__CATG__FORMFACTOR' +C__CATG__IDENTIFIER = 'C__CATG__IDENTIFIER' +C__CATG__IP = 'C__CATG__IP' +C__CATG__LOCATION = 'C__CATG__LOCATION' +C__CATG__MEMORY = 'C__CATG__MEMORY' +C__CATG__MODEL = 'C__CATG__MODEL' +C__CATG__NETWORK_LOG_PORT = 'C__CATG__NETWORK_LOG_PORT' +C__CATG__NETWORK_PORT = 'C__CATG__NETWORK_PORT' +C__CATG__POWER_CONSUMER = 'C__CATG__POWER_CONSUMER' +C__CATG__STORAGE_DEVICE = 'C__CATG__STORAGE_DEVICE' + +C__CATS__ENCLOSURE = 'C__CATS__ENCLOSURE' +C__CATS__LAYER2_NET = 'C__CATS__LAYER2_NET' +C__CATS__NET = 'C__CATS__NET' +C__CATS__PERSON_MASTER = 'C__CATS__PERSON_MASTER' + +C__OBJTYPE__BUILDING = 'C__OBJTYPE__BUILDING' +C__OBJTYPE__CABLE = 'C__OBJTYPE__CABLE' +C__OBJTYPE__CITY = 'C__OBJTYPE__CITY' +C__OBJTYPE__COUNTRY = 'C__OBJTYPE__COUNTRY' +C__OBJTYPE__ENCLOSURE = 'C__OBJTYPE__ENCLOSURE' +C__OBJTYPE__LAYER2_NET = 'C__OBJTYPE__LAYER2_NET' +C__OBJTYPE__LAYER3_NET = 'C__OBJTYPE__LAYER3_NET' +C__OBJTYPE__PATCH_PANEL = 'C__OBJTYPE__PATCH_PANEL' +C__OBJTYPE__PDU = 'C__OBJTYPE__PDU' +C__OBJTYPE__RACK_SEGMENT = 'C__OBJTYPE__RACK_SEGMENT' +C__OBJTYPE__ROOM = 'C__OBJTYPE__ROOM' +C__OBJTYPE__ROOM = 'C__OBJTYPE__ROOM' +C__OBJTYPE__SERVER = 'C__OBJTYPE__SERVER' +C__OBJTYPE__SWITCH = 'C__OBJTYPE__SWITCH' +C__OBJTYPE__WIRING_SYSTEM = 'C__OBJTYPE__WIRING_SYSTEM' +C__OBJTYPE__WHMCS_CUSTOMER = 'C__OBJTYPE__WHMCS_CUSTOMER' diff --git a/idoit_scaleup/dialog.py b/idoit_scaleup/dialog.py new file mode 100755 index 00000000..3cf75e72 --- /dev/null +++ b/idoit_scaleup/dialog.py @@ -0,0 +1,67 @@ +from .base import IDoitApiBase + + +class IDoitDialog(IDoitApiBase): + def __init__(self, cfg, obj_type: str, property: str): + super().__init__(cfg) + self.obj_type = obj_type + self.property = property + self.cache = {} + + def get_all(self): + if self.cache == {}: + params = { + 'category': self.obj_type, + 'property': self.property + } + r = self.xml_rpc_call('cmdb.dialog.read', params) + # Workaround für Ticket #22087 + # https://help.i-doit.com/hc/en-us/requests/22087 + if len(r['result'])==1: + if isinstance(r['result'][0],list): + r['result']=r['result'][0] + self.cache = r + else: + r = self.cache + return self.cache['result'] + + def get(self, value: str, parent: int = None): + for entry in self.get_all(): + if parent is None: + if value == entry['title']: + return int(entry['id']) + else: + if (value == entry['title'] and + entry['parent']['id'] == str(parent)): + return int(entry['id']) + return None + + def get_ignore_case(self, value: str, parent: int = None): + for entry in self.get_all(): + title=entry['title'].lower() + if parent is None: + if value.lower() == title: + return int(entry['id']) + else: + if (value.lower() == title and + entry['parent']['id'] == str(parent)): + return int(entry['id']) + return None + + def create(self, value: str, parent: int = None): + params = { + 'category': self.obj_type, + 'property': self.property, + 'value': value + } + self.cache = {} + if parent is not None: + params['parent'] = parent + r = self.xml_rpc_call('cmdb.dialog.create', params) + return r['result']['entry_id'] + + def create_or_get_id(self, value: str, parent: int = None): + id = self.get(value, parent) + if id is None: + id = self.create(value, parent) + return id diff --git a/idoit_scaleup/object.py b/idoit_scaleup/object.py new file mode 100755 index 00000000..e8c96631 --- /dev/null +++ b/idoit_scaleup/object.py @@ -0,0 +1,98 @@ +from typing import List +from .base import IDoitApiBase + + +class IDoitObject(IDoitApiBase): + def __init__(self, cfg, obj_type: str): + super().__init__(cfg) + self.obj_type = obj_type + + def get_by_title(self, title: str, categories: List = []): + params = { + 'filter': { + 'type': self.obj_type, + 'title': title, + }, + } + if len(categories) > 0: + params['categories'] = categories + rtn = self.xml_rpc_call('cmdb.objects', params) + if len(rtn['result']) == 0: + return None + else: + return rtn['result'][0] + + def get_by_id(self, obj_id: str, categories: List = []): + params = { + 'id': obj_id + } + if len(categories) > 0: + params['categories'] = categories + rtn = self.xml_rpc_call('cmdb.object', params) + return rtn['result'] + + def get_all(self, categories: List = [], ids = None): + params = { + 'filter': { + 'type': self.obj_type + }, + } + if ids: + params['filter']['ids'] = ids + if len(categories) > 0: + params['categories'] = categories + rtn = self.xml_rpc_call('cmdb.objects', params) + return rtn['result'] + + def create_object_with_title(self, title: str): + params = { + 'title': title, + 'type': self.obj_type + } + return self.xml_rpc_call('cmdb.object.create', params) + + def create_object_if_not_there(self, title): + obj = self.get_by_title(title) + if obj is None: + r = self.create_object_with_title(title) + print('-------------------') + print("%s (%s) " % (title, self.obj_type)) + print('-------------------') + objId = r['result']['id'] + else: + objId = obj['id'] + return objId + + def update_object(self, obj_id: str, title: str): + params = { + 'id': obj_id, + 'title': title, + } + return self.xml_rpc_call('cmdb.object.update', params) + + def archive_object(self, obj_id: str): + params = { + 'id': obj_id, + 'status': 'C__RECORD_STATUS__ARCHIVED', + } + return self.xml_rpc_call('cmdb.object.delete', params) + + def delete_object(self, obj_id: str): + params = { + 'id': obj_id, + 'status': 'C__RECORD_STATUS__DELETED', + } + return self.xml_rpc_call('cmdb.object.delete', params) + + def purge_object(self, obj_id: str): + params = { + 'id': obj_id, + 'status': 'C__RECORD_STATUS__PURGE', + } + return self.xml_rpc_call('cmdb.object.delete', params) + + def recycle_object(self, obj_id: str): + params = { + 'id': obj_id, + } + return self.xml_rpc_call('cmdb.object.recycle', params) diff --git a/idoit_scaleup/search.py b/idoit_scaleup/search.py new file mode 100755 index 00000000..be996552 --- /dev/null +++ b/idoit_scaleup/search.py @@ -0,0 +1,12 @@ +from pprint import pprint +from .base import IDoitApiBase + + +class IDoitSearch(IDoitApiBase): + + def search(self, search: str): + params = { + 'q': search, + } + rtn = self.xml_rpc_call('idoit.search', params) + return rtn['result']