Beispiel #1
0
def main():
    """ main :) """
    args = prepare_arguments()

    if args.name_outputfile is not None:
        outputfile = open(args.name_outputfile, 'w')
    else:
        outputfile = sys.stdout

    # Reads ~/.cloudstack.ini
    cs = CloudStack(**read_config())

    all_nics = collect_nics(cs)

    projects_container = cs.listProjects(listall=True)
    projects = projects_container["project"]

    for project in sorted(projects, key=lambda key: key["name"]):
        project_id = project["id"]
        all_nics = all_nics + collect_nics(cs, project_id)

    # pprint.pprint(all_nics)
    filtered_nics = filter_nics(all_nics, args)
    # pprint.pprint(filtered_nics)
    print_nics(filtered_nics, outputfile)

    if args.name_outputfile is not None:
        outputfile.close()
Beispiel #2
0
    def test_transform(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        cs.listVirtualMachines(foo=["foo", "bar"],
                               bar=[{'baz': 'blah', 'foo': 1000}],
                               bytes_param=b'blah')
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('ImJ/5F0P2RDL7yn4LdLnGcEx5WE=', qs['signature'][0])
        self.assertEqual('1000', qs['bar[0].foo'][0])
        self.assertEqual('blah', qs['bar[0].baz'][0])
        self.assertEqual('blah', qs['bytes_param'][0])
        self.assertEqual('foo,bar', qs['foo'][0])
Beispiel #3
0
    def __init__(
        self,
        key,
        secret,
        endpoint="https://api.exoscale.com/v1",
        max_retries=None,
        trace=False,
    ):
        super().__init__(
            endpoint=endpoint,
            key=key,
            secret=secret,
            max_retries=max_retries,
            trace=trace,
        )

        self.cs = CloudStack(
            key=key,
            secret=secret,
            endpoint=endpoint,
            session=self.session,
            headers={**self.session.headers, **{"User-Agent": self.user_agent}},
            trace=self.trace,
            fetch_result=True,
        )
Beispiel #4
0
    def test_request_params_casing(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        timeout=20, expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        machines = cs.listVirtualMachines(zoneId=2, templateId='3',
                                          temPlateidd='4', pageSize='10',
                                          fetch_list=True)
        self.assertEqual(machines, [])

        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=20, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('mMS7XALuGkCXk7kj5SywySku0Z0=', qs['signature'][0])
        self.assertEqual('3', qs['templateId'][0])
        self.assertEqual('4', qs['temPlateidd'][0])
Beispiel #5
0
    def __init__(self):

        parser = argparse.ArgumentParser()
        parser.add_argument('--host')
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--project')

        options = parser.parse_args()
        try:
            self.cs = CloudStack(**read_config())
        except CloudStackException as e:
            print("Error: Could not connect to CloudStack API", file=sys.stderr)

        project_id = ''
        if options.project:
            project_id = self.get_project_id(options.project)

        if options.host:
            data = self.get_host(options.host)
            print(json.dumps(data, indent=2))

        elif options.list:
            data = self.get_list()
            print(json.dumps(data, indent=2))
        else:
            print("usage: --list | --host <hostname> [--project <project>]",
                  file=sys.stderr)
            sys.exit(1)
Beispiel #6
0
Datei: tests.py Projekt: phsm/cs
    def test_signature_v3(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        expiration=600)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'createnetworkresponse': {},
        }
        cs.createNetwork(name="", display_text="")
        self.assertEqual(1, mock.call_count)

        [request], _ = mock.call_args

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('createNetwork', qs['command'][0])
        self.assertEqual('3', qs['signatureVersion'][0])

        expires = qs['expires'][0]
        # we ignore the timezone for Python2's lack of %z
        expires = datetime.datetime.strptime(expires[:19], EXPIRES_FORMAT[:-2])

        self.assertTrue(expires > datetime.datetime.utcnow(), expires)
Beispiel #7
0
 def __init__(self):
     super().__init__()
     self.cs = CloudStack(
         endpoint=os.getenv("CLOUDSTACK_API_ENDPOINT"),
         key=os.getenv("CLOUDSTACK_API_KEY"),
         secret=os.getenv("CLOUDSTACK_API_SECRET"),
     )
Beispiel #8
0
 def test_transform(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(foo=["foo", "bar"],
                            bar=[{
                                'baz': 'blah',
                                'foo': 1000
                            }],
                            bytes_param=b'blah')
     get.assert_called_once_with(
         'localhost',
         timeout=10,
         cert=None,
         verify=True,
         headers=None,
         params={
             'command': 'listVirtualMachines',
             'response': 'json',
             'bar[0].foo': '1000',
             'bar[0].baz': 'blah',
             'foo': 'foo,bar',
             'bytes_param': b'blah',
             'apiKey': 'foo',
             'signature': 'ImJ/5F0P2RDL7yn4LdLnGcEx5WE=',
         },
     )
Beispiel #9
0
    def test_request_params(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        timeout=20, expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        machines = cs.listVirtualMachines(listall='true',
                                          headers={'Accept-Encoding': 'br'})
        self.assertEqual(machines, {})

        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=20, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertEqual('br', request.headers['Accept-Encoding'])

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('B0d6hBsZTcFVCiioSxzwKA9Pke8=', qs['signature'][0])
        self.assertEqual('true', qs['listall'][0])
Beispiel #10
0
 def test_method(self, get, post):
     cs = CloudStack(endpoint='localhost',
                     key='foo',
                     secret='bar',
                     method='post')
     post.return_value.status_code = 200
     post.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(blah='brah')
     self.assertEqual(get.call_args_list, [])
     self.assertEqual(post.call_args_list, [
         call('localhost',
              timeout=10,
              verify=True,
              cert=None,
              headers=None,
              data={
                  'command': 'listVirtualMachines',
                  'blah': 'brah',
                  'apiKey': 'foo',
                  'response': 'json',
                  'signature': '58VvLSaVUqHnG9DhXNOAiDFwBoA=',
              })
     ])
Beispiel #11
0
 def test_transform_dict(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'scalevirtualmachineresponse': {},
     }
     cs.scaleVirtualMachine(id='a',
                            details={
                                'cpunumber': 1000,
                                'memory': '640k'
                            })
     get.assert_called_once_with(
         'localhost',
         timeout=10,
         cert=None,
         verify=True,
         headers=None,
         params={
             'command': 'scaleVirtualMachine',
             'response': 'json',
             'id': 'a',
             'details[0].cpunumber': '1000',
             'details[0].memory': '640k',
             'apiKey': 'foo',
             'signature': 'ZNl66z3gFhnsx2Eo3vvCIM0kAgI=',
         },
     )
Beispiel #12
0
 def test_request_params_casing(self, get):
     cs = CloudStack(endpoint='localhost',
                     key='foo',
                     secret='bar',
                     timeout=20)
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     machines = cs.listVirtualMachines(zoneId=2,
                                       templateId='3',
                                       temPlateidd='4',
                                       pageSize='10',
                                       fetch_list=True)
     self.assertEqual(machines, [])
     get.assert_called_once_with(
         'localhost',
         timeout=20,
         verify=True,
         cert=None,
         headers=None,
         params={
             'apiKey': 'foo',
             'response': 'json',
             'command': 'listVirtualMachines',
             'signature': 'mMS7XALuGkCXk7kj5SywySku0Z0=',
             'templateId': '3',
             'temPlateidd': '4',
             'zoneId': '2',
             'page': '1',
             'pageSize': '10',
         },
     )
Beispiel #13
0
Datei: tests.py Projekt: phsm/cs
    def test_transform_empty(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'createnetworkresponse': {},
        }
        cs.createNetwork(name="", display_text="")
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('createNetwork', qs['command'][0])
        self.assertEqual('CistTEiPt/4Rv1v4qSyILvPbhmg=', qs['signature'][0])
        self.assertEqual('', qs['name'][0])
        self.assertEqual('', qs['display_text'][0])
Beispiel #14
0
Datei: tests.py Projekt: phsm/cs
    def test_method(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        method='post',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        cs.listVirtualMachines(blah='brah')
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('POST', request.method)
        self.assertEqual('application/x-www-form-urlencoded',
                         request.headers['Content-Type'])

        qs = parse_qs(request.body, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('58VvLSaVUqHnG9DhXNOAiDFwBoA=', qs['signature'][0])
        self.assertEqual('brah', qs['blah'][0])
Beispiel #15
0
Datei: tests.py Projekt: phsm/cs
    def test_transform(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        cs.listVirtualMachines(foo=["foo", "bar"],
                               bar=[{
                                   'baz': 'blah',
                                   'foo': 1000
                               }],
                               bytes_param=b'blah')
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('ImJ/5F0P2RDL7yn4LdLnGcEx5WE=', qs['signature'][0])
        self.assertEqual('1000', qs['bar[0].foo'][0])
        self.assertEqual('blah', qs['bar[0].baz'][0])
        self.assertEqual('blah', qs['bytes_param'][0])
        self.assertEqual('foo,bar', qs['foo'][0])
Beispiel #16
0
Datei: tests.py Projekt: phsm/cs
    def test_transform_dict(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'scalevirtualmachineresponse': {},
        }
        cs.scaleVirtualMachine(id='a',
                               details={
                                   'cpunumber': 1000,
                                   'memory': '640k'
                               })
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('scaleVirtualMachine', qs['command'][0])
        self.assertEqual('ZNl66z3gFhnsx2Eo3vvCIM0kAgI=', qs['signature'][0])
        self.assertEqual('1000', qs['details[0].cpunumber'][0])
        self.assertEqual('640k', qs['details[0].memory'][0])
Beispiel #17
0
Datei: tests.py Projekt: phsm/cs
    def test_request_params_casing(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        timeout=20,
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        machines = cs.listVirtualMachines(zoneId=2,
                                          templateId='3',
                                          temPlateidd='4',
                                          pageSize='10',
                                          fetch_list=True)
        self.assertEqual(machines, [])

        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=20, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('mMS7XALuGkCXk7kj5SywySku0Z0=', qs['signature'][0])
        self.assertEqual('3', qs['templateId'][0])
        self.assertEqual('4', qs['temPlateidd'][0])
Beispiel #18
0
Datei: tests.py Projekt: phsm/cs
    def test_request_params(self, mock):
        cs = CloudStack(endpoint='https://localhost',
                        key='foo',
                        secret='bar',
                        timeout=20,
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        machines = cs.listVirtualMachines(listall='true',
                                          headers={'Accept-Encoding': 'br'})
        self.assertEqual(machines, {})

        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=20, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertEqual('br', request.headers['Accept-Encoding'])

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('B0d6hBsZTcFVCiioSxzwKA9Pke8=', qs['signature'][0])
        self.assertEqual('true', qs['listall'][0])
Beispiel #19
0
    def _connect(self):
        api_region = self.module.params.get('api_region') or os.environ.get(
            'CLOUDSTACK_REGION')
        try:
            config = read_config(api_region)
        except KeyError:
            config = {}

        api_config = {
            'endpoint':
            self.module.params.get('api_url') or config.get('endpoint'),
            'key':
            self.module.params.get('api_key') or config.get('key'),
            'secret':
            self.module.params.get('api_secret') or config.get('secret'),
            'timeout':
            self.module.params.get('api_timeout') or config.get('timeout'),
            'method':
            self.module.params.get('api_http_method') or config.get('method'),
        }
        self.result.update({
            'api_region': api_region,
            'api_url': api_config['endpoint'],
            'api_key': api_config['key'],
            'api_timeout': api_config['timeout'],
            'api_http_method': api_config['method'],
        })
        if not all(
            [api_config['endpoint'], api_config['key'], api_config['secret']]):
            self.module.fail_json(
                msg="Missing api credentials: can not authenticate",
                result=self.result)
        self.cs = CloudStack(**api_config)
Beispiel #20
0
class CloudStackChaotic(Chaotic):
    def __init__(self) -> None:
        self.cs = CloudStack(
            endpoint=CLOUDSTACK_API_ENDPOINT,
            key=CLOUDSTACK_API_KEY,
            secret=CLOUDSTACK_API_SECRET,
        )

    def action(self) -> None:
        tag = self.configs.get('tag')
        log.info(f"Querying with tag: {tag['key']}={tag['value']}")

        instances = self.cs.listVirtualMachines(
            tags=[tag],
            projectid=self.configs.get('projectid'),
            zoneid=self.configs.get('zoneid'),
            fetch_list=True,
        )
        if instances:
            instance = random.choice(instances)
            log.info(f"Choose server {instance['name']}")
            if not self.dry_run:
                log.info(f"Stopping server {instance['name']}")
                self.cs.stopVirtualMachine(id=instance['id'])
                wait_before_restart = int(
                    self.configs.get('wait_before_restart', 60))
                log.info(f"Sleeping for {wait_before_restart} seconds")
                time.sleep(wait_before_restart)

                log.info(f"Starting server {instance['name']}")
                self.cs.startVirtualMachine(id=instance['id'])
        else:
            log.info("No servers found")

        log.info(f"done")
Beispiel #21
0
 def test_request_params(self, get):
     cs = CloudStack(endpoint='localhost',
                     key='foo',
                     secret='bar',
                     timeout=20)
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     machines = cs.listVirtualMachines(listall='true',
                                       headers={'Accept-Encoding': 'br'})
     self.assertEqual(machines, {})
     get.assert_called_once_with(
         'localhost',
         timeout=20,
         verify=True,
         cert=None,
         headers={
             'Accept-Encoding': 'br',
         },
         params={
             'apiKey': 'foo',
             'response': 'json',
             'command': 'listVirtualMachines',
             'listall': 'true',
             'signature': 'B0d6hBsZTcFVCiioSxzwKA9Pke8=',
         },
     )
Beispiel #22
0
 def test_transformt(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(foo=["foo", "bar"],
                            bar=[{
                                'baz': 'blah',
                                'foo': 'meh'
                            }])
     get.assert_called_once_with(
         'localhost',
         timeout=10,
         cert=None,
         verify=True,
         params={
             'command': 'listVirtualMachines',
             'response': 'json',
             'bar[0].foo': 'meh',
             'bar[0].baz': 'blah',
             'foo': 'foo,bar',
             'apiKey': 'foo',
             'signature': 'UGUVEfCOfGfOlqoTj1D2m5adr2g=',
         },
     )
Beispiel #23
0
 def test_request_params_casing(self, get):
     cs = CloudStack(endpoint='localhost',
                     key='foo',
                     secret='bar',
                     timeout=20)
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     machines = cs.listVirtualMachines(zoneId=2,
                                       templateId='3',
                                       temPlateidd='4')
     self.assertEqual(machines, {})
     get.assert_called_once_with(
         'localhost',
         timeout=20,
         verify=True,
         cert=None,
         params={
             'apiKey': 'foo',
             'response': 'json',
             'command': 'listVirtualMachines',
             'signature': 'dMRxAZcs2OPK15WUulzUtnrLWD0=',
             'templateId': '3',
             'temPlateidd': '4',
             'zoneId': '2'
         },
     )
    def __init__(self):
        tempdict = {}
        path = os.path.dirname(os.path.abspath(__file__))
        # Read config
        try:
            f = open(path + "/../cloudstack.config", "r")
            config = f.readlines()
        except:
            print("Somethings wrong with your config file")
            sys.exit(2)

        for entry in config:
            entry = entry.replace("\n", "")
            temarray = entry.split("=")
            tempdict[temarray[0]] = temarray[1]

        if tempdict['verify'] is None:
            tempdict['verify'] = False

        self.cs = CloudStack(
            endpoint=tempdict['endpoint'],
            key=tempdict['key'],
            secret=tempdict['secret'],
            verify=False
        )
Beispiel #25
0
 def __init__(self, zoneid, debug):
     self.zoneid = zoneid
     self._debug = debug
     self._config = read_config()
     self._cs = CloudStack(**self._config)
     self._vms = {}
     self._zones = {}
     self._lbs = {}
     self._templates = {}
     self._offerings = {}
     self._networks = {}
Beispiel #26
0
    def __init__(self, endpoint=None, key=None, secret=None, profile=None, timeout=60, dry_run=True,
                 log_to_slack=False):
        if profile:
            (endpoint, key, secret) = _load_cloud_monkey_profile(profile)

        self.endpoint = endpoint
        self.key = key
        self.secret = secret
        self.timeout = timeout
        self.dry_run = dry_run
        self.log_to_slack = log_to_slack
        self.cs = CloudStack(self.endpoint, self.key, self.secret, self.timeout)
 def __init__(self, key=None, secret=None, endpoint=None):
     if key is None or secret is None or endpoint is None:
         self._config = read_config()
         self._cs = CloudStack(**self._config)
     else:
         self._cs = CloudStack(key=key, secret=secret, endpoint=endpoint)
     self.vms = []
     self.zones = []
     self.lbs = []
     self.tps = []
     self.nws = []
     self.svs = []
    def _connect(self):
        api_key = self.module.params.get("api_key")
        api_secret = self.module.params.get("secret_key")
        api_url = self.module.params.get("api_url")
        api_http_method = self.module.params.get("api_http_method")
        api_timeout = self.module.params.get("api_timeout")

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method
            )
        else:
            self.cs = CloudStack(**read_config())
Beispiel #29
0
    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(endpoint=api_url,
                                 key=api_key,
                                 secret=api_secret,
                                 method=api_http_method)
        else:
            self.cs = CloudStack(**read_config())
Beispiel #30
0
 def test_encoding(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(listall=1, unicode_param=u'éèààû')
     get.assert_called_once_with('localhost', timeout=10, params={
         'apiKey': 'foo',
         'response': 'json',
         'command': 'listVirtualMachines',
         'listall': '1',
         'unicode_param': u'éèààû',
         'signature': 'gABU/KFJKD3FLAgKDuxQoryu4sA='})
    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                method=api_http_method
                )
        else:
            self.cs = CloudStack(**read_config())
Beispiel #32
0
 def test_request_params(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar',
                     timeout=20)
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     machines = cs.listVirtualMachines(listall='true')
     self.assertEqual(machines, {})
     get.assert_called_once_with('localhost', timeout=20, params={
         'apiKey': 'foo',
         'response': 'json',
         'command': 'listVirtualMachines',
         'listall': 'true',
         'signature': 'B0d6hBsZTcFVCiioSxzwKA9Pke8='})
def cs_list(method, key_name, **kwargs):
    timeout = 300
    cs = CloudStack(endpoint=API_MONITORS,
                    key=APIKEY_MONITORS,
                    secret=SECRET_MONITORS,
                    timeout=timeout)
    querypage = 1
    querypagesize = 500
    values = getattr(cs, method)(listall='true',
                                 pagesize=querypagesize,
                                 page=querypage,
                                 **kwargs).get(key_name, [])

    if len(values) == querypagesize:
        all_values = []
        query_tmp = values
        while len(query_tmp):
            all_values.extend(query_tmp)
            querypage = querypage + 1
            query_tmp = getattr(cs, method)(listall='true',
                                            pagesize=querypagesize,
                                            page=querypage,
                                            **kwargs).get(key_name, [])
        values = all_values

    return values
Beispiel #34
0
 def __init__(self):
     super().__init__()
     self.cs = CloudStack(
         endpoint="https://api.exoscale.com/compute",
         key=os.getenv("EXOSCALE_API_KEY"),
         secret=os.getenv("EXOSCALE_API_SECRET"),
     )
Beispiel #35
0
    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('api_secret')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')
        api_timeout = self.module.params.get('api_timeout')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(endpoint=api_url,
                                 key=api_key,
                                 secret=api_secret,
                                 timeout=api_timeout,
                                 method=api_http_method)
        else:
            api_region = self.module.params.get('api_region', 'cloudstack')
            self.cs = CloudStack(**read_config(api_region))
Beispiel #36
0
 def test_transform_empty(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'createnetworkresponse': {},
     }
     cs.createNetwork(name="", display_text="")
     get.assert_called_once_with(
         'localhost', timeout=10, cert=None, verify=True, params={
             'command': 'createNetwork',
             'response': 'json',
             'name': '',
             'display_text': '',
             'apiKey': 'foo',
             'signature': 'CistTEiPt/4Rv1v4qSyILvPbhmg=',
         },
     )
    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')
        api_timeout = self.module.params.get('api_timeout')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                timeout=api_timeout,
                method=api_http_method)
        else:
            api_region = self.module.params.get('api_region', 'cloudstack')
            self.cs = CloudStack(**read_config(api_region))
Beispiel #38
0
 def test_transformt(self, get):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar')
     get.return_value.status_code = 200
     get.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(foo=["foo", "bar"],
                            bar=[{'baz': 'blah', 'foo': 'meh'}])
     get.assert_called_once_with('localhost', timeout=10, params={
         'command': 'listVirtualMachines',
         'response': 'json',
         'bar[0].foo': 'meh',
         'bar[0].baz': 'blah',
         'foo': 'foo,bar',
         'apiKey': 'foo',
         'signature': 'UGUVEfCOfGfOlqoTj1D2m5adr2g=',
     })
Beispiel #39
0
 def test_method(self, get, post):
     cs = CloudStack(endpoint='localhost', key='foo', secret='bar',
                     method='post')
     post.return_value.status_code = 200
     post.return_value.json.return_value = {
         'listvirtualmachinesresponse': {},
     }
     cs.listVirtualMachines(blah='brah')
     self.assertEqual(get.call_args_list, [])
     self.assertEqual(post.call_args_list, [
         call('localhost', timeout=10, data={
             'command': 'listVirtualMachines',
             'blah': 'brah',
             'apiKey': 'foo',
             'response': 'json',
             'signature': '58VvLSaVUqHnG9DhXNOAiDFwBoA=',
         })]
     )
Beispiel #40
0
    def test_encoding(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        cs.listVirtualMachines(listall=1, unicode_param=u'éèààû')
        self.assertEqual(1, mock.call_count)

        [request], _ = mock.call_args

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('gABU/KFJKD3FLAgKDuxQoryu4sA=', qs['signature'][0])
        self.assertEqual('éèààû', qs['unicode_param'][0])
Beispiel #41
0
    def test_method(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        method='post', expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'listvirtualmachinesresponse': {},
        }
        cs.listVirtualMachines(blah='brah')
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('POST', request.method)
        self.assertEqual('application/x-www-form-urlencoded',
                         request.headers['Content-Type'])

        qs = parse_qs(request.body, True)

        self.assertEqual('listVirtualMachines', qs['command'][0])
        self.assertEqual('58VvLSaVUqHnG9DhXNOAiDFwBoA=', qs['signature'][0])
        self.assertEqual('brah', qs['blah'][0])
Beispiel #42
0
    def test_transform_empty(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'createnetworkresponse': {},
        }
        cs.createNetwork(name="", display_text="")
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('createNetwork', qs['command'][0])
        self.assertEqual('CistTEiPt/4Rv1v4qSyILvPbhmg=', qs['signature'][0])
        self.assertEqual('', qs['name'][0])
        self.assertEqual('', qs['display_text'][0])
Beispiel #43
0
    def test_transform_dict(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        expiration=-1)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'scalevirtualmachineresponse': {},
        }
        cs.scaleVirtualMachine(id='a',
                               details={'cpunumber': 1000, 'memory': '640k'})
        self.assertEqual(1, mock.call_count)

        [request], kwargs = mock.call_args

        self.assertEqual(dict(cert=None, timeout=10, verify=True), kwargs)
        self.assertEqual('GET', request.method)
        self.assertFalse(request.headers)

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('scaleVirtualMachine', qs['command'][0])
        self.assertEqual('ZNl66z3gFhnsx2Eo3vvCIM0kAgI=', qs['signature'][0])
        self.assertEqual('1000', qs['details[0].cpunumber'][0])
        self.assertEqual('640k', qs['details[0].memory'][0])
Beispiel #44
0
    def test_signature_v3(self, mock):
        cs = CloudStack(endpoint='https://localhost', key='foo', secret='bar',
                        expiration=600)
        mock.return_value.status_code = 200
        mock.return_value.json.return_value = {
            'createnetworkresponse': {},
        }
        cs.createNetwork(name="", display_text="")
        self.assertEqual(1, mock.call_count)

        [request], _ = mock.call_args

        url = urlparse(request.url)
        qs = parse_qs(url.query, True)

        self.assertEqual('createNetwork', qs['command'][0])
        self.assertEqual('3', qs['signatureVersion'][0])

        expires = qs['expires'][0]
        # we ignore the timezone for Python2's lack of %z
        expires = datetime.datetime.strptime(expires[:19],
                                             EXPIRES_FORMAT[:-2])

        self.assertTrue(expires > datetime.datetime.utcnow(), expires)
Beispiel #45
0
    def __init__(self):

        parser = argparse.ArgumentParser()
        parser.add_argument('--host')
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--tag', help="Filter machines by a tag. Should be in the form key=value.")
        parser.add_argument('--project')
        parser.add_argument('--domain')

        options = parser.parse_args()
        try:
            self.cs = CloudStack(**read_config())
        except CloudStackException:
            print("Error: Could not connect to CloudStack API", file=sys.stderr)

        domain_id = None
        if options.domain:
            domain_id = self.get_domain_id(options.domain)

        project_id = None
        if options.project:
            project_id = self.get_project_id(options.project, domain_id)

        if options.host:
            data = self.get_host(options.host, project_id, domain_id)
            print(json.dumps(data, indent=2))

        elif options.list:
            tags = dict()
            if options.tag:
                tags['tags[0].key'], tags['tags[0].value'] = options.tag.split('=')
            data = self.get_list(project_id, domain_id, **tags)
            print(json.dumps(data, indent=2))
        else:
            print("usage: --list [--tag <tag>] | --host <hostname> [--project <project>] [--domain <domain_path>]",
                  file=sys.stderr)
            sys.exit(1)
Beispiel #46
0
class CloudStackInventory(object):
    def __init__(self):

        parser = argparse.ArgumentParser()
        parser.add_argument('--host')
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--tag', help="Filter machines by a tag. Should be in the form key=value.")
        parser.add_argument('--project')
        parser.add_argument('--domain')

        options = parser.parse_args()
        try:
            self.cs = CloudStack(**read_config())
        except CloudStackException:
            print("Error: Could not connect to CloudStack API", file=sys.stderr)

        domain_id = None
        if options.domain:
            domain_id = self.get_domain_id(options.domain)

        project_id = None
        if options.project:
            project_id = self.get_project_id(options.project, domain_id)

        if options.host:
            data = self.get_host(options.host, project_id, domain_id)
            print(json.dumps(data, indent=2))

        elif options.list:
            tags = dict()
            if options.tag:
                tags['tags[0].key'], tags['tags[0].value'] = options.tag.split('=')
            data = self.get_list(project_id, domain_id, **tags)
            print(json.dumps(data, indent=2))
        else:
            print("usage: --list [--tag <tag>] | --host <hostname> [--project <project>] [--domain <domain_path>]",
                  file=sys.stderr)
            sys.exit(1)

    def get_domain_id(self, domain):
        domains = self.cs.listDomains(listall=True)
        if domains:
            for d in domains['domain']:
                if d['path'].lower() == domain.lower():
                    return d['id']
        print("Error: Domain %s not found." % domain, file=sys.stderr)
        sys.exit(1)

    def get_project_id(self, project, domain_id=None):
        projects = self.cs.listProjects(domainid=domain_id)
        if projects:
            for p in projects['project']:
                if p['name'] == project or p['id'] == project:
                    return p['id']
        print("Error: Project %s not found." % project, file=sys.stderr)
        sys.exit(1)

    def get_host(self, name, project_id=None, domain_id=None, **kwargs):
        hosts = self.cs.listVirtualMachines(projectid=project_id, domainid=domain_id, fetch_list=True, **kwargs)
        data = {}
        if not hosts:
            return data
        for host in hosts:
            host_name = host['displayname']
            if name == host_name:
                data['zone'] = host['zonename']
                if 'group' in host:
                    data['group'] = host['group']
                data['state'] = host['state']
                data['service_offering'] = host['serviceofferingname']
                data['affinity_group'] = host['affinitygroup']
                data['security_group'] = host['securitygroup']
                data['cpu_number'] = host['cpunumber']
                data['cpu_speed'] = host['cpuspeed']
                if 'cpuused' in host:
                    data['cpu_used'] = host['cpuused']
                data['memory'] = host['memory']
                data['tags'] = host['tags']
                data['hypervisor'] = host['hypervisor']
                data['created'] = host['created']
                data['nic'] = []
                for nic in host['nic']:
                    data['nic'].append({
                        'ip': nic['ipaddress'],
                        'mac': nic['macaddress'],
                        'netmask': nic['netmask'],
                        'gateway': nic['gateway'],
                        'type': nic['type'],
                    })
                    if nic['isdefault']:
                        data['default_ip'] = nic['ipaddress']
                break
        return data

    def get_list(self, project_id=None, domain_id=None, **kwargs):
        data = {
            'all': {
                'hosts': [],
            },
            '_meta': {
                'hostvars': {},
            },
        }

        groups = self.cs.listInstanceGroups(projectid=project_id, domainid=domain_id)
        if groups:
            for group in groups['instancegroup']:
                group_name = group['name']
                if group_name and group_name not in data:
                    data[group_name] = {
                        'hosts': []
                    }

        hosts = self.cs.listVirtualMachines(projectid=project_id, domainid=domain_id, fetch_list=True, **kwargs)
        if not hosts:
            return data
        for host in hosts:
            host_name = host['displayname']
            data['all']['hosts'].append(host_name)
            data['_meta']['hostvars'][host_name] = {}

            # Make a group per zone
            data['_meta']['hostvars'][host_name]['zone'] = host['zonename']
            group_name = host['zonename']
            if group_name not in data:
                data[group_name] = {
                    'hosts': []
                }
            data[group_name]['hosts'].append(host_name)

            if 'group' in host:
                data['_meta']['hostvars'][host_name]['group'] = host['group']
            data['_meta']['hostvars'][host_name]['state'] = host['state']
            data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname']
            data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup']
            data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup']
            data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber']
            data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed']
            if 'cpuused' in host:
                data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused']
            data['_meta']['hostvars'][host_name]['created'] = host['created']
            data['_meta']['hostvars'][host_name]['memory'] = host['memory']
            data['_meta']['hostvars'][host_name]['tags'] = host['tags']
            data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor']
            data['_meta']['hostvars'][host_name]['created'] = host['created']
            data['_meta']['hostvars'][host_name]['nic'] = []
            for nic in host['nic']:
                data['_meta']['hostvars'][host_name]['nic'].append({
                    'ip': nic['ipaddress'],
                    'mac': nic['macaddress'],
                    'netmask': nic['netmask'],
                    'gateway': nic['gateway'],
                    'type': nic['type'],
                })
                if nic['isdefault']:
                    data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress']

            group_name = ''
            if 'group' in host:
                group_name = host['group']

            if group_name and group_name in data:
                data[group_name]['hosts'].append(host_name)
        return data
class AnsibleCloudStack(object):
    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.result = {
            'changed': False,
        }

        # Common returns, will be merged with self.returns
        # search_for_key: replace_with_key
        self.common_returns = {
            'id': 'id',
            'name': 'name',
            'created': 'created',
            'zonename': 'zone',
            'state': 'state',
            'project': 'project',
            'account': 'account',
            'domain': 'domain',
            'displaytext': 'display_text',
            'displayname': 'display_name',
            'description': 'description',
        }

        # Init returns dict for use in subclasses
        self.returns = {}
        # these values will be casted to int
        self.returns_to_int = {}

        self.module = module
        self._connect()

        self.domain = None
        self.account = None
        self.project = None
        self.ip_address = None
        self.zone = None
        self.vm = None
        self.os_type = None
        self.hypervisor = None
        self.capabilities = None
        self.tags = None

    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')
        api_timeout = self.module.params.get('api_timeout')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                timeout=api_timeout,
                method=api_http_method)
        else:
            api_region = self.module.params.get('api_region', 'cloudstack')
            self.cs = CloudStack(**read_config(api_region))

    def get_or_fallback(self, key=None, fallback_key=None):
        value = self.module.params.get(key)
        if not value:
            value = self.module.params.get(fallback_key)
        return value

    # TODO: for backward compatibility only, remove if not used anymore
    def _has_changed(self, want_dict, current_dict, only_keys=None):
        return self.has_changed(
            want_dict=want_dict,
            current_dict=current_dict,
            only_keys=only_keys)

    def has_changed(self, want_dict, current_dict, only_keys=None):
        for key, value in want_dict.iteritems():

            # Optionally limit by a list of keys
            if only_keys and key not in only_keys:
                continue

            # Skip None values
            if value is None:
                continue

            if key in current_dict:

                # API returns string for int in some cases, just to make sure
                if isinstance(value, int):
                    current_dict[key] = int(current_dict[key])
                elif isinstance(value, str):
                    current_dict[key] = str(current_dict[key])

                # Only need to detect a singe change, not every item
                if value != current_dict[key]:
                    return True
        return False

    def _get_by_key(self, key=None, my_dict=None):
        if my_dict is None:
            my_dict = {}
        if key:
            if key in my_dict:
                return my_dict[key]
            self.module.fail_json(
                msg="Something went wrong: %s not found" % key)
        return my_dict

    def get_project(self, key=None):
        if self.project:
            return self._get_by_key(key, self.project)

        project = self.module.params.get('project')
        if not project:
            return None
        args = {}
        args['account'] = self.get_account(key='name')
        args['domainid'] = self.get_domain(key='id')
        projects = self.cs.listProjects(**args)
        if projects:
            for p in projects['project']:
                if project.lower() in [p['name'].lower(), p['id']]:
                    self.project = p
                    return self._get_by_key(key, self.project)
        self.module.fail_json(msg="project '%s' not found" % project)

    def get_ip_address(self, key=None):
        if self.ip_address:
            return self._get_by_key(key, self.ip_address)

        ip_address = self.module.params.get('ip_address')
        if not ip_address:
            self.module.fail_json(
                msg="IP address param 'ip_address' is required")

        args = {}
        args['ipaddress'] = ip_address
        args['account'] = self.get_account(key='name')
        args['domainid'] = self.get_domain(key='id')
        args['projectid'] = self.get_project(key='id')
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(
                msg="IP address '%s' not found" % args['ipaddress'])

        self.ip_address = ip_addresses['publicipaddress'][0]
        return self._get_by_key(key, self.ip_address)

    def get_vm(self, key=None):
        if self.vm:
            return self._get_by_key(key, self.vm)

        vm = self.module.params.get('vm')
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        args = {}
        args['account'] = self.get_account(key='name')
        args['domainid'] = self.get_domain(key='id')
        args['projectid'] = self.get_project(key='id')
        args['zoneid'] = self.get_zone(key='id')
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms['virtualmachine']:
                if vm in [v['name'], v['displayname'], v['id']]:
                    self.vm = v
                    return self._get_by_key(key, self.vm)
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)

    def get_zone(self, key=None):
        if self.zone:
            return self._get_by_key(key, self.zone)

        zone = self.module.params.get('zone')
        zones = self.cs.listZones()

        # use the first zone if no zone param given
        if not zone:
            self.zone = zones['zone'][0]
            return self._get_by_key(key, self.zone)

        if zones:
            for z in zones['zone']:
                if zone in [z['name'], z['id']]:
                    self.zone = z
                    return self._get_by_key(key, self.zone)
        self.module.fail_json(msg="zone '%s' not found" % zone)

    def get_os_type(self, key=None):
        if self.os_type:
            return self._get_by_key(key, self.zone)

        os_type = self.module.params.get('os_type')
        if not os_type:
            return None

        os_types = self.cs.listOsTypes()
        if os_types:
            for o in os_types['ostype']:
                if os_type in [o['description'], o['id']]:
                    self.os_type = o
                    return self._get_by_key(key, self.os_type)
        self.module.fail_json(msg="OS type '%s' not found" % os_type)

    def get_hypervisor(self):
        if self.hypervisor:
            return self.hypervisor

        hypervisor = self.module.params.get('hypervisor')
        hypervisors = self.cs.listHypervisors()

        # use the first hypervisor if no hypervisor param given
        if not hypervisor:
            self.hypervisor = hypervisors['hypervisor'][0]['name']
            return self.hypervisor

        for h in hypervisors['hypervisor']:
            if hypervisor.lower() == h['name'].lower():
                self.hypervisor = h['name']
                return self.hypervisor
        self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)

    def get_account(self, key=None):
        if self.account:
            return self._get_by_key(key, self.account)

        account = self.module.params.get('account')
        if not account:
            return None

        domain = self.module.params.get('domain')
        if not domain:
            self.module.fail_json(msg="Account must be specified with Domain")

        args = {}
        args['name'] = account
        args['domainid'] = self.get_domain(key='id')
        args['listall'] = True
        accounts = self.cs.listAccounts(**args)
        if accounts:
            self.account = accounts['account'][0]
            return self._get_by_key(key, self.account)
        self.module.fail_json(msg="Account '%s' not found" % account)

    def get_domain(self, key=None):
        if self.domain:
            return self._get_by_key(key, self.domain)

        domain = self.module.params.get('domain')
        if not domain:
            return None

        args = {}
        args['listall'] = True
        domains = self.cs.listDomains(**args)
        if domains:
            for d in domains['domain']:
                if d['path'].lower() in [
                        domain.lower(), "root/" + domain.lower(),
                        "root" + domain.lower()
                ]:
                    self.domain = d
                    return self._get_by_key(key, self.domain)
        self.module.fail_json(msg="Domain '%s' not found" % domain)

    def get_tags(self, resource=None):
        if not self.tags:
            args = {}
            args['projectid'] = self.get_project(key='id')
            args['account'] = self.get_account(key='name')
            args['domainid'] = self.get_domain(key='id')
            args['resourceid'] = resource['id']
            response = self.cs.listTags(**args)
            self.tags = response.get('tag', [])

        existing_tags = []
        if self.tags:
            for tag in self.tags:
                existing_tags.append({
                    'key': tag['key'],
                    'value': tag['value']
                })
        return existing_tags

    def _process_tags(self, resource, resource_type, tags, operation="create"):
        if tags:
            self.result['changed'] = True
            if not self.module.check_mode:
                args = {}
                args['resourceids'] = resource['id']
                args['resourcetype'] = resource_type
                args['tags'] = tags
                if operation == "create":
                    response = self.cs.createTags(**args)
                else:
                    response = self.cs.deleteTags(**args)
                self.poll_job(response)

    def _tags_that_should_exist_or_be_updated(self, resource, tags):
        existing_tags = self.get_tags(resource)
        return [tag for tag in tags if tag not in existing_tags]

    def _tags_that_should_not_exist(self, resource, tags):
        existing_tags = self.get_tags(resource)
        return [tag for tag in existing_tags if tag not in tags]

    def ensure_tags(self, resource, resource_type=None):
        if not resource_type or not resource:
            self.module.fail_json(
                msg="Error: Missing resource or resource_type for tags.")

        if 'tags' in resource:
            tags = self.module.params.get('tags')
            if tags is not None:
                self._process_tags(
                    resource,
                    resource_type,
                    self._tags_that_should_not_exist(resource, tags),
                    operation="delete")
                self._process_tags(
                    resource, resource_type,
                    self._tags_that_should_exist_or_be_updated(resource, tags))
                self.tags = None
                resource['tags'] = self.get_tags(resource)
        return resource

    def get_capabilities(self, key=None):
        if self.capabilities:
            return self._get_by_key(key, self.capabilities)
        capabilities = self.cs.listCapabilities()
        self.capabilities = capabilities['capability']
        return self._get_by_key(key, self.capabilities)

    # TODO: for backward compatibility only, remove if not used anymore
    def _poll_job(self, job=None, key=None):
        return self.poll_job(job=job, key=key)

    def poll_job(self, job=None, key=None):
        if 'jobid' in job:
            while True:
                res = self.cs.queryAsyncJobResult(jobid=job['jobid'])
                if res['jobstatus'] != 0 and 'jobresult' in res:
                    if 'errortext' in res['jobresult']:
                        self.module.fail_json(
                            msg="Failed: '%s'" % res['jobresult']['errortext'])
                    if key and key in res['jobresult']:
                        job = res['jobresult'][key]
                    break
                time.sleep(2)
        return job

    def get_result(self, resource):
        if resource:
            returns = self.common_returns.copy()
            returns.update(self.returns)
            for search_key, return_key in returns.iteritems():
                if search_key in resource:
                    self.result[return_key] = resource[search_key]

            # Bad bad API does not always return int when it should.
            for search_key, return_key in self.returns_to_int.iteritems():
                if search_key in resource:
                    self.result[return_key] = int(resource[search_key])

            # Special handling for tags
            if 'tags' in resource:
                self.result['tags'] = []
                for tag in resource['tags']:
                    result_tag = {}
                    result_tag['key'] = tag['key']
                    result_tag['value'] = tag['value']
                    self.result['tags'].append(result_tag)
        return self.result
class CloudStackInventory(object):
    def __init__(self):

        parser = argparse.ArgumentParser()
        parser.add_argument('--host')
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--project')

        options = parser.parse_args()
        try:
            self.cs = CloudStack(**read_config())
        except CloudStackException as e:
            print("Error: Could not connect to CloudStack API", file=sys.stderr)

        project_id = ''
        if options.project:
            project_id = self.get_project_id(options.project)

        if options.host:
            data = self.get_host(options.host)
            print(json.dumps(data, indent=2))

        elif options.list:
            data = self.get_list()
            print(json.dumps(data, indent=2))
        else:
            print("usage: --list | --host <hostname> [--project <project>]",
                  file=sys.stderr)
            sys.exit(1)


    def get_project_id(self, project):
        projects = self.cs.listProjects()
        if projects:
            for p in projects['project']:
                if p['name'] == project or p['id'] == project:
                    return p['id']
        print("Error: Project %s not found." % project, file=sys.stderr)
        sys.exit(1)


    def get_host(self, name, project_id=''):
        hosts = self.cs.listVirtualMachines(projectid=project_id)
        data = {}
        if not hosts:
            return data
        for host in hosts['virtualmachine']:
            host_name = host['displayname']
            if name == host_name:
                data['zone'] = host['zonename']
                if 'group' in host:
                    data['group'] = host['group']
                data['state'] = host['state']
                data['service_offering'] = host['serviceofferingname']
                data['affinity_group'] = host['affinitygroup']
                data['security_group'] = host['securitygroup']
                data['cpu_number'] = host['cpunumber']
                data['cpu_speed'] = host['cpuspeed']
                if 'cpuused' in host:
                    data['cpu_used'] = host['cpuused']
                data['memory'] = host['memory']
                data['tags'] = host['tags']
                data['hypervisor'] = host['hypervisor']
                data['created'] = host['created']
                data['nic'] = []
                for nic in host['nic']:
                    data['nic'].append({
                        'ip': nic['ipaddress'],
                        'mac': nic['macaddress'],
                        'netmask': nic['netmask'],
                        'gateway': nic['gateway'],
                        'type': nic['type'],
                    })
                    if nic['isdefault']:
                        data['default_ip'] = nic['ipaddress']
                break;
        return data


    def get_list(self, project_id=''):
        data = {
            'all': {
                'hosts': [],
                },
            '_meta': {
                'hostvars': {},
                },
            }

        groups = self.cs.listInstanceGroups(projectid=project_id)
        if groups:
            for group in groups['instancegroup']:
                group_name = group['name']
                if group_name and not group_name in data:
                    data[group_name] = {
                            'hosts': []
                        }

        hosts = self.cs.listVirtualMachines(projectid=project_id)
        if not hosts:
            return data
        for host in hosts['virtualmachine']:
            host_name = host['displayname']
            data['all']['hosts'].append(host_name)
            data['_meta']['hostvars'][host_name] = {}
            data['_meta']['hostvars'][host_name]['zone'] = host['zonename']
            if 'group' in host:
                data['_meta']['hostvars'][host_name]['group'] = host['group']
            data['_meta']['hostvars'][host_name]['state'] = host['state']
            data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname']
            data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup']
            data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup']
            data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber']
            data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed']
            if 'cpuused' in host:
                data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused']
            data['_meta']['hostvars'][host_name]['created'] = host['created']
            data['_meta']['hostvars'][host_name]['memory'] = host['memory']
            data['_meta']['hostvars'][host_name]['tags'] = host['tags']
            data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor']
            data['_meta']['hostvars'][host_name]['created'] = host['created']
            data['_meta']['hostvars'][host_name]['nic'] = []
            for nic in host['nic']:
                data['_meta']['hostvars'][host_name]['nic'].append({
                    'ip': nic['ipaddress'],
                    'mac': nic['macaddress'],
                    'netmask': nic['netmask'],
                    'gateway': nic['gateway'],
                    'type': nic['type'],
                    })
                if nic['isdefault']:
                    data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress']

            group_name = ''
            if 'group' in host:
                group_name = host['group']

            if group_name and group_name in data:
                data[group_name]['hosts'].append(host_name)
        return data
class AnsibleCloudStack:

    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.module = module
        self._connect()

        self.project_id = None
        self.ip_address_id = None
        self.zone_id = None
        self.vm_id = None
        self.os_type_id = None
        self.hypervisor = None


    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                method=api_http_method
                )
        else:
            self.cs = CloudStack(**read_config())


    def get_project_id(self):
        if self.project_id:
            return self.project_id

        project = self.module.params.get('project')
        if not project:
            return None

        projects = self.cs.listProjects()
        if projects:
            for p in projects['project']:
                if project in [ p['name'], p['displaytext'], p['id'] ]:
                    self.project_id = p['id']
                    return self.project_id
        self.module.fail_json(msg="project '%s' not found" % project)


    def get_ip_address_id(self):
        if self.ip_address_id:
            return self.ip_address_id

        ip_address = self.module.params.get('ip_address')
        if not ip_address:
            self.module.fail_json(msg="IP address param 'ip_address' is required")

        args = {}
        args['ipaddress'] = ip_address
        args['projectid'] = self.get_project_id()
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress'])

        self.ip_address_id = ip_addresses['publicipaddress'][0]['id']
        return self.ip_address_id


    def get_vm_id(self):
        if self.vm_id:
            return self.vm_id

        vm = self.module.params.get('vm')
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        args = {}
        args['projectid'] = self.get_project_id()
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms['virtualmachine']:
                if vm in [ v['name'], v['displaytext'] v['id'] ]:
                    self.vm_id = v['id']
                    return self.vm_id
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)
class AnsibleCloudStack(object):
    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.result = {"changed": False}

        # Common returns, will be merged with self.returns
        # search_for_key: replace_with_key
        self.common_returns = {
            "id": "id",
            "name": "name",
            "created": "created",
            "zonename": "zone",
            "state": "state",
            "project": "project",
            "account": "account",
            "domain": "domain",
            "displaytext": "display_text",
            "displayname": "display_name",
            "description": "description",
        }

        # Init returns dict for use in subclasses
        self.returns = {}

        self.module = module
        self._connect()

        self.domain = None
        self.account = None
        self.project = None
        self.ip_address = None
        self.zone = None
        self.vm = None
        self.os_type = None
        self.hypervisor = None
        self.capabilities = None

    def _connect(self):
        api_key = self.module.params.get("api_key")
        api_secret = self.module.params.get("secret_key")
        api_url = self.module.params.get("api_url")
        api_http_method = self.module.params.get("api_http_method")
        api_timeout = self.module.params.get("api_timeout")

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method
            )
        else:
            api_region = self.module.params.get("api_region", "cloudstack")
            self.cs = CloudStack(**read_config(api_region))

    def get_or_fallback(self, key=None, fallback_key=None):
        value = self.module.params.get(key)
        if not value:
            value = self.module.params.get(fallback_key)
        return value

    # TODO: for backward compatibility only, remove if not used anymore
    def _has_changed(self, want_dict, current_dict, only_keys=None):
        return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys)

    def has_changed(self, want_dict, current_dict, only_keys=None):
        for key, value in want_dict.iteritems():

            # Optionally limit by a list of keys
            if only_keys and key not in only_keys:
                continue

            # Skip None values
            if value is None:
                continue

            if key in current_dict:

                # API returns string for int in some cases, just to make sure
                if isinstance(value, int):
                    current_dict[key] = int(current_dict[key])
                elif isinstance(value, str):
                    current_dict[key] = str(current_dict[key])

                # Only need to detect a singe change, not every item
                if value != current_dict[key]:
                    return True
        return False

    def _get_by_key(self, key=None, my_dict={}):
        if key:
            if key in my_dict:
                return my_dict[key]
            self.module.fail_json(msg="Something went wrong: %s not found" % key)
        return my_dict

    def get_project(self, key=None):
        if self.project:
            return self._get_by_key(key, self.project)

        project = self.module.params.get("project")
        if not project:
            return None
        args = {}
        args["account"] = self.get_account(key="name")
        args["domainid"] = self.get_domain(key="id")
        projects = self.cs.listProjects(**args)
        if projects:
            for p in projects["project"]:
                if project.lower() in [p["name"].lower(), p["id"]]:
                    self.project = p
                    return self._get_by_key(key, self.project)
        self.module.fail_json(msg="project '%s' not found" % project)

    def get_ip_address(self, key=None):
        if self.ip_address:
            return self._get_by_key(key, self.ip_address)

        ip_address = self.module.params.get("ip_address")
        if not ip_address:
            self.module.fail_json(msg="IP address param 'ip_address' is required")

        args = {}
        args["ipaddress"] = ip_address
        args["account"] = self.get_account(key="name")
        args["domainid"] = self.get_domain(key="id")
        args["projectid"] = self.get_project(key="id")
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(msg="IP address '%s' not found" % args["ipaddress"])

        self.ip_address = ip_addresses["publicipaddress"][0]
        return self._get_by_key(key, self.ip_address)

    def get_vm(self, key=None):
        if self.vm:
            return self._get_by_key(key, self.vm)

        vm = self.module.params.get("vm")
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        args = {}
        args["account"] = self.get_account(key="name")
        args["domainid"] = self.get_domain(key="id")
        args["projectid"] = self.get_project(key="id")
        args["zoneid"] = self.get_zone(key="id")
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms["virtualmachine"]:
                if vm in [v["name"], v["displayname"], v["id"]]:
                    self.vm = v
                    return self._get_by_key(key, self.vm)
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)

    def get_zone(self, key=None):
        if self.zone:
            return self._get_by_key(key, self.zone)

        zone = self.module.params.get("zone")
        zones = self.cs.listZones()

        # use the first zone if no zone param given
        if not zone:
            self.zone = zones["zone"][0]
            return self._get_by_key(key, self.zone)

        if zones:
            for z in zones["zone"]:
                if zone in [z["name"], z["id"]]:
                    self.zone = z
                    return self._get_by_key(key, self.zone)
        self.module.fail_json(msg="zone '%s' not found" % zone)

    def get_os_type(self, key=None):
        if self.os_type:
            return self._get_by_key(key, self.zone)

        os_type = self.module.params.get("os_type")
        if not os_type:
            return None

        os_types = self.cs.listOsTypes()
        if os_types:
            for o in os_types["ostype"]:
                if os_type in [o["description"], o["id"]]:
                    self.os_type = o
                    return self._get_by_key(key, self.os_type)
        self.module.fail_json(msg="OS type '%s' not found" % os_type)

    def get_hypervisor(self):
        if self.hypervisor:
            return self.hypervisor

        hypervisor = self.module.params.get("hypervisor")
        hypervisors = self.cs.listHypervisors()

        # use the first hypervisor if no hypervisor param given
        if not hypervisor:
            self.hypervisor = hypervisors["hypervisor"][0]["name"]
            return self.hypervisor

        for h in hypervisors["hypervisor"]:
            if hypervisor.lower() == h["name"].lower():
                self.hypervisor = h["name"]
                return self.hypervisor
        self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)

    def get_account(self, key=None):
        if self.account:
            return self._get_by_key(key, self.account)

        account = self.module.params.get("account")
        if not account:
            return None

        domain = self.module.params.get("domain")
        if not domain:
            self.module.fail_json(msg="Account must be specified with Domain")

        args = {}
        args["name"] = account
        args["domainid"] = self.get_domain(key="id")
        args["listall"] = True
        accounts = self.cs.listAccounts(**args)
        if accounts:
            self.account = accounts["account"][0]
            return self._get_by_key(key, self.account)
        self.module.fail_json(msg="Account '%s' not found" % account)

    def get_domain(self, key=None):
        if self.domain:
            return self._get_by_key(key, self.domain)

        domain = self.module.params.get("domain")
        if not domain:
            return None

        args = {}
        args["listall"] = True
        domains = self.cs.listDomains(**args)
        if domains:
            for d in domains["domain"]:
                if d["path"].lower() in [domain.lower(), "root/" + domain.lower(), "root" + domain.lower()]:
                    self.domain = d
                    return self._get_by_key(key, self.domain)
        self.module.fail_json(msg="Domain '%s' not found" % domain)

    def get_tags(self, resource=None):
        existing_tags = self.cs.listTags(resourceid=resource["id"])
        if existing_tags:
            return existing_tags["tag"]
        return []

    def _delete_tags(self, resource, resource_type, tags):
        existing_tags = resource["tags"]
        tags_to_delete = []
        for existing_tag in existing_tags:
            if existing_tag["key"] in tags:
                if existing_tag["value"] != tags[key]:
                    tags_to_delete.append(existing_tag)
            else:
                tags_to_delete.append(existing_tag)
        if tags_to_delete:
            self.result["changed"] = True
            if not self.module.check_mode:
                args = {}
                args["resourceids"] = resource["id"]
                args["resourcetype"] = resource_type
                args["tags"] = tags_to_delete
                self.cs.deleteTags(**args)

    def _create_tags(self, resource, resource_type, tags):
        tags_to_create = []
        for i, tag_entry in enumerate(tags):
            tag = {"key": tag_entry["key"], "value": tag_entry["value"]}
            tags_to_create.append(tag)
        if tags_to_create:
            self.result["changed"] = True
            if not self.module.check_mode:
                args = {}
                args["resourceids"] = resource["id"]
                args["resourcetype"] = resource_type
                args["tags"] = tags_to_create
                self.cs.createTags(**args)

    def ensure_tags(self, resource, resource_type=None):
        if not resource_type or not resource:
            self.module.fail_json(msg="Error: Missing resource or resource_type for tags.")

        if "tags" in resource:
            tags = self.module.params.get("tags")
            if tags is not None:
                self._delete_tags(resource, resource_type, tags)
                self._create_tags(resource, resource_type, tags)
                resource["tags"] = self.get_tags(resource)
        return resource

    def get_disk_offering(self, key=None):
        disk_offering = self.module.params.get("disk_offering")

        if not disk_offering:
            return None

        disk_offerings = self.cs.listDiskOfferings()
        if disk_offerings:
            for d in disk_offerings["diskoffering"]:
                if disk_offering in [d["displaytext"], d["name"], d["id"]]:
                    return self._get_by_key(key, d)
        self.module.fail_json(msg="Disk offering '%s' not found" % disk_offering)

    def get_capabilities(self, key=None):
        if self.capabilities:
            return self._get_by_key(key, self.capabilities)
        capabilities = self.cs.listCapabilities()
        self.capabilities = capabilities["capability"]
        return self._get_by_key(key, self.capabilities)

    # TODO: for backward compatibility only, remove it if not used anymore
    def _poll_job(self, job=None, key=None):
        return self.poll_job(job=job, key=key)

    def poll_job(self, job=None, key=None):
        if "jobid" in job:
            while True:
                res = self.cs.queryAsyncJobResult(jobid=job["jobid"])
                if res["jobstatus"] != 0 and "jobresult" in res:
                    if "errortext" in res["jobresult"]:
                        self.module.fail_json(msg="Failed: '%s'" % res["jobresult"]["errortext"])
                    if key and key in res["jobresult"]:
                        job = res["jobresult"][key]
                    break
                time.sleep(2)
        return job

    def get_result(self, resource):
        if resource:
            returns = self.common_returns.copy()
            returns.update(self.returns)
            for search_key, return_key in returns.iteritems():
                if search_key in resource:
                    self.result[return_key] = resource[search_key]

            # Special handling for tags
            if "tags" in resource:
                self.result["tags"] = []
                for tag in resource["tags"]:
                    result_tag = {}
                    result_tag["key"] = tag["key"]
                    result_tag["value"] = tag["value"]
                    self.result["tags"].append(result_tag)
        return self.result
class AnsibleCloudStack:

    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.module = module
        self._connect()

        self.project_id = None
        self.ip_address_id = None
        self.zone_id = None
        self.vm_id = None
        self.os_type_id = None
        self.hypervisor = None


    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('api_secret')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                method=api_http_method
                )
        else:
            self.cs = CloudStack(**read_config())


    def get_project_id(self):
        if self.project_id:
            return self.project_id

        project = self.module.params.get('project')
        if not project:
            return None

        projects = self.cs.listProjects()
        if projects:
            for p in projects['project']:
                if project in [ p['name'], p['displaytext'], p['id'] ]:
                    self.project_id = p['id']
                    return self.project_id
        self.module.fail_json(msg="project '%s' not found" % project)


    def get_ip_address_id(self):
        if self.ip_address_id:
            return self.ip_address_id

        ip_address = self.module.params.get('ip_address')
        if not ip_address:
            self.module.fail_json(msg="IP address param 'ip_address' is required")

        args = {}
        args['ipaddress'] = ip_address
        args['projectid'] = self.get_project_id()
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress'])

        self.ip_address_id = ip_addresses['publicipaddress'][0]['id']
        return self.ip_address_id


    def get_vm_id(self):
        if self.vm_id:
            return self.vm_id

        vm = self.module.params.get('vm')
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        args = {}
        args['projectid'] = self.get_project_id()
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms['virtualmachine']:
                if vm in [ v['name'], v['displayname'], v['id'] ]:
                    self.vm_id = v['id']
                    return self.vm_id
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)


    def get_zone_id(self):
        if self.zone_id:
            return self.zone_id

        zone = self.module.params.get('zone')
        zones = self.cs.listZones()

        # use the first zone if no zone param given
        if not zone:
            self.zone_id = zones['zone'][0]['id']
            return self.zone_id

        if zones:
            for z in zones['zone']:
                if zone in [ z['name'], z['id'] ]:
                    self.zone_id = z['id']
                    return self.zone_id
        self.module.fail_json(msg="zone '%s' not found" % zone)


    def get_os_type_id(self):
        if self.os_type_id:
            return self.os_type_id

        os_type = self.module.params.get('os_type')
        if not os_type:
            return None

        os_types = self.cs.listOsTypes()
        if os_types:
            for o in os_types['ostype']:
                if os_type in [ o['description'], o['id'] ]:
                    self.os_type_id = o['id']
                    return self.os_type_id
        self.module.fail_json(msg="OS type '%s' not found" % os_type)


    def get_hypervisor(self):
        if self.hypervisor:
            return self.hypervisor

        hypervisor = self.module.params.get('hypervisor')
        hypervisors = self.cs.listHypervisors()

        # use the first hypervisor if no hypervisor param given
        if not hypervisor:
            self.hypervisor = hypervisors['hypervisor'][0]['name']
            return self.hypervisor

        for h in hypervisors['hypervisor']:
            if hypervisor.lower() == h['name'].lower():
                self.hypervisor = h['name']
                return self.hypervisor
        self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)


    def _poll_job(self, job=None, key=None):
        if 'jobid' in job:
            while True:
                res = self.cs.queryAsyncJobResult(jobid=job['jobid'])
                if res['jobstatus'] != 0 and 'jobresult' in res:
                    if 'errortext' in res['jobresult']:
                        self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext'])
                    if key and key in res['jobresult']:
                        job = res['jobresult'][key]
                    break
                time.sleep(2)
        return job
def get_stats():
    timeout = 300
    stats = dict()
    hvmrunning = defaultdict(int)
    hvmstopped = defaultdict(int)
    hvmstopping = defaultdict(int)
    hvmstarting = defaultdict(int)

    cs = CloudStack(endpoint=API_MONITORS, key=APIKEY_MONITORS, secret=SECRET_MONITORS, timeout=timeout)

    logger('verb', "get_stats calls API %s KEY %s SECRET %s" % (API_MONITORS, APIKEY_MONITORS, SECRET_MONITORS))

    try:
        logger('verb', "Performing listhosts API call")
        hypervisors = cs_list(cs, 'listHosts', 'host', type='Routing', resourcestate='Enabled', state='Up')
        logger('verb', "Completed listhosts API call")

    except Exception:
        logger('warn', "status err Unable to connect to CloudStack URL at %s for Hosts" % API_MONITORS)

    for h in hypervisors:
        metricnameMemUsed = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'memoryused'])
        metricnameMemTotal = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'memorytotal'])
        metricnameMemAlloc = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'memoryallocated'])
        metricnameMemAllocPercent = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'memoryallocatedpercent'])
        metricnameCpuAlloc = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'cpuallocated'])
        try:
            memorytotal = int(h['memorytotal'])
            memoryallocated = int(h['memoryallocated'])
            stats[metricnameMemUsed] = h['memoryused']
            stats[metricnameMemTotal] = memorytotal
            stats[metricnameMemAlloc] = memoryallocated
            stats[metricnameMemAllocPercent] = round(memoryallocated / memorytotal, 4) if memorytotal > 0 else 0
            cpuallocated = h['cpuallocated'].replace("%", "")
            stats[metricnameCpuAlloc] = cpuallocated
            logger('verb', "readings :  %s memory used %s " % (h['name'], h['memoryused']))

        except (TypeError, ValueError, KeyError):
            pass

    # collect disk metrics
    try:
        logger('verb', "Performing liststoragepools API call")
        pools = cs_list(cs, 'listStoragePools', 'storagepool')
        logger('verb', "Completed liststoragepools API call")

    except Exception:
        logger('warn', "status err Unable to connect to CloudStack URL at %s for storage pools" % API_MONITORS)

    for h in pools:
        name = h['name'].split()[0]  # to avoid "Local Storage" in the pool name
        metricnameDiskAlloc = METRIC_DELIM.join([name.lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'disksizeallocated'])
        metricnameDiskAllocPercent = METRIC_DELIM.join([name.lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'disksizeallocatedpercent'])
        metricnameDiskUsed = METRIC_DELIM.join([name.lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'disksizeused'])
        metricnameDiskTotal = METRIC_DELIM.join([name.lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'disksizetotal'])
        try:
            disksizeallocated = int(h['disksizeallocated'])
            disksizetotal = int(h['disksizetotal'])
            stats[metricnameDiskAlloc] = disksizeallocated
            stats[metricnameDiskUsed] = h['disksizeused']
            stats[metricnameDiskTotal] = h['disksizetotal']
            stats[metricnameDiskAllocPercent] = round(disksizeallocated / disksizetotal, 4) if disksizetotal > 0 else 0

        except (TypeError, ValueError, KeyError):
            pass

    # collect number of active console sessions
    try:
        logger('verb', "Performing listSystemVms API call")
        systemvms = cs_list(cs, 'listSystemVms', 'systemvm', systemvmtype='consoleproxy')
        logger('verb', "Completed listSystemVms API call")

    except Exception:
        logger('warn', "status err Unable to connect to CloudStack URL at %s for SystemVms" % API_MONITORS)

    for systemvm in systemvms:
        metricnameSessions = METRIC_DELIM.join(['activeviewersessions', systemvm['zonename'].lower(), systemvm['name'].lower(), 'activeviewersessions'])
        if 'activeviewersessions' in systemvm:
            stats[metricnameSessions] = systemvm['activeviewersessions']

    # collect number of zones, available public ips and VMs
    try:
        logger('verb', "Performing listZones API call")
        zones = cs_list(cs, 'listZones', 'zone', showcapacities='true')
        logger('verb', "Completed listZones API call")

    except Exception:
        logger('warn', "status err Unable to connect to CloudStack URL at %s for ListZone" % API_MONITORS)

    for zone in zones:
        metricnameIpAllocated = METRIC_DELIM.join(['zonepublicipallocated', zone['name'].lower(), 'zonepublicipallocated'])
        metricnameIpTotal = METRIC_DELIM.join(['zonepubliciptotal', zone['name'].lower(), 'zonepubliciptotal'])
        metricnameIpAllocatedPercent = METRIC_DELIM.join(['zonepublicippercent', zone['name'].lower(), 'zonepublicippercent'])
        metricnameVmZoneTotalRunning = METRIC_DELIM.join(['zonevmtotalrunning', zone['name'].lower(), 'zonevmtotalrunning'])
        metricnameVmZoneTotalRunningMicro = METRIC_DELIM.join(['zonevmtotalrunningmicro', zone['name'].lower(), 'zonevmtotalrunningmicro'])
        metricnameVmZoneTotalRunningTiny = METRIC_DELIM.join(['zonevmtotalrunningtiny', zone['name'].lower(), 'zonevmtotalrunningtiny'])
        metricnameVmZoneTotalRunningSmall = METRIC_DELIM.join(['zonevmtotalrunningsmall', zone['name'].lower(), 'zonevmtotalrunningsmall'])
        metricnameVmZoneTotalRunningMedium = METRIC_DELIM.join(['zonevmtotalrunningmedium', zone['name'].lower(), 'zonevmtotalrunningmedium'])
        metricnameVmZoneTotalRunningLarge = METRIC_DELIM.join(['zonevmtotalrunninglarge', zone['name'].lower(), 'zonevmtotalrunninglarge'])
        metricnameVmZoneTotalRunningXLarge = METRIC_DELIM.join(['zonevmtotalrunningxlarge', zone['name'].lower(), 'zonevmtotalrunningxlarge'])
        metricnameVmZoneTotalRunningHuge = METRIC_DELIM.join(['zonevmtotalrunninghuge', zone['name'].lower(), 'zonevmtotalrunninghuge'])
        metricnameVmZoneTotalRunningMega = METRIC_DELIM.join(['zonevmtotalrunningmega', zone['name'].lower(), 'zonevmtotalrunningmega'])
        metricnameVmZoneTotalRunningTitan = METRIC_DELIM.join(['zonevmtotalrunningtitan', zone['name'].lower(), 'zonevmtotalrunningtitan'])
        metricnameVmZoneTotalStopped = METRIC_DELIM.join(['zonevmtotalstopped', zone['name'].lower(), 'zonevmtotalstopped'])
        metricnameVmZoneTotalStoppedMicro = METRIC_DELIM.join(['zonevmtotalstoppedmicro', zone['name'].lower(), 'zonevmtotalstoppedmicro'])
        metricnameVmZoneTotalStoppedTiny = METRIC_DELIM.join(['zonevmtotalstoppedtiny', zone['name'].lower(), 'zonevmtotalstoppedtiny'])
        metricnameVmZoneTotalStoppedSmall = METRIC_DELIM.join(['zonevmtotalstoppedsmall', zone['name'].lower(), 'zonevmtotalstoppedsmall'])
        metricnameVmZoneTotalStoppedMedium = METRIC_DELIM.join(['zonevmtotalstoppedmedium', zone['name'].lower(), 'zonevmtotalstoppedmedium'])
        metricnameVmZoneTotalStoppedLarge = METRIC_DELIM.join(['zonevmtotalstoppedlarge', zone['name'].lower(), 'zonevmtotalstoppedlarge'])
        metricnameVmZoneTotalStoppedXLarge = METRIC_DELIM.join(['zonevmtotalstoppedxlarge', zone['name'].lower(), 'zonevmtotalstoppedxlarge'])
        metricnameVmZoneTotalStoppedHuge = METRIC_DELIM.join(['zonevmtotalstoppedhuge', zone['name'].lower(), 'zonevmtotalstoppedhuge'])
        metricnameVmZoneTotalStoppedMega = METRIC_DELIM.join(['zonevmtotalstoppedmega', zone['name'].lower(), 'zonevmtotalstoppedmega'])
        metricnameVmZoneTotalStoppedTitan = METRIC_DELIM.join(['zonevmtotalstoppedtitan', zone['name'].lower(), 'zonevmtotalstoppedtitan'])
        metricnameVmZoneTotalStopping = METRIC_DELIM.join(['zonevmtotalstopping', zone['name'].lower(), 'zonevmtotalstopping'])
        metricnameVmZoneTotalStarting = METRIC_DELIM.join(['zonevmtotalstarting', zone['name'].lower(), 'zonevmtotalstarting'])
        metricnameVmZoneTotalMicro = METRIC_DELIM.join(['zonevmtotalmicro', zone['name'].lower(), 'zonevmtotalmicro'])
        metricnameVmZoneTotalTiny = METRIC_DELIM.join(['zonevmtotaltiny', zone['name'].lower(), 'zonevmtotaltiny'])
        metricnameVmZoneTotalSmall = METRIC_DELIM.join(['zonevmtotalsmall', zone['name'].lower(), 'zonevmtotalsmall'])
        metricnameVmZoneTotalMedium = METRIC_DELIM.join(['zonevmtotalmedium', zone['name'].lower(), 'zonevmtotalmedium'])
        metricnameVmZoneTotalLarge = METRIC_DELIM.join(['zonevmtotallarge', zone['name'].lower(), 'zonevmtotallarge'])
        metricnameVmZoneTotalXLarge = METRIC_DELIM.join(['zonevmtotalxlarge', zone['name'].lower(), 'zonevmtotalxlarge'])
        metricnameVmZoneTotalHuge = METRIC_DELIM.join(['zonevmtotalhuge', zone['name'].lower(), 'zonevmtotalhuge'])
        metricnameVmZoneTotalMega = METRIC_DELIM.join(['zonevmtotalmega', zone['name'].lower(), 'zonevmtotalmega'])
        metricnameVmZoneTotalTitan = METRIC_DELIM.join(['zonevmtotaltitan', zone['name'].lower(), 'zonevmtotaltitan'])
        metricnameVmZoneTotal = METRIC_DELIM.join(['zonevmtotal', zone['name'].lower(), 'zonevmtotal'])
        metricnameZonesCount = METRIC_DELIM.join(['zonescount', 'zonescount'])
        metricnameHostZoneTotal = METRIC_DELIM.join(['zonehosttotal', zone['name'].lower(), 'zonehosttotal'])
        metricnameVMZoneRAMavgSize = METRIC_DELIM.join(['zonevmramavgsize', zone['name'].lower(), 'zonevmramavgsize'])
        metricnameVMZoneCPUavgSize = METRIC_DELIM.join(['zonevmcpuavgsize', zone['name'].lower(), 'zonevmcpuavgsize'])

        # collect number of virtual machines

        zoneid = zone['id']

        try:
            logger('verb', "Performing listVirtualMachines API call")
            virtualmachines = cs_list(cs, 'listVirtualMachines', 'virtualmachine', details='all', zoneid=zoneid)
            logger('verb', "Completed listVirtualMachines API call")
        except Exception:
            logger('warn', "status err Unable to connect to CloudStack URL at %s for ListVms" % API_MONITORS)

        virtualMachineZoneRunningCount = 0
        virtualMachineZoneRunningMicroCount = 0
        virtualMachineZoneRunningTinyCount = 0
        virtualMachineZoneRunningSmallCount = 0
        virtualMachineZoneRunningMediumCount = 0
        virtualMachineZoneRunningLargeCount = 0
        virtualMachineZoneRunningXLargeCount = 0
        virtualMachineZoneRunningHugeCount = 0
        virtualMachineZoneRunningMegaCount = 0
        virtualMachineZoneRunningTitanCount = 0
        virtualMachineZoneStoppedCount = 0
        virtualMachineZoneStoppedMicroCount = 0
        virtualMachineZoneStoppedTinyCount = 0
        virtualMachineZoneStoppedSmallCount = 0
        virtualMachineZoneStoppedMediumCount = 0
        virtualMachineZoneStoppedLargeCount = 0
        virtualMachineZoneStoppedXLargeCount = 0
        virtualMachineZoneStoppedHugeCount = 0
        virtualMachineZoneStoppedMegaCount = 0
        virtualMachineZoneStoppedTitanCount = 0
        virtualMachineZoneStartingCount = 0
        virtualMachineZoneStoppingCount = 0
        virtualMachineZoneMicro = 0
        virtualMachineZoneTiny = 0
        virtualMachineZoneSmall = 0
        virtualMachineZoneMedium = 0
        virtualMachineZoneLarge = 0
        virtualMachineZoneXLarge = 0
        virtualMachineZoneHuge = 0
        virtualMachineZoneMega = 0
        virtualMachineZoneTitan = 0
        cpu = 0
        ram = 0

        for virtualmachine in virtualmachines:
            cpu += virtualmachine['cpunumber']
            ram += virtualmachine['memory']
            if virtualmachine['state'] == 'Running':
                virtualMachineZoneRunningCount += 1
            elif virtualmachine['state'] == 'Stopped':
                virtualMachineZoneStoppedCount += 1
            elif virtualmachine['state'] == 'Stopping':
                virtualMachineZoneStartingCount += 1
            elif virtualmachine['state'] == 'Starting':
                virtualMachineZoneStoppingCount += 1
            if virtualmachine['serviceofferingname'] == 'Micro':
                virtualMachineZoneMicro += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningMicroCount += 1
                else:
                    virtualMachineZoneStoppedMicroCount += 1
            elif virtualmachine['serviceofferingname'] == 'Tiny':
                virtualMachineZoneTiny += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningTinyCount += 1
                else:
                    virtualMachineZoneStoppedTinyCount += 1
            elif virtualmachine['serviceofferingname'] == 'Small':
                virtualMachineZoneSmall += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningSmallCount += 1
                else:
                    virtualMachineZoneStoppedSmallCount += 1
            elif virtualmachine['serviceofferingname'] == 'Medium':
                virtualMachineZoneMedium += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningMediumCount += 1
                else:
                    virtualMachineZoneStoppedMediumCount += 1
            elif virtualmachine['serviceofferingname'] == 'Large':
                virtualMachineZoneLarge += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningLargeCount += 1
                else:
                    virtualMachineZoneStoppedLargeCount += 1
            elif virtualmachine['serviceofferingname'] == 'Extra-large':
                virtualMachineZoneXLarge += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningXLargeCount += 1
                else:
                    virtualMachineZoneStoppedXLargeCount += 1
            elif virtualmachine['serviceofferingname'] == 'Huge':
                virtualMachineZoneHuge += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningHugeCount += 1
                else:
                    virtualMachineZoneStoppedHugeCount += 1
            elif virtualmachine['serviceofferingname'] == 'Mega':
                virtualMachineZoneMega += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningMegaCount += 1
                else:
                    virtualMachineZoneStoppedMegaCount += 1
            elif virtualmachine['serviceofferingname'] == 'Titan':
                virtualMachineZoneTitan += 1
                if virtualmachine['state'] == 'Running':
                    virtualMachineZoneRunningTitanCount += 1
                else:
                    virtualMachineZoneStoppedTitanCount += 1

        if len(virtualmachines) == 0:
            ramavg = 0
            cpuavg = 0
        else:
            ram = (ram / 1024)
            ramavg = (ram / len(virtualmachines))
            cpuavg = (cpu / len(virtualmachines))

        stats[metricnameVMZoneRAMavgSize] = ramavg
        stats[metricnameVMZoneCPUavgSize] = cpuavg
        stats[metricnameVmZoneTotal] = len(virtualmachines)
        stats[metricnameVmZoneTotalRunning] = virtualMachineZoneRunningCount
        stats[metricnameVmZoneTotalRunningMicro] = virtualMachineZoneRunningMicroCount
        stats[metricnameVmZoneTotalRunningTiny] = virtualMachineZoneRunningTinyCount
        stats[metricnameVmZoneTotalRunningSmall] = virtualMachineZoneRunningSmallCount
        stats[metricnameVmZoneTotalRunningMedium] = virtualMachineZoneRunningMediumCount
        stats[metricnameVmZoneTotalRunningLarge] = virtualMachineZoneRunningLargeCount
        stats[metricnameVmZoneTotalRunningXLarge] = virtualMachineZoneRunningXLargeCount
        stats[metricnameVmZoneTotalRunningHuge] = virtualMachineZoneRunningHugeCount
        stats[metricnameVmZoneTotalRunningMega] = virtualMachineZoneRunningMegaCount
        stats[metricnameVmZoneTotalRunningTitan] = virtualMachineZoneRunningTitanCount
        stats[metricnameVmZoneTotalStopped] = virtualMachineZoneStoppedCount
        stats[metricnameVmZoneTotalStoppedMicro] = virtualMachineZoneStoppedMicroCount
        stats[metricnameVmZoneTotalStoppedTiny] = virtualMachineZoneStoppedTinyCount
        stats[metricnameVmZoneTotalStoppedSmall] = virtualMachineZoneStoppedSmallCount
        stats[metricnameVmZoneTotalStoppedMedium] = virtualMachineZoneStoppedMediumCount
        stats[metricnameVmZoneTotalStoppedLarge] = virtualMachineZoneStoppedLargeCount
        stats[metricnameVmZoneTotalStoppedXLarge] = virtualMachineZoneStoppedXLargeCount
        stats[metricnameVmZoneTotalStoppedHuge] = virtualMachineZoneStoppedHugeCount
        stats[metricnameVmZoneTotalStoppedMega] = virtualMachineZoneStoppedMegaCount
        stats[metricnameVmZoneTotalStoppedTitan] = virtualMachineZoneStoppedTitanCount
        stats[metricnameVmZoneTotalStopping] = virtualMachineZoneStoppingCount
        stats[metricnameVmZoneTotalStarting] = virtualMachineZoneStartingCount
        stats[metricnameVmZoneTotalMicro] = virtualMachineZoneMicro
        stats[metricnameVmZoneTotalTiny] = virtualMachineZoneTiny
        stats[metricnameVmZoneTotalSmall] = virtualMachineZoneSmall
        stats[metricnameVmZoneTotalMedium] = virtualMachineZoneMedium
        stats[metricnameVmZoneTotalLarge] = virtualMachineZoneLarge
        stats[metricnameVmZoneTotalXLarge] = virtualMachineZoneXLarge
        stats[metricnameVmZoneTotalHuge] = virtualMachineZoneHuge
        stats[metricnameVmZoneTotalMega] = virtualMachineZoneMega
        stats[metricnameVmZoneTotalTitan] = virtualMachineZoneTitan

        # collect number of root volumes
        try:
            logger('verb', "Performing listVolumes API call")
            rootvolumes = cs_list(cs, 'listVolumes', 'volume', type='ROOT', zoneid=zoneid)
            logger('verb', "Completed listVolumes API call")
        except Exception:
            logger('warn', "status err Unable to connect to CloudStack URL at %s for ListVolumes" % API_MONITORS)

        rootvolsize = 0
        for rootvolume in rootvolumes:
            rootvolsize += rootvolume['size']

            if rootvolume['vmstate'] == 'Running':
                # add to a dict to get the Running VMs per hypervisor
                host = rootvolume['storage']
                hvmrunning[host] += 1
            elif rootvolume['vmstate'] == 'Stopped' and not rootvolume['state'] == 'Allocated':
                # add to a dict to get the Stopped VMs per hypervisor
                host = rootvolume['storage']
                hvmstopped[host] += 1
            elif rootvolume['vmstate'] == 'Stopping':
                # add to a dict to get the Stopping VMs per hypervisor
                host = rootvolume['storage']
                hvmstopping[host] += 1
            elif rootvolume['vmstate'] == 'Starting':
                # add to a dict to get the Starting VMs per hypervisor
                host = rootvolume['storage']
                hvmstarting[host] += 1

        if len(rootvolumes) != 0:
            rootvolsize = (rootvolsize / 1073741824)
            rootavgsize = rootvolsize / len(rootvolumes)
        else:
            rootvolsize = 0
            rootavgsize = 0

        metricnameRootAvgSizeZone = METRIC_DELIM.join(['zonerootdiskavgsize', zone['name'].lower(), 'zonerootdiskavgsize'])
        stats[metricnameRootAvgSizeZone] = rootavgsize

        # add metric VMs per hypervisor
        for h in hypervisors:
            virtualMachineHTotalCount = 0
            metricnameVmHTotal = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'hvmtotal'])
            metricnameVmHTotalRunning = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'hvmtotalrunning'])
            metricnameVmHTotalStarting = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'hvmtotalstarting'])
            metricnameVmHTotalStopping = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'hvmtotalstopping'])
            metricnameVmHTotalStopped = METRIC_DELIM.join([h['name'].lower(), h['podname'].lower(), re.sub(r"\s+", '-', h['zonename'].lower()), 'hvmtotalstopped'])

            hname = h['name'].lower()
            if hname in hvmrunning:
                virtualMachineHTotalCount = virtualMachineHTotalCount + hvmrunning[hname]
                stats[metricnameVmHTotalRunning] = hvmrunning[hname]
            else:
                stats[metricnameVmHTotalRunning] = 0
            if hname in hvmstarting:
                virtualMachineHTotalCount = virtualMachineHTotalCount + hvmstarting[hname]
                stats[metricnameVmHTotalStarting] = hvmstarting[hname]
            else:
                stats[metricnameVmHTotalStarting] = 0
            if hname in hvmstopping:
                virtualMachineHTotalCount = virtualMachineHTotalCount + hvmstopping[hname]
                stats[metricnameVmHTotalStopping] = hvmstopping[hname]
            else:
                stats[metricnameVmHTotalStopping] = 0
            if hname in hvmstopped:
                virtualMachineHTotalCount = virtualMachineHTotalCount + hvmstopped[hname]
                stats[metricnameVmHTotalStopped] = hvmstopped[hname]
            else:
                stats[metricnameVmHTotalStopped] = 0

            stats[metricnameVmHTotal] = virtualMachineHTotalCount

        for capacity in zone['capacity']:
            if capacity['type'] == 8:
                stats[metricnameIpTotal] = capacity['capacitytotal']
                stats[metricnameIpAllocated] = capacity['capacityused']
                stats[metricnameIpAllocatedPercent] = capacity['percentused']

    stats[metricnameZonesCount] = len(zones)
    stats[metricnameHostZoneTotal] = len(hypervisors)

    # collect accounts
    try:
        logger('verb', "Performing accountStats API call")
        accounts_stats = cs.accountStats()['accountstats']
        logger('verb', "Completed accountStats API call")
    except Exception:
        logger('err', "status err Unable to connect to CloudStack URL at %s for accountStats")

    stats[metric_name('accounts', 'accountscount')] = accounts_stats['total']
    stats[metric_name('accounts', 'accountenabled')] = accounts_stats['enabled']
    stats[metric_name('accounts', 'accountdisabled')] = accounts_stats['disabled']

    # collect capacity
    try:
        capacity = cs.listCapacity(sharedonly=True)['capacity']
    except Exception:
        logger('err', "status err Unable to connect to CloudStack URL at %s for ListCapacity")

    types = {0: 'memory',
             1: 'cpu',
             2: 'disk',
             5: 'privateip',
             6: 'ss',
             9: 'diskalloc'}

    for c in capacity:
        def name(typ, metric):
            return metric_name('zonecapacity',
                               c['zonename'].lower(),
                               'zonecapa{0}{1}'.format(types[typ], metric))

        if c['type'] not in types:
            continue

        for metric, key in [
            ('total', 'capacitytotal'),
            ('used', 'capacityused'),
            ('percentused', 'percentused'),
        ]:
            stats[name(c['type'], metric)] = c[key]

    return stats
class CloudStackInventory(object):
    def __init__(self):

        parser = argparse.ArgumentParser()
        parser.add_argument('--host')
        parser.add_argument('--list', action='store_true')

        options = parser.parse_args()
        try:
            self.cs = CloudStack(**read_config())
        except CloudStackException as e:
            print("Error: Could not connect to CloudStack API", file=sys.stderr)

        if options.host:
            data = self.get_host(options.host)
            print(json.dumps(data, indent=2))

        elif options.list:
            data = self.get_list()
            print(json.dumps(data, indent=2))
        else:
            print("usage: --list | --host <hostname>", file=sys.stderr)
            sys.exit(1)


    def add_group(self, data, group_name, router_name):
        if group_name not in data:
            data[group_name] = {
                'hosts': []
            }
        data[group_name]['hosts'].append(router_name)
        return data


    def get_host(self, name):
        routers = []

        routers_projects = self.cs.listRouters(projectid=-1, listall=True)
        if routers_projects and 'router' in routers_projects:
            routers = routers + routers_projects['router']

        routers_accounts = self.cs.listRouters(listall=True)
        if routers_accounts and 'router' in routers_accounts:
            routers = routers + routers_accounts['router']

        data = {}
        for router in routers:
            router_name = router['name']
            if name == router_name:
                data['zone'] = router['zonename']
                if 'linklocalip' in router:
                    data['ansible_ssh_host'] = router['linklocalip']
                data['state'] = router['state']
                data['redundant_state'] = router['redundantstate']
                if 'account' in router:
                    data['account'] = router['account']
                if 'project' in router:
                    data['project'] = router['project']
                data['service_offering'] = router['serviceofferingname']
                data['role'] = router['role']
                data['nic'] = []
                for nic in router['nic']:
                    data['nic'].append({
                        'ip': nic['ipaddress'],
                        'mac': nic['macaddress'],
                        'netmask': nic['netmask'],
                    })
                    if nic['isdefault']:
                        data['default_ip'] = nic['ipaddress']
                break;
        return data


    def get_list(self):
        data = {
            'all': {
                'hosts': [],
                },
            '_meta': {
                'hostvars': {},
                },
            }

        routers = []

        routers_projects = self.cs.listRouters(projectid=-1, listall=True)
        if routers_projects and 'router' in routers_projects:
            routers = routers + routers_projects['router']

        routers_accounts = self.cs.listRouters(listall=True)
        if routers_accounts and 'router' in routers_accounts:
            routers = routers + routers_accounts['router']

        for router in routers:
            if router['state'] != 'Running':
                continue
            router_name = router['name']
            data['all']['hosts'].append(router_name)
            # Make a group per domain
            data = self.add_group(data, router['domain'], router_name)

            data['_meta']['hostvars'][router_name] = {}
            data['_meta']['hostvars'][router_name]['group'] = router['domain']
            data['_meta']['hostvars'][router_name]['domain'] = router['domain']
            if 'networkdomain' in router:
                data['_meta']['hostvars'][router_name]['networkdomain'] = router['networkdomain']

            data['_meta']['hostvars'][router_name]['zone'] = router['zonename']
            # Make a group per zone
            data = self.add_group(data, router['zonename'], router_name)

            if 'project' in router:
                data['_meta']['hostvars'][router_name]['project'] = router['project']

                # Make a group per project
                data = self.add_group(data, router['project'], router_name)

            if 'account' in router:
                data['_meta']['hostvars'][router_name]['account'] = router['account']

                # Make a group per account
                data = self.add_group(data, router['account'], router_name)

            data['_meta']['hostvars'][router_name]['ansible_ssh_host'] = router['linklocalip']
            data['_meta']['hostvars'][router_name]['state'] = router['state']
            if 'redundantstate' in router:
                data['_meta']['hostvars'][router_name]['redundant_state'] = router['redundantstate']

                if router['redundantstate'] in [ 'MASTER', 'BACKUP' ]:
                    data = self.add_group(data, 'redundant_routers', router_name)

                if router['redundantstate'] in [ 'MASTER' ]:
                    data = self.add_group(data, 'redundant_master_routers', router_name)

                if router['redundantstate'] in [ 'BACKUP' ]:
                    data = self.add_group(data, 'redundant_backup_routers', router_name)

                if router['redundantstate'] in [ 'UNKNOWN' ]:
                    data = self.add_group(data, 'non_redundant_routers', router_name)

            data['_meta']['hostvars'][router_name]['service_offering'] = router['serviceofferingname']
            data['_meta']['hostvars'][router_name]['nic'] = []
            for nic in router['nic']:
                data['_meta']['hostvars'][router_name]['nic'].append({
                    'ip': nic['ipaddress'],
                    'mac': nic['macaddress'],
                    'netmask': nic['netmask'],
                    })
                if nic['isdefault']:
                    data['_meta']['hostvars'][router_name]['default_ip'] = nic['ipaddress']
        return data
class AnsibleCloudStack:

    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.module = module
        self._connect()

        self.project = None
        self.ip_address = None
        self.zone = None
        self.vm = None
        self.os_type = None
        self.hypervisor = None
        self.capabilities = None


    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('secret_key')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                method=api_http_method
                )
        else:
            self.cs = CloudStack(**read_config())


    def _has_changed(self, want_dict, current_dict, only_keys=None):
        for key, value in want_dict.iteritems():

            # Optionally limit by a list of keys
            if only_keys and key not in only_keys:
                continue;

            if key in current_dict:

                # API returns string for int in some cases, just to make sure
                if isinstance(value, int):
                    current_dict[key] = int(current_dict[key])
                elif isinstance(value, str):
                    current_dict[key] = str(current_dict[key])

                # Only need to detect a singe change, not every item
                if value != current_dict[key]:
                    return True
        return False


    def _get_by_key(self, key=None, my_dict={}):
        if key:
            if key in my_dict:
                return my_dict[key]
            self.module.fail_json(msg="Something went wrong: %s not found" % key)
        return my_dict


    # TODO: for backward compatibility only, remove if not used anymore
    def get_project_id(self):
        return self.get_project(key='id')


    def get_project(self, key=None):
        if self.project:
            return self._get_by_key(key, self.project)

        project = self.module.params.get('project')
        if not project:
            return None

        projects = self.cs.listProjects(listall=True)
        if projects:
            for p in projects['project']:
                if project in [ p['name'], p['displaytext'], p['id'] ]:
                    self.project = p
                    return self._get_by_key(key, self.project)
        self.module.fail_json(msg="project '%s' not found" % project)


    # TODO: for backward compatibility only, remove if not used anymore
    def get_ip_address_id(self):
        return self.get_ip_address(key='id')


    def get_ip_address(self, key=None):
        if self.ip_address:
            return self._get_by_key(key, self.ip_address)

        ip_address = self.module.params.get('ip_address')
        if not ip_address:
            self.module.fail_json(msg="IP address param 'ip_address' is required")

        args = {}
        args['ipaddress'] = ip_address
        args['projectid'] = self.get_project(key='id')
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress'])

        self.ip_address = ip_addresses['publicipaddress'][0]
        return self._get_by_key(key, self.ip_address)


    # TODO: for backward compatibility only, remove if not used anymore
    def get_vm_id(self):
        return self.get_vm(key='id')


    def get_vm(self, key=None):
        if self.vm:
            return self._get_by_key(key, self.vm)

        vm = self.module.params.get('vm')
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        args = {}
        args['projectid'] = self.get_project(key='id')
        args['zoneid'] = self.get_zone(key='id')
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms['virtualmachine']:
                if vm in [ v['name'], v['displayname'], v['id'] ]:
                    self.vm = v
                    return self._get_by_key(key, self.vm)
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)


    # TODO: for backward compatibility only, remove if not used anymore
    def get_zone_id(self):
        return self.get_zone(key='id')


    def get_zone(self, key=None):
        if self.zone:
            return self._get_by_key(key, self.zone)

        zone = self.module.params.get('zone')
        zones = self.cs.listZones()

        # use the first zone if no zone param given
        if not zone:
            self.zone = zones['zone'][0]
            return self._get_by_key(key, self.zone)

        if zones:
            for z in zones['zone']:
                if zone in [ z['name'], z['id'] ]:
                    self.zone = z
                    return self._get_by_key(key, self.zone)
        self.module.fail_json(msg="zone '%s' not found" % zone)


    # TODO: for backward compatibility only, remove if not used anymore
    def get_os_type_id(self):
        return self.get_os_type(key='id')


    def get_os_type(self, key=None):
        if self.os_type:
            return self._get_by_key(key, self.zone)

        os_type = self.module.params.get('os_type')
        if not os_type:
            return None

        os_types = self.cs.listOsTypes()
        if os_types:
            for o in os_types['ostype']:
                if os_type in [ o['description'], o['id'] ]:
                    self.os_type = o
                    return self._get_by_key(key, self.os_type)
        self.module.fail_json(msg="OS type '%s' not found" % os_type)


    def get_hypervisor(self):
        if self.hypervisor:
            return self.hypervisor

        hypervisor = self.module.params.get('hypervisor')
        hypervisors = self.cs.listHypervisors()

        # use the first hypervisor if no hypervisor param given
        if not hypervisor:
            self.hypervisor = hypervisors['hypervisor'][0]['name']
            return self.hypervisor

        for h in hypervisors['hypervisor']:
            if hypervisor.lower() == h['name'].lower():
                self.hypervisor = h['name']
                return self.hypervisor
        self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)


    def get_capabilities(self, key=None):
        if self.capabilities:
            return self._get_by_key(key, self.capabilities)
        capabilities = self.cs.listCapabilities()
        self.capabilities = capabilities['capability']
        return self._get_by_key(key, self.capabilities)


    def _poll_job(self, job=None, key=None):
        if 'jobid' in job:
            while True:
                res = self.cs.queryAsyncJobResult(jobid=job['jobid'])
                if res['jobstatus'] != 0 and 'jobresult' in res:
                    if 'errortext' in res['jobresult']:
                        self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext'])
                    if key and key in res['jobresult']:
                        job = res['jobresult'][key]
                    break
                time.sleep(2)
        return job
Beispiel #55
0
class AnsibleCloudStack(object):

    def __init__(self, module):
        if not has_lib_cs:
            module.fail_json(msg="python library cs required: pip install cs")

        self.result = {
            'changed': False,
            'diff' : {
                'before': dict(),
                'after': dict()
            }
        }

        # Common returns, will be merged with self.returns
        # search_for_key: replace_with_key
        self.common_returns = {
            'id':           'id',
            'name':         'name',
            'created':      'created',
            'zonename':     'zone',
            'state':        'state',
            'project':      'project',
            'account':      'account',
            'domain':       'domain',
            'displaytext':  'display_text',
            'displayname':  'display_name',
            'description':  'description',
        }

        # Init returns dict for use in subclasses
        self.returns = {}
        # these values will be casted to int
        self.returns_to_int = {}
        # these keys will be compared case sensitive in self.has_changed()
        self.case_sensitive_keys = [
            'id',
            'displaytext',
            'displayname',
            'description',
        ]

        self.module = module
        self._connect()

        # Helper for VPCs
        self._vpc_networks_ids = None

        self.domain = None
        self.account = None
        self.project = None
        self.ip_address = None
        self.network = None
        self.vpc = None
        self.zone = None
        self.vm = None
        self.vm_default_nic = None
        self.os_type = None
        self.hypervisor = None
        self.capabilities = None


    def _connect(self):
        api_key = self.module.params.get('api_key')
        api_secret = self.module.params.get('api_secret')
        api_url = self.module.params.get('api_url')
        api_http_method = self.module.params.get('api_http_method')
        api_timeout = self.module.params.get('api_timeout')

        if api_key and api_secret and api_url:
            self.cs = CloudStack(
                endpoint=api_url,
                key=api_key,
                secret=api_secret,
                timeout=api_timeout,
                method=api_http_method
                )
        else:
            api_region = self.module.params.get('api_region', 'cloudstack')
            self.cs = CloudStack(**read_config(api_region))


    def get_or_fallback(self, key=None, fallback_key=None):
        value = self.module.params.get(key)
        if not value:
            value = self.module.params.get(fallback_key)
        return value


    # TODO: for backward compatibility only, remove if not used anymore
    def _has_changed(self, want_dict, current_dict, only_keys=None):
        return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys)


    def has_changed(self, want_dict, current_dict, only_keys=None):
        result = False
        for key, value in want_dict.iteritems():

            # Optionally limit by a list of keys
            if only_keys and key not in only_keys:
                continue

            # Skip None values
            if value is None:
                continue

            if key in current_dict:
                if isinstance(value, (int, float, long, complex)):
                    # ensure we compare the same type
                    if isinstance(value, int):
                        current_dict[key] = int(current_dict[key])
                    elif isinstance(value, float):
                        current_dict[key] = float(current_dict[key])
                    elif isinstance(value, long):
                        current_dict[key] = long(current_dict[key])
                    elif isinstance(value, complex):
                        current_dict[key] = complex(current_dict[key])

                    if value != current_dict[key]:
                        self.result['diff']['before'][key] = current_dict[key]
                        self.result['diff']['after'][key] = value
                        result = True
                else:
                    if self.case_sensitive_keys and key in self.case_sensitive_keys:
                        if value != current_dict[key].encode('utf-8'):
                            self.result['diff']['before'][key] = current_dict[key].encode('utf-8')
                            self.result['diff']['after'][key] = value
                            result = True

                    # Test for diff in case insensitive way
                    elif value.lower() != current_dict[key].encode('utf-8').lower():
                        self.result['diff']['before'][key] = current_dict[key].encode('utf-8')
                        self.result['diff']['after'][key] = value
                        result = True
            else:
                self.result['diff']['before'][key] = None
                self.result['diff']['after'][key] = value
                result = True
        return result


    def _get_by_key(self, key=None, my_dict=None):
        if my_dict is None:
            my_dict = {}
        if key:
            if key in my_dict:
                return my_dict[key]
            self.module.fail_json(msg="Something went wrong: %s not found" % key)
        return my_dict


    def get_vpc(self, key=None):
        """Return a VPC dictionary or the value of given key of."""
        if self.vpc:
            return self._get_by_key(key, self.vpc)

        vpc = self.module.params.get('vpc')
        if not vpc:
            vpc = os.environ.get('CLOUDSTACK_VPC')
        if not vpc:
            return None

        args = {
            'account': self.get_account(key='name'),
            'domainid': self.get_domain(key='id'),
            'projectid': self.get_project(key='id'),
            'zoneid': self.get_zone(key='id'),
        }
        vpcs = self.cs.listVPCs(**args)
        if not vpcs:
            self.module.fail_json(msg="No VPCs available.")

        for v in vpcs['vpc']:
            if vpc in [v['displaytext'], v['name'], v['id']]:
                self.vpc = v
                return self._get_by_key(key, self.vpc)
        self.module.fail_json(msg="VPC '%s' not found" % vpc)


    def is_vm_in_vpc(self, vm):
        for n in vm.get('nic'):
            if n.get('isdefault', False):
                return self.is_vpc_network(network_id=n['networkid'])
        self.module.fail_json(msg="VM has no default nic")


    def is_vpc_network(self, network_id):
        """Returns True if network is in VPC."""
        # This is an efficient way to query a lot of networks at a time
        if self._vpc_networks_ids is None:
            args = {
                'account': self.get_account(key='name'),
                'domainid': self.get_domain(key='id'),
                'projectid': self.get_project(key='id'),
                'zoneid': self.get_zone(key='id'),
            }
            vpcs = self.cs.listVPCs(**args)
            self._vpc_networks_ids = []
            if vpcs:
                for vpc in vpcs['vpc']:
                    for n in vpc.get('network',[]):
                        self._vpc_networks_ids.append(n['id'])
        return network_id in self._vpc_networks_ids


    def get_network(self, key=None):
        """Return a network dictionary or the value of given key of."""
        if self.network:
            return self._get_by_key(key, self.network)

        network = self.module.params.get('network')
        if not network:
            return None

        args = {
            'account': self.get_account(key='name'),
            'domainid': self.get_domain(key='id'),
            'projectid': self.get_project(key='id'),
            'zoneid': self.get_zone(key='id'),
            'vpcid': self.get_vpc(key='id')
        }
        networks = self.cs.listNetworks(**args)
        if not networks:
            self.module.fail_json(msg="No networks available.")

        for n in networks['network']:
            # ignore any VPC network if vpc param is not given
            if 'vpcid' in n and not self.get_vpc(key='id'):
                continue
            if network in [n['displaytext'], n['name'], n['id']]:
                self.network = n
                return self._get_by_key(key, self.network)
        self.module.fail_json(msg="Network '%s' not found" % network)


    def get_project(self, key=None):
        if self.project:
            return self._get_by_key(key, self.project)

        project = self.module.params.get('project')
        if not project:
            project = os.environ.get('CLOUDSTACK_PROJECT')
        if not project:
            return None
        args = {}
        args['account'] = self.get_account(key='name')
        args['domainid'] = self.get_domain(key='id')
        projects = self.cs.listProjects(**args)
        if projects:
            for p in projects['project']:
                if project.lower() in [ p['name'].lower(), p['id'] ]:
                    self.project = p
                    return self._get_by_key(key, self.project)
        self.module.fail_json(msg="project '%s' not found" % project)


    def get_ip_address(self, key=None):
        if self.ip_address:
            return self._get_by_key(key, self.ip_address)

        ip_address = self.module.params.get('ip_address')
        if not ip_address:
            self.module.fail_json(msg="IP address param 'ip_address' is required")

        args = {
            'ipaddress': ip_address,
            'account': self.get_account(key='name'),
            'domainid': self.get_domain(key='id'),
            'projectid': self.get_project(key='id'),
            'vpcid': self.get_vpc(key='id'),
        }
        ip_addresses = self.cs.listPublicIpAddresses(**args)

        if not ip_addresses:
            self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress'])

        self.ip_address = ip_addresses['publicipaddress'][0]
        return self._get_by_key(key, self.ip_address)


    def get_vm_guest_ip(self):
        vm_guest_ip = self.module.params.get('vm_guest_ip')
        default_nic = self.get_vm_default_nic()

        if not vm_guest_ip:
            return default_nic['ipaddress']

        for secondary_ip in default_nic['secondaryip']:
            if vm_guest_ip == secondary_ip['ipaddress']:
                return vm_guest_ip
        self.module.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip)


    def get_vm_default_nic(self):
        if self.vm_default_nic:
            return self.vm_default_nic

        nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id'))
        if nics:
            for n in nics['nic']:
                if n['isdefault']:
                    self.vm_default_nic = n
                    return self.vm_default_nic
        self.module.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm'))


    def get_vm(self, key=None):
        if self.vm:
            return self._get_by_key(key, self.vm)

        vm = self.module.params.get('vm')
        if not vm:
            self.module.fail_json(msg="Virtual machine param 'vm' is required")

        vpc_id = self.get_vpc(key='id')

        args = {
            'account': self.get_account(key='name'),
            'domainid': self.get_domain(key='id'),
            'projectid': self.get_project(key='id'),
            'zoneid': self.get_zone(key='id'),
            'vpcid': vpc_id,
        }
        vms = self.cs.listVirtualMachines(**args)
        if vms:
            for v in vms['virtualmachine']:
                # Due the limitation of the API, there is no easy way (yet) to get only those VMs
                # not belonging to a VPC.
                if not vpc_id and self.is_vm_in_vpc(vm=v):
                    continue
                if vm.lower() in [ v['name'].lower(), v['displayname'].lower(), v['id'] ]:
                    self.vm = v
                    return self._get_by_key(key, self.vm)
        self.module.fail_json(msg="Virtual machine '%s' not found" % vm)


    def get_zone(self, key=None):
        if self.zone:
            return self._get_by_key(key, self.zone)

        zone = self.module.params.get('zone')
        if not zone:
            zone = os.environ.get('CLOUDSTACK_ZONE')
        zones = self.cs.listZones()

        # use the first zone if no zone param given
        if not zone:
            self.zone = zones['zone'][0]
            return self._get_by_key(key, self.zone)

        if zones:
            for z in zones['zone']:
                if zone.lower() in [ z['name'].lower(), z['id'] ]:
                    self.zone = z
                    return self._get_by_key(key, self.zone)
        self.module.fail_json(msg="zone '%s' not found" % zone)


    def get_os_type(self, key=None):
        if self.os_type:
            return self._get_by_key(key, self.zone)

        os_type = self.module.params.get('os_type')
        if not os_type:
            return None

        os_types = self.cs.listOsTypes()
        if os_types:
            for o in os_types['ostype']:
                if os_type in [ o['description'], o['id'] ]:
                    self.os_type = o
                    return self._get_by_key(key, self.os_type)
        self.module.fail_json(msg="OS type '%s' not found" % os_type)


    def get_hypervisor(self):
        if self.hypervisor:
            return self.hypervisor

        hypervisor = self.module.params.get('hypervisor')
        hypervisors = self.cs.listHypervisors()

        # use the first hypervisor if no hypervisor param given
        if not hypervisor:
            self.hypervisor = hypervisors['hypervisor'][0]['name']
            return self.hypervisor

        for h in hypervisors['hypervisor']:
            if hypervisor.lower() == h['name'].lower():
                self.hypervisor = h['name']
                return self.hypervisor
        self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)


    def get_account(self, key=None):
        if self.account:
            return self._get_by_key(key, self.account)

        account = self.module.params.get('account')
        if not account:
            account = os.environ.get('CLOUDSTACK_ACCOUNT')
        if not account:
            return None

        domain = self.module.params.get('domain')
        if not domain:
            self.module.fail_json(msg="Account must be specified with Domain")

        args = {}
        args['name'] = account
        args['domainid'] = self.get_domain(key='id')
        args['listall'] = True
        accounts = self.cs.listAccounts(**args)
        if accounts:
            self.account = accounts['account'][0]
            return self._get_by_key(key, self.account)
        self.module.fail_json(msg="Account '%s' not found" % account)


    def get_domain(self, key=None):
        if self.domain:
            return self._get_by_key(key, self.domain)

        domain = self.module.params.get('domain')
        if not domain:
            domain = os.environ.get('CLOUDSTACK_DOMAIN')
        if not domain:
            return None

        args = {}
        args['listall'] = True
        domains = self.cs.listDomains(**args)
        if domains:
            for d in domains['domain']:
                if d['path'].lower() in [ domain.lower(), "root/" + domain.lower(), "root" + domain.lower() ]:
                    self.domain = d
                    return self._get_by_key(key, self.domain)
        self.module.fail_json(msg="Domain '%s' not found" % domain)


    def get_tags(self, resource=None):
        existing_tags = []
        for tag in resource.get('tags',[]):
            existing_tags.append({'key': tag['key'], 'value': tag['value']})
        return existing_tags


    def _process_tags(self, resource, resource_type, tags, operation="create"):
        if tags:
            self.result['changed'] = True
            if not self.module.check_mode:
                args = {}
                args['resourceids']  = resource['id']
                args['resourcetype'] = resource_type
                args['tags']         = tags
                if operation == "create":
                    response = self.cs.createTags(**args)
                else:
                    response = self.cs.deleteTags(**args)
                self.poll_job(response)


    def _tags_that_should_exist_or_be_updated(self, resource, tags):
        existing_tags = self.get_tags(resource)
        return [tag for tag in tags if tag not in existing_tags]


    def _tags_that_should_not_exist(self, resource, tags):
        existing_tags = self.get_tags(resource)
        return [tag for tag in existing_tags if tag not in tags]


    def ensure_tags(self, resource, resource_type=None):
        if not resource_type or not resource:
            self.module.fail_json(msg="Error: Missing resource or resource_type for tags.")

        if 'tags' in resource:
            tags = self.module.params.get('tags')
            if tags is not None:
                self._process_tags(resource, resource_type, self._tags_that_should_not_exist(resource, tags), operation="delete")
                self._process_tags(resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags))
                resource['tags'] = tags
        return resource


    def get_capabilities(self, key=None):
        if self.capabilities:
            return self._get_by_key(key, self.capabilities)
        capabilities = self.cs.listCapabilities()
        self.capabilities = capabilities['capability']
        return self._get_by_key(key, self.capabilities)


    # TODO: for backward compatibility only, remove if not used anymore
    def _poll_job(self, job=None, key=None):
        return self.poll_job(job=job, key=key)


    def poll_job(self, job=None, key=None):
        if 'jobid' in job:
            while True:
                res = self.cs.queryAsyncJobResult(jobid=job['jobid'])
                if res['jobstatus'] != 0 and 'jobresult' in res:
                    if 'errortext' in res['jobresult']:
                        self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext'])
                    if key and key in res['jobresult']:
                        job = res['jobresult'][key]
                    break
                time.sleep(2)
        return job


    def get_result(self, resource):
        if resource:
            returns = self.common_returns.copy()
            returns.update(self.returns)
            for search_key, return_key in returns.iteritems():
                if search_key in resource:
                    self.result[return_key] = resource[search_key]

            # Bad bad API does not always return int when it should.
            for search_key, return_key in self.returns_to_int.iteritems():
                if search_key in resource:
                    self.result[return_key] = int(resource[search_key])

            # Special handling for tags
            if 'tags' in resource:
                self.result['tags'] = []
                for tag in resource['tags']:
                    result_tag          = {}
                    result_tag['key']   = tag['key']
                    result_tag['value'] = tag['value']
                    self.result['tags'].append(result_tag)
        return self.result
try:
    from cs import CloudStack, CloudStackException, read_config
except ImportError:
    print 'python library required: cs'
    print 'pip install <library>'
    sys.exit(1)

parser = argparse.ArgumentParser()

parser.add_argument('--project')
parser.add_argument('--zone')

args = parser.parse_args()

try:
    cs = CloudStack(**read_config())

    project_id = ''
    if args.project:
        projects = cs.listProjects(listall=True)
        if projects:
            for p in projects['project']:
                if p['name'] == args.project:
                    project_id = p['id']
                    break

    if not project_id:
        print "project %s not found" % args.project
        sys.exit(1)

    zone_id = ''