1# -*- coding: utf-8 -*-
2# This code is part of Ansible, but is an independent component.
3# This particular file snippet, and this file snippet only, is BSD licensed.
4# Modules you write using this snippet, which is embedded dynamically by Ansible
5# still belong to the author of the module, and may assign their own license
6# to the complete work.
7#
8# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
9#
10# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
11
12from __future__ import (absolute_import, division, print_function)
13__metaclass__ = type
14
15import os
16import json
17from ansible.module_utils.basic import env_fallback
18
19try:
20    import footmark
21    import footmark.ecs
22    import footmark.slb
23    import footmark.vpc
24    import footmark.rds
25    import footmark.ess
26    import footmark.sts
27    import footmark.dns
28    import footmark.ram
29    import footmark.market
30    HAS_FOOTMARK = True
31except ImportError:
32    HAS_FOOTMARK = False
33
34
35class AnsibleACSError(Exception):
36    pass
37
38
39def acs_common_argument_spec():
40    return dict(
41        alicloud_access_key=dict(aliases=['access_key_id', 'access_key'], no_log=True,
42                                 fallback=(env_fallback, ['ALICLOUD_ACCESS_KEY', 'ALICLOUD_ACCESS_KEY_ID'])),
43        alicloud_secret_key=dict(aliases=['secret_access_key', 'secret_key'], no_log=True,
44                                 fallback=(env_fallback, ['ALICLOUD_SECRET_KEY', 'ALICLOUD_SECRET_ACCESS_KEY'])),
45        alicloud_security_token=dict(aliases=['security_token'], no_log=True,
46                                     fallback=(env_fallback, ['ALICLOUD_SECURITY_TOKEN'])),
47        ecs_role_name=dict(aliases=['role_name'], fallback=(env_fallback, ['ALICLOUD_ECS_ROLE_NAME']))
48    )
49
50
51def ecs_argument_spec():
52    spec = acs_common_argument_spec()
53    spec.update(
54        dict(
55            alicloud_region=dict(required=True, aliases=['region', 'region_id'],
56                                 fallback=(env_fallback, ['ALICLOUD_REGION', 'ALICLOUD_REGION_ID'])),
57            alicloud_assume_role_arn=dict(fallback=(env_fallback, ['ALICLOUD_ASSUME_ROLE_ARN']),
58                                          aliases=['assume_role_arn']),
59            alicloud_assume_role_session_name=dict(fallback=(env_fallback, ['ALICLOUD_ASSUME_ROLE_SESSION_NAME']),
60                                                   aliases=['assume_role_session_name']),
61            alicloud_assume_role_session_expiration=dict(type='int',
62                                                         fallback=(env_fallback,
63                                                                   ['ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION']),
64                                                         aliases=['assume_role_session_expiration']),
65            alicloud_assume_role=dict(type='dict', aliases=['assume_role']),
66            profile=dict(fallback=(env_fallback, ['ALICLOUD_PROFILE'])),
67            shared_credentials_file=dict(fallback=(env_fallback, ['ALICLOUD_SHARED_CREDENTIALS_FILE']))
68        )
69    )
70    return spec
71
72
73def get_acs_connection_info(params):
74
75    ecs_params = dict(acs_access_key_id=params.get('alicloud_access_key'),
76                      acs_secret_access_key=params.get('alicloud_secret_key'),
77                      security_token=params.get('alicloud_security_token'),
78                      ecs_role_name=params.get('ecs_role_name'),
79                      user_agent='Ansible-Provider-Alicloud')
80    return ecs_params
81
82
83def connect_to_acs(acs_module, region, **params):
84    conn = acs_module.connect_to_region(region, **params)
85    if not conn:
86        if region not in [acs_module_region.id for acs_module_region in acs_module.regions()]:
87            raise AnsibleACSError(
88                "Region %s does not seem to be available for acs module %s." % (region, acs_module.__name__))
89        else:
90            raise AnsibleACSError(
91                "Unknown problem connecting to region %s for acs module %s." % (region, acs_module.__name__))
92    return conn
93
94
95def get_assume_role(params):
96    """ Return new params """
97    sts_params = get_acs_connection_info(params)
98    assume_role = {}
99    if params.get('assume_role'):
100        assume_role['alicloud_assume_role_arn'] = params['assume_role'].get('role_arn')
101        assume_role['alicloud_assume_role_session_name'] = params['assume_role'].get('session_name')
102        assume_role['alicloud_assume_role_session_expiration'] = params['assume_role'].get('session_expiration')
103        assume_role['alicloud_assume_role_policy'] = params['assume_role'].get('policy')
104
105    assume_role_params = {
106        'role_arn': params.get('alicloud_assume_role_arn') if params.get('alicloud_assume_role_arn') else assume_role.get('alicloud_assume_role_arn'),
107        'role_session_name': params.get('alicloud_assume_role_session_name') if params.get('alicloud_assume_role_session_name')
108        else assume_role.get('alicloud_assume_role_session_name'),
109        'duration_seconds': params.get('alicloud_assume_role_session_expiration') if params.get('alicloud_assume_role_session_expiration')
110        else assume_role.get('alicloud_assume_role_session_expiration', 3600),
111        'policy': assume_role.get('alicloud_assume_role_policy', {})
112    }
113
114    try:
115        sts = connect_to_acs(footmark.sts, params.get('alicloud_region'), **sts_params).assume_role(**assume_role_params).read()
116        sts_params['acs_access_key_id'], sts_params['acs_secret_access_key'], sts_params['security_token'] \
117            = sts['access_key_id'], sts['access_key_secret'], sts['security_token']
118    except AnsibleACSError as e:
119        params.fail_json(msg=str(e))
120    return sts_params
121
122
123def get_profile(params):
124    if not params['alicloud_access_key'] and not params['ecs_role_name'] and params['profile']:
125        path = params['shared_credentials_file'] if params['shared_credentials_file'] else os.getenv('HOME') + '/.aliyun/config.json'
126        auth = {}
127        with open(path, 'r') as f:
128            for pro in json.load(f)['profiles']:
129                if params['profile'] == pro['name']:
130                    auth = pro
131        if auth:
132            if auth['mode'] == 'AK' and auth.get('access_key_id') and auth.get('access_key_secret'):
133                params['alicloud_access_key'] = auth.get('access_key_id')
134                params['alicloud_secret_key'] = auth.get('access_key_secret')
135                params['alicloud_region'] = auth.get('region_id')
136                params = get_acs_connection_info(params)
137            elif auth['mode'] == 'StsToken' and auth.get('access_key_id') and auth.get('access_key_secret') and auth.get('sts_token'):
138                params['alicloud_access_key'] = auth.get('access_key_id')
139                params['alicloud_secret_key'] = auth.get('access_key_secret')
140                params['security_token'] = auth.get('sts_token')
141                params['alicloud_region'] = auth.get('region_id')
142                params = get_acs_connection_info(params)
143            elif auth['mode'] == 'EcsRamRole':
144                params['ecs_role_name'] = auth.get('ram_role_name')
145                params['alicloud_region'] = auth.get('region_id')
146                params = get_acs_connection_info(params)
147            elif auth['mode'] == 'RamRoleArn' and auth.get('ram_role_arn'):
148                params['alicloud_access_key'] = auth.get('access_key_id')
149                params['alicloud_secret_key'] = auth.get('access_key_secret')
150                params['security_token'] = auth.get('sts_token')
151                params['ecs_role_name'] = auth.get('ram_role_name')
152                params['alicloud_assume_role_arn'] = auth.get('ram_role_arn')
153                params['alicloud_assume_role_session_name'] = auth.get('ram_session_name')
154                params['alicloud_assume_role_session_expiration'] = auth.get('expired_seconds')
155                params['alicloud_region'] = auth.get('region_id')
156                params = get_assume_role(params)
157    elif params.get('alicloud_assume_role_arn') or params.get('assume_role'):
158        params = get_assume_role(params)
159    else:
160        params = get_acs_connection_info(params)
161    return params
162
163
164def ecs_connect(module):
165    """ Return an ecs connection"""
166    ecs_params = get_profile(module.params)
167    # If we have a region specified, connect to its endpoint.
168    region = module.params.get('alicloud_region')
169    if region:
170        try:
171            ecs = connect_to_acs(footmark.ecs, region, **ecs_params)
172        except AnsibleACSError as e:
173            module.fail_json(msg=str(e))
174    # Otherwise, no region so we fallback to the old connection method
175    return ecs
176
177
178def slb_connect(module):
179    """ Return an slb connection"""
180    slb_params = get_profile(module.params)
181    # If we have a region specified, connect to its endpoint.
182    region = module.params.get('alicloud_region')
183    if region:
184        try:
185            slb = connect_to_acs(footmark.slb, region, **slb_params)
186        except AnsibleACSError as e:
187            module.fail_json(msg=str(e))
188    # Otherwise, no region so we fallback to the old connection method
189    return slb
190
191
192def dns_connect(module):
193    """ Return an dns connection"""
194    dns_params = get_profile(module.params)
195    # If we have a region specified, connect to its endpoint.
196    region = module.params.get('alicloud_region')
197    if region:
198        try:
199            dns = connect_to_acs(footmark.dns, region, **dns_params)
200        except AnsibleACSError as e:
201            module.fail_json(msg=str(e))
202    # Otherwise, no region so we fallback to the old connection method
203    return dns
204
205
206def vpc_connect(module):
207    """ Return an vpc connection"""
208    vpc_params = get_profile(module.params)
209    # If we have a region specified, connect to its endpoint.
210    region = module.params.get('alicloud_region')
211    if region:
212        try:
213            vpc = connect_to_acs(footmark.vpc, region, **vpc_params)
214        except AnsibleACSError as e:
215            module.fail_json(msg=str(e))
216    # Otherwise, no region so we fallback to the old connection method
217    return vpc
218
219
220def rds_connect(module):
221    """ Return an rds connection"""
222    rds_params = get_profile(module.params)
223    # If we have a region specified, connect to its endpoint.
224    region = module.params.get('alicloud_region')
225    if region:
226        try:
227            rds = connect_to_acs(footmark.rds, region, **rds_params)
228        except AnsibleACSError as e:
229            module.fail_json(msg=str(e))
230    # Otherwise, no region so we fallback to the old connection method
231    return rds
232
233
234def ess_connect(module):
235    """ Return an ess connection"""
236    ess_params = get_profile(module.params)
237    # If we have a region specified, connect to its endpoint.
238    region = module.params.get('alicloud_region')
239    if region:
240        try:
241            ess = connect_to_acs(footmark.ess, region, **ess_params)
242        except AnsibleACSError as e:
243            module.fail_json(msg=str(e))
244    # Otherwise, no region so we fallback to the old connection method
245    return ess
246
247
248def sts_connect(module):
249    """ Return an sts connection"""
250    sts_params = get_profile(module.params)
251    # If we have a region specified, connect to its endpoint.
252    region = module.params.get('alicloud_region')
253    if region:
254        try:
255            sts = connect_to_acs(footmark.sts, region, **sts_params)
256        except AnsibleACSError as e:
257            module.fail_json(msg=str(e))
258    # Otherwise, no region so we fallback to the old connection method
259    return sts
260
261
262def ram_connect(module):
263    """ Return an ram connection"""
264    ram_params = get_profile(module.params)
265    # If we have a region specified, connect to its endpoint.
266    region = module.params.get('alicloud_region')
267    if region:
268        try:
269            ram = connect_to_acs(footmark.ram, region, **ram_params)
270        except AnsibleACSError as e:
271            module.fail_json(msg=str(e))
272    # Otherwise, no region so we fallback to the old connection method
273    return ram
274
275
276def market_connect(module):
277    """ Return an market connection"""
278    market_params = get_profile(module.params)
279    # If we have a region specified, connect to its endpoint.
280    region = module.params.get('alicloud_region')
281    if region:
282        try:
283            market = connect_to_acs(footmark.market, region, **market_params)
284        except AnsibleACSError as e:
285            module.fail_json(msg=str(e))
286    # Otherwise, no region so we fallback to the old connection method
287    return market
288