#!/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()