From e06c448a46fdfe58825d99881eb30e60eb9f78ef Mon Sep 17 00:00:00 2001 From: Sven Riwoldt Date: Sun, 15 Dec 2024 14:38:48 +0100 Subject: [PATCH] Idoit-Tests 01 --- Api01.py | 26 ++ api.json | 6 + idoit_scaleup/__init__.py | 90 +++++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 5187 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4373 bytes .../__pycache__/api_log.cpython-312.pyc | Bin 0 -> 1525 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 2854 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 2492 bytes .../__pycache__/cat_access.cpython-312.pyc | Bin 0 -> 954 bytes .../cat_application.cpython-312.pyc | Bin 0 -> 1934 bytes .../__pycache__/cat_connector.cpython-312.pyc | Bin 0 -> 2405 bytes .../__pycache__/cat_cpu.cpython-312.pyc | Bin 0 -> 948 bytes .../__pycache__/cat_ip.cpython-312.pyc | Bin 0 -> 2572 bytes .../__pycache__/cat_location.cpython-312.pyc | Bin 0 -> 2455 bytes .../__pycache__/cat_memory.cpython-312.pyc | Bin 0 -> 960 bytes .../__pycache__/cat_network.cpython-312.pyc | Bin 0 -> 2507 bytes .../cat_network_log_port.cpython-312.pyc | Bin 0 -> 4211 bytes .../cat_networkport.cpython-312.pyc | Bin 0 -> 4244 bytes .../cat_power_consumer.cpython-312.pyc | Bin 0 -> 988 bytes .../cat_racktables.cpython-312.pyc | Bin 0 -> 3145 bytes .../cat_storage_device.cpython-312.pyc | Bin 0 -> 1243 bytes .../__pycache__/cat_vlan.cpython-312.pyc | Bin 0 -> 1362 bytes .../__pycache__/category.cpython-312.pyc | Bin 0 -> 10828 bytes .../conditional_read.cpython-312.pyc | Bin 0 -> 1700 bytes .../__pycache__/consts.cpython-312.pyc | Bin 0 -> 1372 bytes .../__pycache__/dialog.cpython-312.pyc | Bin 0 -> 3259 bytes .../__pycache__/object.cpython-311.pyc | Bin 0 -> 4912 bytes .../__pycache__/object.cpython-312.pyc | Bin 0 -> 4277 bytes .../__pycache__/search.cpython-312.pyc | Bin 0 -> 700 bytes idoit_scaleup/api_log.py | 30 +++ idoit_scaleup/base.py | 54 ++++ idoit_scaleup/cat_access.py | 13 + idoit_scaleup/cat_application.py | 42 +++ idoit_scaleup/cat_connector.py | 31 +++ idoit_scaleup/cat_cpu.py | 13 + idoit_scaleup/cat_ip.py | 41 +++ idoit_scaleup/cat_location.py | 42 +++ idoit_scaleup/cat_memory.py | 13 + idoit_scaleup/cat_network.py | 40 +++ idoit_scaleup/cat_network_log_port.py | 64 +++++ idoit_scaleup/cat_networkport.py | 69 +++++ idoit_scaleup/cat_power_consumer.py | 13 + idoit_scaleup/cat_racktables.py | 62 +++++ idoit_scaleup/cat_storage_device.py | 16 ++ idoit_scaleup/cat_vlan.py | 20 ++ idoit_scaleup/category.py | 252 ++++++++++++++++++ idoit_scaleup/conditional_read.py | 29 ++ idoit_scaleup/consts.py | 38 +++ idoit_scaleup/dialog.py | 67 +++++ idoit_scaleup/object.py | 98 +++++++ idoit_scaleup/search.py | 12 + 51 files changed, 1181 insertions(+) create mode 100644 Api01.py create mode 100644 api.json create mode 100755 idoit_scaleup/__init__.py create mode 100644 idoit_scaleup/__pycache__/__init__.cpython-311.pyc create mode 100644 idoit_scaleup/__pycache__/__init__.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/api_log.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/base.cpython-311.pyc create mode 100644 idoit_scaleup/__pycache__/base.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_access.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_application.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_connector.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_cpu.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_ip.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_location.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_memory.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_network.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_network_log_port.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_networkport.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_power_consumer.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_racktables.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_storage_device.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/cat_vlan.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/category.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/conditional_read.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/consts.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/dialog.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/object.cpython-311.pyc create mode 100644 idoit_scaleup/__pycache__/object.cpython-312.pyc create mode 100644 idoit_scaleup/__pycache__/search.cpython-312.pyc create mode 100755 idoit_scaleup/api_log.py create mode 100755 idoit_scaleup/base.py create mode 100755 idoit_scaleup/cat_access.py create mode 100755 idoit_scaleup/cat_application.py create mode 100755 idoit_scaleup/cat_connector.py create mode 100755 idoit_scaleup/cat_cpu.py create mode 100755 idoit_scaleup/cat_ip.py create mode 100755 idoit_scaleup/cat_location.py create mode 100755 idoit_scaleup/cat_memory.py create mode 100755 idoit_scaleup/cat_network.py create mode 100755 idoit_scaleup/cat_network_log_port.py create mode 100755 idoit_scaleup/cat_networkport.py create mode 100755 idoit_scaleup/cat_power_consumer.py create mode 100755 idoit_scaleup/cat_racktables.py create mode 100755 idoit_scaleup/cat_storage_device.py create mode 100755 idoit_scaleup/cat_vlan.py create mode 100755 idoit_scaleup/category.py create mode 100755 idoit_scaleup/conditional_read.py create mode 100755 idoit_scaleup/consts.py create mode 100755 idoit_scaleup/dialog.py create mode 100755 idoit_scaleup/object.py create mode 100755 idoit_scaleup/search.py 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 0000000000000000000000000000000000000000..6178bc1f5a28ba7fb01b8477cf1eba3d4cba3755 GIT binary patch literal 5187 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 literal 0 HcmV?d00001 diff --git a/idoit_scaleup/__pycache__/__init__.cpython-312.pyc b/idoit_scaleup/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..964739fb1f3eaecc89ea61f156b80ee6e3d3574f GIT binary patch literal 4373 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 H76<$Xaq2y( literal 0 HcmV?d00001 diff --git a/idoit_scaleup/__pycache__/base.cpython-311.pyc b/idoit_scaleup/__pycache__/base.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d351706eadfb87fb1c6809c08e9cf36a03d56362 GIT binary patch literal 2854 zcmd58O-~zF@a=lPY!=K%%y$A!2;fTKv{lk5qK#-EAuWQ6q)}rjT5bFmFkRc5-E{)U zs2p-|Dh?cw!XXDvG%1lS`sVNIm7|h(tv>RGnF4!;&Va9y+_8ee?0= z&CHuO&)?V9h6upVe-F%b2!#BJL!=PMrEK#p^F|+0=C$R%Td-1B&KIv zo@%$VGeB{crcaxgSPRW{cfURtyOGheDb0$FsJ50q^FeGRlK?txTQNIx>DrB0l0v|W zl~8ppYs7AYTZ6`eD=A7c4SZ!Wa@ga+3Ok5v90RmWoTlbHw<0-_cKC$y@azJgT>tmz zaLyaiVLS}%hFJsBC~L~_@y7dMn6nzPyyik=CY{+7V&ojReCs>HUzXQk2FmNs> zD)j{ff!ZslU5DgDtud5MGD{q!pT*ju{3C9O&yhLQ^?(X(l_Bx*#RL8{yCnEz(t!-@ z28X~xkl480`!H861>d~T!8}{6=n^tljUdNwLRoB8OCu-bxTiInAm6I$Y#dtWz{VyF zx2oiln6^$4+dGqGW)Cr^8De7&_kqg(wT?(@-z9xpRPUNY>yAokILFiagPfEzY2&W$ z!_j;&BuSRUn{5P86jRe@FwrO9PHv zzcRKOFUs8;eUHz-2r$93FA1QMhXexR3+Urv)%RKVLmt)?_ZaYG8;wOJj< zvZCC{s(M*s)`1Zdd#M=%^n`eKt5;sA>&ptwZyfZv+UrR&f2tPH?0S|9d z>svsj0K+g2saf_OhlH2C$02P6|Lc(Mg8y|$f5HEjgh2*EdK0}s-phXxcn^coVH4eB3da8B&3Rn|zv&^Bkc*Dww{s z5Ep^RO^z7}iyGsMSu9bBQ5mZN$@W2zi^L!ZDb7=FnZ$imr2H}$7pO#iFpCR9 zRCd)`^pAAgNYvJ2G~|8oza4?0Nb+2sXuws7qJ#iFS|Cw=@^qAUeTkV_SER<}+$>0f zZJ09*57WkAk;seVu?v=&OB;4p^4a-^<(9g>&% z)26zX2J<3qMgW!jaj_%0TqUd9_BD%9SJtyh?Mq|9m4LxnNr9`D zV`kLn3RHJ=SI~3LoGZ^6IyIQR*n)jHGB$o{YHU0*K0Q4(?fO1rrYnD!i+09!**;}$Xd4J3J4IoXD*$!88!u1^d16+1mPoxHBgYZRMOE?oLie)zglV^xGmgO?kGQYmj*{S!ygm^<(|IpM{bVXI$P=)E=Yy> zlG3@wH~G6Cb@UaN)|Sf6ZAE2GS??}2_dg1E72|91Tk^f|U^(1Z?(Ey~i=9o+10>jb zo!*$)Y9Ni_odd+*_CV<^DZTfU0}qs;k}_0Qo4=j7KCv1vsgYYpZjbEvIRCLNiGcdV z*C%dMsti4X&Rkg$C}cM^F*HulAPTiWSbRPL$L) z*5^v74z0ewGF7vpI09Zzf*pn6pF3}p=Due{hIG`H&Cucd>Hs?a_weP8t{W3uK?wB1 z##{dRq&yZQzr;>Z-7Blc;OI~|qq>CEV9L(|-VhVNC*L|)ajD>>b)SXdJT zVc2k442CfbAIEVINy9@Dd`!9?lgMK- P_*@$1I-e03y!C$rmHrMk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d3c82b5f8f45dfe0bea6b7ccff2c6443da76c486 GIT binary patch literal 954 zcmah|&1(};5TCan>85QAw0<7cfF4}hG%A8nidxcAy;Lw#a4(N#_oc4vX5)LCYDx|{ z1Z)pNJxJ?GuhOG`h8K%?Sri0M-h@;sJ&5yOHc&)x*qt|DJ2StTx9&2Bsc>f@4w9;yu+^Biy{wWtv`pz=Lg5(}X5bJX9F6IYu>p1Blb+?6 zzSaXLw*gOyGg1RvPsdcsdCy})c*7(zmw~VBt3QkECE11q&jD>}9e9p7JEQ}TWZYl0 zo8=+UMj*>r#T2b364Hsnk83 ztE|LfD`KLufVdl%uT&Od5A{YODoI?etyBUZheCzthOE`Bcy2;n1!tz&=6TGre*6f= znPCgkBTgba=)z~KusO14o!*(;@n1);qEFUzA3gBzfm}onjzg|VQ3q#x1|?sUd^fYp z=`N=uO&&xpZ>Q#(8@3qkP!Af3t?@AoO%2~oTp_1bD)D2|a^8y@Ym6tf9g#O{kz{+e~waNF+j)OD$Zby69ZqF%|O`2Jv!Mht}Mn*&F;UsAjR@Y&B^d@+^_Mq)>(i1fP5pSBv;i`hByTh*@I19GKsH=bSm``+e8> zGn*YEF#h=OXzjL1$P=8b516N;a}HihgcDAE5>T5`lr>)qbXx~m=Z0?trfpK9kr~3x zMZz;eU(vhA?M!o^Xg#GcE7D~56k}86A7>aVU%dGJEQqrgDzKiNosPVC%85lSlFg@h z6jm)RMBR<*O}Bej7q#A`GYP`aKur*va$;*jw~afbN;zG$Y?Et?#LjRXaDW?t7E-eZ zc?NLk4!K3`9hXRPAQ?$@m+N)kbDh|WLU&yQDl*_fQ##)O{7RQd4Dar^tId;Nz)kaX zo?OT8HjS0uJ(M)Tno3bmOx36hnOKZ@p%*ijSm1&0W-k121vU*O{A!}R)mk#hnCm-A zF{V&=p|J3}T)G_jjX0)?^lh#NbZsDD*?2AV+ZJpc{pALdLA>37@l@UVM#(P9PYV>(xTR8H7QE zO0>T2WBR^FKLbS_B(gyM&W*^OAibNo%)=*gH^Lr(w+J8GRx8?g)lmX`e@z5VtNh`_ zH(jv{nT#TQ8u!E7a>tAjat>VHn)qGLJ9QMEAnDKTg{xR_~^dNvz&ck9%|Et1E%{R`(` z7}Pxae2AJmpIaM{q2`%?0nVmm(}NZGEo8u(Dpo12#DaF3{+cAlGcoX|Br_m(m8G2n tOEo3%+M4K^cutPP8ZuL8$TOW%x<(GJk^O6A?1eE(&))m#C4pC}`G3Uo{QLj_ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d3b54498daf85e1fbea18bc7264845290f6cd12d GIT binary patch literal 2405 zcmb_dL2MgE6rI_%olP2dlQ@tBA+&{*#3hMIld2S<0*cd+R!T%0Rl*V)ZFa`)hV|Oa zuA9cnAwrR84@kYBloQuPIdbWdI~Q9K0<9VeiBoS8DX8iJ@&B$}h)qP0I)}G^XZQd4 z@BNuSKO~b~1jcuNju%E^g#3yx%@J=HhnKp$ zN)$3jczl!agiv?Y$Uhff>qu*lDZHg=&^N=_%!f;JfQt(YvolMJ*I_m}pDo$`jOmL) zNv=IcN0?2N%hGmzD~c1L(s((%0>+n+mLNIGNlp=JE_RpXDW{uSF3y!rl1p$Eu!F|{ zHB2kl$rFHGyaVvmU2=!!x^IxQ7MzaOFS)L;{F1bqtKecF3qdM}?*e>Hw}=nVXxvlQ z$yXrLI$b9#_+QJ|ZN#Bu4frgi)gbOw%0dPjW43Gij0GBqF*~<-{S){Y^MsQRR4ZQy zIvKMZ)AJbfaBaibc=mqgX341(g_p^KPVU>2nQX~|ch~naere{$&5X@KMCMthBP!*L zW%|r&GMXr_$x{%hI&hTfL^BcG2Hwj6PdCUTEqSk_t_?m&J>cIKzbXExjUB=lazqxS z7Ru5G#uiAx=@3|4K?G6Ng`gJ**acT`2DyZl4dRfI=kJgmMJD0DAjVDK^e~oT$R3FmRA>quT>@eX^!a%3hd1 z!W7Rz$aEA8U`keSFSiU%K>?+?=Og}of2Z86Ly(Xn&uqR!gC0zmnNpf-5m$-wAJO96 ze=}+Zu3q}zVl-V*c5e_MRO2UoxULV^&R%*r{r%vcezm4ueSwnnSmv@Ln)}i&xq)JH z`6?{l&VKYb_N_6h7f;B&)e*NqJM)VV`ycvydbXxz+nGP_2E*VBz2Ny)IC=nf8}p5g zAcbT4z?N}aLWcoMo&kT@1u8VScIw9#zCF;13*YVPuHVE$G;HMYG}Po3Y> zr)t_%#2in3bD$k|0Tk??h3B+c>@R3qS*U(|rQ&{uCAOi98E!=ytHN~+;f1!aU13~{ z+Mz9sg1zjI8O4g{8AaPS=Cg)Z7F9cMS3~brA-QnjD(sxSV~H|GO{-DojF*;I=D8ez zY#LWYqk#^3=507(jgHIgJhN_@Zb9(sNXs!f?ngY1UOxa_1*ZoN6q=kkR24mQK$Ol* zs7yMES6JAVS;gAbL`5%B>}h^(_Lc)>Lef?%QXY7 z18!jjo)ULx25ddEdL^5wKd7Jz8ThFib+4j&Mb<##+LKkS4KEO3o3!Dv%KJ+;`gI7j z5W+OJFh$yAJhLbb8=<5$vk=p*sk`?dV$cvQs%N@ipU;LV^&?LRN`>lOsVwhaapsaJ zjhS#J5p-js>P#j+`VA=@nbc-6Tgl>TNk?+fqzqR1|vv>8ci;3r1Usifu{6Ee_xS0oekGH zTS!^MZ)K(oWyElJW<5$gX={8Rre{XrNlz%QDiyw8?VR$H#uLURt%ocM=wc`z(S{c@ zx>sxI{2rYD=8j@XaTFE8EFFG1yMAG_eD>|)X4&~vn)ql<@Dj#sOHCzjLbDM{<0MEU zh7%g4bRqSkUWBV@aP{1CQh6Dztk@rx&!UJ@oA4s;3Pr+=91bt2e5KUiAP%D1>A=_0 ziRIhd1~JEe6^L2QFOerr+h$fkP(q5lU1s4Z^H1Lbjl?1*f+Q7OAd0`w6(@2mb(l_v0)8 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b3f26e5cc2b23c7505c5368be9af58da73442d1 GIT binary patch literal 2572 zcmcImTWcFf6rRy)rIpt5x>j4eNgKPi8e6`}X-#f~q>bIUrojnKNLxV}7R}n-bam;> z%Bh3`E*NS<3-&{?@)SMB_$APv(HE^vz$^&_`qZ~#rzPZ}^vp`K4gBlL%8Gzzr6vh^W+mJvl1TPTf_7>l~k@};Gugpm)8BPz`y z>NliSsWv6)FUax0pBP$k?5d-LjDB)`oREpD(2PusWleijw~gs6E2L^OYUryswk|{E zCkSewB&I0oGo++{9!+72=ai&OeRC)o5J8hc>IYP)3>cyTz%UI0s#F1Nnnyq2WaJu( zhuokScH(NP5k$oD!%wVv>ny;pcp2I7t(65|3H<^ZlyC{%6m~C^)w&;y3P5r)F1a$# z=M3g5gqRuACd5^M^{3-kzW5pj{oJsoTq!j*?S=?RSvu#0a1pz+bN1Qk#Eq<#Pa8Zj z2D)U1Pb9{&DQIVGp0Kl{*KQ~JmOzW)R$}_r^H!iJUS28p9ijS!<*Y#7~*13?cvy(N8o{m&DGh;BDOqqs7 z$t~0VmSps_LF$f*Jq?0~7&d`7-Ul#?HdJjfv~Zz(%2B&3*VokeMzsB**19-S4lP}% zoN}W5>)Jq78DJWizdkJjddfC|TPsAl#G-)gEr4D0j~&FSHn({EF!nXOH-z6E^2);b za*v~SRmRrT-fi;A()mh{6YX2q`m0L+YbO6@o*RVQdWPzZl4(foTYBN8QVbj<#eSZO zBZmpI-z;$~_z-}XBh{AUj@olC=%~Z@V{7WkhEUb^At#!+A9SLp*R?ZM<;-5GLK3r)z`nvpMBu(P zr!xj6xh%6?+1rH!7XQIP8?nnraqzI6*kvws;yYV$#nEDwFCA^LstoQWB%WR`v%Z4L zGMYxWeH54Q3<%J;cYi$AsEwg-Af<0hCEv~M+Zm?SOG&ET;f)&_qo3RTsN}2VXP9O< zq1m)_3%!1El?b2%eJ<9_e5X5zHC60uiX^U?n9EmSV8!gf0@KU(^QTf)L za}Qd2|B|5lvDP{t_B4$*i^te|NQfIIB%P&s%YcWuiIDH}x>XxtLTM)c2YHVvYZJ92 z0^SqQCE&OKaka8x05?*9!Bd815faBN0=-K-bw3HjaCAQXH-e8B>p$N9fOY@? literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..294d60f4ebe4140c96d965090b7db4222f646c92 GIT binary patch literal 2455 zcmd5;&2Jk;6rb4-dmYDd^VyUn)cLMU8>c1Z096GfRR}7HL`o4BXk@(W;CAgbv+K0B zQjtR>5~)ZI2-Q82W1}4TGq}VJh-lSFNE}daB?+SH0rB3hH;vP@$_0s$_4dv8{C@Av zZ~RjzR7arv_|MsyKO{na$BI|vsE`l$rk}hSWxa9b< zE@ymkA0+}AC#r9OsD4dc6Wz7q{(=$>JfcvGQoC`KvC(&~jx%;;3ceFllh7EN7&Fq= zXu{HF3|@GI^Ptt2%kgy90#8vjEtfQMg`~TrYlUiMXA+#R;j|iwQF=p4C@;|`Ry>N=DZ$SR$TineU#a~ii5#?sle#h9%C zEtkiqKKukaB~#N=wwO%K*a60pdcrgrGjZ=oWd7-;*fm4XXEZZ52GnJT&&9@!B-FE( z8MBPh>1(mH3cNBinb5U-E(TDTUIjFiEASAkQ*3j%_izq*#|T27*#_pxJb9>umi!f^ zz1&<@f5_a+{Gy!MfgW-=DSglblwiD_G6!)u0iG(LU8}YO$Ks?d<_yyg&8E$KLe~o{ z#f=PvTZE{u3Oo$MZAnd72@_e1M0gXnbR>=Jti~;tN^82xZlQ-7_3!{)}ndI z&<0q`0SNfQO{I~ty!=-At4echBRssW4D)8_KOEpN^xTCAd$4YtB81bzA&_gqDmtW+ zl~*+zLT}3{-AGux9gndeUgSk*_GTA{j)u`2--tW(fSD(o!EmXr66`4R2f^;m=Hm~; zEv2jF)0Ni#jqt#_GVmXO4(LX9#zD=y;0*j#G|z$Vej#XYb}@ED)ZPTl-F>iwm0;(J z@F3W;2m7^3>%c~Ma9tUE3G8=a6_7zXMm~FmklVtXFh^%8_o;Zpg^SR8Cxo7x;Ft*} zz003<89{gZ6XEw7soeSI?C4a)MliwB$Yn?HniE;TAEqzTItzn?AvSn zGXP^kNdlb&{Q>SoPZOsJ zN7ac++z%fp(|IayIouE;+t18`54ZdSs5D<9pzyb(UXuAKk> zkrdA7fP-f=@zaRw02xC_MprEv2j(H|N26n4C4XPr2#>5QBMu$W5WZrL6f?Vyv5cYS zbq!v$dd9v4fp>d2<_Jgib>1Hi*NX56nqD+c)G=lp&uWLNuU=Bqb;hF94Kb$Q+irLR zI1~-$jEU0xWP$v4tb6{wU5UzPw|!JTx9t<bP`&V&fN{?5 zEHMesz7Lehv`puew&LX+$He76Ft&vML);8?_U^|Xd@P*vJXJ2burG1Efp}08b?>oA bDcvIdTcmf3oPgi1ua{mZeef57k2C#WU<*Hu literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..553b252604708a297f89063fdd67649e2e1b6eea GIT binary patch literal 960 zcmah|OKTKC5U!rbX7X4UH1Tmz7F2K~$wZG5g0czGLn4?Ea4yX-)4Pt$?5y3r>V`e! zuwZf!a*@Q7UL{9=h8Kf)X%qxc-h_=3auBO$cOeA9Vy3#fs-~*HuX;Wdiz9&Y{?Alx zjsSeun}OJUqk9vL9f%+z38cg)MB7GUq^55YFklfPvjdUE%ssRBjc>JVx9|fif=g7X zLTTmB>LR5}3rqJOJVaOV-h39zN+4M+{k%}Xw^R2Li+(X}jm^|`vYP`x5MAaU)hFnD;PmK^q6vKBexgOiVUsy`hLys zvU0+p5rna9DO<)eVK_v)nq+}=4L*w5%8CLR2u)bI%ui`gIn3%$8JDygvm~O=V)=;H zgOt%hsrAX@c>b3?hBd`GR0y*)zP-A8eShNW+uHub^rw>d(eZc*W3HoTlXszNgwiyN znhC=>jZylf86>?3*E8Yz>gT-j3A9wPKeCV|38gOKMf3_y!tERnFX()&^q(P4qdM%s zx6-AJy9XArr+ycRJ(C|IPg-_uRX|KaioBg>uy@_LzG->RJLu(Wx{jRL-eKqXMcnGX ZIoh8lA>=Du#^<2mkeThNUw}*g;a`Yu=Z*jX literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..faed1e5268e2c435cba05245d62afe0f0a8aed95 GIT binary patch literal 2507 zcmd5;-ER{|5Z}AKv(H~~hyx^$k|08gTY^h~wjlcHPzn#}M<^`^)#~t_Q-||M_s#@I zPMe2FwWU@Kq7{y!3il1fWB-Ic_9Y=jjZTh))R*#x2vrdeZD;p4+I?|CYm?$f#g1}_a6th^xAY)xJrL3e% zT$W8a>r;JP_L=^yqAFbWgB(x;0us?lq$@K>4;c71cK1+&rC>aCUw~0uaKcGKl5d?E zC1m8p85l=S4d*j-oJc!#IPsw^ip&9 za&wq+_j0+|9IU<#!dGxS1F3?JRMEhyR6%J$7iJVy*2Nj5`g9E3&u1l=Rk$RB6sVvp zf*Kq}ai0_BQF?oM{@FGFEMf;;JqGN1;X1P6>rz!Lqi+D{vQS3jOm7q2Zu${W3EdJ# z;@FX`V!@z}LP#c;u?cY$fa=7__dYxai;`uSX$Pm$V@`mOl&M)3Ar{-auW$OvYl*XY zvzRrk#4uo)d+A_eIG=)X&bAVEJ~?_ek9ba%_cb)$^(TGje?uWt*xg# z-~|zZC2Je7$J3~xM5_KprK=XJ={K@-*}KaARail(R!ICX!6EqsgzIPwO^IcIyc48o@B z>VNt}8bF7ADGq5tSY`>~3@ z5o)X0v+q>POQD{cb|=)^*wRz`cxlVtYO)b&pY5DYU+r0n?5az5BK_V{CE4iisg2$^ zKX-nqJ6@40=c>`hbA9z#{evI3&7X&f3yaE*#?D`Pn@a}bWP|v&^58wAx9BqT?pW2Wkp|PtPPkG zv9!wufnLU6k6W^P)8+id@l(2+6*O^{(l%niv@sS9VL2z zoof>oLohelvjK}uW_zC*je&acrm++~yr3NBXv9P8MdQtsI6*?PdA(>F@VbQw`Mju^ z?h0iULRpLCj}hI)B%W!OC&~hJBF%R%Wtb)*aluV1E4eq_l!llpW9*(<1Hc~7pnK7s z)9*f#1bOhGPm*H~!-~A`Q74uU{T0OWkp~DEkCPjYK(sz+>6UHrh#bYMF3L)j`hYnS z`$NKO(FtS<&4#nyAh@Gj$aTtImnX_`_t39X)~Hy()*fKc+y!9;^{t>iE2x|QcCVn_ Rk9@ttk=fV&MDXEG{smHSN8&8C)i{6 z&KQE_twNdghEk6uLjHjRw~3X`{w3(F5tXQ1nq;^bm*Hc4Mu-U< z&hcqaMvRHj7gSH$n`wwOaD*q9i7MVFs+Wq71cy&dT5JeQUve-C3)aa=MVY*K>$0Lm zr*FM`?Z%99_1b0S`n4OkEMIl$%6042>cHKHd%~O*xH6U1%*nV(Q(3n7CEfwEl1gbV zk67p(MtsB*s%A=2<35PUY@PF;V63lbBZ(fHtTepc2XuYgD~J zo74uNK2-whR~vx_?2}D!vf1wWpch;wcez;0O%nE7ZT1VJ)Lh82xvSZfmWZdXXPKFB zpM$URz&`@Ce-6lRxiwt3 zW-5v$0mRdnuif|&EP4!@PFg}DnX(!cC6SIBhN2jFw=6Gxbt&>rHl5ESte% zOl1==)=eW~W+!jH6VX(_TQNWgn$JZNaZ}M9(Uf#HrQ|@c;oKr?27U#(j^xAc!_erA zZvlC}L_U`SD-C6-eLb|Uev)~Zc_Iz&!wNYXrXE-UkkE?kRs`Lhgr3SHeN>qT{Dr9H zjVDZPo?7C3Je{Xt9fGEtTRdxlot8(9n{fjnlw}sena)I3pQp@Jk{V5`%7SLTujug% zRU9^j@70s)<>vyx+m;nX4W3vXTbCYO*yt;V`nQ7vC24@Q!urv&Mq%YBSzRere(nq* zj9VmT#mCTC4&tE-hfwiEtwsQWvSd6#Sp$mAYigLcgf!JzJB%5gcmbELdh%5n72Z59 zG}WW13*&VlfR3+u*<76|2jy*F*T(E;zSBFQHU~0aIn=W;TMqSYddi`p?ci`p8h#0I z{lIUHfY(&b1STrY1;_;vOoazf;;PE1VNhd>v;3~WY0WT?fk&0c@$sl0eHET*pdOaU zPB8ShpbT(qv~J9myGFKyqa|tdC1MZ4F%Y|A0Vz_ojw%c)U*z1d@e2-p?_>tR#)qVud-)4!7VqbxlO&07Kr*TTU!F;Q&Nc z1w_?p!pt(u%V<&o@8PT#lwXN67GHFtM=eDC^&qNcT=?N}F{iGr_}rA zAIBcIZwKElNpII;gDJ+w=J@U-Kq_F}aK2XVj(T##k;3S41%*SXd7}-;iylIM-?1GW zDoI0*VAX(F5y}<}@i-VzRnCn9;0%1UFnbA1A1z#iG$8AFCNEH3m#Ja*sisqT#wj>7 zDyOvf%t1MmHw-zWnUL~iBS#BbQY+Y}7bsI9q@taL1t;Z*+4t^TQQ5a)s*-mrnWx=| z&r=0bs$I({T2e{8AJE)+ zE~)nhRV)f9>w$aQEBAWv-bMQ_?2WQhFe;qtRh63h^-A>C^o>gwC#TtWVUHzZ--IB{uU9|+rK4DMAb`n^xSOPP3OQ30Lpts7|83=ffzioZ|lXDNxeSE&`@7tuG&Xk8IO2b!ox+Wfv|EZ%qdAr?H zO8)LzhevE|tNsae?4r7w0gJq=JJ2yX%Fe#FE{>SU$!;qiD8`|bgJN88Gw4mi{ICcr zf=?&HE+D~b&kDTYom--1Plwk^;Gx**EjGuM(XF_ z=`HQ+tsgZni#wA4H~tm>>g~Tu9iInVR~8`ZZ`|IT-VTnGq>=wWpn~}vnDy78k6lEm z4uLX>p!C%P#*x@oF@Zgl!wOVBy%IE?R+KR3m;svSgku6txD^S8 zW@8%2^ZVp!@XXR%&pezswCCl-!9A}aM)sOGacs})5y$uZoH)bb%5Y^RAa*_L6vWYg zHwohSE&*ce-sT$(H(Fju%7$q;;-HN)#N3kH6vXgy%Yz@%j&0+Ewur+7KbF3d&N~QQ oU{_&??>8{yfs1n7-^py5%zi<-zaS@{dCzg<>zRKM__2@w7g1IXxBvhE literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..24a73392116b02f5de156977af218f23eca5c08f GIT binary patch literal 4244 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 F`yYb{pxgie literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d0697f35034b1bcf769ab01237577bc15f6fc9d8 GIT binary patch literal 1243 zcmb_c&1(}u6rb76rrVDg=!aE`5U2{#CR;yFB4SBO?4^aK6x_?O>`u~^O*YO>swp|- z5U@Q6y-4H9UQ3VO{1?156fcVfK`-9KR4E?BHiZu!9`rU=NkBiLsCsPbsOUijjhH$WfcfiBNT4?cOmXH7yza z2p&-qhm#pXGIJ|ALh>t1%X7JTlAT{&$jpN(wvhE*p0PMB`mAR6BFaVSZ8~WXowBPfCQ4P>7Ce=_2sujvYoe;O|aza8K;Lhik zmhXcy5>T%Ys&=6m4iaK}RuB*ph}ell<6G8P^}T9|21XW&P@cVJWPKa9$#sOuQE|os%Yd7r)%NcdRH{>a&iu z*{@NorDZzOaxtleQRrd70~uHhPWJ_1;(wDid#GzKrsSJ`yPNhMHVs=5Tn752VvLW_ T)gv^09395#)`c$!7J2apg)=sx literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fce61790306309f508411fd83093be63ae2cfc1f GIT binary patch literal 1362 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3816cb69fa94280abe58844db47a99a827b94619 GIT binary patch literal 1700 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 literal 0 HcmV?d00001 diff --git a/idoit_scaleup/__pycache__/object.cpython-311.pyc b/idoit_scaleup/__pycache__/object.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cd6f01a40c5a780643197c37c1b6fc0efc50f61 GIT binary patch literal 4912 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 literal 0 HcmV?d00001 diff --git a/idoit_scaleup/__pycache__/search.cpython-312.pyc b/idoit_scaleup/__pycache__/search.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ef560915ccb90ea4f199a72f66d74565d500c7a GIT binary patch literal 700 zcmZ8fL2DCH5T5tm?k1#Zq7kYEYrQViuJ&RMBB%lJl1re6y*$=^uNv9iY~R~JQxfoy zL%rozk6xu0!Jpwp@t`ae1W&yMQZGG-^LA4a2i`X`?0hrdF#BnBwFYP(f8TkrLI8f6 z;#MGw*C_DvWO+na_p0JYRvytOg&P?Kx+(}c_PqH%{ zVAA@t&Y+(?O8bukEyI#fB3&kn8yFme1fK}-sUVW=Q9-AcZ;hC1zCgL#<&@k} zQV*ldtZ@)WT%{oogD65#mX^_;sv;E^4pb29oT)6)M$2uqi?+8rh=#G$UMI+8a`&Fs z8HAW8nf9{5lisdZOyU|F%3P1y5MINNdULimXS7v2a^?;+ zuYGH5erasJYkY9NHtrv>@6Fb%{busHdUJvG&~@#+%6SsRlJmUAc{~uq$i#Kd_lH5W z{K9mmit+D3T}7U{V!+HT!2BY*fiQ)+W!oEXd*^_zEHzbK*HY#Xx%C{6txSE%UNh0( mju+JjrGSUZoZcKsUq_fTLdYqspTezkr$RPnTfYEZLHq-x-l8x7 literal 0 HcmV?d00001 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']