328 Commits

Author SHA1 Message Date
becbe65993 10.0.0 2025-11-14 12:44:02 +03:00
06336697a6 9.0.0 2025-07-24 16:59:28 +03:00
4113719334 8.0.0 2025-05-07 14:08:17 +03:00
f8c32d609b 7.2.0 2025-03-03 17:44:25 +03:00
e537eadda6 7.1.0 2025-02-07 13:04:30 +03:00
5f3df12742 7.0.0 2024-12-26 12:37:38 +03:00
6b102946de 6.1.2 2024-12-23 16:59:51 +03:00
45355b3dd3 6.1.1 2024-12-18 14:08:14 +03:00
54c306b13b 6.1.0 2024-11-29 13:48:11 +03:00
dd2fca15f3 6.0.0 2024-11-13 11:51:38 +03:00
aa3f84095f 5.6.0 2024-09-03 09:20:19 +03:00
ba305a0ccb 5.5.0 2024-08-29 16:58:25 +03:00
Dmitriy Smirnov
7d60e5f97b 5.4.1 2024-08-23 15:16:14 +03:00
bbc352715d Add docs for the modules version 5.4.0 2024-07-24 14:46:43 +03:00
c34f02f7bd Merge branch 'BANS-481' into 'dev_5.4.0'
Implement functionality of the API method `/cloudapi/account/audits` in the...

See merge request rudecs/dev/decort-ansible!107
2024-07-24 11:31:10 +00:00
3c6ce85dba Implement functionality of the API method /cloudapi/account/audits in the module decort_account_info 2024-07-24 14:23:35 +03:00
fe1e8a32f9 Merge branch 'BANS-480' into 'dev_5.4.0'
Implement functionality of the API method `/cloudapi/account/listFlipGroups`

See merge request rudecs/dev/decort-ansible!106
2024-07-24 08:38:40 +00:00
0ca3399026 Implement functionality of the API method /cloudapi/account/listFlipGroups 2024-07-23 17:53:45 +03:00
ce54341a64 Merge branch 'BANS-479' into 'dev_5.4.0'
Implement functionality of the API method `cloudapi/account/listTemplates`

See merge request rudecs/dev/decort-ansible!105
2024-07-23 11:13:58 +00:00
7422464109 Implement functionality of the API method cloudapi/account/listTemplates 2024-07-23 10:46:58 +03:00
0c0fde8470 Add the argument disk_type validation in the method DecortController.account_disks 2024-07-22 16:32:49 +03:00
aefc920e1a Merge branch 'BANS-478' into 'dev_5.4.0'
Implement functionality of the API method `cloudapi/account/listDisks`

See merge request rudecs/dev/decort-ansible!104
2024-07-22 10:49:19 +00:00
b73a57dd0d Implement functionality of the API method cloudapi/account/listDisks 2024-07-22 13:35:59 +03:00
cd663a4a01 Improve style of version compliance in README.md 2024-07-22 11:42:27 +03:00
0cdfa6a0ec Add annotation for result of the method DecortController.decort_api_call and improve its docstring code style 2024-07-21 11:31:47 +03:00
53938d9d94 Improve code style and docstring for the method DecortController.account_find 2024-07-21 11:02:57 +03:00
8e4ce18d8a Improve naming of some methods and variable in library/decort_account_info.py and module_utils/decort_utils.py 2024-07-21 10:11:57 +03:00
ced031bba8 Add docstring for the method DecortAccountInfo.mapped_vinses_params 2024-07-19 18:19:35 +03:00
25795e9fe9 Add docstring for the method DecortAccountInfo.mapped_rg_params 2024-07-19 18:18:45 +03:00
a46ed24168 Add docstring for the method DecortAccountInfo.mapped_computes_params 2024-07-19 18:16:53 +03:00
53ba9a4f02 Add brief docstring for the method DecortController.account_vinses 2024-07-19 16:50:50 +03:00
682f19c4ce Add brief docstring for the method DecortController.account_computes 2024-07-19 16:50:06 +03:00
3516843c41 Add brief docstring for the method DecortController.account_resource_groups 2024-07-19 16:48:50 +03:00
94586345a1 Add brief docstring for the method DecortController.account_resource_consumption 2024-07-19 16:47:10 +03:00
c1dfaccb61 Merge branch 'BANS-477' into 'dev_5.4.0'
Implement functionality of the API method `cloudapi/account/listVins` in...

See merge request rudecs/dev/decort-ansible!103
2024-07-19 13:08:11 +00:00
d99af4498a Implement functionality of the API method cloudapi/account/listVins in decort_account_info module 2024-07-19 15:59:05 +03:00
a0805d45b3 Update docstring and args type hints for the method DecortController.account_find 2024-07-19 13:09:43 +03:00
3b2be18346 Add decorator for adding the name of called method to the string self.result['waypoints']. 2024-07-19 11:42:23 +03:00
f230325968 Remove erroneously added module decort_account_info parameters 2024-07-18 17:55:30 +03:00
1d56940e7e Merge branch 'BANS-476' into 'dev_5.4.0'
Implement functionality of `cloudapi/account/listComputes` API method in...

See merge request rudecs/dev/decort-ansible!102
2024-07-18 13:40:41 +00:00
10dba22834 Implement functionality of cloudapi/account/listComputes API method in decort_account_info module 2024-07-18 16:30:58 +03:00
b6bbc31961 Merge branch 'BANS-475' into 'dev_5.4.0'
Implement functionality of `/cloudapi/account/listRG` API method in `decort_account_info` module

See merge request rudecs/dev/decort-ansible!101
2024-07-17 12:36:08 +00:00
412bd704f1 Implement functionality of /cloudapi/account/listRG API method in decort_account_info module 2024-07-17 15:33:32 +03:00
77a2b6a182 Refactor DecortAccountInfo class (MODULE_ARGS attribute -> module_args property) 2024-07-16 15:49:15 +03:00
f00055e009 Refactor DecortController.account_find method 2024-07-16 15:11:33 +03:00
37de8afbc1 (2)Remove required=False optional setting from decort_account_info module argument specification 2024-07-16 11:28:07 +03:00
53e30105b1 Remove required=False optional setting from decort_account_info module argument specification 2024-07-16 11:19:08 +03:00
734408ab10 Merge branch 'BANS-472' into 'dev_5.4.0'
Implement functionality of `/cloudapi/account/getResourceConsumption` API method

See merge request rudecs/dev/decort-ansible!100
2024-07-15 15:53:30 +00:00
0fcde3f4bd Merge branch 'BANS-474' into 'BANS-472'
Update changelog after implementation functionality of /cloudapi/account/getResourceConsumption

See merge request rudecs/dev/decort-ansible!99
2024-07-15 15:30:50 +00:00
32324ee184 Update changelog after implementation functionality of /cloudapi/account/getResourceConsumption 2024-07-15 18:28:32 +03:00
5161649fe9 Implement functionality of /cloudapi/account/getResourceConsumption API method 2024-07-15 18:19:08 +03:00
ce4ac4630c Merge branch 'BANS-462' into 'dev_5.4.0'
Implement `cloudapi/account/get` API

See merge request rudecs/dev/decort-ansible!98
2024-07-12 15:52:45 +00:00
8ee5bcce52 Update Ansible modules version for 4.0.0 version platform in README.md 2024-07-12 18:47:18 +03:00
1d6141117d Merge branch 'BANS-469' into 'BANS-462'
Update changelog for 5.4.0 version

See merge request rudecs/dev/decort-ansible!97
2024-07-12 15:45:02 +00:00
5d6a278b8f Update changelog for 5.4.0 version 2024-07-12 18:42:50 +03:00
cf4d43d23e Merge branch 'BANS-471' into 'BANS-462'
Add `decort_account_info` module with implementation of `cloudapi/account/get` functionality

See merge request rudecs/dev/decort-ansible!96
2024-07-12 11:49:59 +00:00
11168827e6 Add decort_account_info module with implementation of cloudapi/account/get functionality 2024-07-12 14:46:34 +03:00
2507a65d89 Merge branch 'BANS-467' into 'BANS-462'
Improve `DecortController.account_find` method

See merge request rudecs/dev/decort-ansible!95
2024-07-12 11:08:02 +00:00
22f487b626 Improve DecortController.account_find method 2024-07-12 14:06:25 +03:00
89b03213df Merge branch 'BANS-468' into 'BANS-462'
Add `not_fail_codes` parameter to `DecortController.decort_api_call` method

See merge request rudecs/dev/decort-ansible!94
2024-07-12 10:55:26 +00:00
96b163ba00 Add not_fail_codes parameter to DecortController.decort_api_call method 2024-07-12 13:52:50 +03:00
21470542ea Merge branch 'BANS-466' into 'BANS-462'
Disable dependency on `password`, `user`, `workflow_callback` and...

See merge request rudecs/dev/decort-ansible!92
2024-07-12 10:21:34 +00:00
7eed30d2ab Disable dependency on password, user, workflow_callback and workflow_context module parameters 2024-07-12 13:16:12 +03:00
efa60a5caf Add wiki directory with documentation files 2024-07-09 15:30:44 +03:00
b7b02fdb85 Merge branch 'BANS-463' into 'dev_5.3.0'
Delete decort-ansible.tar file

See merge request rudecs/dev/decort-ansible!90
2024-07-09 12:28:08 +00:00
c5f2e143ba Delete decort-ansible.tar file 2024-07-09 15:27:10 +03:00
68f4bcbcc6 Merge branch 'BANS-460' into 'dev_5.3.0'
Update README.md file

See merge request rudecs/dev/decort-ansible!89
2024-07-09 12:25:12 +00:00
a94a5a2e62 Update README.md file 2024-07-09 15:24:16 +03:00
3b84a5f633 Fix BANS-434 task description in CHANGELOG.md file 2024-07-08 16:58:09 +03:00
2d04cad3d4 Merge branch 'BANS-459' into 'dev_5.3.0'
Add CHANGELOG.md file for 5.3.0 version

See merge request rudecs/dev/decort-ansible!88
2024-07-08 13:47:34 +00:00
f12e6fc941 Merge branch 'rc-5.2.6' into 'dev_5.3.0'
Update dev_5.3.0 branch

See merge request rudecs/dev/decort-ansible!87
2024-07-08 13:45:55 +00:00
02e55e77f4 Add CHANGELOG.md file for 5.3.0 version 2024-07-08 16:28:17 +03:00
dac66fac77 Merge branch 'BANS-457' into 'rc-5.2.6'
Fix access to lb_facts attribute in decort_lb class (in two places)

See merge request rudecs/dev/decort-ansible!86
2024-07-04 14:57:51 +00:00
3ce022a800 Fix access to lb_facts attribute in decort_lb class (in two places) 2024-07-04 17:54:26 +03:00
59000feb00 Merge branch 'BANS-451' into 'rc-5.2.6'
Fix logic of deleting all port forwarding rules for compute in...

See merge request rudecs/dev/decort-ansible!85
2024-07-02 12:10:43 +00:00
614c7d98d9 Fix logic of deleting all port forwarding rules for compute in DecortController.pfw_configure method 2024-07-02 14:52:23 +03:00
3428b74b00 Merge branch 'BANS-445' into 'rc-5.2.6'
Fix ci_user_data param type in decort_kvmvm module

See merge request rudecs/dev/decort-ansible!84
2024-06-26 10:39:37 +00:00
36930bda0d Fix ci_user_data param type in decort_kvmvm module 2024-06-26 13:35:11 +03:00
Алексей Даньков
eb91d5200f Merge branch 'dev_bsgroup_rc-5.2.6' into 'rc-5.2.6'
decort_group module bug fixes

See merge request rudecs/dev/decort-ansible!80
2024-06-21 06:59:17 +00:00
d7711e58ca Merge branch 'BANS-434' into 'rc-5.2.6'
Fix bs groups info keys in decort_bservice.package_facts method

See merge request rudecs/dev/decort-ansible!83
2024-06-19 08:01:40 +00:00
d287c88293 Fix bs groups info keys in decort_bservice.package_facts method 2024-06-19 10:35:36 +03:00
309d5b91eb Merge branch 'BANS-425' into 'rc-5.2.6'
Fix check mode logic for RG creating in decort_rg module

See merge request rudecs/dev/decort-ansible!82
2024-06-17 13:54:58 +00:00
db854acc11 Fix check mode logic for RG creating in decort_rg module 2024-06-17 12:31:04 +03:00
7b682f0340 Merge branch 'BANS-419' into 'dev_bsgroup_rc-5.2.6'
Fix ViNS list comparison in DecortController.group_update_net method

See merge request rudecs/dev/decort-ansible!79
2024-06-14 12:30:14 +00:00
b51136b711 Fix ViNS list comparison in DecortController.group_update_net method 2024-06-14 15:26:23 +03:00
Алексей Даньков
daa91bee95 Merge branch 'dev_bs_rc-5.2.6' into 'rc-5.2.6'
decort_bservice module bug fixes

See merge request rudecs/dev/decort-ansible!72
2024-06-14 07:44:13 +00:00
Алексей Даньков
5227e2be0b Merge branch 'dev_rc-5.2.6' into 'rc-5.2.6'
decort_lb module bug fixes

See merge request rudecs/dev/decort-ansible!67
2024-06-14 07:43:03 +00:00
9ccf2de256 Merge branch 'BANS-399' into 'dev_bsgroup_rc-5.2.6'
Fix logic of decort_group.destroy method

See merge request rudecs/dev/decort-ansible!78
2024-06-11 14:32:05 +00:00
825ce068c8 Fix logic of decort_group.destroy method 2024-06-11 17:30:23 +03:00
efb51ab8b4 Merge branch 'BANS-396' into 'dev_bsgroup_rc-5.2.6'
Add sub-elements specification for 'networks' parameter of decort_group module

See merge request rudecs/dev/decort-ansible!77
2024-06-11 13:21:37 +00:00
190a1d302c Add sub-elements specification for 'networks' parameter of decort_group module 2024-06-11 16:16:35 +03:00
840e4bec21 Merge branch 'BANS-395' into 'dev_bsgroup_rc-5.2.6'
Add default value '' for 'role' parameter in decort_group module.

See merge request rudecs/dev/decort-ansible!76
2024-06-11 11:32:56 +00:00
f22be4fe08 Add default value '' for 'role' parameter in decort_group module. 2024-06-11 14:16:23 +03:00
4b1a7d9d9e Merge branch 'BANS-392' into 'dev_bsgroup_rc-5.2.6'
Update DecortController.group_find method logic for >=3.8.6 Dynamix version.

See merge request rudecs/dev/decort-ansible!75
2024-06-11 09:51:04 +00:00
3a2d9904cf Update DecortController.group_find method logic for >=3.8.6 Dynamix version. 2024-06-11 12:46:57 +03:00
9393bb76cc Merge branch 'BANS-391' into 'dev_bsgroup_rc-5.2.6'
Fix adding networks logic in DecortController.group_provision method

See merge request rudecs/dev/decort-ansible!74
2024-06-10 14:04:40 +00:00
27e7c2749f Fix adding networks logic in DecortController.group_provision method 2024-06-10 17:01:41 +03:00
e578742bb2 Merge branch 'BANS-390' into 'dev_bsgroup_rc-5.2.6'
Fix return value for DecortController.group_provision method.

See merge request rudecs/dev/decort-ansible!73
2024-06-10 13:04:14 +00:00
7998046cfb Fix return value for DecortController.group_provision method. 2024-06-10 16:02:19 +03:00
06b4686e18 Merge branch 'BANS-369' into 'dev_bs_rc-5.2.6'
Bans 369

See merge request rudecs/dev/decort-ansible!71
2024-06-07 11:57:33 +00:00
cb13649586 Fix executing logic of DecortController.bservice_state method call in decort_bservice.create method 2024-06-07 14:54:30 +03:00
7e372511bc Add check mode simple logic in DecortController.bservice_provision method 2024-06-07 14:53:28 +03:00
bb8e9ad6f7 Merge branch 'BANS-368' into 'dev_bs_rc-5.2.6'
Add existing check for 'groupsName' key of self.bservice_info dict in...

See merge request rudecs/dev/decort-ansible!70
2024-06-07 11:16:32 +00:00
3fec6f014b Add existing check for 'groupsName' key of self.bservice_info dict in decort_bservice.package_facts method 2024-06-07 14:12:45 +03:00
4a08cd86f6 Merge branch 'BANS-367' into 'dev_bs_rc-5.2.6'
Add rg_name param value to call of DecortController.rg_find method in...

See merge request rudecs/dev/decort-ansible!69
2024-06-07 09:58:05 +00:00
3dc9cbcbd8 Add rg_name param value to call of DecortController.rg_find method in decort_bservice.__init__ method 2024-06-07 12:56:04 +03:00
0008372e6b Merge branch 'BANS-366' into 'dev_bs_rc-5.2.6'
Fix variable name for if-condition in decort_bservice.nop method

See merge request rudecs/dev/decort-ansible!68
2024-06-07 09:41:12 +00:00
5c3194b94d Fix variable name for if-condition in decort_bservice.nop method 2024-06-07 12:39:29 +03:00
20b9228351 Merge branch 'BANS-357' into 'dev_rc-5.2.6'
Add check mode simple logic to DecortController.lb_update method

See merge request rudecs/dev/decort-ansible!66
2024-06-06 13:31:30 +00:00
9449afa2ac Add check mode simple logic to DecortController.lb_update method 2024-06-06 16:29:22 +03:00
b2477d2035 Merge branch 'BANS-354' into 'dev_rc-5.2.6'
Fix API param values for servers adding in DecortController._lb_update_backends method

See merge request rudecs/dev/decort-ansible!65
2024-06-06 12:37:27 +00:00
ea63959289 Fix API param values for servers adding in DecortController._lb_update_backends method 2024-06-06 15:34:01 +03:00
b3b47c57a1 Merge branch 'BANS-353' into 'dev_rc-5.2.6'
Fix location of lb_front_list formation code in DecortController.lb_update method

See merge request rudecs/dev/decort-ansible!64
2024-06-06 12:00:53 +00:00
bb6394873b Fix location of lb_front_list formation code in DecortController.lb_update method 2024-06-06 14:54:16 +03:00
ff1c43e8de Merge branch 'BANS-352' into 'dev_rc-5.2.6'
Fix deleting element of frontends list in DecortController._lb_delete_fronts method

See merge request rudecs/dev/decort-ansible!63
2024-06-06 11:51:38 +00:00
6b4957f8aa Fix deleting element of frontends list in DecortController._lb_delete_fronts method 2024-06-06 14:46:50 +03:00
d824e599b9 Merge branch 'BANS-346' into 'dev_rc-5.2.6'
Fix logic of LB state changing

See merge request rudecs/dev/decort-ansible!62
2024-06-05 14:41:36 +00:00
b1f2167d00 Fix logic of LB state changing 2024-06-05 17:39:27 +03:00
aad2f89e6d Merge branch 'BANS-341' into 'dev_rc-5.2.6'
Add LB starting logic in LB restoring logic and fix DecortController.lb_state...

See merge request rudecs/dev/decort-ansible!61
2024-06-05 10:25:28 +00:00
4311eee435 Add LB starting logic in LB restoring logic and fix DecortController.lb_state method VALID_TARGET_STATES list 2024-06-05 13:22:05 +03:00
3bdac96760 Merge branch 'BANS-340' into 'dev_rc-5.2.6'
Add lb_facts updating logic after DecortController.lb_restore method call in...

See merge request rudecs/dev/decort-ansible!60
2024-06-05 08:08:58 +00:00
a6a6954d46 Add lb_facts updating logic after DecortController.lb_restore method call in decort_lb.action method 2024-06-05 11:03:33 +03:00
6d003e4541 Merge branch 'BANS-339' into 'dev_rc-5.2.6'
Fix lb_dict argument of DecortController.lb_state method call in decort_lb.action method

See merge request rudecs/dev/decort-ansible!59
2024-06-05 07:42:06 +00:00
0ae16ddc1d Fix lb_dict argument of DecortController.lb_state method call in decort_lb.action method 2024-06-05 10:39:34 +03:00
c5f68fea38 Merge branch 'BANS-338' into 'dev_rc-5.2.6'
Fix argument name of DecortController.lb_restore method call in decort_lb.action method

See merge request rudecs/dev/decort-ansible!58
2024-06-05 07:32:10 +00:00
23ad78b1cf Fix argument name of DecortController.lb_restore method call in decort_lb.action method 2024-06-05 10:30:03 +03:00
93a929aff5 Merge branch 'BANS-334' into 'dev_rc-5.2.6'
Fix logic of LB deleting from Recycle Bin and LB list getting

See merge request rudecs/dev/decort-ansible!57
2024-06-04 13:33:44 +00:00
90ae212d0c Fix logic of LB deleting from Recycle Bin and LB list getting 2024-06-04 16:30:00 +03:00
421d19bfa8 Merge branch 'BANS-331' into 'dev_rc-5.2.6'
Fix logic of DecortController.lb_update method running in decort_lb.create method

See merge request rudecs/dev/decort-ansible!56
2024-06-04 08:51:04 +00:00
41731c3dd7 Fix logic of DecortController.lb_update method running in decort_lb.create method 2024-06-04 11:49:14 +03:00
034aeca3f0 Merge branch 'BANS-324' into 'dev_rc-5.2.6'
Fix logic of LB frontends updating in DecortController._lb_update_fronts method

See merge request rudecs/dev/decort-ansible!55
2024-06-03 16:16:07 +00:00
db67a3b2d2 Fix logic of LB frontends updating in DecortController._lb_update_fronts method 2024-06-03 19:14:02 +03:00
af6eff33f7 Merge branch 'BANS-326' into 'dev_rc-5.2.6'
Fix logic of frontends creating in DecortController._lb_add_fronts method

See merge request rudecs/dev/decort-ansible!54
2024-06-03 15:14:13 +00:00
ca45f49c2e Fix logic of frontends creating in DecortController._lb_add_fronts method 2024-06-03 18:11:40 +03:00
88d9ddcdbe Merge branch 'BANS-325' into 'dev_rc-5.2.6'
Fix API request parameter name for LB description in DecortController.lb_provision method

See merge request rudecs/dev/decort-ansible!53
2024-06-03 13:14:43 +00:00
876ff5b98d Fix API request parameter name for LB description in DecortController.lb_provision method 2024-06-03 16:12:05 +03:00
c46edd4f86 Merge branch 'BANS-323' into 'dev_rc-5.2.6'
Add logic for processing vins_name parameter

See merge request rudecs/dev/decort-ansible!52
2024-06-03 12:13:07 +00:00
9ce5a3d711 Add logic for processing vins_name parameter 2024-06-03 15:09:35 +03:00
38757aa902 Merge branch 'BANS-322' into 'dev_rc-5.2.6'
Fix access to acc_id class attrubute in decort_lb.__init__ method

See merge request rudecs/dev/decort-ansible!51
2024-06-02 19:31:23 +00:00
56f7f354c1 Fix access to acc_id class attrubute in decort_lb.__init__ method 2024-06-02 22:27:52 +03:00
2b09b9449a Merge branch 'BANS-321' into 'dev_rc-5.2.6'
Fix if-condition for rg_name parameter check

See merge request rudecs/dev/decort-ansible!50
2024-06-02 19:20:29 +00:00
eb0766b15f Fix if-condition for rg_name parameter check 2024-06-02 22:18:58 +03:00
d7a32376db Merge branch 'dev_rc-5.2.6' into 'rc-5.2.6'
osimage module bugs fix

See merge request rudecs/dev/decort-ansible!49
2024-05-30 13:50:41 +00:00
8a9c354f20 Merge branch 'BANS-304' into 'dev_rc-5.2.6'
Fix if-condition for executing logic of virtual image link changing

See merge request rudecs/dev/decort-ansible!48
2024-05-30 13:27:57 +00:00
1e994410fe Fix if-condition for executing logic of virtual image link changing 2024-05-30 16:24:28 +03:00
e2bbeb7ffb Merge branch 'BANS-303' into 'dev_rc-5.2.6'
Add logic for virtual image renaming in decort_osimage class

See merge request rudecs/dev/decort-ansible!47
2024-05-30 13:08:15 +00:00
2777059b6b Add logic for virtual image renaming in decort_osimage class 2024-05-30 16:06:25 +03:00
38f11ee480 Merge branch 'BANS-300' into 'dev_rc-5.2.6'
Fix access to API response image list in DecortController.virt_image_find method

See merge request rudecs/dev/decort-ansible!46
2024-05-30 11:44:21 +00:00
dc24c0e7ee Merge branch 'BANS-296' into 'dev_rc-5.2.6'
Exclude failed setting in case when image not found in DecortController.image_find method

See merge request rudecs/dev/decort-ansible!41
2024-05-30 11:36:30 +00:00
9a5fd176f0 Merge branch 'BANS-297' into 'dev_rc-5.2.6'
Fix account_Id parameter value for call of decort_osimage.decort_image_create method

See merge request rudecs/dev/decort-ansible!42
2024-05-30 11:35:47 +00:00
279d60083d Merge branch 'BANS-299' into 'dev_rc-5.2.6'
Fix image renaming logic in decort_osimage.__init__ method

See merge request rudecs/dev/decort-ansible!43
2024-05-30 11:35:14 +00:00
1d53fd3213 Merge branch 'BANS-301' into 'dev_rc-5.2.6'
Exclude 'failed' setting in case when image not found in DecortController.virt_image_find method

See merge request rudecs/dev/decort-ansible!44
2024-05-30 11:34:33 +00:00
a0da034499 Merge branch 'BANS-302' into 'dev_rc-5.2.6'
Fix if-condition logic in main function in decort_osimage.py

See merge request rudecs/dev/decort-ansible!45
2024-05-30 11:34:00 +00:00
e5504b3ac9 Add if-condition logic correction in main function in decort_osimage.py 2024-05-30 14:30:10 +03:00
2eb43815e7 Fix if-condition logic in main function in decort_osimage.py 2024-05-29 19:02:27 +03:00
2f9716a51d Merge branch 'BANS-295' into 'rc-5.2.6'
Add validated_image_id check to if-condition of deleting image logic

See merge request rudecs/dev/decort-ansible!40
2024-05-29 15:50:14 +00:00
aa96af1455 Exclude 'failed' setting in case when image not found in DecortController.virt_image_find method 2024-05-29 18:11:05 +03:00
a39ab95c1f Fix access to API response image list in DecortController.virt_image_find method 2024-05-29 14:55:47 +03:00
76a1ff1788 Fix image renaming logic in decort_osimage.__init__ method 2024-05-29 13:18:12 +03:00
9dc3a5e780 Fix account_Id parameter value for call of decort_osimage.decort_image_create method 2024-05-28 17:31:44 +03:00
ff32b509f8 Exclude failed setting in case when image not found in DecortController.image_find method 2024-05-28 16:41:17 +03:00
5b731d009b Add validated_image_id check to if-condition of deleting image logic 2024-05-28 16:26:42 +03:00
57dba89b5a Merge branch 'BANS-283' into 'rc-5.2.6'
Fix if-condition in DecortController.pfw_configure method for excluding case of TypeError

See merge request rudecs/dev/decort-ansible!39
2024-05-27 09:41:08 +00:00
007c7f4bad Fix if-condition in DecortController.pfw_configure method for excluding case of TypeError 2024-05-27 12:20:33 +03:00
Алексей Даньков
7f87642b47 Merge branch 'BANS-264' into 'rc-5.2.6'
Remove if-condition for check mode from DecortController.disk_find method

See merge request rudecs/dev/decort-ansible!38
2024-05-27 07:50:33 +00:00
2873e4da82 Remove if-condition for check mode from DecortController.disk_find method 2024-05-24 14:08:43 +03:00
9c97f4b645 Merge branch 'BANS-260' into 'rc-5.2.6'
Fix call of DecortController.disk_rename method in decort_disk.action method

See merge request rudecs/dev/decort-ansible!37
2024-05-23 16:02:04 +00:00
772f389d98 Fix call of DecortController.disk_rename method in decort_disk.action method 2024-05-23 19:00:29 +03:00
36e1383c7e Merge branch 'BANS-255' into 'rc-5.2.6'
Fix disk deleting logic for permanently deleting from recycle bin

See merge request rudecs/dev/decort-ansible!36
2024-05-23 13:51:37 +00:00
35fe2bdf0e Fix disk deleting logic for permanently deleting from recycle bin 2024-05-23 16:41:45 +03:00
7e7a4898ec Merge branch 'BANS-250' into 'rc-5.2.6'
Fix call of DecortController.disk_share method in decort_disk.create method

See merge request rudecs/dev/decort-ansible!35
2024-05-23 10:58:11 +00:00
fde986bb42 Fix call of DecortController.disk_share method in decort_disk.create method 2024-05-23 13:56:35 +03:00
538c7e6f47 Merge branch 'BANS-249' into 'rc-5.2.6'
Fix call of DecortController.disk_limitIO method in decort_disk.create method

See merge request rudecs/dev/decort-ansible!34
2024-05-22 16:36:26 +00:00
53634b7fa5 Fix call of DecortController.disk_limitIO method in decort_disk.create method 2024-05-22 19:34:40 +03:00
b68e562fc1 Merge branch 'BANS-248' into 'rc-5.2.6'
Fix incorrect access to MIN_IOPS variable in DecortController.disk_check_iotune_arg method

See merge request rudecs/dev/decort-ansible!33
2024-05-22 15:03:08 +00:00
df619e7998 Fix incorrect access to MIN_IOPS variable in DecortController.disk_check_iotune_arg method 2024-05-22 17:59:38 +03:00
Алексей Даньков
877b84d650 Merge branch 'BANS-243' into 'rc-5.2.6'
Add check mode logic to DecortController.k8s_workers_modify method.

See merge request rudecs/dev/decort-ansible!31
2024-05-22 11:00:13 +00:00
10b98b7c4a Merge branch 'BANS-246' into 'rc-5.2.6'
Fix call of AnsibleModule.fail_json method in decort_disk.__init__ method.

See merge request rudecs/dev/decort-ansible!32
2024-05-22 10:05:13 +00:00
51707cbb69 Fix call of AnsibleModule.fail_json method in decort_disk.__init__ method. 2024-05-22 12:53:22 +03:00
3f4cfd40d6 Add check mode logic to DecortController.k8s_workers_modify method. 2024-05-22 11:50:06 +03:00
Алексей Даньков
4cf0316dad Merge branch 'BANS-236' into 'rc-5.2.6'
Move execution of getConfig before if-condition of check mode in decort_k8s.package_facts method

See merge request rudecs/dev/decort-ansible!30
2024-05-21 14:59:44 +00:00
abcb7d52f8 Move execution of getConfig before if-condition of check mode in decort_k8s.package_facts method 2024-05-21 16:17:12 +03:00
Алексей Даньков
8a6d624069 Merge branch 'BANS-226' into 'rc-5.2.6'
Fix logic of starting k8s after restore it from recycle bin

See merge request rudecs/dev/decort-ansible!29
2024-05-21 08:08:12 +00:00
Алексей Даньков
2b96881849 Merge branch 'BANS-225' into 'rc-5.2.6'
Fix logic for 'changed' setting in DecortController.k8s_workers_modify method

See merge request rudecs/dev/decort-ansible!28
2024-05-21 08:07:28 +00:00
6725e4342e Fix logic of starting k8s after restore it from recycle bin 2024-05-20 20:45:45 +03:00
5003991cf5 Fix logic for 'changed' setting in DecortController.k8s_workers_modify method 2024-05-20 16:37:41 +03:00
8a99097b6c Merge branch 'BANS-221' into 'rc-5.2.6'
Fix parameter name 'vinsId' for API request in DecortController.k8s_provision method

See merge request rudecs/dev/decort-ansible!27
2024-05-20 09:52:44 +00:00
6a99ea4d85 Fix parameter name 'vinsId' for API request in DecortController.k8s_provision method 2024-05-20 12:41:06 +03:00
236c3b6d26 Merge branch 'BANS-218' into 'rc-5.2.6'
Fix logic for permanently deleting k8s from recycle bin

See merge request rudecs/dev/decort-ansible!26
2024-05-17 13:42:35 +00:00
bcf384a910 Fix logic for permanently deleting k8s from recycle bin 2024-05-17 16:37:35 +03:00
8cf3d05d0b Merge branch 'BANS-216' into 'rc-5.2.6'
Fix logic for the case when DecortController.k8s_provision method returns 0

See merge request rudecs/dev/decort-ansible!25
2024-05-17 11:26:18 +00:00
5da120f2d3 Fix logic for the case when DecortController.k8s_provision method returns 0 2024-05-17 14:21:17 +03:00
6abd78882c Merge branch 'BANS-202' into 'rc-5.2.6'
Add rg_name parameter to the arguments of the DecortController.rg_find method...

See merge request rudecs/dev/decort-ansible!22
2024-05-08 13:29:15 +00:00
7fa2d07ab0 Merge branch 'BANS-203' into 'rc-5.2.6'
Change cluster_conf, kublet_conf, kubeproxy_conf, join_conf decort_k8s parameter types to dict.

See merge request rudecs/dev/decort-ansible!23
2024-05-08 13:29:05 +00:00
8b407c6f69 Change cluster_conf, kublet_conf, kubeproxy_conf, join_conf decort_k8s parameter types to dict. 2024-05-08 16:06:39 +03:00
4c4be07550 Add rg_name parameter to the arguments of the DecortController.rg_find method call in library/decort_k8s.py 2024-05-08 14:34:33 +03:00
Алексей Даньков
eb542fa46c Merge branch 'BANS-197' into 'rc-5.2.6'
Assign upload_files variable outside of if construction in DecortController.k8s_provision method

See merge request rudecs/dev/decort-ansible!21
2024-05-07 07:06:12 +00:00
240e2ce2df Assign upload_files variable outside of if construction in DecortController.k8s_provision method 2024-05-06 18:48:08 +03:00
Алексей Даньков
f4fcf5b7b7 Merge branch 'fix/rc-5.2.6/jira_bans-39' into 'rc-5.2.6'
(Jira BANS-39) Add NoneType check for 'aaff' argument of the 'Decort.Controller.compute_affinity' method.

See merge request rudecs/dev/decort-ansible!20
2024-04-16 11:12:37 +00:00
Алексей Даньков
d2dabdb194 Merge branch 'fix/rc-5.2.6/jira_bans-38' into 'rc-5.2.6'
(Jira BANS-38) Add NoneType check for 'aff' argument of the 'Decort.Controller.compute_affinity' method.

See merge request rudecs/dev/decort-ansible!19
2024-04-16 11:10:56 +00:00
694e68fe22 Add NoneType check for the 'aaff' argument to Affinity Rules management logic of the 'Decort.Controller.compute_affinity' method. 2024-04-15 18:02:50 +03:00
9e7a33a44a Add NoneType check for the 'aff' argument to Affinity Rules management logic of the 'Decort.Controller.compute_affinity' method. 2024-04-15 17:03:51 +03:00
Алексей Даньков
31b72b3806 Merge branch 'fix/rc-5.2.6/decort_utils.py/1' into 'rc-5.2.6'
Enable output of deleted RGs for API request /restmachine/cloudapi/rg/list in...

See merge request rudecs/dev/decort-ansible!18
2024-04-11 12:21:58 +00:00
Алексей Даньков
fa79b90269 Merge branch 'fix/rc-5.2.6/decort_utils.py/2' into 'rc-5.2.6'
Add status DESTROYED for condition excluding of API request...

See merge request rudecs/dev/decort-ansible!17
2024-04-11 12:21:16 +00:00
Алексей Даньков
aa9f26bf1a Merge branch 'fix/rc-5.2.6/decort_rg.py/1' into 'rc-5.2.6'
Add check for "rg_name" parameter in case of resource group creating

See merge request rudecs/dev/decort-ansible!16
2024-04-11 12:20:41 +00:00
6cd828d031 Add status DESTROYED for condition excluding of API request /restmachine/cloudapi/rg/getResourceConsumption in the DecortController._rg_get_by_id method. 2024-04-05 12:17:05 +03:00
d50509e0c3 Enable output of deleted RGs for API request /restmachine/cloudapi/rg/list in the DecortController.rg_find method. 2024-04-05 11:10:40 +03:00
a45ab19d38 Add check for "rg_name" parameter in case of resource group creating 2024-04-04 18:30:07 +03:00
Алексей Даньков
a3c1dcad7a Merge branch 'fix/rc-5.2.6/decort_rg.py/1' into 'rc-5.2.6'
Add execution of restoring in the case of state=disabled and state=enabled

See merge request rudecs/dev/decort-ansible!15
2024-04-04 13:14:18 +00:00
Алексей Даньков
9bf50c958f Merge branch 'fix/decort_utils.py/_rg_get_by_id/1' into 'rc-5.2.6'
Add call of API /cloudapi/rg/getResourceConsumption to...

See merge request rudecs/dev/decort-ansible!14
2024-04-04 13:13:00 +00:00
Алексей Даньков
d769119ade Merge branch 'fix/rc-5.2.6/decort_utils.py/2' into 'rc-5.2.6'
Fix value for query_key_map['disk'] in method DecortController.rg_update: 'CU_D' -> 'CU_DM'

See merge request rudecs/dev/decort-ansible!13
2024-04-04 13:10:56 +00:00
200e8f7151 Add execution of restoring in the case of state=disabled and state=enabled 2024-04-04 15:53:29 +03:00
f11ec8fefb Update logic to only request RG resources info if the RG is not deleted. 2024-04-04 11:11:18 +03:00
19534384a8 Fix value for query_key_map['disk'] in method DecortController.rg_update: 'CU_D' -> 'CU_DM' 2024-04-03 13:18:53 +03:00
1304a0fcbf Add call of API /cloudapi/rg/getResourceConsumption to DecortController._rg_get_by_id and update its logic and code style 2024-04-03 11:00:56 +03:00
Алексей Даньков
a5f03389f2 Merge branch 'fix/rc-5.2.6/decort_utils.py_decort_api_call' into 'rc-5.2.6'
Add response text to error msg of decort_api_call method

See merge request rudecs/dev/decort-ansible!11
2024-03-25 12:34:54 +00:00
Алексей Даньков
6760167e4e Merge branch 'fix/rc-5.2.6/decort_utils.py_compute_resize' into 'rc-5.2.6'
Remove new_ram value conversion from GB to MB

See merge request rudecs/dev/decort-ansible!9
2024-03-25 12:29:56 +00:00
Алексей Даньков
2b4ba7ee55 Merge branch 'fix/rc-5.2.6/decort_kvmvm.py' into 'rc-5.2.6'
Exclude call of compute_bootdisk_size method with new_size=None

See merge request rudecs/dev/decort-ansible!8
2024-03-25 12:25:04 +00:00
Алексей Даньков
5b66c98cc6 Merge branch 'fix/rc-5.2.6/decort_utils.py' into 'rc-5.2.6'
Fix account vins find logic

See merge request rudecs/dev/decort-ansible!7
2024-03-25 12:24:39 +00:00
Алексей Даньков
55268beaad Merge branch 'fix/rc-5.2.6/decort_rg' into 'rc-5.2.6'
bug fix

See merge request rudecs/dev/decort-ansible!6
2024-03-25 12:23:53 +00:00
b18bdef269 fix code style 2024-03-22 18:54:57 +03:00
21e853c1f2 Add response text to error msg of decort_api_call method 2024-03-22 18:42:44 +03:00
1c6b46c535 remove new_ram value conversion from GB to MB 2024-03-22 17:01:50 +03:00
058de4884f exclude call of compute_bootdisk_size method with new_size=None 2024-03-22 13:39:06 +03:00
Dmitriy Smirnov
d622dd8453 fix account vins find logic 2024-03-20 14:58:02 +03:00
Dmitriy Smirnov
e26011ab20 bug fix 2024-03-18 19:27:32 +03:00
Alex_geth
f2e9b550bb cloud-init example 2023-11-07 16:52:17 +03:00
Alex_geth
a8f50bfb6b k8s/lb update && bug fixes 2023-11-07 16:45:37 +03:00
Alex_geth
9222fdd866 k8s provision update
fix */list
2023-08-16 19:04:27 +03:00
Alex_geth
e94faef2ad extnet list fix 2023-08-14 17:42:34 +03:00
Alex_geth
66e72a3d3b fix list data 2023-08-01 10:14:51 +03:00
Alex_geth
740271b2f2 version update 2023-07-31 17:25:51 +03:00
Alex_geth
207c04bb77 4.8.7 decort/basis version update 2023-07-31 17:19:50 +03:00
Alex_geth
5be2e850ce kvmvm provision fix 2023-05-03 14:35:53 +03:00
Alex_geth
7fffdd0ab5 affinit/tag/label fix 2023-05-02 10:58:24 +03:00
Alex_geth
ae85826129 Some impruvments and updates 2023-04-21 14:23:07 +03:00
Алексей Даньков
b03b82e492 Merge branch 'fix/rc-5.2.2' into 'master'
Fix/rc 5.2.2

See merge request rudecs/decort-ansible!5
2022-10-31 08:44:40 +00:00
Алексей Даньков
15893f58bb Merge branch 'feature/decort_rg_refactoring' into 'master'
Feature/decort rg refactoring

See merge request rudecs/decort-ansible!4
2022-10-31 08:44:18 +00:00
msbolshakov
3681949ea6 decort_rg fix 2022-10-25 14:37:42 +07:00
msbolshakov
8713ebe099 decort_rg ifx 2022-10-21 23:27:40 +07:00
msbolshakov
87ecb762aa decort_rg ifx 2022-10-21 22:36:46 +07:00
msbolshakov
b30d8f2b3c decort_rg ifx 2022-10-21 21:53:57 +07:00
msbolshakov
36d6fe092d add exampels 2022-10-21 20:43:08 +07:00
msbolshakov
8f737397de decort_rg module refactoring 2022-10-21 18:47:13 +07:00
msbolshakov
72d9da0234 add account id in osimage facts 2022-10-11 21:11:38 +07:00
msbolshakov
4b3f34376b add account id in osimage facts 2022-10-11 20:18:07 +07:00
72b591723f decort_disk bug fix 2022-09-22 16:10:43 +00:00
f0e9ac10cc Bug fix 2022-09-21 15:53:02 +00:00
Alex_geth
82eef4492d Merge branch 'feature/decort_lb' 2022-09-21 13:00:55 +03:00
Alex_geth
889618f843 added LB module & some other fix 2022-09-20 21:44:32 +03:00
msbolshakov
ef33532a83 fix module errors 2022-09-19 17:56:45 +07:00
msbolshakov
8f7c933fb8 decort_disk remake 2022-09-16 23:26:11 +07:00
Alex_geth
dd28084b76 Merge branch 'rc-5.0-demo' of ssh://git.digitalenergy.online:2221/rudecs/decort-ansible into rc-5.0-demo 2022-09-02 14:57:06 +03:00
Alex_geth
8d6ed618ab decort_vins update & k8 wg fix 2022-09-02 14:56:58 +03:00
Maksim Bolshakov
e2c9f591b8 Add new file 2022-08-23 10:07:44 +03:00
Maksim Bolshakov
ebdf9aa012 Add new file 2022-08-23 10:07:18 +03:00
Maksim Bolshakov
31d6774475 Add new example 2022-08-23 10:06:23 +03:00
Maksim Bolshakov
6148e67dd1 Add new file 2022-08-02 15:02:18 +03:00
Maksim Bolshakov
4c7922cb55 Add new file 2022-08-02 15:01:45 +03:00
Maksim Bolshakov
8d51555db1 Add new file 2022-08-02 15:00:52 +03:00
Maksim Bolshakov
ebfb465531 Add new file 2022-08-02 15:00:02 +03:00
Maksim Bolshakov
f3504a3d50 Add new file 2022-07-18 16:59:23 +03:00
Alex_geth
1c3558d6f3 Merge branch 'rc-5.0-demo' of ssh://git.digitalenergy.online:2221/rudecs/decort-ansible into rc-5.0-demo 2022-07-18 14:24:25 +03:00
Alex_geth
12a80e7cf4 vins connect 2022-07-18 14:21:38 +03:00
Maksim Bolshakov
38d24cfa0a Update kubernetes.yaml 2022-07-12 17:17:40 +03:00
Maksim Bolshakov
ebe1c76a43 Update basicservices.yaml 2022-07-12 11:32:10 +03:00
msbolshakov
224ac59779 osimage fix 2022-07-11 16:05:07 +07:00
msbolshakov
ff4273cbce module_utils_fix 2022-07-11 15:59:04 +07:00
msbolshakov
5e5b6f6b8a osimage examples 2022-07-11 15:48:02 +07:00
msbolshakov
c497979efa osimage update 2022-07-11 15:43:53 +07:00
msbolshakov
28876ae38d decort_osimage update 2022-07-08 19:12:17 +07:00
Aleksandr Malyavin
ebe1a9194f change directory name example>examples 2022-06-29 18:26:58 +03:00
msbolshakov
d9ad1fee21 examples 2022-06-29 20:13:09 +07:00
Alex_geth
4b23cf8bae bservice module fix 2022-06-29 15:54:21 +03:00
Alex_geth
5b809dee4f basic services addon 2022-06-28 03:55:28 +03:00
Maksim Bolshakov
ac93e76005 Add new file 2022-05-27 18:15:53 +03:00
Maksim Bolshakov
aae60a46b9 Add new file 2022-05-27 18:15:21 +03:00
Maksim Bolshakov
25af1b4428 Add new file 2022-05-27 18:14:37 +03:00
Alex_geth
e058925f9b fix 2 2022-05-27 17:45:12 +03:00
Alex_geth
f6b0da976b k8s update 2022-05-27 07:42:48 +03:00
Maksim Bolshakov
9b9b92ff07 Update cloud_init-example 2022-05-25 17:01:13 +03:00
Maksim Bolshakov
498b46b4f2 New example for DECORT kvmvm module 2022-05-25 17:00:45 +03:00
Maksim Bolshakov
85da544614 Add new directory 2022-05-25 16:52:01 +03:00
Alex_geth
be7841ba38 fix 1 2022-05-25 10:56:44 +03:00
Alex_geth
aabd5dab6e demo update 2022-05-20 14:37:13 +03:00
Alex_geth
4a9d181782 pyJWT > 2.0.0 2022-04-01 14:50:47 +03:00
Aleksandr Malyavin
ec7d1fd181 Fix if else statement 2022-04-01 14:35:20 +03:00
Aleksandr Malyavin
4f151e174b Some changes 2022-04-01 12:01:44 +03:00
Aleksandr Malyavin
0237c469c5 Change API for get task request cloudbroker>cloudapi 2022-04-01 12:00:50 +03:00
Alex_geth
ab44d18d21 pyJWT > 2.0.0 2022-03-29 15:01:15 +03:00
Alex_geth
75bda6d76d update pyJWT > 2.0.0 2022-03-29 14:59:32 +03:00
Aleksandr Malyavin
41ca42dcab add k8s library 2022-03-28 19:53:34 +03:00
Filipp Ignatenko
36773c7bb3 Update README.md 2022-03-10 13:05:08 +03:00
Alex_geth
3d9917b8a7 api update, arch update 2022-01-27 13:17:14 +03:00
Sergey Shubin svs1370
a166ce1c8d Update module info for DECORT API 3.6.1 2021-04-21 12:45:30 +03:00
Sergey Shubin svs1370
e81bf1ca16 Initial adaptation for DECORT API ver.3.6.1 2021-04-21 12:29:15 +03:00
Sergey Shubin svs1370
2c95c6ef0c Add extra checks for compute data disks management 2021-04-06 14:49:09 +03:00
Sergey Shubin svs1370
18067b82b7 Fix pool_name argument name in call to provision_disk 2021-04-06 13:32:25 +03:00
Sergey Shubin svs1370
bc317d1438 Change default pool name for disk provisioning to empty string 2021-03-29 10:14:43 +03:00
Sergey Shubin svs1370
e17c8be53a Rework conditions which cause compute_network method to fail module execution 2021-03-25 21:34:53 +03:00
Sergey Shubin svs1370
2014863c37 Fix incorrect condition when listing ext nets in compute_networks 2021-03-25 18:49:04 +03:00
Sergey Shubin svs1370
4b57777a2c Change external network list API URL to use new group extnet 2020-12-21 09:48:12 +03:00
Sergey Shubin svs1370
f7437ebcad Avoid extra rg/get API calls when locating compute instance 2020-11-03 23:57:01 +03:00
Sergey Shubin svs1370
a654f76d78 Avoid excessive compute/get calls in compute_find, bug fix 01 2020-11-03 18:34:09 +03:00
Sergey Shubin svs1370
e2021f895b Avoid excessive compute/get calls in compute_find 2020-11-03 18:12:12 +03:00
Sergey Shubin svs1370
27f4b2a88b Optimize number of compute/get calls when creating new kvmvm, step 2 2020-11-03 16:49:06 +03:00
Sergey Shubin svs1370
1505d48b83 Optimize number of compute/get calls when creating new kvmvm, step 1 2020-11-03 13:24:57 +03:00
Sergey Shubin svs1370
20190532b5 Minor bug fixes and add missing waypoints logging for compute mgmt 2020-11-02 16:58:19 +03:00
Sergey Shubin svs1370
fdbcdbe208 Workaround for KVM VM created without vNICs, more errir info for API connection failures 2020-08-10 22:13:02 +03:00
Sergey Shubin svs1370
22f54c1191 Update README markdown 2020-07-09 23:47:36 +03:00
Sergey Shubin svs1370
ca2e5d3b3f Update requirements section in modules and README 2020-07-09 23:44:51 +03:00
Sergey Shubin svs1370
06272ce7e0 Add environment preparation script template to examples 2020-07-09 17:25:11 +03:00
Sergey Shubin svs1370
39ea3dd18c Add some examples in a separate directory 2020-07-09 17:22:48 +03:00
Sergey Shubin svs1370
07e9959f90 Add extra parameters to return structure of decrt_pfw 2020-07-09 12:10:51 +03:00
Sergey Shubin svs1370
c7a3b5d6b2 Optimizing PFW management workflows 2020-07-09 01:11:20 +03:00
Sergey Shubin svs1370
c870989ed7 Merge branch 'master' of https://github.com/rudecs/decort-ansible 2020-07-07 14:37:02 +03:00
Sergey Shubin svs1370
b1b2ce86b1 Debug PFW module and minor fixes to the rest of the modules 2020-07-07 14:34:29 +03:00
svs1370
9431e940e8 Update compatible API version 2020-07-03 19:28:36 +03:00
Sergey Shubin svs1370
bdb4eab9d7 Switching to updated external network list API function 2020-07-03 19:26:40 +03:00
Sergey Shubin svs1370
53fa24fc8d Fixing various compute reconfig and resize scenarios 2020-07-03 13:44:40 +03:00
Sergey Shubin svs1370
b02187058d Fixing Jinja templating quirks for compute_networks method 2020-07-02 19:10:28 +03:00
Sergey Shubin svs1370
f0c6c432d5 Fixing KVM VM mgmt scenarios (contd.) 2020-06-27 16:56:42 +03:00
Sergey Shubin svs1370
9a2c909961 Debug and fix for disk and KVM VM handling 2020-06-26 20:42:32 +03:00
Sergey Shubin svs1370
e5edf40b6e Fixing initial bugs in Disk and KvmVM modules, adjusting utility module accordingly 2020-05-29 20:02:40 +03:00
Sergey Shubin svs1370
125ebb1fb8 Initial injection of decort_pfw and collateral changes to other modules 2020-05-28 00:59:52 +03:00
Sergey Shubin svs1370
e5e6db6586 Initial code injection for disk and kvmvm modules, collateral changes to other modules 2020-05-27 18:10:05 +03:00
Sergey Shubin svs1370
ca740f98fd Initial implementation of ViNS mgmt module and minor bug fixes 2020-05-18 18:55:47 +03:00
Sergey Shubin svs1370
ce9fb0ceea Initial injection of code base. Some modules may not be fully functional yet. 2020-05-15 20:07:40 +03:00
58 changed files with 18501 additions and 1 deletions

30
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,30 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: end-of-file-fixer
- id: no-commit-to-branch
name: no-commit-to-branch (main, master, dev_*)
args:
- --pattern
- dev_.*
- repo: https://github.com/pycqa/flake8
rev: 7.2.0
hooks:
- id: flake8
exclude: |
(?x)^(
module_utils/decort_utils.py |
library/decort_bservice.py |
library/decort_disk.py |
library/decort_group.py |
library/decort_k8s.py |
library/decort_kvmvm.py |
library/decort_lb.py |
library/decort_osimage.py |
library/decort_pfw.py |
library/decort_rg.py |
library/decort_vins.py
)$
args:
- --extend-ignore=E402

174
CHANGELOG.md Normal file
View File

@@ -0,0 +1,174 @@
# Список изменений в версии 10.0.0
## Добавлено
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-807 | Модуль переименован из `decort_snapshot` в `decort_vm_snapshot`. |
### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-723 | Добавлен параметр `cdrom` и возвращаемое значение `cd_image_id`. |
| BANS-727 | Добавлен параметр `boot.from_cdrom`. |
| BANS-728 | Добавлен параметр `boot.order` и возвращаемое значение `boot_order`. |
| BANS-871 | Добавлен параметр `storage_policy_id`. |
| BANS-729 | Добавлен параметр `boot.disk_redeploy`. |
| BANS-693 | Добавлена проверка при подключении сетей к запущенной ВМ. |
| BANS-881 | Добавлены параметры `networks.security_group_mode` и `networks.security_group_ids` и возвращаемые значения `interfaces.security_group_mode` и `interfaces.security_group_ids`. |
| BANS-895 | Добавлен возможный тип данных `null` для возвращаемого значения `image_id`. |
| BANS-894 | Добавлен параметр `os_version` и возвращаемое значение `os_version`. |
| BANS-890 | Добавлен параметр `networks.enabled` и возвращаемое значение `interfaces.enabled`. |
| BANS-902 | Добавлено возвращаемое значение `boot_loader_metaiso`. |
| BANS-904 | Добавлены параметры `clone_from.sep_pool_name`, `clone_from.sep_id`, `clone_from.storage_policy_id`. |
| BANS-905 | Добавлены параметры `abort_cloning`, `get_cloning_status` и возвращаемое значение `cloning_status`. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-865 | Добавлен параметр `storage_policy_id`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-867 | Добавлен параметр `storage_policy_id` и возвращаемое значение `storage_policy_id`. |
| BANS-866 | Добавлена возможность изменять `storage_policy_id` диска. |
| BANS-897 | Добавлено возвращаемое значение `to_clean`. |
| BANS-898 | Добавлены параметры `disks.objects`, `disks.objects.pci_slot_num_hex`, `disks.objects.bus_num_hex`. |
### Модуль decort_osimage
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-868 | Добавлен параметр `storage_policy_id` и возвращаемое значение `storage_policy_id`. |
| BANS-885 | Добавлена возможность изменять `storage_policy_id` шаблонного образа. |
| BANS-914 | Добавлено возвращаемое значение `to_clean`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-869 | Добавлен параметр `storage_policy_id`. |
| BANS-234 | Добавлена возможность переименования кластера. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-877 | Добавлено возвращаемое значение `storage_policy_ids`. |
| BANS-882 | Добавлен параметр `quotas.storage_policies`. |
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-875 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
### Модуль decort_account_info
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-876 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
| BANS-911 | У параметра `computes.filter.tech_status` добавлены значения `MIGRATING_IN` и `MIGRATING_OUT`. |
| BANS-903 | Добавлены новые возвращаемые значения в `audits`. |
### Модуль decort_storage_policy
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-878 | Добавлен модуль `decort_storage_policy`. |
### Модуль decort_user_info
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-879 | Добавлен параметр `storage_policies` и возвращаемое значение `storage_policies`. |
| BANS-889 | Добавлен параметр `accounts.filter.zone_id`. |
| BANS-892 | Добавлено возвращаемое значение `accounts.zone_ids`. |
| BANS-891 | Добавлен параметр `security_groups` и возвращаемое значение `security_groups`. |
### Модуль decort_security_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-883 | Добавлен модуль `decort_security_group`. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-147 | Добавлен параметр `permanently` для удаления внутренней сети в корзину или безвозвратно. |
## Удалено
### Модуль decort_osimage
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-862 | Удален параметр `drivers`. |
| BANS-292 | Удален неиспользуемый параметр `gid`. |
### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-730 | Удалён параметр `image_name`. |
| BANS-893 | Удалена необходимость указывать параметр `networks.mac` при подключении сети `SDN`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-867 | Удалён параметр `iops`. |
| BANS-898 | Удалён параметр `disks.ids`. |
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-320 | Удалён неиспользуемый параметр `ext_ip_addr`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-199 | Удалён неиспользуемый параметр `quotas`. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-388 | Удалён неиспользуемый параметр `image_name`. |
## Исправлено
### Модуль decort_bservice
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-851 | Скорректирована логика параметра целевого состояния `present`. Теперь состояние `present` соответствует тому, что базовая служба существует, и не приводит к изменению состояния существующей базовой службы. Также для параметра `state` значение по умолчанию `present` теперь только при создании базовой службы. |
| BANS-873 | Указание параметров `account_id`/`account_name`, `rg_name`, `name` могло найти базовую службу с таким же именем, но принадлежащей другому аккаунту.|
### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-685 | Исправлена ошибка, приводящая к отображению неактуальной информации о виртуальной машине после её удаления. |
| BANS-699 | Исправлена логика удаления ВМ в статусе DISABLED, приводящая к возникновению ошибок. |
| BANS-748 | Исправлена логика работы с несуществующей ВМ, по которой получение информации о ВМ приводило к возникновению ошибок. |
| BANS-697 | Исправлена логика, приводящая к ошибке при указании идентификатора несуществующего образа. |
| BANS-122 | Исправлена логика, приводящая к изменению `affinity rules`, `anti-affinity rules`, `anti-affinity rules` и ` tags` в режиме `check_mode`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-742 | Исправлена ошибка, приводящая к отображению неактуальной информации о статусе диска после его удаления. |
| BANS-743 | Исправлена логика работы с несуществующим диском, по которой получение информации о диске приводило к возникновению ошибок. |
| BANS-247 | Модуль завершал свою работу ошибкой запроса API при создании/изменении диска с указанием параметра `size` меньше 1. |
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-752 | Исправлена ошибка, приводящая к отображению неактуальной информации о статусе балансировщика после его удаления. |
| BANS-754 | Исправлена логика работы с несуществующими объектами, по которой получение информации об объектах приводило к возникновению ошибок. |
| BANS-753 | Исправлена логика работы с несуществующим балансировщиком, по которой получение информации о балансировщике приводило к возникновению ошибок. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-648 | Исправлена логика передачи параметров, приводящая к ошибке при передаче параметра `name` без параметра `id`. |
| BANS-418 | Исправлена логика обновления сетей у группы базовой службы, из-за которой не происходило обновление внешних сетей. |
| BANS-646 | Исправлена логика повторного удаления группы базовой службы, которая приводила к возникновению ошибок. |
| BANS-559 | При создании группы с указанием параметра `timeoutStart` модуль производил попытку запустить группу после её создания. |
### Модуль decort_osimage
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-653 | Исправлена ошибка, приводящая к отображению неактуальной информации об образе после его удаления. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-663 | Исправлена ошибка передачи параметра `description` при создании внутренней сети. |
| BANS-174 | Исправлена логика, приводящая к невозможности задать внешнюю сеть для внутренней сети без внешней сети. |

3
Makefile Normal file
View File

@@ -0,0 +1,3 @@
dev:
pip install -r requirements-dev.txt
pre-commit install

View File

@@ -1,2 +1,22 @@
# decort-ansible
Ansible modules for Digital Energy Orchestration Technology (DECORT) platform v3.4.0 and above
Модули Ansible для платформы Digital Energy Orchestration Technology (DECORT).
## Соответствие версий платформы версиям модулей Ansible
| Версия платформы | Версия модулей Ansible |
|:----------------:|:--------------------------:|
| 4.4.0 | 10.0.x |
| 4.4.0 build 963 | 9.0.x |
| 4.3.0 | 8.0.x |
| 4.2.0 | 7.0.x, 7.1.x, 7.2.x |
| 4.1.0 | 6.0.x, 6.1.x |
| 4.0.0 | 5.6.x, 5.5.x, 5.4.x, 5.3.x |
| 3.8.8, 3.8.9 | 5.2.6 |
| 3.8.7 | 5.2.5 |
| 3.8.6 | 5.2.4 |
## Ссылки
- [Документация](https://repository.basistech.ru/BASIS/wiki-decort-ansible/src/branch/main/Home.md)
- [Список изменений](./CHANGELOG.md)

0
examples/.gitkeep Normal file
View File

40
examples/VINS.yaml Normal file
View File

@@ -0,0 +1,40 @@
---
#
# DECORT vins module example
#
- hosts: localhost
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "https://sso.digitalenergy.online"
validity: 1200
register: my_jwt
delegate_to: localhost
- name: print out JWT
debug:
var: my_jwt.jwt
delegate_to: localhost
- name: Manage ViNS at resource group level
decort_vins:
authenticator: jwt
jwt: "{{ my_jwt.jwt }}"
controller_url: "https://ds1.digitalenergy.online"
vins_name: "vins_created_by_decort_VINS_module"
state: present
rg_id: 198
ext_net_id: -1
ipcidr: "10.20.30.0/24"
mgmtaddr: "10.20.30.1"
custom_config: false
config_save: false
verify_ssl: false
register: managed_vins
- name: print VINS facter
debug:
msg: "{{managed_vins.facts.password}}"
when: managed_vins.facts.password is defined

40
examples/annotations.yaml Normal file
View File

@@ -0,0 +1,40 @@
---
#
# DECORT k8s module labels, taints, annotations example
#
- hosts: localhost
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "https://sso.digitalenergy.online"
validity: 1200
register: my_jwt
delegate_to: localhost
- name: print out JWT
debug:
var: my_jwt.jwt
delegate_to: localhost
- name: Create k8s cluster
decort_k8s:
authenticator: jwt
jwt: "{{ my_jwt.jwt }}"
controller_url: "https://mr4.digitalenergy.online"
name: "example_kubernetes"
rg_id: 199
k8ci_id: 4
state: present
workers:
- name: workgroup1
labels:
- disktype1=ssd1
- disktype2=ssd2
taints:
- key1=value1:NoSchedule
- key2=value2:NoSchedule
annotations:
- node.deckhouse.io/group1=g1
- node.deckhouse.io/group2=g2
register: kube

View File

@@ -0,0 +1,31 @@
---
#
# DECORT bservice module example
#
- hosts: localhost
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "https://sso.digitalenergy.online"
validity: 1200
register: my_jwt
delegate_to: localhost
- name: print out JWT
debug:
var: my_jwt.jwt
delegate_to: localhost
- name: Manage bservice at RG
decort_bservice:
account_id: 98
verify_ssl: false
authenticator: jwt
jwt: "{{ my_jwt.jwt }}"
controller_url: "https://ds1.digitalenergy.online"
rg_id: 1629
state: present
name: databases
started: True
register: db_bservice

View File

@@ -0,0 +1,22 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
name: "example_disk"
sep_id: 1
pool: 0
gid: 0
size: 2
type: "D"
description: "Disk created by decort_disk module"
iops: 2000
state: present
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,18 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
name: "example_disk"
permanently: False
force_detach: True
reason: "Just to test module decort_disk"
state: absent
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,28 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
limitIO:
read_bytes_sec: 100
read_bytes_sec_max: 100
read_iops_sec: 100
read_iops_sec_max: 100
size_iops_sec: 100
write_bytes_sec: 100
write_bytes_sec_max: 100
write_iops_sec: 100
write_iops_sec_max: 100
total_bytes_sec: 0
total_iops_sec: 0
total_bytes_sec_max: 0
total_iops_sec_max: 0
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
name: "example_disk2"
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
---
- hosts: localhost
tasks:
- name: manage data disk 01
decort_disk:
authenticator: oauth2
app_id: #Application id from SSO DigitalEnergy
app_secret: #Application secret from SSO DigitalEnergy
controller_url: "https://cloud.digitalenergy.online"
account_name: "account_name"
id: 111
state: present
verify_ssl: false
delegate_to: localhost

View File

@@ -0,0 +1,27 @@
---
#
# DECORT osimage module example
#
- hosts: localhost
tasks:
- name: create
decort_osimage:
authenticator: oauth2
verify_ssl: False
controller_url: "https://ds1.digitalenergy.online"
state: present
image_name: "alpine_linux3.14.0"
account_Id: 12345
url: "https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-virt-3.14.0-x86_64.iso"
boottype: "uefi"
imagetype: "linux"
hotresize: False
image_username: "test"
image_password: "p@ssword"
usernameDL: "testDL"
passwordDL: "p@sswordDL"
architecture: "X86_64"
drivers: "KVM_X86"
delegate_to: localhost
register: simple_vm

View File

@@ -0,0 +1,14 @@
---
#
# DECORT osimage module example
#
- hosts: localhost
tasks:
- name: create_virtual_osimage
decort_osimage:
authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0"
virt_name: "alpine_last"
delegate_to: localhost
register: osimage

View File

@@ -0,0 +1,14 @@
---
#
# DECORT osimage module example
#
- hosts: localhost
tasks:
- name: get_osimage
decort_osimage:
authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0"
account_Id: 79349
delegate_to: localhost
register: simple_vm

View File

@@ -0,0 +1,14 @@
---
#
# DECORT osimage module example
#
- hosts: localhost
tasks:
- name: rename_osimage
decort_osimage:
authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0v2.0"
image_id: 54321
delegate_to: localhost
register: osimage

View File

@@ -0,0 +1,20 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
quotas:
cpu: 8
ram: 4096
disk: 20
ext_ips: 10
net_transfer: 200
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,21 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
resType:
- vins
- compute
- k8s
- openshift
- lb
- flipgroup
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,30 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
account_id: 99
owner: "user_1" #Leave blank to set current user as owner.
quotas:
cpu: 8
ram: 4096
disk: 20
ext_ips: 10
net_transfer: 200
access:
action: "grant"
user: "user_2"
right: "RCX"
def_netType: "PRIVATE"
ipcidr: "" "192.168.1.1"
extNetId: 0
extNetIp: "" "10.100.1.10"
resType:
- vins
- compute
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "test_rg"
# or
#rg_id: 999
account_id: 99
state: present
permanently: True
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,12 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_id: 999 # rg can be restored only by rg id
account_id: 99
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,14 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
state: enabled
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,18 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
access:
action: "grant"
user: "new_user"
right: "R"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,15 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "old_rg_name"
# or
#rg_id: 1737
account_id: 99
rename: "new_rg_name"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,17 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
access:
action: "revoke"
user: "old_user"
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,16 @@
- hosts: localhost
tasks:
- name: create
decort_rg:
authenticator: oauth2
controller_url: "https://cloud.digitalenergy.online"
rg_name: "rg_created_by_module"
# or
#rg_id: 999
account_id: 99
def_netType: "PRIVATE"
def_netId: 199
state: present
verify_ssl: false
register: my_rg
delegate_to: localhost

View File

@@ -0,0 +1,14 @@
---
#
# This playbook create engine "test".
#
- hosts: localhost
tasks:
- hashivault_secret_engine:
url: "https://vault.domain.local"
authtype: ldap
username: "user"
password: "p@ssword"
state: present
name: test
backend: generic

View File

@@ -0,0 +1,17 @@
---
#
# This playbook create secret "secret" with data foo:foe. If secret "secret" exists - add data foo:foe.
#
- hosts: localhost
tasks:
- hashivault_secret:
url: "https://vault.domain.local"
authtype: ldap
username: "user"
password: "p@ssword"
mount_point: "kv"
state: present
permanent: true
secret: secret
data:
foo: foe

View File

@@ -0,0 +1,35 @@
---
- hosts: localhost
tasks:
- hashivault_read:
url: "https://vault.domain.local"
authtype: ldap
username: "user"
password: "p@ssword"
mount_point: kv
secret: secrets/myaccount
key: app_secret
version: 2
register: key
- name: create a VM using app_secret from hashicorp vault
decort_kvmvm:
annotation: "VM managed by decort_kvmvm module"
authenticator: oauth2
app_id: "" # Application id from SSO Digital Energy
app_secret: "{{ key }}" # API key from SSO Digital Energy
controller_url: "https://cloud.digitalenergy.online"
name: hashivault_read_example
cpu: 2
ram: 2048
boot_disk: 10
image_name: "DECS Ubuntu 18.04 v1.2.3" #Name of OS image
networks:
- type: VINS
id: 99 #VINS id
tags: "Ansible hashivault_read example"
state: present
rg_id: 99 #Resource group id
delegate_to: localhost
register: simple_vm

View File

@@ -0,0 +1,31 @@
- hosts: localhost
tasks:
- name: Read a kv2 secret with kv mount point
vars:
ansible_hashi_vault_auth_method: ldap
ansible_hashi_vault_username: username
ansible_hashi_vault_password: pwd
ansible_hashi_vault_engine_mount_point: kv
ansible.builtin.set_fact:
response: "{{ lookup('community.hashi_vault.vault_kv2_get', 'secret', url='https://vault.domain.local') }}"
- name: create a VM using app_secret from hashicorp vault
decort_kvmvm:
annotation: "VM managed by decort_kvmvm module"
authenticator: oauth2
app_id: "" # Application id from SSO Digital Energy
app_secret: "{{ response.data.password }}" # API key from SSO Digital Energy
controller_url: "https://cloud.digitalenergy.online"
name: hashivault_read_example
cpu: 2
ram: 2048
boot_disk: 10
image_name: "DECS Ubuntu 18.04 v1.2.3" #Name of OS image
networks:
- type: VINS
id: 99 #VINS id
tags: "Ansible hashivault_read example"
state: present
rg_id: 99 #Resource group id
delegate_to: localhost
register: simple_vm

View File

@@ -0,0 +1,16 @@
- hosts: localhost
tasks:
- name: Get auth token from vault
set_fact:
login_data: "{{ lookup('community.hashi_vault.vault_login', url='https://vault.domain.local', auth_method='ldap', username='username', password='pwd') }}"
- name: Perform multiple kv2 reads with a single Vault login, showing the secrets
vars:
ansible_hashi_vault_auth_method: token
ansible_hashi_vault_token: '{{ login_data | community.hashi_vault.vault_login_token }}'
ansible_hashi_vault_engine_mount_point: kv
paths:
- secret
- secret2
ansible.builtin.debug:
msg: "{{ lookup('community.hashi_vault.vault_kv2_get', *paths, auth_method='token', url='https://vault.domain.local') }}"

View File

@@ -0,0 +1,18 @@
- hosts: localhost
tasks:
- name: Read a kv2 secret with the default mount point
vars:
ansible_hashi_vault_auth_method: ldap
ansible_hashi_vault_username: username
ansible_hashi_vault_password: pwd
ansible_hashi_vault_engine_mount_point: kv
ansible.builtin.set_fact:
response: "{{ lookup('community.hashi_vault.vault_kv2_get', 'secret', url='https://vault.domain.local') }}"
- name: Display the results
ansible.builtin.debug:
msg:
- "Secret: {{ response.secret }}"
- "Data: {{ response.data }} (contains secret data & metadata in kv2)"
- "Metadata: {{ response.metadata }}"
- "Full response: {{ response.raw }}"

View File

@@ -0,0 +1,13 @@
---
- hosts: localhost
tasks:
- hashivault_read:
url: "https://vault.domain.local"
authtype: ldap
username: "uset"
password: "p@ssword"
mount_point: kv
secret: secret
key: foo
version: 2
register: key

39
examples/kubernetes.yaml Normal file
View File

@@ -0,0 +1,39 @@
---
#
# DECORT k8s module example
#
- hosts: ansible_master
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "https://sso.digitalenergy.online"
validity: 1200
verify_ssl: false
register: token
delegate_to: localhost
- name: create a VM named cluster-test
decort_k8s:
state: present
started: True
getConfig: True
authenticator: jwt
jwt: "{{ token.jwt }}"
controller_url: "https://ds1.digitalenergy.online"
name: "cluster-test"
rg_id: 125
k8ci_id: 18
workers:
- name: wg1
ram: 1024
cpu: 10
disk: 10
num: 1
- name: wg2
ram: 1024
cpu: 10
disk: 10
num: 2
verify_ssl: false
delegate_to: localhost
register: kube

View File

@@ -0,0 +1,42 @@
---
#
# DECORT vins module example
#
- hosts: localhost
tasks:
- name: obtain JWT
decort_jwt:
oauth2_url: "https://sso.digitalenergy.online"
validity: 1200
register: my_jwt
delegate_to: localhost
- name: print out JWT
debug:
var: my_jwt.jwt
delegate_to: localhost
- name: Manage ViNS at resource group level
decort_vins:
authenticator: jwt
jwt: "{{ my_jwt.jwt }}"
controller_url: "https://cloud.digitalenergy.online"
vins_name: "vins_connected_by_decort_vins_module"
state: present
rg_id: 98
connect_to:
- type: VINS
id: 864
ipaddr: 192.168.5.66
netmask: 24
- type: VINS
id: 196
ipaddr: 192.168.9.133
netmask: 24
register: managed_vins
- name: print VINS facter
debug:
msg: "{{managed_vins.facts.password}}"
when: managed_vins.facts.password is defined

421
library/decort_account.py Normal file
View File

@@ -0,0 +1,421 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_account
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Iterable
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortAccount(DecortController):
OBJ = 'account'
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args()
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
access_emails=dict(
type='bool',
),
acl=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
'match',
'revoke',
'update',
],
default='update',
),
users=dict(
type='list',
required=True,
elements='dict',
options=dict(
rights=dict(
type='str',
choices=['R', 'RCX', 'ARCXDU'],
default='R',
),
id=dict(
type='str',
required=True,
),
),
),
),
),
id=dict(
type='int',
),
name=dict(
type='str',
),
quotas=dict(
type='dict',
options=dict(
cpu=dict(
type='int',
),
disks_size=dict(
type='int',
),
ext_traffic=dict(
type='int',
),
gpu=dict(
type='int',
),
public_ip=dict(
type='int',
),
ram=dict(
type='int',
),
),
),
state=dict(
type='str',
choices=[
'absent',
'absent_permanently',
'confirmed',
'disabled',
'present',
],
default='present',
),
sep_pools=dict(
type='list',
elements='dict',
options=dict(
sep_id=dict(
type='int',
required=True,
),
pool_names=dict(
type='list',
required=True,
elements='str',
),
),
),
description=dict(
type='str',
),
default_zone_id=dict(
type='int',
),
),
required_one_of=[
('id', 'name')
],
supports_check_mode=True,
)
def check_amodule_args(self):
"""
Additional Ansible Module arguments validation that
cannot be implemented using Ansible Argument spec.
"""
arg_state = self.aparams['state']
if 'absent' in arg_state:
# Parameters or combinations of parameters that can
# cause changing the object.
changing_params = [
'access_emails',
'acl',
['id', 'name'],
'quotas',
]
check_error = False
for elem in changing_params:
if isinstance(elem, str):
param = elem
if self.aparams[elem] is not None:
self.message(
f'If the parameter "state" is set to'
f' "{arg_state}", then using the parameter'
f' "{param}" is not allowed.'
)
check_error = True
elif isinstance(elem, Iterable):
params = elem
params_using = map(
lambda x: self.aparams[x] is not None, params
)
if all(params_using):
params_str = ', '.join(f'"{p}"' for p in params)
self.message(
f'If the parameter "state" is set to'
f' "{arg_state}", then using the combination'
f' of parameters {params_str} are not allowed.'
)
check_error = True
if check_error:
self.exit(fail=True)
def check_amodule_args_for_change(self):
check_error = False
if self.check_aparam_default_zone_id() is False:
check_error = True
if check_error:
self.exit(fail=True)
def run(self):
self.get_info()
self.check_amodule_args_for_change()
self.change()
self.exit()
def get_info(self):
# If this is the first getting info
if self._acc_info is None:
self.acc_id, self._acc_info = self.account_find(
account_name=self.aparams['name'],
account_id=self.aparams['id'],
)
# If this is a repeated getting info
else:
# If check mode is enabled, there is no needed to
# request info again
if not self.amodule.check_mode:
self.acc_id, self._acc_info = self.account_find(
account_id=self.acc_id,
)
self.facts = self.acc_info
def change(self):
self.change_state()
self.change_acl()
if self.account_update_args:
self.account_update(account_id=self.acc_id,
**self.account_update_args)
self.get_info()
def change_state(self):
match self._acc_info:
case None:
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
match self.aparams:
case {'state': 'absent' | 'absent_permanently'}:
pass
case {'state': 'confirmed' | 'disabled' | 'present'}:
self.exit(fail=True)
case {'status': 'DESTROYED'}:
match self.aparams:
case {'state': 'absent' | 'absent_permanently'}:
self.message(
self.MESSAGES.obj_deleted(
obj=self.OBJ,
id=self.acc_id,
permanently=True,
already=True,
)
)
case {'state': 'confirmed' | 'disabled' | 'present'}:
self.message(
self.MESSAGES.obj_not_restored(obj=self.OBJ,
id=self.acc_id)
)
self.exit(fail=True)
case {'status': 'DELETED'}:
match self.aparams:
case {'state': 'absent'}:
self.message(
self.MESSAGES.obj_deleted(
obj=self.OBJ,
id=self.acc_id,
permanently=False,
already=True,
)
)
case {'state': 'absent_permanently'}:
self.delete(permanently=True)
case {'state': 'confirmed' | 'present'}:
self.restore()
case {'state': 'disabled'}:
self.restore()
self.disable()
case {'status': 'CONFIRMED'}:
match self.aparams:
case {'state': 'absent'}:
self.delete()
case {'state': 'absent_permanently'}:
self.delete(permanently=True)
case {'state': 'confirmed' | 'present'}:
pass
case {'state': 'disabled'}:
self.disable()
case {'status': 'DISABLED'}:
match self.aparams:
case {'state': 'absent'}:
self.delete()
case {'state': 'absent_permanently'}:
self.delete(permanently=True)
case {'state': 'confirmed'}:
self.enable()
case {'state': 'present' | 'disabled'}:
pass
def delete(self, permanently=False):
self.account_delete(account_id=self.acc_id, permanently=permanently)
self.get_info()
def disable(self):
self.account_disable(account_id=self.acc_id)
self.get_info()
def enable(self):
self.account_enable(account_id=self.acc_id)
self.get_info()
def restore(self):
self.account_restore(account_id=self.acc_id)
self.get_info()
def change_acl(self):
if not self.aparams['acl']:
return
actual_users = {
u['userGroupId']: u['right'] for u in self.acc_info['acl']
}
actual_users_ids = set(actual_users.keys())
aparams_acl = self.aparams['acl']
aparams_users = {u['id']: u['rights'] for u in aparams_acl['users']}
aparams_users_ids = set(aparams_users.keys())
del_users_ids = None
upd_users = None
new_users = None
match aparams_acl:
case {'mode': 'revoke'}:
del_users_ids = aparams_users_ids.intersection(
actual_users_ids,
)
case {'mode': 'update' | 'match' as mode}:
new_users_ids = aparams_users_ids.difference(
actual_users_ids,
)
new_users = dict(
u for u in aparams_users.items() if u[0] in new_users_ids
)
upd_users_ids =\
aparams_users_ids.intersection(actual_users_ids)
upd_users = dict()
for id in upd_users_ids:
if actual_users[id] == 'CXDRAU':
actual_user_rights = 'ARCXDU'
else:
actual_user_rights = actual_users[id]
if actual_user_rights != aparams_users[id]:
upd_users[id] = aparams_users[id]
if mode == 'match':
del_users_ids =\
actual_users_ids.difference(aparams_users_ids)
if del_users_ids or new_users or upd_users:
self.account_change_acl(account_id=self.acc_id,
del_users=del_users_ids,
add_users=new_users,
upd_users=upd_users)
self.get_info()
@property
def account_update_args(self) -> dict:
result_args = dict()
aparam_access_emails = self.aparams['access_emails']
if (aparam_access_emails is not None
and self.acc_info['sendAccessEmails'] != aparam_access_emails):
result_args['access_emails'] = aparam_access_emails
aparam_name = self.aparams['name']
if (self.aparams['id'] and aparam_name
and self.acc_info['name'] != aparam_name):
result_args['name'] = aparam_name
aparam_quotas = self.aparams['quotas']
if aparam_quotas:
quotas_naming = [
['cpu', 'CU_C', 'cpu_quota'],
['disks_size', 'CU_DM', 'disks_size_quota'],
['ext_traffic', 'CU_NP', 'ext_traffic_quota'],
['gpu', 'gpu_units', 'gpu_quota'],
['public_ip', 'CU_I', 'public_ip_quota'],
['ram', 'CU_M', 'ram_quota'],
]
for aparam, info_key, result_arg in quotas_naming:
current_value = int(self.acc_info['resourceLimits'][info_key])
if (aparam_quotas[aparam] is not None
and current_value != aparam_quotas[aparam]):
result_args[result_arg] = aparam_quotas[aparam]
aparam_sep_pools = self.aparams['sep_pools']
if aparam_sep_pools is not None:
sep_pools = set()
for sep in aparam_sep_pools:
for pool_name in sep['pool_names']:
sep_pools.add(
f'{sep["sep_id"]}_{pool_name}'
)
if set(self.acc_info['uniqPools']) != sep_pools:
result_args['sep_pools'] = sep_pools
aparam_desc = self.aparams['description']
if (
aparam_desc is not None
and self.acc_info['description'] != aparam_desc
):
result_args['description'] = aparam_desc
aparam_default_zone_id = self.aparams['default_zone_id']
if (
aparam_default_zone_id is not None
and self.acc_info['defaultZoneId'] != aparam_default_zone_id
):
result_args['default_zone_id'] = aparam_default_zone_id
return result_args
def check_aparam_default_zone_id(self) -> bool | None:
aparam_default_zone_id = self.aparams['default_zone_id']
if aparam_default_zone_id is not None:
if aparam_default_zone_id in self.acc_zone_ids:
return True
else:
self.message(
'Check for parameter "default_zone_id" failed: '
f'zone ID {aparam_default_zone_id} not available '
f'for account ID {self.acc_id}.'
)
return False
def main():
DecortAccount().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,573 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_account_info
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortAccountInfo(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
audits=dict(
type='bool',
default=False
),
computes=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_net_id=dict(
type='int',
),
ext_net_name=dict(
type='str'
),
id=dict(
type='int',
),
ip=dict(
type='str'
),
name=dict(
type='str'
),
rg_id=dict(
type='int',
),
rg_name=dict(
type='str'
),
tech_status=dict(
type='str',
choices=self.COMPUTE_TECH_STATUSES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_COMPUTE_LIST, # noqa: E501
required=True,
),
),
),
),
),
disks=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str',
),
size=dict(
type='int',
),
type=dict(
type='str',
choices=self.DISK_TYPES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_DISK_LIST, # noqa: E501
required=True,
),
),
),
),
),
flip_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_net_id=dict(
type='int',
),
id=dict(
type='int',
),
ip=dict(
type='str',
),
name=dict(
type='str',
),
vins_id=dict(
type='int',
),
vins_name=dict(
type='str',
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
),
),
id=dict(
type='int',
),
images=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str',
),
type=dict(
type='str',
choices=self.IMAGE_TYPES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_IMAGE_LIST, # noqa: E501
required=True,
),
),
),
),
),
name=dict(
type='str',
),
resource_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str'
),
status=dict(
type='str',
choices=self.RESOURCE_GROUP_STATUSES,
),
vins_id=dict(
type='int'
),
vm_id=dict(
type='int'
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_RG_LIST, # noqa: E501
required=True,
),
),
),
),
),
resource_consumption=dict(
type='bool',
default=False
),
vinses=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_ip=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str'
),
rg_id=dict(
type='int',
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_VINS_LIST, # noqa: E501
required=True,
),
),
),
),
),
),
mutually_exclusive=[
('id', 'name')
],
required_one_of=[
('id', 'name')
],
supports_check_mode=True,
)
@property
def mapped_computes_args(self) -> None | dict:
"""
Map the module argument `computes` to
arguments dictionary for the method
`DecortController.account_computes`
(excluding for `account_id`).
"""
input_args = self.aparams['computes']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['compute_id'] = input_args['filter']['id']
mapped_args['compute_ip'] = input_args['filter']['ip']
mapped_args['compute_name'] = input_args['filter']['name']
mapped_args['compute_tech_status'] =\
input_args['filter']['tech_status']
mapped_args['ext_net_id'] = input_args['filter']['ext_net_id']
mapped_args['ext_net_name'] =\
input_args['filter']['ext_net_name']
mapped_args['rg_id'] = input_args['filter']['rg_id']
mapped_args['rg_name'] = input_args['filter']['rg_name']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_disks_args(self) -> None | dict:
"""
Map the module argument `disks` to
arguments dictionary for the method
`DecortController.account_disks`
(excluding for `account_id`).
"""
input_args = self.aparams['disks']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['disk_id'] = input_args['filter']['id']
mapped_args['disk_name'] = input_args['filter']['name']
mapped_args['disk_size'] = input_args['filter']['size']
mapped_args['disk_type'] = input_args['filter']['type']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_flip_groups_args(self) -> None | dict:
"""
Map the module argument `flip_groups` to
arguments dictionary for the method
`DecortController.account_flip_groups`
(excluding for `account_id`).
"""
input_args = self.aparams['flip_groups']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['ext_net_id'] = input_args['filter']['ext_net_id']
mapped_args['flig_group_id'] = input_args['filter']['id']
mapped_args['flig_group_ip'] = input_args['filter']['ip']
mapped_args['flig_group_name'] = input_args['filter']['name']
mapped_args['vins_id'] = input_args['filter']['vins_id']
mapped_args['vins_name'] = input_args['filter']['vins_name']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
return mapped_args
@property
def mapped_images_args(self) -> None | dict:
"""
Map the module argument `images` to
arguments dictionary for the method
`DecortController.account_images`
(excluding for `account_id`).
"""
input_args = self.aparams['images']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['image_id'] = input_args['filter']['id']
mapped_args['image_name'] = input_args['filter']['name']
mapped_args['image_type'] = input_args['filter']['type']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_rg_args(self) -> None | dict:
"""
Map the module argument `resource_groups` to
arguments dictionary for the method
`DecortController.account_resource_groups`
(excluding for `account_id`).
"""
input_args = self.aparams['resource_groups']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['rg_id'] =\
input_args['filter']['id']
mapped_args['rg_name'] =\
input_args['filter']['name']
mapped_args['rg_status'] =\
input_args['filter']['status']
mapped_args['vins_id'] =\
input_args['filter']['vins_id']
mapped_args['vm_id'] =\
input_args['filter']['vm_id']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_vinses_args(self) -> None | dict:
"""
Map the module argument `vinses` to
arguments dictionary for the method
`DecortController.account_vinses`
(excluding for `account_id`).
"""
input_args = self.aparams['vinses']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['vins_id'] = input_args['filter']['id']
mapped_args['vins_name'] = input_args['filter']['name']
mapped_args['ext_ip'] = input_args['filter']['ext_ip']
mapped_args['rg_id'] = input_args['filter']['rg_id']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.id, self.facts = self.account_find(
account_name=self.aparams['name'],
account_id=self.aparams['id'],
audits=self.aparams['audits'],
computes_args=self.mapped_computes_args,
disks_args=self.mapped_disks_args,
flip_groups_args=self.mapped_flip_groups_args,
images_args=self.mapped_images_args,
resource_consumption=self.aparams['resource_consumption'],
resource_groups_args=self.mapped_rg_args,
vinses_args=self.mapped_vinses_args,
fail_if_not_found=True,
)
def main():
DecortAccountInfo().run()
if __name__ == '__main__':
main()

334
library/decort_bservice.py Normal file
View File

@@ -0,0 +1,334 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_bservice
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_bservice(DecortController):
def __init__(self):
super(decort_bservice, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
validated_acc_id = 0
validated_rg_id = 0
self.bservice_info = None
self.is_bservice_stopped_or_will_be_stopped: None | bool = None
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot manage Basic Services when its ID is 0 and name is empty."
self.fail_json(**self.result)
if not arg_amodule.params['id']:
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
if not validated_acc_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Current user does not have access to the account ID {} / "
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
arg_amodule.params['account_name'])
self.fail_json(**self.result)
# fail the module -> exit
# now validate RG
validated_rg_id, validated_rg_facts = self.rg_find(
arg_account_id=validated_acc_id,
arg_rg_id=arg_amodule.params['rg_id'],
arg_rg_name=arg_amodule.params['rg_name']
)
if not validated_rg_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
arg_amodule.params['rg_name'])
self.fail_json(**self.result)
arg_amodule.params['rg_id'] = validated_rg_id
arg_amodule.params['rg_name'] = validated_rg_facts['name']
validated_acc_id = validated_rg_facts['accountId']
self.bservice_id, self.bservice_info = self.bservice_find(
validated_acc_id,
validated_rg_id,
arg_amodule.params['name'],
arg_amodule.params['id']
)
self.acc_id = validated_acc_id or self.bservice_info['accountId']
if self.bservice_id and self.bservice_info['status'] != 'DESTROYED':
self.bservice_should_exist = True
self.check_amodule_args_for_change()
else:
self.bservice_should_exist = False
self.check_amodule_args_for_create()
def nop(self):
"""No operation (NOP) handler for B-service.
This function is intended to be called from the main switch construct of the module
when current state -> desired state change logic does not require any changes to
the actual Compute state.
"""
self.result['failed'] = False
self.result['changed'] = False
if self.bservice_id:
self.result['msg'] = ("No state change required for B-service ID {} because of its "
"current status '{}'.").format(self.bservice_id, self.bservice_info['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent B-service instance.").format(self.amodule.params['state'])
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.bservice_id:
self.result['msg'] = ("Invalid target state '{}' requested for B-service ID {} in the "
"current status '{}'.").format(self.bservice_id,
self.amodule.params['state'],
self.bservice_info['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent B-service name '{}' "
"in RG ID {} / name '{}'").format(self.amodule.params['state'],
self.amodule.params['name'],
self.amodule.params['rg_id'],
self.amodule.params['rg_name'])
return
def create(self):
self.bservice_id = self.bservice_id = self.bservice_provision(
self.amodule.params['name'],
self.amodule.params['rg_id'],
self.amodule.params['sshuser'],
self.amodule.params['sshkey'],
zone_id=self.aparams['zone_id'],
)
if self.bservice_id:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self.bservice_state(self.bservice_info, self.aparams['state'])
return
def action(self,d_state):
self.bservice_state(self.bservice_info,d_state)
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zoneId']:
self.bservice_migrate_to_zone(
bs_id=self.bservice_id,
zone_id=aparam_zone_id,
)
return
def restore(self):
self.result['failed'] = True
self.result['msg'] = "Restore B-Service ID {} manualy.".format(self.bservice_id)
pass
def destroy(self):
self.bservice_delete(self.bservice_id)
self.bservice_info['status'] = 'DELETED'
self.bservice_should_exist = False
return
def package_facts(self,check_mode=False):
ret_dict = dict(
name="",
state="CHECK_MODE",
account_id=0,
rg_id=0,
config=None,
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
ret_dict['id'] = self.bservice_info['id']
ret_dict['name'] = self.bservice_info['name']
ret_dict['techStatus'] = self.bservice_info['techStatus']
ret_dict['state'] = self.bservice_info['status']
ret_dict['rg_id'] = self.bservice_info['rgId']
ret_dict['account_id'] = self.bservice_info['accountId']
ret_dict['groups'] = self.bservice_info['groups']
ret_dict['zone_id'] = self.bservice_info['zoneId']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
default='',
),
state=dict(
type='str',
choices=[
'absent',
'disabled',
'enabled',
'present',
'started',
'stopped',
],
),
name=dict(
type='str',
default='',
),
sshuser=dict(
type='str',
),
sshkey=dict(
type='str',
),
id=dict(
type='int',
default=0,
),
rg_id=dict(
type='int',
default=0,
),
rg_name=dict(
type='str',
default='',
),
zone_id=dict(
type='int',
),
),
supports_check_mode=True,
required_one_of=[
('id', 'name'),
('rg_id', 'rg_name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
self.is_bservice_stopped_or_will_be_stopped = (
(
self.bservice_info['techStatus'] == 'STOPPED'
and (
self.aparams['state'] is None
or self.aparams['state'] in ('present', 'stopped')
)
)
or (
self.bservice_info['techStatus'] != 'STOPPED'
and self.aparams['state'] == 'stopped'
)
)
if self.check_aparam_zone_id() is False:
check_errors = True
if (
self.aparams['zone_id'] is not None
and self.aparams['zone_id'] != self.bservice_info['zoneId']
and not self.is_bservice_stopped_or_will_be_stopped
):
check_errors = True
self.message(
'Check for parameter "zone_id" failed: '
'Basic Service must be stopped to migrate to a zone.'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
if self.check_aparam_zone_id() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def main():
subj = decort_bservice()
amodule = subj.amodule
if subj.amodule.check_mode:
subj.result['changed'] = False
if subj.bservice_id:
subj.result['failed'] = False
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
# we exit the module at this point
else:
subj.result['failed'] = True
subj.result['msg'] = ("Cannot locate B-service name '{}'. Other arguments are: B-service ID {}, "
"RG name '{}', RG ID {}, Account '{}'.").format(amodule.params['name'],
amodule.params['id'],
amodule.params['rg_name'],
amodule.params['rg_id'],
amodule.params['account_name'])
amodule.fail_json(**subj.result)
pass
#MAIN MANAGE PART
if subj.bservice_id:
if subj.bservice_info['status'] in ("DELETING","DESTROYNG","RECONFIGURING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
subj.error()
elif subj.bservice_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
subj.restore(subj.bservice_id)
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
elif subj.bservice_info['status'] in (
'ENABLED', 'DISABLED', 'CREATED',
):
if amodule.params['state'] == 'absent':
subj.destroy()
else:
subj.action(amodule.params['state'])
elif subj.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
subj.create()
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
subj.nop()
if state in ('present','started'):
subj.create()
elif state in ('stopped', 'disabled','enabled'):
subj.error()
if subj.result['failed']:
amodule.fail_json(**subj.result)
else:
if subj.bservice_should_exist:
_, subj.bservice_info = subj.bservice_get_by_id(subj.bservice_id)
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
else:
amodule.exit_json(**subj.result)
if __name__ == "__main__":
main()

435
library/decort_disk.py Normal file
View File

@@ -0,0 +1,435 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_disk
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_disk(DecortController):
def __init__(self):
super(decort_disk, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
validated_acc_id = 0
validated_acc_info = None
self.disk_id = 0
self.account_id = 0
# limitIO check for exclusive parameters
if arg_amodule.params['limitIO']:
self.disk_check_iotune_arg(arg_amodule.params['limitIO'])
if not arg_amodule.params['id']:
if (
not arg_amodule.params['account_id']
and not arg_amodule.params['account_name']
):
self.result['changed'] = False
self.result['msg'] = (
'Cannot manage Disk by name without specifying account ID '
'or name.'
)
self.amodule.fail_json(**self.result)
validated_acc_id, validated_acc_info = self.account_find(
arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
self.acc_id = validated_acc_id
self._acc_info = validated_acc_info
if not validated_acc_id:
self.result['changed'] = False
self.result['msg'] = (
f"Current user does not have access to the account "
f"ID {arg_amodule.params['account_id']} / "
f"name '{arg_amodule.params['account_name']}' "
f"or non-existent account specified."
)
self.amodule.fail_json(**self.result)
validated_disk_id, validated_disk_facts = self.disk_find(
disk_id=arg_amodule.params['id'],
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
account_id=self.acc_id,
check_state=False,
)
if arg_amodule.params['place_with']:
image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0)
arg_amodule.params['sep_id'] = image_facts['sepId']
self.disk_id = validated_disk_id
self.disk_info = validated_disk_facts
if self.disk_id:
self.acc_id = validated_disk_facts['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
def create(self):
self.disk_id = self.disk_create(
accountId=self.acc_id,
name = self.amodule.params['name'],
description=self.amodule.params['description'],
size=self.amodule.params['size'],
sep_id=self.amodule.params['sep_id'],
pool=self.amodule.params['pool'],
storage_policy_id=self.aparams['storage_policy_id'],
)
#IO tune
if self.amodule.params['limitIO']:
self.disk_limitIO(disk_id=self.disk_id,
limits=self.amodule.params['limitIO'])
#set share status
if self.amodule.params['shareable']:
self.disk_share(self.disk_id,self.amodule.params['shareable'])
return
def action(self,restore=False):
#restore never be done
if restore:
self.disk_restore(self.disk_id)
#rename if id present
if (
self.amodule.params['name'] is not None
and self.amodule.params['name'] != self.disk_info['name']
):
self.disk_rename(disk_id=self.disk_id,
name=self.amodule.params['name'])
#resize
if (
self.amodule.params['size'] is not None
and self.amodule.params['size'] != self.disk_info['sizeMax']
):
self.disk_resize(self.disk_info,self.amodule.params['size'])
#IO TUNE
if self.amodule.params['limitIO']:
clean_io = [param for param in self.amodule.params['limitIO'] \
if self.amodule.params['limitIO'][param] == None]
for key in clean_io: del self.amodule.params['limitIO'][key]
if self.amodule.params['limitIO'] != self.disk_info['iotune']:
self.disk_limitIO(self.disk_id,self.amodule.params['limitIO'])
#share check/update
#raise Exception(self.amodule.params['shareable'])
if self.amodule.params['shareable'] != self.disk_info['shareable']:
self.disk_share(self.disk_id,self.amodule.params['shareable'])
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and aparam_storage_policy_id != self.disk_info['storage_policy_id']
):
self.disk_change_storage_policy(
disk_id=self.disk_id,
storage_policy_id=aparam_storage_policy_id,
)
return
def delete(self):
self.disk_delete(disk_id=self.disk_id,
detach=self.amodule.params['force_detach'],
permanently=self.amodule.params['permanently'],
reason=self.amodule.params['reason'])
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
return
def rename(self):
self.disk_rename(diskId = self.disk_id,
name = self.amodule.params['name'])
self.disk_info['name'] = self.amodule.params['name']
return
def nop(self):
self.result['failed'] = False
self.result['changed'] = False
if self.disk_id:
self.result['msg'] = ("No state change required for Disk ID {} because of its "
"current status '{}'.").format(self.disk_id, self.disk_info['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent Disk.").format(self.amodule.params['state'])
return
def package_facts(self, check_mode=False):
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
size=0,
account_id=0,
sep_id=0,
pool="none",
gid=0
)
if check_mode or self.disk_info is None:
return ret_dict
# remove io param with zero value
clean_io = [param for param in self.disk_info['iotune'] if self.disk_info['iotune'][param] == 0]
for key in clean_io: del self.disk_info['iotune'][key]
ret_dict['id'] = self.disk_info['id']
ret_dict['name'] = self.disk_info['name']
ret_dict['size'] = self.disk_info['sizeMax']
ret_dict['state'] = self.disk_info['status']
ret_dict['account_id'] = self.disk_info['accountId']
ret_dict['sep_id'] = self.disk_info['sepId']
ret_dict['pool'] = self.disk_info['pool']
ret_dict['computes'] = self.disk_info['computes']
ret_dict['gid'] = self.disk_info['gid']
ret_dict['iotune'] = self.disk_info['iotune']
ret_dict['size_available'] = self.disk_info['sizeAvailable']
ret_dict['size_used'] = self.disk_info['sizeUsed']
ret_dict['storage_policy_id'] = self.disk_info['storage_policy_id']
ret_dict['to_clean'] = self.disk_info['to_clean']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
default=0,
),
account_name=dict(
type='str',
default='',
),
description=dict(
type='str',
),
id=dict(
type='int',
default=0,
),
name=dict(
type='str',
),
force_detach=dict(
type='bool',
default=False,
),
place_with=dict(
type='int',
default=0,
),
pool=dict(
type='str',
default='',
),
sep_id=dict(
type='int',
default=0,
),
size=dict(
type='int',
),
limitIO=dict(
type='dict',
options=dict(
total_bytes_sec=dict(
type='int',
),
read_bytes_sec=dict(
type='int',
),
write_bytes_sec=dict(
type='int',
),
total_iops_sec=dict(
type='int',
),
read_iops_sec=dict(
type='int',
),
write_iops_sec=dict(
type='int',
),
total_bytes_sec_max=dict(
type='int',
),
read_bytes_sec_max=dict(
type='int',
),
write_bytes_sec_max=dict(
type='int',
),
total_iops_sec_max=dict(
type='int',
),
read_iops_sec_max=dict(
type='int',
),
write_iops_sec_max=dict(
type='int',
),
size_iops_sec=dict(
type='int',
),
),
),
permanently=dict(
type='bool',
default=False,
),
shareable=dict(
type='bool',
default=False,
),
reason=dict(
type='str',
default='Managed by Ansible decort_disk',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'present',
],
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
required_one_of=[
('id', 'name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
if self.check_aparam_storage_policy_id() is False:
check_errors = True
if self.check_aparam_size() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new disk'
)
if self.check_aparam_storage_policy_id() is False:
check_errors = True
if self.check_aparam_size() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_aparam_storage_policy_id(self) -> bool:
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
return not check_errors
def check_aparam_size(self) -> bool:
check_errors = False
aparam_size = self.aparams['size']
if (
aparam_size is not None
and aparam_size <= 0
):
check_errors = True
self.message(
msg=(
'Check for parameter "size" failed: '
f'Disk cannot be size {aparam_size}'
),
)
return not check_errors
def main():
decon = decort_disk()
amodule = decon.amodule
#
#Full range of Disk status is as follows:
#
# "ASSIGNED","MODELED", "CREATING","CREATED","DELETED", "DESTROYED","PURGED",
#
if decon.disk_id:
#disk exist
if decon.disk_info['status'] in ["MODELED", "CREATING"]:
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
"status '{}'").format(decon.disk_id, decon.disk_info['status'])
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
elif decon.disk_info['status'] in ["ASSIGNED","CREATED"]:
if amodule.params['state'] == 'absent':
decon.delete()
elif amodule.params['state'] == 'present':
decon.action()
elif decon.disk_info['status'] in ["PURGED", "DESTROYED"]:
#re-provision disk
if amodule.params['state'] in ('present'):
decon.create()
else:
decon.nop()
elif decon.disk_info['status'] == "DELETED":
if amodule.params['state'] in ('present'):
decon.action(restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
decon.delete()
else:
decon.nop()
else:
# preexisting Disk was not found
if amodule.params['state'] == 'absent':
decon.nop()
else:
decon.create()
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
if decon.result['changed']:
_, decon.disk_info = decon.disk_find(decon.disk_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()
#SHARE

461
library/decort_group.py Normal file
View File

@@ -0,0 +1,461 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_group
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_group(DecortController):
def __init__(self):
super(decort_group, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
self.group_should_exist = False
validated_bservice_id = None
#find and validate B-Service
validated_bservice_id, bservice_info = self.bservice_get_by_id(arg_amodule.params['bservice_id'])
if not validated_bservice_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Cannot find B-service ID {}.").format(arg_amodule.params['bservice_id'])
self.amodule.fail_json(**self.result)
#find group
self.bservice_id = validated_bservice_id
self.bservice_info = bservice_info
self.group_id,self.group_info = self.group_find(
bs_id=validated_bservice_id,
bs_info=bservice_info,
group_id=arg_amodule.params['id'],
group_name=arg_amodule.params['name'],
)
self.acc_id = self.bservice_info['accountId']
self.rg_id = self.bservice_info['rgId']
if self.group_id:
self.group_should_exist = True
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return
def nop(self):
"""No operation (NOP) handler for B-service.
This function is intended to be called from the main switch construct of the module
when current state -> desired state change logic does not require any changes to
the actual Compute state.
"""
self.result['failed'] = False
self.result['changed'] = False
if self.group_id:
self.result['msg'] = ("No state change required for B-service ID {} because of its "
"current status '{}'.").format(self.group_id, self.group_info['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent B-service instance.").format(self.amodule.params['state'])
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.group_id:
self.result['msg'] = ("Invalid target state '{}' requested for Group ID {} in the "
"current status '{}'.").format(self.group_id,
self.amodule.params['state'],
self.group_info['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent Group name '{}' "
"in B-service {}").format(self.amodule.params['state'],
self.amodule.params['name'],
self.amodule.params['bservice_id'],
)
return
def create(self):
chipset = self.aparams['chipset']
if chipset is None:
chipset = 'i440fx'
self.message(
msg=f'Chipset not specified, '
f'default value "{chipset}" will be used.',
warning=True,
)
driver = self.aparams['driver']
if driver is None:
driver = 'KVM_X86'
self.message(
msg=self.MESSAGES.default_value_used(
param_name='driver',
default_value=driver,
),
warning=True,
)
self.group_id=self.group_provision(
bs_id=self.bservice_id,
arg_name=self.amodule.params['name'],
arg_count=self.amodule.params['count'],
arg_cpu=self.amodule.params['cpu'],
arg_ram=self.amodule.params['ram'],
arg_boot_disk=self.amodule.params['boot_disk'],
arg_image_id=self.amodule.params['image_id'],
arg_role=self.amodule.params['role'],
arg_network=self.amodule.params['networks'],
arg_timeout=self.amodule.params['timeoutStart'],
chipset=chipset,
storage_policy_id=self.aparams['storage_policy_id'],
driver=driver,
)
if (
self.amodule.params['state'] in ('started','present')
and self.amodule.params['timeoutStart'] is None
):
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
self.group_should_exist = True
return
def action(self):
#change desired state
if (
self.group_info['techStatus'] == 'STARTED' and self.amodule.params['state'] == 'stopped') or (
self.group_info['techStatus'] == 'STOPPED' and self.amodule.params['state'] in ('started','present')
):
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
aparam_chipset = self.aparams['chipset']
if (
self.aparams['count'] is not None
and self.aparams['count'] != len(self.group_info['computes'])
):
self.group_resize_count(
bs_id=self.bservice_id,
gr_dict=self.group_info,
desired_count=self.aparams['count'],
chipset=aparam_chipset,
)
if aparam_chipset is not None:
for vm in self.group_info['computes']:
if vm['chipset'] != aparam_chipset:
self.compute_update(
compute_id=vm['id'],
chipset=aparam_chipset,
)
for aparam_name, info_key in {'cpu': 'cpu',
'boot_disk': 'disk',
'role': 'role',
'ram': 'ram',
'name': 'name',
}.items():
aparam_value = self.aparams[aparam_name]
group_info_value = self.group_info[info_key]
if aparam_value != None and aparam_value != group_info_value:
self.group_update(
bs_id=self.bservice_id,
gr_dict=self.group_info,
arg_cpu=self.aparams['cpu'],
arg_disk=self.aparams['boot_disk'],
arg_name=self.aparams['name'],
arg_role=self.aparams['role'],
arg_ram=self.aparams['ram'],
)
break
if self.aparams['networks'] != None:
self.group_update_net(
bs_id=self.bservice_id,
gr_dict=self.group_info,
arg_net=self.aparams['networks'],
)
def destroy(self):
self.group_delete(
self.bservice_id,
self.group_id
)
self.group_should_exist = False
return
def package_facts(self,check_mode=False):
ret_dict = dict(
name="",
state="CHECK_MODE",
account_id=0,
rg_id=0,
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
if self.result['changed'] == True:
self.group_id,self.group_info = self.group_find(
self.bservice_id,
self.bservice_info,
self.group_id
)
ret_dict['account_id'] = self.group_info['accountId']
ret_dict['rg_id'] = self.group_info['rgId']
ret_dict['id'] = self.group_info['id']
ret_dict['name'] = self.group_info['name']
ret_dict['techStatus'] = self.group_info['techStatus']
ret_dict['state'] = self.group_info['status']
ret_dict['Computes'] = self.group_info['computes']
ret_dict['driver'] = self.group_info['driver']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
default='',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'started',
'stopped',
'present',
'check',
],
),
name=dict(
type='str',
),
id=dict(
type='int',
),
image_id=dict(
type='int',
),
boot_disk=dict(
type='int',
),
bservice_id=dict(
type='int',
required=True,
),
count=dict(
type='int',
),
timeoutStart=dict(
type='int',
),
role=dict(
type='str',
),
cpu=dict(
type='int',
),
ram=dict(
type='int',
),
networks=dict(
type='list',
elements='dict',
options=dict(
type=dict(
type='str',
required=True,
choices=[
'VINS',
'EXTNET',
]
),
id=dict(
type='int',
required=True,
)
)
),
chipset=dict(
type='str',
choices=[
'Q35',
'i440fx',
]
),
storage_policy_id=dict(
type='int',
),
driver=dict(
type='str',
),
),
supports_check_mode=True,
required_one_of=[
('id', 'name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
if (
self.aparams['chipset'] is None
and (self.aparams['count'] if self.aparams['count'] is not None else 0) > len(self.group_info['computes'])
):
check_errors = True
self.message(
msg='Check for parameter "chipset" failed: '
'Chipset must be specified when increasing '
'VM count in group'
)
if (
self.aparams['count'] is None
or self.aparams['count'] == len(self.group_info['computes'])
):
aparam_chipset = self.aparams['chipset']
if aparam_chipset is not None:
for vm in self.group_info['computes']:
if (
vm['chipset'] != aparam_chipset
and self.group_info['techStatus'] != 'STOPPED'
and self.amodule.params['state'] != 'stopped'
):
check_errors = True
self.message(
msg=f'Check for parameter "chipset" failed: '
f'group ID {self.group_id} must be stopped '
f'to change chipset',
)
break
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
for compute in self.group_info['computes']:
_, compute_info, _ = self._compute_get_by_id(compute['id'])
for disk in compute_info['disks']:
if aparam_storage_policy_id != disk['storage_policy_id']:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" '
'failed: storage_policy_id can not be changed '
f'for compute ID {compute['id']} '
f'disk ID {disk['id']}'
)
aparam_driver = self.aparams['driver']
if (
aparam_driver is not None
and aparam_driver != self.group_info['driver']
):
check_errors = True
self.message(
msg='Check for parameter "driver" failed: '
'driver can not be changed'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new group'
)
else:
if (
aparam_storage_policy_id
not in self.rg_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'RG ID {self.rg_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if (
aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def main():
subj = decort_group()
amodule = subj.amodule
if amodule.params['state'] == 'check':
subj.result['changed'] = False
if subj.group_id:
# cluster is found - package facts and report success to Ansible
subj.result['failed'] = False
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
# we exit the module at this point
else:
subj.result['failed'] = True
subj.result['msg'] = ("Cannot locate Group name '{}'. "
"B-service ID {}").format(amodule.params['name'],
amodule.params['bservice_id'],)
amodule.fail_json(**subj.result)
if subj.group_id:
if subj.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED","DESTROYED"):
subj.error()
elif subj.group_info['status'] in ("DELETED","DESTROYED"):
if amodule.params['state'] == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started','stopped'):
subj.create()
elif subj.group_info['techStatus'] in ("STARTED","STOPPED"):
if amodule.params['state'] == 'absent':
subj.destroy()
else:
subj.action()
else:
if amodule.params['state'] == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started','stopped'):
subj.create()
if subj.result['failed']:
amodule.fail_json(**subj.result)
else:
if subj.group_should_exist:
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
else:
amodule.exit_json(**subj.result)
if __name__ == "__main__":
main()

38
library/decort_jwt.py Normal file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_jwt
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortJWT(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
@property
def amodule_init_args(self) -> dict:
amodule_init_args = self.common_amodule_init_args
amodule_argument_spec = amodule_init_args['argument_spec']
del amodule_argument_spec['controller_url']
del amodule_argument_spec['jwt']
amodule_argument_spec['authenticator']['choices'].remove('jwt')
return amodule_init_args
def run(self):
self.result['jwt'] = self.jwt
self.amodule.exit_json(**self.result)
def main():
DecortJWT().run()
if __name__ == '__main__':
main()

664
library/decort_k8s.py Normal file
View File

@@ -0,0 +1,664 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_k8s
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
from copy import deepcopy
class decort_k8s(DecortController):
def __init__(self):
super(decort_k8s, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
validated_acc_id = 0
validated_rg_id = 0
validated_rg_facts = None
validated_k8ci_id = 0
self.k8s_should_exist = False
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
self.wg_default_params = {
'num': 1,
'cpu': 1,
'ram': 1024,
'labels': [],
'taints': [],
'annotations': [],
'ci_user_data': {},
'chipset': 'i440fx',
}
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] is None:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot manage k8s cluster when its ID is 0 and name is empty."
self.amodule.fail_json(**self.result)
if arg_amodule.params['id'] is None:
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
validated_acc_id, _ = self.account_find(arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
if not validated_acc_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Current user does not have access to the account ID {} / "
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
arg_amodule.params['account_name'])
self.amodule.fail_json(**self.result)
# fail the module -> exit
# now validate RG
validated_rg_id, validated_rg_facts = self.rg_find(
arg_account_id=validated_acc_id,
arg_rg_id=arg_amodule.params['rg_id'],
arg_rg_name=arg_amodule.params['rg_name']
)
if not validated_rg_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
arg_amodule.params['rg_name'])
self.amodule.fail_json(**self.result)
# fail the module - exit
self.rg_id = validated_rg_id
arg_amodule.params['rg_id'] = validated_rg_id
arg_amodule.params['rg_name'] = validated_rg_facts['name']
self.acc_id = validated_rg_facts['accountId']
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
k8s_name=arg_amodule.params['name'],
rg_id=validated_rg_id,
check_state=False)
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
self.k8s_should_exist = True
self.acc_id = self.k8s_info['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return
def package_facts(self,check_mode=False):
ret_dict = dict(
name="",
state="CHECK_MODE",
account_id=0,
rg_id=0,
config=None,
)
if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED":
ret_dict['config'] = self.k8s_getConfig()
if check_mode:
# in check mode return immediately with the default values
return ret_dict
#if self.k8s_facts is None:
# #if void facts provided - change state value to ABSENT and return
# ret_dict['state'] = "ABSENT"
# return ret_dict
ret_dict['id'] = self.k8s_info['id']
ret_dict['name'] = self.k8s_info['name']
ret_dict['techStatus'] = self.k8s_info['techStatus']
ret_dict['state'] = self.k8s_info['status']
ret_dict['rg_id'] = self.k8s_info['rgId']
ret_dict['vins_id'] = self.k8s_vins_id
ret_dict['account_id'] = self.acc_id
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
ret_dict['lb_id'] = self.k8s_info['lbId']
ret_dict['description'] = self.k8s_info['desc']
ret_dict['zone_id'] = self.k8s_info['zoneId']
return ret_dict
def nop(self):
"""No operation (NOP) handler for k8s cluster management by decort_k8s module.
This function is intended to be called from the main switch construct of the module
when current state -> desired state change logic does not require any changes to
the actual k8s cluster state.
"""
self.result['failed'] = False
self.result['changed'] = False
if self.k8s_id:
self.result['msg'] = ("No state change required for K8s ID {} because of its "
"current status '{}'.").format(self.k8s_id, self.k8s_info['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent K8s instance.").format(self.amodule.params['state'])
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.k8s_id:
self.result['msg'] = ("Invalid target state '{}' requested for K8s cluster ID {} in the "
"current status '{}'.").format(self.k8s_id,
self.amodule.params['state'],
self.k8s_info['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent K8s Cluster name '{}' "
"in RG ID {} / name '{}'").format(self.amodule.params['state'],
self.amodule.params['name'],
self.amodule.params['rg_id'],
self.amodule.params['rg_name'])
return
def create(self):
master_chipset = self.amodule.params['master_chipset']
if master_chipset is None:
master_chipset = 'i440fx'
target_wgs = deepcopy(self.amodule.params['workers'])
for wg in target_wgs:
for param, default_value in self.wg_default_params.items():
if wg[param] is None:
wg[param] = default_value
k8s_id = self.k8s_provision(
self.amodule.params['name'],
self.amodule.params['k8ci_id'],
self.amodule.params['rg_id'],
self.amodule.params['vins_id'],
self.amodule.params['network_plugin'],
self.amodule.params['master_count'],
self.amodule.params['master_cpu'],
self.amodule.params['master_ram'],
self.amodule.params['master_disk'],
self.amodule.params['master_sepid'],
self.amodule.params['master_pool'],
target_wgs[0],
self.amodule.params['extnet_id'],
self.amodule.params['with_lb'],
self.amodule.params['ha_lb'],
self.amodule.params['additionalSANs'],
self.amodule.params['init_conf'],
self.amodule.params['cluster_conf'],
self.amodule.params['kublet_conf'],
self.amodule.params['kubeproxy_conf'],
self.amodule.params['join_conf'],
self.amodule.params['oidc_cert'],
self.amodule.params['description'],
self.amodule.params['extnet_only'],
master_chipset=master_chipset,
lb_sysctl=self.amodule.params['lb_sysctl'],
zone_id=self.aparams['zone_id'],
storage_policy_id=self.aparams['storage_policy_id'],
)
if not k8s_id:
if k8s_id == 0:
return
else:
self.result['failed'] = True
self.amodule.fail_json(**self.result)
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=k8s_id,
k8s_name=self.amodule.params['name'],
rg_id=self.rg_id,
check_state=False)
if self.k8s_id:
self.k8s_should_exist = True
if len(target_wgs) > 1:
self.k8s_workers_modify(
arg_k8swg=self.k8s_info,
arg_modwg=target_wgs,
)
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
return
def destroy(self):
self.k8s_delete(self.k8s_id,self.amodule.params['permanent'])
self.k8s_info['status'] = 'DELETED'
self.k8s_should_exist = False
return
def action(self, disared_state, preupdate: bool = False):
if self.amodule.params['master_chipset'] is not None:
for master_node in self.k8s_info['k8sGroups']['masters'][
'detailedInfo'
]:
_, master_node_info, _ = self._compute_get_by_id(
comp_id=master_node['id']
)
if (
master_node_info['chipset']
!= self.amodule.params['master_chipset']
):
self.result['msg'] = (
'"master_chipset" cannot be changed '
'for existing K8s cluster.'
)
self.exit(fail=True)
if (
self.aparams['name'] is not None
and self.aparams['name'] != self.k8s_info['name']
):
self.k8s_update(id=self.k8s_id, name=self.aparams['name'])
if preupdate:
# K8s info updating
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
#k8s state
self.k8s_state(self.k8s_info, disared_state)
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
#check groups and modify if needed
if self.aparams['workers'] is not None:
self.k8s_workers_modify(self.k8s_info, self.amodule.params['workers'])
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.k8s_info['zoneId']:
self.k8s_migrate_to_zone(
k8s_id=self.k8s_id,
zone_id=aparam_zone_id,
)
if self.result['changed'] == True:
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
#TODO check workers metadata and modify if needed
return
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
default='',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'disabled',
'enabled',
'present',
'started',
'stopped',
],
),
permanent=dict(
type='bool',
default=False,
),
name=dict(
type='str',
default='',
),
id=dict(
type='int',
),
getConfig=dict(
type='bool',
default=False,
),
rg_id=dict(
type='int',
default=0,
),
rg_name=dict(
type='str',
default='',
),
vins_id=dict(
type='int',
),
k8ci_id=dict(
type='int',
),
network_plugin=dict(
type='str',
default='flannel',
),
master_count=dict(
type='int',
default=1,
choices=[1, 3, 5, 7],
),
master_cpu=dict(
type='int',
default=2,
),
master_ram=dict(
type='int',
default=2048,
),
master_disk=dict(
type='int',
default=10,
),
master_sepid=dict(
type='int',
),
master_pool=dict(
type='str',
),
workers=dict(
type='list',
elements='dict',
options=dict(
name=dict(
type='str',
required=True,
),
num=dict(
type='int',
),
cpu=dict(
type='int',
),
ram=dict(
type='int',
),
disk=dict(
type='int',
),
sep_id=dict(
type='int',
),
pool=dict(
type='str',
),
annotations=dict(
type='list',
elements='str',
),
ci_user_data=dict(
type='dict',
),
labels=dict(
type='list',
elements='str',
),
taints=dict(
type='list',
elements='str',
),
chipset=dict(
type='str',
choices=['Q35', 'i440fx'],
),
),
),
workers_metadata=dict(
type='bool',
default=False,
),
extnet_id=dict(
type='int',
default=0,
),
description=dict(
type='str',
default='Created by decort ansible module',
),
with_lb=dict(
type='bool',
default=True,
),
ha_lb=dict(
type='bool',
default=False,
),
extnet_only=dict(
type='bool',
default=False,
),
additionalSANs=dict(
type='list',
),
init_conf=dict(
type='dict',
),
cluster_conf=dict(
type='dict',
),
kublet_conf=dict(
type='dict',
),
kubeproxy_conf=dict(
type='dict',
),
join_conf=dict(
type='dict',
),
oidc_cert=dict(
type='raw',
),
master_chipset=dict(
type='str',
choices=['Q35', 'i440fx'],
),
lb_sysctl=dict(
type='dict',
),
zone_id=dict(
type='int',
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
required_one_of=[
('id', 'name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
self.is_k8s_stopped_or_will_be_stopped = (
(
self.k8s_info['techStatus'] == 'STOPPED'
and (
self.aparams['state'] is None
or self.aparams['state'] in ('present', 'stopped')
)
)
or (
self.k8s_info['techStatus'] != 'STOPPED'
and self.aparams['state'] == 'stopped'
)
)
aparam_sysctl = self.aparams['lb_sysctl']
if aparam_sysctl is not None:
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
sysctl_with_str_values = {
k: str(v) for k, v in aparam_sysctl.items()
}
if sysctl_with_str_values != lb_info['sysctlParams']:
self.message(
'Check for parameter "lb_sysctl" failed: '
'cannot change lb_sysctl for an existing cluster '
'load balancer.'
)
check_errors = True
if self.check_aparam_zone_id() is False:
check_errors = True
if (
self.aparams['zone_id'] is not None
and self.aparams['zone_id'] != self.k8s_info['zoneId']
and not self.is_k8s_stopped_or_will_be_stopped
):
check_errors = True
self.message(
'Check for parameter "zone_id" failed: '
'K8s cluster must be stopped to migrate to a zone.'
)
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
computes_ids = []
for master_node in self.k8s_info['k8sGroups']['masters'][
'detailedInfo'
]:
computes_ids.append(master_node['id'])
for wg in self.k8s_info['k8sGroups']['workers']:
workers_ids = [
worker['id'] for worker in wg['detailedInfo']
]
computes_ids.extend(workers_ids)
for compute_id in computes_ids:
_, compute_info, _ = self._compute_get_by_id(
comp_id=compute_id
)
for disk in compute_info['disks']:
if aparam_storage_policy_id != disk['storage_policy_id']:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" '
'failed: storage_policy_id can not be changed '
f'for k8s cluster ID {self.k8s_id} compute ID '
f'{compute_id} disk ID {disk['id']}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
validated_k8ci_id = self.k8s_k8ci_find(self.aparams['k8ci_id'])
if not validated_k8ci_id:
self.message(f'Cannot find K8CI ID {"k8ci_id"}.')
check_errors = True
if not self.aparams['workers']:
self.message('At least one worker group must be present.')
check_errors = True
if (
self.aparams['lb_sysctl'] is not None
and not self.aparams['with_lb']
):
self.message(
'Check for parameter "lb_sysctl" failed: '
'"lb_sysctl" can only be set if the parameter "with_lb" '
'is set to True.'
)
check_errors = True
if (
self.aparams['master_count'] is not None
and self.aparams['master_count'] > 1
and not self.aparams['with_lb']
):
self.message(
'Check for parameter "master_count" failed: '
'master_count can be more than 1 only if the parameter '
'"with_lb" is set to True.'
)
check_errors = True
if self.check_aparam_zone_id() is False:
check_errors = True
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new cluster'
)
elif (
aparam_storage_policy_id
not in self.rg_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'RG ID {self.rg_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def main():
subj = decort_k8s()
amodule = subj.amodule
if subj.amodule.check_mode:
subj.result['changed'] = False
if subj.k8s_id:
# cluster is found - package facts and report success to Ansible
subj.result['failed'] = False
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
# we exit the module at this point
else:
subj.result['failed'] = True
subj.result['msg'] = ("Cannot locate K8s cluster name '{}'. "
"RG ID {}").format(amodule.params['name'],
amodule.params['rg_id'],)
amodule.fail_json(**subj.result)
if subj.k8s_id:
if subj.k8s_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
subj.error()
elif subj.k8s_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
subj.k8s_restore(subj.k8s_id)
subj.action(disared_state=amodule.params['state'],
preupdate=True)
if amodule.params['state'] == 'absent':
if amodule.params['permanent']:
subj.destroy()
else:
subj.nop()
elif subj.k8s_info['status'] in ('ENABLED', 'DISABLED'):
if amodule.params['state'] == 'absent':
subj.destroy()
else:
subj.action(disared_state=amodule.params['state'])
elif subj.k8s_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
subj.create()
if amodule.params['state'] == 'absent':
subj.nop()
else:
if amodule.params['state'] == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started'):
subj.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
subj.error()
if subj.result['failed']:
amodule.fail_json(**subj.result)
else:
if subj.k8s_should_exist:
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
else:
amodule.exit_json(**subj.result)
if __name__ == "__main__":
main()

2492
library/decort_kvmvm.py Normal file

File diff suppressed because it is too large Load Diff

416
library/decort_lb.py Normal file
View File

@@ -0,0 +1,416 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_lb
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_lb(DecortController):
def __init__(self) -> None:
super(decort_lb,self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
self.lb_id = 0
self.lb_facts = None
self.vins_id = 0
self.vins_facts = None
self.rg_id = 0
self.rg_facts = None
self.default_server_check = "enabled"
self.default_alg = "roundrobin"
self.default_settings = {
"downinter": 10000,
"fall": 2,
"inter": 5000,
"maxconn": 250,
"maxqueue": 256,
"rise": 2,
"slowstart": 60000,
"weight": 100,
}
self.is_lb_stopped_or_will_be_stopped: None | bool = None
if arg_amodule.params['lb_id']:
self.lb_id, self.lb_facts = self.lb_find(arg_amodule.params['lb_id'])
if not self.lb_id:
self.result['failed'] = True
self.result['msg'] = "Specified LB ID {} not found."\
.format(arg_amodule.params['lb_id'])
self.amodule.fail_json(**self.result)
self.rg_id = self.lb_facts['rgId']
self.vins_id = self.lb_facts['vinsId']
elif arg_amodule.params['rg_id']:
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
if not self.rg_id:
self.result['failed'] = True
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['rg_id'])
self.amodule.fail_json(**self.result)
self.acc_id = self.rg_facts['accountId']
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
if not arg_amodule.params['rg_name']:
self.result['failed'] = True
self.result['msg'] = ("RG name must be specified with account present")
self.amodule.fail_json(**self.result)
self.acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
if not self.acc_id:
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.amodule.fail_json(**self.result)
self.rg_id, self.rg_facts = self.rg_find(self.acc_id,0, arg_rg_name=arg_amodule.params['rg_name'])
if arg_amodule.params['vins_id']:
self.vins_id, self.vins_facts = self.vins_find(
vins_id=arg_amodule.params['vins_id']
)
if not self.vins_id:
self.result['failed'] = True
self.result['msg'] = (
f'Specified ViNS ID {arg_amodule.params["vins_id"]}'
f' not found'
)
self.amodule.fail_json(**self.result)
elif arg_amodule.params['vins_name']:
self.vins_id, self.vins_facts = self.vins_find(
vins_id=arg_amodule.params['vins_id'],
vins_name=arg_amodule.params['vins_name'],
rg_id=self.rg_id)
if not self.vins_id:
self.result['failed'] = True
self.result['msg'] = (
f'Specified ViNS name {arg_amodule.params["vins_name"]}'
f' not found in RG ID {self.rg_id}'
)
self.amodule.fail_json(**self.result)
if self.rg_id and arg_amodule.params['lb_name']:
self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id)
if self.lb_id and self.lb_facts['status'] != 'DESTROYED':
self.acc_id = self.lb_facts['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return
def create(self):
start_after_create = self.aparams['state'] != 'stopped'
self.lb_id = self.lb_provision(self.amodule.params['lb_name'],
self.rg_id,self.vins_id,
self.amodule.params['ext_net_id'],
self.amodule.params['ha_lb'],
self.amodule.params['description'],
sysctl=self.amodule.params['sysctl'],
zone_id=self.aparams['zone_id'],
start=start_after_create,
)
if self.lb_id and (self.amodule.params['backends'] or
self.amodule.params['frontends']):
self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id)
self.lb_update(
lb_facts=self.lb_facts,
aparam_backends=self.amodule.params['backends'],
aparam_frontends=self.amodule.params['frontends'],
aparam_servers=self.amodule.params['servers'],
)
return
def action(self,d_state='',restore=False):
if restore == True:
self.lb_restore(lb_id=self.lb_id)
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
self.lb_state(self.lb_facts, 'enabled')
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
self.lb_update(
lb_facts=self.lb_facts,
aparam_backends=self.amodule.params['backends'],
aparam_frontends=self.amodule.params['frontends'],
aparam_servers=self.amodule.params['servers'],
aparam_sysctl=self.aparams['sysctl'],
)
if d_state != '':
self.lb_state(self.lb_facts, d_state)
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
if (d_state == 'enabled' and
self.lb_facts.get('status') == 'ENABLED' and
self.lb_facts.get('techStatus') == 'STOPPED'):
self.lb_state(self.lb_facts, 'started')
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.lb_facts['zoneId']:
self.lb_migrate_to_zone(
lb_id=self.lb_id,
zone_id=aparam_zone_id,
)
return
def delete(self):
self.lb_delete(self.lb_id, self.amodule.params['permanently'])
self.lb_id, self.lb_facts = self._lb_get_by_id(self.lb_id)
return
def nop(self):
"""No operation (NOP) handler for LB management by decort_lb module.
This function is intended to be called from the main switch construct of the module
when current state -> desired state change logic does not require any changes to
the actual LB state.
"""
self.result['failed'] = False
self.result['changed'] = False
if self.lb_id:
self.result['msg'] = ("No state change required for LB ID {} because of its "
"current status '{}'.").format(self.lb_id, self.lb_facts['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent LB instance.").format(self.amodule.params['state'])
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.vins_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Invalid target state '{}' requested for LB ID {} in the "
"current status '{}'").format(self.lb_id,
self.amodule.params['state'],
self.lb_facts['status'])
else:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Invalid target state '{}' requested for non-existent "
"LB name '{}'").format(self.amodule.params['state'],
self.amodule.params['lb_name'])
return
def package_facts(self, arg_check_mode=False):
"""Package a dictionary of LB facts according to the decort_lb module specification.
This dictionary will be returned to the upstream Ansible engine at the completion of
the module run.
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
"""
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
sysctl={},
)
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict
if self.lb_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['id'] = self.lb_facts['id']
ret_dict['name'] = self.lb_facts['name']
ret_dict['state'] = self.lb_facts['status']
ret_dict['account_id'] = self.lb_facts['accountId']
ret_dict['rg_id'] = self.lb_facts['rgId']
ret_dict['gid'] = self.lb_facts['gid']
if self.amodule.params['state']!="absent":
ret_dict['backends'] = self.lb_facts['backends']
ret_dict['frontends'] = self.lb_facts['frontends']
ret_dict['sysctl'] = self.lb_facts['sysctlParams']
ret_dict['zone_id'] = self.lb_facts['zoneId']
ret_dict['tech_status'] = self.lb_facts['techStatus']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
default='',
),
description=dict(
type='str',
),
ext_net_id=dict(
type='int',
default=0,
),
state=dict(
type='str',
choices=[
'absent',
'disabled',
'enabled',
'present',
'restart',
'started',
'stopped',
],
),
rg_id=dict(
type='int',
default=0,
),
rg_name=dict(
type='str',
default='',
),
vins_name=dict(
type='str',
default='',
),
vins_id=dict(
type='int',
default=0,
),
lb_id=dict(
type='int',
default=0,
),
lb_name=dict(
type='str',
),
ha_lb=dict(
type='bool',
default=False,
),
backends=dict(
type='list',
),
frontends=dict(
type='list',
),
servers=dict(
type='list',
),
permanently=dict(
type='bool',
default=False,
),
sysctl=dict(
type='dict',
),
zone_id=dict(
type=int,
),
),
supports_check_mode=True,
required_one_of=[
('rg_id', 'rg_name'),
('lb_id', 'lb_name'),
('vins_id', 'vins_name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
lb_info: dict = self.lb_facts
self.is_lb_stopped_or_will_be_stopped = (
(
lb_info['techStatus'] == 'STOPPED'
and (
self.aparams['state'] is None
or self.aparams['state'] in ('present', 'stopped')
)
)
or (
lb_info['techStatus'] != 'STOPPED'
and self.aparams['state'] == 'stopped'
)
)
if self.check_aparam_zone_id() is False:
check_errors = True
if (
self.aparams['zone_id'] is not None
and self.aparams['zone_id'] != lb_info['zoneId']
and not self.is_lb_stopped_or_will_be_stopped
):
check_errors = True
self.message(
'Check for parameter "zone_id" failed: '
'Load balancer must be stopped to migrate to a zone.'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
if self.check_aparam_zone_id() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def main():
decon = decort_lb()
amodule = decon.amodule
if decon.lb_id:
if decon.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
"status '{}'").format(decon.lb_id, decon.lb_facts['status'])
elif decon.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
if amodule.params['state'] == 'absent':
decon.delete()
else:
decon.action(d_state=amodule.params['state'])
elif decon.lb_facts['status'] == "DELETED":
if amodule.params['state'] == 'present':
decon.action(restore=True)
elif amodule.params['state'] == 'enabled':
decon.action(d_state='enabled', restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
decon.delete()
elif amodule.params['state'] == 'disabled':
decon.error()
elif decon.lb_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
decon.create()
elif amodule.params['state'] == 'absent':
decon.nop()
elif amodule.params['state'] == 'disabled':
decon.error()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
decon.nop()
elif state in ('present', 'enabled', 'stopped', 'started'):
decon.create()
elif state == 'disabled':
decon.error()
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
if decon.result['changed']:
_, decon.lb_facts = decon.lb_find(lb_id=decon.lb_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

555
library/decort_osimage.py Normal file
View File

@@ -0,0 +1,555 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_osimage
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_osimage(DecortController):
def __init__(self):
super(decort_osimage, self).__init__(AnsibleModule(**self.amodule_init_args))
amodule = self.amodule
self.validated_image_id = 0
self.validated_virt_image_id = 0
self.validated_image_name = amodule.params['image_name']
self.validated_virt_image_name = None
self.image_info: dict
self.virt_image_info: dict
if amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
else:
self.validated_account_id = amodule.params['account_id']
if self.validated_account_id == 0:
# we failed either to find or access the specified account - fail the module
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result)
self.acc_id = self.validated_account_id
if (
self.aparams['virt_id'] != 0
or self.aparams['virt_name'] is not None
):
self.validated_virt_image_id, self.virt_image_info = (
self.decort_virt_image_find(amodule)
)
if self.virt_image_info:
_, linked_image_info = self._image_get_by_id(
image_id=self.virt_image_info['linkTo']
)
self.acc_id = linked_image_info['accountId']
if (
self.aparams['virt_name'] is not None
and self.aparams['virt_name']
!= self.virt_image_info['name']
):
self.decort_virt_image_rename(amodule)
self.result['msg'] = 'Virtual image renamed successfully'
elif (
self.aparams['image_id'] != 0
or self.aparams['image_name'] is not None
):
self.validated_image_id, self.image_info = (
self.decort_image_find(amodule)
)
if self.image_info:
self.acc_id = self.image_info['accountId']
if (
amodule.params['image_name']
and amodule.params['image_name'] != self.image_info['name']
):
decort_osimage.decort_image_rename(self,amodule)
self.result['msg'] = ("Image renamed successfully")
if self.validated_image_id:
self.check_amodule_args_for_change()
elif self.validated_virt_image_id:
self.check_amodule_args_for_change_virt_image()
elif self.aparams['virt_name']:
self.check_amodule_args_for_create_virt_image()
else:
self.check_amodule_args_for_create_image()
def decort_image_find(self, amodule):
# function that finds the OS image
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_virt_image_find(self, amodule):
# function that finds a virtual image
image_id, image_facts = self.virt_image_find(image_id=amodule.params['virt_id'],
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
virt_name=amodule.params['virt_name'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_image_create(self,amodule):
aparam_boot = self.aparams['boot']
boot_mode = 'bios'
loader_type = 'unknown'
if aparam_boot is not None:
if aparam_boot['mode'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.mode',
default_value=boot_mode
),
warning=True,
)
else:
boot_mode = aparam_boot['mode']
if aparam_boot['loader_type'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.loader_type',
default_value=loader_type
),
warning=True,
)
else:
loader_type = aparam_boot['loader_type']
network_interface_naming = self.aparams['network_interface_naming']
if network_interface_naming is None:
network_interface_naming = 'ens'
self.message(
msg=self.MESSAGES.default_value_used(
param_name='network_interface_naming',
default_value=network_interface_naming
),
warning=True,
)
hot_resize = self.aparams['hot_resize']
if hot_resize is None:
hot_resize = False
self.message(
msg=self.MESSAGES.default_value_used(
param_name='hot_resize',
default_value=hot_resize
),
warning=True,
)
# function that creates OS image
image_facts = self.image_create(
img_name=self.validated_image_name,
url=amodule.params['url'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
network_interface_naming=network_interface_naming,
storage_policy_id=amodule.params['storage_policy_id'],
)
self.result['changed'] = True
return image_facts
def decort_virt_image_link(self,amodule):
# function that links an OS image to a virtual one
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
decort_osimage.decort_osimage_package_facts(image_facts)['id'],)
return image_id, image_facts
def decort_image_delete(self,amodule):
# function that removes an image
self.image_delete(imageId=amodule.image_id_delete)
_, image_facts = decort_osimage._image_get_by_id(self, amodule.image_id_delete)
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
return
def decort_virt_image_create(self,amodule):
# function that creates a virtual image
image_facts = self.virt_image_create(
name=amodule.params['virt_name'],
target_id=self.target_image_id,
account_id=self.aparams['account_id'],
)
image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
return image_id, image_facts
def decort_image_rename(self,amodule):
# image renaming function
image_facts = self.image_rename(imageId=self.validated_image_id, name=amodule.params['image_name'])
self.result['msg'] = ("Image renamed successfully")
image_id, image_facts = decort_osimage.decort_image_find(self, amodule)
return image_id, image_facts
def decort_virt_image_rename(self, amodule):
image_facts = self.image_rename(imageId=self.validated_virt_image_id,
name=amodule.params['virt_name'])
self.result['msg'] = ("Virtual image renamed successfully")
image_id, image_facts = self.decort_virt_image_find(amodule)
return image_id, image_facts
@staticmethod
def decort_osimage_package_facts(
arg_osimage_facts: dict | None,
arg_check_mode=False,
):
"""Package a dictionary of OS image according to the decort_osimage module specification. This
dictionary will be returned to the upstream Ansible engine at the completion of the module run.
@param arg_osimage_facts: dictionary with OS image facts as returned by API call to .../images/list
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode.
@return: dictionary with OS image specs populated from arg_osimage_facts.
"""
ret_dict = dict(id=0,
name="none",
size=0,
type="none",
state="CHECK_MODE", )
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict
if arg_osimage_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['id'] = arg_osimage_facts['id']
ret_dict['name'] = arg_osimage_facts['name']
ret_dict['size'] = arg_osimage_facts['size']
# ret_dict['arch'] = arg_osimage_facts['architecture']
ret_dict['sep_id'] = arg_osimage_facts['sepId']
ret_dict['pool'] = arg_osimage_facts['pool']
ret_dict['state'] = arg_osimage_facts['status']
ret_dict['linkto'] = arg_osimage_facts['linkTo']
ret_dict['accountId'] = arg_osimage_facts['accountId']
ret_dict['boot_mode'] = arg_osimage_facts['bootType']
ret_dict['boot_loader_type'] = ''
match arg_osimage_facts['type']:
case 'cdrom' | 'virtual' as type:
ret_dict['type'] = type
case _ as boot_loader_type:
ret_dict['type'] = 'template'
ret_dict['boot_loader_type'] = boot_loader_type
ret_dict['network_interface_naming'] = arg_osimage_facts[
'networkInterfaceNaming'
]
ret_dict['hot_resize'] = arg_osimage_facts['hotResize']
ret_dict['storage_policy_id'] = arg_osimage_facts['storage_policy_id']
ret_dict['to_clean'] = arg_osimage_facts['to_clean']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
pool=dict(
type='str',
default='',
),
sep_id=dict(
type='int',
default=0,
),
account_name=dict(
type='str',
),
account_id=dict(
type='int',
),
image_name=dict(
type='str',
),
image_id=dict(
type='int',
default=0,
),
virt_id=dict(
type='int',
default=0,
),
virt_name=dict(
type='str',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'present',
],
),
url=dict(
type='str',
),
sepId=dict(
type='int',
default=0,
),
poolName=dict(
type='str',
),
hot_resize=dict(
type='bool',
),
image_username=dict(
type='str',
),
image_password=dict(
type='str',
),
usernameDL=dict(
type='str',
),
passwordDL=dict(
type='str',
),
boot=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
'bios',
'uefi',
],
),
loader_type=dict(
type='str',
choices=[
'windows',
'linux',
'unknown',
],
),
),
),
network_interface_naming=dict(
type='str',
choices=[
'ens',
'eth',
],
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
)
def check_amodule_args_for_change(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_change_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and (
aparam_storage_policy_id
!= self.virt_image_info['storage_policy_id']
)
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be changed in virtual image'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_image(self):
check_errors = False
aparam_account_id = self.aparams['account_id']
if aparam_account_id is None:
check_errors = True
self.message(
msg='Check for parameter "account_id" failed: '
'account_id must be specified when creating '
'a new image'
)
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new image'
)
elif (
aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be specified when creating '
'virtual image'
)
if check_errors:
self.exit(fail=True)
def main():
decon = decort_osimage()
amodule = decon.amodule
if amodule.params['virt_name'] or amodule.params['virt_id']:
image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule)
if amodule.params['image_name'] or amodule.params['image_id']:
decon.target_image_id, _ = decort_osimage.decort_image_find(decon, amodule)
else:
decon.target_image_id = 0
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0:
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
decon.validated_virt_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
decon.validated_virt_image_name = decort_osimage.decort_osimage_package_facts(image_facts)['name']
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id > 0:
image_id, image_facts = decort_osimage.decort_virt_image_create(decon,amodule)
decon.result['msg'] = ("Virtual image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id'])
decon.result['changed'] = True
elif decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id == 0:
decon.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**decon.result)
if decon.validated_virt_image_id:
if (
decon.target_image_id
and decort_osimage.decort_osimage_package_facts(image_facts)[
'linkto'
] != decon.target_image_id
):
decort_osimage.decort_virt_image_link(decon,amodule)
decon.result['changed'] = True
amodule.exit_json(**decon.result)
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
decon.image_change_storage_policy(
image_id=decon.validated_virt_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if amodule.params['state'] == "absent" and decon.validated_virt_image_id:
amodule.image_id_delete = decon.validated_virt_image_id
image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule)
if image_facts['status'] != 'PURGED':
decort_osimage.decort_image_delete(decon,amodule)
elif amodule.params['image_name'] or amodule.params['image_id']:
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0:
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
if amodule.params['state'] == "present" and decon.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']:
decort_osimage.decort_image_create(decon,amodule)
decon.result['changed'] = True
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
decon.result['msg'] = ("OS image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id'])
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and decon.validated_image_id:
amodule.image_id_delete = decon.validated_image_id
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
if image_facts['status'] != 'DESTROYED':
decort_osimage.decort_image_delete(decon,amodule)
if decon.validated_image_id:
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
decon.image_change_storage_policy(
image_id=decon.validated_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if decon.result['failed'] == True:
# we failed to find the specified image - fail the module
decon.result['changed'] = False
amodule.fail_json(**decon.result)
else:
if decon.validated_image_id:
_, image_facts = decon.decort_image_find(amodule=amodule)
elif decon.validated_virt_image_id:
_, image_facts = decon.decort_virt_image_find(amodule=amodule)
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(
arg_osimage_facts=image_facts,
arg_check_mode=amodule.check_mode,
)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

145
library/decort_pfw.py Normal file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_pfw
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import *
class decort_pfw(DecortController):
def __init__(self):
super(decort_pfw, self).__init__(AnsibleModule(**self.amodule_init_args))
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
compute_id=dict(
type='int',
required=True,
),
rules=dict(
type='list',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'present',
],
),
vins_id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
)
def decort_pfw_package_facts(self, comp_facts, vins_facts, pfw_facts, check_mode=False):
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
This dictionary will be returned to the upstream Ansible engine at the completion of
the module run.
@param (dict) pfw_facts: dictionary with PFW facts as returned by API call to .../???/get
@param (bool) check_mode: boolean that tells if this Ansible module is run in check mode
"""
ret_dict = dict(state="CHECK_MODE",
compute_id=0,
public_ip="",
rules=[],
vins_id=0,
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
if pfw_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['compute_id'] = comp_facts['id']
ret_dict['vins_id'] = vins_facts['id']
ret_dict['public_ip'] = vins_facts['vnfs']['GW']['config']['ext_net_ip']
if len(pfw_facts) != 0:
ret_dict['state'] = 'PRESENT'
ret_dict['rules'] = pfw_facts
else:
ret_dict['state'] = 'ABSENT'
return ret_dict
def decort_pfw_parameters(self):
"""Build and return a dictionary of parameters expected by decort_pfw module in a form accepted
by AnsibleModule utility class."""
return
def main():
decon = decort_pfw()
amodule = decon.amodule
pfw_facts = None # will hold PFW facts as returned by pfw_configure
#
# Validate module arguments:
# 1) specified Compute instance exists in correct state
# 2) specified ViNS exists
# 3) ViNS has GW function
# 4) Compute is connected to this ViNS
#
validated_comp_id, comp_facts, rg_id = decon.compute_find(amodule.params['compute_id'])
if not validated_comp_id:
decon.result['failed'] = True
decon.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
amodule.fail_json(**decon.result)
validated_vins_id, vins_facts = decon.vins_find(amodule.params['vins_id'])
if not validated_vins_id:
decon.result['failed'] = True
decon.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
amodule.fail_json(**decon.result)
gw_vnf_facts = vins_facts['vnfs'].get('GW')
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
decon.result['failed'] = True
decon.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
amodule.fail_json(**decon.result)
#
# Initial validation of module arguments is complete
#
if amodule.params['state'] == 'absent':
# ignore amodule.params['rules'] and remove all rules associated with this Compute
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, None)
elif amodule.params['rules'] is not None:
# manage PFW rules accodring to the module arguments
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
else:
pfw_facts = decon._pfw_get(comp_facts['id'], vins_facts['id'])
#
# complete module run
#
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
# prepare PFW facts to be returned as part of decon.result and then call exit_json(...)
decon.result['facts'] = decon.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

479
library/decort_rg.py Normal file
View File

@@ -0,0 +1,479 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_rg
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_rg(DecortController):
def __init__(self):
super(decort_rg, self).__init__(AnsibleModule(**self.amodule_init_args))
amodule = self.amodule
self.validated_acc_id = 0
self.validated_rg_id = 0
self.validated_rg_facts = None
if amodule.params['rg_id'] is None:
if self.amodule.params['account_id']:
self.validated_acc_id, _ = self.account_find("", amodule.params['account_id'])
elif amodule.params['account_name']:
self.validated_acc_id, _ = self.account_find(amodule.params['account_name'])
if not self.validated_acc_id:
# we failed to locate account by either name or ID - abort with an error
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.amodule.fail_json(**self.result)
# Check if the RG with the specified parameters already exists
self.get_info()
if amodule.params['state'] != "absent":
self.rg_should_exist = True
else:
self.rg_should_exist = False
if self.validated_rg_id and self.rg_facts['status'] != 'DESTROYED':
self.check_amodule_args_for_change()
def get_info(self):
# If this is the first getting info
if not self.validated_rg_id:
self.validated_rg_id, self.rg_facts = self.rg_find(
arg_account_id=self.validated_acc_id,
arg_rg_id=self.aparams['rg_id'],
arg_rg_name=self.aparams['rg_name'],
arg_check_state=False,
)
# If this is a repeated getting info
else:
# If check mode is enabled, there is no needed to
# request info again
if self.amodule.check_mode:
return
_, self.rg_facts = self.rg_find(arg_rg_id=self.validated_rg_id)
def access(self):
should_change_access = False
acc_granted = False
for rg_item in self.rg_facts['acl']:
if rg_item['userGroupId'] == self.amodule.params['access']['user']:
acc_granted = True
if self.amodule.params['access']['action'] == 'grant':
if rg_item['right'] != self.amodule.params['access']['right']:
should_change_access = True
if self.amodule.params['access']['action'] == 'revoke':
should_change_access = True
if acc_granted == False and self.amodule.params['access']['action'] == 'grant':
should_change_access = True
if should_change_access == True:
self.rg_access(self.validated_rg_id, self.amodule.params['access'])
self.rg_facts['access'] = self.amodule.params['access']
self.rg_should_exist = True
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.validated_rg_id > 0:
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
"current status '{}'.").format(self.validated_rg_id,
self.amodule.params['state'],
self.rg_facts['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
"in account ID {} ").format(self.amodule.params['state'],
self.amodule.params['rg_name'],
self.validated_acc_id)
return
def update(self):
resources = self.rg_facts['Resources']['Reserved']
incorrect_quota = dict(Requested=dict(),
Reserved=dict(),)
query_key_map = dict(
cpu='cpu',
ram='ram',
disk='disksize',
ext_ips='extips',
net_transfer='exttraffic',
storage_policies='policies',
)
if self.amodule.params['quotas']:
for quota_item in self.amodule.params['quotas']:
if quota_item == 'storage_policies':
rg_storage_policies = resources[query_key_map[quota_item]]
aparam_storage_policies = self.amodule.params['quotas'][
quota_item
]
for aparam_storage_policy in aparam_storage_policies:
aparam_storage_policy_id = aparam_storage_policy['id']
rg_storage_policy = rg_storage_policies.get(
str(aparam_storage_policy_id)
)
if (
rg_storage_policy
and aparam_storage_policy['storage_size_gb']
< rg_storage_policy['disksize']
):
incorrect_quota['Requested'][quota_item] = (
self.amodule.params['quotas'][quota_item]
)
incorrect_quota['Reserved'][quota_item] = (
resources[query_key_map[quota_item]]
)
elif (
self.amodule.params['quotas'][quota_item]
< resources[query_key_map[quota_item]]
):
incorrect_quota['Requested'][quota_item] = (
self.amodule.params['quotas'][quota_item]
)
incorrect_quota['Reserved'][quota_item] = (
resources[query_key_map[quota_item]]
)
if incorrect_quota['Requested']:
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
self.result['failed'] = True
if not self.result['failed']:
self.rg_update(
arg_rg_dict=self.rg_facts,
arg_quotas=self.amodule.params['quotas'],
arg_res_types=self.amodule.params['resType'],
arg_newname=self.amodule.params['rename'],
arg_sep_pools=self.amodule.params['sep_pools'],
arg_desc=self.amodule.params['description'],
)
self.rg_should_exist = True
return
def setDefNet(self):
rg_def_net_type = self.rg_facts['def_net_type']
rg_def_net_id = self.rg_facts['def_net_id']
aparam_def_net_type = self.aparams['def_netType']
aparam_def_net_id = self.aparams['def_netId']
need_to_reset = (aparam_def_net_type == 'NONE'
and rg_def_net_type != aparam_def_net_type)
need_to_change = False
if aparam_def_net_id is not None:
need_to_change = (aparam_def_net_id != rg_def_net_id
or aparam_def_net_type != rg_def_net_type)
if need_to_reset or need_to_change:
self.rg_setDefNet(
arg_rg_id=self.validated_rg_id,
arg_net_type=aparam_def_net_type,
arg_net_id=aparam_def_net_id,
)
self.rg_should_exist = True
return
def create(self):
self.validated_rg_id = self.rg_provision(
self.validated_acc_id,
self.amodule.params['rg_name'],
self.amodule.params['owner'],
self.amodule.params['description'],
self.amodule.params['resType'],
self.amodule.params['def_netType'],
self.amodule.params['ipcidr'],
self.amodule.params['extNetId'],
self.amodule.params['extNetIp'],
self.amodule.params['quotas'],
"", # this is location code. TODO: add module argument
sdn_access_group_id=self.aparams['sdn_access_group_id'],
)
if self.validated_rg_id:
self.validated_rg_id, self.rg_facts = self.rg_find(
arg_account_id=self.validated_acc_id,
arg_rg_id=self.validated_rg_id,
arg_rg_name="",
arg_check_state=False
)
self.rg_should_exist = True
return
def enable(self):
self.rg_enable(self.validated_rg_id,
self.amodule.params['state'])
if self.amodule.params['state'] == "enabled":
self.rg_facts['status'] = 'CREATED'
else:
self.rg_facts['status'] = 'DISABLED'
self.rg_should_exist = True
return
def restore(self):
self.rg_restore(self.validated_rg_id)
self.rg_facts['status'] = 'DISABLED'
self.rg_should_exist = True
return
def destroy(self):
self.rg_delete(
rg_id=self.validated_rg_id,
permanently=self.amodule.params['permanently'],
recursively=self.aparams['recursive_deletion'],
)
if self.amodule.params['permanently'] == True:
self.rg_facts['status'] = 'DESTROYED'
else:
self.rg_facts['status'] = 'DELETED'
self.rg_should_exist = False
return
def package_facts(self, check_mode=False):
"""Package a dictionary of RG facts according to the decort_rg module specification. This dictionary will
be returned to the upstream Ansible engine at the completion of the module run.
@param arg_rg_facts: dictionary with RG facts as returned by API call to .../rg/get
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
"""
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
#if arg_rg_facts is None:
# # if void facts provided - change state value to ABSENT and return
# ret_dict['state'] = "ABSENT"
# return ret_dict
ret_dict['id'] = self.rg_facts['id']
ret_dict['name'] = self.rg_facts['name']
ret_dict['state'] = self.rg_facts['status']
ret_dict['account_id'] = self.rg_facts['accountId']
ret_dict['gid'] = self.rg_facts['gid']
ret_dict['quota'] = self.rg_facts['resourceLimits']
ret_dict['resTypes'] = self.rg_facts['resourceTypes']
ret_dict['defNetId'] = self.rg_facts['def_net_id']
ret_dict['defNetType'] = self.rg_facts['def_net_type']
ret_dict['ViNS'] = self.rg_facts['vins']
ret_dict['computes'] = self.rg_facts['vms']
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
ret_dict['description'] = self.rg_facts['desc']
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
ret_dict['storage_policy_ids'] = self.rg_facts['storage_policy_ids']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
default='',
),
access=dict(
type='dict',
),
description=dict(
type='str',
default='',
),
def_netType=dict(
type='str',
choices=[
'PRIVATE',
'PUBLIC',
'NONE',
],
default='PRIVATE',
),
def_netId=dict(
type='int',
),
extNetId=dict(
type='int',
default=0,
),
extNetIp=dict(
type='str',
default='',
),
owner=dict(
type='str',
default='',
),
ipcidr=dict(
type='str',
default='',
),
rename=dict(
type='str',
default='',
),
quotas=dict(
type='dict',
),
resType=dict(
type='list',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'disabled',
'enabled',
'present',
],
),
permanently=dict(
type='bool',
default='False',
),
rg_name=dict(
type='str',
),
rg_id=dict(
type='int',
),
sep_pools=dict(
type='list',
elements='dict',
options=dict(
sep_id=dict(
type='int',
required=True,
),
pool_names=dict(
type='list',
required=True,
elements='str',
),
),
),
recursive_deletion=dict(
type='bool',
default=False,
),
sdn_access_group_id=dict(
type='str',
),
),
supports_check_mode=True,
)
def check_amodule_args_for_change(self):
check_errors = False
if (
self.aparams['sdn_access_group_id'] is not None
and (
self.aparams['sdn_access_group_id']
!= self.rg_facts['sdn_access_group_id']
)
):
self.message(
'Check for parameter "sdn_access_group_id" failed: '
'cannot change sdn_access_group_id for an existing resource '
f'group ID {self.validated_rg_id}.'
)
check_errors = True
if check_errors:
self.exit(fail=True)
# Workflow digest:
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController
# 2) check if the RG with the specified id or rg_name:name exists
# 3) if RG does not exist -> deploy
# 4) if RG exists: check desired state, desired configuration -> initiate action accordingly
# 5) report result to Ansible
def main():
decon = decort_rg()
amodule = decon.amodule
#amodule.check_mode=True
if decon.validated_rg_id > 0:
if decon.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
decon.error()
elif decon.rg_facts['status'] in ("CREATED"):
if amodule.params['state'] == 'absent':
decon.destroy()
elif amodule.params['state'] == "disabled":
decon.enable()
if amodule.params['state'] in ['present', 'enabled']:
if (
amodule.params['quotas']
or amodule.params['resType']
or amodule.params['rename'] != ""
or amodule.params['sep_pools'] is not None
or amodule.params['description'] is not None
):
decon.update()
if amodule.params['access']:
decon.access()
if amodule.params['def_netType'] is not None:
decon.setDefNet()
elif decon.rg_facts['status'] == "DELETED":
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
decon.destroy()
elif (amodule.params['state'] == 'present'
or amodule.params['state'] == 'disabled'):
decon.restore()
elif amodule.params['state'] == 'enabled':
decon.restore()
decon.enable()
elif decon.rg_facts['status'] in ("DISABLED"):
if amodule.params['state'] == 'absent':
decon.destroy()
elif amodule.params['state'] == ("enabled"):
decon.enable()
else:
if amodule.params['state'] in ('present', 'enabled'):
if not amodule.params['rg_name']:
decon.result['failed'] = True
decon.result['msg'] = (
'Resource group could not be created because'
' the "rg_name" parameter was not specified.'
)
else:
decon.create()
if amodule.params['access'] and not amodule.check_mode:
decon.access()
elif amodule.params['state'] in ('disabled'):
decon.error()
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
if decon.rg_should_exist:
if decon.result['changed']:
decon.get_info()
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
else:
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,351 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_security_group
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortSecurityGroup(DecortController):
id: int = 0
facts: dict = dict()
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
rules=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
e.value
for e in self.SecurityGroupRuleMode
],
default=self.SecurityGroupRuleMode.update.value,
),
objects=dict(
type='list',
elements='dict',
required=True,
options=dict(
direction=dict(
type='str',
choices=[
e.name for e in
self.SecurityGroupRuleDirection
],
required=True,
),
ethertype=dict(
type='str',
choices=[
e.name for e in
self.SecurityGroupRuleEtherType
],
),
id=dict(
type='int',
),
port_range=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
protocol=dict(
type='str',
choices=[
e.name for e in
self.SecurityGroupRuleProtocol
],
),
remote_ip_prefix=dict(
type='str',
),
),
),
),
),
state=dict(
type='str',
choices=[e.value for e in self.SecurityGroupState],
),
),
supports_check_mode=True,
)
def run(self):
if self.aparams['id'] is not None:
self.id = self.aparams['id']
elif self.aparams['name'] is not None:
security_group = self.security_group_find(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
)
if security_group:
self.id = security_group['id']
if self.id:
self.get_info()
self.check_amodule_args_for_change()
self.change()
else:
self.check_amodule_args_for_create()
self.create()
if not self.amodule.check_mode:
self.change_rules()
if self.result['changed']:
self.get_info()
self.exit()
def get_info(self):
self.facts: dict = self.security_group_get(id=self.id)
self.facts['created_timestamp'] = self.facts.pop('created_at')
self.facts['updated_timestamp'] = self.facts.pop('updated_at')
for rule in self.facts['rules']:
rule['port_range'] = {
'min': rule.pop('port_range_min'),
'max': rule.pop('port_range_max'),
}
def check_amodule_args_for_create(self):
check_errors = False
aparam_account_id = self.aparams['account_id']
if aparam_account_id is None:
check_errors = True
self.message(
msg='Check for parameter "account_id" failed: '
'account_id must be specified when creating '
'a new security group'
)
aparam_name = self.aparams['name']
if aparam_name is None:
check_errors = True
self.message(
msg='Check for parameter "name" failed: '
'name must be specified when creating '
'a new security group'
)
aparam_state = self.aparams['state']
if (
aparam_state is not None
and aparam_state == self.SecurityGroupState.absent.value
):
check_errors = True
self.message(
msg='Check for parameter "state" failed: '
'state can not be "absent" when creating '
'a new security group'
)
if self.check_aparam_rules(for_create=True) is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_change(self):
check_errors = False
if self.check_aparam_rules() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_aparam_rules(self, for_create: bool = False):
check_errors = False
aparam_rules = self.aparams['rules']
if aparam_rules is None:
return True
mode = aparam_rules['mode']
rules = aparam_rules['objects']
if for_create and mode == self.SecurityGroupRuleMode.delete.value:
check_errors = True
self.message(
msg='Check for parameter "rules.mode" failed: '
'mode can not be "delete" when creating '
'new security group'
)
sg_rules_ids = []
if self.facts.get('rules'):
sg_rules_ids = [rule['id'] for rule in self.facts['rules']]
for rule in rules:
rule_id = rule.get('id')
if rule_id is not None:
if for_create:
check_errors = True
self.message(
msg='Check for parameter "rules.objects.id" failed: '
'can not set rule id when creating new '
'security group'
)
elif rule_id not in sg_rules_ids:
check_errors = True
self.message(
msg='Check for parameter "rules.objects.id" failed: '
f'rule ID {rule_id} not found for '
f'security group ID {self.id}'
)
if mode == self.SecurityGroupRuleMode.delete.value:
for param, value in rule.items():
if param != 'id' and value is not None:
check_errors = True
self.message(
msg='Check for parameter "rules.objects" '
'failed: only rule id can be specified if'
'rules.mode is "delete"'
)
break
elif mode == self.SecurityGroupRuleMode.delete.value:
check_errors = True
self.message(
msg='Check for parameter "rules.objects" '
'failed: rule id must be specified if mode is delete'
)
return not check_errors
def create(self):
security_groups_by_account_id = self.user_security_groups(
account_id=self.aparams['account_id']
)
sg_names = [sg['name'] for sg in security_groups_by_account_id]
if self.aparams['name'] not in sg_names:
self.id = self.security_group_create(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
description=self.aparams['description'],
)
def change(self):
self.change_state()
self.change_params()
self.change_rules()
def change_state(self):
if self.aparams['state'] == self.SecurityGroupState.absent.value:
self.delete()
def change_params(self):
aparam_name = self.aparams['name']
aparam_description = self.aparams['description']
new_name, new_description = None, None
if (
aparam_name is not None
and aparam_name != self.facts['name']
):
new_name = aparam_name
if (
aparam_description is not None
and aparam_description != self.facts['description']
):
new_description = aparam_description
if new_name or new_description:
self.security_group_update(
security_group_id=self.id,
name=new_name,
description=new_description,
)
def change_rules(self):
aparam_rules = self.aparams['rules']
if aparam_rules is not None:
rules = aparam_rules['objects']
match aparam_rules['mode']:
case self.SecurityGroupRuleMode.delete.value:
for rule in rules:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule['id'],
)
case self.SecurityGroupRuleMode.match.value:
for rule in rules:
if rule.get('id') is None:
self.create_rule(rule=rule)
sg_rules_ids = set(
[rule['id'] for rule in self.facts['rules']]
)
aparam_rules_ids = set(
[rule['id'] for rule in rules if rule.get('id')]
)
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
for rule_id in rules_ids_to_delete:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule_id,
)
case self.SecurityGroupRuleMode.update.value:
for rule in rules:
if rule.get('id') is None:
self.create_rule(rule=rule)
def delete(self):
self.security_group_detele(security_group_id=self.id)
self.facts = {}
self.exit()
def create_rule(self, rule: dict):
port_range_min, port_range_max = None, None
if rule.get('port_range'):
port_range_min = rule['port_range'].get('min')
port_range_max = rule['port_range'].get('max')
self.security_group_create_rule(
security_group_id=self.id,
direction=self.SecurityGroupRuleDirection[rule['direction']],
ethertype=(
self.SecurityGroupRuleEtherType[rule['ethertype']]
if rule.get('ethertype') else None
),
protocol=(
self.SecurityGroupRuleProtocol[rule['protocol']]
if rule.get('protocol') else None
),
port_range_min=port_range_min,
port_range_max=port_range_max,
remote_ip_prefix=rule.get('remote_ip_prefix'),
)
def main():
DecortSecurityGroup().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,50 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_storage_policy
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortStoragePolicy(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.id: int = self.aparams['id']
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
)
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.storage_policy_get(id=self.id)
self.facts['sep_pools'] = self.facts.pop('access_seps_pools')
self.facts['iops_limit'] = self.facts.pop('limit_iops')
self.facts['usage']['account_ids'] = self.facts['usage'].pop(
'accounts'
)
self.facts['usage']['rg_ids'] = self.facts['usage'].pop('resgroups')
def main():
DecortStoragePolicy().run()
if __name__ == '__main__':
main()

51
library/decort_trunk.py Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_trunk
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortTrunk(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.id: int = self.aparams['id']
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
)
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.trunk_get(id=self.id)
self.facts['account_ids'] = self.facts.pop('accountIds')
self.facts['created_timestamp'] = self.facts.pop('created_at')
self.facts['deleted_timestamp'] = self.facts.pop('deleted_at')
self.facts['updated_timestamp'] = self.facts.pop('updated_at')
self.facts['native_vlan_id'] = self.facts.pop('nativeVlanId')
self.facts['ovs_bridge'] = self.facts.pop('ovsBridge')
self.facts['vlan_ids'] = self.facts.pop('trunkTags')
def main():
DecortTrunk().run()
if __name__ == '__main__':
main()

879
library/decort_user_info.py Normal file
View File

@@ -0,0 +1,879 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_user_info
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortUserInfo(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args()
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
accounts=dict(
type='dict',
options=dict(
deleted=dict(
type='bool',
default=False,
),
filter=dict(
type='dict',
options=dict(
rights=dict(
type='str',
choices=[
e.value for e in self.AccountUserRights
],
),
id=dict(
type='int',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=[
e.value for e in self.AccountStatus
],
),
zone_id=dict(
type='int',
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
resource_consumption=dict(
type='bool',
default=False,
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.value
for e in self.AccountSortableField
],
required=True,
),
),
),
),
),
api_methods=dict(
type='bool',
default=False,
),
audits=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
api_method=dict(
type='str',
),
status_code=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
time=dict(
type='dict',
options=dict(
start=dict(
type='dict',
options=dict(
timestamp=dict(
type='int',
),
datetime=dict(
type='str',
),
),
mutually_exclusive=[
('timestamp', 'datetime'),
],
),
end=dict(
type='dict',
options=dict(
timestamp=dict(
type='int',
),
datetime=dict(
type='str',
),
),
mutually_exclusive=[
('timestamp', 'datetime'),
],
),
),
),
),
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
default=50,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.value
for e in self.AuditsSortableField
],
required=True,
),
),
),
),
),
objects_search=dict(
type='str',
),
resource_consumption=dict(
type='bool',
default=False,
),
zones=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
deletable=dict(
type='bool',
),
description=dict(
type='str',
),
grid_id=dict(
type='int',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
node_id=dict(
type='int',
),
status=dict(
type='str',
choices=[
e.value for e in self.ZoneStatus
],
),
),
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
default=50,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.ZoneField._member_names_,
required=True,
),
),
),
),
),
trunks=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ids=dict(
type='list',
),
account_ids=dict(
type='list',
),
status=dict(
type='str',
choices=[
e.value for e in self.TrunkStatus
],
),
vlan_ids=dict(
type='list',
),
),
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
default=50,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.value
for e in self.TrunksSortableField
],
required=True,
),
),
),
),
),
storage_policies=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
iops_limit=dict(
type='int',
),
name=dict(
type='str',
),
pool_name=dict(
type='str',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
status=dict(
type='str',
choices=[
e.value for e
in self.StoragePolicyStatus
],
),
),
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
default=50,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.value for e
in self.StoragePoliciesSortableField
],
required=True,
),
),
),
),
),
security_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
account_id=dict(
type='int',
),
created_timestamp_max=dict(
type='int',
),
created_timestamp_min=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
updated_timestamp_max=dict(
type='int',
),
updated_timestamp_min=dict(
type='int',
),
),
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
default=50,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.name for e
in self.SecurityGroupSortableField
],
required=True,
),
),
),
),
),
),
supports_check_mode=True,
)
def check_amodule_args(self):
"""
Additional validation of Ansible Module arguments.
This validation cannot be implemented using
Ansible Argument spec.
"""
check_error = False
match self.aparams['audits']:
case {
'filter': {'time': {'start': {'datetime': str() as dt_str}}}
}:
if self.dt_str_to_sec(dt_str=dt_str) is None:
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
check_error = True
match self.aparams['audits']:
case {
'filter': {'time': {'end': {'datetime': str() as dt_str}}}
}:
if self.dt_str_to_sec(dt_str=dt_str) is None:
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
check_error = True
aparam_trunks = self.aparams['trunks']
if (
aparam_trunks is not None
and aparam_trunks['filter'] is not None
and aparam_trunks['filter']['vlan_ids'] is not None
):
for vlan_id in aparam_trunks['filter']['vlan_ids']:
if not (
self.TRUNK_VLAN_ID_MIN_VALUE
<= vlan_id
<= self.TRUNK_VLAN_ID_MAX_VALUE
):
check_error = True
self.message(
'Check for parameter "trunks.filter.vlan_ids" failed: '
f'VLAN ID {vlan_id} must be in range 1-4095.'
)
if check_error:
self.exit(fail=True)
@property
def mapped_accounts_args(self) -> None | dict:
"""
Map the module argument `accounts` to
arguments dictionary for the method
`DecortController.user_accounts`.
"""
input_args = self.aparams['accounts']
if not input_args:
return input_args
mapped_args = {}
mapped_args['deleted'] = input_args['deleted']
mapped_args['resource_consumption'] = (
input_args['resource_consumption']
)
input_args_filter = input_args['filter']
if input_args_filter:
input_args_filter_rights = input_args_filter['rights']
if input_args_filter_rights:
mapped_args['account_user_rights'] = (
self.AccountUserRights(input_args_filter_rights)
)
mapped_args['account_id'] = input_args_filter['id']
mapped_args['account_name'] = input_args_filter['name']
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['account_status'] = (
self.AccountStatus(input_args_filter_status)
)
mapped_args['zone_id'] = input_args_filter['zone_id']
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.AccountSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_audits_args(self):
"""
Map the module argument `audits` to
arguments dictionary for the method
`DecortController.user_audits`.
"""
input_args = self.aparams['audits']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args['api_method'] = input_args_filter['api_method']
match input_args_filter['status_code']:
case {'min': int() as min_status_code}:
mapped_args['min_status_code'] = min_status_code
match input_args_filter['status_code']:
case {'max': int() as max_status_code}:
mapped_args['max_status_code'] = max_status_code
match input_args_filter['time']:
case {'start': {'timestamp': int() as start_unix_time}}:
mapped_args['start_unix_time'] = start_unix_time
case {'start': {'datetime': str() as start_dt_str}}:
mapped_args['start_unix_time'] = self.dt_str_to_sec(
dt_str=start_dt_str
)
match input_args_filter['time']:
case {'end': {'timestamp': int() as end_unix_time}}:
mapped_args['end_unix_time'] = end_unix_time
case {'end': {'datetime': str() as end_dt_str}}:
mapped_args['end_unix_time'] = self.dt_str_to_sec(
dt_str=end_dt_str
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.AuditsSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_zones_args(self):
"""
Map the module argument `zones` to
arguments dictionary for the method
`DecortController.user_zones`.
"""
input_args = self.aparams['zones']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['status'] = (
self.ZoneStatus(input_args_filter_status)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.ZoneField._member_map_[input_args_sorting_field]
)
return mapped_args
@property
def mapped_trunks_args(self):
"""
Map the module argument `trunks` to
arguments dictionary for the method
`DecortController.user_trunks`.
"""
input_args = self.aparams['trunks']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['status'] = (
self.TrunkStatus(input_args_filter_status)
)
input_args_filter_vlan_ids = input_args_filter['vlan_ids']
if input_args_filter_vlan_ids is not None:
mapped_args['vlan_ids'] = ', '.join(
map(str, input_args_filter_vlan_ids)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.TrunksSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_storage_policies_args(self):
"""
Map the module argument `storage_policies` to
arguments dictionary for the method
`DecortController.user_storage_policies`.
"""
input_args = self.aparams['storage_policies']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['status'] = (
self.StoragePolicyStatus(input_args_filter_status)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.StoragePoliciesSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_security_groups_args(self):
"""
Map the module argument `security_groups` to
arguments dictionary for the method
`DecortController.user_security_groups`.
"""
input_args = self.aparams['security_groups']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.SecurityGroupSortableField[input_args_sorting_field]
)
return mapped_args
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.user_whoami()
self.id = self.facts['name']
user_get = self.user_get(id=self.id)
for key in ['emailaddresses', 'data']:
self.facts[key] = user_get[key]
if self.aparams['accounts']:
self.facts['accounts'] = self.user_accounts(
**self.mapped_accounts_args,
)
if self.aparams['resource_consumption']:
self.facts.update(self.user_resource_consumption())
if self.aparams['audits']:
self.facts['audits'] = self.user_audits(**self.mapped_audits_args)
if self.aparams['api_methods']:
self.facts['api_methods'] = self.user_api_methods(id=self.id)
search_string = self.aparams['objects_search']
if search_string:
self.facts['objects_search'] = self.user_objects_search(
search_string=search_string,
)
if self.aparams['zones']:
self.facts['zones'] = self.user_zones(**self.mapped_zones_args)
if self.aparams['trunks']:
self.facts['trunks'] = self.user_trunks(**self.mapped_trunks_args)
for trunk_facts in self.facts['trunks']:
trunk_facts['account_ids'] = trunk_facts.pop('accountIds')
trunk_facts['created_timestamp'] = trunk_facts.pop(
'created_at'
)
trunk_facts['deleted_timestamp'] = trunk_facts.pop(
'deleted_at'
)
trunk_facts['updated_timestamp'] = trunk_facts.pop(
'updated_at'
)
trunk_facts['native_vlan_id'] = trunk_facts.pop(
'nativeVlanId'
)
trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge')
trunk_facts['vlan_ids'] = trunk_facts.pop('trunkTags')
if self.aparams['storage_policies']:
self.facts['storage_policies'] = self.user_storage_policies(
**self.mapped_storage_policies_args
)
for storage_policy_facts in self.facts['storage_policies']:
storage_policy_facts['sep_pools'] = storage_policy_facts.pop(
'access_seps_pools'
)
storage_policy_facts['iops_limit'] = storage_policy_facts.pop(
'limit_iops'
)
storage_policy_facts['usage']['account_ids'] = (
storage_policy_facts['usage'].pop('accounts')
)
storage_policy_facts['usage']['rg_ids'] = (
storage_policy_facts['usage'].pop('resgroups')
)
if self.aparams['security_groups']:
self.facts['security_groups'] = self.user_security_groups(
**self.mapped_security_groups_args
)
for security_groups_facts in self.facts['security_groups']:
for rule in security_groups_facts.get('rules', []):
rule['port_range'] = {
'min': rule.pop('port_range_min'),
'max': rule.pop('port_range_max'),
}
security_groups_facts['created_timestamp'] = (
security_groups_facts.pop('created_at')
)
security_groups_facts['created_timestamp_readable'] = (
self.sec_to_dt_str(security_groups_facts[
'created_timestamp'
])
)
security_groups_facts['updated_timestamp'] = (
security_groups_facts.pop('updated_at')
)
security_groups_facts['updated_timestamp_readable'] = (
self.sec_to_dt_str(security_groups_facts[
'updated_timestamp'
])
)
def main():
DecortUserInfo().run()
if __name__ == '__main__':
main()

466
library/decort_vins.py Normal file
View File

@@ -0,0 +1,466 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vins
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_vins(DecortController):
def __init__(self):
super(decort_vins, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
self.vins_id = 0
self.vins_level = "" # "ID" if specified by ID, "RG" - at resource group, "ACC" - at account level
vins_facts = None # will hold ViNS facts
validated_rg_id = 0
rg_facts = None # will hold RG facts
validated_acc_id = 0
if arg_amodule.params['vins_id']:
# expect existing ViNS with the specified ID
# This call to vins_find will abort the module if no ViNS with such ID is present
self.vins_id, self.vins_facts = self.vins_find(arg_amodule.params['vins_id'],check_state=False)
if self.vins_id == 0:
self.result['failed'] = True
self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id'])
self.amodule.fail_json(**self.result)
self.vins_level = "ID"
#raise Exception(self.vins_facts)
validated_acc_id = self.vins_facts['accountId']
validated_rg_id = self.vins_facts['rgId']
elif arg_amodule.params['rg_id']:
# expect ViNS @ RG level in the RG with specified ID
self.vins_level = "RG"
# This call to rg_find will abort the module if no RG with such ID is present
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
arg_amodule.params['rg_id'], arg_rg_name="")
validated_acc_id = rg_facts['accountId']
# This call to vins_find may return vins_id=0 if no ViNS found
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
account_id=0,
rg_id=arg_amodule.params['rg_id'],
rg_facts=rg_facts,
check_state=False)
# TODO: add checks and setup ViNS presence flags accordingly
pass
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
# Specified account must be present and accessible by the user, otherwise abort the module
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'], arg_amodule.params['account_id'])
if not validated_acc_id:
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.amodule.fail_json(**self.result)
if arg_amodule.params['rg_name'] != "": # at this point we know that rg_id=0
# expect ViNS @ RG level in the RG with specified name under specified account
# RG with the specified name must be present under the account, otherwise abort the module
validated_rg_id, rg_facts = self.rg_find(validated_acc_id, 0, arg_amodule.params['rg_name'])
if (not validated_rg_id or
rg_facts['status'] in ["DESTROYING", "DESTROYED", "DELETING", "DELETED", "DISABLING", "ENABLING"]):
self.result['failed'] = True
self.result['msg'] = "RG name '{}' not found or has invalid state.".format(arg_amodule.params['rg_name'])
self.amodule.fail_json(**self.result)
# This call to vins_find may return vins_id=0 if no ViNS with this name found under specified RG
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
account_id=0, # set to 0, as we are looking for ViNS under RG
rg_id=validated_rg_id,
rg_facts=rg_facts,
check_state=False)
self.vins_level = "RG"
# TODO: add checks and setup ViNS presence flags accordingly
else: # At this point we know for sure that rg_name="" and rg_id=0
# So we expect ViNS @ account level
# This call to vins_find may return vins_id=0 if no ViNS found
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
account_id=validated_acc_id,
rg_id=0,
rg_facts=rg_facts,
check_state=False)
self.vins_level = "ACC"
# TODO: add checks and setup ViNS presence flags accordingly
else:
# this is "invalid arguments combination" sink
# if we end up here, it means that module was invoked with vins_id=0 and rg_id=0
self.result['failed'] = True
self.result['msg'] = "Cannot find ViNS by name"
if arg_amodule.params['account_id'] == 0 and arg_amodule.params['account_name'] == '':
self.result['msg'] = "Cannot find ViNS by name when account name is empty and account ID is 0."
if arg_amodule.params['rg_name'] == "":
# rg_name without account specified
self.result['msg'] = "Cannot find ViNS by name when RG name is empty and RG ID is 0."
self.amodule.fail_json(**self.result)
return
self.rg_id = validated_rg_id
self.acc_id = validated_acc_id
if self.vins_id and self.vins_facts['status'] != 'DESTROYED':
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return
def create(self):
self.vins_id = self.vins_provision(self.amodule.params['vins_name'],
self.acc_id, self.rg_id,
self.amodule.params['ipcidr'],
self.amodule.params['ext_net_id'], self.amodule.params['ext_ip_addr'],
self.amodule.params['description'],
zone_id=self.amodule.params['zone_id'],
)
if self.amodule.params['mgmtaddr'] or self.amodule.params['connect_to']:
_, self.vins_facts = self.vins_find(self.vins_id)
if self.amodule.params['connect_to']:
self.vins_update_ifaces(self.vins_facts,self.amodule.params['connect_to'],)
if self.amodule.params['mgmtaddr']:
self.vins_update_mgmt(self.vins_facts,self.amodule.params['mgmtaddr'])
return
def action(self,d_state='',restore=False):
if restore == True:
self.vins_restore(arg_vins_id=self.vins_id)
self.vins_state(self.vins_facts, 'enabled')
self.vins_facts['status'] = "ENABLED"
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
self.vins_update_extnet(self.vins_facts,
self.amodule.params['ext_net_id'],
self.amodule.params['ext_ip_addr'],
)
if d_state == 'enabled' and self.vins_facts['status'] == "DISABLED":
self.vins_state(self.vins_facts, d_state)
self.vins_facts['status'] = "ENABLED"
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
d_state = ''
if self.vins_facts['status'] == "ENABLED" and self.vins_facts['VNFDev']['techStatus'] == "STARTED":
self.vins_update_ifaces(self.vins_facts,
self.amodule.params['connect_to'],
)
if self.result['changed']:
_, self.vins_facts = self.vins_find(self.vins_id)
self.vins_update_mgmt(self.vins_facts,
self.amodule.params['mgmtaddr'],
)
if d_state != '':
self.vins_state(self.vins_facts, d_state)
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.vins_facts['zoneId']:
self.vins_migrate_to_zone(
net_id=self.vins_id,
zone_id=aparam_zone_id,
)
return
def delete(self):
self.vins_delete(self.vins_id, self.amodule.params['permanently'])
self.vins_facts['status'] = 'DESTROYED'
return
def nop(self):
"""No operation (NOP) handler for ViNS management by decort_vins module.
This function is intended to be called from the main switch construct of the module
when current state -> desired state change logic does not require any changes to
the actual ViNS state.
"""
self.result['failed'] = False
self.result['changed'] = False
if self.vins_id:
self.result['msg'] = ("No state change required for ViNS ID {} because of its "
"current status '{}'.").format(self.vins_id, self.vins_facts['status'])
else:
self.result['msg'] = ("No state change to '{}' can be done for "
"non-existent ViNS instance.").format(self.amodule.params['state'])
return
def error(self):
self.result['failed'] = True
self.result['changed'] = False
if self.vins_id:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Invalid target state '{}' requested for ViNS ID {} in the "
"current status '{}'").format(self.vins_id,
self.amodule.params['state'],
self.vins_facts['status'])
else:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Invalid target state '{}' requested for non-existent "
"ViNS name '{}'").format(self.amodule.params['state'],
self.amodule.params['vins_name'])
return
def package_facts(self, arg_check_mode=False):
"""Package a dictionary of ViNS facts according to the decort_vins module specification.
This dictionary will be returned to the upstream Ansible engine at the completion of
the module run.
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
"""
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
)
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict
if self.vins_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['id'] = self.vins_facts['id']
ret_dict['name'] = self.vins_facts['name']
ret_dict['state'] = self.vins_facts['status']
ret_dict['account_id'] = self.vins_facts['accountId']
ret_dict['rg_id'] = self.vins_facts['rgId']
ret_dict['int_net_addr'] = self.vins_facts['network']
ret_dict['gid'] = self.vins_facts['gid']
custom_interfaces = list(filter(lambda i: i['type']=="CUSTOM",self.vins_facts['VNFDev']['interfaces']))
if custom_interfaces:
ret_dict['custom_net_addr'] = []
for runner in custom_interfaces:
ret_dict['custom_net_addr'].append(runner['ipAddress'])
mgmt_interfaces = list(filter(lambda i: i['listenSsh'] and i['name']!="ens9",self.vins_facts['VNFDev']['interfaces']))
if mgmt_interfaces:
ret_dict['ssh_ipaddr'] = []
for runner in mgmt_interfaces:
ret_dict['ssh_ipaddr'].append(runner['ipAddress'])
ret_dict['ssh_password'] = self.vins_facts['VNFDev']['config']['mgmt']['password']
ret_dict['ssh_port'] = 9022
if self.vins_facts['vnfs'].get('GW'):
gw_config = self.vins_facts['vnfs']['GW']['config']
ret_dict['ext_ip_addr'] = gw_config['ext_net_ip']
ret_dict['ext_net_id'] = gw_config['ext_net_id']
else:
ret_dict['ext_ip_addr'] = ""
ret_dict['ext_net_id'] = -1
ret_dict['zone_id'] = self.vins_facts['zoneId']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
account_id=dict(
type='int',
default=0,
),
account_name=dict(
type='str',
default='',
),
description=dict(
type='str',
default='',
),
ext_net_id=dict(
type='int',
default=-1,
),
ext_ip_addr=dict(
type='str',
default='',
),
ipcidr=dict(
type='str',
default='',
),
mgmtaddr=dict(
type='list',
default=[],
),
custom_config=dict(
type='bool',
default=False,
),
config_save=dict(
type='bool',
default=False,
),
connect_to=dict(
type='list',
default=[],
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'disabled',
'enabled',
'present',
],
),
rg_id=dict(
type='int',
default=0,
),
rg_name=dict(
type='str',
default='',
),
permanently=dict(
type='bool',
default=False,
),
vins_id=dict(
type='int',
default=0,
),
vins_name=dict(
type='str',
default='',
),
zone_id=dict(
type=int,
),
),
supports_check_mode=True,
required_one_of=[
('vins_id', 'vins_name'),
],
)
def check_amodule_args_for_change(self):
check_errors = False
if self.check_aparam_zone_id() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
if self.check_aparam_zone_id() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
# Workflow digest:
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController
# 2) check if the ViNS with this id or name exists under specified account / resource group
# 3) if ViNS does not exist -> deploy
# 4) if ViNS exists: check desired state, desired configuration -> initiate action(s) accordingly
# 5) report result to Ansible
def main():
decon = decort_vins()
amodule = decon.amodule
#
# Initial validation of module arguments is complete
#
# At this point non-zero vins_id means that we will be managing pre-existing ViNS
# Otherwise we are about to create a new one as follows:
# - if validated_rg_id is non-zero, create ViNS @ RG level
# - if validated_rg_id is zero, create ViNS @ account level
#
# When managing existing ViNS we need to account for both "static" and "transient"
# status. Full range of ViNS statii is as follows:
#
# "MODELED", "CREATED", "ENABLED", "ENABLING", "DISABLED", "DISABLING", "DELETED", "DELETING", "DESTROYED", "DESTROYING"
#
# if cconfig_save is true, only config save without other updates
vins_should_exist = False
if decon.vins_id:
vins_should_exist = True
if decon.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
# error: nothing can be done to existing ViNS in the listed statii regardless of
# the requested state
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
"status '{}'").format(decon.vins_id, decon.vins_facts['status'])
elif decon.vins_facts['status'] == "DISABLED":
if amodule.params['state'] == 'absent':
decon.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'disabled'):
# update ViNS, leave in disabled state
decon.action()
elif amodule.params['state'] == 'enabled':
# update ViNS and enable
decon.action('enabled')
elif decon.vins_facts['status'] in ["CREATED", "ENABLED"]:
if amodule.params['state'] == 'absent':
decon.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'enabled'):
# update ViNS
decon.action()
elif amodule.params['state'] == 'disabled':
# disable and update ViNS
decon.action('disabled')
elif decon.vins_facts['status'] == "DELETED":
if amodule.params['state'] in ['present', 'enabled']:
# restore and enable
decon.action(restore=True)
vins_should_exist = True
elif amodule.params['state'] == 'absent':
# destroy permanently
if decon.amodule.params['permanently']:
decon.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
decon.error()
vins_should_exist = False
elif decon.vins_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
# need to re-provision ViNS;
decon.create()
vins_should_exist = True
elif amodule.params['state'] == 'absent':
decon.nop()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
decon.error()
else:
# Preexisting ViNS was not found.
vins_should_exist = False # we will change it back to True if ViNS is created or restored
# If requested state is 'absent' - nothing to do
if amodule.params['state'] == 'absent':
decon.nop()
elif amodule.params['state'] in ('present', 'enabled'):
decon.check_amodule_argument('vins_name')
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
decon.create()
vins_should_exist = True
elif amodule.params['state'] == 'disabled':
decon.error()
#
# conditional switch end - complete module run
#
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
# prepare ViNS facts to be returned as part of decon.result and then call exit_json(...)
if decon.result['changed']:
_, decon.vins_facts = decon.vins_find(decon.vins_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,188 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_snapshot
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortVMSnapshot(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args()
self.vm_id: int
self.vm_facts: dict
self.aparams_label = self.aparams['label']
self.aparams_vm_id = self.aparams['vm_id']
self.vm_id, self.vm_facts, _ = self._compute_get_by_id(
comp_id=self.aparams_vm_id,
)
if not self.vm_id:
self.message(f'VM {self.aparams_vm_id} not found')
self.exit(fail=True)
self.vm_name = self.vm_facts['name']
self.vm_snapshots = self.vm_facts['snapSets']
self.vm_snapshot_labels = [
snapshot['label'] for snapshot in self.vm_snapshots
]
self.new_snapshot_label = None
if self.aparams['state'] == 'present':
if self.aparams_label is None:
self.new_snapshot_label = (
f'{self.vm_name}_{self.sec_to_dt_str(time.time())}'
)
elif self.aparams_label not in self.vm_snapshot_labels:
self.new_snapshot_label = self.aparams_label
if (
self.new_snapshot_label is None
and self.aparams_label is not None
and self.aparams_label not in self.vm_snapshot_labels
):
self.message(
f'Snapshot {self.aparams_label} '
f'not found for VM {self.aparams_vm_id}'
)
self.exit(fail=True)
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
label=dict(
type='str',
),
state=dict(
type='str',
choices=(
'absent',
'present',
'merge_aborted',
),
),
usage=dict(
type='bool',
default=False,
),
vm_id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
required_if=[
('state', 'absent', ('label',)),
],
required_one_of=[
('label', 'state'),
],
)
def check_amodule_args(self):
check_error = False
if (
self.aparams['state'] == 'absent'
and self.aparams['usage']
):
self.message(
'Parameter "usage" is not supported when deleting snapshot'
)
check_error = True
if check_error:
self.exit(fail=True)
def run(self):
self.get_info(first_run=True)
self.check_amodule_args_for_change()
self.change()
self.exit()
def get_info(self, first_run: bool = False):
if not first_run:
self.vm_snapshots = self.snapshot_list(
compute_id=self.aparams_vm_id,
)
label = self.new_snapshot_label or self.aparams_label
for snapshot in self.vm_snapshots:
if snapshot['label'] == label:
self.facts = snapshot
if self.aparams['usage']:
self.facts['stored'] = self.get_snapshot_usage()
self.facts['vm_id'] = self.aparams_vm_id
break
def change(self):
match self.aparams['state']:
case 'present':
if self.new_snapshot_label:
self.create()
case 'absent':
if self.aparams_label in self.vm_snapshot_labels:
self.delete()
case 'merge_aborted':
self.abort_merge()
def create(self):
self.snapshot_create(
compute_id=self.aparams_vm_id,
label=self.new_snapshot_label,
)
self.get_info()
def delete(self):
self.snapshot_delete(
compute_id=self.aparams_vm_id,
label=self.aparams_label,
)
self.facts = {}
def abort_merge(self):
self.snapshot_abort_merge(
vm_id=self.aparams_vm_id,
label=self.aparams_label,
)
self.get_info()
def get_snapshot_usage(self) -> int:
label = self.new_snapshot_label or self.aparams_label
common_snapshots_usage_info, _ = self.snapshot_usage(
compute_id=self.aparams_vm_id,
label=label,
)
return common_snapshots_usage_info['stored']
def check_amodule_args_for_change(self):
check_errors = False
if (
self.aparams['state'] == 'merge_aborted'
and self.vm_facts['techStatus'] != 'MERGE'
):
check_errors = True
self.message(
f'Check for parameter "state" failed: '
'Merge can be aborted only for VM in "MERGE" tech status.'
)
if check_errors:
self.exit(fail=True)
def main():
DecortVMSnapshot().run()
if __name__ == '__main__':
main()

48
library/decort_zone.py Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_zone
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortZone(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.id: int = self.aparams['id']
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
)
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.zone_get(id=self.id)
self.facts['grid_id'] = self.facts.pop('gid')
self.facts['created_timestamp'] = self.facts.pop('createdTime')
self.facts['updated_timestamp'] = self.facts.pop('updatedTime')
self.facts['node_ids'] = self.facts.pop('nodeIds')
def main():
DecortZone().run()
if __name__ == '__main__':
main()

8542
module_utils/decort_utils.py Normal file

File diff suppressed because it is too large Load Diff

2
requirements-dev.txt Normal file
View File

@@ -0,0 +1,2 @@
-r requirements.txt
pre-commit==4.1.0

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
ansible==11.6.0
requests==2.32.3