1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Add param for doc/fragments/alicloud (#108)

This commit is contained in:
lx 2020-04-13 14:23:38 +08:00 committed by GitHub
parent 6d7f66539c
commit 4ebb65e6f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 791 additions and 378 deletions

View file

@ -0,0 +1,6 @@
changes:
- "alicloud modules - now only support Python 3.6, not support Python 2.x"
- "ali_instance and ali_instance_info - the required package footmark needs a version higher than 1.19.0"
- "ali_instance - Add params ``unique_suffix``, ``tags``, ``purge_tags``, ``ram_role_name``, ``spot_price_limit``, ``spot_strategy``, ``period_unit``, ``dry_run``, ``include_data_disks``"
- "ali_instance_info - Add params ``name_prefix``, ``filters``"
- "alicloud modules - Add authentication params to all modules"

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: (c) 2017 Alibaba Group Holding Limited. He Guimin <heguimin36@163.com> # Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -11,46 +11,95 @@ class ModuleDocFragment(object):
options: options:
alicloud_access_key: alicloud_access_key:
description: description:
- Aliyun Cloud access key. - Alibaba Cloud access key. If not set then the value of environment variable C(ALICLOUD_ACCESS_KEY),
- If not set then the value of environment variable C(ALICLOUD_ACCESS_KEY), C(ALICLOUD_ACCESS_KEY_ID) will be used instead.
C(ALICLOUD_ACCESS_KEY_ID) will be used instead. aliases: ['access_key_id', 'access_key']
type: str type: str
aliases: [ access_key_id, access_key ]
alicloud_secret_key: alicloud_secret_key:
description: description:
- Aliyun Cloud secret key. - Alibaba Cloud secret key. If not set then the value of environment variable C(ALICLOUD_SECRET_KEY),
- If not set then the value of environment variable C(ALICLOUD_SECRET_KEY), C(ALICLOUD_SECRET_ACCESS_KEY) will be used instead.
C(ALICLOUD_SECRET_ACCESS_KEY) will be used instead. aliases: ['secret_access_key', 'secret_key']
type: str type: str
aliases: [ secret_access_key, secret_key ]
alicloud_region: alicloud_region:
description: description:
- The Aliyun Cloud region to use. - The Alibaba Cloud region to use. If not specified then the value of environment variable
- If not specified then the value of environment variable C(ALICLOUD_REGION), C(ALICLOUD_REGION_ID) will be used instead.
C(ALICLOUD_REGION), C(ALICLOUD_REGION_ID) will be used instead. aliases: ['region', 'region_id']
required: true
type: str type: str
aliases: [ region, region_id ]
alicloud_security_token: alicloud_security_token:
description: description:
- The Aliyun Cloud security token. - The Alibaba Cloud security token. If not specified then the value of environment variable
- If not specified then the value of environment variable C(ALICLOUD_SECURITY_TOKEN) will be used instead.
C(ALICLOUD_SECURITY_TOKEN) will be used instead. aliases: ['security_token']
type: str
alicloud_assume_role:
description:
- If provided with a role ARN, Ansible will attempt to assume this role using the supplied credentials.
- The nested assume_role block supports I(alicloud_assume_role_arn), I(alicloud_assume_role_session_name),
I(alicloud_assume_role_session_expiration) and I(alicloud_assume_role_policy)
type: dict
aliases: ['assume_role']
alicloud_assume_role_arn:
description:
- The Alibaba Cloud role_arn. The ARN of the role to assume. If ARN is set to an empty string,
it does not perform role switching. It supports environment variable ALICLOUD_ASSUME_ROLE_ARN.
ansible will execute with provided credentials.
aliases: ['assume_role_arn']
type: str
alicloud_assume_role_session_name:
description:
- The Alibaba Cloud session_name. The session name to use when assuming the role. If omitted,
'ansible' is passed to the AssumeRole call as session name. It supports environment variable
ALICLOUD_ASSUME_ROLE_SESSION_NAME
aliases: ['assume_role_session_name']
type: str
alicloud_assume_role_session_expiration:
description:
- The Alibaba Cloud session_expiration. The time after which the established session for assuming
role expires. Valid value range 900-3600 seconds. Default to 3600 (in this case Alicloud use own default
value). It supports environment variable ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION
aliases: ['assume_role_session_expiration']
type: int
ecs_role_name:
description:
- The RAM Role Name attached on a ECS instance for API operations. You can retrieve this from the 'Access Control'
section of the Alibaba Cloud console.
- If you're running Ansible from an ECS instance with RAM Instance using RAM Role, Ansible will just access the
metadata U(http://100.100.100.200/latest/meta-data/ram/security-credentials/<ecs_role_name>) to obtain the STS
credential. This is a preferred approach over any other when running in ECS as you can avoid hard coding
credentials. Instead these are leased on-the-fly by Ansible which reduces the chance of leakage.
aliases: ['role_name']
type: str
profile:
description:
- This is the Alicloud profile name as set in the shared credentials file. It can also be sourced from the
ALICLOUD_PROFILE environment variable.
type: str
shared_credentials_file:
description:
- This is the path to the shared credentials file. It can also be sourced from the ALICLOUD_SHARED_CREDENTIALS_FILE
environment variable.
- If this is not set and a profile is specified, ~/.aliyun/config.json will be used.
type: str type: str
aliases: [ security_token ]
author: author:
- He Guimin (@xiaozhu36) - "He Guimin (@xiaozhu36)"
requirements: requirements:
- python >= 2.6 - "python >= 3.6"
extends_documentation_fragment:
- community.general.alicloud
notes: notes:
- If parameters are not set within the module, the following - If parameters are not set within the module, the following
environment variables can be used in decreasing order of precedence environment variables can be used in decreasing order of precedence
C(ALICLOUD_ACCESS_KEY) or C(ALICLOUD_ACCESS_KEY_ID), C(ALICLOUD_ACCESS_KEY) or C(ALICLOUD_ACCESS_KEY_ID),
C(ALICLOUD_SECRET_KEY) or C(ALICLOUD_SECRET_ACCESS_KEY), C(ALICLOUD_SECRET_KEY) or C(ALICLOUD_SECRET_ACCESS_KEY),
C(ALICLOUD_REGION) or C(ALICLOUD_REGION_ID), C(ALICLOUD_REGION) or C(ALICLOUD_REGION_ID),
C(ALICLOUD_SECURITY_TOKEN) C(ALICLOUD_SECURITY_TOKEN),
C(ALICLOUD_ECS_ROLE_NAME),
C(ALICLOUD_SHARED_CREDENTIALS_FILE),
C(ALICLOUD_PROFILE),
C(ALICLOUD_ASSUME_ROLE_ARN),
C(ALICLOUD_ASSUME_ROLE_SESSION_NAME),
C(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION),
- C(ALICLOUD_REGION) or C(ALICLOUD_REGION_ID) can be typically be used to specify the - C(ALICLOUD_REGION) or C(ALICLOUD_REGION_ID) can be typically be used to specify the
ALICLOUD region, when required, but this can also be configured in the footmark config file ALICLOUD region, when required, but this can also be configured in the footmark config file
''' '''

View file

@ -4,7 +4,7 @@
# still belong to the author of the module, and may assign their own license # still belong to the author of the module, and may assign their own license
# to the complete work. # to the complete work.
# #
# Copyright (c) 2017 Alibaba Group Holding Limited. He Guimin <heguimin36@163.com> # Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met: # are permitted provided that the following conditions are met:
@ -26,6 +26,8 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
import os
import json
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
try: try:
@ -35,6 +37,10 @@ try:
import footmark.vpc import footmark.vpc
import footmark.rds import footmark.rds
import footmark.ess import footmark.ess
import footmark.sts
import footmark.dns
import footmark.ram
import footmark.market
HAS_FOOTMARK = True HAS_FOOTMARK = True
except ImportError: except ImportError:
HAS_FOOTMARK = False HAS_FOOTMARK = False
@ -46,12 +52,13 @@ class AnsibleACSError(Exception):
def acs_common_argument_spec(): def acs_common_argument_spec():
return dict( return dict(
alicloud_access_key=dict(required=True, aliases=['access_key_id', 'access_key'], no_log=True, alicloud_access_key=dict(aliases=['access_key_id', 'access_key'], no_log=True,
fallback=(env_fallback, ['ALICLOUD_ACCESS_KEY', 'ALICLOUD_ACCESS_KEY_ID'])), fallback=(env_fallback, ['ALICLOUD_ACCESS_KEY', 'ALICLOUD_ACCESS_KEY_ID'])),
alicloud_secret_key=dict(required=True, aliases=['secret_access_key', 'secret_key'], no_log=True, alicloud_secret_key=dict(aliases=['secret_access_key', 'secret_key'], no_log=True,
fallback=(env_fallback, ['ALICLOUD_SECRET_KEY', 'ALICLOUD_SECRET_ACCESS_KEY'])), fallback=(env_fallback, ['ALICLOUD_SECRET_KEY', 'ALICLOUD_SECRET_ACCESS_KEY'])),
alicloud_security_token=dict(aliases=['security_token'], no_log=True, alicloud_security_token=dict(aliases=['security_token'], no_log=True,
fallback=(env_fallback, ['ALICLOUD_SECURITY_TOKEN'])), fallback=(env_fallback, ['ALICLOUD_SECURITY_TOKEN'])),
ecs_role_name=dict(aliases=['role_name'], fallback=(env_fallback, ['ALICLOUD_ECS_ROLE_NAME']))
) )
@ -61,19 +68,30 @@ def ecs_argument_spec():
dict( dict(
alicloud_region=dict(required=True, aliases=['region', 'region_id'], alicloud_region=dict(required=True, aliases=['region', 'region_id'],
fallback=(env_fallback, ['ALICLOUD_REGION', 'ALICLOUD_REGION_ID'])), fallback=(env_fallback, ['ALICLOUD_REGION', 'ALICLOUD_REGION_ID'])),
alicloud_assume_role_arn=dict(fallback=(env_fallback, ['ALICLOUD_ASSUME_ROLE_ARN']),
aliases=['assume_role_arn']),
alicloud_assume_role_session_name=dict(fallback=(env_fallback, ['ALICLOUD_ASSUME_ROLE_SESSION_NAME']),
aliases=['assume_role_session_name']),
alicloud_assume_role_session_expiration=dict(type='int',
fallback=(env_fallback,
['ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION']),
aliases=['assume_role_session_expiration']),
alicloud_assume_role=dict(type='dict', aliases=['assume_role']),
profile=dict(fallback=(env_fallback, ['ALICLOUD_PROFILE'])),
shared_credentials_file=dict(fallback=(env_fallback, ['ALICLOUD_SHARED_CREDENTIALS_FILE']))
) )
) )
return spec return spec
def get_acs_connection_info(module): def get_acs_connection_info(params):
ecs_params = dict(acs_access_key_id=module.params.get('alicloud_access_key'), ecs_params = dict(acs_access_key_id=params.get('alicloud_access_key'),
acs_secret_access_key=module.params.get('alicloud_secret_key'), acs_secret_access_key=params.get('alicloud_secret_key'),
security_token=module.params.get('alicloud_security_token'), security_token=params.get('alicloud_security_token'),
ecs_role_name=params.get('ecs_role_name'),
user_agent='Ansible-Provider-Alicloud') user_agent='Ansible-Provider-Alicloud')
return ecs_params
return module.params.get('alicloud_region'), ecs_params
def connect_to_acs(acs_module, region, **params): def connect_to_acs(acs_module, region, **params):
@ -88,11 +106,80 @@ def connect_to_acs(acs_module, region, **params):
return conn return conn
def get_assume_role(params):
""" Return new params """
sts_params = get_acs_connection_info(params)
assume_role = {}
if params.get('assume_role'):
assume_role['alicloud_assume_role_arn'] = params['assume_role'].get('role_arn')
assume_role['alicloud_assume_role_session_name'] = params['assume_role'].get('session_name')
assume_role['alicloud_assume_role_session_expiration'] = params['assume_role'].get('session_expiration')
assume_role['alicloud_assume_role_policy'] = params['assume_role'].get('policy')
assume_role_params = {
'role_arn': params.get('alicloud_assume_role_arn') if params.get('alicloud_assume_role_arn') else assume_role.get('alicloud_assume_role_arn'),
'role_session_name': params.get('alicloud_assume_role_session_name') if params.get('alicloud_assume_role_session_name')
else assume_role.get('alicloud_assume_role_session_name'),
'duration_seconds': params.get('alicloud_assume_role_session_expiration') if params.get('alicloud_assume_role_session_expiration')
else assume_role.get('alicloud_assume_role_session_expiration', 3600),
'policy': assume_role.get('alicloud_assume_role_policy', {})
}
try:
sts = connect_to_acs(footmark.sts, params.get('alicloud_region'), **sts_params).assume_role(**assume_role_params).read()
sts_params['acs_access_key_id'], sts_params['acs_secret_access_key'], sts_params['security_token'] \
= sts['access_key_id'], sts['access_key_secret'], sts['security_token']
except AnsibleACSError as e:
params.fail_json(msg=str(e))
return sts_params
def get_profile(params):
if not params['alicloud_access_key'] and not params['ecs_role_name'] and params['profile']:
path = params['shared_credentials_file'] if params['shared_credentials_file'] else os.getenv('HOME') + '/.aliyun/config.json'
auth = {}
with open(path, 'r') as f:
for pro in json.load(f)['profiles']:
if params['profile'] == pro['name']:
auth = pro
if auth:
if auth['mode'] == 'AK' and auth.get('access_key_id') and auth.get('access_key_secret'):
params['alicloud_access_key'] = auth.get('access_key_id')
params['alicloud_secret_key'] = auth.get('access_key_secret')
params['alicloud_region'] = auth.get('region_id')
params = get_acs_connection_info(params)
elif auth['mode'] == 'StsToken' and auth.get('access_key_id') and auth.get('access_key_secret') and auth.get('sts_token'):
params['alicloud_access_key'] = auth.get('access_key_id')
params['alicloud_secret_key'] = auth.get('access_key_secret')
params['security_token'] = auth.get('sts_token')
params['alicloud_region'] = auth.get('region_id')
params = get_acs_connection_info(params)
elif auth['mode'] == 'EcsRamRole':
params['ecs_role_name'] = auth.get('ram_role_name')
params['alicloud_region'] = auth.get('region_id')
params = get_acs_connection_info(params)
elif auth['mode'] == 'RamRoleArn' and auth.get('ram_role_arn'):
params['alicloud_access_key'] = auth.get('access_key_id')
params['alicloud_secret_key'] = auth.get('access_key_secret')
params['security_token'] = auth.get('sts_token')
params['ecs_role_name'] = auth.get('ram_role_name')
params['alicloud_assume_role_arn'] = auth.get('ram_role_arn')
params['alicloud_assume_role_session_name'] = auth.get('ram_session_name')
params['alicloud_assume_role_session_expiration'] = auth.get('expired_seconds')
params['alicloud_region'] = auth.get('region_id')
params = get_assume_role(params)
elif params.get('alicloud_assume_role_arn') or params.get('assume_role'):
params = get_assume_role(params)
else:
params = get_acs_connection_info(params)
return params
def ecs_connect(module): def ecs_connect(module):
""" Return an ecs connection""" """ Return an ecs connection"""
ecs_params = get_profile(module.params)
region, ecs_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint. # If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region: if region:
try: try:
ecs = connect_to_acs(footmark.ecs, region, **ecs_params) ecs = connect_to_acs(footmark.ecs, region, **ecs_params)
@ -104,9 +191,9 @@ def ecs_connect(module):
def slb_connect(module): def slb_connect(module):
""" Return an slb connection""" """ Return an slb connection"""
slb_params = get_profile(module.params)
region, slb_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint. # If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region: if region:
try: try:
slb = connect_to_acs(footmark.slb, region, **slb_params) slb = connect_to_acs(footmark.slb, region, **slb_params)
@ -116,11 +203,25 @@ def slb_connect(module):
return slb return slb
def dns_connect(module):
""" Return an dns connection"""
dns_params = get_profile(module.params)
# If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region:
try:
dns = connect_to_acs(footmark.dns, region, **dns_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return dns
def vpc_connect(module): def vpc_connect(module):
""" Return an vpc connection""" """ Return an vpc connection"""
vpc_params = get_profile(module.params)
region, vpc_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint. # If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region: if region:
try: try:
vpc = connect_to_acs(footmark.vpc, region, **vpc_params) vpc = connect_to_acs(footmark.vpc, region, **vpc_params)
@ -132,9 +233,9 @@ def vpc_connect(module):
def rds_connect(module): def rds_connect(module):
""" Return an rds connection""" """ Return an rds connection"""
rds_params = get_profile(module.params)
region, rds_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint. # If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region: if region:
try: try:
rds = connect_to_acs(footmark.rds, region, **rds_params) rds = connect_to_acs(footmark.rds, region, **rds_params)
@ -146,9 +247,9 @@ def rds_connect(module):
def ess_connect(module): def ess_connect(module):
""" Return an ess connection""" """ Return an ess connection"""
ess_params = get_profile(module.params)
region, ess_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint. # If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region: if region:
try: try:
ess = connect_to_acs(footmark.ess, region, **ess_params) ess = connect_to_acs(footmark.ess, region, **ess_params)
@ -156,3 +257,45 @@ def ess_connect(module):
module.fail_json(msg=str(e)) module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method # Otherwise, no region so we fallback to the old connection method
return ess return ess
def sts_connect(module):
""" Return an sts connection"""
sts_params = get_profile(module.params)
# If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region:
try:
sts = connect_to_acs(footmark.sts, region, **sts_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return sts
def ram_connect(module):
""" Return an ram connection"""
ram_params = get_profile(module.params)
# If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region:
try:
ram = connect_to_acs(footmark.ram, region, **ram_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return ram
def market_connect(module):
""" Return an market connection"""
market_params = get_profile(module.params)
# If we have a region specified, connect to its endpoint.
region = module.params.get('alicloud_region')
if region:
try:
market = connect_to_acs(footmark.market, region, **market_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return market

View file

@ -1,5 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# Copyright (c) 2017 Alibaba Group Holding Limited. He Guimin <heguimin36@163.com> # -*- coding: utf-8 -*-
# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# #
# This file is part of Ansible # This file is part of Ansible
@ -17,196 +19,236 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see http://www.gnu.org/licenses/. # along with Ansible. If not, see http://www.gnu.org/licenses/.
from __future__ import absolute_import, division, print_function from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = { ANSIBLE_METADATA = {'metadata_version': '1.1',
'metadata_version': '1.1', 'status': ['preview'],
'status': ['preview'], 'supported_by': 'community'}
'supported_by': 'community'
}
DOCUMENTATION = r''' DOCUMENTATION = '''
--- ---
module: ali_instance module: ali_instance
short_description: Create, start, stop, restart or terminate an instance in ECS, add or remove an instance to/from a security group short_description: Create, Start, Stop, Restart or Terminate an Instance in ECS. Add or Remove Instance to/from a Security Group.
description: description:
- Create, start, stop, restart, modify or terminate ecs instances. - Create, start, stop, restart, modify or terminate ecs instances.
- Add or remove ecs instances to/from security group. - Add or remove ecs instances to/from security group.
options: options:
state: state:
description:
- The state of the instance after operating.
default: 'present'
choices: ['present', 'running', 'stopped', 'restarted', 'absent']
type: str
availability_zone:
description:
- Aliyun availability zone ID in which to launch the instance.
If it is not specified, it will be allocated by system automatically.
aliases: ['alicloud_zone', 'zone_id']
type: str
image_id:
description:
- Image ID used to launch instances. Required when C(state=present) and creating new ECS instances.
aliases: ['image']
type: str
instance_type:
description:
- Instance type used to launch instances. Required when C(state=present) and creating new ECS instances.
aliases: ['type']
type: str
security_groups:
description:
- A list of security group IDs.
aliases: ['group_ids']
type: list
elements: str
vswitch_id:
description:
- The subnet ID in which to launch the instances (VPC).
aliases: ['subnet_id']
type: str
instance_name:
description:
- The name of ECS instance, which is a string of 2 to 128 Chinese or English characters. It must begin with an
uppercase/lowercase letter or a Chinese character and can contain numerals, ".", "_" or "-".
It cannot begin with http:// or https://.
aliases: ['name']
type: str
description: description:
- The state of the instance after operating. description:
type: str - The description of ECS instance, which is a string of 2 to 256 characters. It cannot begin with http:// or https://.
default: 'present' type: str
choices: ['absent', 'present', 'restarted', 'running', 'stopped'] internet_charge_type:
availability_zone: description:
description: - Internet charge type of ECS instance.
- Aliyun availability zone ID in which to launch the instance. default: 'PayByBandwidth'
- If it is not specified, it will be allocated by system automatically. choices: ['PayByBandwidth', 'PayByTraffic']
aliases: ['alicloud_zone'] type: str
type: str max_bandwidth_in:
image_id: description:
description: - Maximum incoming bandwidth from the public network, measured in Mbps (Megabits per second).
- Image ID used to launch instances. default: 200
- Required when I(state=present) and creating new ECS instances. type: int
aliases: ['image'] max_bandwidth_out:
type: str description:
instance_type: - Maximum outgoing bandwidth to the public network, measured in Mbps (Megabits per second).
description: Required when C(allocate_public_ip=True). Ignored when C(allocate_public_ip=False).
- Instance type used to launch instances. default: 0
- Required when I(state=present) and creating new ECS instances. type: int
aliases: ['type'] host_name:
type: str description:
security_groups: - Instance host name. Ordered hostname is not supported.
description: type: str
- A list of security group IDs. unique_suffix:
type: list description:
vswitch_id: - Specifies whether to add sequential suffixes to the host_name.
description: The sequential suffix ranges from 001 to 999.
- The subnet ID in which to launch the instances (VPC). default: False
aliases: ['subnet_id'] type: bool
type: str password:
instance_name: description:
description: - The password to login instance. After rebooting instances, modified password will take effect.
- The name of ECS instance, which is a string of 2 to 128 Chinese or English characters. type: str
- It must begin with an uppercase/lowercase letter or a Chinese character and system_disk_category:
can contain numerals, ".", "_" or "-". It cannot begin with http:// or https://. description:
aliases: ['name'] - Category of the system disk.
type: str default: 'cloud_efficiency'
description: choices: ['cloud_efficiency', 'cloud_ssd']
description: type: str
- The description of ECS instance, which is a string of 2 to 256 characters. system_disk_size:
- It cannot begin with http:// or https://. description:
type: str - Size of the system disk, in GB. The valid values are 40~500.
internet_charge_type: default: 40
description: type: int
- Internet charge type of ECS instance. system_disk_name:
type: str description:
default: 'PayByBandwidth' - Name of the system disk.
choices: ['PayByBandwidth', 'PayByTraffic'] type: str
max_bandwidth_in: system_disk_description:
description: description:
- Maximum incoming bandwidth from the public network, - Description of the system disk.
measured in Mbps (Megabits per second). type: str
default: 200 count:
type: int description:
max_bandwidth_out: - The number of the new instance. An integer value which indicates how many instances that match I(count_tag)
description: should be running. Instances are either created or terminated based on this value.
- Maximum outgoing bandwidth to the public network, measured in Mbps (Megabits per second). default: 1
type: int type: int
default: 0 count_tag:
host_name: description:
description: - I(count) determines how many instances based on a specific tag criteria should be present.
- Instance host name. This can be expressed in multiple ways and is shown in the EXAMPLES section.
type: str The specified count_tag must already exist or be passed in as the I(tags) option.
password: If it is not specified, it will be replaced by I(instance_name).
description: type: str
- The password to login instance. allocate_public_ip:
- After rebooting instances, modified password will take effect. description:
type: str - Whether allocate a public ip for the new instance.
system_disk_category: default: False
description: aliases: [ 'assign_public_ip' ]
- Category of the system disk. type: bool
type: str instance_charge_type:
default: 'cloud_efficiency' description:
choices: ['cloud_efficiency', 'cloud_ssd'] - The charge type of the instance.
system_disk_size: choices: ['PrePaid', 'PostPaid']
description: default: 'PostPaid'
- Size of the system disk, in GB. The valid values are 40~500. type: str
type: int period:
default: 40 description:
system_disk_name: - The charge duration of the instance, in month. Required when C(instance_charge_type=PrePaid).
description: - The valid value are [1-9, 12, 24, 36].
- Name of the system disk. default: 1
type: str type: int
system_disk_description: auto_renew:
description: description:
- Description of the system disk. - Whether automate renew the charge of the instance.
type: str type: bool
count: default: False
description: auto_renew_period:
- The number of the new instance. description:
- Indicates how many instances that match I(count_tag) should be running. - The duration of the automatic renew the charge of the instance. Required when C(auto_renew=True).
- Instances are either created or terminated based on this value. choices: [1, 2, 3, 6, 12]
type: int type: int
default: 1 instance_ids:
count_tag: description:
description: - A list of instance ids. It is required when need to operate existing instances.
- Determines how many instances based on a specific tag criteria should be present. If it is specified, I(count) will lose efficacy.
- This can be expressed in multiple ways and is shown in the EXAMPLES section. type: list
- The specified count_tag must already exist or be passed in as the I(instance_tags) option. elements: str
- If it is not specified, it will be replaced by I(instance_name). force:
type: str description:
allocate_public_ip: - Whether the current operation needs to be execute forcibly.
description: default: False
- Whether allocate a public ip for the new instance. type: bool
default: False tags:
aliases: ['assign_public_ip'] description:
type: bool - A hash/dictionaries of instance tags, to add to the new instance or for starting/stopping instance by tag. C({"key":"value"})
instance_charge_type: aliases: ["instance_tags"]
description: type: dict
- The charge type of the instance. purge_tags:
type: str description:
choices: ['PrePaid', 'PostPaid'] - Delete any tags not specified in the task that are on the instance.
default: 'PostPaid' If True, it means you have to specify all the desired tags on each task affecting an instance.
period: default: False
description: type: bool
- The charge duration of the instance, in month. key_name:
- Required when I(instance_charge_type=PrePaid). description:
- The valid value are [1-9, 12, 24, 36]. - The name of key pair which is used to access ECS instance in SSH.
type: int required: false
default: 1 type: str
auto_renew: aliases: ['keypair']
description: user_data:
- Whether automate renew the charge of the instance. description:
type: bool - User-defined data to customize the startup behaviors of an ECS instance and to pass data into an ECS instance.
default: False It only will take effect when launching the new ECS instances.
auto_renew_period: required: false
description: type: str
- The duration of the automatic renew the charge of the instance. ram_role_name:
- Required when I(auto_renew=True). description:
type: int - The name of the instance RAM role.
choices: [1, 2, 3, 6, 12] type: str
instance_ids: spot_price_limit:
description: description:
- A list of instance ids. It is required when need to operate existing instances. - The maximum hourly price for the preemptible instance. This parameter supports a maximum of three decimal
- If it is specified, I(count) will lose efficacy. places and takes effect when the SpotStrategy parameter is set to SpotWithPriceLimit.
type: list type: float
force: spot_strategy:
description: description:
- Whether the current operation needs to be execute forcibly. - The bidding mode of the pay-as-you-go instance. This parameter is valid when InstanceChargeType is set to PostPaid.
default: False choices: ['NoSpot', 'SpotWithPriceLimit', 'SpotAsPriceGo']
type: bool default: 'NoSpot'
instance_tags: type: str
description: period_unit:
- A hash/dictionaries of instance tags, to add to the new instance or description:
for starting/stopping instance by tag (C({"key":"value"})). - The duration unit that you will buy the resource. It is valid when C(instance_charge_type=PrePaid)
aliases: ['tags'] choices: ['Month', 'Week']
type: dict default: 'Month'
key_name: type: str
description: dry_run:
- The name of key pair which is used to access ECS instance in SSH. description:
type: str - Specifies whether to send a dry-run request.
required: false - If I(dry_run=True), Only a dry-run request is sent and no instance is created. The system checks whether the
aliases: ['keypair'] required parameters are set, and validates the request format, service permissions, and available ECS instances.
user_data: If the validation fails, the corresponding error code is returned. If the validation succeeds, the DryRunOperation error code is returned.
description: - If I(dry_run=False), A request is sent. If the validation succeeds, the instance is created.
- User-defined data to customize the startup behaviors of an ECS instance and to pass data into an ECS instance. default: False
It only will take effect when launching the new ECS instances. type: bool
required: false include_data_disks:
type: str description:
- Whether to change instance disks charge type when changing instance charge type.
default: True
type: bool
author: author:
- "He Guimin (@xiaozhu36)" - "He Guimin (@xiaozhu36)"
requirements: requirements:
- "python >= 2.6" - "python >= 3.6"
- "footmark >= 1.1.16" - "footmark >= 1.19.0"
extends_documentation_fragment: extends_documentation_fragment:
- community.general.alicloud - community.general.alicloud
''' '''
EXAMPLES = r''' EXAMPLES = '''
# basic provisioning example vpc network # basic provisioning example vpc network
- name: basic provisioning example - name: basic provisioning example
hosts: localhost hosts: localhost
@ -243,7 +285,7 @@ EXAMPLES = r'''
assign_public_ip: '{{ assign_public_ip }}' assign_public_ip: '{{ assign_public_ip }}'
internet_charge_type: '{{ internet_charge_type }}' internet_charge_type: '{{ internet_charge_type }}'
max_bandwidth_out: '{{ max_bandwidth_out }}' max_bandwidth_out: '{{ max_bandwidth_out }}'
instance_tags: tags:
Name: created_one Name: created_one
host_name: '{{ host_name }}' host_name: '{{ host_name }}'
password: '{{ password }}' password: '{{ password }}'
@ -261,7 +303,7 @@ EXAMPLES = r'''
security_groups: '{{ security_groups }}' security_groups: '{{ security_groups }}'
internet_charge_type: '{{ internet_charge_type }}' internet_charge_type: '{{ internet_charge_type }}'
max_bandwidth_out: '{{ max_bandwidth_out }}' max_bandwidth_out: '{{ max_bandwidth_out }}'
instance_tags: tags:
Name: created_one Name: created_one
Version: 0.1 Version: 0.1
count: 2 count: 2
@ -296,9 +338,9 @@ EXAMPLES = r'''
security_groups: '{{ security_groups }}' security_groups: '{{ security_groups }}'
''' '''
RETURN = r''' RETURN = '''
instances: instances:
description: List of ECS instances. description: List of ECS instances
returned: always returned: always
type: complex type: complex
contains: contains:
@ -432,6 +474,11 @@ instances:
returned: always returned: always
type: str type: str
sample: ecs.sn1ne.xlarge sample: ecs.sn1ne.xlarge
instance_type_family:
description: The instance type family of the instance belongs.
returned: always
type: str
sample: ecs.sn1ne
internet_charge_type: internet_charge_type:
description: The billing method of the network bandwidth. description: The billing method of the network bandwidth.
returned: always returned: always
@ -493,7 +540,7 @@ instances:
type: str type: str
sample: 10.0.0.1 sample: 10.0.0.1
public_ip_address: public_ip_address:
description: The public IPv4 address assigned to the instance. description: The public IPv4 address assigned to the instance or eip address
returned: always returned: always
type: str type: str
sample: 43.0.0.1 sample: 43.0.0.1
@ -509,15 +556,15 @@ instances:
elements: dict elements: dict
contains: contains:
group_id: group_id:
description: The ID of the security group. description: The ID of the security group.
returned: always returned: always
type: str type: str
sample: sg-0123456 sample: sg-0123456
group_name: group_name:
description: The name of the security group. description: The name of the security group.
returned: always returned: always
type: str type: str
sample: my-security-group sample: my-security-group
status: status:
description: The current status of the instance. description: The current status of the instance.
returned: always returned: always
@ -528,6 +575,11 @@ instances:
returned: always returned: always
type: dict type: dict
sample: sample:
user_data:
description: User-defined data.
returned: always
type: dict
sample:
vswitch_id: vswitch_id:
description: The ID of the vswitch in which the instance is running. description: The ID of the vswitch in which the instance is running.
returned: always returned: always
@ -536,15 +588,28 @@ instances:
vpc_id: vpc_id:
description: The ID of the VPC the instance is in. description: The ID of the VPC the instance is in.
returned: always returned: always
type: dict type: str
sample: vpc-0011223344 sample: vpc-0011223344
spot_price_limit:
description:
- The maximum hourly price for the preemptible instance.
returned: always
type: float
sample: 0.97
spot_strategy:
description:
- The bidding mode of the pay-as-you-go instance.
returned: always
type: str
sample: NoSpot
ids: ids:
description: List of ECS instance IDs. description: List of ECS instance IDs
returned: always returned: always
type: list type: list
sample: [i-12345er, i-3245fs] sample: [i-12345er, i-3245fs]
''' '''
import re
import time import time
import traceback import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
@ -562,14 +627,17 @@ except ImportError:
def get_instances_info(connection, ids): def get_instances_info(connection, ids):
result = [] result = []
instances = connection.get_all_instances(instance_ids=ids) instances = connection.describe_instances(instance_ids=ids)
if len(instances) > 0: if len(instances) > 0:
for inst in instances: for inst in instances:
volumes = connection.describe_disks(instance_id=inst.id)
setattr(inst, 'block_device_mappings', volumes)
setattr(inst, 'user_data', inst.describe_user_data())
result.append(inst.read()) result.append(inst.read())
return result return result
def create_instance(module, ecs, exact_count): def run_instance(module, ecs, exact_count):
if exact_count <= 0: if exact_count <= 0:
return None return None
zone_id = module.params['availability_zone'] zone_id = module.params['availability_zone']
@ -581,7 +649,7 @@ def create_instance(module, ecs, exact_count):
description = module.params['description'] description = module.params['description']
internet_charge_type = module.params['internet_charge_type'] internet_charge_type = module.params['internet_charge_type']
max_bandwidth_out = module.params['max_bandwidth_out'] max_bandwidth_out = module.params['max_bandwidth_out']
max_bandwidth_in = module.params['max_bandwidth_out'] max_bandwidth_in = module.params['max_bandwidth_in']
host_name = module.params['host_name'] host_name = module.params['host_name']
password = module.params['password'] password = module.params['password']
system_disk_category = module.params['system_disk_category'] system_disk_category = module.params['system_disk_category']
@ -589,14 +657,16 @@ def create_instance(module, ecs, exact_count):
system_disk_name = module.params['system_disk_name'] system_disk_name = module.params['system_disk_name']
system_disk_description = module.params['system_disk_description'] system_disk_description = module.params['system_disk_description']
allocate_public_ip = module.params['allocate_public_ip'] allocate_public_ip = module.params['allocate_public_ip']
instance_tags = module.params['instance_tags']
period = module.params['period'] period = module.params['period']
auto_renew = module.params['auto_renew'] auto_renew = module.params['auto_renew']
instance_charge_type = module.params['instance_charge_type'] instance_charge_type = module.params['instance_charge_type']
auto_renew_period = module.params['auto_renew_period'] auto_renew_period = module.params['auto_renew_period']
user_data = module.params['user_data'] user_data = module.params['user_data']
key_name = module.params['key_name'] key_name = module.params['key_name']
ram_role_name = module.params['ram_role_name']
spot_price_limit = module.params['spot_price_limit']
spot_strategy = module.params['spot_strategy']
unique_suffix = module.params['unique_suffix']
# check whether the required parameter passed or not # check whether the required parameter passed or not
if not image_id: if not image_id:
module.fail_json(msg='image_id is required for new instance') module.fail_json(msg='image_id is required for new instance')
@ -611,17 +681,17 @@ def create_instance(module, ecs, exact_count):
try: try:
# call to create_instance method from footmark # call to create_instance method from footmark
instances = ecs.create_instance(image_id=image_id, instance_type=instance_type, security_group_id=security_groups[0], instances = ecs.run_instances(image_id=image_id, instance_type=instance_type, security_group_id=security_groups[0],
zone_id=zone_id, instance_name=instance_name, description=description, zone_id=zone_id, instance_name=instance_name, description=description,
internet_charge_type=internet_charge_type, max_bandwidth_out=max_bandwidth_out, internet_charge_type=internet_charge_type, internet_max_bandwidth_out=max_bandwidth_out,
max_bandwidth_in=max_bandwidth_in, host_name=host_name, password=password, internet_max_bandwidth_in=max_bandwidth_in, host_name=host_name, password=password,
io_optimized='optimized', system_disk_category=system_disk_category, io_optimized='optimized', system_disk_category=system_disk_category,
system_disk_size=system_disk_size, system_disk_name=system_disk_name, system_disk_size=system_disk_size, system_disk_disk_name=system_disk_name,
system_disk_description=system_disk_description, system_disk_description=system_disk_description, vswitch_id=vswitch_id,
vswitch_id=vswitch_id, count=exact_count, allocate_public_ip=allocate_public_ip, amount=exact_count, instance_charge_type=instance_charge_type, period=period, period_unit="Month",
instance_charge_type=instance_charge_type, period=period, auto_renew=auto_renew, auto_renew=auto_renew, auto_renew_period=auto_renew_period, key_pair_name=key_name,
auto_renew_period=auto_renew_period, instance_tags=instance_tags, user_data=user_data, client_token=client_token, ram_role_name=ram_role_name,
key_pair_name=key_name, user_data=user_data, client_token=client_token) spot_price_limit=spot_price_limit, spot_strategy=spot_strategy, unique_suffix=unique_suffix)
except Exception as e: except Exception as e:
module.fail_json(msg='Unable to create instance, error: {0}'.format(e)) module.fail_json(msg='Unable to create instance, error: {0}'.format(e))
@ -629,11 +699,69 @@ def create_instance(module, ecs, exact_count):
return instances return instances
def modify_instance(module, instance):
# According to state to modify instance's some special attribute
state = module.params["state"]
name = module.params['instance_name']
unique_suffix = module.params['unique_suffix']
if not name:
name = instance.name
description = module.params['description']
if not description:
description = instance.description
host_name = module.params['host_name']
if unique_suffix and host_name:
suffix = instance.host_name[-3:]
host_name = host_name + suffix
if not host_name:
host_name = instance.host_name
# password can be modified only when restart instance
password = ""
if state == "restarted":
password = module.params['password']
# userdata can be modified only when instance is stopped
setattr(instance, "user_data", instance.describe_user_data())
user_data = instance.user_data
if state == "stopped":
user_data = module.params['user_data'].encode()
try:
return instance.modify(name=name, description=description, host_name=host_name, password=password, user_data=user_data)
except Exception as e:
module.fail_json(msg="Modify instance {0} attribute got an error: {1}".format(instance.id, e))
def wait_for_instance_modify_charge(ecs, instance_ids, charge_type, delay=10, timeout=300):
"""
To verify instance charge type has become expected after modify instance charge type
"""
try:
while True:
instances = ecs.describe_instances(instance_ids=instance_ids)
flag = True
for inst in instances:
if inst and inst.instance_charge_type != charge_type:
flag = False
if flag:
return
timeout -= delay
time.sleep(delay)
if timeout <= 0:
raise Exception("Timeout Error: Waiting for instance to {0}. ".format(charge_type))
except Exception as e:
raise e
def main(): def main():
argument_spec = ecs_argument_spec() argument_spec = ecs_argument_spec()
argument_spec.update(dict( argument_spec.update(dict(
security_groups=dict(type='list'), security_groups=dict(type='list', elements='str', aliases=['group_ids']),
availability_zone=dict(type='str', aliases=['alicloud_zone']), availability_zone=dict(type='str', aliases=['alicloud_zone', 'zone_id']),
instance_type=dict(type='str', aliases=['type']), instance_type=dict(type='str', aliases=['type']),
image_id=dict(type='str', aliases=['image']), image_id=dict(type='str', aliases=['image']),
count=dict(type='int', default=1), count=dict(type='int', default=1),
@ -650,17 +778,25 @@ def main():
system_disk_name=dict(type='str'), system_disk_name=dict(type='str'),
system_disk_description=dict(type='str'), system_disk_description=dict(type='str'),
force=dict(type='bool', default=False), force=dict(type='bool', default=False),
instance_tags=dict(type='dict', aliases=['tags']), tags=dict(type='dict', aliases=['instance_tags']),
purge_tags=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'running', 'stopped', 'restarted', 'absent']), state=dict(default='present', choices=['present', 'running', 'stopped', 'restarted', 'absent']),
description=dict(type='str'), description=dict(type='str'),
allocate_public_ip=dict(type='bool', aliases=['assign_public_ip'], default=False), allocate_public_ip=dict(type='bool', aliases=['assign_public_ip'], default=False),
instance_charge_type=dict(type='str', default='PostPaid', choices=['PrePaid', 'PostPaid']), instance_charge_type=dict(type='str', default='PostPaid', choices=['PrePaid', 'PostPaid']),
period=dict(type='int', default=1), period=dict(type='int', default=1),
auto_renew=dict(type='bool', default=False), auto_renew=dict(type='bool', default=False),
instance_ids=dict(type='list'), instance_ids=dict(type='list', elements='str'),
auto_renew_period=dict(type='int', choices=[1, 2, 3, 6, 12]), auto_renew_period=dict(type='int', choices=[1, 2, 3, 6, 12]),
key_name=dict(type='str', aliases=['keypair']), key_name=dict(type='str', aliases=['keypair']),
user_data=dict(type='str') user_data=dict(type='str'),
ram_role_name=dict(type='str'),
spot_price_limit=dict(type='float'),
spot_strategy=dict(type='str', default='NoSpot', choices=['NoSpot', 'SpotWithPriceLimit', 'SpotAsPriceGo']),
unique_suffix=dict(type='bool', default=False),
period_unit=dict(type='str', default='Month', choices=['Month', 'Week']),
dry_run=dict(type='bool', default=False),
include_data_disks=dict(type='bool', default=True)
) )
) )
module = AnsibleModule(argument_spec=argument_spec) module = AnsibleModule(argument_spec=argument_spec)
@ -669,6 +805,7 @@ def main():
module.fail_json(msg=missing_required_lib('footmark'), exception=FOOTMARK_IMP_ERR) module.fail_json(msg=missing_required_lib('footmark'), exception=FOOTMARK_IMP_ERR)
ecs = ecs_connect(module) ecs = ecs_connect(module)
host_name = module.params['host_name']
state = module.params['state'] state = module.params['state']
instance_ids = module.params['instance_ids'] instance_ids = module.params['instance_ids']
count_tag = module.params['count_tag'] count_tag = module.params['count_tag']
@ -677,22 +814,50 @@ def main():
force = module.params['force'] force = module.params['force']
zone_id = module.params['availability_zone'] zone_id = module.params['availability_zone']
key_name = module.params['key_name'] key_name = module.params['key_name']
tags = module.params['tags']
max_bandwidth_out = module.params['max_bandwidth_out']
instance_charge_type = module.params['instance_charge_type']
if instance_charge_type == "PrePaid":
module.params['spot_strategy'] = ''
changed = False changed = False
instances = [] instances = []
if instance_ids: if instance_ids:
if not isinstance(instance_ids, list): if not isinstance(instance_ids, list):
module.fail_json(msg='The parameter instance_ids should be a list, aborting') module.fail_json(msg='The parameter instance_ids should be a list, aborting')
instances = ecs.get_all_instances(zone_id=zone_id, instance_ids=instance_ids) instances = ecs.describe_instances(zone_id=zone_id, instance_ids=instance_ids)
if not instances: if not instances:
module.fail_json(msg="There are no instances in our record based on instance_ids {0}. " module.fail_json(msg="There are no instances in our record based on instance_ids {0}. "
"Please check it and try again.".format(instance_ids)) "Please check it and try again.".format(instance_ids))
elif count_tag: elif count_tag:
instances = ecs.get_all_instances(zone_id=zone_id, instance_tags=eval(count_tag)) instances = ecs.describe_instances(zone_id=zone_id, tags=eval(count_tag))
elif instance_name: elif instance_name:
instances = ecs.get_all_instances(zone_id=zone_id, instance_name=instance_name) instances = ecs.describe_instances(zone_id=zone_id, instance_name=instance_name)
ids = [] ids = []
if state == 'absent':
if len(instances) < 1:
module.fail_json(msg='Please specify ECS instances that you want to operate by using '
'parameters instance_ids, tags or instance_name, aborting')
try:
targets = []
for inst in instances:
if inst.status != 'stopped' and not force:
module.fail_json(msg="Instance is running, and please stop it or set 'force' as True.")
targets.append(inst.id)
if ecs.delete_instances(instance_ids=targets, force=force):
changed = True
ids.extend(targets)
module.exit_json(changed=changed, ids=ids, instances=[])
except Exception as e:
module.fail_json(msg='Delete instance got an error: {0}'.format(e))
if module.params['allocate_public_ip'] and max_bandwidth_out < 0:
module.fail_json(msg="'max_bandwidth_out' should be greater than 0 when 'allocate_public_ip' is True.")
if not module.params['allocate_public_ip']:
module.params['max_bandwidth_out'] = 0
if state == 'present': if state == 'present':
if not instance_ids: if not instance_ids:
if len(instances) > count: if len(instances) > count:
@ -702,13 +867,17 @@ def main():
module.fail_json(msg="That to delete instance {0} is failed results from it is running, " module.fail_json(msg="That to delete instance {0} is failed results from it is running, "
"and please stop it or set 'force' as True.".format(inst.id)) "and please stop it or set 'force' as True.".format(inst.id))
try: try:
changed = inst.terminate(force=force) if inst.terminate(force=force):
changed = True
except Exception as e: except Exception as e:
module.fail_json(msg="Delete instance {0} got an error: {1}".format(inst.id, e)) module.fail_json(msg="Delete instance {0} got an error: {1}".format(inst.id, e))
instances.pop(len(instances) - 1) instances.pop(len(instances) - 1)
else: else:
try: try:
new_instances = create_instance(module, ecs, count - len(instances)) if re.search(r"-\[\d+,\d+\]-", host_name):
module.fail_json(msg='Ordered hostname is not supported, If you want to add an ordered '
'suffix to the hostname, you can set unique_suffix to True')
new_instances = run_instance(module, ecs, count - len(instances))
if new_instances: if new_instances:
changed = True changed = True
instances.extend(new_instances) instances.extend(new_instances)
@ -717,9 +886,9 @@ def main():
# Security Group join/leave begin # Security Group join/leave begin
security_groups = module.params['security_groups'] security_groups = module.params['security_groups']
if not isinstance(security_groups, list): if security_groups:
module.fail_json(msg='The parameter security_groups should be a list, aborting') if not isinstance(security_groups, list):
if len(security_groups) > 0: module.fail_json(msg='The parameter security_groups should be a list, aborting')
for inst in instances: for inst in instances:
existing = inst.security_group_ids['security_group_id'] existing = inst.security_group_ids['security_group_id']
remove = list(set(existing).difference(set(security_groups))) remove = list(set(existing).difference(set(security_groups)))
@ -737,80 +906,102 @@ def main():
for inst in instances: for inst in instances:
if key_name is not None and key_name != inst.key_name: if key_name is not None and key_name != inst.key_name:
if key_name == "": if key_name == "":
changed = inst.detach_key_pair() if inst.detach_key_pair():
changed = True
else: else:
inst_ids.append(inst.id) inst_ids.append(inst.id)
if inst_ids: if inst_ids:
changed = ecs.attach_key_pair(instance_ids=inst_ids, key_pair_name=key_name) changed = ecs.attach_key_pair(instance_ids=inst_ids, key_pair_name=key_name)
# Modify instance attribute # Modify instance attribute
description = module.params['description']
host_name = module.params['host_name']
password = module.params['password']
for inst in instances: for inst in instances:
if not instance_name: if modify_instance(module, inst):
instance_name = inst.name changed = True
if not description:
description = inst.description
if not host_name:
host_name = inst.host_name
try:
if inst.modify(name=instance_name, description=description, host_name=host_name, password=password):
changed = True
except Exception as e:
module.fail_json(msg="Modify instance attribute {0} got an error: {1}".format(inst.id, e))
if inst.id not in ids: if inst.id not in ids:
ids.append(inst.id) ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids)) # Modify instance charge type
ids = []
for inst in instances:
if inst.instance_charge_type != instance_charge_type:
ids.append(inst.id)
if ids:
params = {"instance_ids": ids, "instance_charge_type": instance_charge_type,
"include_data_disks": module.params['include_data_disks'], "dry_run": module.params['dry_run'],
"auto_pay": True}
if instance_charge_type == 'PrePaid':
params['period'] = module.params['period']
params['period_unit'] = module.params['period_unit']
if ecs.modify_instance_charge_type(**params):
changed = True
wait_for_instance_modify_charge(ecs, ids, instance_charge_type)
else: else:
if len(instances) < 1: if len(instances) < 1:
module.fail_json(msg='Please specify ECS instances that you want to operate by using ' module.fail_json(msg='Please specify ECS instances that you want to operate by using '
'parameters instance_ids, instance_tags or instance_name, aborting') 'parameters instance_ids, tags or instance_name, aborting')
force = module.params['force']
if state == 'running': if state == 'running':
try: try:
targets = []
for inst in instances: for inst in instances:
if inst.start(): if modify_instance(module, inst):
changed = True changed = True
ids.append(inst.id) if inst.status != "running":
targets.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids)) ids.append(inst.id)
if targets and ecs.start_instances(instance_ids=targets):
changed = True
ids.extend(targets)
except Exception as e: except Exception as e:
module.fail_json(msg='Start instances got an error: {0}'.format(e)) module.fail_json(msg='Start instances got an error: {0}'.format(e))
elif state == 'stopped': elif state == 'stopped':
try: try:
targets = []
for inst in instances: for inst in instances:
if inst.stop(force=force): if inst.status != "stopped":
targets.append(inst.id)
if targets and ecs.stop_instances(instance_ids=targets, force_stop=force):
changed = True
ids.extend(targets)
for inst in instances:
if modify_instance(module, inst):
changed = True changed = True
ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids))
except Exception as e: except Exception as e:
module.fail_json(msg='Stop instances got an error: {0}'.format(e)) module.fail_json(msg='Stop instances got an error: {0}'.format(e))
elif state == 'restarted': elif state == 'restarted':
try: try:
targets = []
for inst in instances: for inst in instances:
if inst.reboot(force=module.params['force']): if modify_instance(module, inst):
changed = True changed = True
ids.append(inst.id) targets.append(inst.id)
if ecs.reboot_instances(instance_ids=targets, force_stop=module.params['force']):
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids)) changed = True
ids.extend(targets)
except Exception as e: except Exception as e:
module.fail_json(msg='Reboot instances got an error: {0}'.format(e)) module.fail_json(msg='Reboot instances got an error: {0}'.format(e))
else:
try:
for inst in instances:
if inst.status != 'stopped' and not force:
module.fail_json(msg="Instance is running, and please stop it or set 'force' as True.")
if inst.terminate(force=module.params['force']):
changed = True
module.exit_json(changed=changed, ids=[], instances=[]) tags = module.params['tags']
if module.params['purge_tags']:
for inst in instances:
if not tags:
tags = inst.tags
try:
if inst.remove_tags(tags):
changed = True
except Exception as e: except Exception as e:
module.fail_json(msg='Delete instance got an error: {0}'.format(e)) module.fail_json(msg="{0}".format(e))
module.exit_json(changed=changed, instances=get_instances_info(ecs, ids))
if tags:
for inst in instances:
try:
if inst.add_tags(tags):
changed = True
except Exception as e:
module.fail_json(msg="{0}".format(e))
module.exit_json(changed=changed, instances=get_instances_info(ecs, ids))
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -1,5 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# Copyright (c) 2017 Alibaba Group Holding Limited. He Guimin <heguimin36@163.com> # -*- coding: utf-8 -*-
# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# #
# This file is part of Ansible # This file is part of Ansible
@ -17,7 +19,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see http://www.gnu.org/licenses/. # along with Ansible. If not, see http://www.gnu.org/licenses/.
from __future__ import absolute_import, division, print_function from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
@ -36,68 +39,70 @@ description:
options: options:
availability_zone: availability_zone:
description: description:
- Aliyun availability zone ID in which to launch the instance - (Deprecated) Aliyun availability zone ID in which to launch the instance. Please use filter item 'zone_id' instead.
aliases: ['alicloud_zone'] aliases: ['alicloud_zone']
type: str
instance_names: instance_names:
description: description:
- A list of ECS instance names. - (Deprecated) A list of ECS instance names. Please use filter item 'instance_name' instead.
aliases: [ "names"] aliases: ["names"]
type: list
elements: str
instance_ids: instance_ids:
description: description:
- A list of ECS instance ids. - A list of ECS instance ids.
aliases: ["ids"] aliases: ["ids"]
instance_tags: type: list
elements: str
name_prefix:
description:
- Use a instance name prefix to filter ecs instances.
type: str
tags:
description: description:
- A hash/dictionaries of instance tags. C({"key":"value"}) - A hash/dictionaries of instance tags. C({"key":"value"})
aliases: ["tags"] aliases: ["instance_tags"]
type: dict
filters:
description:
- A dict of filters to apply. Each dict item consists of a filter key and a filter value. The filter keys can be
all of request parameters. See U(https://www.alibabacloud.com/help/doc-detail/25506.htm) for parameter details.
Filter keys can be same as request parameter name or be lower case and use underscore ("_") or dash ("-") to
connect different words in one parameter. 'InstanceIds' should be a list and it will be appended to
I(instance_ids) automatically. 'Tag.n.Key' and 'Tag.n.Value' should be a dict and using I(tags) instead.
type: dict
author: author:
- "He Guimin (@xiaozhu36)" - "He Guimin (@xiaozhu36)"
requirements: requirements:
- "python >= 2.6" - "python >= 3.6"
- "footmark >= 1.1.16" - "footmark >= 1.13.0"
extends_documentation_fragment: extends_documentation_fragment:
- community.general.alicloud - community.general.alicloud
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Fetch instances details according to setting different filters # Fetch instances details according to setting different filters
- name: fetch instances details example
hosts: localhost
vars:
alicloud_access_key: <your-alicloud-access-key>
alicloud_secret_key: <your-alicloud-secret-key>
alicloud_region: cn-beijing
availability_zone: cn-beijing-a
tasks: - name: Find all instances in the specified region
- name: Find all instances in the specified region ali_instance_info:
ali_instance_info: register: all_instances
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
register: all_instances
- name: Find all instances based on the specified ids - name: Find all instances based on the specified ids
ali_instance_info: ali_instance_info:
alicloud_access_key: '{{ alicloud_access_key }}' instance_ids:
alicloud_secret_key: '{{ alicloud_secret_key }}' - "i-35b333d9"
alicloud_region: '{{ alicloud_region }}' - "i-ddav43kd"
instance_ids: register: instances_by_ids
- "i-35b333d9"
- "i-ddav43kd"
register: instances_by_ids
- name: Find all instances based on the specified names/name-prefixes - name: Find all instances based on the specified name_prefix
ali_instance_info: ali_instance_info:
alicloud_access_key: '{{ alicloud_access_key }}' name_prefix: "ecs_instance_"
alicloud_secret_key: '{{ alicloud_secret_key }}' register: instances_by_name_prefix
alicloud_region: '{{ alicloud_region }}'
instance_names:
- "ecs_instance-1"
- "ecs_instance_2"
register: instances_by_ids
- name: Find instances based on tags
ali_instance_info:
tags:
Test: "add"
''' '''
RETURN = ''' RETURN = '''
@ -231,6 +236,11 @@ instances:
returned: always returned: always
type: str type: str
sample: my-ecs sample: my-ecs
instance_type_family:
description: The instance type family of the instance belongs.
returned: always
type: str
sample: ecs.sn1ne
instance_type: instance_type:
description: The instance type of the running instance. description: The instance type of the running instance.
returned: always returned: always
@ -297,7 +307,7 @@ instances:
type: str type: str
sample: 10.0.0.1 sample: 10.0.0.1
public_ip_address: public_ip_address:
description: The public IPv4 address assigned to the instance description: The public IPv4 address assigned to the instance or eip address
returned: always returned: always
type: str type: str
sample: 43.0.0.1 sample: 43.0.0.1
@ -313,15 +323,15 @@ instances:
elements: dict elements: dict
contains: contains:
group_id: group_id:
description: The ID of the security group. description: The ID of the security group.
returned: always returned: always
type: str type: str
sample: sg-0123456 sample: sg-0123456
group_name: group_name:
description: The name of the security group. description: The name of the security group.
returned: always returned: always
type: str type: str
sample: my-security-group sample: my-security-group
status: status:
description: The current status of the instance. description: The current status of the instance.
returned: always returned: always
@ -340,7 +350,7 @@ instances:
vpc_id: vpc_id:
description: The ID of the VPC the instance is in. description: The ID of the VPC the instance is in.
returned: always returned: always
type: dict type: str
sample: vpc-0011223344 sample: vpc-0011223344
ids: ids:
description: List of ECS instance IDs description: List of ECS instance IDs
@ -349,11 +359,9 @@ ids:
sample: [i-12345er, i-3245fs] sample: [i-12345er, i-3245fs]
''' '''
# import time
# import sys
import traceback import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.alicloud_ecs import get_acs_connection_info, ecs_argument_spec, ecs_connect from ansible_collections.community.general.plugins.module_utils.alicloud_ecs import ecs_argument_spec, ecs_connect
HAS_FOOTMARK = False HAS_FOOTMARK = False
FOOTMARK_IMP_ERR = None FOOTMARK_IMP_ERR = None
@ -369,9 +377,11 @@ def main():
argument_spec = ecs_argument_spec() argument_spec = ecs_argument_spec()
argument_spec.update(dict( argument_spec.update(dict(
availability_zone=dict(aliases=['alicloud_zone']), availability_zone=dict(aliases=['alicloud_zone']),
instance_ids=dict(type='list', aliases=['ids']), instance_ids=dict(type='list', elements='str', aliases=['ids']),
instance_names=dict(type='list', aliases=['names']), instance_names=dict(type='list', elements='str', aliases=['names']),
instance_tags=dict(type='list', aliases=['tags']), name_prefix=dict(type='str'),
tags=dict(type='dict', aliases=['instance_tags']),
filters=dict(type='dict')
) )
) )
module = AnsibleModule(argument_spec=argument_spec) module = AnsibleModule(argument_spec=argument_spec)
@ -386,23 +396,43 @@ def main():
instances = [] instances = []
instance_ids = [] instance_ids = []
ids = module.params['instance_ids'] ids = module.params['instance_ids']
name_prefix = module.params['name_prefix']
names = module.params['instance_names'] names = module.params['instance_names']
zone_id = module.params['availability_zone'] zone_id = module.params['availability_zone']
if ids and (not isinstance(ids, list) or len(ids) < 1): if ids and (not isinstance(ids, list) or len(ids) < 1):
module.fail_json(msg='instance_ids should be a list of instances, aborting') module.fail_json(msg='instance_ids should be a list of instances, aborting')
if names and (not isinstance(names, list) or len(names) < 1): if names and (not isinstance(names, list) or len(names) < 1):
module.fail_json(msg='instance_ids should be a list of instances, aborting') module.fail_json(msg='instance_names should be a list of instances, aborting')
filters = module.params['filters']
if not filters:
filters = {}
if not ids:
ids = []
for key, value in list(filters.items()):
if key in ["InstanceIds", "instance_ids", "instance-ids"] and isinstance(ids, list):
for id in value:
if id not in ids:
ids.append(value)
if ids:
filters['instance_ids'] = ids
if module.params['tags']:
filters['tags'] = module.params['tags']
if zone_id:
filters['zone_id'] = zone_id
if names: if names:
for name in names: filters['instance_name'] = names[0]
for inst in ecs.get_all_instances(zone_id=zone_id, instance_ids=ids, instance_name=name):
instances.append(inst.read()) for inst in ecs.describe_instances(**filters):
instance_ids.append(inst.id) if name_prefix:
else: if not str(inst.instance_name).startswith(name_prefix):
for inst in ecs.get_all_instances(zone_id=zone_id, instance_ids=ids): continue
instances.append(inst.read()) volumes = ecs.describe_disks(instance_id=inst.id)
instance_ids.append(inst.id) setattr(inst, 'block_device_mappings', volumes)
setattr(inst, 'user_data', inst.describe_user_data())
instances.append(inst.read())
instance_ids.append(inst.id)
module.exit_json(changed=False, ids=instance_ids, instances=instances) module.exit_json(changed=False, ids=instance_ids, instances=instances)

View file

@ -152,12 +152,6 @@ plugins/module_utils/univention_umc.py future-import-boilerplate
plugins/module_utils/univention_umc.py metaclass-boilerplate plugins/module_utils/univention_umc.py metaclass-boilerplate
plugins/module_utils/vexata.py future-import-boilerplate plugins/module_utils/vexata.py future-import-boilerplate
plugins/module_utils/vexata.py metaclass-boilerplate plugins/module_utils/vexata.py metaclass-boilerplate
plugins/modules/cloud/alicloud/ali_instance.py validate-modules:doc-required-mismatch
plugins/modules/cloud/alicloud/ali_instance.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/alicloud/ali_instance_info.py validate-modules:doc-missing-type
plugins/modules/cloud/alicloud/ali_instance_info.py validate-modules:doc-required-mismatch
plugins/modules/cloud/alicloud/ali_instance_info.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/alicloud/ali_instance_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/atomic/atomic_container.py validate-modules:doc-missing-type plugins/modules/cloud/atomic/atomic_container.py validate-modules:doc-missing-type
plugins/modules/cloud/atomic/atomic_container.py validate-modules:doc-required-mismatch plugins/modules/cloud/atomic/atomic_container.py validate-modules:doc-required-mismatch
plugins/modules/cloud/atomic/atomic_container.py validate-modules:no-default-for-required-parameter plugins/modules/cloud/atomic/atomic_container.py validate-modules:no-default-for-required-parameter