Files
decort-ansible/library/decort_sdn_segment.py
2026-06-01 18:27:15 +03:00

358 lines
11 KiB
Python

#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_sdn_segment
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
import requests
class DecortSDNSegment(DecortController):
REQUIRED_FIELDS = (
'access_group_id',
'description',
'dhcp_v4',
'dhcp_v6',
'display_name',
'subnet_v4',
'subnet_v6',
'type',
)
segment_id: str | None = None
_segment_info: dict[str, Any] | None = None
need_final_get: bool = True
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(
access_group_id=dict(
type='str',
),
description=dict(
type='str',
),
dhcp_v4=dict(
type='dict',
),
dhcp_v6=dict(
type='dict',
),
display_name=dict(
type='str',
),
segment_id=dict(
type='str',
),
state=dict(
type='str',
choices=[
'present',
'enabled',
'disabled',
'absent',
'absent_force',
],
),
subnet_v4=dict(
type='str',
),
subnet_v6=dict(
type='str',
),
type=dict(
type='str',
choices=['User', 'ExtNet'],
),
version_id=dict(
type='int',
),
),
supports_check_mode=True,
)
@property
def segment_info(self) -> dict[str, Any]:
if self._segment_info is None:
if not isinstance(self.segment_id, str):
raise TypeError
segment_info = self.segment_get()
if segment_info is None:
raise TypeError
self._segment_info = segment_info
return self._segment_info
def run(self):
state = self.aparams['state']
if self.aparams['segment_id']:
self.segment_id = self.aparams['segment_id']
self._segment_info = self.segment_get(fail_if_not_found=False)
elif self.aparams['display_name']:
segment_info = self.segment_find(
display_name=self.aparams['display_name']
)
self._segment_info = segment_info
if segment_info:
self.segment_id = segment_info['id']
if state in ('absent', 'absent_force'):
if self._segment_info is None:
self.exit()
if self.segment_id:
self.segment_delete(
force=state == 'absent_force',
)
else:
self.need_final_get = False
elif state == 'present':
if self.segment_id:
self.check_amodule_args_for_update()
self.segment_update()
else:
self.check_amodule_args_for_create()
self.segment_create()
elif state in ('enabled', 'disabled'):
if not self.segment_id:
self.message(
'Check for parameter "state" failed: state values '
'"enabled"/"disabled" can only be applied to an existing '
'segment.'
)
self.check_amodule_args_for_update()
self.segment_update(
desired_enabled=(state == 'enabled'),
)
if self.need_final_get:
self.facts = self.segment_get()
self.exit()
def check_amodule_args_for_create(self):
check_errors = False
if (
self.aparams['subnet_v4'] is None
and self.aparams['subnet_v6'] is None
):
check_errors = True
self.message(
'Check for parameters "subnet_v4/subnet_v6" failed: at '
'least one of these parameters must be specified when '
'creating a segment.'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_update(self):
check_errors = False
if (
self.aparams['version_id'] is not None
and self.aparams['version_id'] != self.segment_info['version_id']
):
check_errors = True
self.message(
'Check for parameters "version_id" failed: '
'segment version mismatch: '
f'given version: {self.aparams['version_id']}, '
f'current version: {self.segment_info['version_id']}.'
)
if check_errors:
self.exit(fail=True)
@DecortController.waypoint
@DecortController.checkmode
def segment_get(
self,
access_group_id: str | None = None,
fail_if_not_found=True,
) -> dict[str, Any] | None:
params = {'segment_id': self.segment_id}
if access_group_id is not None:
params['access_group_id'] = access_group_id
response = self.decort_api_call(
arg_req_function=requests.get,
arg_api_name='/restmachine/sdn/segment/get',
arg_params=params,
not_fail_codes=[404],
accept_json_response=True,
)
if response.status_code == 404:
if fail_if_not_found:
self.message(
self.MESSAGES.obj_not_found(
obj='segment',
id=self.segment_id,
)
)
self.exit(fail=True)
else:
return None
return response.json()
@DecortController.waypoint
@DecortController.checkmode
def segment_find(self, display_name: str) -> dict[str, Any] | None:
response = self.decort_api_call(
arg_req_function=requests.get,
arg_api_name='/restmachine/sdn/segment/list',
arg_params={
'display_name': display_name,
},
accept_json_response=True,
)
return response.json()[0] if response.json() else None
@DecortController.waypoint
@DecortController.checkmode
def segment_create(self):
payload = dict()
for field in self.REQUIRED_FIELDS:
value = self.aparams[field]
if value is not None:
payload[field] = value
payload['enabled'] = True
response = self.decort_api_call(
arg_req_function=requests.post,
arg_api_name='/restmachine/sdn/segment/create',
arg_json_body=payload,
accept_json_response=True,
)
self._segment_info = response.json()
self.segment_id = response.json()['id']
self.set_changed()
@DecortController.waypoint
@DecortController.checkmode
def segment_update(
self,
desired_enabled: bool | None = None,
):
need_update = False
for field in self.REQUIRED_FIELDS:
value = self.aparams[field]
if value is None:
continue
current_value = self.segment_info.get(field)
if isinstance(value, dict) and isinstance(current_value, dict):
if any(
current_value.get(key) != expected_value
for key, expected_value in value.items()
):
need_update = True
break
continue
if value != current_value:
need_update = True
break
if (
not need_update
and desired_enabled is not None
and desired_enabled != self.segment_info['enabled']
):
need_update = True
if need_update:
payload = {
'segment_id': self.segment_id,
'version_id': (
self.aparams['version_id']
or self.segment_info['version_id']
),
'access_group_id': (
self.aparams['access_group_id']
or self.segment_info['access_group_id']
),
'description': (
self.aparams['description']
or self.segment_info['description']
),
'dhcp_v4': (
self.aparams['dhcp_v4'] or self.segment_info.get('dhcp_v4')
),
'dhcp_v6': (
self.aparams['dhcp_v6'] or self.segment_info.get('dhcp_v6')
),
'display_name': (
self.aparams['display_name']
or self.segment_info['display_name']
),
'enabled': (
desired_enabled
if desired_enabled is not None
else self.segment_info['enabled']
),
'subnet_v4': (
self.aparams['subnet_v4']
or self.segment_info.get('subnet_v4')
),
'subnet_v6': (
self.aparams['subnet_v6']
or self.segment_info.get('subnet_v6')
),
'type': (
self.aparams['type'] or self.segment_info['type']
),
}
self.decort_api_call(
arg_req_function=requests.put,
arg_api_name='/restmachine/sdn/segment/update',
arg_json_body=payload,
accept_json_response=True,
)
self.set_changed()
@DecortController.waypoint
@DecortController.checkmode
def segment_delete(
self,
force: bool = False,
):
version_id = self.aparams['version_id']
if version_id is None:
version_id = self.segment_info['version_id']
payload = {
'segment_id': self.segment_id,
'version_id': version_id,
}
self.decort_api_call(
arg_req_function=requests.delete,
arg_api_name='/restmachine/sdn/segment/delete',
arg_params=payload,
)
self.need_final_get = False
self.set_changed()
self.message(
self.MESSAGES.obj_deleted(
obj='segment',
id=self.segment_id,
permanently=force,
)
)
self.facts = {}
def main():
DecortSDNSegment().run()
if __name__ == '__main__':
main()