diff --git a/cloud/digital_ocean b/cloud/digital_ocean index 400bf3bf54b..bd975cf1b90 100644 --- a/cloud/digital_ocean +++ b/cloud/digital_ocean @@ -112,7 +112,7 @@ EXAMPLES = ''' - digital_ocean: > state=present command=droplet - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 @@ -131,7 +131,7 @@ EXAMPLES = ''' state=present command=droplet id=123 - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 @@ -147,7 +147,7 @@ EXAMPLES = ''' - digital_ocean: > state=present ssh_key_ids=id1,id2 - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 @@ -221,7 +221,7 @@ class Droplet(JsonfyMixIn): raise TimeoutError('Wait for droplet running timeout', self.id) def destroy(self): - return self.manager.destroy_droplet(self.id) + return self.manager.destroy_droplet(self.id, scrub_data=True) @classmethod def setup(cls, client_id, api_key): diff --git a/cloud/docker b/cloud/docker index 9abf63e18ab..d4fb1f372a9 100644 --- a/cloud/docker +++ b/cloud/docker @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# +#!/usr/bin/python # (c) 2013, Cove Schneider # diff --git a/cloud/ec2 b/cloud/ec2 index 0e0b8aaf0fd..a565b0359a6 100644 --- a/cloud/ec2 +++ b/cloud/ec2 @@ -294,15 +294,6 @@ local_action: import sys 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: import boto.ec2 from boto.exception import EC2ResponseError @@ -653,24 +644,7 @@ 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) - - # 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") + ec2 = ec2_connect(module) if module.params.get('state') == 'absent': instance_ids = module.params.get('instance_ids') diff --git a/cloud/ec2_ami b/cloud/ec2_ami index ea7e0ad86dc..f90f23db999 100644 --- a/cloud/ec2_ami +++ b/cloud/ec2_ami @@ -156,15 +156,6 @@ EXAMPLES = ''' import sys 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: import boto import boto.ec2 @@ -279,24 +270,7 @@ 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) - - # 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") + ec2 = ec2_connect(module) if module.params.get('state') == 'absent': if not module.params.get('image_id'): diff --git a/cloud/ec2_eip b/cloud/ec2_eip index 1c5db8cf4c1..4399c6bdf6a 100644 --- a/cloud/ec2_eip +++ b/cloud/ec2_eip @@ -102,38 +102,6 @@ else: 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): if ip_is_associated_with_instance(ec2, address.public_ip, instance_id, module): module.exit_json(changed=False, public_ip=address.public_ip) @@ -248,8 +216,8 @@ def main(): state = dict(required=False, default='present', choices=['present', 'absent']), ec2_url = dict(required=False, aliases=['EC2_URL']), - ec2_secret_key = dict(required=False, aliases=['EC2_SECRET_KEY'], no_log=True), - ec2_access_key = dict(required=False, aliases=['EC2_ACCESS_KEY']), + ec2_secret_key = dict(aliases=['aws_secret_key', 'secret_key'], no_log=True), + ec2_access_key = dict(aliases=['aws_access_key', 'access_key']), region = dict(required=False, aliases=['ec2_region']), in_vpc = dict(required=False, choices=BOOLEANS, default=False), ), @@ -259,13 +227,7 @@ def main(): if not boto_found: module.fail_json(msg="boto is required") - ec2_url, ec2_access_key, ec2_secret_key, region = get_ec2_creds(module) - - ec2 = connect(ec2_url, - ec2_access_key, - ec2_secret_key, - region, - module) + ec2 = ec2_connect(module) instance_id = module.params.get('instance_id') public_ip = module.params.get('public_ip') diff --git a/cloud/ec2_elb b/cloud/ec2_elb index 3132d9e9517..6a8a25986ec 100644 --- a/cloud/ec2_elb +++ b/cloud/ec2_elb @@ -17,9 +17,9 @@ DOCUMENTATION = """ --- 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: - - 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. - Returns fact "ec2_elbs" which is a list of elbs attached to the instance if state=absent is passed as an argument. @@ -32,6 +32,7 @@ options: description: - register or deregister the instance required: true + choices: ['present', 'absent'] instance_id: description: @@ -102,15 +103,6 @@ import time import sys 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: import boto import boto.ec2 diff --git a/cloud/ec2_group b/cloud/ec2_group index c325c1ce301..bdba3f5b050 100644 --- a/cloud/ec2_group +++ b/cloud/ec2_group @@ -113,16 +113,12 @@ def main(): ec2_url=dict(aliases=['EC2_URL']), 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']), - 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']), ), 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'] description = module.params['description'] vpc_id = module.params['vpc_id'] @@ -131,21 +127,7 @@ def main(): changed = False - # 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(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)) + ec2 = ec2_connect(module) # find the group if present group = None diff --git a/cloud/ec2_tag b/cloud/ec2_tag index 53d7af18ccc..7c7d8171e3d 100644 --- a/cloud/ec2_tag +++ b/cloud/ec2_tag @@ -102,15 +102,6 @@ except ImportError: print "failed=True msg='boto required for this module'" 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(): module = AnsibleModule( 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') tags = module.params['tags'] state = module.params.get('state') - # 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)) - # 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") + ec2 = ec2_connect(module) # 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. diff --git a/cloud/ec2_vol b/cloud/ec2_vol index e5c7d1eab19..a60e0b71f81 100644 --- a/cloud/ec2_vol +++ b/cloud/ec2_vol @@ -127,15 +127,6 @@ except ImportError: print "failed=True msg='boto required for this module'" 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(): module = AnsibleModule( 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') volume_size = module.params.get('volume_size') iops = module.params.get('iops') device_name = module.params.get('device_name') zone = module.params.get('zone') - # 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)) - # 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") + ec2 = ec2_connect(module) # Here we need to get the zone info for the instance. This covers situation where # instance is specified but zone isn't. diff --git a/cloud/ec2_vpc b/cloud/ec2_vpc index 663a574f956..53b60c9dfcd 100644 --- a/cloud/ec2_vpc +++ b/cloud/ec2_vpc @@ -164,15 +164,6 @@ except ImportError: print "failed=True msg='boto required for this module'" 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): """ Retrieves vpc information from an instance diff --git a/cloud/elasticache b/cloud/elasticache index 9b40107d981..a54deafc25d 100644 --- a/cloud/elasticache +++ b/cloud/elasticache @@ -17,9 +17,9 @@ DOCUMENTATION = """ --- module: elasticache +short_description: Manage cache clusters in Amazon Elasticache. description: - Manage cache clusters in Amazon Elasticache. -short_description: Manage cache clusters in Amazon Elasticache. - Returns information about the specified cache cluster. version_added: "1.4" requirements: [ "boto" ] @@ -137,15 +137,6 @@ import sys import os 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: import boto from boto.elasticache.layer1 import ElastiCacheConnection diff --git a/cloud/quantum_floating_ip b/cloud/quantum_floating_ip index 1d755e67c27..531ddfe2b82 100644 --- a/cloud/quantum_floating_ip +++ b/cloud/quantum_floating_ip @@ -18,11 +18,14 @@ try: from novaclient.v1_1 import client as nova_client - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient import time 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 = ''' --- @@ -64,7 +67,7 @@ options: default: present network_name: description: - - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network + - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network required: true default: None instance_name: @@ -72,14 +75,19 @@ options: - The name of the instance to which the IP address should be assigned required: true 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 = ''' # Assign a floating ip to the instance from an external network - quantum_floating_ip: state=present login_username=admin login_password=admin login_tenant_name=admin network_name=external_network - instance_name=vm1 + instance_name=vm1 internal_network_name=internal_network ''' def _get_ksclient(module, kwargs): @@ -99,10 +107,10 @@ def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -111,10 +119,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _get_server_state(module, nova): server_info = None @@ -130,68 +138,81 @@ def _get_server_state(module, nova): break except Exception as e: module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_info(quantum, module, instance_id): + return server_info, server + +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 = { 'device_id': instance_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: 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']: return None, None - return ports['ports'][0]['fixed_ips'][0]['ip_address'], ports['ports'][0]['id'] - -def _get_floating_ip(module, quantum, fixed_ip_address): + return fixed_ip_address, port_id + +def _get_floating_ip(module, neutron, fixed_ip_address): kwargs = { 'fixed_ip_address': fixed_ip_address } try: - ips = quantum.list_floatingips(**kwargs) + ips = neutron.list_floatingips(**kwargs) except Exception as e: module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) if not ips['floatingips']: return None, None 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 = { 'port_id': port_id, 'floating_network_id': net_id } try: - result = quantum.create_floatingip({'floatingip': kwargs}) + result = neutron.create_floatingip({'floatingip': kwargs}) except Exception as e: 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']) -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'name': module.params['network_name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) 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']: return None 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 = { 'port_id': port_id } try: - result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) except Exception as e: module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) module.exit_json(changed=True, result=result) def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -200,39 +221,40 @@ def main(): auth_url = dict(default='http://127.0.0.1:35357/v2.0/'), region_name = dict(default=None), network_name = dict(required=True), - instance_name = dict(required=True), - state = dict(default='present', choices=['absent', 'present']) + instance_name = dict(required=True), + state = dict(default='present', choices=['absent', 'present']), + internal_network_name = dict(default=None), ), ) - + 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') - quantum = _get_quantum_client(module, module.params) + neutron = _get_neutron_client(module, module.params) except Exception as e: module.fail_json(msg="Error in authenticating to nova: %s" % e.message) - + server_info, server_obj = _get_server_state(module, nova) if not server_info: 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: 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 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: 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 floating_ip: - _update_floating_ip(quantum, module, None, floating_id) + _update_floating_ip(neutron, module, None, floating_id) module.exit_json(changed=False) # this is magic, see lib/ansible/module.params['common.py diff --git a/cloud/quantum_floating_ip_associate b/cloud/quantum_floating_ip_associate index 9b720ea232c..e878fe5086a 100644 --- a/cloud/quantum_floating_ip_associate +++ b/cloud/quantum_floating_ip_associate @@ -18,11 +18,14 @@ try: from novaclient.v1_1 import client as nova_client - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient import time 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 = ''' --- @@ -72,7 +75,7 @@ options: - floating ip that should be assigned to the instance required: true default: None -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -103,10 +106,10 @@ def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -115,10 +118,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _get_server_state(module, nova): server_info = None @@ -134,24 +137,24 @@ def _get_server_state(module, nova): break except Exception as e: module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_id(quantum, module, instance_id): + return server_info, server + +def _get_port_id(neutron, module, instance_id): kwargs = dict(device_id = instance_id) try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: return None return ports['ports'][0]['id'] - -def _get_floating_ip_id(module, quantum): + +def _get_floating_ip_id(module, neutron): kwargs = { 'floating_ip_address': module.params['ip_address'] } try: - ips = quantum.list_floatingips(**kwargs) + ips = neutron.list_floatingips(**kwargs) except Exception as e: module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) if not ips['floatingips']: @@ -163,18 +166,18 @@ def _get_floating_ip_id(module, quantum): state = "attached" 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 = { 'port_id': port_id } try: - result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) except Exception as e: 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']) def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -183,33 +186,34 @@ def main(): auth_url = dict(default='http://127.0.0.1:35357/v2.0/'), region_name = dict(default=None), ip_address = dict(required=True), - instance_name = dict(required=True), + instance_name = dict(required=True), state = dict(default='present', choices=['absent', 'present']) ), ) - + 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: module.fail_json( msg = " Error in authenticating to nova: %s" % e.message) - quantum = _get_quantum_client(module, module.params) - state, floating_ip_id = _get_floating_ip_id(module, quantum) + neutron = _get_neutron_client(module, module.params) + state, floating_ip_id = _get_floating_ip_id(module, neutron) if module.params['state'] == 'present': if state == 'attached': module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address']) server_info, server_obj = _get_server_state(module, nova) if not server_info: 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: 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 state == 'detached': module.exit_json(changed = False, result = 'detached') 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") # this is magic, see lib/ansible/module.params['common.py diff --git a/cloud/quantum_network b/cloud/quantum_network index 4eb416f7520..be6951c0b72 100644 --- a/cloud/quantum_network +++ b/cloud/quantum_network @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient 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 = ''' --- @@ -67,7 +70,7 @@ options: default: present name: description: - - Name to be assigned to the nework + - Name to be assigned to the nework required: true default: None provider_network_type: @@ -100,7 +103,7 @@ options: - Whether the state should be marked as up or down required: false default: true -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' @@ -125,21 +128,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = " Error in connecting to quantum: %s " %e.message) - return quantum + module.fail_json(msg = " Error in connecting to neutron: %s " %e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -159,7 +162,7 @@ def _set_tenant_id(module): tenant_name = module.params['login_tenant_name'] else: tenant_name = module.params['tenant_name'] - + for tenant in _os_keystone.tenants.list(): if tenant.name == tenant_name: _os_tenant_id = 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") -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'tenant_id': _os_tenant_id, 'name': module.params['name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) except Exception as e: - module.fail_json(msg = "Error in listing quantum networks: %s" % e.message) - if not networks['networks']: + module.fail_json(msg = "Error in listing neutron networks: %s" % e.message) + if not networks['networks']: return None return networks['networks'][0]['id'] -def _create_network(module, quantum): +def _create_network(module, neutron): - quantum.format = 'json' + neutron.format = 'json' network = { 'name': module.params.get('name'), @@ -212,21 +215,21 @@ def _create_network(module, quantum): network.pop('provider:segmentation_id', None) try: - net = quantum.create_network({'network':network}) + net = neutron.create_network({'network':network}) except Exception as e: module.fail_json(msg = "Error in creating network: %s" % e.message) return net['network']['id'] - -def _delete_network(module, net_id, quantum): + +def _delete_network(module, net_id, neutron): try: - id = quantum.delete_network(net_id) - except Exception as e: + id = neutron.delete_network(net_id) + except Exception as e: module.fail_json(msg = "Error in deleting the network: %s" % e.message) return True def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -237,8 +240,8 @@ def main(): name = dict(required=True), tenant_name = dict(default=None), provider_network_type = dict(default=None, choices=['local', 'vlan', 'flat', 'gre']), - provider_physical_network = dict(default=None), - provider_segmentation_id = dict(default=None), + provider_physical_network = dict(default=None), + provider_segmentation_id = dict(default=None), router_external = dict(default=False, type='bool'), shared = dict(default=False, type='bool'), admin_state_up = dict(default=True, type='bool'), @@ -254,24 +257,24 @@ def main(): if not module.params['provider_segmentation_id']: 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': - network_id = _get_net_id(quantum, module) + if module.params['state'] == 'present': + network_id = _get_net_id(neutron, module) 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) else: module.exit_json(changed = False, result = "Success", id = network_id) if module.params['state'] == 'absent': - network_id = _get_net_id(quantum, module) + network_id = _get_net_id(neutron, module) if not network_id: module.exit_json(changed = False, result = "Success") else: - _delete_network(module, network_id, quantum) + _delete_network(module, network_id, neutron) module.exit_json(changed = True, result = "Deleted") # this is magic, see lib/ansible/module.params['common.py diff --git a/cloud/quantum_router b/cloud/quantum_router index 26387de205f..9d31da2f19a 100644 --- a/cloud/quantum_router +++ b/cloud/quantum_router @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient 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 = ''' --- @@ -75,7 +78,7 @@ options: - desired admin state of the created router . required: false default: true -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -96,21 +99,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -119,10 +122,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -139,38 +142,38 @@ def _set_tenant_id(module): 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 = { 'name': module.params['name'], 'tenant_id': _os_tenant_id, } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: return None return routers['routers'][0]['id'] -def _create_router(module, quantum): +def _create_router(module, neutron): router = { 'name': module.params['name'], 'tenant_id': _os_tenant_id, 'admin_state_up': module.params['admin_state_up'], } try: - new_router = quantum.create_router(dict(router=router)) + new_router = neutron.create_router(dict(router=router)) except Exception as e: module.fail_json( msg = "Error in creating router: %s" % e.message) return new_router['router']['id'] -def _delete_router(module, quantum, router_id): +def _delete_router(module, neutron, router_id): try: - quantum.delete_router(router_id) + neutron.delete_router(router_id) except: module.fail_json("Error in deleting the router") return True - + def main(): module = AnsibleModule( argument_spec = dict( @@ -185,26 +188,26 @@ def main(): admin_state_up = dict(type='bool', default=True), ), ) - - quantum = _get_quantum_client(module, module.params) + + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) if module.params['state'] == 'present': - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) 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) else: module.exit_json(changed=False, result="success" , id=router_id) else: - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) if not router_id: module.exit_json(changed=False, result="success") else: - _delete_router(module, quantum, router_id) + _delete_router(module, neutron, router_id) module.exit_json(changed=True, result="deleted") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() diff --git a/cloud/quantum_router_gateway b/cloud/quantum_router_gateway index 60d500e6f61..68372e785d3 100644 --- a/cloud/quantum_router_gateway +++ b/cloud/quantum_router_gateway @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient 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 = ''' --- module: quantum_router_gateway @@ -69,7 +72,7 @@ options: - Name of the external network which should be attached to the router. required: true default: None -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -86,21 +89,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -109,68 +112,68 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron -def _get_router_id(module, quantum): +def _get_router_id(module, neutron): kwargs = { 'name': module.params['router_name'], } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: return None return routers['routers'][0]['id'] -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'name': module.params['network_name'], 'router:external': True } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) 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']: return None 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 = { 'device_id': router_id, 'network_id': network_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: return None 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 = { 'network_id': network_id } try: - quantum.add_gateway_router(router_id, kwargs) + neutron.add_gateway_router(router_id, kwargs) except Exception as e: module.fail_json(msg = "Error in adding gateway to router: %s" % e.message) return True - -def _remove_gateway_router(quantum, module, router_id): + +def _remove_gateway_router(neutron, module, router_id): try: - quantum.remove_gateway_router(router_id) + neutron.remove_gateway_router(router_id) except Exception as e: module.fail_json(msg = "Error in removing gateway to router: %s" % e.message) return True - + def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -183,29 +186,29 @@ def main(): state = dict(default='present', choices=['absent', 'present']), ), ) - - quantum = _get_quantum_client(module, module.params) - router_id = _get_router_id(module, quantum) + + neutron = _get_neutron_client(module, module.params) + router_id = _get_router_id(module, neutron) if not router_id: 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: 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': - 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: - _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=False, result="success") 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: 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") # this is magic, see lib/ansible/module.params['common.py diff --git a/cloud/quantum_router_interface b/cloud/quantum_router_interface index f34aecacf58..05f1f303a8f 100644 --- a/cloud/quantum_router_interface +++ b/cloud/quantum_router_interface @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient 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 = ''' --- module: quantum_router_interface @@ -81,7 +84,7 @@ EXAMPLES = ''' # Attach tenant1's subnet to the external router - quantum_router_interface: state=present login_username=admin login_password=admin - login_tenant_name=admin + login_tenant_name=admin tenant_name=tenant1 router_name=external_route subnet_name=t1subnet @@ -97,21 +100,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -120,10 +123,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _set_tenant_id(module): 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") -def _get_router_id(module, quantum): +def _get_router_id(module, neutron): kwargs = { 'name': module.params['router_name'], } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: @@ -153,27 +156,27 @@ def _get_router_id(module, quantum): return routers['routers'][0]['id'] -def _get_subnet_id(module, quantum): +def _get_subnet_id(module, neutron): subnet_id = None kwargs = { 'tenant_id': _os_tenant_id, 'name': module.params['subnet_name'], } try: - subnets = quantum.list_subnets(**kwargs) + subnets = neutron.list_subnets(**kwargs) except Exception as e: module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) if not subnets['subnets']: return None 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 = { 'tenant_id': _os_tenant_id, 'device_id': router_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: @@ -184,26 +187,26 @@ def _get_port_id(quantum, module, router_id, subnet_id): return port['id'] return None -def _add_interface_router(quantum, module, router_id, subnet_id): +def _add_interface_router(neutron, module, router_id, subnet_id): kwargs = { 'subnet_id': subnet_id } try: - quantum.add_interface_router(router_id, kwargs) + neutron.add_interface_router(router_id, kwargs) except Exception as e: module.fail_json(msg = "Error in adding interface to router: %s" % e.message) return True - -def _remove_interface_router(quantum, module, router_id, subnet_id): + +def _remove_interface_router(neutron, module, router_id, subnet_id): kwargs = { 'subnet_id': subnet_id } try: - quantum.remove_interface_router(router_id, kwargs) + neutron.remove_interface_router(router_id, kwargs) except Exception as e: module.fail_json(msg="Error in removing interface from router: %s" % e.message) return True - + def main(): module = AnsibleModule( argument_spec = dict( @@ -218,32 +221,32 @@ def main(): state = dict(default='present', choices=['absent', 'present']), ), ) - - quantum = _get_quantum_client(module, module.params) + + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) if not router_id: 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: module.fail_json(msg="failed to get the subnet id, please check the subnet name") - + 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: - _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=False, result="success", id=port_id) 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: 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") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() diff --git a/cloud/quantum_subnet b/cloud/quantum_subnet index 372d346f717..9d40131061a 100644 --- a/cloud/quantum_subnet +++ b/cloud/quantum_subnet @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantum and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystoneclient are required'") DOCUMENTATION = ''' --- @@ -77,7 +80,7 @@ options: default: None ip_version: description: - - The IP version of the subnet 4 or 6 + - The IP version of the subnet 4 or 6 required: false default: 4 enable_dhcp: @@ -105,7 +108,7 @@ options: - From the subnet pool the last IP that should be assigned to the virtual machines required: false default: None -requirements: ["quantum", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -125,21 +128,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') 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 -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = " Error in connecting to quantum: %s" % e.message) - return quantum + module.fail_json(msg = " Error in connecting to neutron: %s" % e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -167,24 +170,24 @@ def _set_tenant_id(module): if not _os_tenant_id: 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 = { 'tenant_id': _os_tenant_id, 'name': module.params['network_name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) 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']: return None return networks['networks'][0]['id'] -def _get_subnet_id(module, quantum): +def _get_subnet_id(module, neutron): global _os_network_id subnet_id = None - _os_network_id = _get_net_id(quantum, module) + _os_network_id = _get_net_id(neutron, module) if not _os_network_id: module.fail_json(msg = "network id of network not found.") else: @@ -193,15 +196,15 @@ def _get_subnet_id(module, quantum): 'name': module.params['name'], } try: - subnets = quantum.list_subnets(**kwargs) + subnets = neutron.list_subnets(**kwargs) except Exception as e: module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) if not subnets['subnets']: return None return subnets['subnets'][0]['id'] -def _create_subnet(module, quantum): - quantum.format = 'json' +def _create_subnet(module, neutron): + neutron.format = 'json' subnet = { 'name': module.params['name'], 'ip_version': module.params['ip_version'], @@ -214,7 +217,7 @@ def _create_subnet(module, quantum): } if module.params['allocation_pool_start'] and module.params['allocation_pool_end']: allocation_pools = [ - { + { 'start' : module.params['allocation_pool_start'], 'end' : module.params['allocation_pool_end'] } @@ -227,22 +230,22 @@ def _create_subnet(module, quantum): else: subnet.pop('dns_nameservers') try: - new_subnet = quantum.create_subnet(dict(subnet=subnet)) + new_subnet = neutron.create_subnet(dict(subnet=subnet)) 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'] - - -def _delete_subnet(module, quantum, subnet_id): + + +def _delete_subnet(module, neutron, subnet_id): try: - quantum.delete_subnet(subnet_id) + neutron.delete_subnet(subnet_id) except Exception as e: module.fail_json( msg = "Error in deleting subnet: %s" % e.message) return True - - + + def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -263,23 +266,23 @@ def main(): allocation_pool_end = dict(default=None), ), ) - quantum = _get_quantum_client(module, module.params) + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) if module.params['state'] == 'present': - subnet_id = _get_subnet_id(module, quantum) + subnet_id = _get_subnet_id(module, neutron) 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) else: module.exit_json(changed = False, result = "success" , id = subnet_id) else: - subnet_id = _get_subnet_id(module, quantum) + subnet_id = _get_subnet_id(module, neutron) if not subnet_id: module.exit_json(changed = False, result = "success") else: - _delete_subnet(module, quantum, subnet_id) + _delete_subnet(module, neutron, subnet_id) module.exit_json(changed = True, result = "deleted") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() diff --git a/cloud/rax b/cloud/rax index d67802ce1e0..03e99ea2a90 100644 --- a/cloud/rax +++ b/cloud/rax @@ -26,6 +26,13 @@ options: api_key: description: - 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: description: - number of instances to launch @@ -147,6 +154,26 @@ EXAMPLES = ''' networks: - private - 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 @@ -199,7 +226,7 @@ def create(module, names, flavor, image, meta, key_name, files, lpath = os.path.expanduser(files[rpath]) try: fileobj = open(lpath, 'r') - files[rpath] = fileobj + files[rpath] = fileobj.read() except Exception, e: module.fail_json(msg='Failed to load %s' % lpath) try: @@ -347,7 +374,8 @@ def delete(module, instance_ids, wait, wait_timeout): def cloudservers(module, state, name, flavor, image, meta, key_name, files, 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 cnw = pyrax.cloud_networks servers = [] @@ -358,6 +386,15 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files, elif 'group' in meta and group is None: 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 # appropriate image using human_id and name if image: @@ -416,27 +453,43 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files, module.fail_json(msg='"group" must be provided when using ' '"exact_count"') else: - numbers = set() + if auto_increment: + numbers = set() - try: - name % 0 - except TypeError, e: - if e.message.startswith('not all'): - name = '%s%%d' % name + try: + name % 0 + except TypeError, e: + if e.message.startswith('not all'): + name = '%s%%d' % name + else: + module.fail_json(msg=e.message) + + pattern = re.sub(r'%\d+[sd]', r'(\d+)', name) + for server in cs.servers.list(): + if server.metadata.get('group') == group: + servers.append(server) + match = re.search(pattern, server.name) + if match: + number = int(match.group(1)) + numbers.add(number) + + number_range = xrange(count_offset, count_offset + count) + 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: - module.fail_json(msg=e.message) - - pattern = re.sub(r'%\d+[sd]', r'(\d+)', name) - for server in cs.servers.list(): - if server.metadata.get('group') == group: - servers.append(server) - match = re.search(pattern, server.name) - if match: - number = int(match.group(1)) - numbers.add(number) - - number_range = xrange(count_offset, count_offset + count) - available_numbers = list(set(number_range).difference(numbers)) + count = diff + if len(servers) > count: state = 'absent' del servers[:count] @@ -445,45 +498,52 @@ def cloudservers(module, state, name, flavor, image, meta, key_name, files, instance_ids.append(server.id) delete(module, instance_ids, wait, wait_timeout) elif len(servers) < count: - names = [] - numbers_to_use = available_numbers[:count - len(servers)] - for number in numbers_to_use: - names.append(name % number) + if auto_increment: + names = [] + name_slice = count - len(servers) + numbers_to_use = available_numbers[:name_slice] + for number in numbers_to_use: + names.append(name % number) + else: + names = [name] * (count - len(servers)) else: module.exit_json(changed=False, action=None, instances=[], success=[], error=[], timeout=[], instance_ids={'instances': [], 'success': [], 'error': [], 'timeout': []}) - else: if group is not None: - numbers = set() + if auto_increment: + numbers = set() - try: - name % 0 - except TypeError, e: - if e.message.startswith('not all'): - name = '%s%%d' % name - else: - module.fail_json(msg=e.message) - - pattern = re.sub(r'%\d+[sd]', r'(\d+)', name) - for server in cs.servers.list(): - if server.metadata.get('group') == group: - servers.append(server) - match = re.search(pattern, server.name) - if match: - number = int(match.group(1)) - numbers.add(number) - - number_range = xrange(count_offset, - count_offset + count + len(numbers)) - available_numbers = list(set(number_range).difference(numbers)) - names = [] - numbers_to_use = available_numbers[:count] - for number in numbers_to_use: - names.append(name % number) + try: + name % 0 + except TypeError, e: + if e.message.startswith('not all'): + name = '%s%%d' % name + else: + module.fail_json(msg=e.message) + + pattern = re.sub(r'%\d+[sd]', r'(\d+)', name) + for server in cs.servers.list(): + if server.metadata.get('group') == group: + servers.append(server) + match = re.search(pattern, server.name) + if match: + number = int(match.group(1)) + numbers.add(number) + + number_range = xrange(count_offset, + count_offset + count + len(numbers)) + available_numbers = list(set(number_range) + .difference(numbers)) + names = [] + numbers_to_use = available_numbers[:count] + for number in numbers_to_use: + names.append(name % number) + else: + names = [name] * count else: search_opts = { 'name': name, @@ -552,6 +612,7 @@ def main(): argument_spec = rax_argument_spec() argument_spec.update( dict( + auto_increment=dict(choices=BOOLEANS, default=True, type='bool'), count=dict(default=1, type='int'), count_offset=dict(default=1, type='int'), disk_config=dict(default='auto', choices=['auto', 'manual']), @@ -584,6 +645,7 @@ def main(): 'please remove "service: cloudservers" from your ' 'playbook pertaining to the "rax" module') + auto_increment = module.params.get('auto_increment') count = module.params.get('count') count_offset = module.params.get('count_offset') disk_config = module.params.get('disk_config').upper() @@ -605,7 +667,8 @@ def main(): cloudservers(module, state, name, flavor, image, meta, key_name, files, 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 diff --git a/cloud/rax_clb b/cloud/rax_clb index 65435a42be4..56d7f62d78e 100644 --- a/cloud/rax_clb +++ b/cloud/rax_clb @@ -118,7 +118,6 @@ EXAMPLES = ''' ''' import sys -import os 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'] +def node_to_dict(obj): + node = obj.to_dict() + node['id'] = obj.id + return node + + def to_dict(obj): instance = {} for key in dir(obj): @@ -151,7 +156,7 @@ def to_dict(obj): elif key == 'nodes': instance[key] = [] for node in value: - instance[key].append(node.to_dict()) + instance[key].append(node_to_dict(node)) elif (isinstance(value, NON_CALLABLES) and not key.startswith('_')): instance[key] = value diff --git a/cloud/rds b/cloud/rds index c7e2844113d..47539cb332a 100644 --- a/cloud/rds +++ b/cloud/rds @@ -262,15 +262,6 @@ EXAMPLES = ''' import sys 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: import boto.rds except ImportError: @@ -346,25 +337,7 @@ def main(): apply_immediately = module.params.get('apply_immediately') new_instance_name = module.params.get('new_instance_name') - # allow environment variables to be used if ansible vars aren't set - 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'] - + ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) if not 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 from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import * main() diff --git a/commands/shell b/commands/shell index 5b3969a1fb1..03299b967cc 100644 --- a/commands/shell +++ b/commands/shell @@ -7,20 +7,26 @@ DOCUMENTATION = ''' module: shell short_description: Execute commands in nodes. description: - - The shell module takes the command name followed by a list of arguments, - space delimited. It is almost exactly like the M(command) module but runs + - The M(shell) module takes the command name followed by a list of space-delimited arguments. + It is almost exactly like the M(command) module but runs the command through a shell (C(/bin/sh)) on the remote node. version_added: "0.2" options: - (free form): + free_form: description: - - The command module takes a free form command to run - required: null + - The shell module takes a free form command to run + required: true default: null creates: description: - - a filename, when it already exists, this step will NOT be run - required: false + - a filename, when it already exists, this step will B(not) be run. + 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 chdir: description: diff --git a/database/mysql_db b/database/mysql_db index f949cced301..cf987011152 100644 --- a/database/mysql_db +++ b/database/mysql_db @@ -116,13 +116,13 @@ def db_delete(cursor, db): cursor.execute(query) 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 += " --quick --user=%s --password=%s" %(user, password) + cmd += " --quick --user=%s --password='%s'" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: - cmd += " --host=%s" % host + cmd += " --host=%s --port=%s" % (host, port) cmd += " %s" % db_name if os.path.splitext(target)[-1] == '.gz': 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) 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 += " --user=%s --password=%s" %(user, password) + cmd += " --user=%s --password='%s'" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: - cmd += " --host=%s" % host + cmd += " --host=%s --port=%s" % (host, port) cmd += " -D %s" % db_name if os.path.splitext(target)[-1] == '.gz': cmd = 'gunzip < ' + target + ' | ' + cmd @@ -282,6 +282,7 @@ def main(): elif state == "dump": rc, stdout, stderr = db_dump(module, login_host, login_user, login_password, db, target, + port=module.params['login_port'], socket=module.params['login_unix_socket']) if rc != 0: module.fail_json(msg="%s" % stderr) @@ -290,6 +291,7 @@ def main(): elif state == "import": rc, stdout, stderr = db_import(module, login_host, login_user, login_password, db, target, + port=module.params['login_port'], socket=module.params['login_unix_socket']) if rc != 0: module.fail_json(msg="%s" % stderr) diff --git a/database/mysql_user b/database/mysql_user index 1eeb81f112c..e7fad3d77c6 100644 --- a/database/mysql_user +++ b/database/mysql_user @@ -114,6 +114,9 @@ EXAMPLES = """ # Create database user with name 'bob' and password '12345' with all database privileges - 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. - mysql_user: login_user=root login_password=123456 name=sally state=absent diff --git a/files/file b/files/file index 8dcead1b402..bef175873e7 100644 --- a/files/file +++ b/files/file @@ -124,7 +124,7 @@ options: choices: [ "yes", "no" ] description: - '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).' notes: - See also M(copy), M(template), M(assemble) @@ -245,7 +245,7 @@ def main(): module.exit_json(path=path, changed=True) 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)) if prev_state == 'absent' and state == 'absent': @@ -307,6 +307,10 @@ def main(): if not force: module.fail_json(dest=path, src=src, msg='Cannot link, file exists at destination') 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: module.fail_json(dest=path, src=src, msg='unexpected position reached') diff --git a/files/template b/files/template index 35ac831e181..5f64b6d9e60 100644 --- a/files/template +++ b/files/template @@ -47,9 +47,12 @@ options: - all arguments accepted by the M(file) module also work here required: false notes: - - 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)' + - "Since Ansible version 0.9, templates are loaded with C(trim_blocks=True)." + + - "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: [] author: Michael DeHaan ''' diff --git a/net_infrastructure/bigip_monitor_http b/net_infrastructure/bigip_monitor_http index 924c826eaa3..7a05808e74c 100644 --- a/net_infrastructure/bigip_monitor_http +++ b/net_infrastructure/bigip_monitor_http @@ -94,19 +94,19 @@ options: required: true default: none ip: - description: + description: - IP address part of the ipport definition. The default API setting is "0.0.0.0". required: false default: none port: - description: + description: - port address part op the ipport definition. Tyhe default API setting is 0. required: false default: none interval: - description: + description: - The interval specifying how frequently the monitor instance of this template will run. By default, this interval is used for up and down states. The default API setting is 5. @@ -199,7 +199,7 @@ def check_monitor_exists(module, api, monitor, parent): def create_monitor(api, monitor, template_attributes): - try: + try: api.LocalLB.Monitor.create_template(templates=[{'template_name': monitor, 'template_type': TEMPLATE_TYPE}], template_attributes=[template_attributes]) except bigsuds.OperationFailed, e: if "already exists" in str(e): @@ -282,7 +282,7 @@ def set_ipport(api, monitor, ipport): # =========================================== # main loop # -# writing a module for other monitor types should +# writing a module for other monitor types should # only need an updated main() (and monitor specific functions) def main(): @@ -345,19 +345,19 @@ def main(): if port is None: port = cur_ipport['ipport']['port'] else: # use API defaults if not defined to create it - if interval is None: + if interval is None: interval = 5 - if timeout is None: + if timeout is None: timeout = 16 - if ip is None: + if ip is None: ip = '0.0.0.0' - if port is None: + if port is None: port = 0 - if send is None: + if send is None: send = '' - if receive is None: + if receive is None: receive = '' - if receive_disable is None: + if receive_disable is None: receive_disable = '' # define and set address type @@ -394,7 +394,7 @@ def main(): {'type': 'ITYPE_TIMEOUT', 'value': timeout}, {'type': 'ITYPE_TIME_UNTIL_UP', - 'value': interval}] + 'value': time_until_up}] # main logic, monitor generic @@ -405,7 +405,7 @@ def main(): if state == 'absent': if monitor_exists: if not module.check_mode: - # possible race condition if same task + # possible race condition if same task # on other node deleted it first result['changed'] |= delete_monitor(api, monitor) else: @@ -414,26 +414,24 @@ def main(): else: # state present ## check for monitor itself if not monitor_exists: # create it - if not module.check_mode: + if not module.check_mode: # again, check changed status here b/c race conditions # if other task already created it result['changed'] |= create_monitor(api, monitor, template_attributes) - else: + else: result['changed'] |= True ## check for monitor parameters # whether it already existed, or was just created, now update # the update functions need to check for check mode but # 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, - template_string_properties, - template_integer_properties) - # else assume nothing changed + result['changed'] |= update_monitor_properties(api, module, monitor, + template_string_properties, + template_integer_properties) # we just have to update the ipport if monitor already exists and it's different if monitor_exists and cur_ipport != ipport: - set_ipport(api, monitor, ipport) + set_ipport(api, monitor, ipport) result['changed'] |= True #else: monitor doesn't exist (check mode) or ipport is already ok diff --git a/net_infrastructure/openvswitch_bridge b/net_infrastructure/openvswitch_bridge index 9e8d521d39e..4b05f4079f5 100644 --- a/net_infrastructure/openvswitch_bridge +++ b/net_infrastructure/openvswitch_bridge @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/python #coding: utf-8 -*- # This module is free software: you can redistribute it and/or modify diff --git a/net_infrastructure/openvswitch_port b/net_infrastructure/openvswitch_port index a33946e9a1b..00684496b45 100644 --- a/net_infrastructure/openvswitch_port +++ b/net_infrastructure/openvswitch_port @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/python #coding: utf-8 -*- # This module is free software: you can redistribute it and/or modify diff --git a/packaging/apt b/packaging/apt index 409eb898e62..eb64f8701fb 100644 --- a/packaging/apt +++ b/packaging/apt @@ -324,7 +324,10 @@ def upgrade(m, mode="yes", force=False, upgrade_command = "safe-upgrade" if force: - force_yes = '--force-yes' + if apt_cmd == APT_GET_CMD: + force_yes = '--force-yes' + else: + force_yes = '' else: force_yes = '' diff --git a/packaging/apt_key b/packaging/apt_key index dee73762c62..eee86337020 100644 --- a/packaging/apt_key +++ b/packaging/apt_key @@ -124,7 +124,7 @@ def all_keys(module, keyring): return results 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 def download_key(module, url): diff --git a/packaging/apt_repository b/packaging/apt_repository index 9965bc22a38..26b18ec4bcc 100644 --- a/packaging/apt_repository +++ b/packaging/apt_repository @@ -3,6 +3,7 @@ # (c) 2012, Matt Wright # (c) 2013, Alexander Saltanov +# (c) 2014, Rutger Spiertz # # This file is part of Ansible # @@ -111,8 +112,9 @@ class SourcesList(object): self.files = {} # group sources by file self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist') - # read sources.list - self.load(self.default_file) + # read sources.list if it exists + if os.path.isfile(self.default_file): + self.load(self.default_file) # read sources.list.d for file in glob.iglob('%s/*.list' % apt_pkg.config.find_dir('Dir::Etc::sourceparts')): diff --git a/packaging/easy_install b/packaging/easy_install index eae361c4321..bdacf8e464b 100644 --- a/packaging/easy_install +++ b/packaging/easy_install @@ -135,7 +135,6 @@ def main(): name = module.params['name'] env = module.params['virtualenv'] executable = module.params['executable'] - easy_install = _get_easy_install(module, env, executable) site_packages = module.params['virtualenv_site_packages'] virtualenv_command = module.params['virtualenv_command'] @@ -159,6 +158,8 @@ def main(): out += out_venv err += err_venv + easy_install = _get_easy_install(module, env, executable) + cmd = None changed = False installed = _is_package_installed(module, name, easy_install) diff --git a/packaging/homebrew b/packaging/homebrew index 8801a4e6c4d..ab1362acf1d 100644 --- a/packaging/homebrew +++ b/packaging/homebrew @@ -71,7 +71,7 @@ def query_package(module, brew_path, name, state="present"): """ Returns whether a package is installed or not. """ 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: return True diff --git a/packaging/npm b/packaging/npm index 0cdcf64c63b..3a4cd13f5d7 100644 --- a/packaging/npm +++ b/packaging/npm @@ -30,7 +30,7 @@ options: name: description: - The name of a node.js library to install - requires: false + required: false path: description: - The base path where to install the node.js libraries @@ -101,7 +101,7 @@ class Npm(object): self.version = kwargs['version'] self.path = kwargs['path'] self.production = kwargs['production'] - + if kwargs['executable']: self.executable = kwargs['executable'] else: @@ -119,7 +119,7 @@ class Npm(object): if self.glbl: cmd.append('--global') if self.production: - cmd.append('--production') + cmd.append('--production') if self.name: cmd.append(self.name_version) @@ -180,7 +180,7 @@ def main(): executable=dict(default=None), state=dict(default='present', choices=['present', 'absent', 'latest']) ) - arg_spec['global']=dict(default='no', type='bool') + arg_spec['global'] = dict(default='no', type='bool') module = AnsibleModule( argument_spec=arg_spec, supports_check_mode=True diff --git a/packaging/pip b/packaging/pip index 13cf162fe15..56775177ded 100644 --- a/packaging/pip +++ b/packaging/pip @@ -147,9 +147,8 @@ EXAMPLES = ''' def _get_cmd_options(module, cmd): thiscmd = cmd + " --help" rc, stdout, stderr = module.run_command(thiscmd) - #import epdb; epdb.serve() 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() cmd_options = [ x for x in words if x.startswith('--') ] @@ -275,6 +274,7 @@ def main(): pip = _get_pip(module, env, module.params['executable']) 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 # installations that exist as binaries within this virtualenv. Example: we @@ -319,7 +319,11 @@ def main(): is_local_path = True # 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 - 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 += ' %s' % _get_full_name(name, version) elif requirements: diff --git a/packaging/rpm_key b/packaging/rpm_key index a1c4b036a62..82532477348 100644 --- a/packaging/rpm_key +++ b/packaging/rpm_key @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- """ diff --git a/packaging/yum b/packaging/yum index 4c930f2f777..744a876a04f 100644 --- a/packaging/yum +++ b/packaging/yum @@ -31,7 +31,7 @@ module: yum version_added: historical short_description: Manages packages with the I(yum) package manager 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: name: description: @@ -41,7 +41,7 @@ options: aliases: [] list: 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 default: null state: @@ -94,13 +94,26 @@ author: Seth Vidal ''' EXAMPLES = ''' -- yum: name=httpd state=latest -- yum: name=httpd state=removed -- yum: name=httpd enablerepo=testing state=installed -- yum: name=* state=latest -- yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present -- 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 Apache + yum: name=httpd state=latest + +- name: remove the Apache package + yum: name=httpd state=removed + +- 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}" diff --git a/packaging/zypper_repository b/packaging/zypper_repository index f585d0bde2e..2dc177dc7bf 100644 --- a/packaging/zypper_repository +++ b/packaging/zypper_repository @@ -120,7 +120,7 @@ def main(): description=dict(required=False), disable_gpg_check = dict(required=False, default='no', type='bool'), ), - supports_check_mode=True, + supports_check_mode=False, ) repo = module.params['repo'] diff --git a/source_control/git b/source_control/git index 61ef24e1485..41cf53133d5 100644 --- a/source_control/git +++ b/source_control/git @@ -43,6 +43,12 @@ options: - 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 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: required: false default: null @@ -118,6 +124,7 @@ EXAMPLES = ''' import re import tempfile + def get_version(git_path, dest, ref="HEAD"): ''' samples the version of the git repo ''' os.chdir(dest) @@ -352,6 +359,7 @@ def main(): force=dict(default='yes', type='bool'), depth=dict(default=None, type='int'), update=dict(default='yes', type='bool'), + accept_hostkey=dict(default='no', type='bool'), executable=dict(default=None), bare=dict(default='no', type='bool'), ), @@ -369,6 +377,10 @@ def main(): reference = module.params['reference'] 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: gitconfig = os.path.join(dest, 'config') else: @@ -430,4 +442,6 @@ def main(): # import module snippets from ansible.module_utils.basic import * +from ansible.module_utils.known_hosts import * + main() diff --git a/source_control/hg b/source_control/hg index e6730ad6f59..fcaa73457ad 100644 --- a/source_control/hg +++ b/source_control/hg @@ -59,9 +59,7 @@ options: choices: [ "yes", "no" ] purge: description: - - Deletes untracked files. Runs C(hg purge). Note this requires C(purge) extension to - 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. + - Deletes untracked files. Runs C(hg purge). required: false default: "no" choices: [ "yes", "no" ] @@ -85,36 +83,6 @@ EXAMPLES = ''' - 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): def __init__(self, module, dest, repo, revision, hg_path): @@ -129,7 +97,8 @@ class Hg(object): return (rc, out, err) 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): """ @@ -168,10 +137,6 @@ class Hg(object): return True 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 (rc1, out1, err1) = self._list_untracked() if rc1 != 0: @@ -179,10 +144,9 @@ class Hg(object): # there are some untrackd files if out1 != '': - (rc2, out2, err2) = self._command(['purge', '-R', self.dest]) - if rc2 == 0: - _undo_hgrc(hgrc, purge_option) - else: + args = ['purge', '--config', 'extensions.purge=', '-R', self.dest] + (rc2, out2, err2) = self._command(args) + if rc2 != 0: self.module.fail_json(msg=err2) return True else: diff --git a/source_control/subversion b/source_control/subversion index 38417e801b5..0bb25605533 100644 --- a/source_control/subversion +++ b/source_control/subversion @@ -94,6 +94,7 @@ class Subversion(object): def _exec(self, args): bits = [ + 'LANG=C', self.svn_path, '--non-interactive', '--trust-server-cert', diff --git a/system/authorized_key b/system/authorized_key index 7626a9a07cb..c9b178c3742 100644 --- a/system/authorized_key +++ b/system/authorized_key @@ -114,6 +114,27 @@ import tempfile import re 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): """ Calculate name of authorized keys file, optionally creating the @@ -176,7 +197,7 @@ def parseoptions(module, options): reads a string containing ssh-key options and returns a dictionary of those options ''' - options_dict = {} + options_dict = keydict() #ordered dict if options: token_exp = [ # matches separator @@ -246,9 +267,8 @@ def parsekey(module, raw_key): # check for options if type_index is None: return None - elif type_index == 1: - # parse the options and store them - options = key_parts[0] + elif type_index > 0: + options = " ".join(key_parts[:type_index]) # parse the options (if any) options = parseoptions(module, options) @@ -292,7 +312,7 @@ def writekeys(module, filename, keys): option_str = "" if options: option_strings = [] - for option_key in sorted(options.keys()): + for option_key in options.keys(): if options[option_key]: option_strings.append("%s=\"%s\"" % (option_key, options[option_key])) else: @@ -321,7 +341,9 @@ def enforce_state(module, params): state = params.get("state", "present") 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 do_write = False @@ -330,10 +352,11 @@ def enforce_state(module, params): # Check our new keys, if any of them exist we'll continue. for new_key in key: + parsed_new_key = parsekey(module, new_key) 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: module.fail_json(msg="invalid key specified: %s" % new_key) diff --git a/system/hostname b/system/hostname index 9aa7c206a4b..781bdcd08aa 100644 --- a/system/hostname +++ b/system/hostname @@ -146,6 +146,12 @@ class DebianStrategy(GenericStrategy): HOSTNAME_FILE = '/etc/hostname' 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: f = open(self.HOSTNAME_FILE) try: @@ -250,6 +256,11 @@ class AmazonLinuxHostname(Hostname): distribution = 'Amazon' strategy_class = RedHatStrategy +class ScientificLinuxHostname(Hostname): + platform = 'Linux' + distribution = 'Scientific' + strategy_class = RedHatStrategy + # =========================================== class FedoraStrategy(GenericStrategy): diff --git a/system/modprobe b/system/modprobe index 80ec66d9b18..82ca86b9bd5 100755 --- a/system/modprobe +++ b/system/modprobe @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/python #coding: utf-8 -*- # This module is free software: you can redistribute it and/or modify diff --git a/system/service b/system/service index aac7319d753..ed5712c09f9 100644 --- a/system/service +++ b/system/service @@ -431,10 +431,10 @@ class LinuxService(Service): if check_systemd(self.name): # service is managed by systemd 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 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 self.enable_cmd = location['update-rc.d'] else: @@ -649,7 +649,7 @@ class LinuxService(Service): return if self.enable: - # make sure the init.d symlinks are created + # make sure the init.d symlinks are created # otherwise enable might not work (rc, out, err) = self.execute_command("%s %s defaults" \ % (self.enable_cmd, self.name)) diff --git a/system/setup b/system/setup index 939752f1451..34d79fe9b2d 100755 --- a/system/setup +++ b/system/setup @@ -83,7 +83,7 @@ ansible all -m setup -a 'filter=ansible_*_mb' # Display only facts returned by 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]' """ @@ -118,7 +118,8 @@ class Facts(object): '/etc/alpine-release': 'Alpine', '/etc/release': 'Solaris', '/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' } # A list of dicts. If there is a platform with more than one @@ -328,6 +329,11 @@ class Facts(object): elif name == 'SuSE': data = get_file_content(path).splitlines() 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: self.facts['distribution'] = name @@ -1540,8 +1546,7 @@ class LinuxNetwork(Network): iface = words[-1] if iface != device: interfaces[iface] = {} - interfaces[iface].update(interfaces[device]) - if "ipv4_secondaries" not in interfaces[iface]: + if not secondary and "ipv4_secondaries" not in interfaces[iface]: interfaces[iface]["ipv4_secondaries"] = [] if not secondary or "ipv4" not in interfaces[iface]: interfaces[iface]['ipv4'] = {'address': address, @@ -1553,6 +1558,15 @@ class LinuxNetwork(Network): 'netmask': netmask, '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 'address' in default_ipv4 and default_ipv4['address'] == address: default_ipv4['netmask'] = netmask @@ -2072,6 +2086,11 @@ class LinuxVirtual(Virtual): self.facts['virtualization_role'] = 'guest' return + if product_name == 'RHEV Hypervisor': + self.facts['virtualization_type'] = 'RHEV' + self.facts['virtualization_role'] = 'guest' + return + if product_name == 'VMware Virtual Platform': self.facts['virtualization_type'] = 'VMware' self.facts['virtualization_role'] = 'guest' diff --git a/system/sysctl b/system/sysctl index 71320a3453c..3541a45aee9 100644 --- a/system/sysctl +++ b/system/sysctl @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # (c) 2012, David "DaviXX" CHANIAL +# (c) 2014, James Tanner # # This file is part of Ansible # @@ -41,19 +42,9 @@ options: aliases: [ 'val' ] state: description: - - Whether the entry should be present or absent. + - Whether the entry should be present or absent in the sysctl file. choices: [ "present", "absent" ] 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: description: - 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). required: false 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: [] requirements: [] author: David "DaviXX" CHANIAL @@ -78,10 +76,14 @@ EXAMPLES = ''' # Remove kernel.panic entry from /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 -# seems writable, but do not reload sysctl, and do not check kernel value -# 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 kernel.panic to 3 in /tmp/test_sysctl.conf +- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf 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 re -# ============================================================== - -def reload_sysctl(module, **sysctl_args): - # update needed ? - if not sysctl_args['reload']: - return 0, '' - - # do it - if get_platform().lower() == 'freebsd': - # 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 - -# ============================================================== - -def write_sysctl(module, lines, **sysctl_args): - # 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() +class SysctlModule(object): - # replace the real one - module.atomic_move(tmp_path, sysctl_args['sysctl_file']) + def __init__(self, module): + self.module = module + self.args = self.module.params - # end - return sysctl_args + self.sysctl_cmd = self.module.get_bin_path('sysctl', required=True) + self.sysctl_file = self.args['sysctl_file'] -# ============================================================== + 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 -def sysctl_args_expand(**sysctl_args): - if get_platform().lower() == 'freebsd': - # FreeBSD does not use the /proc file system, and instead - # just uses the sysctl command to set the values - sysctl_args['key_path'] = None - else: - sysctl_args['key_path'] = sysctl_args['name'].replace('.' ,'/') - sysctl_args['key_path'] = '/proc/sys/' + sysctl_args['key_path'] - return sysctl_args + 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 -# ============================================================== + self.process() -def sysctl_args_collapse(**sysctl_args): - # go ahead - 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 - return sysctl_args + # ============================================================== + # LOGIC + # ============================================================== -# ============================================================== + def process(self): -def sysctl_check(module, current_step, **sysctl_args): - - # no smart checks at this step ? - if sysctl_args['checks'] == 'none': - return 0, '' - if current_step == 'before' and sysctl_args['checks'] not in ['before', 'both']: - return 0, '' - if current_step == 'after' and sysctl_args['checks'] not in ['after', 'both']: - return 0, '' - - # checking coherence - if sysctl_args['state'] == 'absent' and sysctl_args['value'] is not None: - return 1, 'value=x must not be supplied when state=absent' - - if sysctl_args['state'] == 'present' and sysctl_args['value'] is None: - return 1, 'value=x must be supplied when state=present' - - if not sysctl_args['reload'] and sysctl_args['checks'] in ['after', 'both']: - return 1, 'checks cannot be set to after or both if reload=no' - - if sysctl_args['key_path'] is not None: - # getting file stat - if not os.access(sysctl_args['key_path'], os.F_OK): - 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: - # we're on a system without /proc (ie. freebsd), so just - # use the sysctl command to get the currently set value - 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') - output = re.sub(r'\s+', ' ', output) - - # normal case, found value must be equal to the submitted value, and - # we compare the exploded values to handle any whitepsace differences - if output.split() != sysctl_args['value'].split(): - return 1, 'key seems not set to value even after update/sysctl, founded : <%s>, wanted : <%s>' % (output, sysctl_args['value']) - - return 0, '' + # 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 + # ============================================================== + + # 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: - # 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, '' + 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': + # freebsd doesn't support -p, so reload the sysctl service + rc,out,err = self.module.run_command('/etc/rc.d/sysctl reload') + else: + # system supports reloading via the -p flag to sysctl, so we'll use that + rc,out,err = self.module.run_command([self.sysctl_cmd, '-p', self.sysctl_file]) + + if rc != 0: + self.module.fail_json(msg="Failed to reload sysctl: %s" % str(out) + str(err)) + + # ============================================================== + # SYSCTL FILE MANAGEMENT + # ============================================================== + + # Get the token value from the sysctl file + 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 + + k, v = line.split('=',1) + k = k.strip() + v = v.strip() + self.file_values[k] = v.strip() + + # Fix the value in the sysctl file content + def fix_lines(self): + checked = [] + self.fixed_lines = [] + for line in self.file_lines: + if not line.strip() or line.strip().startswith("#"): + self.fixed_lines.append(line) + continue + tmpline = line.strip() + k, v = line.split('=',1) + k = k.strip() + v = v.strip() + if k not in checked: + checked.append(k) + if k == self.args['name']: + if self.args['state'] == "present": + new_line = "%s = %s\n" % (k, self.args['value']) + self.fixed_lines.append(new_line) + else: + new_line = "%s = %s\n" % (k, v) + self.fixed_lines.append(new_line) + + if self.args['name'] not in checked and self.args['state'] == "present": + new_line = "%s = %s\n" % (self.args['name'], self.args['value']) + self.fixed_lines.append(new_line) + + # Completely rewrite the sysctl file + def write_sysctl(self): + # open a tmp file + 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() + + # replace the real one + self.module.atomic_move(tmp_path, self.sysctl_file) - # weird end - return 1, 'unexpected position reached' # ============================================================== # main @@ -233,110 +272,16 @@ def main(): name = dict(aliases=['key'], required=True), value = dict(aliases=['val'], required=False), state = dict(default='present', choices=['present', 'absent']), - checks = dict(default='both', choices=['none', 'before', 'after', 'both']), reload = dict(default=True, type='bool'), + sysctl_set = dict(default=False, type='bool'), sysctl_file = dict(default='/etc/sysctl.conf') - ) + ), + supports_check_mode=True ) - # defaults - 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 + result = SysctlModule(module) - # if not changed, but should be present, so we have to add it - 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) + module.exit_json(changed=result.changed) sys.exit(0) # import module snippets diff --git a/system/user b/system/user index 48bcf751716..0e29e443bf8 100644 --- a/system/user +++ b/system/user @@ -77,8 +77,8 @@ options: description: - Optionally set the user's password to this crypted value. See the user example in the github examples directory for what this looks - like in a playbook. - - Passwords values can be generated with "openssl passwd -salt -1 " + like in a playbook. The `FAQ <http://docs.ansible.com/faq.html#how-do-i-generate-crypted-passwords-for-the-user-module>`_ + contains details on various ways to generate these password values. state: required: false default: "present" @@ -901,8 +901,21 @@ class OpenBSDUser(User): cmd.append(self.shell) if self.login_class is not None: - cmd.append('-L') - cmd.append(self.login_class) + # 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(self.login_class) if self.update_password == 'always' and self.password is not None and info[1] != self.password: cmd.append('-p') diff --git a/utilities/fail b/utilities/fail index 7023f357cac..23f5b83668c 100644 --- a/utilities/fail +++ b/utilities/fail @@ -40,5 +40,5 @@ author: Dag Wieers EXAMPLES = ''' # Example playbook using fail and when together - 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" ''' diff --git a/web_infrastructure/django_manage b/web_infrastructure/django_manage index f6ea9c49149..68eb92c1bfe 100644 --- a/web_infrastructure/django_manage +++ b/web_infrastructure/django_manage @@ -129,12 +129,11 @@ def _ensure_virtualenv(module): if venv_param is None: return - virtualenv = module.get_bin_path('virtualenv', True) - vbin = os.path.join(os.path.expanduser(venv_param), 'bin') activate = os.path.join(vbin, 'activate') if not os.path.exists(activate): + virtualenv = module.get_bin_path('virtualenv', True) vcmd = '%s %s' % (virtualenv, venv_param) vcmd = [virtualenv, venv_param] rc, out_venv, err_venv = module.run_command(vcmd)