{"action":"created","comment":{"url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/comments/95814346","pull_request_review_id":16356019,"id":95814346,"diff_hunk":"@@ -0,0 +1,438 @@\n+#!/usr/bin/python\n+# -*- coding: utf-8 -*-\n+#\n+# (c) 2017, Gaudenz Steinlin <gaudenz.steinlin@cloudscale.ch>\n+#\n+# This file is part of Ansible\n+#\n+# Ansible is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# Ansible is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.\n+\n+ANSIBLE_METADATA = {'status': ['preview'],\n+                    'supported_by': 'community',\n+                    'version': '1.0'}\n+\n+DOCUMENTATION = '''\n+---\n+module: cloudscale_server\n+short_description: Manages servers on the cloudscale.ch IaaS service\n+description:\n+  - Create, start, stop and delete servers on the cloudscale.ch IaaS service\n+  - All operations are performed using the cloudscale.ch public API v1\n+  - \"For details consult the full API documentation: U(https://www.cloudscale.ch/en/api/v1)\"\n+  - An valid API token is required for all operations. You can create as many tokens as you like using the cloudscale.ch control panel at U(https://control.cloudscale.ch)\n+notes:\n+  - Instead of the api_token parameter the CLOUDSCALE_API_TOKEN environment variable can be used.\n+  - To create a new server at least the C(name), C(ssh_key), C(image) and C(flavor) options are required.\n+  - If more than one server with the name given by the C(name) option exists, execution is aborted.\n+  - Once a server is created all parameters except C(state) are read-only. You can't change the name, flavor or any other property. This is a limitation of the cloudscale.ch API. The module will silently ignore differences between the configured parameters and the running server if a server with the correct name or UUID exists. Only state changes will be applied.\n+version_added: 2.3\n+author: \"Gaudenz Steinlin <gaudenz.steinlin@cloudscale.ch>\"\n+options:\n+  state:\n+    description:\n+      - State of the server\n+    required: False\n+    default: running\n+    choices: ['running', 'stopped', 'absent']\n+  name:\n+    description:\n+      - Name of the Server\n+      - Either C(name) or C(uuid) are required. These options are mutually exclusive.\n+    required: False\n+  uuid:\n+    description:\n+      - UUID of the server\n+      - Either C(name) or C(uuid) are required. These options are mutually exclusive.\n+    required: False\n+  flavor:\n+    description:\n+      - Flavor of the server\n+    required: False\n+  image:\n+    description:\n+      - Image used to create the server\n+    required: False\n+  volume_size_gb:\n+    description:\n+      - Size of the root volume in GB\n+    required: False\n+    default: 10\n+  bulk_volume_size_gb:\n+    description:\n+      - Size of the bulk storage volume in GB\n+    required: False\n+    default: null (no bulk storage volume)\n+  ssh_keys:\n+    description:\n+       - List of SSH public keys\n+       - Use the full content of your .pub file here.\n+    required: False\n+  use_public_network:\n+    description:\n+      - Attach a public network interface to the server\n+    required: False\n+    default: True\n+  use_private_network:\n+    description:\n+      - Attach a private network interface to the server\n+    required: False\n+    default: False\n+  use_ipv6:\n+    description:\n+      - Enable IPv6 on the public network interface\n+    required: False\n+    default: True\n+  anti_affinity_with:\n+    description:\n+      - UUID of another server to create an anti-affinity group with\n+    required: False\n+  user_data:\n+    description:\n+      - Cloud-init configuration (cloud-config) data to use for the server.\n+    required: False\n+  api_token:\n+    description:\n+      - cloudscale.ch API token.\n+      - This can also be passed in the CLOUDSCALE_API_TOKEN environment variable.\n+    required: False\n+'''\n+\n+EXAMPLES = '''\n+# Start a server (if it does not exist) and register the server details\n+- name: Start cloudscale.ch server\n+  cloudscale_server:\n+    name: my-shiny-cloudscale-server\n+    image: debian-8\n+    flavor: flex-4\n+    ssh_keys: ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale\n+    use_private_network: True\n+    bulk_volume_size_gb: 100\n+    api_token: xxxxxx\n+  register: server1\n+\n+# Start another server in anti-affinity to the first one\n+- name: Start second cloudscale.ch server\n+  cloudscale_server:\n+    name: my-other-shiny-server\n+    image: ubuntu-16.04\n+    flavor: flex-8\n+    ssh_keys: ssh-rsa XXXXXXXXXXX ansible@cloudscale\n+    anti_affinity_with: '{{ server1.uuid }}'\n+    api_token: xxxxxx\n+\n+# Stop the first server\n+- name: Stop my first server\n+  cloudscale_server:\n+    uuid: '{{ server1.uuid }}'\n+    state: stopped\n+    api_token: xxxxxx\n+\n+# Delete my second server\n+- name: Delete my second server\n+  cloudscale_server:\n+    name: my-other-shiny-server\n+    state: absent\n+    api_token: xxxxxx\n+'''\n+\n+RETURN = '''\n+href:\n+  description: API URL to get details about this server\n+  returned: success when not state == absent\n+  type: string\n+  sample: https://api.cloudscale.ch/v1/servers/cfde831a-4e87-4a75-960f-89b0148aa2cc\n+uuid:\n+  description: The unique identifier for this server\n+  returned: success\n+  type: string\n+  sample: cfde831a-4e87-4a75-960f-89b0148aa2cc\n+name:\n+  description: The display name of the server\n+  returned: success\n+  type: string\n+  sample: its-a-me-mario.cloudscale.ch\n+state:\n+  description: The current status of the server\n+  returned: success\n+  type: string\n+  sample: running\n+flavor:\n+  description: The flavor that has been used for this server\n+  returned: success when not state == absent\n+  type: string\n+  sample: flex-8\n+image:\n+  description: The image used for booting this server\n+  returned: success when not state == absent\n+  type: string\n+  sample: debian-8\n+volumes:\n+  description: List of volumes attached to the server\n+  returned: success when not state == absent\n+  type: list\n+  sample: [ {\"type\": \"ssd\", \"device\": \"/dev/vda\", \"size_gb\": \"50\"} ]\n+interfaces:\n+  description: List of network ports attached to the server\n+  returned: success when not state == absent\n+  type: list\n+  sample: [ { \"type\": \"public\", \"addresses\": [ ... ] } ]\n+anti_affinity_with:\n+  description: List of servers in the same anti-affinity group\n+  returned: success when not state == absent\n+  type: string\n+  sample: []\n+'''\n+\n+from datetime import datetime, timedelta\n+import json\n+import os\n+from time import sleep\n+from urllib import urlencode\n+\n+from ansible.module_utils.basic import AnsibleModule\n+from ansible.module_utils.urls import fetch_url\n+\n+API_URL      = 'https://api.cloudscale.ch/v1/'\n+TIMEOUT_WAIT = 30\n+\n+class AnsibleCloudscaleServer(object):\n+\n+    def __init__(self, module, api_token):\n+        self._module = module\n+        self._auth_header = {'Authorization': 'Bearer %s' % api_token}\n+\n+        # Check if server already exists and load properties\n+        uuid = self._module.params['uuid']\n+        name = self._module.params['name']\n+\n+        # Initialize server dictionary\n+        self.info = {'uuid': uuid, 'name': name, 'state': 'absent'}\n+\n+        servers = self.list_servers()\n+        matching_server = []\n+        for s in servers:\n+            if uuid:\n+                # Look for server by UUID if given\n+                if s['uuid'] == uuid:\n+                    self.info =  self._transform_state(s)\n+                    break\n+            else:\n+                # Look for server by name\n+                if s['name'] == name:\n+                    matching_server.append(s)\n+        else:\n+            if len(matching_server) == 1:\n+                self.info = self._transform_state(matching_server[0])\n+            elif len(matching_server) > 1:\n+                self._module.fail_json(msg=\"More than one server with name '%s' exists. \"\n+                                       \"Use the 'uuid' parameter to identify the server\" % name)\n+\n+\n+    def _get(self, api_call):\n+        resp, info = fetch_url(self._module, API_URL+api_call, headers=self._auth_header)\n+\n+        if info['status'] == 200:\n+            return json.loads(resp.read())\n+        else:\n+            self._module.fail_json(msg='Failure while calling the cloudscale.ch API with GET for '\n+                                       '\"%s\": %s' % (api_call, info['body']))\n+\n+\n+    def _post(self, api_call, data=None):\n+        if data is not None:\n+            data = urlencode(data)\n+\n+        resp, info = fetch_url(self._module,\n+                               API_URL+api_call,\n+                               headers = self._auth_header,\n+                               method='POST',\n+                               data=data)\n+\n+        if info['status'] == 201:\n+            return json.loads(resp.read())\n+        elif info['status'] == 204:\n+            return None\n+        else:\n+            self._module.fail_json(msg='Failure while calling the cloudscale.ch API with POST for '\n+                                       '\"%s\": %s' % (api_call, info['body']))\n+\n+\n+    def _delete(self, api_call):\n+        resp, info = fetch_url(self._module,\n+                               API_URL+api_call,\n+                               headers = self._auth_header,\n+                               method='DELETE')\n+\n+        if info['status'] == 204:\n+            return None\n+        else:\n+            self._module.fail_json(msg='Failure while calling the cloudscale.ch API with DELETE for '\n+                                       '\"%s\": %s' % (api_call, info['body']))\n+\n+\n+    @staticmethod\n+    def _transform_state(server):\n+        if 'status' in server:\n+            server['state'] = server['status']\n+            server.pop('status')\n+        else:\n+            server['state'] = 'absent'\n+        return server\n+\n+\n+    def update_info(self):\n+\n+        # If we don't have a UUID (yet) there is nothing to update\n+        if not 'uuid' in self.info:\n+            return\n+\n+        # Can't use _get here because we want to handle 404\n+        resp, info = fetch_url(self._module,\n+                               API_URL+'servers/'+self.info['uuid'],\n+                               headers=self._auth_header)\n+        if info['status'] == 200:\n+            self.info = self._transform_state(json.loads(resp.read()))\n+        elif info['status'] == 404:\n+            self.info = {'uuid': self.info['uuid'],\n+                         'name': self.info.get('name', None),\n+                         'state': 'absent'}\n+        else:\n+            self._module.fail_json(msg='Failure while calling the cloudscale.ch API for '\n+                                       'update_info: %s' % info['body'])\n+\n+\n+    def wait_for_state(self, state):\n+        start = datetime.now()\n+        while datetime.now() - start < timedelta(TIMEOUT_WAIT):\n+            self.update_info()\n+            if self.info['state'] == state:\n+                return True\n+            sleep(1)\n+\n+        return False\n+\n+\n+    def create_server(self):\n+        data = self._module.params.copy()\n+\n+        # check for required parameters to create a server\n+        missing_parameters = []\n+        for p in ('name', 'ssh_keys', 'image', 'flavor'):\n+            if not p in data or not data[p]:\n+                missing_parameters.append(p)\n+\n+        if len(missing_parameters) > 0:\n+            self._module.fail_json(msg='Missing required parameter(s) to create a new server: %s' %\n+                                   ' '.join(missing_parameters))\n+\n+        # Sanitize data dictionary\n+        for k,v in data.items():\n+\n+            # Remove items not relevant to the create server call\n+            if k in ('api_token', 'uuid', 'state'):\n+                data.pop(k)\n+                continue\n+\n+            # Remove None values, these don't get correctly translated by urlencode\n+            if v is None:\n+                data.pop(k)\n+                continue\n+\n+        self.info = self._transform_state(self._post('servers', data))\n+        self.wait_for_state('running')\n+\n+\n+    def delete_server(self):\n+        self._delete('servers/%s' % self.info['uuid'])\n+        self.wait_for_state('absent')\n+\n+\n+    def start_server(self):\n+        self._post('servers/%s/start' % self.info['uuid'])\n+        self.wait_for_state('running')\n+\n+\n+    def stop_server(self):\n+        self._post('servers/%s/stop' % self.info['uuid'])\n+        self.wait_for_state('stopped')\n+\n+\n+    def list_servers(self):\n+        return self._get('servers')\n+\n+\n+def main():\n+    module = AnsibleModule(\n+        argument_spec = dict(\n+            state               = dict(default='running',\n+                                       choices=['running',\n+                                                'stopped',\n+                                                'absent',\n+                                       ]),\n+            name                = dict(),\n+            uuid                = dict(),\n+            flavor              = dict(),\n+            image               = dict(),\n+            volume_size_gb      = dict(type='int', default=10),\n+            bulk_volume_size_gb = dict(type='int'),\n+            ssh_keys            = dict(type='list'),\n+            use_public_network  = dict(type='bool', default=True),\n+            use_private_network = dict(type='bool', default=False),\n+            use_ipv6            = dict(type='bool', default=True),\n+            anti_affinity_with  = dict(),\n+            user_data           = dict(),\n+            api_token           = dict(no_log=True),\n+        ),\n+        required_one_of=(('name', 'uuid'),),\n+        mutually_exclusive=(('name', 'uuid'),),\n+        supports_check_mode=True,\n+    )\n+\n+    api_token = module.params['api_token'] or os.environ.get('CLOUDSCALE_API_TOKEN')\n+\n+    if not api_token:\n+        module.fail_json(msg='The api_token module parameter or the CLOUDSCALE_API_TOKEN '\n+                             'environment varialbe are required for this module.')\n+\n+    target_state = module.params['state']\n+    server = AnsibleCloudscaleServer(module, api_token)\n+    current_state = server.info['state']\n+\n+    if module.check_mode:\n+        module.exit_json(changed=not target_state == current_state,\n+                         **server.info)\n+\n+    changed = False\n+    if current_state == 'absent' and target_state == 'running':\n+        server.create_server()\n+        changed = True\n+    elif current_state == 'absent' and target_state == 'stopped':\n+        server.create_server()\n+        server.stop_server()\n+        changed = True\n+    elif current_state == 'stopped' and target_state == 'running':\n+        server.start_server()\n+        changed = True\n+    elif current_state in ('running', 'stopped') and target_state == 'absent':\n+        server.delete_server()\n+        changed = True\n+    elif current_state == 'running' and target_state == 'stopped':\n+        server.stop_server()\n+        changed = True","path":"lib/ansible/modules/cloud/cloudscale/cloudscale_server.py","position":432,"original_position":432,"commit_id":"5fc31fe724a5d5eb285ead1c3ae8acffbf14a07c","original_commit_id":"f3937f4645adb50935ab43d4966fa363d62f7159","user":{"login":"davidhalter","id":748594,"avatar_url":"https://avatars.githubusercontent.com/u/748594?v=3","gravatar_id":"","url":"https://api.github.com/users/davidhalter","html_url":"https://github.com/davidhalter","followers_url":"https://api.github.com/users/davidhalter/followers","following_url":"https://api.github.com/users/davidhalter/following{/other_user}","gists_url":"https://api.github.com/users/davidhalter/gists{/gist_id}","starred_url":"https://api.github.com/users/davidhalter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidhalter/subscriptions","organizations_url":"https://api.github.com/users/davidhalter/orgs","repos_url":"https://api.github.com/users/davidhalter/repos","events_url":"https://api.github.com/users/davidhalter/events{/privacy}","received_events_url":"https://api.github.com/users/davidhalter/received_events","type":"User","site_admin":false},"body":"What do we do when the state is changing? Not that I think this is wrong, but I would like to clarify this. Maybe also with a comment in the code.","created_at":"2017-01-12T15:25:52Z","updated_at":"2017-01-12T15:43:36Z","html_url":"https://github.com/cloudscale-ch/ansible/pull/2#discussion_r95814346","pull_request_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/comments/95814346"},"html":{"href":"https://github.com/cloudscale-ch/ansible/pull/2#discussion_r95814346"},"pull_request":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2","id":101256363,"html_url":"https://github.com/cloudscale-ch/ansible/pull/2","diff_url":"https://github.com/cloudscale-ch/ansible/pull/2.diff","patch_url":"https://github.com/cloudscale-ch/ansible/pull/2.patch","issue_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/2","number":2,"state":"open","locked":false,"title":"Another try at merging cloud modules","user":{"login":"davidhalter","id":748594,"avatar_url":"https://avatars.githubusercontent.com/u/748594?v=3","gravatar_id":"","url":"https://api.github.com/users/davidhalter","html_url":"https://github.com/davidhalter","followers_url":"https://api.github.com/users/davidhalter/followers","following_url":"https://api.github.com/users/davidhalter/following{/other_user}","gists_url":"https://api.github.com/users/davidhalter/gists{/gist_id}","starred_url":"https://api.github.com/users/davidhalter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidhalter/subscriptions","organizations_url":"https://api.github.com/users/davidhalter/orgs","repos_url":"https://api.github.com/users/davidhalter/repos","events_url":"https://api.github.com/users/davidhalter/events{/privacy}","received_events_url":"https://api.github.com/users/davidhalter/received_events","type":"User","site_admin":false},"body":"Let's review it here.","created_at":"2017-01-12T13:13:09Z","updated_at":"2017-01-12T15:43:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e44fab6850476151cf14117eef9c04d7a2994628","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2/commits","review_comments_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2/comments","review_comment_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/2/comments","statuses_url":"https://api.github.com/repos/cloudscale-ch/ansible/statuses/5fc31fe724a5d5eb285ead1c3ae8acffbf14a07c","head":{"label":"cloudscale-ch:feature/2.3/cloudscale_server","ref":"feature/2.3/cloudscale_server","sha":"5fc31fe724a5d5eb285ead1c3ae8acffbf14a07c","user":{"login":"cloudscale-ch","id":23522857,"avatar_url":"https://avatars.githubusercontent.com/u/23522857?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudscale-ch","html_url":"https://github.com/cloudscale-ch","followers_url":"https://api.github.com/users/cloudscale-ch/followers","following_url":"https://api.github.com/users/cloudscale-ch/following{/other_user}","gists_url":"https://api.github.com/users/cloudscale-ch/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudscale-ch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudscale-ch/subscriptions","organizations_url":"https://api.github.com/users/cloudscale-ch/orgs","repos_url":"https://api.github.com/users/cloudscale-ch/repos","events_url":"https://api.github.com/users/cloudscale-ch/events{/privacy}","received_events_url":"https://api.github.com/users/cloudscale-ch/received_events","type":"Organization","site_admin":false},"repo":{"id":78655593,"name":"ansible","full_name":"cloudscale-ch/ansible","owner":{"login":"cloudscale-ch","id":23522857,"avatar_url":"https://avatars.githubusercontent.com/u/23522857?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudscale-ch","html_url":"https://github.com/cloudscale-ch","followers_url":"https://api.github.com/users/cloudscale-ch/followers","following_url":"https://api.github.com/users/cloudscale-ch/following{/other_user}","gists_url":"https://api.github.com/users/cloudscale-ch/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudscale-ch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudscale-ch/subscriptions","organizations_url":"https://api.github.com/users/cloudscale-ch/orgs","repos_url":"https://api.github.com/users/cloudscale-ch/repos","events_url":"https://api.github.com/users/cloudscale-ch/events{/privacy}","received_events_url":"https://api.github.com/users/cloudscale-ch/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudscale-ch/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.","fork":true,"url":"https://api.github.com/repos/cloudscale-ch/ansible","forks_url":"https://api.github.com/repos/cloudscale-ch/ansible/forks","keys_url":"https://api.github.com/repos/cloudscale-ch/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudscale-ch/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudscale-ch/ansible/teams","hooks_url":"https://api.github.com/repos/cloudscale-ch/ansible/hooks","issue_events_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/cloudscale-ch/ansible/events","assignees_url":"https://api.github.com/repos/cloudscale-ch/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/cloudscale-ch/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/cloudscale-ch/ansible/tags","blobs_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudscale-ch/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudscale-ch/ansible/languages","stargazers_url":"https://api.github.com/repos/cloudscale-ch/ansible/stargazers","contributors_url":"https://api.github.com/repos/cloudscale-ch/ansible/contributors","subscribers_url":"https://api.github.com/repos/cloudscale-ch/ansible/subscribers","subscription_url":"https://api.github.com/repos/cloudscale-ch/ansible/subscription","commits_url":"https://api.github.com/repos/cloudscale-ch/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudscale-ch/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudscale-ch/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/cloudscale-ch/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudscale-ch/ansible/merges","archive_url":"https://api.github.com/repos/cloudscale-ch/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudscale-ch/ansible/downloads","issues_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudscale-ch/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudscale-ch/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudscale-ch/ansible/labels{/name}","releases_url":"https://api.github.com/repos/cloudscale-ch/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/cloudscale-ch/ansible/deployments","created_at":"2017-01-11T16:08:06Z","updated_at":"2017-01-11T16:08:23Z","pushed_at":"2017-01-12T15:29:36Z","git_url":"git://github.com/cloudscale-ch/ansible.git","ssh_url":"git@github.com:cloudscale-ch/ansible.git","clone_url":"https://github.com/cloudscale-ch/ansible.git","svn_url":"https://github.com/cloudscale-ch/ansible","homepage":"http://ansible.com/","size":63306,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"devel"}},"base":{"label":"cloudscale-ch:devel","ref":"devel","sha":"e35a757ee7cbb02e152698518e40af72d26b28f9","user":{"login":"cloudscale-ch","id":23522857,"avatar_url":"https://avatars.githubusercontent.com/u/23522857?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudscale-ch","html_url":"https://github.com/cloudscale-ch","followers_url":"https://api.github.com/users/cloudscale-ch/followers","following_url":"https://api.github.com/users/cloudscale-ch/following{/other_user}","gists_url":"https://api.github.com/users/cloudscale-ch/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudscale-ch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudscale-ch/subscriptions","organizations_url":"https://api.github.com/users/cloudscale-ch/orgs","repos_url":"https://api.github.com/users/cloudscale-ch/repos","events_url":"https://api.github.com/users/cloudscale-ch/events{/privacy}","received_events_url":"https://api.github.com/users/cloudscale-ch/received_events","type":"Organization","site_admin":false},"repo":{"id":78655593,"name":"ansible","full_name":"cloudscale-ch/ansible","owner":{"login":"cloudscale-ch","id":23522857,"avatar_url":"https://avatars.githubusercontent.com/u/23522857?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudscale-ch","html_url":"https://github.com/cloudscale-ch","followers_url":"https://api.github.com/users/cloudscale-ch/followers","following_url":"https://api.github.com/users/cloudscale-ch/following{/other_user}","gists_url":"https://api.github.com/users/cloudscale-ch/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudscale-ch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudscale-ch/subscriptions","organizations_url":"https://api.github.com/users/cloudscale-ch/orgs","repos_url":"https://api.github.com/users/cloudscale-ch/repos","events_url":"https://api.github.com/users/cloudscale-ch/events{/privacy}","received_events_url":"https://api.github.com/users/cloudscale-ch/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudscale-ch/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.","fork":true,"url":"https://api.github.com/repos/cloudscale-ch/ansible","forks_url":"https://api.github.com/repos/cloudscale-ch/ansible/forks","keys_url":"https://api.github.com/repos/cloudscale-ch/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudscale-ch/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudscale-ch/ansible/teams","hooks_url":"https://api.github.com/repos/cloudscale-ch/ansible/hooks","issue_events_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/cloudscale-ch/ansible/events","assignees_url":"https://api.github.com/repos/cloudscale-ch/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/cloudscale-ch/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/cloudscale-ch/ansible/tags","blobs_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudscale-ch/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudscale-ch/ansible/languages","stargazers_url":"https://api.github.com/repos/cloudscale-ch/ansible/stargazers","contributors_url":"https://api.github.com/repos/cloudscale-ch/ansible/contributors","subscribers_url":"https://api.github.com/repos/cloudscale-ch/ansible/subscribers","subscription_url":"https://api.github.com/repos/cloudscale-ch/ansible/subscription","commits_url":"https://api.github.com/repos/cloudscale-ch/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudscale-ch/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudscale-ch/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudscale-ch/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/cloudscale-ch/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudscale-ch/ansible/merges","archive_url":"https://api.github.com/repos/cloudscale-ch/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudscale-ch/ansible/downloads","issues_url":"https://api.github.com/repos/cloudscale-ch/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/cloudscale-ch/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudscale-ch/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudscale-ch/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudscale-ch/ansible/labels{/name}","releases_url":"https://api.github.com/repos/cloudscale-ch/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/cloudscale-ch/ansible/deployments","created_at":"2017-01-11T16:08:06Z","updated_at":"2017-01-11T16:08:23Z","pushed_at":"2017-01-12T15:29:36Z","git_url":"git://github.com/cloudscale-ch/ansible.git","ssh_url":"git@github.com:cloudscale-ch/ansible.git","clone_url":"https://github.com/cloudscale-ch/ansible.git","svn_url":"https://github.com/cloudscale-ch/ansible","homepage":"http://ansible.com/","size":63306,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"devel"}},"_links":{"self":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2"},"html":{"href":"https://github.com/cloudscale-ch/ansible/pull/2"},"issue":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/issues/2"},"comments":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/cloudscale-ch/ansible/statuses/5fc31fe724a5d5eb285ead1c3ae8acffbf14a07c"}}}}
{"actor":{"display_login":"davidhalter"}}