Merge remote-tracking branch 'upstream/devel' into locale

reviewable/pr18780/r1
Augustus Kling 11 years ago
commit eabc9cd775

@ -112,7 +112,7 @@ EXAMPLES = '''
- digital_ocean: > - digital_ocean: >
state=present state=present
command=droplet command=droplet
name=my_new_droplet name=mydroplet
client_id=XXX client_id=XXX
api_key=XXX api_key=XXX
size_id=1 size_id=1
@ -131,7 +131,7 @@ EXAMPLES = '''
state=present state=present
command=droplet command=droplet
id=123 id=123
name=my_new_droplet name=mydroplet
client_id=XXX client_id=XXX
api_key=XXX api_key=XXX
size_id=1 size_id=1
@ -147,7 +147,7 @@ EXAMPLES = '''
- digital_ocean: > - digital_ocean: >
state=present state=present
ssh_key_ids=id1,id2 ssh_key_ids=id1,id2
name=my_new_droplet name=mydroplet
client_id=XXX client_id=XXX
api_key=XXX api_key=XXX
size_id=1 size_id=1
@ -221,7 +221,7 @@ class Droplet(JsonfyMixIn):
raise TimeoutError('Wait for droplet running timeout', self.id) raise TimeoutError('Wait for droplet running timeout', self.id)
def destroy(self): def destroy(self):
return self.manager.destroy_droplet(self.id) return self.manager.destroy_droplet(self.id, scrub_data=True)
@classmethod @classmethod
def setup(cls, client_id, api_key): def setup(cls, client_id, api_key):

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/python
#
# (c) 2013, Cove Schneider # (c) 2013, Cove Schneider
# #

@ -294,15 +294,6 @@ local_action:
import sys import sys
import time import time
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
try: try:
import boto.ec2 import boto.ec2
from boto.exception import EC2ResponseError from boto.exception import EC2ResponseError
@ -653,24 +644,7 @@ def main():
) )
) )
# def get_ec2_creds(module): ec2 = ec2_connect(module)
# return ec2_url, ec2_access_key, ec2_secret_key, region
ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
# If we specified an ec2_url then try connecting to it
elif ec2_url:
try:
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
else:
module.fail_json(msg="Either region or ec2_url must be specified")
if module.params.get('state') == 'absent': if module.params.get('state') == 'absent':
instance_ids = module.params.get('instance_ids') instance_ids = module.params.get('instance_ids')

@ -156,15 +156,6 @@ EXAMPLES = '''
import sys import sys
import time import time
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
try: try:
import boto import boto
import boto.ec2 import boto.ec2
@ -279,24 +270,7 @@ def main():
) )
) )
# def get_ec2_creds(module): ec2 = ec2_connect(module)
# return ec2_url, ec2_access_key, ec2_secret_key, region
ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
# If we have a region specified, connect to its endpoint.
if region:
try:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
# If we specified an ec2_url then try connecting to it
elif ec2_url:
try:
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
else:
module.fail_json(msg="Either region or ec2_url must be specified")
if module.params.get('state') == 'absent': if module.params.get('state') == 'absent':
if not module.params.get('image_id'): if not module.params.get('image_id'):

@ -102,38 +102,6 @@ else:
boto_found = True boto_found = True
def connect(ec2_url, ec2_access_key, ec2_secret_key, region, module):
""" Return an ec2 connection"""
# allow environment variables to be used if ansible vars aren't set
if not ec2_url and 'EC2_URL' in os.environ:
ec2_url = os.environ['EC2_URL']
if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ:
ec2_secret_key = os.environ['EC2_SECRET_KEY']
if not ec2_access_key and 'EC2_ACCESS_KEY' in os.environ:
ec2_access_key = os.environ['EC2_ACCESS_KEY']
# If we have a region specified, connect to its endpoint.
if region:
try:
ec2 = boto.ec2.connect_to_region(region,
aws_access_key_id=ec2_access_key,
aws_secret_access_key=ec2_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(" %s %s %s " % (region, ec2_access_key,
ec2_secret_key)))
# Otherwise, no region so we fallback to the old connection method
else:
try:
if ec2_url: # if we have an URL set, connect to the specified endpoint
ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key)
else: # otherwise it's Amazon.
ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
return ec2
def associate_ip_and_instance(ec2, address, instance_id, module): def associate_ip_and_instance(ec2, address, instance_id, module):
if ip_is_associated_with_instance(ec2, address.public_ip, instance_id, module): if ip_is_associated_with_instance(ec2, address.public_ip, instance_id, module):
module.exit_json(changed=False, public_ip=address.public_ip) module.exit_json(changed=False, public_ip=address.public_ip)
@ -248,8 +216,8 @@ def main():
state = dict(required=False, default='present', state = dict(required=False, default='present',
choices=['present', 'absent']), choices=['present', 'absent']),
ec2_url = dict(required=False, aliases=['EC2_URL']), ec2_url = dict(required=False, aliases=['EC2_URL']),
ec2_secret_key = dict(required=False, aliases=['EC2_SECRET_KEY'], no_log=True), ec2_secret_key = dict(aliases=['aws_secret_key', 'secret_key'], no_log=True),
ec2_access_key = dict(required=False, aliases=['EC2_ACCESS_KEY']), ec2_access_key = dict(aliases=['aws_access_key', 'access_key']),
region = dict(required=False, aliases=['ec2_region']), region = dict(required=False, aliases=['ec2_region']),
in_vpc = dict(required=False, choices=BOOLEANS, default=False), in_vpc = dict(required=False, choices=BOOLEANS, default=False),
), ),
@ -259,13 +227,7 @@ def main():
if not boto_found: if not boto_found:
module.fail_json(msg="boto is required") module.fail_json(msg="boto is required")
ec2_url, ec2_access_key, ec2_secret_key, region = get_ec2_creds(module) ec2 = ec2_connect(module)
ec2 = connect(ec2_url,
ec2_access_key,
ec2_secret_key,
region,
module)
instance_id = module.params.get('instance_id') instance_id = module.params.get('instance_id')
public_ip = module.params.get('public_ip') public_ip = module.params.get('public_ip')

@ -17,9 +17,9 @@
DOCUMENTATION = """ DOCUMENTATION = """
--- ---
module: ec2_elb module: ec2_elb
short_description: De-registers or registers instances from EC2 ELB(s) short_description: De-registers or registers instances from EC2 ELBs
description: description:
- This module de-registers or registers an AWS EC2 instance from the ELB(s) - This module de-registers or registers an AWS EC2 instance from the ELBs
that it belongs to. that it belongs to.
- Returns fact "ec2_elbs" which is a list of elbs attached to the instance - Returns fact "ec2_elbs" which is a list of elbs attached to the instance
if state=absent is passed as an argument. if state=absent is passed as an argument.
@ -32,6 +32,7 @@ options:
description: description:
- register or deregister the instance - register or deregister the instance
required: true required: true
choices: ['present', 'absent']
instance_id: instance_id:
description: description:
@ -102,15 +103,6 @@ import time
import sys import sys
import os import os
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
try: try:
import boto import boto
import boto.ec2 import boto.ec2

@ -113,16 +113,12 @@ def main():
ec2_url=dict(aliases=['EC2_URL']), ec2_url=dict(aliases=['EC2_URL']),
ec2_secret_key=dict(aliases=['EC2_SECRET_KEY', 'aws_secret_key'], no_log=True), ec2_secret_key=dict(aliases=['EC2_SECRET_KEY', 'aws_secret_key'], no_log=True),
ec2_access_key=dict(aliases=['EC2_ACCESS_KEY', 'aws_access_key']), ec2_access_key=dict(aliases=['EC2_ACCESS_KEY', 'aws_access_key']),
region=dict(choices=['eu-west-1', 'sa-east-1', 'us-east-1', 'ap-northeast-1', 'us-west-2', 'us-west-1', 'ap-southeast-1', 'ap-southeast-2']), region=dict(choices=AWS_REGIONS),
state = dict(default='present', choices=['present', 'absent']), state = dict(default='present', choices=['present', 'absent']),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
# def get_ec2_creds(module):
# return ec2_url, ec2_access_key, ec2_secret_key, region
ec2_url, ec2_access_key, ec2_secret_key, region = get_ec2_creds(module)
name = module.params['name'] name = module.params['name']
description = module.params['description'] description = module.params['description']
vpc_id = module.params['vpc_id'] vpc_id = module.params['vpc_id']
@ -131,21 +127,7 @@ def main():
changed = False changed = False
# If we have a region specified, connect to its endpoint. ec2 = ec2_connect(module)
if region:
try:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=ec2_access_key, aws_secret_access_key=ec2_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
else:
try:
if ec2_url: # if we have an URL set, connect to the specified endpoint
ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key)
else: # otherwise it's Amazon.
ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg=str(e))
# find the group if present # find the group if present
group = None group = None

@ -102,15 +102,6 @@ except ImportError:
print "failed=True msg='boto required for this module'" print "failed=True msg='boto required for this module'"
sys.exit(1) sys.exit(1)
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
@ -124,28 +115,11 @@ def main():
) )
) )
# def get_ec2_creds(module):
# return ec2_url, ec2_access_key, ec2_secret_key, region
ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
resource = module.params.get('resource') resource = module.params.get('resource')
tags = module.params['tags'] tags = module.params['tags']
state = module.params.get('state') state = module.params.get('state')
# If we have a region specified, connect to its endpoint. ec2 = ec2_connect(module)
if region:
try:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
# Otherwise, no region so we fallback to the old connection method
elif ec2_url:
try:
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
else:
module.fail_json(msg="Either region or ec2_url must be specified")
# We need a comparison here so that we can accurately report back changed status. # We need a comparison here so that we can accurately report back changed status.
# Need to expand the gettags return format and compare with "tags" and then tag or detag as appropriate. # Need to expand the gettags return format and compare with "tags" and then tag or detag as appropriate.

@ -127,15 +127,6 @@ except ImportError:
print "failed=True msg='boto required for this module'" print "failed=True msg='boto required for this module'"
sys.exit(1) sys.exit(1)
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
@ -151,30 +142,13 @@ def main():
) )
) )
# def get_ec2_creds(module):
# return ec2_url, ec2_access_key, ec2_secret_key, region
ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
instance = module.params.get('instance') instance = module.params.get('instance')
volume_size = module.params.get('volume_size') volume_size = module.params.get('volume_size')
iops = module.params.get('iops') iops = module.params.get('iops')
device_name = module.params.get('device_name') device_name = module.params.get('device_name')
zone = module.params.get('zone') zone = module.params.get('zone')
# If we have a region specified, connect to its endpoint. ec2 = ec2_connect(module)
if region:
try:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
# Otherwise, no region so we fallback to the old connection method
elif ec2_url:
try:
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
else:
module.fail_json(msg="Either region or ec2_url must be specified")
# Here we need to get the zone info for the instance. This covers situation where # Here we need to get the zone info for the instance. This covers situation where
# instance is specified but zone isn't. # instance is specified but zone isn't.

@ -164,15 +164,6 @@ except ImportError:
print "failed=True msg='boto required for this module'" print "failed=True msg='boto required for this module'"
sys.exit(1) sys.exit(1)
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
def get_vpc_info(vpc): def get_vpc_info(vpc):
""" """
Retrieves vpc information from an instance Retrieves vpc information from an instance

@ -17,9 +17,9 @@
DOCUMENTATION = """ DOCUMENTATION = """
--- ---
module: elasticache module: elasticache
short_description: Manage cache clusters in Amazon Elasticache.
description: description:
- Manage cache clusters in Amazon Elasticache. - Manage cache clusters in Amazon Elasticache.
short_description: Manage cache clusters in Amazon Elasticache.
- Returns information about the specified cache cluster. - Returns information about the specified cache cluster.
version_added: "1.4" version_added: "1.4"
requirements: [ "boto" ] requirements: [ "boto" ]
@ -137,15 +137,6 @@ import sys
import os import os
import time import time
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
try: try:
import boto import boto
from boto.elasticache.layer1 import ElastiCacheConnection from boto.elasticache.layer1 import ElastiCacheConnection

@ -18,11 +18,14 @@
try: try:
from novaclient.v1_1 import client as nova_client from novaclient.v1_1 import client as nova_client
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
import time import time
except ImportError: except ImportError:
print("failed=True msg='glanceclient,keystoneclient and quantumclient client are required'") print("failed=True msg='novaclient,keystoneclient and quantumclient (or neutronclient) are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -72,14 +75,19 @@ options:
- The name of the instance to which the IP address should be assigned - The name of the instance to which the IP address should be assigned
required: true required: true
default: None default: None
requirements: ["novaclient", "quantumclient", "keystoneclient"] internal_network_name:
description:
- The name of the network of the port to associate with the floating ip. Necessary when VM multiple networks.
required: false
default: None
requirements: ["novaclient", "quantumclient", "neutronclient", "keystoneclient"]
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Assign a floating ip to the instance from an external network # Assign a floating ip to the instance from an external network
- quantum_floating_ip: state=present login_username=admin login_password=admin - quantum_floating_ip: state=present login_username=admin login_password=admin
login_tenant_name=admin network_name=external_network login_tenant_name=admin network_name=external_network
instance_name=vm1 instance_name=vm1 internal_network_name=internal_network
''' '''
def _get_ksclient(module, kwargs): def _get_ksclient(module, kwargs):
@ -99,10 +107,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -111,10 +119,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
return quantum return neutron
def _get_server_state(module, nova): def _get_server_state(module, nova):
server_info = None server_info = None
@ -132,59 +140,72 @@ def _get_server_state(module, nova):
module.fail_json(msg = "Error in getting the server list: %s" % e.message) module.fail_json(msg = "Error in getting the server list: %s" % e.message)
return server_info, server return server_info, server
def _get_port_info(quantum, module, instance_id): def _get_port_info(neutron, module, instance_id, internal_network_name=None):
if internal_network_name:
kwargs = {
'name': internal_network_name,
}
networks = neutron.list_networks(**kwargs)
subnet_id = networks['networks'][0]['subnets'][0]
kwargs = { kwargs = {
'device_id': instance_id, 'device_id': instance_id,
} }
try: try:
ports = quantum.list_ports(**kwargs) ports = neutron.list_ports(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in listing ports: %s" % e.message) module.fail_json( msg = "Error in listing ports: %s" % e.message)
if subnet_id:
port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id)
port_id = port['id']
fixed_ip_address = port['fixed_ips'][0]['ip_address']
else:
port_id = ports['ports'][0]['id']
fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address']
if not ports['ports']: if not ports['ports']:
return None, None return None, None
return ports['ports'][0]['fixed_ips'][0]['ip_address'], ports['ports'][0]['id'] return fixed_ip_address, port_id
def _get_floating_ip(module, quantum, fixed_ip_address): def _get_floating_ip(module, neutron, fixed_ip_address):
kwargs = { kwargs = {
'fixed_ip_address': fixed_ip_address 'fixed_ip_address': fixed_ip_address
} }
try: try:
ips = quantum.list_floatingips(**kwargs) ips = neutron.list_floatingips(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) module.fail_json(msg = "error in fetching the floatingips's %s" % e.message)
if not ips['floatingips']: if not ips['floatingips']:
return None, None return None, None
return ips['floatingips'][0]['id'], ips['floatingips'][0]['floating_ip_address'] return ips['floatingips'][0]['id'], ips['floatingips'][0]['floating_ip_address']
def _create_floating_ip(quantum, module, port_id, net_id): def _create_floating_ip(neutron, module, port_id, net_id):
kwargs = { kwargs = {
'port_id': port_id, 'port_id': port_id,
'floating_network_id': net_id 'floating_network_id': net_id
} }
try: try:
result = quantum.create_floatingip({'floatingip': kwargs}) result = neutron.create_floatingip({'floatingip': kwargs})
except Exception as e: except Exception as e:
module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message)
module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address']) module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address'])
def _get_net_id(quantum, module): def _get_net_id(neutron, module):
kwargs = { kwargs = {
'name': module.params['network_name'], 'name': module.params['network_name'],
} }
try: try:
networks = quantum.list_networks(**kwargs) networks = neutron.list_networks(**kwargs)
except Exception as e: except Exception as e:
module.fail_json("Error in listing quantum networks: %s" % e.message) module.fail_json("Error in listing neutron networks: %s" % e.message)
if not networks['networks']: if not networks['networks']:
return None return None
return networks['networks'][0]['id'] return networks['networks'][0]['id']
def _update_floating_ip(quantum, module, port_id, floating_ip_id): def _update_floating_ip(neutron, module, port_id, floating_ip_id):
kwargs = { kwargs = {
'port_id': port_id 'port_id': port_id
} }
try: try:
result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs})
except Exception as e: except Exception as e:
module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message)
module.exit_json(changed=True, result=result) module.exit_json(changed=True, result=result)
@ -201,14 +222,15 @@ def main():
region_name = dict(default=None), region_name = dict(default=None),
network_name = dict(required=True), network_name = dict(required=True),
instance_name = dict(required=True), instance_name = dict(required=True),
state = dict(default='present', choices=['absent', 'present']) state = dict(default='present', choices=['absent', 'present']),
internal_network_name = dict(default=None),
), ),
) )
try: try:
nova = nova_client.Client(module.params['login_username'], module.params['login_password'], nova = nova_client.Client(module.params['login_username'], module.params['login_password'],
module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') module.params['login_tenant_name'], module.params['auth_url'], service_type='compute')
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
except Exception as e: except Exception as e:
module.fail_json(msg="Error in authenticating to nova: %s" % e.message) module.fail_json(msg="Error in authenticating to nova: %s" % e.message)
@ -216,23 +238,23 @@ def main():
if not server_info: if not server_info:
module.fail_json(msg="The instance name provided cannot be found") module.fail_json(msg="The instance name provided cannot be found")
fixed_ip, port_id = _get_port_info(quantum, module, server_info['id']) fixed_ip, port_id = _get_port_info(neutron, module, server_info['id'], module.params['internal_network_name'])
if not port_id: if not port_id:
module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned") module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned")
floating_id, floating_ip = _get_floating_ip(module, quantum, fixed_ip) floating_id, floating_ip = _get_floating_ip(module, neutron, fixed_ip)
if module.params['state'] == 'present': if module.params['state'] == 'present':
if floating_ip: if floating_ip:
module.exit_json(changed = False, public_ip=floating_ip) module.exit_json(changed = False, public_ip=floating_ip)
net_id = _get_net_id(quantum, module) net_id = _get_net_id(neutron, module)
if not net_id: if not net_id:
module.fail_json(msg = "cannot find the network specified, please check") module.fail_json(msg = "cannot find the network specified, please check")
_create_floating_ip(quantum, module, port_id, net_id) _create_floating_ip(neutron, module, port_id, net_id)
if module.params['state'] == 'absent': if module.params['state'] == 'absent':
if floating_ip: if floating_ip:
_update_floating_ip(quantum, module, None, floating_id) _update_floating_ip(neutron, module, None, floating_id)
module.exit_json(changed=False) module.exit_json(changed=False)
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -18,11 +18,14 @@
try: try:
from novaclient.v1_1 import client as nova_client from novaclient.v1_1 import client as nova_client
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
import time import time
except ImportError: except ImportError:
print "failed=True msg='glanceclient,novaclient and keystone client are required'" print "failed=True msg='novaclient, keystone, and quantumclient (or neutronclient) client are required'"
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -72,7 +75,7 @@ options:
- floating ip that should be assigned to the instance - floating ip that should be assigned to the instance
required: true required: true
default: None default: None
requirements: ["quantumclient", "keystoneclient"] requirements: ["quantumclient", "neutronclient", "keystoneclient"]
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -103,10 +106,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -115,10 +118,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
return quantum return neutron
def _get_server_state(module, nova): def _get_server_state(module, nova):
server_info = None server_info = None
@ -136,22 +139,22 @@ def _get_server_state(module, nova):
module.fail_json(msg = "Error in getting the server list: %s" % e.message) module.fail_json(msg = "Error in getting the server list: %s" % e.message)
return server_info, server return server_info, server
def _get_port_id(quantum, module, instance_id): def _get_port_id(neutron, module, instance_id):
kwargs = dict(device_id = instance_id) kwargs = dict(device_id = instance_id)
try: try:
ports = quantum.list_ports(**kwargs) ports = neutron.list_ports(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in listing ports: %s" % e.message) module.fail_json( msg = "Error in listing ports: %s" % e.message)
if not ports['ports']: if not ports['ports']:
return None return None
return ports['ports'][0]['id'] return ports['ports'][0]['id']
def _get_floating_ip_id(module, quantum): def _get_floating_ip_id(module, neutron):
kwargs = { kwargs = {
'floating_ip_address': module.params['ip_address'] 'floating_ip_address': module.params['ip_address']
} }
try: try:
ips = quantum.list_floatingips(**kwargs) ips = neutron.list_floatingips(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) module.fail_json(msg = "error in fetching the floatingips's %s" % e.message)
if not ips['floatingips']: if not ips['floatingips']:
@ -163,12 +166,12 @@ def _get_floating_ip_id(module, quantum):
state = "attached" state = "attached"
return state, ip return state, ip
def _update_floating_ip(quantum, module, port_id, floating_ip_id): def _update_floating_ip(neutron, module, port_id, floating_ip_id):
kwargs = { kwargs = {
'port_id': port_id 'port_id': port_id
} }
try: try:
result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs})
except Exception as e: except Exception as e:
module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message) module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message)
module.exit_json(changed = True, result = result, public_ip=module.params['ip_address']) module.exit_json(changed = True, result = result, public_ip=module.params['ip_address'])
@ -189,27 +192,28 @@ def main():
) )
try: try:
nova = nova_client.Client(module.params['login_username'], module.params['login_password'], module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') nova = nova_client.Client(module.params['login_username'], module.params['login_password'],
module.params['login_tenant_name'], module.params['auth_url'], service_type='compute')
except Exception as e: except Exception as e:
module.fail_json( msg = " Error in authenticating to nova: %s" % e.message) module.fail_json( msg = " Error in authenticating to nova: %s" % e.message)
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
state, floating_ip_id = _get_floating_ip_id(module, quantum) state, floating_ip_id = _get_floating_ip_id(module, neutron)
if module.params['state'] == 'present': if module.params['state'] == 'present':
if state == 'attached': if state == 'attached':
module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address']) module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address'])
server_info, server_obj = _get_server_state(module, nova) server_info, server_obj = _get_server_state(module, nova)
if not server_info: if not server_info:
module.fail_json(msg = " The instance name provided cannot be found") module.fail_json(msg = " The instance name provided cannot be found")
port_id = _get_port_id(quantum, module, server_info['id']) port_id = _get_port_id(neutron, module, server_info['id'])
if not port_id: if not port_id:
module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned") module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned")
_update_floating_ip(quantum, module, port_id, floating_ip_id) _update_floating_ip(neutron, module, port_id, floating_ip_id)
if module.params['state'] == 'absent': if module.params['state'] == 'absent':
if state == 'detached': if state == 'detached':
module.exit_json(changed = False, result = 'detached') module.exit_json(changed = False, result = 'detached')
if state == 'attached': if state == 'attached':
_update_floating_ip(quantum, module, None, floating_ip_id) _update_floating_ip(neutron, module, None, floating_ip_id)
module.exit_json(changed = True, result = "detached") module.exit_json(changed = True, result = "detached")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -17,10 +17,13 @@
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
try: try:
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
except ImportError: except ImportError:
print("failed=True msg='quantumclient and keystone client are required'") print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -100,7 +103,7 @@ options:
- Whether the state should be marked as up or down - Whether the state should be marked as up or down
required: false required: false
default: true default: true
requirements: ["quantumclient", "keystoneclient"] requirements: ["quantumclient", "neutronclient", "keystoneclient"]
''' '''
@ -136,10 +139,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for Quantum: %s " %e.message) module.fail_json(msg = "Error getting network endpoint: %s " %e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = " Error in connecting to quantum: %s " %e.message) module.fail_json(msg = " Error in connecting to neutron: %s " %e.message)
return quantum return neutron
def _set_tenant_id(module): def _set_tenant_id(module):
global _os_tenant_id global _os_tenant_id
@ -168,22 +171,22 @@ def _set_tenant_id(module):
module.fail_json(msg = "The tenant id cannot be found, please check the paramters") module.fail_json(msg = "The tenant id cannot be found, please check the paramters")
def _get_net_id(quantum, module): def _get_net_id(neutron, module):
kwargs = { kwargs = {
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
'name': module.params['name'], 'name': module.params['name'],
} }
try: try:
networks = quantum.list_networks(**kwargs) networks = neutron.list_networks(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in listing quantum networks: %s" % e.message) module.fail_json(msg = "Error in listing neutron networks: %s" % e.message)
if not networks['networks']: if not networks['networks']:
return None return None
return networks['networks'][0]['id'] return networks['networks'][0]['id']
def _create_network(module, quantum): def _create_network(module, neutron):
quantum.format = 'json' neutron.format = 'json'
network = { network = {
'name': module.params.get('name'), 'name': module.params.get('name'),
@ -212,15 +215,15 @@ def _create_network(module, quantum):
network.pop('provider:segmentation_id', None) network.pop('provider:segmentation_id', None)
try: try:
net = quantum.create_network({'network':network}) net = neutron.create_network({'network':network})
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in creating network: %s" % e.message) module.fail_json(msg = "Error in creating network: %s" % e.message)
return net['network']['id'] return net['network']['id']
def _delete_network(module, net_id, quantum): def _delete_network(module, net_id, neutron):
try: try:
id = quantum.delete_network(net_id) id = neutron.delete_network(net_id)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in deleting the network: %s" % e.message) module.fail_json(msg = "Error in deleting the network: %s" % e.message)
return True return True
@ -254,24 +257,24 @@ def main():
if not module.params['provider_segmentation_id']: if not module.params['provider_segmentation_id']:
module.fail_json(msg = " for vlan & gre networks, variable provider_segmentation_id should be set.") module.fail_json(msg = " for vlan & gre networks, variable provider_segmentation_id should be set.")
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
_set_tenant_id(module) _set_tenant_id(module)
if module.params['state'] == 'present': if module.params['state'] == 'present':
network_id = _get_net_id(quantum, module) network_id = _get_net_id(neutron, module)
if not network_id: if not network_id:
network_id = _create_network(module, quantum) network_id = _create_network(module, neutron)
module.exit_json(changed = True, result = "Created", id = network_id) module.exit_json(changed = True, result = "Created", id = network_id)
else: else:
module.exit_json(changed = False, result = "Success", id = network_id) module.exit_json(changed = False, result = "Success", id = network_id)
if module.params['state'] == 'absent': if module.params['state'] == 'absent':
network_id = _get_net_id(quantum, module) network_id = _get_net_id(neutron, module)
if not network_id: if not network_id:
module.exit_json(changed = False, result = "Success") module.exit_json(changed = False, result = "Success")
else: else:
_delete_network(module, network_id, quantum) _delete_network(module, network_id, neutron)
module.exit_json(changed = True, result = "Deleted") module.exit_json(changed = True, result = "Deleted")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -17,10 +17,13 @@
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
try: try:
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
except ImportError: except ImportError:
print("failed=True msg='quantumclient and keystone client are required'") print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -75,7 +78,7 @@ options:
- desired admin state of the created router . - desired admin state of the created router .
required: false required: false
default: true default: true
requirements: ["quantumclient", "keystoneclient"] requirements: ["quantumclient", "neutronclient", "keystoneclient"]
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -107,10 +110,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -119,10 +122,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
return quantum return neutron
def _set_tenant_id(module): def _set_tenant_id(module):
global _os_tenant_id global _os_tenant_id
@ -139,34 +142,34 @@ def _set_tenant_id(module):
module.fail_json(msg = "The tenant id cannot be found, please check the paramters") module.fail_json(msg = "The tenant id cannot be found, please check the paramters")
def _get_router_id(module, quantum): def _get_router_id(module, neutron):
kwargs = { kwargs = {
'name': module.params['name'], 'name': module.params['name'],
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
} }
try: try:
routers = quantum.list_routers(**kwargs) routers = neutron.list_routers(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in getting the router list: %s " % e.message) module.fail_json(msg = "Error in getting the router list: %s " % e.message)
if not routers['routers']: if not routers['routers']:
return None return None
return routers['routers'][0]['id'] return routers['routers'][0]['id']
def _create_router(module, quantum): def _create_router(module, neutron):
router = { router = {
'name': module.params['name'], 'name': module.params['name'],
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
'admin_state_up': module.params['admin_state_up'], 'admin_state_up': module.params['admin_state_up'],
} }
try: try:
new_router = quantum.create_router(dict(router=router)) new_router = neutron.create_router(dict(router=router))
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in creating router: %s" % e.message) module.fail_json( msg = "Error in creating router: %s" % e.message)
return new_router['router']['id'] return new_router['router']['id']
def _delete_router(module, quantum, router_id): def _delete_router(module, neutron, router_id):
try: try:
quantum.delete_router(router_id) neutron.delete_router(router_id)
except: except:
module.fail_json("Error in deleting the router") module.fail_json("Error in deleting the router")
return True return True
@ -186,23 +189,23 @@ def main():
), ),
) )
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
_set_tenant_id(module) _set_tenant_id(module)
if module.params['state'] == 'present': if module.params['state'] == 'present':
router_id = _get_router_id(module, quantum) router_id = _get_router_id(module, neutron)
if not router_id: if not router_id:
router_id = _create_router(module, quantum) router_id = _create_router(module, neutron)
module.exit_json(changed=True, result="Created", id=router_id) module.exit_json(changed=True, result="Created", id=router_id)
else: else:
module.exit_json(changed=False, result="success" , id=router_id) module.exit_json(changed=False, result="success" , id=router_id)
else: else:
router_id = _get_router_id(module, quantum) router_id = _get_router_id(module, neutron)
if not router_id: if not router_id:
module.exit_json(changed=False, result="success") module.exit_json(changed=False, result="success")
else: else:
_delete_router(module, quantum, router_id) _delete_router(module, neutron, router_id)
module.exit_json(changed=True, result="deleted") module.exit_json(changed=True, result="deleted")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -17,10 +17,13 @@
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
try: try:
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
except ImportError: except ImportError:
print("failed=True msg='quantumclient and keystone client are required'") print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: quantum_router_gateway module: quantum_router_gateway
@ -69,7 +72,7 @@ options:
- Name of the external network which should be attached to the router. - Name of the external network which should be attached to the router.
required: true required: true
default: None default: None
requirements: ["quantumclient", "keystoneclient"] requirements: ["quantumclient", "neutronclient", "keystoneclient"]
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -97,10 +100,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -109,62 +112,62 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
return quantum return neutron
def _get_router_id(module, quantum): def _get_router_id(module, neutron):
kwargs = { kwargs = {
'name': module.params['router_name'], 'name': module.params['router_name'],
} }
try: try:
routers = quantum.list_routers(**kwargs) routers = neutron.list_routers(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in getting the router list: %s " % e.message) module.fail_json(msg = "Error in getting the router list: %s " % e.message)
if not routers['routers']: if not routers['routers']:
return None return None
return routers['routers'][0]['id'] return routers['routers'][0]['id']
def _get_net_id(quantum, module): def _get_net_id(neutron, module):
kwargs = { kwargs = {
'name': module.params['network_name'], 'name': module.params['network_name'],
'router:external': True 'router:external': True
} }
try: try:
networks = quantum.list_networks(**kwargs) networks = neutron.list_networks(**kwargs)
except Exception as e: except Exception as e:
module.fail_json("Error in listing quantum networks: %s" % e.message) module.fail_json("Error in listing neutron networks: %s" % e.message)
if not networks['networks']: if not networks['networks']:
return None return None
return networks['networks'][0]['id'] return networks['networks'][0]['id']
def _get_port_id(quantum, module, router_id, network_id): def _get_port_id(neutron, module, router_id, network_id):
kwargs = { kwargs = {
'device_id': router_id, 'device_id': router_id,
'network_id': network_id, 'network_id': network_id,
} }
try: try:
ports = quantum.list_ports(**kwargs) ports = neutron.list_ports(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in listing ports: %s" % e.message) module.fail_json( msg = "Error in listing ports: %s" % e.message)
if not ports['ports']: if not ports['ports']:
return None return None
return ports['ports'][0]['id'] return ports['ports'][0]['id']
def _add_gateway_router(quantum, module, router_id, network_id): def _add_gateway_router(neutron, module, router_id, network_id):
kwargs = { kwargs = {
'network_id': network_id 'network_id': network_id
} }
try: try:
quantum.add_gateway_router(router_id, kwargs) neutron.add_gateway_router(router_id, kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in adding gateway to router: %s" % e.message) module.fail_json(msg = "Error in adding gateway to router: %s" % e.message)
return True return True
def _remove_gateway_router(quantum, module, router_id): def _remove_gateway_router(neutron, module, router_id):
try: try:
quantum.remove_gateway_router(router_id) neutron.remove_gateway_router(router_id)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in removing gateway to router: %s" % e.message) module.fail_json(msg = "Error in removing gateway to router: %s" % e.message)
return True return True
@ -184,28 +187,28 @@ def main():
), ),
) )
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
router_id = _get_router_id(module, quantum) router_id = _get_router_id(module, neutron)
if not router_id: if not router_id:
module.fail_json(msg="failed to get the router id, please check the router name") module.fail_json(msg="failed to get the router id, please check the router name")
network_id = _get_net_id(quantum, module) network_id = _get_net_id(neutron, module)
if not network_id: if not network_id:
module.fail_json(msg="failed to get the network id, please check the network name and make sure it is external") module.fail_json(msg="failed to get the network id, please check the network name and make sure it is external")
if module.params['state'] == 'present': if module.params['state'] == 'present':
port_id = _get_port_id(quantum, module, router_id, network_id) port_id = _get_port_id(neutron, module, router_id, network_id)
if not port_id: if not port_id:
_add_gateway_router(quantum, module, router_id, network_id) _add_gateway_router(neutron, module, router_id, network_id)
module.exit_json(changed=True, result="created") module.exit_json(changed=True, result="created")
module.exit_json(changed=False, result="success") module.exit_json(changed=False, result="success")
if module.params['state'] == 'absent': if module.params['state'] == 'absent':
port_id = _get_port_id(quantum, module, router_id, network_id) port_id = _get_port_id(neutron, module, router_id, network_id)
if not port_id: if not port_id:
module.exit_json(changed=False, result="Success") module.exit_json(changed=False, result="Success")
_remove_gateway_router(quantum, module, router_id) _remove_gateway_router(neutron, module, router_id)
module.exit_json(changed=True, result="Deleted") module.exit_json(changed=True, result="Deleted")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -17,10 +17,13 @@
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
try: try:
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
except ImportError: except ImportError:
print("failed=True msg='quantumclient and keystone client are required'") print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: quantum_router_interface module: quantum_router_interface
@ -108,10 +111,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -120,10 +123,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
return quantum return neutron
def _set_tenant_id(module): def _set_tenant_id(module):
global _os_tenant_id global _os_tenant_id
@ -140,12 +143,12 @@ def _set_tenant_id(module):
module.fail_json(msg = "The tenant id cannot be found, please check the paramters") module.fail_json(msg = "The tenant id cannot be found, please check the paramters")
def _get_router_id(module, quantum): def _get_router_id(module, neutron):
kwargs = { kwargs = {
'name': module.params['router_name'], 'name': module.params['router_name'],
} }
try: try:
routers = quantum.list_routers(**kwargs) routers = neutron.list_routers(**kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in getting the router list: %s " % e.message) module.fail_json(msg = "Error in getting the router list: %s " % e.message)
if not routers['routers']: if not routers['routers']:
@ -153,27 +156,27 @@ def _get_router_id(module, quantum):
return routers['routers'][0]['id'] return routers['routers'][0]['id']
def _get_subnet_id(module, quantum): def _get_subnet_id(module, neutron):
subnet_id = None subnet_id = None
kwargs = { kwargs = {
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
'name': module.params['subnet_name'], 'name': module.params['subnet_name'],
} }
try: try:
subnets = quantum.list_subnets(**kwargs) subnets = neutron.list_subnets(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) module.fail_json( msg = " Error in getting the subnet list:%s " % e.message)
if not subnets['subnets']: if not subnets['subnets']:
return None return None
return subnets['subnets'][0]['id'] return subnets['subnets'][0]['id']
def _get_port_id(quantum, module, router_id, subnet_id): def _get_port_id(neutron, module, router_id, subnet_id):
kwargs = { kwargs = {
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
'device_id': router_id, 'device_id': router_id,
} }
try: try:
ports = quantum.list_ports(**kwargs) ports = neutron.list_ports(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in listing ports: %s" % e.message) module.fail_json( msg = "Error in listing ports: %s" % e.message)
if not ports['ports']: if not ports['ports']:
@ -184,22 +187,22 @@ def _get_port_id(quantum, module, router_id, subnet_id):
return port['id'] return port['id']
return None return None
def _add_interface_router(quantum, module, router_id, subnet_id): def _add_interface_router(neutron, module, router_id, subnet_id):
kwargs = { kwargs = {
'subnet_id': subnet_id 'subnet_id': subnet_id
} }
try: try:
quantum.add_interface_router(router_id, kwargs) neutron.add_interface_router(router_id, kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = "Error in adding interface to router: %s" % e.message) module.fail_json(msg = "Error in adding interface to router: %s" % e.message)
return True return True
def _remove_interface_router(quantum, module, router_id, subnet_id): def _remove_interface_router(neutron, module, router_id, subnet_id):
kwargs = { kwargs = {
'subnet_id': subnet_id 'subnet_id': subnet_id
} }
try: try:
quantum.remove_interface_router(router_id, kwargs) neutron.remove_interface_router(router_id, kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg="Error in removing interface from router: %s" % e.message) module.fail_json(msg="Error in removing interface from router: %s" % e.message)
return True return True
@ -219,29 +222,29 @@ def main():
), ),
) )
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
_set_tenant_id(module) _set_tenant_id(module)
router_id = _get_router_id(module, quantum) router_id = _get_router_id(module, neutron)
if not router_id: if not router_id:
module.fail_json(msg="failed to get the router id, please check the router name") module.fail_json(msg="failed to get the router id, please check the router name")
subnet_id = _get_subnet_id(module, quantum) subnet_id = _get_subnet_id(module, neutron)
if not subnet_id: if not subnet_id:
module.fail_json(msg="failed to get the subnet id, please check the subnet name") module.fail_json(msg="failed to get the subnet id, please check the subnet name")
if module.params['state'] == 'present': if module.params['state'] == 'present':
port_id = _get_port_id(quantum, module, router_id, subnet_id) port_id = _get_port_id(neutron, module, router_id, subnet_id)
if not port_id: if not port_id:
_add_interface_router(quantum, module, router_id, subnet_id) _add_interface_router(neutron, module, router_id, subnet_id)
module.exit_json(changed=True, result="created", id=port_id) module.exit_json(changed=True, result="created", id=port_id)
module.exit_json(changed=False, result="success", id=port_id) module.exit_json(changed=False, result="success", id=port_id)
if module.params['state'] == 'absent': if module.params['state'] == 'absent':
port_id = _get_port_id(quantum, module, router_id, subnet_id) port_id = _get_port_id(neutron, module, router_id, subnet_id)
if not port_id: if not port_id:
module.exit_json(changed = False, result = "Success") module.exit_json(changed = False, result = "Success")
_remove_interface_router(quantum, module, router_id, subnet_id) _remove_interface_router(neutron, module, router_id, subnet_id)
module.exit_json(changed=True, result="Deleted") module.exit_json(changed=True, result="Deleted")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -17,10 +17,13 @@
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
try: try:
try:
from neutronclient.neutron import client
except ImportError:
from quantumclient.quantum import client from quantumclient.quantum import client
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
except ImportError: except ImportError:
print("failed=True msg='quantum and keystone client are required'") print("failed=True msg='quantumclient (or neutronclient) and keystoneclient are required'")
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -105,7 +108,7 @@ options:
- From the subnet pool the last IP that should be assigned to the virtual machines - From the subnet pool the last IP that should be assigned to the virtual machines
required: false required: false
default: None default: None
requirements: ["quantum", "keystoneclient"] requirements: ["quantumclient", "neutronclient", "keystoneclient"]
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -136,10 +139,10 @@ def _get_endpoint(module, ksclient):
try: try:
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
except Exception as e: except Exception as e:
module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
return endpoint return endpoint
def _get_quantum_client(module, kwargs): def _get_neutron_client(module, kwargs):
_ksclient = _get_ksclient(module, kwargs) _ksclient = _get_ksclient(module, kwargs)
token = _ksclient.auth_token token = _ksclient.auth_token
endpoint = _get_endpoint(module, _ksclient) endpoint = _get_endpoint(module, _ksclient)
@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs):
'endpoint_url': endpoint 'endpoint_url': endpoint
} }
try: try:
quantum = client.Client('2.0', **kwargs) neutron = client.Client('2.0', **kwargs)
except Exception as e: except Exception as e:
module.fail_json(msg = " Error in connecting to quantum: %s" % e.message) module.fail_json(msg = " Error in connecting to neutron: %s" % e.message)
return quantum return neutron
def _set_tenant_id(module): def _set_tenant_id(module):
global _os_tenant_id global _os_tenant_id
@ -167,24 +170,24 @@ def _set_tenant_id(module):
if not _os_tenant_id: if not _os_tenant_id:
module.fail_json(msg = "The tenant id cannot be found, please check the paramters") module.fail_json(msg = "The tenant id cannot be found, please check the paramters")
def _get_net_id(quantum, module): def _get_net_id(neutron, module):
kwargs = { kwargs = {
'tenant_id': _os_tenant_id, 'tenant_id': _os_tenant_id,
'name': module.params['network_name'], 'name': module.params['network_name'],
} }
try: try:
networks = quantum.list_networks(**kwargs) networks = neutron.list_networks(**kwargs)
except Exception as e: except Exception as e:
module.fail_json("Error in listing quantum networks: %s" % e.message) module.fail_json("Error in listing neutron networks: %s" % e.message)
if not networks['networks']: if not networks['networks']:
return None return None
return networks['networks'][0]['id'] return networks['networks'][0]['id']
def _get_subnet_id(module, quantum): def _get_subnet_id(module, neutron):
global _os_network_id global _os_network_id
subnet_id = None subnet_id = None
_os_network_id = _get_net_id(quantum, module) _os_network_id = _get_net_id(neutron, module)
if not _os_network_id: if not _os_network_id:
module.fail_json(msg = "network id of network not found.") module.fail_json(msg = "network id of network not found.")
else: else:
@ -193,15 +196,15 @@ def _get_subnet_id(module, quantum):
'name': module.params['name'], 'name': module.params['name'],
} }
try: try:
subnets = quantum.list_subnets(**kwargs) subnets = neutron.list_subnets(**kwargs)
except Exception as e: except Exception as e:
module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) module.fail_json( msg = " Error in getting the subnet list:%s " % e.message)
if not subnets['subnets']: if not subnets['subnets']:
return None return None
return subnets['subnets'][0]['id'] return subnets['subnets'][0]['id']
def _create_subnet(module, quantum): def _create_subnet(module, neutron):
quantum.format = 'json' neutron.format = 'json'
subnet = { subnet = {
'name': module.params['name'], 'name': module.params['name'],
'ip_version': module.params['ip_version'], 'ip_version': module.params['ip_version'],
@ -227,15 +230,15 @@ def _create_subnet(module, quantum):
else: else:
subnet.pop('dns_nameservers') subnet.pop('dns_nameservers')
try: try:
new_subnet = quantum.create_subnet(dict(subnet=subnet)) new_subnet = neutron.create_subnet(dict(subnet=subnet))
except Exception, e: except Exception, e:
module.fail_json(msg = "Failure in creating subnet: %s" % e.message) module.fail_json(msg = "Failure in creating subnet: %s" % e.message)
return new_subnet['subnet']['id'] return new_subnet['subnet']['id']
def _delete_subnet(module, quantum, subnet_id): def _delete_subnet(module, neutron, subnet_id):
try: try:
quantum.delete_subnet(subnet_id) neutron.delete_subnet(subnet_id)
except Exception as e: except Exception as e:
module.fail_json( msg = "Error in deleting subnet: %s" % e.message) module.fail_json( msg = "Error in deleting subnet: %s" % e.message)
return True return True
@ -263,21 +266,21 @@ def main():
allocation_pool_end = dict(default=None), allocation_pool_end = dict(default=None),
), ),
) )
quantum = _get_quantum_client(module, module.params) neutron = _get_neutron_client(module, module.params)
_set_tenant_id(module) _set_tenant_id(module)
if module.params['state'] == 'present': if module.params['state'] == 'present':
subnet_id = _get_subnet_id(module, quantum) subnet_id = _get_subnet_id(module, neutron)
if not subnet_id: if not subnet_id:
subnet_id = _create_subnet(module, quantum) subnet_id = _create_subnet(module, neutron)
module.exit_json(changed = True, result = "Created" , id = subnet_id) module.exit_json(changed = True, result = "Created" , id = subnet_id)
else: else:
module.exit_json(changed = False, result = "success" , id = subnet_id) module.exit_json(changed = False, result = "success" , id = subnet_id)
else: else:
subnet_id = _get_subnet_id(module, quantum) subnet_id = _get_subnet_id(module, neutron)
if not subnet_id: if not subnet_id:
module.exit_json(changed = False, result = "success") module.exit_json(changed = False, result = "success")
else: else:
_delete_subnet(module, quantum, subnet_id) _delete_subnet(module, neutron, subnet_id)
module.exit_json(changed = True, result = "deleted") module.exit_json(changed = True, result = "deleted")
# this is magic, see lib/ansible/module.params['common.py # this is magic, see lib/ansible/module.params['common.py

@ -26,6 +26,13 @@ options:
api_key: api_key:
description: description:
- Rackspace API key (overrides I(credentials)) - Rackspace API key (overrides I(credentials))
auto_increment:
description:
- Whether or not to increment a single number with the name of the
created servers. Only applicable when used with the I(group) attribute
or meta key.
default: yes
version_added: 1.5
count: count:
description: description:
- number of instances to launch - number of instances to launch
@ -147,6 +154,26 @@ EXAMPLES = '''
networks: networks:
- private - private
- public - public
register: rax
- name: Build an exact count of cloud servers with incremented names
hosts: local
gather_facts: False
tasks:
- name: Server build requests
local_action:
module: rax
credentials: ~/.raxpub
name: test%03d.example.org
flavor: performance1-1
image: ubuntu-1204-lts-precise-pangolin
state: present
count: 10
count_offset: 10
exact_count: yes
group: test
wait: yes
register: rax
''' '''
import sys import sys
@ -199,7 +226,7 @@ def create(module, names, flavor, image, meta, key_name, files,
lpath = os.path.expanduser(files[rpath]) lpath = os.path.expanduser(files[rpath])
try: try:
fileobj = open(lpath, 'r') fileobj = open(lpath, 'r')
files[rpath] = fileobj files[rpath] = fileobj.read()
except Exception, e: except Exception, e:
module.fail_json(msg='Failed to load %s' % lpath) module.fail_json(msg='Failed to load %s' % lpath)
try: try:
@ -347,7 +374,8 @@ def delete(module, instance_ids, wait, wait_timeout):
def cloudservers(module, state, name, flavor, image, meta, key_name, files, def cloudservers(module, state, name, flavor, image, meta, key_name, files,
wait, wait_timeout, disk_config, count, group, wait, wait_timeout, disk_config, count, group,
instance_ids, exact_count, networks, count_offset): instance_ids, exact_count, networks, count_offset,
auto_increment):
cs = pyrax.cloudservers cs = pyrax.cloudservers
cnw = pyrax.cloud_networks cnw = pyrax.cloud_networks
servers = [] servers = []
@ -358,6 +386,15 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files,
elif 'group' in meta and group is None: elif 'group' in meta and group is None:
group = meta['group'] group = meta['group']
# When using state=absent with group, the absent block won't match the
# names properly. Use the exact_count functionality to decrease the count
# to the desired level
was_absent = False
if group is not None and state == 'absent':
exact_count = True
state = 'present'
was_absent = True
# Check if the provided image is a UUID and if not, search for an # Check if the provided image is a UUID and if not, search for an
# appropriate image using human_id and name # appropriate image using human_id and name
if image: if image:
@ -416,6 +453,7 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files,
module.fail_json(msg='"group" must be provided when using ' module.fail_json(msg='"group" must be provided when using '
'"exact_count"') '"exact_count"')
else: else:
if auto_increment:
numbers = set() numbers = set()
try: try:
@ -436,7 +474,22 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files,
numbers.add(number) numbers.add(number)
number_range = xrange(count_offset, count_offset + count) number_range = xrange(count_offset, count_offset + count)
available_numbers = list(set(number_range).difference(numbers)) available_numbers = list(set(number_range)
.difference(numbers))
else:
for server in cs.servers.list():
if server.metadata.get('group') == group:
servers.append(server)
# If state was absent but the count was changed,
# assume we only wanted to remove that number of instances
if was_absent:
diff = len(servers) - count
if diff < 0:
count = 0
else:
count = diff
if len(servers) > count: if len(servers) > count:
state = 'absent' state = 'absent'
del servers[:count] del servers[:count]
@ -445,19 +498,23 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files,
instance_ids.append(server.id) instance_ids.append(server.id)
delete(module, instance_ids, wait, wait_timeout) delete(module, instance_ids, wait, wait_timeout)
elif len(servers) < count: elif len(servers) < count:
if auto_increment:
names = [] names = []
numbers_to_use = available_numbers[:count - len(servers)] name_slice = count - len(servers)
numbers_to_use = available_numbers[:name_slice]
for number in numbers_to_use: for number in numbers_to_use:
names.append(name % number) names.append(name % number)
else:
names = [name] * (count - len(servers))
else: else:
module.exit_json(changed=False, action=None, instances=[], module.exit_json(changed=False, action=None, instances=[],
success=[], error=[], timeout=[], success=[], error=[], timeout=[],
instance_ids={'instances': [], instance_ids={'instances': [],
'success': [], 'error': [], 'success': [], 'error': [],
'timeout': []}) 'timeout': []})
else: else:
if group is not None: if group is not None:
if auto_increment:
numbers = set() numbers = set()
try: try:
@ -479,11 +536,14 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files,
number_range = xrange(count_offset, number_range = xrange(count_offset,
count_offset + count + len(numbers)) count_offset + count + len(numbers))
available_numbers = list(set(number_range).difference(numbers)) available_numbers = list(set(number_range)
.difference(numbers))
names = [] names = []
numbers_to_use = available_numbers[:count] numbers_to_use = available_numbers[:count]
for number in numbers_to_use: for number in numbers_to_use:
names.append(name % number) names.append(name % number)
else:
names = [name] * count
else: else:
search_opts = { search_opts = {
'name': name, 'name': name,
@ -552,6 +612,7 @@ def main():
argument_spec = rax_argument_spec() argument_spec = rax_argument_spec()
argument_spec.update( argument_spec.update(
dict( dict(
auto_increment=dict(choices=BOOLEANS, default=True, type='bool'),
count=dict(default=1, type='int'), count=dict(default=1, type='int'),
count_offset=dict(default=1, type='int'), count_offset=dict(default=1, type='int'),
disk_config=dict(default='auto', choices=['auto', 'manual']), disk_config=dict(default='auto', choices=['auto', 'manual']),
@ -584,6 +645,7 @@ def main():
'please remove "service: cloudservers" from your ' 'please remove "service: cloudservers" from your '
'playbook pertaining to the "rax" module') 'playbook pertaining to the "rax" module')
auto_increment = module.params.get('auto_increment')
count = module.params.get('count') count = module.params.get('count')
count_offset = module.params.get('count_offset') count_offset = module.params.get('count_offset')
disk_config = module.params.get('disk_config').upper() disk_config = module.params.get('disk_config').upper()
@ -605,7 +667,8 @@ def main():
cloudservers(module, state, name, flavor, image, meta, key_name, files, cloudservers(module, state, name, flavor, image, meta, key_name, files,
wait, wait_timeout, disk_config, count, group, wait, wait_timeout, disk_config, count, group,
instance_ids, exact_count, networks, count_offset) instance_ids, exact_count, networks, count_offset,
auto_increment)
# import module snippets # import module snippets

@ -118,7 +118,6 @@ EXAMPLES = '''
''' '''
import sys import sys
import os
from types import NoneType from types import NoneType
@ -136,6 +135,12 @@ PROTOCOLS = ['DNS_TCP', 'DNS_UDP', 'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4',
'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP']
def node_to_dict(obj):
node = obj.to_dict()
node['id'] = obj.id
return node
def to_dict(obj): def to_dict(obj):
instance = {} instance = {}
for key in dir(obj): for key in dir(obj):
@ -151,7 +156,7 @@ def to_dict(obj):
elif key == 'nodes': elif key == 'nodes':
instance[key] = [] instance[key] = []
for node in value: for node in value:
instance[key].append(node.to_dict()) instance[key].append(node_to_dict(node))
elif (isinstance(value, NON_CALLABLES) and elif (isinstance(value, NON_CALLABLES) and
not key.startswith('_')): not key.startswith('_')):
instance[key] = value instance[key] = value

@ -262,15 +262,6 @@ EXAMPLES = '''
import sys import sys
import time import time
AWS_REGIONS = ['ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'eu-west-1',
'sa-east-1',
'us-east-1',
'us-west-1',
'us-west-2']
try: try:
import boto.rds import boto.rds
except ImportError: except ImportError:
@ -346,25 +337,7 @@ def main():
apply_immediately = module.params.get('apply_immediately') apply_immediately = module.params.get('apply_immediately')
new_instance_name = module.params.get('new_instance_name') new_instance_name = module.params.get('new_instance_name')
# allow environment variables to be used if ansible vars aren't set ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
if not region:
if 'AWS_REGION' in os.environ:
region = os.environ['AWS_REGION']
elif 'EC2_REGION' in os.environ:
region = os.environ['EC2_REGION']
if not aws_secret_key:
if 'AWS_SECRET_KEY' in os.environ:
aws_secret_key = os.environ['AWS_SECRET_KEY']
elif 'EC2_SECRET_KEY' in os.environ:
aws_secret_key = os.environ['EC2_SECRET_KEY']
if not aws_access_key:
if 'AWS_ACCESS_KEY' in os.environ:
aws_access_key = os.environ['AWS_ACCESS_KEY']
elif 'EC2_ACCESS_KEY' in os.environ:
aws_access_key = os.environ['EC2_ACCESS_KEY']
if not region: if not region:
module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION.")) module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION."))
@ -577,5 +550,6 @@ def main():
# import module snippets # import module snippets
from ansible.module_utils.basic import * from ansible.module_utils.basic import *
from ansible.module_utils.ec2 import *
main() main()

@ -7,20 +7,26 @@ DOCUMENTATION = '''
module: shell module: shell
short_description: Execute commands in nodes. short_description: Execute commands in nodes.
description: description:
- The shell module takes the command name followed by a list of arguments, - The M(shell) module takes the command name followed by a list of space-delimited arguments.
space delimited. It is almost exactly like the M(command) module but runs It is almost exactly like the M(command) module but runs
the command through a shell (C(/bin/sh)) on the remote node. the command through a shell (C(/bin/sh)) on the remote node.
version_added: "0.2" version_added: "0.2"
options: options:
(free form): free_form:
description: description:
- The command module takes a free form command to run - The shell module takes a free form command to run
required: null required: true
default: null default: null
creates: creates:
description: description:
- a filename, when it already exists, this step will NOT be run - a filename, when it already exists, this step will B(not) be run.
required: false required: no
default: null
removes:
description:
- a filename, when it does not exist, this step will B(not) be run.
version_added: "0.8"
required: no
default: null default: null
chdir: chdir:
description: description:

@ -116,13 +116,13 @@ def db_delete(cursor, db):
cursor.execute(query) cursor.execute(query)
return True return True
def db_dump(module, host, user, password, db_name, target, socket=None): def db_dump(module, host, user, password, db_name, target, port, socket=None):
cmd = module.get_bin_path('mysqldump', True) cmd = module.get_bin_path('mysqldump', True)
cmd += " --quick --user=%s --password=%s" %(user, password) cmd += " --quick --user=%s --password='%s'" %(user, password)
if socket is not None: if socket is not None:
cmd += " --socket=%s" % socket cmd += " --socket=%s" % socket
else: else:
cmd += " --host=%s" % host cmd += " --host=%s --port=%s" % (host, port)
cmd += " %s" % db_name cmd += " %s" % db_name
if os.path.splitext(target)[-1] == '.gz': if os.path.splitext(target)[-1] == '.gz':
cmd = cmd + ' | gzip > ' + target cmd = cmd + ' | gzip > ' + target
@ -133,13 +133,13 @@ def db_dump(module, host, user, password, db_name, target, socket=None):
rc, stdout, stderr = module.run_command(cmd) rc, stdout, stderr = module.run_command(cmd)
return rc, stdout, stderr return rc, stdout, stderr
def db_import(module, host, user, password, db_name, target, socket=None): def db_import(module, host, user, password, db_name, target, port, socket=None):
cmd = module.get_bin_path('mysql', True) cmd = module.get_bin_path('mysql', True)
cmd += " --user=%s --password=%s" %(user, password) cmd += " --user=%s --password='%s'" %(user, password)
if socket is not None: if socket is not None:
cmd += " --socket=%s" % socket cmd += " --socket=%s" % socket
else: else:
cmd += " --host=%s" % host cmd += " --host=%s --port=%s" % (host, port)
cmd += " -D %s" % db_name cmd += " -D %s" % db_name
if os.path.splitext(target)[-1] == '.gz': if os.path.splitext(target)[-1] == '.gz':
cmd = 'gunzip < ' + target + ' | ' + cmd cmd = 'gunzip < ' + target + ' | ' + cmd
@ -282,6 +282,7 @@ def main():
elif state == "dump": elif state == "dump":
rc, stdout, stderr = db_dump(module, login_host, login_user, rc, stdout, stderr = db_dump(module, login_host, login_user,
login_password, db, target, login_password, db, target,
port=module.params['login_port'],
socket=module.params['login_unix_socket']) socket=module.params['login_unix_socket'])
if rc != 0: if rc != 0:
module.fail_json(msg="%s" % stderr) module.fail_json(msg="%s" % stderr)
@ -290,6 +291,7 @@ def main():
elif state == "import": elif state == "import":
rc, stdout, stderr = db_import(module, login_host, login_user, rc, stdout, stderr = db_import(module, login_host, login_user,
login_password, db, target, login_password, db, target,
port=module.params['login_port'],
socket=module.params['login_unix_socket']) socket=module.params['login_unix_socket'])
if rc != 0: if rc != 0:
module.fail_json(msg="%s" % stderr) module.fail_json(msg="%s" % stderr)

@ -114,6 +114,9 @@ EXAMPLES = """
# Create database user with name 'bob' and password '12345' with all database privileges # Create database user with name 'bob' and password '12345' with all database privileges
- mysql_user: name=bob password=12345 priv=*.*:ALL state=present - mysql_user: name=bob password=12345 priv=*.*:ALL state=present
# Creates database user 'bob' and password '12345' with all database privileges and 'WITH GRANT OPTION'
- mysql_user: name=bob password=12345 priv=*.*:ALL,GRANT state=present
# Ensure no user named 'sally' exists, also passing in the auth credentials. # Ensure no user named 'sally' exists, also passing in the auth credentials.
- mysql_user: login_user=root login_password=123456 name=sally state=absent - mysql_user: login_user=root login_password=123456 name=sally state=absent

@ -124,7 +124,7 @@ options:
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
description: description:
- 'force the creation of the symlinks in two cases: the source file does - 'force the creation of the symlinks in two cases: the source file does
not exist (but will appear later); the destination exists and a file (so, we need to unlink the not exist (but will appear later); the destination exists and is a file (so, we need to unlink the
"path" file and create symlink to the "src" file in place of it).' "path" file and create symlink to the "src" file in place of it).'
notes: notes:
- See also M(copy), M(template), M(assemble) - See also M(copy), M(template), M(assemble)
@ -245,7 +245,7 @@ def main():
module.exit_json(path=path, changed=True) module.exit_json(path=path, changed=True)
if prev_state != 'absent' and prev_state != state: if prev_state != 'absent' and prev_state != state:
if not (force and (prev_state == 'file' or prev_state == 'directory') and state == 'link') and state != 'touch': if not (force and (prev_state == 'file' or prev_state == 'hard' or prev_state == 'directory') and state == 'link') and state != 'touch':
module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, src)) module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, src))
if prev_state == 'absent' and state == 'absent': if prev_state == 'absent' and state == 'absent':
@ -307,6 +307,10 @@ def main():
if not force: if not force:
module.fail_json(dest=path, src=src, msg='Cannot link, file exists at destination') module.fail_json(dest=path, src=src, msg='Cannot link, file exists at destination')
changed = True changed = True
elif prev_state == 'directory':
if not force:
module.fail_json(dest=path, src=src, msg='Cannot link, directory exists at destination')
changed = True
else: else:
module.fail_json(dest=path, src=src, msg='unexpected position reached') module.fail_json(dest=path, src=src, msg='unexpected position reached')

@ -47,9 +47,12 @@ options:
- all arguments accepted by the M(file) module also work here - all arguments accepted by the M(file) module also work here
required: false required: false
notes: notes:
- Since Ansible version 0.9, templates are loaded with C(trim_blocks=True). - "Since Ansible version 0.9, templates are loaded with C(trim_blocks=True)."
- 'You can override jinja2 settings by adding a special header to template file.
i.e. C(#jinja2: trim_blocks: False)' - "Also, you can override jinja2 settings by adding a special header to template file.
i.e. C(#jinja2:variable_start_string:'[%' , variable_end_string:'%]')
which changes the variable interpolation markers to [% var %] instead of {{ var }}. This is the best way to prevent evaluation of things that look like, but should not be Jinja2. raw/endraw in Jinja2 will not work as you expect because templates in Ansible are recursively evaluated."
requirements: [] requirements: []
author: Michael DeHaan author: Michael DeHaan
''' '''

@ -394,7 +394,7 @@ def main():
{'type': 'ITYPE_TIMEOUT', {'type': 'ITYPE_TIMEOUT',
'value': timeout}, 'value': timeout},
{'type': 'ITYPE_TIME_UNTIL_UP', {'type': 'ITYPE_TIME_UNTIL_UP',
'value': interval}] 'value': time_until_up}]
# main logic, monitor generic # main logic, monitor generic
@ -425,11 +425,9 @@ def main():
# whether it already existed, or was just created, now update # whether it already existed, or was just created, now update
# the update functions need to check for check mode but # the update functions need to check for check mode but
# cannot update settings if it doesn't exist which happens in check mode # cannot update settings if it doesn't exist which happens in check mode
if monitor_exists and not module.check_mode:
result['changed'] |= update_monitor_properties(api, module, monitor, result['changed'] |= update_monitor_properties(api, module, monitor,
template_string_properties, template_string_properties,
template_integer_properties) template_integer_properties)
# else assume nothing changed
# we just have to update the ipport if monitor already exists and it's different # we just have to update the ipport if monitor already exists and it's different
if monitor_exists and cur_ipport != ipport: if monitor_exists and cur_ipport != ipport:

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/python
#coding: utf-8 -*- #coding: utf-8 -*-
# This module is free software: you can redistribute it and/or modify # This module is free software: you can redistribute it and/or modify

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/python
#coding: utf-8 -*- #coding: utf-8 -*-
# This module is free software: you can redistribute it and/or modify # This module is free software: you can redistribute it and/or modify

@ -324,9 +324,12 @@ def upgrade(m, mode="yes", force=False,
upgrade_command = "safe-upgrade" upgrade_command = "safe-upgrade"
if force: if force:
if apt_cmd == APT_GET_CMD:
force_yes = '--force-yes' force_yes = '--force-yes'
else: else:
force_yes = '' force_yes = ''
else:
force_yes = ''
apt_cmd_path = m.get_bin_path(apt_cmd, required=True) apt_cmd_path = m.get_bin_path(apt_cmd, required=True)
cmd = '%s %s -y %s %s %s %s' % (APT_ENVVARS, apt_cmd_path, dpkg_options, cmd = '%s %s -y %s %s %s %s' % (APT_ENVVARS, apt_cmd_path, dpkg_options,

@ -124,7 +124,7 @@ def all_keys(module, keyring):
return results return results
def key_present(module, key_id): def key_present(module, key_id):
(rc, out, err) = module.run_command("apt-key list | 2>&1 grep -q %s" % key_id) (rc, out, err) = module.run_command("apt-key list | 2>&1 grep -i -q %s" % key_id)
return rc == 0 return rc == 0
def download_key(module, url): def download_key(module, url):

@ -3,6 +3,7 @@
# (c) 2012, Matt Wright <matt@nobien.net> # (c) 2012, Matt Wright <matt@nobien.net>
# (c) 2013, Alexander Saltanov <asd@mokote.com> # (c) 2013, Alexander Saltanov <asd@mokote.com>
# (c) 2014, Rutger Spiertz <rutger@kumina.nl>
# #
# This file is part of Ansible # This file is part of Ansible
# #
@ -111,7 +112,8 @@ class SourcesList(object):
self.files = {} # group sources by file self.files = {} # group sources by file
self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist') self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist')
# read sources.list # read sources.list if it exists
if os.path.isfile(self.default_file):
self.load(self.default_file) self.load(self.default_file)
# read sources.list.d # read sources.list.d

@ -135,7 +135,6 @@ def main():
name = module.params['name'] name = module.params['name']
env = module.params['virtualenv'] env = module.params['virtualenv']
executable = module.params['executable'] executable = module.params['executable']
easy_install = _get_easy_install(module, env, executable)
site_packages = module.params['virtualenv_site_packages'] site_packages = module.params['virtualenv_site_packages']
virtualenv_command = module.params['virtualenv_command'] virtualenv_command = module.params['virtualenv_command']
@ -159,6 +158,8 @@ def main():
out += out_venv out += out_venv
err += err_venv err += err_venv
easy_install = _get_easy_install(module, env, executable)
cmd = None cmd = None
changed = False changed = False
installed = _is_package_installed(module, name, easy_install) installed = _is_package_installed(module, name, easy_install)

@ -71,7 +71,7 @@ def query_package(module, brew_path, name, state="present"):
""" Returns whether a package is installed or not. """ """ Returns whether a package is installed or not. """
if state == "present": if state == "present":
rc, out, err = module.run_command("%s list -m1 | grep -q '^%s$'" % (brew_path, name)) rc, out, err = module.run_command("%s list %s" % (brew_path, name))
if rc == 0: if rc == 0:
return True return True

@ -30,7 +30,7 @@ options:
name: name:
description: description:
- The name of a node.js library to install - The name of a node.js library to install
requires: false required: false
path: path:
description: description:
- The base path where to install the node.js libraries - The base path where to install the node.js libraries

@ -147,9 +147,8 @@ EXAMPLES = '''
def _get_cmd_options(module, cmd): def _get_cmd_options(module, cmd):
thiscmd = cmd + " --help" thiscmd = cmd + " --help"
rc, stdout, stderr = module.run_command(thiscmd) rc, stdout, stderr = module.run_command(thiscmd)
#import epdb; epdb.serve()
if rc != 0: if rc != 0:
module.fail_json(msg="Could not get --help output from %s" % virtualenv) module.fail_json(msg="Could not get output from %s: %s" % (thiscmd, stdout + stderr))
words = stdout.strip().split() words = stdout.strip().split()
cmd_options = [ x for x in words if x.startswith('--') ] cmd_options = [ x for x in words if x.startswith('--') ]
@ -275,6 +274,7 @@ def main():
pip = _get_pip(module, env, module.params['executable']) pip = _get_pip(module, env, module.params['executable'])
cmd = '%s %s' % (pip, state_map[state]) cmd = '%s %s' % (pip, state_map[state])
cmd_opts = None
# If there's a virtualenv we want things we install to be able to use other # If there's a virtualenv we want things we install to be able to use other
# installations that exist as binaries within this virtualenv. Example: we # installations that exist as binaries within this virtualenv. Example: we
@ -319,7 +319,11 @@ def main():
is_local_path = True is_local_path = True
# for tarball or vcs source, applying --use-mirrors doesn't really make sense # for tarball or vcs source, applying --use-mirrors doesn't really make sense
is_package = is_vcs or is_tar or is_local_path # just a shortcut for bool is_package = is_vcs or is_tar or is_local_path # just a shortcut for bool
if not is_package and state != 'absent' and use_mirrors:
if cmd_opts is None:
cmd_opts = _get_cmd_options(module, '%s %s' % (pip, state_map[state]))
if not is_package and state != 'absent' and use_mirrors and '--use-mirrors' in cmd_opts:
cmd += ' --use-mirrors' cmd += ' --use-mirrors'
cmd += ' %s' % _get_full_name(name, version) cmd += ' %s' % _get_full_name(name, version)
elif requirements: elif requirements:

@ -1,4 +1,4 @@
#!/usr/bin/python2 #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """

@ -31,7 +31,7 @@ module: yum
version_added: historical version_added: historical
short_description: Manages packages with the I(yum) package manager short_description: Manages packages with the I(yum) package manager
description: description:
- Will install, upgrade, remove, and list packages with the I(yum) package manager. - Installs, upgrade, removes, and lists packages and groups with the I(yum) package manager.
options: options:
name: name:
description: description:
@ -41,7 +41,7 @@ options:
aliases: [] aliases: []
list: list:
description: description:
- Various non-idempotent commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples. - Various (non-idempotent) commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples.
required: false required: false
default: null default: null
state: state:
@ -94,13 +94,26 @@ author: Seth Vidal
''' '''
EXAMPLES = ''' EXAMPLES = '''
- yum: name=httpd state=latest - name: install the latest version of Apache
- yum: name=httpd state=removed yum: name=httpd state=latest
- yum: name=httpd enablerepo=testing state=installed
- yum: name=* state=latest - name: remove the Apache package
- yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present yum: name=httpd state=removed
- yum: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
- yum: name="@Development tools" state=present - name: install the latest version of Apche from the testing repo
yum: name=httpd enablerepo=testing state=installed
- name: upgrade all packages
yum: name=* state=latest
- name: install the nginx rpm from a remote repo
yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
- name: install nginx rpm from a local file
yum: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
- name: install the 'Development tools' package group
yum: name="@Development tools" state=present
''' '''
def_qf = "%{name}-%{version}-%{release}.%{arch}" def_qf = "%{name}-%{version}-%{release}.%{arch}"

@ -120,7 +120,7 @@ def main():
description=dict(required=False), description=dict(required=False),
disable_gpg_check = dict(required=False, default='no', type='bool'), disable_gpg_check = dict(required=False, default='no', type='bool'),
), ),
supports_check_mode=True, supports_check_mode=False,
) )
repo = module.params['repo'] repo = module.params['repo']

@ -43,6 +43,12 @@ options:
- What version of the repository to check out. This can be the - What version of the repository to check out. This can be the
full 40-character I(SHA-1) hash, the literal string C(HEAD), a full 40-character I(SHA-1) hash, the literal string C(HEAD), a
branch name, or a tag name. branch name, or a tag name.
accept_hostkey:
required: false
default: false
version_added: "1.5"
description:
- Add the hostkey for the repo url if not already added.
reference: reference:
required: false required: false
default: null default: null
@ -118,6 +124,7 @@ EXAMPLES = '''
import re import re
import tempfile import tempfile
def get_version(git_path, dest, ref="HEAD"): def get_version(git_path, dest, ref="HEAD"):
''' samples the version of the git repo ''' ''' samples the version of the git repo '''
os.chdir(dest) os.chdir(dest)
@ -352,6 +359,7 @@ def main():
force=dict(default='yes', type='bool'), force=dict(default='yes', type='bool'),
depth=dict(default=None, type='int'), depth=dict(default=None, type='int'),
update=dict(default='yes', type='bool'), update=dict(default='yes', type='bool'),
accept_hostkey=dict(default='no', type='bool'),
executable=dict(default=None), executable=dict(default=None),
bare=dict(default='no', type='bool'), bare=dict(default='no', type='bool'),
), ),
@ -369,6 +377,10 @@ def main():
reference = module.params['reference'] reference = module.params['reference']
git_path = module.params['executable'] or module.get_bin_path('git', True) git_path = module.params['executable'] or module.get_bin_path('git', True)
# add the git repo's hostkey
#if module.params['accept_hostkey']:
add_git_host_key(module, repo, accept_hostkey=module.params['accept_hostkey'])
if bare: if bare:
gitconfig = os.path.join(dest, 'config') gitconfig = os.path.join(dest, 'config')
else: else:
@ -430,4 +442,6 @@ def main():
# import module snippets # import module snippets
from ansible.module_utils.basic import * from ansible.module_utils.basic import *
from ansible.module_utils.known_hosts import *
main() main()

@ -59,9 +59,7 @@ options:
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
purge: purge:
description: description:
- Deletes untracked files. Runs C(hg purge). Note this requires C(purge) extension to - Deletes untracked files. Runs C(hg purge).
be enabled if C(purge=yes). This module will modify hgrc file on behalf of the user
and undo the changes before exiting the task.
required: false required: false
default: "no" default: "no"
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
@ -85,36 +83,6 @@ EXAMPLES = '''
- hg: repo=https://bitbucket.org/user/repo1 dest=/home/user/repo1 revision=stable purge=yes - hg: repo=https://bitbucket.org/user/repo1 dest=/home/user/repo1 revision=stable purge=yes
''' '''
def _set_hgrc(hgrc, vals):
parser = ConfigParser.SafeConfigParser()
parser.read(hgrc)
# val is a list of triple-tuple of the form [(section, option, value),...]
for each in vals:
(section, option, value) = each
if not parser.has_section(section):
parser.add_section(section)
parser.set(section, option, value)
f = open(hgrc, 'w')
parser.write(f)
f.close()
def _undo_hgrc(hgrc, vals):
parser = ConfigParser.SafeConfigParser()
parser.read(hgrc)
for each in vals:
(section, option, value) = each
if parser.has_section(section):
parser.remove_option(section, option)
f = open(hgrc, 'w')
parser.write(f)
f.close()
class Hg(object): class Hg(object):
def __init__(self, module, dest, repo, revision, hg_path): def __init__(self, module, dest, repo, revision, hg_path):
@ -129,7 +97,8 @@ class Hg(object):
return (rc, out, err) return (rc, out, err)
def _list_untracked(self): def _list_untracked(self):
return self._command(['purge', '-R', self.dest, '--print']) args = ['purge', '--config', 'extensions.purge=', '-R', self.dest, '--print']
return self._command(args)
def get_revision(self): def get_revision(self):
""" """
@ -168,10 +137,6 @@ class Hg(object):
return True return True
def purge(self): def purge(self):
hgrc = os.path.join(self.dest, '.hg/hgrc')
purge_option = [('extensions', 'purge', '')]
_set_hgrc(hgrc, purge_option) # enable purge extension
# before purge, find out if there are any untracked files # before purge, find out if there are any untracked files
(rc1, out1, err1) = self._list_untracked() (rc1, out1, err1) = self._list_untracked()
if rc1 != 0: if rc1 != 0:
@ -179,10 +144,9 @@ class Hg(object):
# there are some untrackd files # there are some untrackd files
if out1 != '': if out1 != '':
(rc2, out2, err2) = self._command(['purge', '-R', self.dest]) args = ['purge', '--config', 'extensions.purge=', '-R', self.dest]
if rc2 == 0: (rc2, out2, err2) = self._command(args)
_undo_hgrc(hgrc, purge_option) if rc2 != 0:
else:
self.module.fail_json(msg=err2) self.module.fail_json(msg=err2)
return True return True
else: else:

@ -94,6 +94,7 @@ class Subversion(object):
def _exec(self, args): def _exec(self, args):
bits = [ bits = [
'LANG=C',
self.svn_path, self.svn_path,
'--non-interactive', '--non-interactive',
'--trust-server-cert', '--trust-server-cert',

@ -114,6 +114,27 @@ import tempfile
import re import re
import shlex import shlex
class keydict(dict):
""" a dictionary that maintains the order of keys as they are added """
# http://stackoverflow.com/questions/2328235/pythonextend-the-dict-class
def __init__(self, *args, **kw):
super(keydict,self).__init__(*args, **kw)
self.itemlist = super(keydict,self).keys()
def __setitem__(self, key, value):
self.itemlist.append(key)
super(keydict,self).__setitem__(key, value)
def __iter__(self):
return iter(self.itemlist)
def keys(self):
return self.itemlist
def values(self):
return [self[key] for key in self]
def itervalues(self):
return (self[key] for key in self)
def keyfile(module, user, write=False, path=None, manage_dir=True): def keyfile(module, user, write=False, path=None, manage_dir=True):
""" """
Calculate name of authorized keys file, optionally creating the Calculate name of authorized keys file, optionally creating the
@ -176,7 +197,7 @@ def parseoptions(module, options):
reads a string containing ssh-key options reads a string containing ssh-key options
and returns a dictionary of those options and returns a dictionary of those options
''' '''
options_dict = {} options_dict = keydict() #ordered dict
if options: if options:
token_exp = [ token_exp = [
# matches separator # matches separator
@ -246,9 +267,8 @@ def parsekey(module, raw_key):
# check for options # check for options
if type_index is None: if type_index is None:
return None return None
elif type_index == 1: elif type_index > 0:
# parse the options and store them options = " ".join(key_parts[:type_index])
options = key_parts[0]
# parse the options (if any) # parse the options (if any)
options = parseoptions(module, options) options = parseoptions(module, options)
@ -292,7 +312,7 @@ def writekeys(module, filename, keys):
option_str = "" option_str = ""
if options: if options:
option_strings = [] option_strings = []
for option_key in sorted(options.keys()): for option_key in options.keys():
if options[option_key]: if options[option_key]:
option_strings.append("%s=\"%s\"" % (option_key, options[option_key])) option_strings.append("%s=\"%s\"" % (option_key, options[option_key]))
else: else:
@ -321,7 +341,9 @@ def enforce_state(module, params):
state = params.get("state", "present") state = params.get("state", "present")
key_options = params.get("key_options", None) key_options = params.get("key_options", None)
key = key.split('\n') # extract indivial keys into an array, skipping blank lines and comments
key = [s for s in key.splitlines() if s and not s.startswith('#')]
# check current state -- just get the filename, don't create file # check current state -- just get the filename, don't create file
do_write = False do_write = False
@ -330,10 +352,11 @@ def enforce_state(module, params):
# Check our new keys, if any of them exist we'll continue. # Check our new keys, if any of them exist we'll continue.
for new_key in key: for new_key in key:
parsed_new_key = parsekey(module, new_key)
if key_options is not None: if key_options is not None:
new_key = "%s %s" % (key_options, new_key) parsed_options = parseoptions(module, key_options)
parsed_new_key = (parsed_new_key[0], parsed_new_key[1], parsed_options, parsed_new_key[3])
parsed_new_key = parsekey(module, new_key)
if not parsed_new_key: if not parsed_new_key:
module.fail_json(msg="invalid key specified: %s" % new_key) module.fail_json(msg="invalid key specified: %s" % new_key)

@ -146,6 +146,12 @@ class DebianStrategy(GenericStrategy):
HOSTNAME_FILE = '/etc/hostname' HOSTNAME_FILE = '/etc/hostname'
def get_permanent_hostname(self): def get_permanent_hostname(self):
if not os.path.isfile(self.HOSTNAME_FILE):
try:
open(self.HOSTNAME_FILE, "a").write("")
except IOError, err:
self.module.fail_json(msg="failed to write file: %s" %
str(err))
try: try:
f = open(self.HOSTNAME_FILE) f = open(self.HOSTNAME_FILE)
try: try:
@ -250,6 +256,11 @@ class AmazonLinuxHostname(Hostname):
distribution = 'Amazon' distribution = 'Amazon'
strategy_class = RedHatStrategy strategy_class = RedHatStrategy
class ScientificLinuxHostname(Hostname):
platform = 'Linux'
distribution = 'Scientific'
strategy_class = RedHatStrategy
# =========================================== # ===========================================
class FedoraStrategy(GenericStrategy): class FedoraStrategy(GenericStrategy):

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/python
#coding: utf-8 -*- #coding: utf-8 -*-
# This module is free software: you can redistribute it and/or modify # This module is free software: you can redistribute it and/or modify

@ -431,10 +431,10 @@ class LinuxService(Service):
if check_systemd(self.name): if check_systemd(self.name):
# service is managed by systemd # service is managed by systemd
self.enable_cmd = location['systemctl'] self.enable_cmd = location['systemctl']
elif os.path.exists("/etc/init/%s.conf" % self.name): elif location['initctl'] and os.path.exists("/etc/init/%s.conf" % self.name):
# service is managed by upstart # service is managed by upstart
self.enable_cmd = location['initctl'] self.enable_cmd = location['initctl']
elif os.path.exists("/etc/init.d/%s" % self.name): elif location['update-rc.d'] and os.path.exists("/etc/init.d/%s" % self.name):
# service is managed by with SysV init scripts, but with update-rc.d # service is managed by with SysV init scripts, but with update-rc.d
self.enable_cmd = location['update-rc.d'] self.enable_cmd = location['update-rc.d']
else: else:

@ -83,7 +83,7 @@ ansible all -m setup -a 'filter=ansible_*_mb'
# Display only facts returned by facter. # Display only facts returned by facter.
ansible all -m setup -a 'filter=facter_*' ansible all -m setup -a 'filter=facter_*'
# Display only facts returned by facter. # Display only facts about certain interfaces.
ansible all -m setup -a 'filter=ansible_eth[0-2]' ansible all -m setup -a 'filter=ansible_eth[0-2]'
""" """
@ -118,7 +118,8 @@ class Facts(object):
'/etc/alpine-release': 'Alpine', '/etc/alpine-release': 'Alpine',
'/etc/release': 'Solaris', '/etc/release': 'Solaris',
'/etc/arch-release': 'Archlinux', '/etc/arch-release': 'Archlinux',
'/etc/SuSE-release': 'SuSE' } '/etc/SuSE-release': 'SuSE',
'/etc/os-release': 'Debian' }
SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' } SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' }
# A list of dicts. If there is a platform with more than one # A list of dicts. If there is a platform with more than one
@ -328,6 +329,11 @@ class Facts(object):
elif name == 'SuSE': elif name == 'SuSE':
data = get_file_content(path).splitlines() data = get_file_content(path).splitlines()
self.facts['distribution_release'] = data[2].split('=')[1].strip() self.facts['distribution_release'] = data[2].split('=')[1].strip()
elif name == 'Debian':
data = get_file_content(path).split('\n')[0]
release = re.search("PRETTY_NAME.+ \(?([^ ]+?)\)?\"", data)
if release:
self.facts['distribution_release'] = release.groups()[0]
else: else:
self.facts['distribution'] = name self.facts['distribution'] = name
@ -1540,8 +1546,7 @@ class LinuxNetwork(Network):
iface = words[-1] iface = words[-1]
if iface != device: if iface != device:
interfaces[iface] = {} interfaces[iface] = {}
interfaces[iface].update(interfaces[device]) if not secondary and "ipv4_secondaries" not in interfaces[iface]:
if "ipv4_secondaries" not in interfaces[iface]:
interfaces[iface]["ipv4_secondaries"] = [] interfaces[iface]["ipv4_secondaries"] = []
if not secondary or "ipv4" not in interfaces[iface]: if not secondary or "ipv4" not in interfaces[iface]:
interfaces[iface]['ipv4'] = {'address': address, interfaces[iface]['ipv4'] = {'address': address,
@ -1553,6 +1558,15 @@ class LinuxNetwork(Network):
'netmask': netmask, 'netmask': netmask,
'network': network, 'network': network,
}) })
# add this secondary IP to the main device
if secondary:
interfaces[device]["ipv4_secondaries"].append({
'address': address,
'netmask': netmask,
'network': network,
})
# If this is the default address, update default_ipv4 # If this is the default address, update default_ipv4
if 'address' in default_ipv4 and default_ipv4['address'] == address: if 'address' in default_ipv4 and default_ipv4['address'] == address:
default_ipv4['netmask'] = netmask default_ipv4['netmask'] = netmask
@ -2072,6 +2086,11 @@ class LinuxVirtual(Virtual):
self.facts['virtualization_role'] = 'guest' self.facts['virtualization_role'] = 'guest'
return return
if product_name == 'RHEV Hypervisor':
self.facts['virtualization_type'] = 'RHEV'
self.facts['virtualization_role'] = 'guest'
return
if product_name == 'VMware Virtual Platform': if product_name == 'VMware Virtual Platform':
self.facts['virtualization_type'] = 'VMware' self.facts['virtualization_type'] = 'VMware'
self.facts['virtualization_role'] = 'guest' self.facts['virtualization_role'] = 'guest'

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2012, David "DaviXX" CHANIAL <david.chanial@gmail.com> # (c) 2012, David "DaviXX" CHANIAL <david.chanial@gmail.com>
# (c) 2014, James Tanner <tanner.jc@gmail.com>
# #
# This file is part of Ansible # This file is part of Ansible
# #
@ -41,19 +42,9 @@ options:
aliases: [ 'val' ] aliases: [ 'val' ]
state: state:
description: description:
- Whether the entry should be present or absent. - Whether the entry should be present or absent in the sysctl file.
choices: [ "present", "absent" ] choices: [ "present", "absent" ]
default: present default: present
checks:
description:
- If C(none), no smart/facultative checks will be made. If
C(before), some checks are performed before any update (i.e. is
the sysctl key writable?). If C(after), some checks are performed
after an update (i.e. does kernel return the set value?). If
C(both), all of the smart checks (C(before) and C(after)) are
performed.
choices: [ "none", "before", "after", "both" ]
default: both
reload: reload:
description: description:
- If C(yes), performs a I(/sbin/sysctl -p) if the C(sysctl_file) is - If C(yes), performs a I(/sbin/sysctl -p) if the C(sysctl_file) is
@ -66,6 +57,13 @@ options:
- Specifies the absolute path to C(sysctl.conf), if not C(/etc/sysctl.conf). - Specifies the absolute path to C(sysctl.conf), if not C(/etc/sysctl.conf).
required: false required: false
default: /etc/sysctl.conf default: /etc/sysctl.conf
sysctl_set:
description:
- Verify token value with the sysctl command and set with -w if necessary
choices: [ "yes", "no" ]
required: false
version_added: 1.5
default: False
notes: [] notes: []
requirements: [] requirements: []
author: David "DaviXX" CHANIAL <david.chanial@gmail.com> author: David "DaviXX" CHANIAL <david.chanial@gmail.com>
@ -78,10 +76,14 @@ EXAMPLES = '''
# Remove kernel.panic entry from /etc/sysctl.conf # Remove kernel.panic entry from /etc/sysctl.conf
- sysctl: name=kernel.panic state=absent sysctl_file=/etc/sysctl.conf - sysctl: name=kernel.panic state=absent sysctl_file=/etc/sysctl.conf
# Set kernel.panic to 3 in /tmp/test_sysctl.conf, check if the sysctl key # Set kernel.panic to 3 in /tmp/test_sysctl.conf
# seems writable, but do not reload sysctl, and do not check kernel value - sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf reload=no
# after (not needed, because the real /etc/sysctl.conf was not updated)
- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf check=before reload=no # Set ip fowarding on in /proc and do not reload the sysctl file
- sysctl: name="net.ipv4.ip_forward" value=1 sysctl_set=yes
# Set ip forwarding on in /proc and in the sysctl file and reload if necessary
- sysctl: name="net.ipv4.ip_forward" value=1 sysctl_set=yes state=present reload=yes
''' '''
# ============================================================== # ==============================================================
@ -90,137 +92,174 @@ import os
import tempfile import tempfile
import re import re
# ============================================================== class SysctlModule(object):
def reload_sysctl(module, **sysctl_args): def __init__(self, module):
# update needed ? self.module = module
if not sysctl_args['reload']: self.args = self.module.params
return 0, ''
# do it self.sysctl_cmd = self.module.get_bin_path('sysctl', required=True)
if get_platform().lower() == 'freebsd': self.sysctl_file = self.args['sysctl_file']
# freebsd doesn't support -p, so reload the sysctl service
rc,out,err = module.run_command('/etc/rc.d/sysctl reload')
else:
# system supports reloading via the -p flag to sysctl, so we'll use that
sysctl_cmd = module.get_bin_path('sysctl', required=True)
rc,out,err = module.run_command([sysctl_cmd, '-p', sysctl_args['sysctl_file']])
return rc,out+err self.proc_value = None # current token value in proc fs
self.file_value = None # current token value in file
self.file_lines = [] # all lines in the file
self.file_values = {} # dict of token values
# ============================================================== self.changed = False # will change occur
self.set_proc = False # does sysctl need to set value
self.write_file = False # does the sysctl file need to be reloaded
def write_sysctl(module, lines, **sysctl_args): self.process()
# open a tmp file
fd, tmp_path = tempfile.mkstemp('.conf', '.ansible_m_sysctl_', os.path.dirname(sysctl_args['sysctl_file']))
f = open(tmp_path,"w")
try:
for l in lines:
f.write(l)
except IOError, e:
module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, str(e)))
f.flush()
f.close()
# replace the real one # ==============================================================
module.atomic_move(tmp_path, sysctl_args['sysctl_file']) # LOGIC
# ==============================================================
# end def process(self):
return sysctl_args
# Whitespace is bad
self.args['name'] = self.args['name'].strip()
if self.args['value'] is not None:
self.args['value'] = self.args['value'].strip()
else:
self.args['value'] = ""
thisname = self.args['name']
# get the current proc fs value
self.proc_value = self.get_token_curr_value(thisname)
# get the currect sysctl file value
self.read_sysctl_file()
if thisname not in self.file_values:
self.file_values[thisname] = None
# update file contents with desired token/value
self.fix_lines()
# what do we need to do now?
if self.file_values[thisname] is None and self.args['state'] == "present":
self.changed = True
self.write_file = True
elif self.file_values[thisname] != self.args['value']:
self.changed = True
self.write_file = True
if self.args['sysctl_set']:
if self.proc_value is None:
self.changed = True
elif self.proc_value != self.args['value']:
self.changed = True
self.set_proc = True
# Do the work
if not self.module.check_mode:
if self.write_file:
self.write_sysctl()
if self.write_file and self.args['reload']:
self.reload_sysctl()
if self.set_proc:
self.set_token_value(self.args['name'], self.args['value'])
# ============================================================== # ==============================================================
# SYSCTL COMMAND MANAGEMENT
# ==============================================================
def sysctl_args_expand(**sysctl_args): # Use the sysctl command to find the current value
def get_token_curr_value(self, token):
thiscmd = "%s -e -n %s" % (self.sysctl_cmd, token)
rc,out,err = self.module.run_command(thiscmd)
if rc != 0:
return None
else:
return out
# Use the sysctl command to set the current value
def set_token_value(self, token, value):
if len(value.split()) > 0:
value = '"' + value + '"'
thiscmd = "%s -w %s=%s" % (self.sysctl_cmd, token, value)
rc,out,err = self.module.run_command(thiscmd)
if rc != 0:
self.module.fail_json(msg='setting %s failed: %s' % (token, out + err))
else:
return rc
# Run sysctl -p
def reload_sysctl(self):
# do it
if get_platform().lower() == 'freebsd': if get_platform().lower() == 'freebsd':
# FreeBSD does not use the /proc file system, and instead # freebsd doesn't support -p, so reload the sysctl service
# just uses the sysctl command to set the values rc,out,err = self.module.run_command('/etc/rc.d/sysctl reload')
sysctl_args['key_path'] = None
else: else:
sysctl_args['key_path'] = sysctl_args['name'].replace('.' ,'/') # system supports reloading via the -p flag to sysctl, so we'll use that
sysctl_args['key_path'] = '/proc/sys/' + sysctl_args['key_path'] rc,out,err = self.module.run_command([self.sysctl_cmd, '-p', self.sysctl_file])
return sysctl_args
# ============================================================== if rc != 0:
self.module.fail_json(msg="Failed to reload sysctl: %s" % str(out) + str(err))
def sysctl_args_collapse(**sysctl_args): # ==============================================================
# go ahead # SYSCTL FILE MANAGEMENT
if sysctl_args.get('key_path') is not None: # ==============================================================
del sysctl_args['key_path']
if sysctl_args['state'] == 'absent' and 'value' in sysctl_args:
del sysctl_args['value']
# end # Get the token value from the sysctl file
return sysctl_args def read_sysctl_file(self):
lines = open(self.sysctl_file, "r").readlines()
for line in lines:
line = line.strip()
self.file_lines.append(line)
# ============================================================== # don't split empty lines or comments
if not line or line.startswith("#"):
continue
def sysctl_check(module, current_step, **sysctl_args): k, v = line.split('=',1)
k = k.strip()
# no smart checks at this step ? v = v.strip()
if sysctl_args['checks'] == 'none': self.file_values[k] = v.strip()
return 0, ''
if current_step == 'before' and sysctl_args['checks'] not in ['before', 'both']: # Fix the value in the sysctl file content
return 0, '' def fix_lines(self):
if current_step == 'after' and sysctl_args['checks'] not in ['after', 'both']: checked = []
return 0, '' self.fixed_lines = []
for line in self.file_lines:
# checking coherence if not line.strip() or line.strip().startswith("#"):
if sysctl_args['state'] == 'absent' and sysctl_args['value'] is not None: self.fixed_lines.append(line)
return 1, 'value=x must not be supplied when state=absent' continue
tmpline = line.strip()
if sysctl_args['state'] == 'present' and sysctl_args['value'] is None: k, v = line.split('=',1)
return 1, 'value=x must be supplied when state=present' k = k.strip()
v = v.strip()
if not sysctl_args['reload'] and sysctl_args['checks'] in ['after', 'both']: if k not in checked:
return 1, 'checks cannot be set to after or both if reload=no' checked.append(k)
if k == self.args['name']:
if sysctl_args['key_path'] is not None: if self.args['state'] == "present":
# getting file stat new_line = "%s = %s\n" % (k, self.args['value'])
if not os.access(sysctl_args['key_path'], os.F_OK): self.fixed_lines.append(new_line)
return 1, 'key_path is not an existing file, key %s seems invalid' % sysctl_args['key_path']
if not os.access(sysctl_args['key_path'], os.R_OK):
return 1, 'key_path is not a readable file, key seems to be uncheckable'
# checks before
if current_step == 'before' and sysctl_args['checks'] in ['before', 'both']:
if sysctl_args['key_path'] is not None and not os.access(sysctl_args['key_path'], os.W_OK):
return 1, 'key_path is not a writable file, key seems to be read only'
return 0, ''
# checks after
if current_step == 'after' and sysctl_args['checks'] in ['after', 'both']:
if sysctl_args['value'] is not None:
if sysctl_args['key_path'] is not None:
# reading the virtual file
f = open(sysctl_args['key_path'],'r')
output = f.read()
f.close()
else: else:
# we're on a system without /proc (ie. freebsd), so just new_line = "%s = %s\n" % (k, v)
# use the sysctl command to get the currently set value self.fixed_lines.append(new_line)
sysctl_cmd = module.get_bin_path('sysctl', required=True)
rc,output,stderr = module.run_command("%s -n %s" % (sysctl_cmd, sysctl_args['name']))
if rc != 0:
return 1, 'failed to lookup the value via the sysctl command'
output = output.strip(' \t\n\r') if self.args['name'] not in checked and self.args['state'] == "present":
output = re.sub(r'\s+', ' ', output) new_line = "%s = %s\n" % (self.args['name'], self.args['value'])
self.fixed_lines.append(new_line)
# normal case, found value must be equal to the submitted value, and # Completely rewrite the sysctl file
# we compare the exploded values to handle any whitepsace differences def write_sysctl(self):
if output.split() != sysctl_args['value'].split(): # open a tmp file
return 1, 'key seems not set to value even after update/sysctl, founded : <%s>, wanted : <%s>' % (output, sysctl_args['value']) fd, tmp_path = tempfile.mkstemp('.conf', '.ansible_m_sysctl_', os.path.dirname(self.sysctl_file))
f = open(tmp_path,"w")
try:
for l in self.fixed_lines:
f.write(l.strip() + "\n")
except IOError, e:
self.module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, str(e)))
f.flush()
f.close()
return 0, '' # replace the real one
else: self.module.atomic_move(tmp_path, self.sysctl_file)
# no value was supplied, so we're checking to make sure
# the associated name is absent. We just fudge this since
# the sysctl isn't really gone, just removed from the conf
# file meaning it will be whatever the system default is
return 0, ''
# weird end
return 1, 'unexpected position reached'
# ============================================================== # ==============================================================
# main # main
@ -233,110 +272,16 @@ def main():
name = dict(aliases=['key'], required=True), name = dict(aliases=['key'], required=True),
value = dict(aliases=['val'], required=False), value = dict(aliases=['val'], required=False),
state = dict(default='present', choices=['present', 'absent']), state = dict(default='present', choices=['present', 'absent']),
checks = dict(default='both', choices=['none', 'before', 'after', 'both']),
reload = dict(default=True, type='bool'), reload = dict(default=True, type='bool'),
sysctl_set = dict(default=False, type='bool'),
sysctl_file = dict(default='/etc/sysctl.conf') sysctl_file = dict(default='/etc/sysctl.conf')
) ),
supports_check_mode=True
) )
# defaults result = SysctlModule(module)
sysctl_args = {
'changed': False,
'name': module.params['name'],
'state': module.params['state'],
'checks': module.params['checks'],
'reload': module.params['reload'],
'value': module.params.get('value'),
'sysctl_file': module.params['sysctl_file']
}
# prepare vars
sysctl_args = sysctl_args_expand(**sysctl_args)
if get_platform().lower() == 'freebsd':
# freebsd does not like spaces around the equal sign
pattern = "%s=%s\n"
else:
pattern = "%s = %s\n"
new_line = pattern % (sysctl_args['name'], sysctl_args['value'])
to_write = []
founded = False
# make checks before act
res,msg = sysctl_check(module, 'before', **sysctl_args)
if res != 0:
module.fail_json(msg='checks_before failed with: ' + msg)
if not os.access(sysctl_args['sysctl_file'], os.W_OK):
try:
f = open(sysctl_args['sysctl_file'],'w')
f.close()
except IOError, e:
module.fail_json(msg='unable to create supplied sysctl file (destination directory probably missing)')
# reading the file
for line in open(sysctl_args['sysctl_file'], 'r').readlines():
if not line.strip():
to_write.append(line)
continue
if line.strip().startswith('#'):
to_write.append(line)
continue
# write line if not the one searched
ld = {}
ld['name'], ld['val'] = line.split('=',1)
ld['name'] = ld['name'].strip()
if ld['name'] != sysctl_args['name']:
to_write.append(line)
continue
# should be absent ?
if sysctl_args['state'] == 'absent':
# not writing the founded line
# mark as changed
sysctl_args['changed'] = True
# should be present
if sysctl_args['state'] == 'present':
# is the founded line equal to the wanted one ?
ld['val'] = ld['val'].strip()
if ld['val'] == sysctl_args['value']:
# line is equal, writing it without update (but cancel repeats)
if sysctl_args['changed'] == False and founded == False:
to_write.append(line)
founded = True
else:
# update the line (but cancel repeats)
if sysctl_args['changed'] == False and founded == False:
to_write.append(new_line)
sysctl_args['changed'] = True
continue
# if not changed, but should be present, so we have to add it module.exit_json(changed=result.changed)
if sysctl_args['state'] == 'present' and sysctl_args['changed'] == False and founded == False:
to_write.append(new_line)
sysctl_args['changed'] = True
# has changed ?
res = 0
if sysctl_args['changed'] == True:
sysctl_args = write_sysctl(module, to_write, **sysctl_args)
res,msg = reload_sysctl(module, **sysctl_args)
# make checks after act
res,msg = sysctl_check(module, 'after', **sysctl_args)
if res != 0:
module.fail_json(msg='checks_after failed with: ' + msg)
# look at the next link to avoid this workaround
# https://groups.google.com/forum/?fromgroups=#!topic/ansible-project/LMY-dwF6SQk
changed = sysctl_args['changed']
del sysctl_args['changed']
# end
sysctl_args = sysctl_args_collapse(**sysctl_args)
module.exit_json(changed=changed, **sysctl_args)
sys.exit(0) sys.exit(0)
# import module snippets # import module snippets

@ -77,8 +77,8 @@ options:
description: description:
- Optionally set the user's password to this crypted value. See - Optionally set the user's password to this crypted value. See
the user example in the github examples directory for what this looks the user example in the github examples directory for what this looks
like in a playbook. like in a playbook. The `FAQ <http://docs.ansible.com/faq.html#how-do-i-generate-crypted-passwords-for-the-user-module>`_
- Passwords values can be generated with "openssl passwd -salt <salt> -1 <plaintext>" contains details on various ways to generate these password values.
state: state:
required: false required: false
default: "present" default: "present"
@ -901,6 +901,19 @@ class OpenBSDUser(User):
cmd.append(self.shell) cmd.append(self.shell)
if self.login_class is not None: if self.login_class is not None:
# find current login class
user_login_class = None
userinfo_cmd = [self.module.get_bin_path('userinfo', True), self.name]
(rc, out, err) = self.execute_command(userinfo_cmd)
for line in out.splitlines():
tokens = line.split()
if tokens[0] == 'class' and len(tokens) == 2:
user_login_class = tokens[1]
# act only if login_class change
if self.login_class != user_login_class:
cmd.append('-L') cmd.append('-L')
cmd.append(self.login_class) cmd.append(self.login_class)

@ -40,5 +40,5 @@ author: Dag Wieers
EXAMPLES = ''' EXAMPLES = '''
# Example playbook using fail and when together # Example playbook using fail and when together
- fail: msg="The system may not be provisioned according to the CMDB status." - fail: msg="The system may not be provisioned according to the CMDB status."
when: "{{ cmdb_status }} != 'to-be-staged'" when: cmdb_status != "to-be-staged"
''' '''

@ -129,12 +129,11 @@ def _ensure_virtualenv(module):
if venv_param is None: if venv_param is None:
return return
virtualenv = module.get_bin_path('virtualenv', True)
vbin = os.path.join(os.path.expanduser(venv_param), 'bin') vbin = os.path.join(os.path.expanduser(venv_param), 'bin')
activate = os.path.join(vbin, 'activate') activate = os.path.join(vbin, 'activate')
if not os.path.exists(activate): if not os.path.exists(activate):
virtualenv = module.get_bin_path('virtualenv', True)
vcmd = '%s %s' % (virtualenv, venv_param) vcmd = '%s %s' % (virtualenv, venv_param)
vcmd = [virtualenv, venv_param] vcmd = [virtualenv, venv_param]
rc, out_venv, err_venv = module.run_command(vcmd) rc, out_venv, err_venv = module.run_command(vcmd)

Loading…
Cancel
Save