Add new module ali_instance (#36898)

pull/47533/head
He Guimin 6 years ago committed by Alicia Cozine
parent dc81a3b856
commit 85ba30a3db

@ -13,6 +13,7 @@ The following is a list of ``module_utils`` files and a general description. The
.. include:: shared_snippets/licensing.txt
- alicloud_ecs.py - Definitions and utilities for modules working with Alibaba Cloud ECS.
- api.py - Adds shared support for generic API modules.
- azure_rm_common.py - Definitions and utilities for Microsoft Azure Resource Manager template deployments.
- basic.py - General definitions and helper utilities for Ansible modules.

@ -0,0 +1,125 @@
Alibaba Cloud Compute Services Guide
====================================
.. _alicloud_intro:
Introduction
````````````
Ansible contains several modules for controlling and managing Alibaba Cloud Compute Services (Alicloud). This guide
explains how to use the Alicloud Ansible modules together.
All Alicloud modules require ``footmark`` - install it on your control machine with ``pip install footmark``.
Cloud modules, including Alicloud modules, execute on your local machine (the control machine) with ``connection: local``, rather than on remote machines defined in your hosts.
Normally, you'll use the following pattern for plays that provision Alicloud resources::
- hosts: localhost
connection: local
vars:
- ...
tasks:
- ...
.. _alicloud_authentication:
Authentication
``````````````
You can specify your Alicloud authentication credentials (access key and secret key) by passing them as
environment variables or by storing them in a vars file.
To pass authentication credentials as environment variables::
export ALICLOUD_ACCESS_KEY='Alicloud123'
export ALICLOUD_SECRET_KEY='AlicloudSecret123'
To store authentication credentials in a vars_file, encrypt them with :doc:`Ansible Vault<../user_guide/vault>` to keep them secure, then list them::
---
alicloud_access_key: "--REMOVED--"
alicloud_secret_key: "--REMOVED--"
Note that if you store your credentials in a vars_file, you need to refer to them in each Alicloud module. For example::
- ali_instance:
alicloud_access_key: "{{alicloud_access_key}}"
alicloud_secret_key: "{{alicloud_secret_key}}"
image_id: "..."
.. _alicloud_provisioning:
Provisioning
````````````
Alicloud modules create Alicloud ECS instances, disks, virtual private clouds, virtual switches, security groups and other resources.
You can use the ``count`` parameter to control the number of resources you create or terminate. For example, if you want exactly 5 instances tagged ``NewECS``,
set the ``count`` of instances to 5 and the ``count_tag`` to ``NewECS``, as shown in the last task of the example playbook below.
If there are no instances with the tag ``NewECS``, the task creates 5 new instances. If there are 2 instances with that tag, the task
creates 3 more. If there are 8 instances with that tag, the task terminates 3 of those instances.
If you do not specify a ``count_tag``, the task creates the number of instances you specify in ``count`` with the ``instance_name`` you provide.
::
# alicloud_setup.yml
- hosts: localhost
connection: local
tasks:
- name: Create VPC
ali_vpc:
cidr_block: '{{ cidr_block }}'
vpc_name: new_vpc
register: created_vpc
- name: Create VSwitch
ali_vswitch:
alicloud_zone: '{{ alicloud_zone }}'
cidr_block: '{{ vsw_cidr }}'
vswitch_name: new_vswitch
vpc_id: '{{ created_vpc.vpc.id }}'
register: created_vsw
- name: Create security group
ali_security_group:
name: new_group
vpc_id: '{{ created_vpc.vpc.id }}'
rules:
- proto: tcp
port_range: 22/22
cidr_ip: 0.0.0.0/0
priority: 1
rules_egress:
- proto: tcp
port_range: 80/80
cidr_ip: 192.168.0.54/32
priority: 1
register: created_group
- name: Create a set of instances
ali_instance:
security_groups: '{{ created_group.group_id }}'
instance_type: ecs.n4.small
image_id: "{{ ami_id }}"
instance_name: "My-new-instance"
instance_tags:
Name: NewECS
Version: 0.0.1
count: 5
count_tag:
Name: NewECS
allocate_public_ip: true
max_bandwidth_out: 50
vswitch_id: '{{ created_vsw.vswitch.id}}'
register: create_instance
In the example playbook above, data about the vpc, vswitch, group, and instances created by this playbook
are saved in the variables defined by the "register" keyword in each task.
Each Alicloud module offers a variety of parameter options. Not all options are demonstrated in the above example.
See each individual module for further details and examples.

@ -0,0 +1,158 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Copyright (c) 2017 Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
from ansible.module_utils.basic import env_fallback
try:
import footmark
import footmark.ecs
import footmark.slb
import footmark.vpc
import footmark.rds
import footmark.ess
HAS_FOOTMARK = True
except ImportError:
HAS_FOOTMARK = False
class AnsibleACSError(Exception):
pass
def acs_common_argument_spec():
return dict(
alicloud_access_key=dict(required=True, aliases=['access_key_id', 'access_key'], no_log=True,
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,
fallback=(env_fallback, ['ALICLOUD_SECRET_KEY', 'ALICLOUD_SECRET_ACCESS_KEY'])),
alicloud_security_token=dict(aliases=['security_token'], no_log=True,
fallback=(env_fallback, ['ALICLOUD_SECURITY_TOKEN'])),
)
def ecs_argument_spec():
spec = acs_common_argument_spec()
spec.update(
dict(
alicloud_region=dict(required=True, aliases=['region', 'region_id'],
fallback=(env_fallback, ['ALICLOUD_REGION', 'ALICLOUD_REGION_ID'])),
)
)
return spec
def get_acs_connection_info(module):
ecs_params = dict(acs_access_key_id=module.params.get('alicloud_access_key'),
acs_secret_access_key=module.params.get('alicloud_secret_key'),
security_token=module.params.get('alicloud_security_token'),
user_agent='Ansible-Provider-Alicloud')
return module.params.get('alicloud_region'), ecs_params
def connect_to_acs(acs_module, region, **params):
conn = acs_module.connect_to_region(region, **params)
if not conn:
if region not in [acs_module_region.id for acs_module_region in acs_module.regions()]:
raise AnsibleACSError(
"Region %s does not seem to be available for acs module %s." % (region, acs_module.__name__))
else:
raise AnsibleACSError(
"Unknown problem connecting to region %s for acs module %s." % (region, acs_module.__name__))
return conn
def ecs_connect(module):
""" Return an ecs connection"""
region, ecs_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
ecs = connect_to_acs(footmark.ecs, region, **ecs_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return ecs
def slb_connect(module):
""" Return an slb connection"""
region, slb_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
slb = connect_to_acs(footmark.slb, region, **slb_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return slb
def vpc_connect(module):
""" Return an vpc connection"""
region, vpc_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
vpc = connect_to_acs(footmark.vpc, region, **vpc_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return vpc
def rds_connect(module):
""" Return an rds connection"""
region, rds_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
rds = connect_to_acs(footmark.rds, region, **rds_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return rds
def ess_connect(module):
""" Return an ess connection"""
region, ess_params = get_acs_connection_info(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
ess = connect_to_acs(footmark.ess, region, **ess_params)
except AnsibleACSError as e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
return ess

@ -0,0 +1,777 @@
#!/usr/bin/python
# Copyright (c) 2017 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)
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see http://www.gnu.org/licenses/.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ali_instance
version_added: "2.8"
short_description: Create, Start, Stop, Restart or Terminate an Instance in ECS. Add or Remove Instance to/from a Security Group.
description:
- Create, start, stop, restart, modify or terminate ecs instances.
- Add or remove ecs instances to/from security group.
options:
state:
description:
- The state of the instance after operating.
default: 'present'
choices: [ 'present', 'running', 'stopped', 'restarted', 'absent' ]
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']
image_id:
description:
- Image ID used to launch instances. Required when C(state=present) and creating new ECS instances.
aliases: [ 'image' ]
instance_type:
description:
- Instance type used to launch instances. Required when C(state=present) and creating new ECS instances.
aliases: ['type']
security_groups:
description:
- A list of security group IDs.
vswitch_id:
description:
- The subnet ID in which to launch the instances (VPC).
aliases: ['subnet_id']
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']
description:
description:
- The description of ECS instance, which is a string of 2 to 256 characters. It cannot begin with http:// or https://.
internet_charge_type:
description:
- Internet charge type of ECS instance.
default: 'PayByBandwidth'
choices: ['PayByBandwidth', 'PayByTraffic']
max_bandwidth_in:
description:
- Maximum incoming bandwidth from the public network, measured in Mbps (Megabits per second).
default: 200
max_bandwidth_out:
description:
- Maximum outgoing bandwidth to the public network, measured in Mbps (Megabits per second).
default: 0
host_name:
description:
- Instance host name.
password:
description:
- The password to login instance. After rebooting instances, modified password will take effect.
system_disk_category:
description:
- Category of the system disk.
default: 'cloud_efficiency'
choices: ['cloud_efficiency', 'cloud_ssd']
system_disk_size:
description:
- Size of the system disk, in GB. The valid values are 40~500.
default: 40
system_disk_name:
description:
- Name of the system disk.
system_disk_description:
description:
- Description of the system disk.
count:
description:
- The number of the new instance. An integer value which indicates how many instances that match I(count_tag)
should be running. Instances are either created or terminated based on this value.
default: 1
count_tag:
description:
- I(count) determines how many instances based on a specific tag criteria should be present.
This can be expressed in multiple ways and is shown in the EXAMPLES section.
The specified count_tag must already exist or be passed in as the I(instance_tags) option.
If it is not specified, it will be replaced by I(instance_name).
allocate_public_ip:
description:
- Whether allocate a public ip for the new instance.
default: False
aliases: [ 'assign_public_ip' ]
type: bool
instance_charge_type:
description:
- The charge type of the instance.
choices: ['PrePaid', 'PostPaid']
default: 'PostPaid'
period:
description:
- The charge duration of the instance, in month. Required when C(instance_charge_type=PrePaid).
- The valid value are [1-9, 12, 24, 36].
default: 1
auto_renew:
description:
- Whether automate renew the charge of the instance.
type: bool
default: False
auto_renew_period:
description:
- The duration of the automatic renew the charge of the instance. Required when C(auto_renew=True).
choices: [1, 2, 3, 6, 12]
instance_ids:
description:
- A list of instance ids. It is required when need to operate existing instances.
If it is specified, I(count) will lose efficacy.
force:
description:
- Whether the current operation needs to be execute forcibly.
default: False
type: bool
instance_tags:
description:
- A hash/dictionaries of instance tags, to add to the new instance or for starting/stopping instance by tag. C({"key":"value"})
aliases: ["tags"]
key_name:
description:
- The name of key pair which is used to access ECS instance in SSH.
required: false
aliases: ['keypair']
user_data:
description:
- User-defined data to customize the startup behaviors of an ECS instance and to pass data into an ECS instance.
It only will take effect when launching the new ECS instances.
required: false
author:
- "He Guimin (@xiaozhu36)"
requirements:
- "python >= 2.6"
- "footmark >= 1.1.16"
extends_documentation_fragment:
- alicloud
'''
EXAMPLES = '''
# basic provisioning example vpc network
- name: basic provisioning example
hosts: localhost
vars:
alicloud_access_key: <your-alicloud-access-key-id>
alicloud_secret_key: <your-alicloud-access-secret-key>
alicloud_region: cn-beijing
image: ubuntu1404_64_40G_cloudinit_20160727.raw
instance_type: ecs.n4.small
vswitch_id: vsw-abcd1234
assign_public_ip: True
max_bandwidth_out: 10
host_name: myhost
password: mypassword
system_disk_category: cloud_efficiency
system_disk_size: 100
internet_charge_type: PayByBandwidth
security_groups: ["sg-f2rwnfh23r"]
instance_ids: ["i-abcd12346", "i-abcd12345"]
force: True
tasks:
- name: launch ECS instance in VPC network
ali_instance:
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
image: '{{ image }}'
system_disk_category: '{{ system_disk_category }}'
system_disk_size: '{{ system_disk_size }}'
instance_type: '{{ instance_type }}'
vswitch_id: '{{ vswitch_id }}'
assign_public_ip: '{{ assign_public_ip }}'
internet_charge_type: '{{ internet_charge_type }}'
max_bandwidth_out: '{{ max_bandwidth_out }}'
instance_tags:
Name: created_one
host_name: '{{ host_name }}'
password: '{{ password }}'
- name: with count and count_tag to create a number of instances
ali_instance:
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
image: '{{ image }}'
system_disk_category: '{{ system_disk_category }}'
system_disk_size: '{{ system_disk_size }}'
instance_type: '{{ instance_type }}'
assign_public_ip: '{{ assign_public_ip }}'
security_groups: '{{ security_groups }}'
internet_charge_type: '{{ internet_charge_type }}'
max_bandwidth_out: '{{ max_bandwidth_out }}'
instance_tags:
Name: created_one
Version: 0.1
count: 2
count_tag:
Name: created_one
host_name: '{{ host_name }}'
password: '{{ password }}'
- name: start instance
ali_instance:
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
instance_ids: '{{ instance_ids }}'
state: 'running'
- name: reboot instance forcibly
ecs:
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
instance_ids: '{{ instance_ids }}'
state: 'restarted'
force: '{{ force }}'
- name: Add instances to an security group
ecs:
alicloud_access_key: '{{ alicloud_access_key }}'
alicloud_secret_key: '{{ alicloud_secret_key }}'
alicloud_region: '{{ alicloud_region }}'
instance_ids: '{{ instance_ids }}'
security_groups: '{{ security_groups }}'
'''
RETURN = '''
instances:
description: List of ECS instances
returned: always
type: complex
contains:
availability_zone:
description: The availability zone of the instance is in.
returned: always
type: string
sample: cn-beijing-a
block_device_mappings:
description: Any block device mapping entries for the instance.
returned: always
type: complex
contains:
device_name:
description: The device name exposed to the instance (for example, /dev/xvda).
returned: always
type: string
sample: /dev/xvda
attach_time:
description: The time stamp when the attachment initiated.
returned: always
type: string
sample: "2018-06-25T04:08:26Z"
delete_on_termination:
description: Indicates whether the volume is deleted on instance termination.
returned: always
type: bool
sample: true
status:
description: The attachment state.
returned: always
type: string
sample: in_use
volume_id:
description: The ID of the cloud disk.
returned: always
type: string
sample: d-2zei53pjsi117y6gf9t6
cpu:
description: The CPU core count of the instance.
returned: always
type: int
sample: 4
creation_time:
description: The time the instance was created.
returned: always
type: string
sample: "2018-06-25T04:08Z"
description:
description: The instance description.
returned: always
type: string
sample: "my ansible instance"
eip:
description: The attribution of EIP associated with the instance.
returned: always
type: complex
contains:
allocation_id:
description: The ID of the EIP.
returned: always
type: string
sample: eip-12345
internet_charge_type:
description: The internet charge type of the EIP.
returned: always
type: string
sample: "paybybandwidth"
ip_address:
description: EIP address.
returned: always
type: string
sample: 42.10.2.2
expired_time:
description: The time the instance will expire.
returned: always
type: string
sample: "2099-12-31T15:59Z"
gpu:
description: The attribution of instane GPU.
returned: always
type: complex
contains:
amount:
description: The count of the GPU.
returned: always
type: int
sample: 0
spec:
description: The specification of the GPU.
returned: always
type: string
sample: ""
host_name:
description: The host name of the instance.
returned: always
type: string
sample: iZ2zewaoZ
id:
description: Alias of instance_id.
returned: always
type: string
sample: i-abc12345
instance_id:
description: ECS instance resource ID.
returned: always
type: string
sample: i-abc12345
image_id:
description: The ID of the image used to launch the instance.
returned: always
type: string
sample: m-0011223344
inner_ip_address:
description: The inner IPv4 address of the classic instance.
returned: always
type: string
sample: 10.0.0.2
instance_charge_type:
description: The instance charge type.
returned: always
type: string
sample: PostPaid
instance_name:
description: The name of the instance.
returned: always
type: string
sample: my-ecs
instance_type:
description: The instance type of the running instance.
returned: always
type: string
sample: ecs.sn1ne.xlarge
internet_charge_type:
description: The billing method of the network bandwidth.
returned: always
type: string
sample: PayByBandwidth
internet_max_bandwidth_in:
description: Maximum incoming bandwidth from the internet network.
returned: always
type: int
sample: 200
internet_max_bandwidth_out:
description: Maximum incoming bandwidth from the internet network.
returned: always
type: int
sample: 20
io_optimized:
description: Indicates whether the instance is optimized for EBS I/O.
returned: always
type: bool
sample: false
memory:
description: Memory size of the instance.
returned: always
type: int
sample: 8192
network_interfaces:
description: One or more network interfaces for the instance.
returned: always
type: complex
contains:
mac_address:
description: The MAC address.
returned: always
type: string
sample: "00:11:22:33:44:55"
network_interface_id:
description: The ID of the network interface.
returned: always
type: string
sample: eni-01234567
primary_ip_address:
description: The primary IPv4 address of the network interface within the vswitch.
returned: always
type: string
sample: 10.0.0.1
osname:
description: The operation system name of the instance owned.
returned: always
type: string
sample: CentOS
ostype:
description: The operation system type of the instance owned.
returned: always
type: string
sample: linux
private_ip_address:
description: The IPv4 address of the network interface within the subnet.
returned: always
type: string
sample: 10.0.0.1
public_ip_address:
description: The public IPv4 address assigned to the instance
returned: always
type: string
sample: 43.0.0.1
resource_group_id:
description: The id of the resource group to which the instance belongs.
returned: always
type: string
sample: my-ecs-group
security_groups:
description: One or more security groups for the instance.
returned: always
type: complex
contains:
- group_id:
description: The ID of the security group.
returned: always
type: string
sample: sg-0123456
- group_name:
description: The name of the security group.
returned: always
type: string
sample: my-security-group
status:
description: The current status of the instance.
returned: always
type: string
sample: running
tags:
description: Any tags assigned to the instance.
returned: always
type: dict
sample:
vswitch_id:
description: The ID of the vswitch in which the instance is running.
returned: always
type: string
sample: vsw-dew00abcdef
vpc_id:
description: The ID of the VPC the instance is in.
returned: always
type: dict
sample: vpc-0011223344
ids:
description: List of ECS instance IDs
returned: always
type: list
sample: [i-12345er, i-3245fs]
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.alicloud_ecs import ecs_argument_spec, ecs_connect
HAS_FOOTMARK = False
try:
from footmark.exception import ECSResponseError
HAS_FOOTMARK = True
except ImportError:
HAS_FOOTMARK = False
def get_instances_info(connection, ids):
result = []
instances = connection.get_all_instances(instance_ids=ids)
if len(instances) > 0:
for inst in instances:
result.append(inst.read())
return result
def create_instance(module, ecs, exact_count):
if exact_count <= 0:
return None
zone_id = module.params['availability_zone']
image_id = module.params['image_id']
instance_type = module.params['instance_type']
security_groups = module.params['security_groups']
vswitch_id = module.params['vswitch_id']
instance_name = module.params['instance_name']
description = module.params['description']
internet_charge_type = module.params['internet_charge_type']
max_bandwidth_out = module.params['max_bandwidth_out']
max_bandwidth_in = module.params['max_bandwidth_out']
host_name = module.params['host_name']
password = module.params['password']
system_disk_category = module.params['system_disk_category']
system_disk_size = module.params['system_disk_size']
system_disk_name = module.params['system_disk_name']
system_disk_description = module.params['system_disk_description']
allocate_public_ip = module.params['allocate_public_ip']
instance_tags = module.params['instance_tags']
period = module.params['period']
auto_renew = module.params['auto_renew']
instance_charge_type = module.params['instance_charge_type']
auto_renew_period = module.params['auto_renew_period']
user_data = module.params['user_data']
key_name = module.params['key_name']
# check whether the required parameter passed or not
if not image_id:
module.fail_json(msg='image_id is required for new instance')
if not instance_type:
module.fail_json(msg='instance_type is required for new instance')
if not isinstance(security_groups, list):
module.fail_json(msg='The parameter security_groups should be a list, aborting')
if len(security_groups) <= 0:
module.fail_json(msg='Expected the parameter security_groups is non-empty when create new ECS instances, aborting')
client_token = "Ansible-Alicloud-{0}-{1}".format(hash(str(module.params)), str(time.time()))
try:
# 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],
zone_id=zone_id, instance_name=instance_name, description=description,
internet_charge_type=internet_charge_type, max_bandwidth_out=max_bandwidth_out,
max_bandwidth_in=max_bandwidth_in, host_name=host_name, password=password,
io_optimized='optimized', system_disk_category=system_disk_category,
system_disk_size=system_disk_size, system_disk_name=system_disk_name,
system_disk_description=system_disk_description,
vswitch_id=vswitch_id, count=exact_count, allocate_public_ip=allocate_public_ip,
instance_charge_type=instance_charge_type, period=period, auto_renew=auto_renew,
auto_renew_period=auto_renew_period, instance_tags=instance_tags,
key_pair_name=key_name, user_data=user_data, client_token=client_token)
except Exception as e:
module.fail_json(msg='Unable to create instance, error: {0}'.format(e))
return instances
def main():
argument_spec = ecs_argument_spec()
argument_spec.update(dict(
security_groups=dict(type='list'),
availability_zone=dict(type='str', aliases=['alicloud_zone']),
instance_type=dict(type='str', aliases=['type']),
image_id=dict(type='str', aliases=['image']),
count=dict(type='int', default=1),
count_tag=dict(type='str'),
vswitch_id=dict(type='str', aliases=['subnet_id']),
instance_name=dict(type='str', aliases=['name']),
host_name=dict(type='str'),
password=dict(type='str', no_log=True),
internet_charge_type=dict(type='str', default='PayByBandwidth', choices=['PayByBandwidth', 'PayByTraffic']),
max_bandwidth_in=dict(type='int', default=200),
max_bandwidth_out=dict(type='int', default=0),
system_disk_category=dict(type='str', default='cloud_efficiency', choices=['cloud_efficiency', 'cloud_ssd']),
system_disk_size=dict(type='int', default=40),
system_disk_name=dict(type='str'),
system_disk_description=dict(type='str'),
force=dict(type='bool', default=False),
instance_tags=dict(type='dict', aliases=['tags']),
state=dict(default='present', choices=['present', 'running', 'stopped', 'restarted', 'absent']),
description=dict(type='str'),
allocate_public_ip=dict(type='bool', aliases=['assign_public_ip'], default=False),
instance_charge_type=dict(type='str', default='PostPaid', choices=['PrePaid', 'PostPaid']),
period=dict(type='int', default=1),
auto_renew=dict(type='bool', default=False),
instance_ids=dict(type='list'),
auto_renew_period=dict(type='int', choices=[1, 2, 3, 6, 12]),
key_name=dict(type='str', aliases=['keypair']),
user_data=dict(type='str')
)
)
module = AnsibleModule(argument_spec=argument_spec)
if HAS_FOOTMARK is False:
module.fail_json(msg="Package 'footmark' required for the module ali_instance.")
ecs = ecs_connect(module)
state = module.params['state']
instance_ids = module.params['instance_ids']
count_tag = module.params['count_tag']
count = module.params['count']
instance_name = module.params['instance_name']
force = module.params['force']
zone_id = module.params['availability_zone']
key_name = module.params['key_name']
changed = False
instances = []
if instance_ids:
if not isinstance(instance_ids, list):
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)
if not instances:
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))
elif count_tag:
instances = ecs.get_all_instances(zone_id=zone_id, instance_tags=eval(count_tag))
elif instance_name:
instances = ecs.get_all_instances(zone_id=zone_id, instance_name=instance_name)
ids = []
if state == 'present':
if not instance_ids:
if len(instances) > count:
for i in range(0, len(instances) - count):
inst = instances[len(instances) - 1]
if inst.status is not 'stopped' and not force:
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))
try:
changed = inst.terminate(force=force)
except Exception as e:
module.fail_json(msg="Delete instance {0} got an error: {1}".format(inst.id, e))
instances.pop(len(instances) - 1)
else:
try:
new_instances = create_instance(module, ecs, count - len(instances))
if new_instances:
changed = True
instances.extend(new_instances)
except Exception as e:
module.fail_json(msg="Create new instances got an error: {0}".format(e))
# Security Group join/leave begin
security_groups = module.params['security_groups']
if not isinstance(security_groups, list):
module.fail_json(msg='The parameter security_groups should be a list, aborting')
if len(security_groups) > 0:
for inst in instances:
existing = inst.security_group_ids['security_group_id']
remove = list(set(existing).difference(set(security_groups)))
add = list(set(security_groups).difference(set(existing)))
for sg in remove:
if inst.leave_security_group(sg):
changed = True
for sg in add:
if inst.join_security_group(sg):
changed = True
# Security Group join/leave ends here
# Attach/Detach key pair
inst_ids = []
for inst in instances:
if key_name is not None and key_name != inst.key_name:
if key_name == "":
changed = inst.detach_key_pair()
else:
inst_ids.append(inst.id)
if inst_ids:
changed = ecs.attach_key_pair(instance_ids=inst_ids, key_pair_name=key_name)
# Modify instance attribute
description = module.params['description']
host_name = module.params['host_name']
password = module.params['password']
for inst in instances:
if not instance_name:
instance_name = inst.name
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:
ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids))
else:
if len(instances) < 1:
module.fail_json(msg='Please specify ECS instances that you want to operate by using '
'parameters instance_ids, instance_tags or instance_name, aborting')
force = module.params['force']
if state == 'running':
try:
for inst in instances:
if inst.start():
changed = True
ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids))
except Exception as e:
module.fail_json(msg='Start instances got an error: {0}'.format(e))
elif state == 'stopped':
try:
for inst in instances:
if inst.stop(force=force):
changed = True
ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids))
except Exception as e:
module.fail_json(msg='Stop instances got an error: {0}'.format(e))
elif state == 'restarted':
try:
for inst in instances:
if inst.reboot(force=module.params['force']):
changed = True
ids.append(inst.id)
module.exit_json(changed=changed, ids=ids, instances=get_instances_info(ecs, ids))
except Exception as e:
module.fail_json(msg='Reboot instances got an error: {0}'.format(e))
else:
try:
for inst in instances:
if inst.status is not '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=[])
except Exception as e:
module.fail_json(msg='Delete instance got an error: {0}'.format(e))
if __name__ == '__main__':
main()

@ -0,0 +1,394 @@
#!/usr/bin/python
# Copyright (c) 2017 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)
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see http://www.gnu.org/licenses/.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ali_instance_facts
version_added: "2.8"
short_description: Gather facts on instances of Alibaba Cloud ECS.
description:
- This module fetches data from the Open API in Alicloud.
The module must be called from within the ECS instance itself.
options:
availability_zone:
description:
- Aliyun availability zone ID in which to launch the instance
aliases: ['alicloud_zone']
instance_names:
description:
- A list of ECS instance names.
aliases: [ "names"]
instance_ids:
description:
- A list of ECS instance ids.
aliases: ["ids"]
instance_tags:
description:
- A hash/dictionaries of instance tags. C({"key":"value"})
aliases: ["tags"]
author:
- "He Guimin (@xiaozhu36)"
requirements:
- "python >= 2.6"
- "footmark >= 1.1.16"
extends_documentation_fragment:
- alicloud
'''
EXAMPLES = '''
# 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
ali_instance_facts:
register: all_instances
- name: Find all instances based on the specified ids
ali_instance_facts:
instance_ids:
- "i-35b333d9"
- "i-ddav43kd"
register: instances_by_ids
- name: Find all instances based on the specified names/name-prefixes
ali_instance_facts:
instance_names:
- "ecs_instance-1"
- "ecs_instance_2"
register: instances_by_ids
'''
RETURN = '''
instances:
description: List of ECS instances
returned: always
type: complex
contains:
availability_zone:
description: The availability zone of the instance is in.
returned: always
type: string
sample: cn-beijing-a
block_device_mappings:
description: Any block device mapping entries for the instance.
returned: always
type: complex
contains:
device_name:
description: The device name exposed to the instance (for example, /dev/xvda).
returned: always
type: string
sample: /dev/xvda
attach_time:
description: The time stamp when the attachment initiated.
returned: always
type: string
sample: "2018-06-25T04:08:26Z"
delete_on_termination:
description: Indicates whether the volume is deleted on instance termination.
returned: always
type: bool
sample: true
status:
description: The attachment state.
returned: always
type: string
sample: in_use
volume_id:
description: The ID of the cloud disk.
returned: always
type: string
sample: d-2zei53pjsi117y6gf9t6
cpu:
description: The CPU core count of the instance.
returned: always
type: int
sample: 4
creation_time:
description: The time the instance was created.
returned: always
type: string
sample: "2018-06-25T04:08Z"
description:
description: The instance description.
returned: always
type: string
sample: "my ansible instance"
eip:
description: The attribution of EIP associated with the instance.
returned: always
type: complex
contains:
allocation_id:
description: The ID of the EIP.
returned: always
type: string
sample: eip-12345
internet_charge_type:
description: The internet charge type of the EIP.
returned: always
type: string
sample: "paybybandwidth"
ip_address:
description: EIP address.
returned: always
type: string
sample: 42.10.2.2
expired_time:
description: The time the instance will expire.
returned: always
type: string
sample: "2099-12-31T15:59Z"
gpu:
description: The attribution of instane GPU.
returned: always
type: complex
contains:
amount:
description: The count of the GPU.
returned: always
type: int
sample: 0
spec:
description: The specification of the GPU.
returned: always
type: string
sample: ""
host_name:
description: The host name of the instance.
returned: always
type: string
sample: iZ2zewaoZ
id:
description: Alias of instance_id.
returned: always
type: string
sample: i-abc12345
instance_id:
description: ECS instance resource ID.
returned: always
type: string
sample: i-abc12345
image_id:
description: The ID of the image used to launch the instance.
returned: always
type: string
sample: m-0011223344
inner_ip_address:
description: The inner IPv4 address of the classic instance.
returned: always
type: string
sample: 10.0.0.2
instance_charge_type:
description: The instance charge type.
returned: always
type: string
sample: PostPaid
instance_name:
description: The name of the instance.
returned: always
type: string
sample: my-ecs
instance_type:
description: The instance type of the running instance.
returned: always
type: string
sample: ecs.sn1ne.xlarge
internet_charge_type:
description: The billing method of the network bandwidth.
returned: always
type: string
sample: PayByBandwidth
internet_max_bandwidth_in:
description: Maximum incoming bandwidth from the internet network.
returned: always
type: int
sample: 200
internet_max_bandwidth_out:
description: Maximum incoming bandwidth from the internet network.
returned: always
type: int
sample: 20
io_optimized:
description: Indicates whether the instance is optimized for EBS I/O.
returned: always
type: bool
sample: false
memory:
description: Memory size of the instance.
returned: always
type: int
sample: 8192
network_interfaces:
description: One or more network interfaces for the instance.
returned: always
type: complex
contains:
mac_address:
description: The MAC address.
returned: always
type: string
sample: "00:11:22:33:44:55"
network_interface_id:
description: The ID of the network interface.
returned: always
type: string
sample: eni-01234567
primary_ip_address:
description: The primary IPv4 address of the network interface within the vswitch.
returned: always
type: string
sample: 10.0.0.1
osname:
description: The operation system name of the instance owned.
returned: always
type: string
sample: CentOS
ostype:
description: The operation system type of the instance owned.
returned: always
type: string
sample: linux
private_ip_address:
description: The IPv4 address of the network interface within the subnet.
returned: always
type: string
sample: 10.0.0.1
public_ip_address:
description: The public IPv4 address assigned to the instance
returned: always
type: string
sample: 43.0.0.1
resource_group_id:
description: The id of the resource group to which the instance belongs.
returned: always
type: string
sample: my-ecs-group
security_groups:
description: One or more security groups for the instance.
returned: always
type: complex
contains:
- group_id:
description: The ID of the security group.
returned: always
type: string
sample: sg-0123456
- group_name:
description: The name of the security group.
returned: always
type: string
sample: my-security-group
status:
description: The current status of the instance.
returned: always
type: string
sample: running
tags:
description: Any tags assigned to the instance.
returned: always
type: dict
sample:
vswitch_id:
description: The ID of the vswitch in which the instance is running.
returned: always
type: string
sample: vsw-dew00abcdef
vpc_id:
description: The ID of the VPC the instance is in.
returned: always
type: dict
sample: vpc-0011223344
ids:
description: List of ECS instance IDs
returned: always
type: list
sample: [i-12345er, i-3245fs]
'''
# import time
# import sys
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.alicloud_ecs import get_acs_connection_info, ecs_argument_spec, ecs_connect
HAS_FOOTMARK = False
try:
from footmark.exception import ECSResponseError
HAS_FOOTMARK = True
except ImportError:
HAS_FOOTMARK = False
def main():
argument_spec = ecs_argument_spec()
argument_spec.update(dict(
availability_zone=dict(aliases=['alicloud_zone']),
instance_ids=dict(type='list', aliases=['ids']),
instance_names=dict(type='list', aliases=['names']),
instance_tags=dict(type='list', aliases=['tags']),
)
)
module = AnsibleModule(argument_spec=argument_spec)
if HAS_FOOTMARK is False:
module.fail_json(msg='footmark required for the module ali_instance_facts')
ecs = ecs_connect(module)
instances = []
instance_ids = []
ids = module.params['instance_ids']
names = module.params['instance_names']
zone_id = module.params['availability_zone']
if ids and (not isinstance(ids, list) or len(ids) < 1):
module.fail_json(msg='instance_ids should be a list of instances, aborting')
if names and (not isinstance(names, list) or len(names) < 1):
module.fail_json(msg='instance_ids should be a list of instances, aborting')
if names:
for name in names:
for inst in ecs.get_all_instances(zone_id=zone_id, instance_ids=ids, instance_name=name):
instances.append(inst.read())
instance_ids.append(inst.id)
else:
for inst in ecs.get_all_instances(zone_id=zone_id, instance_ids=ids):
instances.append(inst.read())
instance_ids.append(inst.id)
module.exit_json(changed=False, ids=instance_ids, instances=instances)
if __name__ == '__main__':
main()

@ -0,0 +1,61 @@
# !/usr/bin/python
# Copyright (c) 2017 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)
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
class ModuleDocFragment(object):
# Alicloud only documentation fragment
DOCUMENTATION = """
options:
alicloud_access_key:
description:
- Aliyun Cloud access key. If not set then the value of environment variable C(ALICLOUD_ACCESS_KEY),
C(ALICLOUD_ACCESS_KEY_ID) will be used instead.
aliases: ['access_key_id', 'access_key']
alicloud_secret_key:
description:
- Aliyun Cloud secret key. If not set then the value of environment variable C(ALICLOUD_SECRET_KEY),
C(ALICLOUD_SECRET_ACCESS_KEY) will be used instead.
aliases: ['secret_access_key', 'secret_key']
alicloud_region:
description:
- The Aliyun Cloud region to use. If not specified then the value of environment variable
C(ALICLOUD_REGION), C(ALICLOUD_REGION_ID) will be used instead.
aliases: ['region', 'region_id']
alicloud_security_token:
description:
- The Aliyun Cloud security token. If not specified then the value of environment variable
C(ALICLOUD_SECURITY_TOKEN) will be used instead.
aliases: ['security_token']
author:
- "He Guimin (@xiaozhu36)"
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- alicloud
notes:
- If parameters are not set within the module, the following
environment variables can be used in decreasing order of precedence
C(ALICLOUD_ACCESS_KEY) or C(ALICLOUD_ACCESS_KEY_ID),
C(ALICLOUD_SECRET_KEY) or C(ALICLOUD_SECRET_ACCESS_KEY),
C(ALICLOUD_REGION) or C(ALICLOUD_REGION_ID),
C(ALICLOUD_SECURITY_TOKEN)
- 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
"""
Loading…
Cancel
Save