├── inventory ├── localhost └── gce.py ├── files ├── docker2 ├── docker └── docker.service ├── hosts ├── template ├── ip-detect └── config.yaml ├── install.yml ├── group_vars └── all ├── tasks ├── setup_bootstrap_node.yml ├── create_master_instances.yml └── configure_master_dcos_nodes.yml ├── configure_dcos.yml ├── add_agents.yml ├── README.md └── LICENSE /inventory/localhost: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /files/docker2: -------------------------------------------------------------------------------- 1 | other_args=--storage-driver=overlay 2 | -------------------------------------------------------------------------------- /files/docker: -------------------------------------------------------------------------------- 1 | DOCKER_OPTS="other_args=--storage-driver=overlay" 2 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [masters] 2 | master0 ip=10.132.0.3 3 | 4 | [agents] 5 | agent[0000:9999] 6 | 7 | [bootstrap] 8 | bootstrap 9 | -------------------------------------------------------------------------------- /template/ip-detect: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Example ip-detect script using an external authority 3 | # Uses the GCE metadata server to get the node's internal 4 | # ipv4 address 5 | 6 | curl -fsSL -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/ip 7 | -------------------------------------------------------------------------------- /template/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | bootstrap_url: http://{{ bootstrap_public_ip }}:{{ bootstrap_public_port }} 3 | cluster_name: {{ cluster_name }} 4 | exhibitor_storage_backend: static 5 | ip_detect_filename: /genconf/ip-detect 6 | master_discovery: static 7 | master_list: 8 | {% for master in groups['masters'] %} 9 | - {{ hostvars[master]['ip'] }} 10 | {% endfor %} 11 | resolvers: 12 | - 8.8.4.4 13 | - 8.8.8.8 14 | -------------------------------------------------------------------------------- /install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure dcos installer on bootstrap node 3 | hosts: localhost 4 | become: yes 5 | become_user: root 6 | become_method: sudo 7 | gather_facts: no 8 | connection: local 9 | tasks: 10 | - include: tasks/setup_bootstrap_node.yml 11 | 12 | - name: Create and launch master instances 13 | hosts: localhost 14 | become: yes 15 | become_user: root 16 | become_method: sudo 17 | gather_facts: no 18 | connection: local 19 | tasks: 20 | - include: tasks/create_master_instances.yml 21 | - pause: seconds=20 22 | 23 | - name: initialise master dcos nodes 24 | hosts: masters 25 | become: yes 26 | become_user: root 27 | become_method: sudo 28 | serial: 2 29 | tasks: 30 | - include: tasks/configure_master_dcos_nodes.yml 31 | 32 | -------------------------------------------------------------------------------- /files/docker.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Docker Application Container Engine 3 | Documentation=https://docs.docker.com 4 | After=network.target 5 | 6 | [Service] 7 | Type=notify 8 | # the default is not to use systemd for cgroups because the delegate issues still 9 | # exists and systemd currently does not support the cgroup feature set required 10 | # for containers run by docker 11 | ExecStart=/usr/bin/docker daemon --storage-driver=overlay 12 | ExecReload=/bin/kill -s HUP $MAINPID 13 | # Having non-zero Limit*s causes performance problems due to accounting overhead 14 | # in the kernel. We recommend using cgroups to do container-local accounting. 15 | LimitNOFILE=infinity 16 | LimitNPROC=infinity 17 | LimitCORE=infinity 18 | # Uncomment TasksMax if your systemd version supports it. 19 | # Only systemd 226 and above support this version. 20 | #TasksMax=infinity 21 | TimeoutStartSec=0 22 | # set delegate yes so that systemd does not reset the cgroups of docker containers 23 | Delegate=yes 24 | # kill only the docker process, not all processes in the cgroup 25 | KillMode=process 26 | 27 | [Install] 28 | WantedBy=multi-user.target 29 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | project: trek-trackr 3 | 4 | subnet: default-6f68d4d6fabcb680 5 | 6 | login_name: ajazam 7 | 8 | bootstrap_public_ip: 10.132.0.2 9 | 10 | zone: europe-west1-c 11 | 12 | 13 | 14 | 15 | master_boot_disk_size: 10 16 | 17 | master_machine_type: n1-standard-2 18 | 19 | master_boot_disk_type: pd-standard 20 | 21 | 22 | agent_boot_disk_size: 10 23 | 24 | agent_machine_type: n1-standard-2 25 | 26 | agent_boot_disk_type: pd-standard 27 | 28 | agent_instance_type: "MIGRATE" 29 | 30 | agent_type: private 31 | 32 | start_id: 0001 33 | 34 | end_id: 0001 35 | 36 | 37 | 38 | 39 | gcloudbin: /usr/bin/gcloud 40 | 41 | image: 'centos-7-v20161027' 42 | 43 | image_project: 'centos-cloud' 44 | 45 | bootstrap_public_port: 8080 46 | 47 | cluster_name: cluster_name 48 | 49 | scopes: "https://www.googleapis.com/auth/cloud-platform" 50 | 51 | dcos_installer_filename: dcos_generate_config.sh 52 | 53 | dcos_installer_download_path: "https://downloads.dcos.io/dcos/stable/{{ dcos_installer_filename }}" 54 | 55 | home_directory: "/home/{{ login_name }}" 56 | 57 | downloads_from_bootstrap: 2 58 | 59 | dcos_bootstrap_container: dcosinstaller 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /tasks/setup_bootstrap_node.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create directory genconf 3 | file: dest="{{ home_directory }}/genconf" state=directory 4 | 5 | - name: copy config 6 | template: src=template/config.yaml dest="{{ home_directory }}/genconf" 7 | 8 | - name: copy ipdetect 9 | template: src=template/ip-detect dest="{{ home_directory }}/genconf" mode="u=rwx,g=rwx,o=rwx" 10 | 11 | - name: check presence of dcos installer 12 | stat: path="{{ home_directory }}/{{ dcos_installer_filename }}" 13 | register: dcos_installer_state 14 | 15 | - name: get dcos installer 16 | get_url: url={{ dcos_installer_download_path }} dest={{ home_directory }} mode="u=rwx,g=rwx,o=rwx" 17 | when: dcos_installer_state.stat.islnk is not defined 18 | 19 | - name: stop docker 20 | service: name=docker state=stopped 21 | 22 | - name: ensure docker is running 23 | service: name=docker state=started 24 | 25 | - name: Generate customised build file 26 | command: "{{ home_directory }}/{{ dcos_installer_filename }}" 27 | args: 28 | chdir: "{{ home_directory }}" 29 | # need a when statement here 30 | 31 | - name: start docker container 32 | docker_container: 33 | name: "{{ dcos_bootstrap_container }}" 34 | image: nginx 35 | state: started 36 | volumes: "{{ home_directory }}/genconf/serve:/usr/share/nginx/html:ro" 37 | ports: "{{ bootstrap_public_port }}:80" 38 | -------------------------------------------------------------------------------- /tasks/create_master_instances.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: remove masters from known_hosts file 4 | command: /usr/bin/ssh-keygen -R {{ hostvars[item].inventory_hostname }} 5 | with_items: 6 | - "{{ groups['masters'] }}" 7 | ignore_errors: true 8 | 9 | - name : remove masters IP from known_hosts file 10 | command: /usr/bin/ssh-keygen -R {{ hostvars[item]['ip'] }} 11 | with_items: 12 | - "{{ groups['masters'] }}" 13 | ignore_errors: true 14 | 15 | - name: Create and launch masters 16 | command: "{{ gcloudbin }} compute --project {{ project }} instances create {{ hostvars[item].inventory_hostname }} 17 | --zone {{ zone }} --machine-type {{ master_machine_type }} --subnet {{ subnet }} 18 | --private-network-ip {{ hostvars[item]['ip'] }} --maintenance-policy \"MIGRATE\" --tags \"master\" 19 | --scopes https://www.googleapis.com/auth/cloud-platform --image {{ image }} --image-project {{ image_project }} 20 | --boot-disk-size {{ master_boot_disk_size }} --boot-disk-type {{ master_boot_disk_type }} 21 | --boot-disk-device-name {{ hostvars[item].inventory_hostname }}-boot --metadata hostname={{ hostvars[item].inventory_hostname }}" 22 | with_items: 23 | - "{{ groups['masters'] }}" 24 | register: master_instances 25 | async: 7200 26 | poll: 0 27 | 28 | - name: wait for master instance creation to complete 29 | async_status: jid={{ item.ansible_job_id }} 30 | register: master_instance_jobs 31 | until : master_instance_jobs.finished 32 | retries: 300 33 | with_items: "{{ master_instances.results }}" 34 | -------------------------------------------------------------------------------- /tasks/configure_master_dcos_nodes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create tmp directory 3 | file: dest=/tmp/dcos state=directory 4 | 5 | - name: install dependencies 6 | yum: name={{ item }} state=present 7 | with_items: 8 | - unzip 9 | - ipset 10 | - ntp 11 | 12 | - name: install ntpd 13 | service: name=ntpd state=started enabled=yes 14 | 15 | - name: add group nogroup 16 | group: name=nogroup state=present 17 | 18 | - name: disable selinux 19 | selinux: state=disabled 20 | 21 | - name: restart host 22 | shell: sleep 1;/usr/sbin/reboot 23 | async: 1 24 | poll: 0 25 | ignore_errors: true 26 | 27 | - name: waiting for host to come back online 28 | local_action: wait_for host={{ inventory_hostname }} search_regex=OpenSSH port=22 timeout=300 state=started 29 | 30 | - name: Add docker repository 31 | yum_repository: 32 | name: dockerrepo 33 | description: Docker Respository 34 | baseurl: https://yum.dockerproject.org/repo/main/centos/$releasever/ 35 | state: present 36 | enabled: yes 37 | gpgcheck: yes 38 | gpgkey: https://yum.dockerproject.org/gpg 39 | 40 | - pause: seconds=1 41 | 42 | - name: install docker 43 | yum: name=docker-engine-1.11.2 state=present 44 | 45 | - name: copy docker config for overlayfs 46 | copy: src=files/docker.service dest=/usr/lib/systemd/system 47 | 48 | - name: reload sytemd 49 | command: /usr/bin/systemctl daemon-reload 50 | 51 | - name: start docker 52 | service: name=docker state=started 53 | 54 | # following needs to have serial: 2 55 | - name: fetch installer from bootstrap 56 | get_url: url=http://{{ bootstrap_public_ip }}:{{ bootstrap_public_port }}/dcos_install.sh dest=/tmp/dcos mode="u=rwx,g=rwx,o=rwx" 57 | 58 | # configure master nodes 59 | 60 | - name: install dcos on master 61 | command: bash dcos_install.sh master 62 | args: 63 | chdir: /tmp/dcos 64 | -------------------------------------------------------------------------------- /configure_dcos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: configure hosts 3 | hosts: tag_master,tag_privateagent,tag_publicagent 4 | become: yes 5 | gather_facts: no 6 | tasks: 7 | - name: create tmp directory 8 | file: dest=/tmp/dcos state=directory 9 | 10 | - name: install dependencies 11 | yum: name={{ item }} state=present 12 | with_items: 13 | - unzip 14 | - ipset 15 | 16 | - name: add group nogroup 17 | group: name=nogroup state=present 18 | 19 | - name: disable selinux 20 | selinux: state=disabled 21 | 22 | - name: restart host 23 | shell: sleep 1;/usr/sbin/reboot 24 | async: 1 25 | poll: 0 26 | ignore_errors: true 27 | 28 | - name: waiting for host to come back online 29 | local_action: wait_for host={{ inventory_hostname }} search_regex=OpenSSH port=22 timeout=300 state=started 30 | 31 | # - name: update yum packages 32 | # yum: update_cache=yes state=present 33 | 34 | - name: Add docker repository 35 | yum_repository: 36 | name: dockerrepo 37 | description: Docker Respository 38 | baseurl: https://yum.dockerproject.org/repo/main/centos/$releasever/ 39 | state: present 40 | enabled: yes 41 | gpgcheck: yes 42 | gpgkey: https://yum.dockerproject.org/gpg 43 | 44 | - pause: seconds=1 45 | 46 | - name: install docker 47 | yum: name=docker-engine state=present 48 | 49 | - name: copy docker config for overlayfs 50 | copy: src=files/docker.service dest=/usr/lib/systemd/system 51 | 52 | - name: reload sytemd 53 | command: /usr/bin/systemctl daemon-reload 54 | 55 | - name: start docker 56 | service: name=docker state=started 57 | 58 | 59 | 60 | - name: fetch dcos installer 61 | hosts: tag_master,tag_privateagent,tag_publicagent 62 | become: yes 63 | gather_facts: no 64 | serial: 2 65 | tasks: 66 | - name: fetch installer from bootstrap 67 | get_url: url=http://{{ bootstrap_public_ip }}:{{ bootstrap_public_port }}/dcos_install.sh dest=/tmp/dcos mode="u=rwx,g=rwx,o=rwx" 68 | 69 | - name: configure master nodes with dcos 70 | hosts: tag_master 71 | become: yes 72 | gather_facts: no 73 | tasks: 74 | - name: install dcos on master 75 | command: bash dcos_install.sh master 76 | args: 77 | chdir: /tmp/dcos 78 | 79 | - name: configure private agent nodes with dcos 80 | hosts: tag_privateagent 81 | become: yes 82 | gather_facts: no 83 | tasks: 84 | - name: install dcos on private agents 85 | command: bash dcos_install.sh slave 86 | args: 87 | chdir: /tmp/dcos 88 | 89 | - name: configure public agent nodes with dcos 90 | hosts: tag_publicagent 91 | become: yes 92 | gather_facts: no 93 | tasks: 94 | - name: install dcos on public nodes 95 | command: bash dcos_install.sh slave_public 96 | args: 97 | chdir: /tmp/dcos 98 | -------------------------------------------------------------------------------- /add_agents.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create agent instances 3 | hosts: localhost 4 | become: yes 5 | become_user: root 6 | gather_facts: no 7 | connection: local 8 | tasks: 9 | - name: remove agents from known_hosts file 10 | command: /usr/bin/ssh-keygen -R {{ item }} 11 | with_sequence: start={{ start_id }} end={{ end_id }} format=agent%04x 12 | ignore_errors: true 13 | 14 | - name: launch agent instances 15 | command: "{{ gcloudbin }} compute --project {{ project }} instances create {{ item }} --zone {{ zone }} --machine-type {{ agent_machine_type }} 16 | --subnet {{ subnet }} --maintenance-policy {{ agent_instance_type }} --tags \"{{agent_type}}agent\" --scopes {{ scopes }} 17 | --image {{ image }} --image-project {{ image_project }} --boot-disk-size {{ agent_boot_disk_size }} 18 | --boot-disk-type {{ agent_boot_disk_type }} --boot-disk-device-name boot --metadata hostname={{ item }}" 19 | with_sequence: start={{ start_id }} end={{ end_id }} format=agent%04x 20 | register: vm_instances 21 | async: 7200 22 | poll: 0 23 | 24 | - name: wait for instance creation to complete 25 | async_status: jid={{ item.ansible_job_id }} 26 | register: instances_jobs 27 | until: instances_jobs.finished 28 | retries: 300 29 | with_items: "{{ vm_instances.results }}" 30 | 31 | - name: start dcos bootstrap container 32 | hosts: localhost 33 | gather_facts: no 34 | become: yes 35 | become_user: root 36 | connection: local 37 | tasks: 38 | - name: start docker 39 | service: name=docker state=started 40 | 41 | - name: ensure dcos installer container is started 42 | docker_container: 43 | name: "{{ dcos_bootstrap_container }}" 44 | state: started 45 | 46 | - pause: seconds=10 47 | 48 | - name: Install dcos on nodes 49 | hosts: agents[{{start_id}}:{{ end_id }}] 50 | become: yes 51 | become_user: root 52 | gather_facts: no 53 | tasks: 54 | - name: create tmp directory 55 | file: dest=/tmp/dcos state=directory 56 | 57 | - name: install dependencies 58 | yum: name="{{ item }}" state=present 59 | with_items: 60 | - unzip 61 | - ipset 62 | - ntp 63 | 64 | - name: install ntpd 65 | service: name=ntpd state=started enabled=yes 66 | 67 | - name: add group nogroup 68 | group: name=nogroup state=present 69 | 70 | - name: disable selinux 71 | selinux: state=disabled 72 | 73 | - name: restart host 74 | shell: sleep 1;/usr/sbin/reboot 75 | async: 1 76 | poll: 0 77 | ignore_errors: true 78 | 79 | - name: waiting for host to come back online 80 | local_action: wait_for host={{ inventory_hostname }} search_regex=OpenSSH port=22 timeout=300 state=started 81 | 82 | - name: Add docker repository 83 | yum_repository: 84 | name: dockerrepo 85 | description: Docker Respository 86 | baseurl: https://yum.dockerproject.org/repo/main/centos/$releasever/ 87 | state: present 88 | enabled: yes 89 | gpgcheck: yes 90 | gpgkey: https://yum.dockerproject.org/gpg 91 | 92 | - pause: seconds=1 93 | 94 | - name: install docker 95 | yum: name=docker-engine-1.11.2 state=present 96 | 97 | - name: copy docker config for overlayfs 98 | copy: src=files/docker.service dest=/usr/lib/systemd/system 99 | 100 | - name: reload sytemd 101 | command: /usr/bin/systemctl daemon-reload 102 | 103 | - name: start docker 104 | service: name=docker state=started enabled=yes 105 | 106 | # following needs to have serial: 2 107 | - name: fetch installer from bootstrap 108 | get_url: url=http://{{ bootstrap_public_ip }}:{{ bootstrap_public_port }}/dcos_install.sh dest=/tmp/dcos mode="u=rwx,g=rwx,o=rwx" 109 | 110 | # configure master nodes 111 | - name: install dcos on private agent 112 | command: bash dcos_install.sh slave 113 | args: 114 | chdir: /tmp/dcos 115 | when: agent_type == 'private' 116 | 117 | - name: install dcos on public agent 118 | command: bash dcos_install.sh slave_public 119 | args: 120 | chdir: /tmp/dcos 121 | when: agent_type == 'public' 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DCOS on Google Compute Engine 2 | 3 | This repository contains scripts to configure a DC/OS cluster on Google Compute Engine. 4 | 5 | A bootstrap node is required to run the scripts and to bootstrap the DC/OS cluster. 6 | 7 | **PLEASE READ THE ENTIRE DOCUMENT. YOU MUST MAKE CHANGES FOR THE SCRIPTS TO WORK IN YOUR GCE ENVIRONMENT.** 8 | 9 | ## Bootstrap node configuration 10 | 11 | **YOU MUST CREATE A PROJECT** using the google cloud console. The author created a project called trek-treckr 12 | 13 | You can create the bootstrap node using the google cloud console. The author used a n1-standard-1 instance running centos 7 with a 10 GB persistent disk in 14 | zone europe-west1-c. The bootstrap node must have "Allow full access to all Cloud APIs" in the Identity and API access section. Also enable Block project-wide SSH keys in the SSH Keys section. Create the instance. 15 | 16 | After creating the boot instance run the following from the shell 17 | ```bash 18 | sudo yum update google-cloud-sdk 19 | sudo yum update 20 | sudo yum install epel-release 21 | sudo yum install python-pip 22 | sudo pip install -U pip 23 | sudo pip install 'apache-libcloud==1.2.1' 24 | sudo pip install 'docker-py==1.9.0' 25 | sudo yum install git-1.8.3.1 ansible-2.1.1.0 26 | ``` 27 | 28 | You need to create the rsa public/private keypairs to allow passwordless logins via SSH to the nodes of the DC/OS cluster. This is required by ansible to create the cluster nodes and install DC/OS on the nodes. 29 | 30 | Run the following to generate the keys 31 | ```bash 32 | ssh-keygen -t rsa -f ~/.ssh/id_rsa -C ajazam 33 | ``` 34 | **PLEASE REPLACE ajazam** with your username. Do not enter a password when prompted 35 | 36 | Make a backup copy of id_rsa. 37 | 38 | Open rsa pub key 39 | ```bash 40 | sudo vi ~/.ssh/id_rsa.pub 41 | ``` 42 | 43 | shows 44 | 45 | ```bash 46 | ssh-rsa abcdefghijklmaasnsknsdjfsdfjs;dfj;sdflkjsd ajazam 47 | ``` 48 | Prefix your username, followed by a colon, to the above line. Also replace ajazam at the end with your username. 49 | 50 | ```bash 51 | ajazam:ssh-rsa abcdefghijklmaasnsknsdjfsdfjs;dfj;sdflkjsd ajazam 52 | ``` 53 | save contents of id_rsa.pub. **Please replace the ajazam with your username**. 54 | 55 | 56 | 57 | Add the rsa public key to your project 58 | ```bash 59 | chmod 400 ~/.ssh/id_rsa 60 | gcloud compute project-info add-metadata --metadata-from-file sshKeys=~/.ssh/id_rsa.pub 61 | ``` 62 | 63 | Disable selinux for docker to work 64 | 65 | make the following change to /etc/selinux/config 66 | 67 | ```bash 68 | SELINUX=disabled 69 | ``` 70 | 71 | reboot host 72 | 73 | To install docker add the yum repo 74 | 75 | ```bash 76 | sudo tee /etc/yum.repos.d/docker.repo <<-'EOF' 77 | [dockerrepo] 78 | name=Docker Repository 79 | baseurl=https://yum.dockerproject.org/repo/main/centos/7/ 80 | enabled=1 81 | gpgcheck=1 82 | gpgkey=https://yum.dockerproject.org/gpg 83 | EOF 84 | ``` 85 | 86 | install the docker package 87 | ```bash 88 | sudo yum install docker-engine-1.11.2 89 | ``` 90 | 91 | Add following changes to /usr/lib/systemd/system/docker.service 92 | 93 | ```bash 94 | ExecStart=/usr/bin/docker daemon --storage-driver=overlay 95 | ``` 96 | 97 | reload systemd 98 | 99 | ```bash 100 | sudo systemctl daemon-reload 101 | ``` 102 | 103 | Start docker 104 | ```bash 105 | sudo systemctl start docker.service 106 | ``` 107 | 108 | Verify if docker works 109 | 110 | ```bash 111 | sudo docker run hello-world 112 | ``` 113 | 114 | 115 | download the dcos-gce scripts 116 | ```bash 117 | git clone https://github.com/dcos-labs/dcos-gce 118 | ``` 119 | 120 | change directory 121 | ```bash 122 | cd dcos-gce 123 | ``` 124 | 125 | Please make appropriate changes to group_vars/all. You need to review project, subnet, login_name, bootstrap_public_ip & zone 126 | 127 | insert following into ~/.ansible.cfg to stop host key checking 128 | ```bash 129 | [defaults] 130 | host_key_checking = False 131 | 132 | [paramiko_connection] 133 | record_host_keys = False 134 | 135 | [ssh_connection] 136 | ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null 137 | ``` 138 | 139 | Ensure the IP address for master0 in ./hosts is the next consecutive IP from bootstrap_public_ip. 140 | 141 | To create and configure the master nodes run 142 | ```bash 143 | ansible-playbook -i hosts install.yml 144 | ``` 145 | To create and configure the private nodes run 146 | ```bash 147 | ansible-playbook -i hosts add_agents.yml --extra-vars "start_id=0001 end_id=0002 agent_type=private" 148 | ``` 149 | start_id=0001 and end_id=0002 specify the range of id's that are appended to the hostname "agent" to create unique agent names. If start_id is not specified then a default of 0001 is used. 150 | If the end_id is not specified then a default of 0001 is used. 151 | 152 | *When specifying start_id or end_id via CLI, the leading zeroes must be dropped for any agent id higher than 7* or ansible will throw a format error. 153 | ```bash 154 | ansible-playbook -i hosts add_agents.yml --extra-vars "start_id=0006 end_id=10 agent_type=private" 155 | ``` 156 | The values for agent_type are either private or public. If an agent_type is not specified then it is assumed agent_type is private. 157 | 158 | To create public nodes type 159 | ```bash 160 | ansible-playbook -i hosts add_agents.yml --extra-vars "start_id=0003 end_id=0004 agent_type=public" 161 | ``` 162 | ## Configurable parameters 163 | 164 | File './hosts' is an ansible inventory file. Text wrapped by [] represents a group name and individual entries after the group name represent hosts in that group. 165 | The [masters] group contains node names and IP addresses for the master nodes. In the supplied file the host name is master0 and the ip address 10.132.0.3 is assigned to 166 | master0. **YOU MUST CHANGE** the IP address for master0 for your network. You can create multiple entries e.g. master1, master2 etc. Each node must have a unique IP address. 167 | 168 | The [agents] group has one entry. It specifies the names of all the agents one can have in the DC/OS cluster. The value specifies that agent0000 to agent9999, a 169 | total of 10,000 agents are allowed. This really is an artificial limit because it can easily be changed. 170 | 171 | The [bootstrap] group has the name of the bootstrap node. 172 | 173 | File './group_vars/all' contains miscellaneous parameters that will change the behaviour of the installation scripts. The parameters are split into two groups. Group 1 parameters must be changed to reflect your environment. Group 2 parameters can optionally be changed to change the behaviour of the scripts. 174 | 175 | ### Group 1 parameters YOU MUST CHANGE for your environment 176 | 177 | ```text 178 | project 179 | ``` 180 | Your project id. Default: trek-trackr 181 | 182 | ```text 183 | subnet 184 | ``` 185 | Your network. Default: default 186 | 187 | ```text 188 | login_name 189 | ``` 190 | The login name used for accessing each GCE instance. Default: ajazam 191 | 192 | ```text 193 | bootstrap_public_ip 194 | ``` 195 | The bootstrap nodes public IP. Default: 10.132.0.2 196 | 197 | ```text 198 | zone 199 | ``` 200 | You may change this to your preferred zone. Default: europe-west1-c 201 | 202 | 203 | ### Group 2 parameters which optionally change the behaviour of the installation scripts 204 | 205 | ```text 206 | master_boot_disk_size: 207 | ``` 208 | The size of the master node boot disk. Default 10 GB 209 | 210 | ```text 211 | master_machine_type 212 | ``` 213 | The GCE instance type used for the master nodes. Default: n1-standard-2 214 | 215 | ```text 216 | master_boot_disk_type 217 | ``` 218 | The master boot disk type. Default: pd-standard 219 | 220 | ```text 221 | agent_boot_disk_size 222 | ``` 223 | The size of the agent boot disk. Default 10 GB 224 | 225 | ```text 226 | agent_machine_type 227 | ``` 228 | The GCE instance type used for the agent nodes. Default: n1-standard-2 229 | 230 | ```text 231 | agent_boot_disk_type 232 | ``` 233 | The agent boot disk type. Default: pd-standard 234 | 235 | ```text 236 | agent_instance_type 237 | ``` 238 | Allows agents to be preemptible. If the value is "MIGRATE" then they are not preemptible. If the value is '"TERMINATE" --preemptible' then the instance is preemptible. Default: "MIGRATE" 239 | 240 | ```text 241 | agent_type 242 | ``` 243 | Can specify whether an agent is "public" or "private". Default: "private" 244 | 245 | ```text 246 | start_id 247 | ``` 248 | The number appended to the text *agent* is used to define the hostname of the first agent. e.g. agent0001. Intermediate agents between start_id and end_id will be created if required. Default: 0001 249 | 250 | ```text 251 | end_id 252 | ``` 253 | The number appended to the text *agent* is used to define the hostname of the last agent. e.g. agent0001. Intermediate agents between start_id and end_id will be created if required. Default: 0001 254 | 255 | 256 | ```text 257 | gcloudbin 258 | ``` 259 | The location of the gcloudbin binary. Default: /usr/local/bin/gcloud 260 | 261 | ```text 262 | image 263 | ``` 264 | The disk image used on the master and agent. Default: /centos-cloud/centos-7-v20161027 265 | 266 | ```text 267 | bootstrap_public_port 268 | ``` 269 | The port on the bootstrap node which is used to fetch the dcos installer from each of the master and agent nodes. Default: 8080 270 | 271 | ```text 272 | cluster_name 273 | ``` 274 | The name of the DC/OS cluster. Default: cluster_name 275 | 276 | ```text 277 | scopes 278 | ``` 279 | Don't change this. Required by the google cloud SDK 280 | 281 | ```text 282 | dcos_installer_filename 283 | ``` 284 | The filename for the DC/OS installer. Default dcos_generate_config.sh 285 | 286 | ```text 287 | dcos_installer_download_path 288 | ``` 289 | The location of where the dcos installer is available from dcos.io. Default: https://downloads.dcos.io/dcos/stable/{{ dcos_installer_filename }} The value of {{ dcos_installer_file }} is described above. 290 | 291 | ```text 292 | home_directory 293 | ``` 294 | The home directory for your logins. Default: /home/{{ login_name }} The value of {{ login_name }} is described above. 295 | 296 | ```text 297 | downloads_from_bootstrap 298 | ``` 299 | The concurrent downloads of the dcos installer to the cluster of master and agent nodes. You may need to experiment with this to get the best performance. The performance will be a function of the machine type used for the bootstrap node. Default: 2 300 | 301 | ```text 302 | dcos_bootstrap_container 303 | ``` 304 | Holds the name of the dcos bootstrap container running on the bootstrap node. Default: dcosinstaller 305 | 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /inventory/gce.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 . 18 | 19 | ''' 20 | GCE external inventory script 21 | ================================= 22 | 23 | Generates inventory that Ansible can understand by making API requests 24 | Google Compute Engine via the libcloud library. Full install/configuration 25 | instructions for the gce* modules can be found in the comments of 26 | ansible/test/gce_tests.py. 27 | 28 | When run against a specific host, this script returns the following variables 29 | based 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 | 44 | When 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 | 64 | Examples: 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 | $ contrib/inventory/gce.py --host my_instance 70 | 71 | Author: Eric Johnson 72 | Contributors: Matt Hite 73 | Version: 0.0.2 74 | ''' 75 | 76 | __requires__ = ['pycrypto>=2.6'] 77 | try: 78 | import pkg_resources 79 | except ImportError: 80 | # Use pkg_resources to find the correct versions of libraries and set 81 | # sys.path appropriately when there are multiversion installs. We don't 82 | # fail here as there is code that better expresses the errors where the 83 | # library is used. 84 | pass 85 | 86 | USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin" 87 | USER_AGENT_VERSION="v2" 88 | 89 | import sys 90 | import os 91 | import argparse 92 | import ConfigParser 93 | 94 | import logging 95 | logging.getLogger('libcloud.common.google').addHandler(logging.NullHandler()) 96 | 97 | try: 98 | import json 99 | except ImportError: 100 | import simplejson as json 101 | 102 | try: 103 | from libcloud.compute.types import Provider 104 | from libcloud.compute.providers import get_driver 105 | _ = Provider.GCE 106 | except: 107 | print("GCE inventory script requires libcloud >= 0.13") 108 | sys.exit(1) 109 | 110 | 111 | class GceInventory(object): 112 | def __init__(self): 113 | # Read settings and parse CLI arguments 114 | self.parse_cli_args() 115 | self.config = self.get_config() 116 | self.driver = self.get_gce_driver() 117 | self.ip_type = self.get_inventory_options() 118 | if self.ip_type: 119 | self.ip_type = self.ip_type.lower() 120 | 121 | # Just display data for specific host 122 | if self.args.host: 123 | print(self.json_format_dict(self.node_to_dict( 124 | self.get_instance(self.args.host)), 125 | pretty=self.args.pretty)) 126 | sys.exit(0) 127 | 128 | # Otherwise, assume user wants all instances grouped 129 | print(self.json_format_dict(self.group_instances(), 130 | pretty=self.args.pretty)) 131 | sys.exit(0) 132 | 133 | def get_config(self): 134 | """ 135 | Populates a SafeConfigParser object with defaults and 136 | attempts to read an .ini-style configuration from the filename 137 | specified in GCE_INI_PATH. If the environment variable is 138 | not present, the filename defaults to gce.ini in the current 139 | working directory. 140 | """ 141 | gce_ini_default_path = os.path.join( 142 | os.path.dirname(os.path.realpath(__file__)), "gce.ini") 143 | gce_ini_path = os.environ.get('GCE_INI_PATH', gce_ini_default_path) 144 | 145 | # Create a ConfigParser. 146 | # This provides empty defaults to each key, so that environment 147 | # variable configuration (as opposed to INI configuration) is able 148 | # to work. 149 | config = ConfigParser.SafeConfigParser(defaults={ 150 | 'gce_service_account_email_address': '', 151 | 'gce_service_account_pem_file_path': '', 152 | 'gce_project_id': '', 153 | 'libcloud_secrets': '', 154 | 'inventory_ip_type': '', 155 | }) 156 | if 'gce' not in config.sections(): 157 | config.add_section('gce') 158 | if 'inventory' not in config.sections(): 159 | config.add_section('inventory') 160 | 161 | config.read(gce_ini_path) 162 | return config 163 | 164 | def get_inventory_options(self): 165 | """Determine inventory options. Environment variables always 166 | take precedence over configuration files.""" 167 | ip_type = self.config.get('inventory', 'inventory_ip_type') 168 | # If the appropriate environment variables are set, they override 169 | # other configuration 170 | ip_type = os.environ.get('INVENTORY_IP_TYPE', ip_type) 171 | return ip_type 172 | 173 | def get_gce_driver(self): 174 | """Determine the GCE authorization settings and return a 175 | libcloud driver. 176 | """ 177 | # Attempt to get GCE params from a configuration file, if one 178 | # exists. 179 | secrets_path = self.config.get('gce', 'libcloud_secrets') 180 | secrets_found = False 181 | try: 182 | import secrets 183 | args = list(getattr(secrets, 'GCE_PARAMS', [])) 184 | kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {}) 185 | secrets_found = True 186 | except: 187 | pass 188 | 189 | if not secrets_found and secrets_path: 190 | if not secrets_path.endswith('secrets.py'): 191 | err = "Must specify libcloud secrets file as " 192 | err += "/absolute/path/to/secrets.py" 193 | print(err) 194 | sys.exit(1) 195 | sys.path.append(os.path.dirname(secrets_path)) 196 | try: 197 | import secrets 198 | args = list(getattr(secrets, 'GCE_PARAMS', [])) 199 | kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {}) 200 | secrets_found = True 201 | except: 202 | pass 203 | if not secrets_found: 204 | args = [ 205 | self.config.get('gce','gce_service_account_email_address'), 206 | self.config.get('gce','gce_service_account_pem_file_path') 207 | ] 208 | kwargs = {'project': self.config.get('gce', 'gce_project_id')} 209 | 210 | # If the appropriate environment variables are set, they override 211 | # other configuration; process those into our args and kwargs. 212 | args[0] = os.environ.get('GCE_EMAIL', args[0]) 213 | args[1] = os.environ.get('GCE_PEM_FILE_PATH', args[1]) 214 | kwargs['project'] = os.environ.get('GCE_PROJECT', kwargs['project']) 215 | 216 | # Retrieve and return the GCE driver. 217 | gce = get_driver(Provider.GCE)(*args, **kwargs) 218 | gce.connection.user_agent_append( 219 | '%s/%s' % (USER_AGENT_PRODUCT, USER_AGENT_VERSION), 220 | ) 221 | return gce 222 | 223 | def parse_cli_args(self): 224 | ''' Command line argument processing ''' 225 | 226 | parser = argparse.ArgumentParser( 227 | description='Produce an Ansible Inventory file based on GCE') 228 | parser.add_argument('--list', action='store_true', default=True, 229 | help='List instances (default: True)') 230 | parser.add_argument('--host', action='store', 231 | help='Get all information about an instance') 232 | parser.add_argument('--pretty', action='store_true', default=False, 233 | help='Pretty format (default: False)') 234 | self.args = parser.parse_args() 235 | 236 | 237 | def node_to_dict(self, inst): 238 | md = {} 239 | 240 | if inst is None: 241 | return {} 242 | 243 | if inst.extra['metadata'].has_key('items'): 244 | for entry in inst.extra['metadata']['items']: 245 | md[entry['key']] = entry['value'] 246 | 247 | net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1] 248 | # default to exernal IP unless user has specified they prefer internal 249 | if self.ip_type == 'internal': 250 | ssh_host = inst.private_ips[0] 251 | else: 252 | ssh_host = inst.public_ips[0] if len(inst.public_ips) >= 1 else inst.private_ips[0] 253 | 254 | return { 255 | 'gce_uuid': inst.uuid, 256 | 'gce_id': inst.id, 257 | 'gce_image': inst.image, 258 | 'gce_machine_type': inst.size, 259 | 'gce_private_ip': inst.private_ips[0], 260 | 'gce_public_ip': inst.public_ips[0] if len(inst.public_ips) >= 1 else None, 261 | 'gce_name': inst.name, 262 | 'gce_description': inst.extra['description'], 263 | 'gce_status': inst.extra['status'], 264 | 'gce_zone': inst.extra['zone'].name, 265 | 'gce_tags': inst.extra['tags'], 266 | 'gce_metadata': md, 267 | 'gce_network': net, 268 | # Hosts don't have a public name, so we add an IP 269 | 'ansible_ssh_host': ssh_host 270 | } 271 | 272 | def get_instance(self, instance_name): 273 | '''Gets details about a specific instance ''' 274 | try: 275 | return self.driver.ex_get_node(instance_name) 276 | except Exception as e: 277 | return None 278 | 279 | def group_instances(self): 280 | '''Group all instances''' 281 | groups = {} 282 | meta = {} 283 | meta["hostvars"] = {} 284 | 285 | for node in self.driver.list_nodes(): 286 | name = node.name 287 | 288 | meta["hostvars"][name] = self.node_to_dict(node) 289 | 290 | zone = node.extra['zone'].name 291 | if groups.has_key(zone): groups[zone].append(name) 292 | else: groups[zone] = [name] 293 | 294 | tags = node.extra['tags'] 295 | for t in tags: 296 | if t.startswith('group-'): 297 | tag = t[6:] 298 | else: 299 | tag = 'tag_%s' % t 300 | if groups.has_key(tag): groups[tag].append(name) 301 | else: groups[tag] = [name] 302 | 303 | net = node.extra['networkInterfaces'][0]['network'].split('/')[-1] 304 | net = 'network_%s' % net 305 | if groups.has_key(net): groups[net].append(name) 306 | else: groups[net] = [name] 307 | 308 | machine_type = node.size 309 | if groups.has_key(machine_type): groups[machine_type].append(name) 310 | else: groups[machine_type] = [name] 311 | 312 | image = node.image and node.image or 'persistent_disk' 313 | if groups.has_key(image): groups[image].append(name) 314 | else: groups[image] = [name] 315 | 316 | status = node.extra['status'] 317 | stat = 'status_%s' % status.lower() 318 | if groups.has_key(stat): groups[stat].append(name) 319 | else: groups[stat] = [name] 320 | 321 | groups["_meta"] = meta 322 | 323 | return groups 324 | 325 | def json_format_dict(self, data, pretty=False): 326 | ''' Converts a dict to a JSON object and dumps it as a formatted 327 | string ''' 328 | 329 | if pretty: 330 | return json.dumps(data, sort_keys=True, indent=2) 331 | else: 332 | return json.dumps(data) 333 | 334 | 335 | # Run the script 336 | GceInventory() 337 | --------------------------------------------------------------------------------