1#!/usr/bin/python
2#
3# Copyright (c) 2018 Yuwei Zhou, <yuwzho@microsoft.com>
4#
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'community'}
14
15
16DOCUMENTATION = '''
17---
18module: azure_rm_route
19version_added: "2.7"
20short_description: Manage Azure route resource
21description:
22    - Create, update or delete a route.
23options:
24    resource_group:
25        description:
26            - Name of resource group.
27        required: true
28    name:
29        description:
30            - Name of the route.
31        required: true
32    state:
33        description:
34            - Assert the state of the route. Use C(present) to create or update and C(absent) to delete.
35        default: present
36        choices:
37            - absent
38            - present
39    address_prefix:
40        description:
41            - The destination CIDR to which the route applies.
42    next_hop_type:
43        description:
44            - The type of Azure hop the packet should be sent to.
45        choices:
46            - virtual_network_gateway
47            - vnet_local
48            - internet
49            - virtual_appliance
50            - none
51        default: 'none'
52    next_hop_ip_address:
53        description:
54            - The IP address packets should be forwarded to.
55            - Next hop values are only allowed in routes where the next hop type is VirtualAppliance.
56    route_table_name:
57        description:
58            - The name of the route table.
59        required: true
60
61
62extends_documentation_fragment:
63    - azure
64    - azure_tags
65
66author:
67    - Yuwei Zhou (@yuwzho)
68
69'''
70
71EXAMPLES = '''
72    - name: Create a route
73      azure_rm_route:
74        resource_group: myResourceGroup
75        name: myRoute
76        address_prefix: 10.1.0.0/16
77        next_hop_type: virtual_network_gateway
78        route_table_name: table
79
80    - name: Delete a route
81      azure_rm_route:
82        resource_group: myResourceGroup
83        name: myRoute
84        route_table_name: table
85        state: absent
86'''
87RETURN = '''
88id:
89    description:
90        - Current state of the route.
91    returned: success
92    type: str
93    sample: "/subscriptions/xxxx...xxxx/resourceGroups/v-xisuRG/providers/Microsoft.Network/routeTables/tableb57/routes/routeb57"
94'''
95
96try:
97    from msrestazure.azure_exceptions import CloudError
98except ImportError:
99    # This is handled in azure_rm_common
100    pass
101
102from ansible.module_utils.azure_rm_common import AzureRMModuleBase
103from ansible.module_utils.common.dict_transformations import _snake_to_camel
104
105
106class AzureRMRoute(AzureRMModuleBase):
107
108    def __init__(self):
109
110        self.module_arg_spec = dict(
111            resource_group=dict(type='str', required=True),
112            name=dict(type='str', required=True),
113            state=dict(type='str', default='present', choices=['present', 'absent']),
114            address_prefix=dict(type='str'),
115            next_hop_type=dict(type='str',
116                               choices=['virtual_network_gateway',
117                                        'vnet_local',
118                                        'internet',
119                                        'virtual_appliance',
120                                        'none'],
121                               default='none'),
122            next_hop_ip_address=dict(type='str'),
123            route_table_name=dict(type='str', required=True)
124        )
125
126        required_if = [
127            ('state', 'present', ['next_hop_type'])
128        ]
129
130        self.resource_group = None
131        self.name = None
132        self.state = None
133        self.address_prefix = None
134        self.next_hop_type = None
135        self.next_hop_ip_address = None
136        self.route_table_name = None
137
138        self.results = dict(
139            changed=False,
140            id=None
141        )
142
143        super(AzureRMRoute, self).__init__(self.module_arg_spec,
144                                           required_if=required_if,
145                                           supports_check_mode=True)
146
147    def exec_module(self, **kwargs):
148
149        for key in list(self.module_arg_spec.keys()):
150            setattr(self, key, kwargs[key])
151
152        result = dict()
153        changed = False
154
155        self.next_hop_type = _snake_to_camel(self.next_hop_type, capitalize_first=True)
156
157        result = self.get_route()
158        if self.state == 'absent' and result:
159            changed = True
160            if not self.check_mode:
161                self.delete_route()
162        elif self.state == 'present':
163            if not result:
164                changed = True  # create new route
165            else:  # check update
166                if result.next_hop_type != self.next_hop_type:
167                    self.log('Update: {0} next_hop_type from {1} to {2}'.format(self.name, result.next_hop_type, self.next_hop_type))
168                    changed = True
169                if result.next_hop_ip_address != self.next_hop_ip_address:
170                    self.log('Update: {0} next_hop_ip_address from {1} to {2}'.format(self.name, result.next_hop_ip_address, self.next_hop_ip_address))
171                    changed = True
172                if result.address_prefix != self.address_prefix:
173                    self.log('Update: {0} address_prefix from {1} to {2}'.format(self.name, result.address_prefix, self.address_prefix))
174                    changed = True
175            if changed:
176                result = self.network_models.Route(name=self.name,
177                                                   address_prefix=self.address_prefix,
178                                                   next_hop_type=self.next_hop_type,
179                                                   next_hop_ip_address=self.next_hop_ip_address)
180                if not self.check_mode:
181                    result = self.create_or_update_route(result)
182
183        self.results['id'] = result.id if result else None
184        self.results['changed'] = changed
185        return self.results
186
187    def create_or_update_route(self, param):
188        try:
189            poller = self.network_client.routes.create_or_update(self.resource_group, self.route_table_name, self.name, param)
190            return self.get_poller_result(poller)
191        except Exception as exc:
192            self.fail("Error creating or updating route {0} - {1}".format(self.name, str(exc)))
193
194    def delete_route(self):
195        try:
196            poller = self.network_client.routes.delete(self.resource_group, self.route_table_name, self.name)
197            result = self.get_poller_result(poller)
198            return result
199        except Exception as exc:
200            self.fail("Error deleting route {0} - {1}".format(self.name, str(exc)))
201
202    def get_route(self):
203        try:
204            return self.network_client.routes.get(self.resource_group, self.route_table_name, self.name)
205        except CloudError as cloud_err:
206            # Return None iff the resource is not found
207            if cloud_err.status_code == 404:
208                self.log('{0}'.format(str(cloud_err)))
209                return None
210            self.fail('Error: failed to get resource {0} - {1}'.format(self.name, str(cloud_err)))
211        except Exception as exc:
212            self.fail('Error: failed to get resource {0} - {1}'.format(self.name, str(exc)))
213
214
215def main():
216    AzureRMRoute()
217
218
219if __name__ == '__main__':
220    main()
221