diff --git a/library/mt_ip_address.py b/library/mt_ip_address.py index f42afa3..e75ed4e 100644 --- a/library/mt_ip_address.py +++ b/library/mt_ip_address.py @@ -3,57 +3,93 @@ module: mt_ip_address author: - "Valentin Gurmeza" - "Shaun Smiley" + - "Antoni Matamalas" version_added: "2.3" short_description: Manage mikrotik /ip/addresses requirements: - - rosapi + - mt_api description: - - FILL ME OUT + - Manage addresses on interfaces options: hostname: description: - - + - hotstname of mikrotik router + required: True username: description: - - + - username used to connect to mikrotik router + required: True password: description: - - - interface: + - password used for authentication to mikrotik router + required: True + idempotent: description: - - - address: + - parameter that will define the behavior for the ip address status. + - If "interface" is used, only one IP will be allowed per interface. + The "state" parameter will define if the IP is added, edited or + removed. No settings options are required to removed the IP from an + interface + - If "address" is used, and interface will be able to have multiple IPs, + but address will only be added or removed. In order to change an IP, it + will have to be first removed and then added to the interface in two + tasks. + required: False + default: address + settings: description: - - - network: - description: - - + - All Mikrotik compatible parameters for this particular endpoint. + Any yes/no values must be enclosed in double quotes + required: True state: description: - - - force: - description: - - True/False value to force removing the address on an interface - even if the address does not match. + - Depending on the idempotent option, it will define the status of the IP + on an interface + required: False + default: present ''' EXAMPLES = ''' +# Add IP to an interface with a comment. If the interface has already an IP it +# will add as a sencond IP - mt_ip_address: hostname: "{{ inventory_hostname }}" username: "{{ mt_user }}" password: "{{ mt_pass }}" - interface: "ether2" - address: "192.168.88.2/24" - network: "192.168.88.0/24" + idempotent: "address" state: "present" - comment: "link 3" + settings: + interface: "ether2" + address: "192.168.88.2/24" + network: "192.168.88.0/24" + comment: "link 3" + +# Assign IP to the interface. If the interface has any previous IP, it will be +# replaced by this one. +- mt_ip_address: + hostname: "{{ inventory_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + idempotent: "interface" + state: "present" + settings: + interface: "ether2" + address: "192.168.88.2/24" + network: "192.168.88.0/24" + comment: "link 3" + +# Remove any IP from an interface +- mt_ip_address: + hostname: "{{ inventory_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + idempotent: "interface" + state: "absent" + settings: + interface: "ether2" ''' -from ansible.module_utils import mt_api -import socket - -#import mt_action #TODO: get this working - +from ansible.module_utils.mt_common import clean_params, MikrotikIdempotent from ansible.module_utils.basic import AnsibleModule @@ -64,120 +100,66 @@ def main(): hostname = dict(required=True), username = dict(required=True), password = dict(required=True, no_log=True), - interface = dict(required=True, type='str'), - address = dict(required=True, type='str', aliases=['ip', 'addr', 'ip_address']), - network = dict(required=False, type='str', default=""), - comment = dict(required=False, type='str', default=""), + settings = dict(required=True, type='dict'), + idempotent = dict( + required = False, + default = 'address', + choices = ['address', 'interface'], + type = 'str' + ), state = dict( required = False, default = "present", choices = ['present', 'absent'], type = 'str' ), - ) + ), + supports_check_mode=True ) - hostname = module.params['hostname'] - username = module.params['username'] - password = module.params['password'] - ip_address = module.params['address'] - interface = module.params['interface'] - network = module.params['network'] - ip_state = module.params['state'] - comment = module.params['comment'] - changed = False - msg = "" + params = module.params + mt_obj = MikrotikIdempotent( + hostname = params['hostname'], + username = params['username'], + password = params['password'], + state = params['state'], + desired_params = params['settings'], + idempotent_param = params['idempotent'], + api_path = '/ip/address', + check_mode = module.check_mode + ) - interface_path = '/interface' - address_path = '/ip/address' - address_print_params = { - ".proplist": "interface,address,.id,network,netmask,comment" - } - interface_print_params = { - ".proplist": "name,.id,type" - } - mk = mt_api.Mikrotik(hostname,username,password) - try: - mk.login() - interfaces = mk.api_print(interface_path, interface_print_params) - except: + # exit if login failed + if not mt_obj.login_success: module.fail_json( - msg="Could not log into Mikrotik device." + - " Check the username and password.", + msg = mt_obj.failed_msg ) - ################################### - # Check if interface is present - # exit if interface is not present - ################################### - interfacelist = [] - exitmessage = [] - for i in range(0, len(interfaces) - 1): - interfacelist.append(interfaces[i][1]["name"]) - intExists = False + # add, remove or edit things + mt_obj.sync_state() - if (interface in interfacelist): - intExists = True - # module.exit_json(failed=False, changed=False, msg=interfacelist) - if intExists: - pass - #exitmessage.append("Interface " + interface + " exists.") #this is never used + if mt_obj.failed: + module.fail_json( + msg = mt_obj.failed_msg + ) + elif mt_obj.changed: + module.exit_json( + failed=False, + changed=True, + msg=mt_obj.changed_msg, + diff={ "prepared": { + "old": mt_obj.old_params, + "new": mt_obj.new_params, + }}, + ) else: - exitmessage.append("Interface " + interface + " does not exist.") - module.fail_json(failed=True, msg=exitmessage) - - ############################################## - # Check if IP address is set on the interface - # make no changes if address already set - ############################################## - ip_addresses = mk.api_print(address_path, address_print_params) - - iplist = [] - for i in range(0, len(ip_addresses) - 1): - iplist.append(ip_addresses[i][1]["address"]) - if ip_addresses[i][1]["address"] == ip_address: - ip_id = ip_addresses[i][1][".id"] - - if ip_state == "present": - if ip_address in iplist: - module.exit_json( - failed=False, - #msg="IP Address: " + ip_address + - #" is already configured" + - #" on interface " + interface, - ) - - else: - add_dict = { - 'address': ip_address, - 'interface': interface, - 'comment': comment - } - response = mk.api_add(address_path, add_dict) - module.exit_json( - failed=False, - changed=True, - #msg="IP address: " + ip_address + " has been configured" + - #" on interface " + interface - ) - - if ip_state == "absent": - if ip_address in iplist: - response = mk.api_remove(address_path, ip_id) - module.exit_json( - failed=False, - changed=True, - #msg="IP Address: " + ip_address + - #" has been removed" - ) - - else: - module.exit_json( - failed=False, - changed=False, - #msg="IP Address: " + ip_address + - #" is already absent" - ) + module.exit_json( + failed=False, + changed=False, + #msg='', + msg=params['settings'], + ) if __name__ == '__main__': main() + diff --git a/tests/integration/tasks/test-ip-address.yml b/tests/integration/tasks/test-ip-address.yml index 981324c..fda8be2 100644 --- a/tests/integration/tasks/test-ip-address.yml +++ b/tests/integration/tasks/test-ip-address.yml @@ -16,46 +16,255 @@ - "bridge3" - "bridge4" -- name: ALWAYS_CHANGES Test adding an ip addr ether2 +- name: Remove any ip from bridge interfaces + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: absent + idempotent: interface + settings: + interface: "{{ item }}" + with_items: + - "bridge1" + - "bridge2" + - "bridge3" + - "bridge4" + +- name: ALWAYS_CHANGES Test adding an ip addr bridge2 mt_ip_address: hostname: "{{ mt_hostname }}" username: "{{ mt_user }}" password: "{{ mt_pass }}" state: "present" - interface: "ether2" - address: "192.168.88.2/24" - network: "192.168.88.0" + settings: + interface: "bridge2" + address: "192.168.88.2/24" + network: "192.168.88.0" register: ip_addr_add_2 failed_when: not ( ip_addr_add_2 | changed ) -- name: Test adding an ip addr with comment bridge2 +- name: NEVER_CHANGES Test adding the same ip addr again to bridge2 mt_ip_address: hostname: "{{ mt_hostname }}" username: "{{ mt_user }}" password: "{{ mt_pass }}" state: "present" - interface: "bridge2" - address: "192.168.88.3/24" - comment: "bridge #2!!!" + settings: + interface: "bridge2" + address: "192.168.88.2/24" + network: "192.168.88.0" + register: ip_addr_add_2 + failed_when: ip_addr_add_2 | changed -- name: Test adding an ip addr with comment and network bridge3 +- name: Test adding an ip addr with comment bridge3 mt_ip_address: hostname: "{{ mt_hostname }}" username: "{{ mt_user }}" password: "{{ mt_pass }}" state: "present" - interface: "bridge3" - address: "192.168.88.4/24" - network: "192.168.88.0" - comment: "bridge #3!!!" + settings: + interface: "bridge3" + address: "192.168.88.3/24" + comment: "bridge #3!!!" -- name: ALWAYS_CHANGES Test removing ip addr ether2 +- name: Test adding an ip addr with comment and network bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + settings: + interface: "bridge4" + address: "192.168.88.4/24" + network: "192.168.88.0" + comment: "bridge4 first addition" + +- name: Test adding a second ip addr with comment and network bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + settings: + interface: "bridge4" + address: "192.168.88.5/24" + network: "192.168.88.0" + comment: "bridge #4 second addition" + +- name: ALWAYS_CHANGES Test removing ip addr bridge2 mt_ip_address: hostname: "{{ mt_hostname }}" username: "{{ mt_user }}" password: "{{ mt_pass }}" state: "absent" - interface: "ether2" - address: "192.168.88.2/24" + settings: + interface: "bridge2" + address: "192.168.88.2/24" register: ip_addr_rem_2 failed_when: not ( ip_addr_rem_2 | changed ) + +- name: NEVER_CHANGES Test removing ip addr bridge2 again + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "absent" + settings: + interface: "bridge2" + address: "192.168.88.2/24" + register: ip_addr_rem_2 + failed_when: ip_addr_rem_2 | changed + +- name: ALWAYS_CHANGES Test removing an ip addr from bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "absent" + settings: + interface: "bridge4" + address: "192.168.88.4/24" + register: ip_addr_rem_4 + failed_when: not (ip_addr_rem_4 | changed ) + +- name: NEVER_CHANGES Verify ip addr from bridge4 has been removed + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "absent" + settings: + interface: "bridge4" + address: "192.168.88.4/24" + register: ip_addr_rem_4 + failed_when: ip_addr_rem_4 | changed + +- name: NEVER_CHANGES Verify that we have the right ip address in bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + settings: + interface: "bridge4" + address: "192.168.88.5/24" + register: ip_addr_rem_4 + failed_when: ip_addr_rem_4 | changed + +- name: Remove any IP from bridge interfaces + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: absent + idempotent: interface + settings: + interface: "{{ item }}" + with_items: + - "bridge1" + - "bridge2" + - "bridge3" + - "bridge4" + +- name: ALWAYS_CHANGES Add ip using idempotent interface on bridge2 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge2" + address: "192.168.89.2/24" + register: ip_addr_rem_2 + failed_when: not (ip_addr_rem_2 | changed) + +- name: NEVER_CHANGES Add AGAIN ip using idempotent interface on bridge2 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge2" + address: "192.168.89.2/24" + register: ip_addr_rem_2 + failed_when: ip_addr_rem_2 | changed + +- name: ALWAYS_CHANGES Add ip using idempotent interface on bridge3 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge3" + address: "192.168.89.3/24" + register: ip_addr_rem_3 + failed_when: not (ip_addr_rem_3 | changed) + +- name: ALWAYS_CHANGES Remove ip using idempotent interface on bridge3 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "absent" + idempotent: "interface" + settings: + interface: "bridge3" + register: ip_addr_rem_3 + failed_when: not (ip_addr_rem_3 | changed) + +- name: NEVER_CHANGES Remove ip AGAIN using idempotent interface on bridge3 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "absent" + idempotent: "interface" + settings: + interface: "bridge3" + register: ip_addr_rem_3 + failed_when: ip_addr_rem_3 | changed + +- name: ALWAYS_CHANGES Add ip using idempotent interface on bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge4" + address: "192.168.89.4/24" + register: ip_addr_rem_4 + failed_when: not (ip_addr_rem_4 | changed) + +- name: ALWAYS_CHANGES Edit ip using idempotent interface on bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge4" + address: "192.168.89.5/24" + register: ip_addr_rem_4 + failed_when: not (ip_addr_rem_4 | changed) + +- name: ALWAYS_CHANGES Add comment using idempotent interface on bridge4 + mt_ip_address: + hostname: "{{ mt_hostname }}" + username: "{{ mt_user }}" + password: "{{ mt_pass }}" + state: "present" + idempotent: "interface" + settings: + interface: "bridge4" + comment: "This is a final comment" + register: ip_addr_rem_4 + failed_when: not (ip_addr_rem_4 | changed)