PageRenderTime 26ms CodeModel.GetById 12ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/inventory/gce.py

https://github.com/ajanthanm/ansible
Python | 257 lines | 192 code | 13 blank | 52 comment | 13 complexity | e2e1a5f0af73ab2afc6b7cfafce2061d MD5 | raw file
  1#!/usr/bin/python
  2# Copyright 2013 Google Inc.
  3#
  4# This file is part of Ansible
  5#
  6# Ansible is free software: you can redistribute it and/or modify
  7# it under the terms of the GNU General Public License as published by
  8# the Free Software Foundation, either version 3 of the License, or
  9# (at your option) any later version.
 10#
 11# Ansible is distributed in the hope that it will be useful,
 12# but WITHOUT ANY WARRANTY; without even the implied warranty of
 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14# GNU General Public License for more details.
 15#
 16# You should have received a copy of the GNU General Public License
 17# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 18
 19'''
 20GCE external inventory script
 21=================================
 22
 23Generates inventory that Ansible can understand by making API requests
 24Google Compute Engine via the libcloud library.  Full install/configuration
 25instructions for the gce* modules can be found in the comments of
 26ansible/test/gce_tests.py.
 27
 28When run against a specific host, this script returns the following variables
 29based on the data obtained from the libcloud Node object:
 30 - gce_uuid
 31 - gce_id
 32 - gce_image
 33 - gce_machine_type
 34 - gce_private_ip
 35 - gce_public_ip
 36 - gce_name
 37 - gce_description
 38 - gce_status
 39 - gce_zone
 40 - gce_tags
 41 - gce_metadata
 42 - gce_network
 43
 44When run in --list mode, instances are grouped by the following categories:
 45 - zone:
 46   zone group name examples are us-central1-b, europe-west1-a, etc.
 47 - instance tags:
 48   An entry is created for each tag.  For example, if you have two instances
 49   with a common tag called 'foo', they will both be grouped together under
 50   the 'tag_foo' name.
 51 - network name:
 52   the name of the network is appended to 'network_' (e.g. the 'default'
 53   network will result in a group named 'network_default')
 54 - machine type
 55   types follow a pattern like n1-standard-4, g1-small, etc.
 56 - running status:
 57   group name prefixed with 'status_' (e.g. status_running, status_stopped,..)
 58 - image:
 59   when using an ephemeral/scratch disk, this will be set to the image name
 60   used when creating the instance (e.g. debian-7-wheezy-v20130816).  when
 61   your instance was created with a root persistent disk it will be set to
 62   'persistent_disk' since there is no current way to determine the image.
 63
 64Examples:
 65  Execute uname on all instances in the us-central1-a zone
 66  $ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"
 67
 68  Use the GCE inventory script to print out instance specific information
 69  $ plugins/inventory/gce.py --host my_instance
 70
 71Author: Eric Johnson <erjohnso@google.com>
 72Version: 0.0.1
 73'''
 74
 75USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
 76USER_AGENT_VERSION="v1"
 77
 78import sys
 79import os
 80import argparse
 81import ConfigParser
 82
 83try:
 84    import json
 85except ImportError:
 86    import simplejson as json
 87
 88try:
 89    from libcloud.compute.types import Provider
 90    from libcloud.compute.providers import get_driver
 91    _ = Provider.GCE
 92except:
 93    print("GCE inventory script requires libcloud >= 0.13")
 94    sys.exit(1)
 95
 96
 97class GceInventory(object):
 98    def __init__(self):
 99        # Read settings and parse CLI arguments
100        self.parse_cli_args()
101        self.driver = self.get_gce_driver()
102
103        # Just display data for specific host
104        if self.args.host:
105            print self.json_format_dict(self.node_to_dict(
106                    self.get_instance(self.args.host)))
107            sys.exit(0)
108
109        # Otherwise, assume user wants all instances grouped
110        print self.json_format_dict(self.group_instances())
111        sys.exit(0)
112
113
114    def get_gce_driver(self):
115        '''Determine GCE authorization settings and return libcloud driver.'''
116
117        gce_ini_default_path = os.path.join(
118            os.path.dirname(os.path.realpath(__file__)), "gce.ini")
119        gce_ini_path = os.environ.get('GCE_INI_PATH', gce_ini_default_path)
120
121        config = ConfigParser.SafeConfigParser()
122        config.read(gce_ini_path)
123
124        # the GCE params in 'secrets.py' will override these
125        secrets_path = config.get('gce', 'libcloud_secrets')
126
127        secrets_found = False
128        try:
129            import secrets
130            args = getattr(secrets, 'GCE_PARAMS', ())
131            kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
132            secrets_found = True
133        except:
134            pass
135
136        if not secrets_found and secrets_path:
137            if not secrets_path.endswith('secrets.py'):
138                err = "Must specify libcloud secrets file as "
139                err += "/absolute/path/to/secrets.py"
140                print(err)
141                sys.exit(1)
142            sys.path.append(os.path.dirname(secrets_path))
143            try:
144                import secrets
145                args = getattr(secrets, 'GCE_PARAMS', ())
146                kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
147                secrets_found = True
148            except:
149                pass
150        if not secrets_found:
151            args = (
152                config.get('gce','gce_service_account_email_address'),
153                config.get('gce','gce_service_account_pem_file_path')
154            )
155            kwargs = {'project': config.get('gce','gce_project_id')}
156
157        gce = get_driver(Provider.GCE)(*args, **kwargs)
158        gce.connection.user_agent_append("%s/%s" % (
159                USER_AGENT_PRODUCT, USER_AGENT_VERSION))
160        return gce
161
162
163    def parse_cli_args(self):
164        ''' Command line argument processing '''
165
166        parser = argparse.ArgumentParser(
167                description='Produce an Ansible Inventory file based on GCE')
168        parser.add_argument('--list', action='store_true', default=True,
169                           help='List instances (default: True)')
170        parser.add_argument('--host', action='store',
171                           help='Get all information about an instance')
172        self.args = parser.parse_args()
173
174
175    def node_to_dict(self, inst):
176        md = {}
177
178        if inst is None:
179            return {}
180
181        if inst.extra['metadata'].has_key('items'):
182            for entry in inst.extra['metadata']['items']:
183                md[entry['key']] = entry['value']
184
185        net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
186        return {
187            'gce_uuid': inst.uuid,
188            'gce_id': inst.id,
189            'gce_image': inst.image,
190            'gce_machine_type': inst.size,
191            'gce_private_ip': inst.private_ips[0],
192            'gce_public_ip': inst.public_ips[0],
193            'gce_name': inst.name,
194            'gce_description': inst.extra['description'],
195            'gce_status': inst.extra['status'],
196            'gce_zone': inst.extra['zone'].name,
197            'gce_tags': inst.extra['tags'],
198            'gce_metadata': md,
199            'gce_network': net,
200            # Hosts don't have a public name, so we add an IP
201            'ansible_ssh_host': inst.public_ips[0]
202        }
203
204    def get_instance(self, instance_name):
205        '''Gets details about a specific instance '''
206        try:
207            return self.driver.ex_get_node(instance_name)
208        except Exception, e:
209            return None
210
211    def group_instances(self):
212        '''Group all instances'''
213        groups = {}
214        for node in self.driver.list_nodes():
215            name = node.name
216
217            zone = node.extra['zone'].name
218            if groups.has_key(zone): groups[zone].append(name)
219            else: groups[zone] = [name]
220
221            tags = node.extra['tags']
222            for t in tags:
223                tag = 'tag_%s' % t
224                if groups.has_key(tag): groups[tag].append(name)
225                else: groups[tag] = [name]
226
227            net = node.extra['networkInterfaces'][0]['network'].split('/')[-1]
228            net = 'network_%s' % net
229            if groups.has_key(net): groups[net].append(name)
230            else: groups[net] = [name]
231
232            machine_type = node.size
233            if groups.has_key(machine_type): groups[machine_type].append(name)
234            else: groups[machine_type] = [name]
235
236            image = node.image and node.image or 'persistent_disk'
237            if groups.has_key(image): groups[image].append(name)
238            else: groups[image] = [name]
239
240            status = node.extra['status']
241            stat = 'status_%s' % status.lower()
242            if groups.has_key(stat): groups[stat].append(name)
243            else: groups[stat] = [name]
244        return groups
245
246    def json_format_dict(self, data, pretty=False):
247        ''' Converts a dict to a JSON object and dumps it as a formatted
248        string '''
249
250        if pretty:
251            return json.dumps(data, sort_keys=True, indent=2)
252        else:
253            return json.dumps(data)
254
255
256# Run the script
257GceInventory()