330 Commits

Author SHA1 Message Date
e54a9591e4 11.0.0 2026-02-11 13:50:28 +03:00
8c554c8edd 10.0.1 2025-12-02 15:31:36 +03:00
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
70 changed files with 17878 additions and 1 deletions

170
CHANGELOG.md Normal file
View File

@@ -0,0 +1,170 @@
# Список изменений в версии 11.0.0
## Добавлено
### Глобально
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-909 | В системные требования добавлена библиотека Python `dynamix_sdk`. |
| BANS-918 | Добавлен общий для всех модулей параметр `ignore_api_compatibility`. |
| BANS-913 | Добавлен общий для всех модулей параметр `ignore_sdk_version_check`. |
| BANS-954 | Добавлен модуль `decort_vm` в связи с переименованием из `decort_kvmvm`. |
| BANS-953 | Добавлен модуль `decort_image` в связи с переименованием из `decort_osimage`. |
| BANS-997 | Добавлен модуль `decort_security_group_list`, позволяющий получить список доступных групп безопасности. |
| BANS-884 | Добавлен модуль `decort_disk_list`, позволяющий получить список доступных дисков. |
| BANS-936 | Добавлен модуль `decort_rg_list`, позволяющий получить список доступных ресурсных групп. |
| BANS-949 | Добавлен модуль `decort_vins_list`, позволяющий получить список доступных внутренних сетей. |
| BANS-940 | Добавлен модуль `decort_vm_list`, позволяющий получить список доступных виртуальных машин. |
| BANS-959 | Добавлен модуль `decort_flip_group_list`, позволяющий получить список доступных групп с плавающим IP-адресом. |
| BANS-952 | Добавлен модуль `decort_image_list`, позволяющий получить список доступных образов. |
| BANS-983 | Добавлен модуль `decort_account_list`, позволяющий получить список доступных аккаунтов. |
| BANS-985 | Добавлен модуль `decort_audit_list`, позволяющий получить список аудитов. |
| BANS-988 | Добавлен модуль `decort_trunk_list`, позволяющий получить список доступных транковых портов. |
| BANS-987 | Добавлен модуль `decort_zone_list`, позволяющий получить список доступных зон. |
| BANS-989 | Добавлен модуль `decort_storage_policy_list`, позволяющий получить список политик хранения. |
| BANS-945 | Добавлен модуль `decort_user` в связи с переименованием из `decort_user_info`. |
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-926 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании ВМ. |
| BANS-933 | Добавлено возвращаемое значение `pinned_to_node` в связи с переименованием из `pinned_to_stack`. |
| BANS-934 | Добавлено возвращаемое значение `read_only`. |
| BANS-994 | Добавлена возможность задать параметр `mtu` при создании сетевого интерфейса для TRUNK-сети и изменить `mtu` у существующего интерфейса, подключённого к TRUNK-сети. |
| BANS-991 | Добавлена возможность указать параметр `ip_addr` при присоединении и изменении `DPDK` сети. |
| BANS-1017 | Добавлено возвращаемое значение `disks.cache`. |
| BANS-1034 | Добавлена возможность указать параметр `ip_addr` при присоединении и изменении `VFNIC` сети. |
| BANS-992 | Добавлен параметр `networks.net_prefix`. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-927 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании группы. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-928 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании кластера. |
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-966 | Добавлен параметр `get_resource_consumption` и возвращаемое значение `resource_consumption`. |
### Модуль decort_trunk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-993 | Добавлено возвращаемое значение `mtu`. |
| BANS-976 | Добавлены возвращаемые значения `created_datetime`, `deleted_datetime`, `updated_datetime`. |
### Модуль decort_zone
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-970 | Добавлены возвращаемые значения `created_datetime`, `updated_datetime` и возвращаемые значения `account_ids`, `bservice_ids`, `vm_ids`, `extnet_ids`, `k8s_ids`, `lb_ids`, `vins_ids` в связи с переименованием из `accountIds`, `bserviceIds`, `computeIds`, `extnetIds`, `k8sIds`, `lbIds`, `vinsIds`. |
| BANS-1024 | Добавлено возвращаемое значение `node_auto_start`. |
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-978 | Добавлено возвращаемое значение `datetime`. |
### Модуль decort_storage_policy
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-977 | Добавлены возвращаемые значения `sep_name`, `sep_tech_status`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1019 | Добавлено возвращаемое значение `cache_mode`. |
| BANS-1050 | Добавлено возвращаемое значение `blkdiscard`. |
## Удалено
### Глобально
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-954 | Удалён модуль `decort_kvmvm` в связи с переименованием в `decort_vm`. |
| BANS-969 | Модуль `decort_account_info` расформирован, его функционал перенесён в модули: `decort_disk_list`, `decort_rg_list`, `decort_vins_list`, `decort_vm_list`, `decort_flip_group_list`, `decort_image_list`, `decort_account`. |
| BANS-953 | Удалён модуль `decort_osimage` в связи с переименованием в `decort_image`. |
| BANS-945 | Удалён модуль `decort_user_info` в связи с переименованием в `decort_user`. |
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-924 | Удалён параметр `quotas.ext_traffic`. |
| BANS-998 |Для параметра `state` удалено значение по умолчанию. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-925 | Удалён параметр `quotas.net_transfer`. |
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-926 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании ВМ. |
| BANS-933 | Удалено возвращаемое значение `pinned_to_stack` в связи с переименованием в `pinned_to_node`. |
| BANS-961 | Параметр `storage_policy_id` удалён из обязательных при пересоздании загрузочного диска. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-927 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании группы. |
| BANS-1027 | Удалён параметр `driver`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-928 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании кластера. |
### Модуль decort_user
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-983 | Удалён параметр `accounts` и возвращаемое значение `accounts` в связи с переносом этой функциональности в модуль `decort_account_list`. |
| BANS-985 | Удалён параметр `audits` и возвращаемое значение `audits` в связи с переносом этой функциональности в модуль `decort_audit_list`. |
| BANS-988 | Удалён параметр `trunks` и возвращаемое значение `trunks` в связи с переносом этой функциональности в модуль `decort_trunk_list`. |
| BANS-987 | Удалён параметр `zones` и возвращаемое значение `zones` в связи с переносом этой функциональности в модуль `decort_zone_list`. |
| BANS-989 | Удалён параметр `storage_policies` и возвращаемое значение `storage_policies` в связи с переносом этой функциональности в модуль `decort_storage_policy_list`. |
| BANS-997 | Удалён параметр `security_groups` и возвращаемое значение `security_groups` в связи с переносом этой функциональности в модуль `decort_security_group_list`. |
### Модуль decort_zone
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-970 | Удалены возвращаемые значения `accountIds`, `bserviceIds`, `computeIds`, `extnetIds`, `k8sIds`, `lbIds`, `vinsIds` в связи с переименованием в `account_ids`, `bservice_ids`, `vm_ids`, `extnet_ids`, `k8s_ids`, `lb_ids`, `vins_ids`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1004 | Удалён параметр `reason` |
### Модуль decort_security_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1000 | Удалено возвращаемое значение `rules.remote_ip_prefix` в связи с переименованием в `rules.remote_net_cidr`. |
| BANS-1013 | Удален параметр `rules.objects.remote_ip_prefix` в связи с переименованием в `rules.objects.remote_net_cidr`. |
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1012 | Удалено возвращаемое значение `disks` в связи с переименованием в `disk_ids`. |
## Исправлено
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-996 | Параметры `mac`, `security_groups`, `enable_secgroups`, `enabled` сетевого интерфейса DPDK-сети могли меняться при изменении `mtu`. |
| BANS-1052 | Параметры `numa_affinity`, `cpu_pin`, `hp_backed` не применялись при создании ВМ без образа. |
### Модуль decort_bservice
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-389 | После создания базовой службы, модуль не возвращал информацию о созданном объекте. |
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1022 | После создания снимка не возвращалась информация о снимке. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1033 | Модуль без необходимости выполнял запрос к API `/cloudapi/k8s/update`, передавая в него параметры, не вызывающие изменения. |

View File

@@ -1,2 +1,23 @@
# 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.5.0 | 11.0.x |
| 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 image module example
#
- hosts: localhost
tasks:
- name: create
decort_image:
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 image module example
#
- hosts: localhost
tasks:
- name: create_virtual_image
decort_image:
authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0"
virt_name: "alpine_last"
delegate_to: localhost
register: image

View File

@@ -0,0 +1,14 @@
---
#
# DECORT image module example
#
- hosts: localhost
tasks:
- name: get_image
decort_image:
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 image module example
#
- hosts: localhost
tasks:
- name: rename_image
decort_image:
authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0v2.0"
image_id: 54321
delegate_to: localhost
register: image

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

425
library/decort_account.py Normal file
View File

@@ -0,0 +1,425 @@
#!/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',
),
get_resource_consumption=dict(
type='bool',
default=False,
),
quotas=dict(
type='dict',
options=dict(
cpu=dict(
type='int',
),
disks_size=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',
],
),
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 arg_state is not None and '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)
@DecortController.handle_sdk_exceptions
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'],
resource_consumption=self.aparams['get_resource_consumption'],
)
# 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,
resource_consumption=(
self.aparams['get_resource_consumption']
),
)
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'],
['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,54 @@
#!/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
def main():
module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
audits=dict(type='raw'),
computes=dict(type='raw'),
disks=dict(type='raw'),
flip_groups=dict(type='raw'),
id=dict(type='raw'),
images=dict(type='raw'),
name=dict(type='raw'),
resource_groups=dict(type='raw'),
resource_consumption=dict(type='raw'),
vinses=dict(type='raw'),
),
)
module.fail_json(
msg=(
'The functionality of the module has been moved to the modules '
'"decort_disk_list", "decort_rg_list", "decort_vm_list", '
'"decort_vins_list", "decort_image_list", '
'"decort_flip_group_list", "decort_account".'
'\nPlease use the new modules to get information about the objects'
' available to the account.'
),
)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,130 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_account_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortAccountList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
access_type=dict(
type='str',
choices=sdk_types.AccessType._member_names_,
),
id=dict(
type='int',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.AccountStatus._member_names_,
),
zone_id=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=(
sdk_types.AccountForCAAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_access_type: str | None = aparam_filter['access_type']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.AccountForCAAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.account.list(
access_type=(
sdk_types.AccessType[aparam_access_type]
if aparam_access_type else None
),
id=aparam_filter['id'],
name=aparam_filter['name'],
status=(
sdk_types.AccountStatus[aparam_status]
if aparam_status else None
),
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortAccountList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,168 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_audit_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortAuditList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
api_url_path=dict(
type='str',
),
bservice_id=dict(
type='int',
),
exclude_audit_lines=dict(
type='bool',
),
flip_group_id=dict(
type='int',
),
request_id=dict(
type='str',
),
k8s_id=dict(
type='int',
),
lb_id=dict(
type='int',
),
max_status_code=dict(
type='int',
),
min_status_code=dict(
type='int',
),
request_timestamp_end=dict(
type='int',
),
request_timestamp_start=dict(
type='int',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
user_name=dict(
type='str',
),
vins_id=dict(
type='int',
),
vm_id=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=(
sdk_types.AuditAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.AuditAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.audit.list(
account_id=aparam_filter['account_id'],
api_url_path=aparam_filter['api_url_path'],
bservice_id=aparam_filter['bservice_id'],
exclude_audit_lines=aparam_filter['exclude_audit_lines'] or False,
flip_group_id=aparam_filter['flip_group_id'],
request_id=aparam_filter['request_id'],
k8s_id=aparam_filter['k8s_id'],
lb_id=aparam_filter['lb_id'],
max_status_code=aparam_filter['max_status_code'],
min_status_code=aparam_filter['min_status_code'],
request_timestamp_end=aparam_filter['request_timestamp_end'],
request_timestamp_start=aparam_filter['request_timestamp_start'],
rg_id=aparam_filter['rg_id'],
sep_id=aparam_filter['sep_id'],
user_name=aparam_filter['user_name'],
vins_id=aparam_filter['vins_id'],
vm_id=aparam_filter['vm_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortAuditList().run()
if __name__ == '__main__':
main()

340
library/decort_bservice.py Normal file
View File

@@ -0,0 +1,340 @@
#!/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'])
self.bservice_should_exist = True
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)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if self.amodule.check_mode:
self.result['changed'] = False
if self.bservice_id:
self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
# we exit the module at this point
else:
self.result['failed'] = True
self.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(**self.result)
pass
#MAIN MANAGE PART
if self.bservice_id:
if self.bservice_info['status'] in ("DELETING","DESTROYNG","RECONFIGURING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
self.error()
elif self.bservice_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
self.restore(self.bservice_id)
self.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
self.nop()
elif self.bservice_info['status'] in (
'ENABLED', 'DISABLED', 'CREATED',
):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action(amodule.params['state'])
elif self.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
self.create()
self.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
self.nop()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
self.nop()
if state in ('present','started'):
self.create()
elif state in ('stopped', 'disabled','enabled'):
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.bservice_should_exist:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_bservice().run()
if __name__ == '__main__':
main()

480
library/decort_disk.py Normal file
View File

@@ -0,0 +1,480 @@
#!/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 compare_iotune_params(self, new_iotune: dict, current_iotune: dict):
io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
for field in io_fields:
new_value = new_iotune.get(field)
current_value = current_iotune.get(field)
if new_value != current_value:
return False
return True
def limit_io(self, aparam_limit_io: dict):
self.sdk_checkmode(self.api.cloudapi.disks.limit_io)(
disk_id=self.disk_id,
read_bytes_sec_max=(aparam_limit_io.get('read_bytes_sec_max')),
read_bytes_sec=(aparam_limit_io.get('read_bytes_sec')),
read_iops_sec_max=(aparam_limit_io.get('read_iops_sec_max')),
read_iops_sec=(aparam_limit_io.get('read_iops_sec')),
size_iops_sec=(aparam_limit_io.get('size_iops_sec')),
total_bytes_sec_max=(aparam_limit_io.get('total_bytes_sec_max')),
total_bytes_sec=(aparam_limit_io.get('total_bytes_sec')),
total_iops_sec_max=(aparam_limit_io.get('total_iops_sec_max')),
total_iops_sec=(aparam_limit_io.get('total_iops_sec')),
write_bytes_sec_max=(aparam_limit_io.get('write_bytes_sec_max')),
write_bytes_sec=(aparam_limit_io.get('write_bytes_sec')),
write_iops_sec_max=(aparam_limit_io.get('write_iops_sec_max')),
write_iops_sec=(aparam_limit_io.get('write_iops_sec')),
)
def create(self):
self.disk_id = self.sdk_checkmode(self.api.cloudapi.disks.create)(
account_id=self.acc_id,
name=self.amodule.params['name'],
size_gb=self.amodule.params['size'],
storage_policy_id=self.aparams['storage_policy_id'],
description=self.amodule.params['description'],
sep_id=self.amodule.params['sep_id'],
sep_pool_name=self.amodule.params['pool'],
)
#IO tune
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
self.limit_io(aparam_limit_io=aparam_limit_io)
#set share status
if self.amodule.params['shareable']:
self.sdk_checkmode(self.api.cloudapi.disks.share)(
disk_id=self.disk_id,
)
return
def action(self,restore=False):
#restore never be done
if restore:
self.sdk_checkmode(self.api.cloudapi.disks.restore)(
disk_id=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.rename()
#resize
if (
self.amodule.params['size'] is not None
and self.amodule.params['size'] != self.disk_info['sizeMax']
):
self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
disk_id=self.disk_id,
disk_size_gb=self.amodule.params['size'],
)
#IO TUNE
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
if aparam_limit_io:
if not self.compare_iotune_params(
new_iotune=aparam_limit_io,
current_iotune=self.disk_info['iotune'],
):
self.limit_io(aparam_limit_io=aparam_limit_io)
#share check/update
#raise Exception(self.amodule.params['shareable'])
if self.amodule.params['shareable'] != self.disk_info['shareable']:
if self.amodule.params['shareable']:
self.sdk_checkmode(self.api.cloudapi.disks.share)(
disk_id=self.disk_id,
)
else:
self.sdk_checkmode(self.api.cloudapi.disks.unshare)(
disk_id=self.disk_id,
)
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.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
disk_id=self.disk_id,
storage_policy_id=aparam_storage_policy_id,
)
return
def delete(self):
self.sdk_checkmode(self.api.cloudapi.disks.delete)(
disk_id=self.disk_id,
detach=self.amodule.params['force_detach'],
permanently=self.amodule.params['permanently'],
)
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
return
def rename(self):
self.sdk_checkmode(self.api.cloudapi.disks.rename)(
disk_id=self.disk_id,
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']
ret_dict['cache_mode'] = self.disk_info['cache']
ret_dict['blkdiscard'] = self.disk_info['blkdiscard']
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',
apply_defaults=True,
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,
),
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
@DecortController.handle_sdk_exceptions
def run(self):
#
#Full range of Disk status is as follows:
#
# "ASSIGNED","MODELED", "CREATING","CREATED","DELETED", "DESTROYED","PURGED",
#
amodule = self.amodule
if self.disk_id:
#disk exist
if self.disk_info['status'] in ["MODELED", "CREATING"]:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
"status '{}'").format(self.disk_id, self.disk_info['status'])
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
elif self.disk_info['status'] in ["ASSIGNED","CREATED"]:
if amodule.params['state'] == 'absent':
self.delete()
elif amodule.params['state'] == 'present':
self.action()
elif self.disk_info['status'] in ["PURGED", "DESTROYED"]:
#re-provision disk
if amodule.params['state'] in ('present'):
self.create()
else:
self.nop()
elif self.disk_info['status'] == "DELETED":
if amodule.params['state'] in ('present'):
self.action(restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
self.delete()
else:
self.nop()
else:
# preexisting Disk was not found
if amodule.params['state'] == 'absent':
self.nop()
else:
self.create()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.result['changed']:
_, self.disk_info = self.disk_find(self.disk_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
def main():
decort_disk().run()
if __name__ == '__main__':
main()

155
library/decort_disk_list.py Normal file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_disk_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortDiskList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
sep_id=dict(
type='int',
),
sep_pool_name=dict(
type='str',
),
shared=dict(
type='bool',
),
disk_max_size_gb=dict(
type='int',
),
status=dict(
type='str',
choices=sdk_types.DiskStatus._member_names_,
),
storage_policy_id=dict(
type='int',
),
type=dict(
type='str',
choices=sdk_types.DiskType._member_names_,
),
),
),
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=(
sdk_types.DiskForListAndListDeletedAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_type: str | None = aparam_filter['type']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.DiskForListAndListDeletedAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.disks.list(
account_id=aparam_filter['account_id'],
account_name=aparam_filter['account_name'],
disk_max_size_gb=aparam_filter['disk_max_size_gb'],
id=aparam_filter['id'],
name=aparam_filter['name'],
sep_id=aparam_filter['sep_id'],
sep_pool_name=aparam_filter['sep_pool_name'],
shared=aparam_filter['shared'],
status=(
sdk_types.DiskStatus[aparam_status]
if aparam_status else None
),
storage_policy_id=aparam_filter['storage_policy_id'],
type=(
sdk_types.DiskType[aparam_type]
if aparam_type else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortDiskList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,150 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_flip_group_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortFlipGroupList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
client_ids=dict(
type='list',
elements='int',
),
conn_id=dict(
type='int',
),
ext_net_id=dict(
type='int',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
default=False,
),
ip_addr=dict(
type='str',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.FlipGroupStatus._member_names_,
),
vins_id=dict(
type='int',
),
vins_name=dict(
type='str',
),
),
),
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=(
sdk_types.FlipGroupForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.FlipGroupForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.flipgroup.list(
account_id=aparam_filter['account_id'],
client_ids=aparam_filter['client_ids'],
conn_id=aparam_filter['conn_id'],
ext_net_id=aparam_filter['ext_net_id'],
id=aparam_filter['id'],
ip_addr=aparam_filter['ip_addr'],
name=aparam_filter['name'],
status=(
sdk_types.FlipGroupStatus[aparam_status]
if aparam_status else None
),
vins_id=aparam_filter['vins_id'],
vins_name=aparam_filter['vins_name'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortFlipGroupList().run()
if __name__ == '__main__':
main()

437
library/decort_group.py Normal file
View File

@@ -0,0 +1,437 @@
#!/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 = 'Q35'
self.message(
msg=f'Chipset not specified, '
f'default value "{chipset}" will be used.',
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'],
)
if self.amodule.params['state'] in ('started','present'):
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',
),
),
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']}'
)
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)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if amodule.params['state'] == 'check':
self.result['changed'] = False
if self.group_id:
# cluster is found - package facts and report success to Ansible
self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
# we exit the module at this point
else:
self.result['failed'] = True
self.result['msg'] = ("Cannot locate Group name '{}'. "
"B-service ID {}").format(amodule.params['name'],
amodule.params['bservice_id'],)
amodule.fail_json(**self.result)
if self.group_id:
if self.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED","DESTROYED"):
self.error()
elif self.group_info['status'] in ("DELETED","DESTROYED"):
if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started','stopped'):
self.create()
elif self.group_info['techStatus'] in ("STARTED","STOPPED"):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action()
else:
if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started','stopped'):
self.create()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.group_should_exist:
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_group().run()
if __name__ == '__main__':
main()

558
library/decort_image.py Normal file
View File

@@ -0,0 +1,558 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_image
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_image(DecortController):
def __init__(self):
super(decort_image, 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_image.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_image.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
decort_image.decort_image_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_image._image_get_by_id(self, amodule.image_id_delete)
self.result['facts'] = decort_image.decort_image_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_image.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_image.decort_image_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_image.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_image_package_facts(
arg_image_facts: dict | None,
arg_check_mode=False,
):
"""Package a dictionary of OS image according to the decort_image module specification. This
dictionary will be returned to the upstream Ansible engine at the completion of the module run.
@param arg_image_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_image_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_image_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_image_facts['id']
ret_dict['name'] = arg_image_facts['name']
ret_dict['size'] = arg_image_facts['size']
# ret_dict['arch'] = arg_image_facts['architecture']
ret_dict['sep_id'] = arg_image_facts['sepId']
ret_dict['pool'] = arg_image_facts['pool']
ret_dict['state'] = arg_image_facts['status']
ret_dict['linkto'] = arg_image_facts['linkTo']
ret_dict['accountId'] = arg_image_facts['accountId']
ret_dict['boot_mode'] = arg_image_facts['bootType']
ret_dict['boot_loader_type'] = ''
match arg_image_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_image_facts[
'networkInterfaceNaming'
]
ret_dict['hot_resize'] = arg_image_facts['hotResize']
ret_dict['storage_policy_id'] = arg_image_facts['storage_policy_id']
ret_dict['to_clean'] = arg_image_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)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if amodule.params['virt_name'] or amodule.params['virt_id']:
image_id, image_facts = self.decort_virt_image_find(amodule)
if amodule.params['image_name'] or amodule.params['image_id']:
self.target_image_id, _ = self.decort_image_find(amodule)
else:
self.target_image_id = 0
if self.decort_image_package_facts(image_facts)['id'] > 0:
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
self.validated_virt_image_id = self.decort_image_package_facts(image_facts)['id']
self.validated_virt_image_name = self.decort_image_package_facts(image_facts)['name']
if self.decort_image_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and self.target_image_id > 0:
image_id, image_facts = self.decort_virt_image_create(amodule)
self.result['msg'] = ("Virtual image '{}' created").format(self.decort_image_package_facts(image_facts)['id'])
self.result['changed'] = True
elif self.decort_image_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and self.target_image_id == 0:
self.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**self.result)
if self.validated_virt_image_id:
if (
self.target_image_id
and self.decort_image_package_facts(image_facts)[
'linkto'
] != self.target_image_id
):
self.decort_virt_image_link(amodule)
self.result['changed'] = True
amodule.exit_json(**self.result)
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
image_id=self.validated_virt_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if amodule.params['state'] == "absent" and self.validated_virt_image_id:
amodule.image_id_delete = self.validated_virt_image_id
image_id, image_facts = self.decort_virt_image_find(amodule)
if image_facts['status'] != 'PURGED':
self.decort_image_delete(amodule)
elif amodule.params['image_name'] or amodule.params['image_id']:
image_id, image_facts = self.decort_image_find(amodule)
self.validated_image_id = self.decort_image_package_facts(image_facts)['id']
if self.decort_image_package_facts(image_facts)['id'] > 0:
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
if amodule.params['state'] == "present" and self.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']:
self.decort_image_create(amodule)
self.result['changed'] = True
image_id, image_facts = self.decort_image_find(amodule)
self.result['msg'] = ("OS image '{}' created").format(self.decort_image_package_facts(image_facts)['id'])
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
self.validated_image_id = self.decort_image_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and self.validated_image_id:
amodule.image_id_delete = self.validated_image_id
image_id, image_facts = self.decort_image_find(amodule)
if image_facts['status'] != 'DESTROYED':
self.decort_image_delete(amodule)
if self.validated_image_id:
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
image_id=self.validated_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if self.result['failed'] == True:
# we failed to find the specified image - fail the module
self.result['changed'] = False
amodule.fail_json(**self.result)
else:
if self.validated_image_id:
_, image_facts = self.decort_image_find(amodule=amodule)
elif self.validated_virt_image_id:
_, image_facts = self.decort_virt_image_find(amodule=amodule)
self.result['facts'] = self.decort_image_package_facts(
arg_image_facts=image_facts,
arg_check_mode=amodule.check_mode,
)
amodule.exit_json(**self.result)
def main():
decort_image().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,162 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_image_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortImageList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
bootable=dict(
type='bool',
),
id=dict(
type='int',
),
enabled=dict(
type='bool',
),
hot_resize=dict(
type='bool',
),
size_gb=dict(
type='int',
),
name=dict(
type='str',
),
public=dict(
type='bool',
),
sep_id=dict(
type='int',
),
sep_name=dict(
type='str',
),
sep_pool_name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.ImageStatus._member_names_,
),
type=dict(
type='str',
choices=sdk_types.ImageType._member_names_,
),
storage_policy_id=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=(
sdk_types.ImageForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_type: str | None = aparam_filter['type']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ImageForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.ca.image.list(
bootable=aparam_filter['bootable'],
enabled=aparam_filter['enabled'],
hot_resize=aparam_filter['hot_resize'],
id=aparam_filter['id'],
name=aparam_filter['name'],
public=aparam_filter['public'],
sep_id=aparam_filter['sep_id'],
sep_name=aparam_filter['sep_name'],
sep_pool_name=aparam_filter['sep_pool_name'],
size_gb=aparam_filter['size_gb'],
status=(
sdk_types.ImageStatus[aparam_status]
if aparam_status else None
),
storage_policy_id=aparam_filter['storage_policy_id'],
type=(
sdk_types.ImageType[aparam_type]
if aparam_type else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortImageList().run()
if __name__ == '__main__':
main()

39
library/decort_jwt.py Normal file
View File

@@ -0,0 +1,39 @@
#!/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
@DecortController.handle_sdk_exceptions
def run(self):
self.result['jwt'] = self.jwt
self.amodule.exit_json(**self.result)
def main():
DecortJWT().run()
if __name__ == '__main__':
main()

668
library/decort_k8s.py Normal file
View File

@@ -0,0 +1,668 @@
#!/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': 'Q35',
}
if arg_amodule.params['name'] is None 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 = 'Q35'
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',
),
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)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if self.amodule.check_mode:
self.result['changed'] = False
if self.k8s_id:
# cluster is found - package facts and report success to Ansible
self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
# we exit the module at this point
else:
self.result['failed'] = True
self.result['msg'] = ("Cannot locate K8s cluster name '{}'. "
"RG ID {}").format(amodule.params['name'],
amodule.params['rg_id'],)
amodule.fail_json(**self.result)
if self.k8s_id:
if self.k8s_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
self.error()
elif self.k8s_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
self.k8s_restore(self.k8s_id)
self.action(disared_state=amodule.params['state'],
preupdate=True)
if amodule.params['state'] == 'absent':
if amodule.params['permanent']:
self.destroy()
else:
self.nop()
elif self.k8s_info['status'] in ('ENABLED', 'DISABLED'):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action(disared_state=amodule.params['state'])
elif self.k8s_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
self.create()
if amodule.params['state'] == 'absent':
self.nop()
else:
if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started'):
self.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.k8s_should_exist:
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_k8s().run()
if __name__ == '__main__':
main()

87
library/decort_kvmvm.py Normal file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_kvmvm
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
account_id=dict(type='raw'),
account_name=dict(type='raw'),
description=dict(type='raw'),
boot=dict(type='raw'),
sep_id=dict(type='raw'),
pool=dict(type='raw'),
cpu=dict(type='raw'),
disks=dict(type='raw'),
id=dict(type='raw'),
image_id=dict(type='raw'),
name=dict(type='raw'),
networks=dict(type='raw'),
network_order_changing=dict(type='raw'),
ram=dict(type='raw'),
rg_id=dict(type='raw'),
rg_name=dict(type='raw'),
ssh_key=dict(type='raw'),
ssh_key_user=dict(type='raw'),
tag=dict(type='raw'),
affinity_label=dict(type='raw'),
aff_rule=dict(type='raw'),
aaff_rule=dict(type='raw'),
ci_user_data=dict(type='raw'),
state=dict(type='raw'),
tags=dict(type='raw'),
chipset=dict(type='raw'),
cpu_pin=dict(type='raw'),
hp_backed=dict(type='raw'),
numa_affinity=dict(type='raw'),
custom_fields=dict(type='raw'),
auto_start=dict(type='raw'),
rollback_to=dict(type='raw'),
preferred_cpu_cores=dict(type='raw'),
get_console_url=dict(type='raw'),
clone_from=dict(type='raw'),
network_interface_naming=dict(type='raw'),
hot_resize=dict(type='raw'),
zone_id=dict(type='raw'),
guest_agent=dict(type='raw'),
get_snapshot_merge_status=dict(type='raw'),
cdrom=dict(type='raw'),
storage_policy_id=dict(type='raw'),
os_version=dict(type='raw'),
get_cloning_status=dict(type='raw'),
abort_cloning=dict(type='raw'),
),
supports_check_mode=True,
)
module.fail_json(
msg=(
'The module "decort_kvmvm" has been renamed to "decort_vm". '
'Please update your playbook to use "decort_vm" '
'instead of "decort_kvmvm".'
),
)
if __name__ == '__main__':
main()

420
library/decort_lb.py Normal file
View File

@@ -0,0 +1,420 @@
#!/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)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if self.lb_id:
if self.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
"status '{}'").format(self.lb_id, self.lb_facts['status'])
elif self.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
if amodule.params['state'] == 'absent':
self.delete()
else:
self.action(d_state=amodule.params['state'])
elif self.lb_facts['status'] == "DELETED":
if amodule.params['state'] == 'present':
self.action(restore=True)
elif amodule.params['state'] == 'enabled':
self.action(d_state='enabled', restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
self.delete()
elif amodule.params['state'] == 'disabled':
self.error()
elif self.lb_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
self.create()
elif amodule.params['state'] == 'absent':
self.nop()
elif amodule.params['state'] == 'disabled':
self.error()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
self.nop()
elif state in ('present', 'enabled', 'stopped', 'started'):
self.create()
elif state == 'disabled':
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.result['changed']:
_, self.lb_facts = self.lb_find(lb_id=self.lb_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
def main():
decort_lb().run()
if __name__ == '__main__':
main()

62
library/decort_osimage.py Normal file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_osimage
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
pool=dict(type='raw'),
sep_id=dict(type='raw'),
account_name=dict(type='raw'),
account_id=dict(type='raw'),
image_name=dict(type='raw'),
image_id=dict(type='raw'),
virt_id=dict(type='raw'),
virt_name=dict(type='raw'),
state=dict(type='raw'),
url=dict(type='raw'),
sepId=dict(type='raw'),
poolName=dict(type='raw'),
hot_resize=dict(type='raw'),
image_username=dict(type='raw'),
image_password=dict(type='raw'),
usernameDL=dict(type='raw'),
passwordDL=dict(type='raw'),
boot=dict(type='raw'),
network_interface_naming=dict(type='raw'),
storage_policy_id=dict(type='raw'),
),
supports_check_mode=True,
)
module.fail_json(
msg=(
'The module "decort_osimage" has been renamed to "decort_image". '
'Please update your playbook to use "decort_image" '
'instead of "decort_osimage".'
),
)
if __name__ == '__main__':
main()

150
library/decort_pfw.py Normal file
View File

@@ -0,0 +1,150 @@
#!/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
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.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 = self.compute_find(amodule.params['compute_id'])
if not validated_comp_id:
self.result['failed'] = True
self.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
amodule.fail_json(**self.result)
validated_vins_id, vins_facts = self.vins_find(amodule.params['vins_id'])
if not validated_vins_id:
self.result['failed'] = True
self.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
amodule.fail_json(**self.result)
gw_vnf_facts = vins_facts['vnfs'].get('GW')
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
self.result['failed'] = True
self.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
amodule.fail_json(**self.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 = self.pfw_configure(comp_facts, vins_facts, None)
elif amodule.params['rules'] is not None:
# manage PFW rules accodring to the module arguments
pfw_facts = self.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
else:
pfw_facts = self._pfw_get(comp_facts['id'], vins_facts['id'])
#
# complete module run
#
if self.result['failed']:
amodule.fail_json(**self.result)
else:
# prepare PFW facts to be returned as part of self.result and then call exit_json(...)
self.result['facts'] = self.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
amodule.exit_json(**self.result)
def main():
decort_pfw().run()
if __name__ == '__main__':
main()

483
library/decort_rg.py Normal file
View File

@@ -0,0 +1,483 @@
#!/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',
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
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
#amodule.check_mode=True
if self.validated_rg_id > 0:
if self.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
self.error()
elif self.rg_facts['status'] in ("CREATED"):
if amodule.params['state'] == 'absent':
self.destroy()
elif amodule.params['state'] == "disabled":
self.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
):
self.update()
if amodule.params['access']:
self.access()
if amodule.params['def_netType'] is not None:
self.setDefNet()
elif self.rg_facts['status'] == "DELETED":
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
self.destroy()
elif (amodule.params['state'] == 'present'
or amodule.params['state'] == 'disabled'):
self.restore()
elif amodule.params['state'] == 'enabled':
self.restore()
self.enable()
elif self.rg_facts['status'] in ("DISABLED"):
if amodule.params['state'] == 'absent':
self.destroy()
elif amodule.params['state'] == ("enabled"):
self.enable()
else:
if amodule.params['state'] in ('present', 'enabled'):
if not amodule.params['rg_name']:
self.result['failed'] = True
self.result['msg'] = (
'Resource group could not be created because'
' the "rg_name" parameter was not specified.'
)
else:
self.create()
if amodule.params['access'] and not amodule.check_mode:
self.access()
elif amodule.params['state'] in ('disabled'):
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.rg_should_exist:
if self.result['changed']:
self.get_info()
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_rg().run()
if __name__ == '__main__':
main()

148
library/decort_rg_list.py Normal file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_rg_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortRGList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
),
created_after_timestamp=dict(
type='int',
),
created_before_timestamp=dict(
type='int',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
lock_status=dict(
type='str',
choices=sdk_types.LockStatus._member_names_,
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=(
sdk_types.ResourceGroupStatus._member_names_
),
),
),
),
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=(
sdk_types.ResourceGroupAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_lock_status: str | None = aparam_filter['lock_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ResourceGroupAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.rg.list(
account_id=aparam_filter['account_id'],
account_name=aparam_filter['account_name'],
created_after_timestamp=aparam_filter['created_after_timestamp'],
created_before_timestamp=aparam_filter['created_before_timestamp'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
lock_status=(
sdk_types.LockStatus[aparam_lock_status]
if aparam_lock_status else None
),
name=aparam_filter['name'],
status=(
sdk_types.ResourceGroupStatus[aparam_status]
if aparam_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortRGList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,366 @@
#!/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
from dynamix_sdk import exceptions as sdk_exceptions
import dynamix_sdk.types as sdk_types
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=(
sdk_types.TrafficDirection.
_member_names_
),
required=True,
),
ethertype=dict(
type='str',
choices=(
sdk_types.SGRuleEthertype.
_member_names_
),
),
id=dict(
type='int',
),
port_range=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
protocol=dict(
type='str',
choices=(
sdk_types.SGRuleProtocol._member_names_
),
),
remote_net_cidr=dict(
type='str',
),
),
),
),
),
state=dict(
type='str',
choices=[e.value for e in self.SecurityGroupState],
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
if self.aparams['id'] is not None:
self.id = self.aparams['id']
elif self.aparams['name'] is not None:
security_groups = self.api.cloudapi.security_group.list(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
)
if security_groups.data:
self.id = security_groups.data[0].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):
try:
storage_policy_model = self.api.cloudapi.security_group.get(
security_group_id=self.id
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='security_group',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = storage_policy_model.model_dump()
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):
id = self.sdk_checkmode(self.api.cloudapi.security_group.create)(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
description=self.aparams['description'],
)
if id:
self.id = id
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.sdk_checkmode(self.api.cloudapi.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.sdk_checkmode(self.api.cloudapi.security_group.delete)(
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.sdk_checkmode(self.api.cloudapi.security_group.create_rule)(
security_group_id=self.id,
traffic_direction=(
sdk_types.TrafficDirection[rule['direction']]
),
ethertype=(
sdk_types.SGRuleEthertype[rule['ethertype']]
if rule.get('ethertype') else sdk_types.SGRuleEthertype.IPV4
),
protocol=(
sdk_types.SGRuleProtocol[rule['protocol']]
if rule.get('protocol') else None
),
port_range_min=port_range_min,
port_range_max=port_range_max,
remote_net_cidr=rule.get('remote_net_cidr'),
)
def main():
DecortSecurityGroup().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,132 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_security_group_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortSecurityGroupList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
created_after_timestamp=dict(
type='int',
),
created_before_timestamp=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
updated_after_timestamp=dict(
type='int',
),
updated_before_timestamp=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=(
sdk_types.SecurityGroupAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.SecurityGroupAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.security_group.list(
account_id=aparam_filter['account_id'],
created_after_timestamp=aparam_filter['created_after_timestamp'],
created_before_timestamp=aparam_filter['created_before_timestamp'],
description=aparam_filter['description'],
id=aparam_filter['id'],
name=aparam_filter['name'],
updated_after_timestamp=aparam_filter['updated_after_timestamp'],
updated_before_timestamp=aparam_filter['updated_before_timestamp'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortSecurityGroupList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,65 @@
#!/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
from dynamix_sdk import exceptions as sdk_exceptions
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,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
try:
storage_policy_model = self.api.cloudapi.storage_policy.get(
id=self.id
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='storage_policy',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = storage_policy_model.model_dump()
def main():
DecortStoragePolicy().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,152 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_storage_policy_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortStoragePolicyList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
iops_limit=dict(
type='int',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
sep_pool_name=dict(
type='str',
),
status=dict(
type='str',
choices=(
sdk_types.StoragePolicyStatus._member_names_
),
),
sep_tech_status=dict(
type='str',
choices=sdk_types.SEPTechStatus._member_names_,
),
),
),
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=(
sdk_types.StoragePolicyAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_sep_tech_status: str | None = aparam_filter['sep_tech_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.StoragePolicyAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.storage_policy.list(
account_id=aparam_filter['account_id'],
description=aparam_filter['description'],
id=aparam_filter['id'],
iops_limit=aparam_filter['iops_limit'],
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
sep_id=aparam_filter['sep_id'],
sep_pool_name=aparam_filter['sep_pool_name'],
status=(
sdk_types.StoragePolicyStatus[aparam_status]
if aparam_status else None
),
sep_tech_status=(
sdk_types.SEPTechStatus[aparam_sep_tech_status]
if aparam_sep_tech_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortStoragePolicyList().run()
if __name__ == '__main__':
main()

65
library/decort_trunk.py Normal file
View File

@@ -0,0 +1,65 @@
#!/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
from dynamix_sdk import exceptions as sdk_exceptions
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,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
try:
trunk_model = self.api.cloudapi.trunk.get(
id=self.id
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='trunk',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = trunk_model.model_dump()
def main():
DecortTrunk().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,123 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_trunk_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortTrunkList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_ids=dict(
type='list',
elements='int',
),
ids=dict(
type='list',
elements='int',
),
status=dict(
type='str',
choices=sdk_types.TrunkStatus._member_names_,
),
vlan_ids=dict(
type='str',
),
),
),
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=(
sdk_types.TrunkAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.TrunkAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.trunk.list(
account_ids=aparam_filter['account_ids'],
ids=aparam_filter['ids'],
status=(
sdk_types.TrunkStatus[aparam_status]
if aparam_status else None
),
vlan_ids=aparam_filter['vlan_ids'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortTrunkList().run()
if __name__ == '__main__':
main()

68
library/decort_user.py Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_user
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 DecortUser(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(
api_methods=dict(
type='bool',
default=False,
),
objects_search=dict(
type='str',
),
resource_consumption=dict(
type='bool',
default=False,
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.usermanager_whoami_result
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['resource_consumption']:
self.facts.update(self.user_resource_consumption())
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,
)
def main():
DecortUser().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,45 @@
#!/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
def main():
module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
api_methods=dict(type='raw'),
objects_search=dict(type='raw'),
resource_consumption=dict(type='raw'),
),
supports_check_mode=True,
)
module.fail_json(
msg=(
'The module "decort_user_info" has been renamed to "decort_user". '
'Please update your playbook to use "decort_user" '
'instead of "decort_user_info".'
),
)
if __name__ == '__main__':
main()

470
library/decort_vins.py Normal file
View File

@@ -0,0 +1,470 @@
#!/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
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.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 self.vins_id:
vins_should_exist = True
if self.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
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
"status '{}'").format(self.vins_id, self.vins_facts['status'])
elif self.vins_facts['status'] == "DISABLED":
if amodule.params['state'] == 'absent':
self.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'disabled'):
# update ViNS, leave in disabled state
self.action()
elif amodule.params['state'] == 'enabled':
# update ViNS and enable
self.action('enabled')
elif self.vins_facts['status'] in ["CREATED", "ENABLED"]:
if amodule.params['state'] == 'absent':
self.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'enabled'):
# update ViNS
self.action()
elif amodule.params['state'] == 'disabled':
# disable and update ViNS
self.action('disabled')
elif self.vins_facts['status'] == "DELETED":
if amodule.params['state'] in ['present', 'enabled']:
# restore and enable
self.action(restore=True)
vins_should_exist = True
elif amodule.params['state'] == 'absent':
# destroy permanently
if self.amodule.params['permanently']:
self.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
self.error()
vins_should_exist = False
elif self.vins_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
# need to re-provision ViNS;
self.create()
vins_should_exist = True
elif amodule.params['state'] == 'absent':
self.nop()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
self.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':
self.nop()
elif amodule.params['state'] in ('present', 'enabled'):
self.check_amodule_argument('vins_name')
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
self.create()
vins_should_exist = True
elif amodule.params['state'] == 'disabled':
self.error()
#
# conditional switch end - complete module run
#
if self.result['failed']:
amodule.fail_json(**self.result)
else:
# prepare ViNS facts to be returned as part of self.result and then call exit_json(...)
if self.result['changed']:
_, self.vins_facts = self.vins_find(self.vins_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
def main():
decort_vins().run()
if __name__ == '__main__':
main()

141
library/decort_vins_list.py Normal file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vins_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortVINSList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
ext_net_ip=dict(
type='str',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
status=dict(
type='str',
choices=sdk_types.VINSStatus._member_names_,
),
vnfdev_id=dict(
type='int',
),
zone_id=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=(
sdk_types.VINSForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.VINSForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.vins.list(
account_id=aparam_filter['account_id'],
ext_net_ip=aparam_filter['ext_net_ip'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
status=(
sdk_types.VINSStatus[aparam_status]
if aparam_status else None
),
vnfdev_id=aparam_filter['vnfdev_id'],
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortVINSList().run()
if __name__ == '__main__':
main()

2510
library/decort_vm.py Normal file

File diff suppressed because it is too large Load Diff

158
library/decort_vm_list.py Normal file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortVMList(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(
filter=dict(
type='dict',
apply_defaults=True,
options=dict(
account_id=dict(
type='int',
),
ext_net_id=dict(
type='int',
),
ext_net_name=dict(
type='str',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
ip_addr=dict(
type='str',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
rg_name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.VMStatus._member_names_,
),
tech_status=dict(
type='str',
choices=sdk_types.VMTechStatus._member_names_,
),
zone_id=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=(
sdk_types.VMAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_tech_status: str | None = aparam_filter['tech_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.VMAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.compute.list(
account_id=aparam_filter['account_id'],
ext_net_id=aparam_filter['ext_net_id'],
ext_net_name=aparam_filter['ext_net_name'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
ip_addr=aparam_filter['ip_addr'],
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
rg_name=aparam_filter['rg_name'],
status=(
sdk_types.VMStatus[aparam_status]
if aparam_status else None
),
tech_status=(
sdk_types.VMTechStatus[aparam_tech_status]
if aparam_tech_status else None
),
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortVMList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,190 @@
#!/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.api.ca.compute.snapshot_list(
vm_id=self.vm_id).data
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)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.check_amodule_args_for_change()
self.change()
self.exit()
def get_info(self, update_vm_snapshots: bool = False):
if update_vm_snapshots:
self.vm_snapshots = self.api.cloudapi.compute.snapshot_list(
vm_id=self.aparams_vm_id,
).data
label = self.new_snapshot_label or self.aparams_label
for snapshot in self.vm_snapshots:
if snapshot.label == label:
self.facts = snapshot.model_dump()
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.sdk_checkmode(self.api.cloudapi.compute.snapshot_create)(
vm_id=self.aparams_vm_id,
label=self.new_snapshot_label,
)
self.get_info(update_vm_snapshots=True)
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(
'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()

63
library/decort_zone.py Normal file
View File

@@ -0,0 +1,63 @@
#!/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
from dynamix_sdk import exceptions as sdk_exceptions
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,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
try:
zone_model = self.api.cloudapi.zone.get(id=self.id)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='zone',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = zone_model.model_dump()
def main():
DecortZone().run()
if __name__ == '__main__':
main()

133
library/decort_zone_list.py Normal file
View File

@@ -0,0 +1,133 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_zone_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortZoneList(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(
filter=dict(
type='dict',
apply_defaults=True,
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=sdk_types.ZoneStatus._member_names_,
),
),
),
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=(
sdk_types.ZoneForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ZoneForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.zone.list(
deletable=aparam_filter['deletable'],
description=aparam_filter['description'],
grid_id=aparam_filter['grid_id'],
id=aparam_filter['id'],
name=aparam_filter['name'],
node_id=aparam_filter['node_id'],
status=(
sdk_types.ZoneStatus[aparam_status]
if aparam_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortZoneList().run()
if __name__ == '__main__':
main()

7205
module_utils/decort_utils.py Normal file

File diff suppressed because it is too large Load Diff

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
ansible==11.6.0
requests==2.32.3
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.4.latest