<commit_before># Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# libcloud.org licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Amazon EC2 driver
"""
from libcloud.providers import Provider
from libcloud.types import NodeState, InvalidCredsException
from libcloud.base import Node, Response, ConnectionUserAndKey, NodeDriver, NodeSize, NodeImage, NodeLocation
import base64
import hmac
from hashlib import sha256
import time
import urllib
from xml.etree import ElementTree as ET

EC2_US_EAST_HOST = 'ec2.us-east-1.amazonaws.com'
EC2_US_WEST_HOST = 'ec2.us-west-1.amazonaws.com'
EC2_EU_WEST_HOST = 'ec2.eu-west-1.amazonaws.com'

API_VERSION = '2009-04-04'
NAMESPACE = "http://ec2.amazonaws.com/doc/%s/" % (API_VERSION)

# Sizes must be hardcoded, because amazon doesn't provide an API to fetch them.
# From http://aws.amazon.com/ec2/instance-types/
EC2_INSTANCE_TYPES = {'m1.small': {'id': 'm1.small',
                       'name': 'Small Instance',
                       'ram': 1740,
                       'disk': 160,
                       'bandwidth': None},
                      'm1.large': {'id': 'm1.large',
                       'name': 'Large Instance',
                       'ram': 7680,
                       'disk': 850,
                       'bandwidth': None},
                      'm1.xlarge': {'id': 'm1.xlarge',
                       'name': 'Extra Large Instance',
                       'ram': 15360,
                       'disk': 1690,
                       'bandwidth': None},
                      'c1.medium': {'id': 'c1.medium',
                       'name': 'High-CPU Medium Instance',
                       'ram': 1740,
                       'disk': 350,
                       'bandwidth': None},
                      'c1.xlarge': {'id': 'c1.xlarge',
                       'name': 'High-CPU Extra Large Instance',
                       'ram': 7680,
                       'disk': 1690,
                       'bandwidth': None},
                      'm2.2xlarge': {'id': 'm2.2xlarge',
                       'name': 'High-Memory Double Extra Large Instance',
                       'ram': 35021,
                       'disk': 850,
                       'bandwidth': None},
                      'm2.4xlarge': {'id': 'm2.4xlarge',
                       'name': 'High-Memory Quadruple Extra Large Instance',
                       'ram': 70042,
                       'disk': 1690,
                       'bandwidth': None},
                       }

EC2_US_EAST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)
EC2_US_WEST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)
EC2_EU_WEST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)

EC2_US_EAST_INSTANCE_TYPES['m1.small']['price'] = '.085'
EC2_US_EAST_INSTANCE_TYPES['m1.large']['price'] = '.34'
EC2_US_EAST_INSTANCE_TYPES['m1.xlarge']['price'] = '.68'
EC2_US_EAST_INSTANCE_TYPES['c1.medium']['price'] = '.17'
EC2_US_EAST_INSTANCE_TYPES['c1.xlarge']['price'] = '.68'
EC2_US_EAST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.2'
EC2_US_EAST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.4'

EC2_US_WEST_INSTANCE_TYPES['m1.small']['price'] = '.095'
EC2_US_WEST_INSTANCE_TYPES['m1.large']['price'] = '.38'
EC2_US_WEST_INSTANCE_TYPES['m1.xlarge']['price'] = '.76'
EC2_US_WEST_INSTANCE_TYPES['c1.medium']['price'] = '.19'
EC2_US_WEST_INSTANCE_TYPES['c1.xlarge']['price'] = '.76'
EC2_US_WEST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.34'
EC2_US_WEST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.68'

EC2_EU_WEST_INSTANCE_TYPES['m1.small']['price'] = '.095'
EC2_EU_WEST_INSTANCE_TYPES['m1.large']['price'] = '.38'
EC2_EU_WEST_INSTANCE_TYPES['m1.xlarge']['price'] = '.76'
EC2_EU_WEST_INSTANCE_TYPES['c1.medium']['price'] = '.19'
EC2_EU_WEST_INSTANCE_TYPES['c1.xlarge']['price'] = '.76'
EC2_EU_WEST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.34'
EC2_EU_WEST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.68'

class EC2Response(Response):

    def parse_body(self):
        if not self.body:
            return None
        return ET.XML(self.body)

    def parse_error(self):
        err_list = []
        for err in ET.XML(self.body).findall('Errors/Error'):
            code, message = err.getchildren()
            err_list.append("%s: %s" % (code.text, message.text))
            if code.text == "InvalidClientTokenId":
                raise InvalidCredsException(err_list[-1])
            if code.text == "SignatureDoesNotMatch":
                raise InvalidCredsException(err_list[-1])
        return "\n".join(err_list)

class EC2Connection(ConnectionUserAndKey):

    host = EC2_US_EAST_HOST
    responseCls = EC2Response

    def add_default_params(self, params):
        params['SignatureVersion'] = '2'
        params['SignatureMethod'] = 'HmacSHA256'
        params['AWSAccessKeyId'] = self.user_id
        params['Version'] = API_VERSION
        params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
                                            time.gmtime())
        params['Signature'] = self._get_aws_auth_param(params, self.key)
        return params

    def _get_aws_auth_param(self, params, secret_key, path='/'):
        """
        creates the signature required for AWS, per:
        http://docs.amazonwebservices.com/AWSEC2/2009-04-04/DeveloperGuide/index.html?using-query-api.html#query-authentication
        """
        """
            StringToSign = HTTPVerb + "\n" +
                           ValueOfHostHeaderInLowercase + "\n" +
                           HTTPRequestURI + "\n" +
                           CanonicalizedQueryString <from the preceding step>
        """
        keys = params.keys()
        keys.sort()
        pairs = []
        for key in keys:
            pairs.append(urllib.quote(key, safe='') + '=' +
                         urllib.quote(params[key], safe='-_~'))

        qs = '&'.join(pairs)
        string_to_sign = '\n'.join(('GET', self.host, path, qs))

        b64_hmac = base64.b64encode(
                        hmac.new(secret_key, string_to_sign,
                            digestmod=sha256).digest())
        return b64_hmac

class EC2NodeDriver(NodeDriver):

    connectionCls = EC2Connection
    type = Provider.EC2
    name = 'Amazon EC2 (us-east-1)'

    _instance_types = EC2_US_EAST_INSTANCE_TYPES

    NODE_STATE_MAP = { 'pending': NodeState.PENDING,
                       'running': NodeState.RUNNING,
                       'shutting-down': NodeState.TERMINATED,
                       'terminated': NodeState.TERMINATED }

    def _findtext(self, element, xpath):
        return element.findtext(self._fixxpath(xpath))

    def _fixxpath(self, xpath):
        # ElementTree wants namespaces in its xpaths, so here we add them.
        return "/".join(["{%s}%s" % (NAMESPACE, e) for e in xpath.split("/")])

    def _findattr(self, element, xpath):
        return element.findtext(self._fixxpath(xpath))

    def _findall(self, element, xpath):
        return element.findall(self._fixxpath(xpath))

    def _pathlist(self, key, arr):
        """Converts a key and an array of values into AWS query param
           format."""
        params = {}
        i = 0
        for value in arr:
            i += 1
            params["%s.%s" % (key, i)] = value
        return params

    def _get_boolean(self, element):
        tag = "{%s}%s" % (NAMESPACE, 'return')
        return element.findtext(tag) == 'true'

    def _get_terminate_boolean(self, element):
        status = element.findtext(".//{%s}%s" % (NAMESPACE, 'name'))
        return any([ term_status == status for term_status
                     in ('shutting-down', 'terminated') ])

    def _to_nodes(self, object, xpath):
        return [ self._to_node(el)
                 for el in object.findall(
                    self._fixxpath(xpath)) ]

    def _to_node(self, element):
        try:
            state = self.NODE_STATE_MAP[self._findattr(element,
                                        "instanceState/name")]
        except KeyError:
            state = NodeState.UNKNOWN

        n = Node(id=self._findtext(element, 'instanceId'),
                 name=self._findtext(element, 'instanceId'),
                 state=state,
                 public_ip=[self._findtext(element, 'dnsName')],
                 private_ip=[self._findtext(element, 'privateDnsName')],
                 driver=self.connection.driver,
                 extra = {
                  'dns_name': self._findattr(element, "dnsName"),
                  'instanceId': self._findattr(element, "instanceId"),
                  'imageId': self._findattr(element, "imageId"),
                  'private_dns': self._findattr(element, "privateDnsName"),
                  'status': self._findattr(element, "instanceState/name"),
                  'keyname': self._findattr(element, "keyName"),
                  'launchindex': self._findattr(element, "amiLaunchIndex"),
                  'productcode': [p.text for p in self._findall(element, "productCodesSet/item/productCode")],
                  'instancetype': self._findattr(element, "instanceType"),
                  'launchdatetime': self._findattr(element, "launchTime"),
                  'availability': self._findattr(element, "placement/availabilityZone"),
                  'kernelid': self._findattr(element, "kernelId"),
                  'ramdiskid': self._findattr(element, "ramdiskId")
                 })
        return n

    def _to_images(self, object):
        return [ self._to_image(el)
                 for el in object.findall(
                    self._fixxpath('imagesSet/item')) ]

    def _to_image(self, element):
        n = NodeImage(id=self._findtext(element, 'imageId'),
                      name=self._findtext(element, 'imageLocation'),
                      driver=self.connection.driver)
        return n

    def list_nodes(self):
        params = {'Action': 'DescribeInstances' }
        nodes = self._to_nodes(
                    self.connection.request('/', params=params).object,
                    'reservationSet/item/instancesSet/item')
        return nodes

    def list_sizes(self):
        return [ NodeSize(driver=self.connection.driver, **i)
                    for i in self._instance_types.values() ]

    def list_images(self):
        params = {'Action': 'DescribeImages'}
        images = self._to_images(
                    self.connection.request('/', params=params).object)
        return images

    def create_security_group(self, name, description):
        params = {'Action': 'CreateSecurityGroup',
                  'GroupName': name,
                  'GroupDescription': description}
        return self.connection.request('/', params=params).object

    def authorize_security_group_permissive(self, name):
        results = []
        params = {'Action': 'AuthorizeSecurityGroupIngress',
                  'GroupName': name,
                  'IpProtocol': 'tcp',
                  'FromPort': '0',
                  'ToPort': '65535',
                  'CidrIp': '0.0.0.0/0'}
        try:
            results.append(self.connection.request('/', params=params.copy()).object)
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e
        params['IpProtocol'] = 'udp'

        try:
            results.append(self.connection.request('/', params=params.copy()).object)
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e

        params.update({'IpProtocol': 'icmp', 'FromPort': '-1', 'ToPort': '-1'})

        try:
            results.append(self.connection.request('/', params=params.copy()).object)
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e
        return results

    # name doesn't apply to EC2 nodes.
    def create_node(self, **kwargs):
        name = kwargs["name"]
        image = kwargs["image"]
        size = kwargs["size"]
        params = {'Action': 'RunInstances',
                  'ImageId': image.id,
                  'MinCount': kwargs.get('mincount','1'),
                  'MaxCount': kwargs.get('maxcount','1'),
                  'InstanceType': size.id}

        try: params['SecurityGroup'] = kwargs['securitygroup']
        except KeyError: pass

        try: params['KeyName'] = kwargs['keyname']
        except KeyError: pass

        try: params['UserData'] = base64.b64encode(kwargs['userdata'])
        except KeyError: pass

        object = self.connection.request('/', params=params).object
        nodes = self._to_nodes(object, 'instancesSet/item')

        if len(nodes) == 1:
            return nodes[0]
        else: return nodes

    def reboot_node(self, node):
        """
        Reboot the node by passing in the node object
        """
        params = {'Action': 'RebootInstances'}
        params.update(self._pathlist('InstanceId', [node.id]))
        res = self.connection.request('/', params=params).object
        return self._get_boolean(res)

    def destroy_node(self, node):
        """
        Destroy node by passing in the node object
        """
        params = {'Action': 'TerminateInstances'}
        params.update(self._pathlist('InstanceId', [node.id]))
        res = self.connection.request('/', params=params).object
        return self._get_terminate_boolean(res)

    def list_locations(self):
        return [NodeLocation(0, "Amazon US N. Virginia", 'US', self)]

class EC2EUConnection(EC2Connection):

    host = EC2_EU_WEST_HOST

class EC2EUNodeDriver(EC2NodeDriver):

    connectionCls = EC2EUConnection
    _instance_types = EC2_EU_WEST_INSTANCE_TYPES
    def list_locations(self):
        return [NodeLocation(0, "Amazon Europe Ireland", 'IE', self)]

class EC2USWestConnection(EC2Connection):

    host = EC2_US_WEST_HOST

class EC2USWestNodeDriver(EC2NodeDriver):

    connectionCls = EC2USWestConnection
    _instance_types = EC2_US_WEST_INSTANCE_TYPES
    def list_locations(self):
        return [NodeLocation(0, "Amazon US N. California", 'US', self)]
<commit_msg>80-col formatting update: drivers/ec2.py<commit_after># Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# libcloud.org licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Amazon EC2 driver
"""
from libcloud.providers import Provider
from libcloud.types import NodeState, InvalidCredsException
from libcloud.base import Node, Response, ConnectionUserAndKey
from libcloud.base import NodeDriver, NodeSize, NodeImage, NodeLocation
import base64
import hmac
from hashlib import sha256
import time
import urllib
from xml.etree import ElementTree as ET

EC2_US_EAST_HOST = 'ec2.us-east-1.amazonaws.com'
EC2_US_WEST_HOST = 'ec2.us-west-1.amazonaws.com'
EC2_EU_WEST_HOST = 'ec2.eu-west-1.amazonaws.com'

API_VERSION = '2009-04-04'
NAMESPACE = "http://ec2.amazonaws.com/doc/%s/" % (API_VERSION)

"""
Sizes must be hardcoded, because Amazon doesn't provide an API to fetch them.
From http://aws.amazon.com/ec2/instance-types/
"""
EC2_INSTANCE_TYPES = {
    'm1.small': {
        'id': 'm1.small',
        'name': 'Small Instance',
        'ram': 1740,
        'disk': 160,
        'bandwidth': None
    },
    'm1.large': {
        'id': 'm1.large',
        'name': 'Large Instance',
        'ram': 7680,
        'disk': 850,
        'bandwidth': None
    },
    'm1.xlarge': {
        'id': 'm1.xlarge',
        'name': 'Extra Large Instance',
        'ram': 15360,
        'disk': 1690,
        'bandwidth': None
    },
    'c1.medium': {
        'id': 'c1.medium',
        'name': 'High-CPU Medium Instance',
        'ram': 1740,
        'disk': 350,
        'bandwidth': None
    },
    'c1.xlarge': {
        'id': 'c1.xlarge',
        'name': 'High-CPU Extra Large Instance',
        'ram': 7680,
        'disk': 1690,
        'bandwidth': None
    },
    'm2.2xlarge': {
        'id': 'm2.2xlarge',
        'name': 'High-Memory Double Extra Large Instance',
        'ram': 35021,
        'disk': 850,
        'bandwidth': None
    },
    'm2.4xlarge': {
        'id': 'm2.4xlarge',
        'name': 'High-Memory Quadruple Extra Large Instance',
        'ram': 70042,
        'disk': 1690,
        'bandwidth': None
    },
}

EC2_US_EAST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)
EC2_US_WEST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)
EC2_EU_WEST_INSTANCE_TYPES = dict(EC2_INSTANCE_TYPES)

EC2_US_EAST_INSTANCE_TYPES['m1.small']['price'] = '.085'
EC2_US_EAST_INSTANCE_TYPES['m1.large']['price'] = '.34'
EC2_US_EAST_INSTANCE_TYPES['m1.xlarge']['price'] = '.68'
EC2_US_EAST_INSTANCE_TYPES['c1.medium']['price'] = '.17'
EC2_US_EAST_INSTANCE_TYPES['c1.xlarge']['price'] = '.68'
EC2_US_EAST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.2'
EC2_US_EAST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.4'

EC2_US_WEST_INSTANCE_TYPES['m1.small']['price'] = '.095'
EC2_US_WEST_INSTANCE_TYPES['m1.large']['price'] = '.38'
EC2_US_WEST_INSTANCE_TYPES['m1.xlarge']['price'] = '.76'
EC2_US_WEST_INSTANCE_TYPES['c1.medium']['price'] = '.19'
EC2_US_WEST_INSTANCE_TYPES['c1.xlarge']['price'] = '.76'
EC2_US_WEST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.34'
EC2_US_WEST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.68'

EC2_EU_WEST_INSTANCE_TYPES['m1.small']['price'] = '.095'
EC2_EU_WEST_INSTANCE_TYPES['m1.large']['price'] = '.38'
EC2_EU_WEST_INSTANCE_TYPES['m1.xlarge']['price'] = '.76'
EC2_EU_WEST_INSTANCE_TYPES['c1.medium']['price'] = '.19'
EC2_EU_WEST_INSTANCE_TYPES['c1.xlarge']['price'] = '.76'
EC2_EU_WEST_INSTANCE_TYPES['m2.2xlarge']['price'] = '1.34'
EC2_EU_WEST_INSTANCE_TYPES['m2.4xlarge']['price'] = '2.68'

class EC2Response(Response):

    def parse_body(self):
        if not self.body:
            return None
        return ET.XML(self.body)

    def parse_error(self):
        err_list = []
        for err in ET.XML(self.body).findall('Errors/Error'):
            code, message = err.getchildren()
            err_list.append("%s: %s" % (code.text, message.text))
            if code.text == "InvalidClientTokenId":
                raise InvalidCredsException(err_list[-1])
            if code.text == "SignatureDoesNotMatch":
                raise InvalidCredsException(err_list[-1])
        return "\n".join(err_list)

class EC2Connection(ConnectionUserAndKey):

    host = EC2_US_EAST_HOST
    responseCls = EC2Response

    def add_default_params(self, params):
        params['SignatureVersion'] = '2'
        params['SignatureMethod'] = 'HmacSHA256'
        params['AWSAccessKeyId'] = self.user_id
        params['Version'] = API_VERSION
        params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
                                            time.gmtime())
        params['Signature'] = self._get_aws_auth_param(params, self.key)
        return params

    def _get_aws_auth_param(self, params, secret_key, path='/'):
        """
        Creates the signature required for AWS, per
        http://bit.ly/aR7GaQ [docs.amazonwebservices.com]:

        StringToSign = HTTPVerb + "\n" +
                       ValueOfHostHeaderInLowercase + "\n" +
                       HTTPRequestURI + "\n" +
                       CanonicalizedQueryString <from the preceding step>
        """
        keys = params.keys()
        keys.sort()
        pairs = []
        for key in keys:
            pairs.append(urllib.quote(key, safe='') + '=' +
                         urllib.quote(params[key], safe='-_~'))

        qs = '&'.join(pairs)
        string_to_sign = '\n'.join(('GET', self.host, path, qs))

        b64_hmac = base64.b64encode(
            hmac.new(secret_key, string_to_sign, digestmod=sha256).digest()
        )
        return b64_hmac

class EC2NodeDriver(NodeDriver):

    connectionCls = EC2Connection
    type = Provider.EC2
    name = 'Amazon EC2 (us-east-1)'

    _instance_types = EC2_US_EAST_INSTANCE_TYPES

    NODE_STATE_MAP = {
        'pending': NodeState.PENDING,
        'running': NodeState.RUNNING,
        'shutting-down': NodeState.TERMINATED,
        'terminated': NodeState.TERMINATED
    }

    def _findtext(self, element, xpath):
        return element.findtext(self._fixxpath(xpath))

    def _fixxpath(self, xpath):
        # ElementTree wants namespaces in its xpaths, so here we add them.
        return "/".join(["{%s}%s" % (NAMESPACE, e) for e in xpath.split("/")])

    def _findattr(self, element, xpath):
        return element.findtext(self._fixxpath(xpath))

    def _findall(self, element, xpath):
        return element.findall(self._fixxpath(xpath))

    def _pathlist(self, key, arr):
        """
        Converts a key and an array of values into AWS query param format.
        """
        params = {}
        i = 0
        for value in arr:
            i += 1
            params["%s.%s" % (key, i)] = value
        return params

    def _get_boolean(self, element):
        tag = "{%s}%s" % (NAMESPACE, 'return')
        return element.findtext(tag) == 'true'

    def _get_terminate_boolean(self, element):
        status = element.findtext(".//{%s}%s" % (NAMESPACE, 'name'))
        return any([ term_status == status
                     for term_status
                     in ('shutting-down', 'terminated') ])

    def _to_nodes(self, object, xpath):
        return [ self._to_node(el)
                 for el in object.findall(self._fixxpath(xpath)) ]

    def _to_node(self, element):
        try:
            state = self.NODE_STATE_MAP[
                self._findattr(element, "instanceState/name")
            ]
        except KeyError:
            state = NodeState.UNKNOWN

        n = Node(
            id=self._findtext(element, 'instanceId'),
            name=self._findtext(element, 'instanceId'),
            state=state,
            public_ip=[self._findtext(element, 'dnsName')],
            private_ip=[self._findtext(element, 'privateDnsName')],
            driver=self.connection.driver,
            extra={
                'dns_name': self._findattr(element, "dnsName"),
                'instanceId': self._findattr(element, "instanceId"),
                'imageId': self._findattr(element, "imageId"),
                'private_dns': self._findattr(element, "privateDnsName"),
                'status': self._findattr(element, "instanceState/name"),
                'keyname': self._findattr(element, "keyName"),
                'launchindex': self._findattr(element, "amiLaunchIndex"),
                'productcode':
                    [p.text for p in self._findall(
                        element, "productCodesSet/item/productCode"
                     )],
                'instancetype': self._findattr(element, "instanceType"),
                'launchdatetime': self._findattr(element, "launchTime"),
                'availability': self._findattr(element,
                                               "placement/availabilityZone"),
                'kernelid': self._findattr(element, "kernelId"),
                'ramdiskid': self._findattr(element, "ramdiskId")
            }
        )
        return n

    def _to_images(self, object):
        return [ self._to_image(el)
                 for el in object.findall(
                    self._fixxpath('imagesSet/item')
                 ) ]

    def _to_image(self, element):
        n = NodeImage(id=self._findtext(element, 'imageId'),
                      name=self._findtext(element, 'imageLocation'),
                      driver=self.connection.driver)
        return n

    def list_nodes(self):
        params = {'Action': 'DescribeInstances' }
        nodes = self._to_nodes(
                    self.connection.request('/', params=params).object,
                    'reservationSet/item/instancesSet/item')
        return nodes

    def list_sizes(self):
        return [ NodeSize(driver=self.connection.driver, **i)
                    for i in self._instance_types.values() ]

    def list_images(self):
        params = {'Action': 'DescribeImages'}
        images = self._to_images(
            self.connection.request('/', params=params).object
        )
        return images

    def create_security_group(self, name, description):
        params = {'Action': 'CreateSecurityGroup',
                  'GroupName': name,
                  'GroupDescription': description}
        return self.connection.request('/', params=params).object

    def authorize_security_group_permissive(self, name):
        results = []
        params = {'Action': 'AuthorizeSecurityGroupIngress',
                  'GroupName': name,
                  'IpProtocol': 'tcp',
                  'FromPort': '0',
                  'ToPort': '65535',
                  'CidrIp': '0.0.0.0/0'}
        try:
            results.append(
                self.connection.request('/', params=params.copy()).object
            )
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e
        params['IpProtocol'] = 'udp'

        try:
            results.append(
                self.connection.request('/', params=params.copy()).object
            )
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e

        params.update({'IpProtocol': 'icmp', 'FromPort': '-1', 'ToPort': '-1'})

        try:
            results.append(
                self.connection.request('/', params=params.copy()).object
            )
        except Exception, e:
            if e.args[0].find("InvalidPermission.Duplicate") == -1:
                raise e
        return results

    # name doesn't apply to EC2 nodes.
    def create_node(self, **kwargs):
        name = kwargs["name"]
        image = kwargs["image"]
        size = kwargs["size"]
        params = {'Action': 'RunInstances',
                  'ImageId': image.id,
                  'MinCount': kwargs.get('mincount','1'),
                  'MaxCount': kwargs.get('maxcount','1'),
                  'InstanceType': size.id}

        try: params['SecurityGroup'] = kwargs['securitygroup']
        except KeyError: pass

        try: params['KeyName'] = kwargs['keyname']
        except KeyError: pass

        try: params['UserData'] = base64.b64encode(kwargs['userdata'])
        except KeyError: pass

        object = self.connection.request('/', params=params).object
        nodes = self._to_nodes(object, 'instancesSet/item')

        if len(nodes) == 1:
            return nodes[0]
        else: return nodes

    def reboot_node(self, node):
        """
        Reboot the node by passing in the node object
        """
        params = {'Action': 'RebootInstances'}
        params.update(self._pathlist('InstanceId', [node.id]))
        res = self.connection.request('/', params=params).object
        return self._get_boolean(res)

    def destroy_node(self, node):
        """
        Destroy node by passing in the node object
        """
        params = {'Action': 'TerminateInstances'}
        params.update(self._pathlist('InstanceId', [node.id]))
        res = self.connection.request('/', params=params).object
        return self._get_terminate_boolean(res)

    def list_locations(self):
        return [NodeLocation(0, 'Amazon US N. Virginia', 'US', self)]

class EC2EUConnection(EC2Connection):

    host = EC2_EU_WEST_HOST

class EC2EUNodeDriver(EC2NodeDriver):

    connectionCls = EC2EUConnection
    _instance_types = EC2_EU_WEST_INSTANCE_TYPES
    def list_locations(self):
        return [NodeLocation(0, 'Amazon Europe Ireland', 'IE', self)]

class EC2USWestConnection(EC2Connection):

    host = EC2_US_WEST_HOST

class EC2USWestNodeDriver(EC2NodeDriver):

    connectionCls = EC2USWestConnection
    _instance_types = EC2_US_WEST_INSTANCE_TYPES
    def list_locations(self):
        return [NodeLocation(0, 'Amazon US N. California', 'US', self)]
<|endoftext|>


<commit_before># Copyright 2009 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""A base user-interface workflow, to be inherited by UI modules."""

import tempfile

import addr_util
import benchmark
import better_webbrowser
import config
import data_sources
import geoip
import nameserver
import reporter
import providers
import site_connector
import util

DEFAULT_DISTANCE_KM=2000

__author__ = 'tstromberg@google.com (Thomas Stromberg)'

class BaseUI(object):
  """Common methods for all UI implementations."""

  def __init__(self):
    self.SetupDataStructures()

  def SetupDataStructures(self):
    """Instead of requiring users to inherit __init__(), this sets up structures."""
    self.reporter = None
    self.nameservers = None
    self.bmark = None
    self.report_path = None
    self.csv_path = None
    self.geodata = None
    self.country = None
    self.sources = {}
    self.url = None
    self.share_state = None
    self.test_records = []

  def UpdateStatus(self, msg, **kwargs):
    """Update the little status message on the bottom of the window."""
    if hasattr(self, 'status_callback') and self.status_callback:
      self.status_callback(msg, **kwargs)
    else:
      print msg

  def DebugMsg(self, message):
    self.UpdateStatus(message, debug=True)

  def LoadDataSources(self):
    self.data_src = data_sources.DataSources(status_callback=self.UpdateStatus)

  def PrepareTestRecords(self):
    """Figure out what data source a user wants, and create test_records."""
    if self.options.input_source:
      src_type = self.options.input_source
    else:
      src_type = self.data_src.GetBestSourceDetails()[0]
      self.options.input_source = src_type

    self.test_records = self.data_src.GetTestsFromSource(
        src_type,
        self.options.query_count,
        select_mode=self.options.select_mode
    )

  def GatherNameServerData(self):
    """Build a nameserver data set from config and other sources."""

    ns_data = config.GetNameServerData()
    for i, ip in enumerate(self.options.servers):
      ns = nameserver.NameServer(ip, tags=['specified'], name='USR%s-%s' % (i, ip))
      ns_data.append(ns)
    return ns_data

  def PrepareNameServers(self, distance=DEFAULT_DISTANCE_KM):
    """Setup self.nameservers to have a list of healthy fast servers."""
    self.nameservers = self.GatherNameServerData()
    require_tags = set()
    include_tags = self.options.tags

    if self.options.ipv6_only:
      require_tags.add('ipv6')
    elif self.options.ipv4_only:
      require_tags.add('ipv4')

    if 'regional' in self.options.tags:
      country, lat, lon = self.ConfiguredLocationData()
      if country:
        self.nameservers.SetClientLocation(lat, lon, country)
      client_ip = providers.MyResolverInfo().ClientIp()
      local_ns = providers.SystemResolver()
      try:
        hostname = local_ns.GetReverseIp(client_ip)
        domain = addr_util.GetDomainFromHostname(hostname)
      except:
        domain = 'UNKNOWN'
      self.nameservers.SetNetworkLocation(domain, local_ns.GetAsnForIp(client_ip))
      new_tags = self.nameservers.AddLocalityTags(max_distance=DEFAULT_DISTANCE_KM)
      include_tags.discard('regional')
      include_tags.update(new_tags)

    self.nameservers.status_callback = self.UpdateStatus
    self.UpdateStatus("DNS server filter: %s %s" % (','.join(include_tags),
                                                    ','.join(require_tags)))
    self.nameservers.FilterByTag(include_tags=include_tags,
                                 require_tags=require_tags)

  def ConfiguredLocationData(self):
    if not self.geodata:
      self.DiscoverLocation()
      country_code = self.geodata.get('country_code')
      lat = self.geodata.get('latitude')
      lon = self.geodata.get('longitude')

    if self.options.country and self.options.country != country_code:
      country_code, lat, lon = config.GetCodeAndCoordinatesForCountry(self.options.country)
      self.UpdateStatus("Set country to %s (%s,%s)" % (country_code, lat, lon))

    return country_code, lat, lon

  def CheckNameServerHealth(self):
    self.nameservers.SetTimeouts(self.options.timeout,
                                 self.options.ping_timeout,
                                 self.options.health_timeout)
    self.nameservers.CheckHealth(sanity_checks=config.GetSanityChecks())

  def PrepareBenchmark(self):
    """Setup the benchmark object with the appropriate dataset."""
    if len(self.nameservers) == 1:
      thread_count = 1
    else:
      thread_count = self.options.benchmark_thread_count

    self.bmark = benchmark.Benchmark(self.nameservers,
                                     query_count=self.options.query_count,
                                     run_count=self.options.run_count,
                                     thread_count=thread_count,
                                     status_callback=self.UpdateStatus)

  def RunBenchmark(self):
    """Run the benchmark."""
    results = self.bmark.Run(self.test_records)
    index = []
    if self.options.upload_results in (1, True):
      connector = site_connector.SiteConnector(self.options, status_callback=self.UpdateStatus)
      index_hosts = connector.GetIndexHosts()
      if index_hosts:
        index = self.bmark.RunIndex(index_hosts)
      else:
        index = []
      self.DiscoverLocation()
      if len(self.nameservers) > 1:
        self.nameservers.RunPortBehaviorThreads()

    self.reporter = reporter.ReportGenerator(self.options, self.nameservers,
                                             results, index=index, geodata=self.geodata)

  def DiscoverLocation(self):
    if not getattr(self, 'geodata', None):
      self.geodata = geoip.GetGeoData()
    return self.geodata

  def RunAndOpenReports(self):
    """Run the benchmark and open up the report on completion."""
    self.RunBenchmark()
    best = self.reporter.BestOverallNameServer()
    self.CreateReports()
    if self.options.template == 'html':
      self.DisplayHtmlReport()
    if self.url:
      self.UpdateStatus('Complete! Your results: %s' % self.url)
    else:
      self.UpdateStatus('Complete! %s [%s] is the best.' % (best.name, best.ip))

  def CreateReports(self):
    """Create CSV & HTML reports for the latest run."""

    if self.options.output_file:
      self.report_path = self.options.output_file
    else:
      self.report_path = util.GenerateOutputFilename(self.options.template)

    if self.options.csv_file:
      self.csv_path = self.options_csv_file
    else:
      self.csv_path = util.GenerateOutputFilename('csv')

    if self.options.upload_results in (1, True):
      # This is for debugging and transparency only.
      self.json_path = util.GenerateOutputFilename('js')
      self.UpdateStatus('Saving anonymized JSON to %s' % self.json_path)
      json_data = self.reporter.CreateJsonData()
      f = open(self.json_path, 'w')
      f.write(json_data)
      f.close()

      self.UpdateStatus('Uploading results to %s' % self.options.site_url)
      connector = site_connector.SiteConnector(self.options, status_callback=self.UpdateStatus)
      self.url, self.share_state = connector.UploadJsonResults(
          json_data,
          hide_results=self.options.hide_results
      )

      if self.url:
        self.UpdateStatus('Your sharing URL: %s (%s)' % (self.url, self.share_state))

    self.UpdateStatus('Saving report to %s' % self.report_path)
    f = open(self.report_path, 'w')
    self.reporter.CreateReport(format=self.options.template,
                               output_fp=f,
                               csv_path=self.csv_path,
                               sharing_url=self.url,
                               sharing_state=self.share_state)
    f.close()

    self.UpdateStatus('Saving detailed results to %s' % self.csv_path)
    self.reporter.SaveResultsToCsv(self.csv_path)

  def DisplayHtmlReport(self):
    self.UpdateStatus('Opening %s' % self.report_path)
    better_webbrowser.output = self.DebugMsg
    better_webbrowser.open(self.report_path)

<commit_msg>Make sure isp and network tags get populated with ASN/domain info. Improve output<commit_after># Copyright 2009 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""A base user-interface workflow, to be inherited by UI modules."""

import tempfile

import addr_util
import benchmark
import better_webbrowser
import config
import data_sources
import geoip
import nameserver
import reporter
import providers
import site_connector
import util

DEFAULT_DISTANCE_KM=2000

__author__ = 'tstromberg@google.com (Thomas Stromberg)'

class BaseUI(object):
  """Common methods for all UI implementations."""

  def __init__(self):
    self.SetupDataStructures()

  def SetupDataStructures(self):
    """Instead of requiring users to inherit __init__(), this sets up structures."""
    self.reporter = None
    self.nameservers = None
    self.bmark = None
    self.report_path = None
    self.csv_path = None
    self.geodata = None
    self.country = None
    self.sources = {}
    self.url = None
    self.share_state = None
    self.test_records = []

  def UpdateStatus(self, msg, **kwargs):
    """Update the little status message on the bottom of the window."""
    if hasattr(self, 'status_callback') and self.status_callback:
      self.status_callback(msg, **kwargs)
    else:
      print msg

  def DebugMsg(self, message):
    self.UpdateStatus(message, debug=True)

  def LoadDataSources(self):
    self.data_src = data_sources.DataSources(status_callback=self.UpdateStatus)

  def PrepareTestRecords(self):
    """Figure out what data source a user wants, and create test_records."""
    if self.options.input_source:
      src_type = self.options.input_source
    else:
      src_type = self.data_src.GetBestSourceDetails()[0]
      self.options.input_source = src_type

    self.test_records = self.data_src.GetTestsFromSource(
        src_type,
        self.options.query_count,
        select_mode=self.options.select_mode
    )

  def GatherNameServerData(self):
    """Build a nameserver data set from config and other sources."""

    ns_data = config.GetNameServerData()
    for i, ip in enumerate(self.options.servers):
      ns = nameserver.NameServer(ip, tags=['specified'], name='USR%s-%s' % (i, ip))
      ns_data.append(ns)
    return ns_data

  def PrepareNameServers(self, distance=DEFAULT_DISTANCE_KM):
    """Setup self.nameservers to have a list of healthy fast servers."""
    self.nameservers = self.GatherNameServerData()
    require_tags = set()
    include_tags = self.options.tags

    if self.options.ipv6_only:
      require_tags.add('ipv6')
    elif self.options.ipv4_only:
      require_tags.add('ipv4')

    if 'regional' in self.options.tags:
      country, lat, lon = self.ConfiguredLocationData()
      if country:
        self.UpdateStatus("Detected country as %s (%s,%s)" % (country, lat, lon))
        self.nameservers.SetClientLocation(lat, lon, country)

    if self.options.tags.intersection(set(['regional','isp','network'])):
      client_ip = providers.MyResolverInfo().ClientIp()
      local_ns = providers.SystemResolver()
      try:
        hostname = local_ns.GetReverseIp(client_ip)
        domain = addr_util.GetDomainFromHostname(hostname)
      except:
        domain = 'UNKNOWN'
      asn = local_ns.GetAsnForIp(client_ip)
      self.nameservers.SetNetworkLocation(domain, asn)
      self.UpdateStatus("Detected ISP as %s (AS%s)" % (domain, asn))
      new_tags = self.nameservers.AddLocalityTags(max_distance=DEFAULT_DISTANCE_KM)

    if 'regional' in self.options.tags:
      include_tags.discard('regional')
      include_tags.update(new_tags)

    self.nameservers.status_callback = self.UpdateStatus
    self.UpdateStatus("DNS server filter: %s %s" % (','.join(include_tags),
                                                    ','.join(require_tags)))
    self.nameservers.FilterByTag(include_tags=include_tags,
                                 require_tags=require_tags)

  def ConfiguredLocationData(self):
    if not self.geodata:
      self.DiscoverLocation()
      country_code = self.geodata.get('country_code')
      lat = self.geodata.get('latitude')
      lon = self.geodata.get('longitude')

    if self.options.country and self.options.country != country_code:
      country_code, lat, lon = config.GetCodeAndCoordinatesForCountry(self.options.country)
      self.UpdateStatus("Set country to %s (%s,%s)" % (country_code, lat, lon))

    return country_code, lat, lon

  def CheckNameServerHealth(self):
    self.nameservers.SetTimeouts(self.options.timeout,
                                 self.options.ping_timeout,
                                 self.options.health_timeout)
    self.nameservers.CheckHealth(sanity_checks=config.GetSanityChecks())

  def PrepareBenchmark(self):
    """Setup the benchmark object with the appropriate dataset."""
    if len(self.nameservers) == 1:
      thread_count = 1
    else:
      thread_count = self.options.benchmark_thread_count

    self.bmark = benchmark.Benchmark(self.nameservers,
                                     query_count=self.options.query_count,
                                     run_count=self.options.run_count,
                                     thread_count=thread_count,
                                     status_callback=self.UpdateStatus)

  def RunBenchmark(self):
    """Run the benchmark."""
    results = self.bmark.Run(self.test_records)
    index = []
    if self.options.upload_results in (1, True):
      connector = site_connector.SiteConnector(self.options, status_callback=self.UpdateStatus)
      index_hosts = connector.GetIndexHosts()
      if index_hosts:
        index = self.bmark.RunIndex(index_hosts)
      else:
        index = []
      self.DiscoverLocation()
      if len(self.nameservers) > 1:
        self.nameservers.RunPortBehaviorThreads()

    self.reporter = reporter.ReportGenerator(self.options, self.nameservers,
                                             results, index=index, geodata=self.geodata)

  def DiscoverLocation(self):
    if not getattr(self, 'geodata', None):
      self.geodata = geoip.GetGeoData()
    return self.geodata

  def RunAndOpenReports(self):
    """Run the benchmark and open up the report on completion."""
    self.RunBenchmark()
    best = self.reporter.BestOverallNameServer()
    self.CreateReports()
    if self.options.template == 'html':
      self.DisplayHtmlReport()
    if self.url:
      self.UpdateStatus('Complete! Your results: %s' % self.url)
    else:
      self.UpdateStatus('Complete! %s [%s] is the best.' % (best.name, best.ip))

  def CreateReports(self):
    """Create CSV & HTML reports for the latest run."""

    if self.options.output_file:
      self.report_path = self.options.output_file
    else:
      self.report_path = util.GenerateOutputFilename(self.options.template)

    if self.options.csv_file:
      self.csv_path = self.options_csv_file
    else:
      self.csv_path = util.GenerateOutputFilename('csv')

    if self.options.upload_results in (1, True):
      # This is for debugging and transparency only.
      self.json_path = util.GenerateOutputFilename('js')
      self.UpdateStatus('Saving anonymized JSON to %s' % self.json_path)
      json_data = self.reporter.CreateJsonData()
      f = open(self.json_path, 'w')
      f.write(json_data)
      f.close()

      self.UpdateStatus('Uploading results to %s' % self.options.site_url)
      connector = site_connector.SiteConnector(self.options, status_callback=self.UpdateStatus)
      self.url, self.share_state = connector.UploadJsonResults(
          json_data,
          hide_results=self.options.hide_results
      )

      if self.url:
        self.UpdateStatus('Your sharing URL: %s (%s)' % (self.url, self.share_state))

    self.UpdateStatus('Saving report to %s' % self.report_path)
    f = open(self.report_path, 'w')
    self.reporter.CreateReport(format=self.options.template,
                               output_fp=f,
                               csv_path=self.csv_path,
                               sharing_url=self.url,
                               sharing_state=self.share_state)
    f.close()

    self.UpdateStatus('Saving detailed results to %s' % self.csv_path)
    self.reporter.SaveResultsToCsv(self.csv_path)

  def DisplayHtmlReport(self):
    self.UpdateStatus('Opening %s' % self.report_path)
    better_webbrowser.output = self.DebugMsg
    better_webbrowser.open(self.report_path)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from devices.models import Device, Room, Building, Manufacturer
from devicetypes.models import Type
from devicegroups.models import Devicegroup
from users.models import Lageruser
from network.models import IpAddress
from mail.models import MailTemplate
from django.shortcuts import get_object_or_404
from devicetypes.models import Type
import json
from django.core.urlresolvers import reverse
from devices.forms import AddForm
from django.forms.models import modelform_factory
from django.template.loader import render_to_string
from django.template import Template, Context
from devicegroups.models import Devicegroup
from django.views.generic.base import View
from django.http import HttpResponse
import pystache
from django.http import QueryDict
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.db.models import Q
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.dateparse import parse_date
from devicetags.models import Devicetag

class AutocompleteDevice(View):
    def post(self, request):
        name = request.POST["name"]
        devices = Device.objects.filter(name__icontains = name )[:20]
        results = []
        for device in devices:
            device_json = {}
            device_json['id'] = device.pk
            device_json['label'] = device.name
            device_json['value'] = device.name
            results.append(device_json)
        return HttpResponse(json.dumps(results), content_type='application/json')

class AutocompleteName(View):
    def post(self, request):
        name = request.POST["name"]
        classtype = request.POST["classtype"]
        if classtype == "type":
            objects = Type.objects.filter(name__icontains = name )[:20]
            urlname = "type-detail"
        elif classtype == "room":
            objects = Room.objects.filter(name__icontains = name )[:20]
            urlname = "room-detail"
        elif classtype == "building":
            objects = Building.objects.filter(name__icontains = name )[:20]
            urlname = "building-detail"
        elif classtype == "manufacturer":
            objects = Manufacturer.objects.filter(name__icontains = name )[:20]
            urlname = "manufacturer-detail"
        else:
            return HttpResponse("")
        if len(objects) > 0:
            retobjects = ["
{1}
".format(
                reverse(urlname, kwargs={"pk":obj[0]}), obj[1])
                for obj in objects.values_list("pk", "name")]
            return HttpResponse(json.dumps(retobjects), content_type='application/json')
        else:
            return HttpResponse("")

class AddDeviceField(View):
    def post(self, request):
        dform = QueryDict(query_string=unicode(request.POST["form"]).encode('utf-8'))
        classname = dform["classname"]
        if classname == "manufacturer":
            form = modelform_factory(Manufacturer, form=AddForm)(dform)
        elif classname == "devicetype":
            form = modelform_factory(Type, form=AddForm)(dform)
        elif classname == "room":
            form = modelform_factory(Room, form=AddForm)(dform)
        elif classname == "group":
            form = modelform_factory(Devicegroup, form=AddForm)(dform)
        else:
            return HttpResponse("")
        data = {}
        if form.is_valid():
            if request.user.is_staff:
                classname = form.cleaned_data["classname"]
                if classname == "manufacturer":
                    newitem = Manufacturer()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                elif classname == "devicetype":
                    newitem = Type()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                elif classname == "room":
                    newitem = Room()
                    newitem.name = form.cleaned_data["name"]
                    newitem.building = form.cleaned_data["building"]
                    newitem.section = form.cleaned_data["section"]
                    newitem.save()
                elif classname == "group":
                    newitem = Devicegroup()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                data["id"] = newitem.pk
                data["name"] = newitem.name
                data["classname"] = classname
        else:
            print form.errors
            data["error"] = "Error: {0}".format(form.non_field_errors())
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadExtraform(View):
    def post(self, request):
        classname = request.POST["classname"]
        if classname == "manufacturer":
            form = modelform_factory(Manufacturer, form=AddForm)()
        elif classname == "devicetype":
            form = modelform_factory(Type, form=AddForm)()
        elif classname == "room":
            form = modelform_factory(Room, form=AddForm)()
        elif classname == "group":
            form = modelform_factory(Devicegroup, form=AddForm)()
        else:
            return HttpResponse("")

        return HttpResponse(render_to_string('snippets/formfields.html', {"form":form}))

class PreviewMail(View):
    def post(self, request):
        template = request.POST["template"]
        device = {
            "currentlending": request.POST.get("device[currentlending]", ""),
            "description": request.POST.get("device[description]", ""),
            "devicetype": request.POST.get("device[devicetype]", ""),
            "group": request.POST.get("device[group]", ""),
            "hostname": request.POST.get("device[hostname]", ""),
            "inventorynumber": request.POST.get("device[inventorynumber]", ""),
            "manufacturer": request.POST.get("device[manufacturer]", ""),
            "name": request.POST.get("device[name]", ""),
            "room": request.POST.get("device[room]", "") ,
            "serialnumber": request.POST.get("device[serialnumber]", ""),
            "templending": request.POST.get("device[templending]", ""),
            "webinterface": request.POST.get("device[webinterface]", "")
        }
        if template == "":
            return HttpResponse("")

        if device["manufacturer"] != "":
            device["manufacturer"] = get_object_or_404(Manufacturer, pk=device["manufacturer"])
        else:
            del device["manufacturer"]

        if device["devicetype"] != "":
            device["devicetype"] = get_object_or_404(Type, pk=device["devicetype"])
        else:
            del device["devicetype"]

        if device["room"] != "":
            device["room"] = get_object_or_404(Room, pk=device["room"])
        else:
            del device["room"]

        template = get_object_or_404(MailTemplate, pk=template)
        datadict = {}
        datadict["device"] = device
        datadict["user"] = {
            "username" : request.user.username,
            "first_name" : request.user.first_name,
            "last_name" : request.user.last_name
        }
        data = {}
        data["subject"] = pystache.render(template.subject, datadict)
        data["body"] = pystache.render(template.body, datadict).replace("\n", "
")
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadMailtemplate(View):
    def post(self, request):
        template = request.POST["template"]
        recipients = request.POST.get("recipients[]", [])
        if template == "":
            return HttpResponse("")
        template = get_object_or_404(MailTemplate, pk=template)
        data = {}
        data["subject"] = template.subject
        data["body"] = template.body
        if isinstance(recipients, unicode):
            recipients = [recipients]
        newrecipients = [obj for obj in recipients]
        newrecipients += [obj.content_type.name[0].lower()+str(obj.object_id) for obj in template.default_recipients.all()]
        newrecipients = list(set(newrecipients))
        data["recipients"] = newrecipients
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadSearchoptions(View):
    def post(self, request):
        term = request.POST["searchTerm"]
        if term[:4] == "not ":
            term = term[4:]
            invert = True
        else:
            invert = False
        facet = request.POST["facet"]
        if facet == "manufacturer":
            items = Manufacturer.objects.filter(name__icontains=term)
        elif facet == "devicetype":
            items = Type.objects.filter(name__icontains=term)
        elif facet == "room":
            items = Room.objects.filter(name__icontains=term)
        elif facet == "devicegroup":
            items = Devicegroup.objects.filter(name__icontains=term)
        elif facet == "user":
            items = Lageruser.objects.filter(username__icontains=term)
        elif facet == "tag":
            items = Devicetag.objects.filter(name__icontains=term)
        else:
            return HttpResponse("")
        if invert:
            data = [{"value": "not " + str(object.pk)+"-"+object.__unicode__(), "label" : "not " + object.__unicode__()}
                for object in items]
        else:
            data = [{"value": str(object.pk)+"-"+object.__unicode__(), "label" : object.__unicode__()}
                for object in items]
        return HttpResponse(json.dumps(data), content_type='application/json')

class AjaxSearch(View):
    def post(self, request):
        search = json.loads(request.POST["search"])
        searchdict = {}
        excludedict = {}
        textfilter = None
        statusfilter = None
        displayed_columns  = []
        searchvalues = ["id", "name", "inventorynumber", "serialnumber", "devicetype__name", "room__name", "room__building__name"]
        for searchitem in search:
            key, value = searchitem.items()[0]

            if value[:4] == "not ":
                value = value[4:]
                dictionary = excludedict
            else:
                dictionary = searchdict

            if key == "manufacturer":
                value = value.split("-", 1)[0]
                if len(displayed_columns) < 8:
                    displayed_columns.append(("manufacturer", _("Manufacturer")))
                    searchvalues.append("manufacturer__name")
                if "manufacturer__in" in dictionary:
                    dictionary["manufacturer__in"].append(value)
                else:
                    dictionary["manufacturer__in"] = [value]

            elif key == "room":
                value = value.split("-", 1)[0]
                if "room__in" in dictionary:
                    dictionary["room__in"].append(value)
                else:
                    dictionary["room__in"] = [value]

            elif key == "devicetype":
                value = value.split("-", 1)[0]
                if "devicetype__in" in dictionary:
                    dictionary["devicetype__in"].append(value)
                else:
                    dictionary["devicetype__in"] = [value]

            elif key == "devicegroup":
                value = value.split("-", 1)[0]
                if len(displayed_columns) < 8:
                    displayed_columns.append(("group", _("Group")))
                    searchvalues.append("group__name")
                if "group__in" in dictionary:
                    dictionary["group__in"].append(value)
                else:
                    dictionary["group__in"] = [value]

            elif key == "user":
                value = value.split("-", 1)[0]
                if len(displayed_columns) < 8:
                    displayed_columns.append(("user", _("User")))
                    searchvalues.append("currentlending__owner__username")
                if value.lower() == "null":
                    dictionary["currentlending"] = None
                else:
                    dictionary["currentlending__owner__id"] = value

            elif key == "ipaddress":
                if len(displayed_columns) < 8:
                    displayed_columns.append(("ipaddress", _("IP-Address")))
                    searchvalues.append("ipaddress__address")
                if value.lower() == "null":
                    dictionary["ipaddress"] = None
                else:
                    dictionary["ipaddress__address__icontains"] = value

            elif key == "inventoried" or key == "trashed" or key == "archived":
                if value.startswith("before"):
                    value = value[7:]
                    modifier = "__lt"
                elif value.startswith("after"):
                    value = value[6:]
                    modifier = "__gt"
                else:
                    modifier = ""

                if len(displayed_columns) < 8:
                    displayed_columns.append((key, _("{0} on").format(key.capitalize())))
                    searchvalues.append(key)
                if key == "archived" or key == "inventoried":
                    statusfilter = "all"
                dictionary[key + modifier] = parse_date(value)

            elif key == "tag":
                value = value.split("-", 1)[0]
                if "tags__in" in dictionary:
                    dictionary["tags__in"].append(value)
                else:
                    dictionary["tags__in"] = [value]

            elif key == "text":
                textfilter = value

            elif key == "status":
                statusfilter = value

            elif key == "id":
                try:
                    value = int(value)
                    context = {"device_list": Device.objects.filter(id=value).values("id", "name", "inventorynumber", "devicetype__name", "room__name", "room__building__name")}
                    return render_to_response('devices/searchresult.html', context, RequestContext(self.request))
                except:
                    return render_to_response('devices/searchempty.html', {}, RequestContext(self.request))
            elif key == "shortterm":
                if value.lower() == "yes":
                    dictionary["templending"] = True
                else:
                    dictionary["templending"] = False

        devices = Device.objects.filter(**searchdict)
        devices = devices.exclude(**excludedict)

        if statusfilter == "all":
                pass
        elif statusfilter == "available":
            devices = devices.filter(currentlending=None, archived=None, trashed=None)
        elif statusfilter == "unavailable":
            devices = devices.exclude(currentlending=None).filter(archived=None, trashed=None)
        elif statusfilter == "archived":
            devices = devices.exclude(archived=None)
        elif statusfilter == "trashed":
            devices = devices.exclude(trashed=None)
        else:
            devices = devices.filter(archived=None, trashed=None)

        if textfilter != None:
            if "text" in settings.SEARCHSTRIP:
                textfilter = textfilter.strip(settings.SEARCHSTRIP["text"]).strip()
            try:
                searchid = int(textfilter.replace(" ", ""))
                devices = devices.filter(Q(name__icontains=textfilter)|
                    Q(inventorynumber__icontains=textfilter.replace( " ", ""))|Q(serialnumber__icontains=textfilter.replace( " ", ""))|Q(id=searchid))
            except ValueError:
                devices = devices.filter(Q(name__icontains=textfilter)|
                    Q(inventorynumber__icontains=textfilter.replace( " ", ""))|Q(serialnumber__icontains=textfilter.replace( " ", "")))
        context = {
            "device_list": devices.values(*searchvalues),
            "columns": displayed_columns
        }
        return render_to_response('devices/searchresult.html', context, RequestContext(self.request))
<commit_msg>prevent crash, when search input is wrong. Fixes #100<commit_after># -*- coding: utf-8 -*-
from devices.models import Device, Room, Building, Manufacturer
from devicetypes.models import Type
from devicegroups.models import Devicegroup
from users.models import Lageruser
from network.models import IpAddress
from mail.models import MailTemplate
from django.shortcuts import get_object_or_404
from devicetypes.models import Type
import json
from django.core.urlresolvers import reverse
from devices.forms import AddForm
from django.forms.models import modelform_factory
from django.template.loader import render_to_string
from django.template import Template, Context
from devicegroups.models import Devicegroup
from django.views.generic.base import View
from django.http import HttpResponse
import pystache
from django.http import QueryDict
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.db.models import Q
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.dateparse import parse_date
from devicetags.models import Devicetag

class AutocompleteDevice(View):
    def post(self, request):
        name = request.POST["name"]
        devices = Device.objects.filter(name__icontains = name )[:20]
        results = []
        for device in devices:
            device_json = {}
            device_json['id'] = device.pk
            device_json['label'] = device.name
            device_json['value'] = device.name
            results.append(device_json)
        return HttpResponse(json.dumps(results), content_type='application/json')

class AutocompleteName(View):
    def post(self, request):
        name = request.POST["name"]
        classtype = request.POST["classtype"]
        if classtype == "type":
            objects = Type.objects.filter(name__icontains = name )[:20]
            urlname = "type-detail"
        elif classtype == "room":
            objects = Room.objects.filter(name__icontains = name )[:20]
            urlname = "room-detail"
        elif classtype == "building":
            objects = Building.objects.filter(name__icontains = name )[:20]
            urlname = "building-detail"
        elif classtype == "manufacturer":
            objects = Manufacturer.objects.filter(name__icontains = name )[:20]
            urlname = "manufacturer-detail"
        else:
            return HttpResponse("")
        if len(objects) > 0:
            retobjects = ["
{1}
".format(
                reverse(urlname, kwargs={"pk":obj[0]}), obj[1])
                for obj in objects.values_list("pk", "name")]
            return HttpResponse(json.dumps(retobjects), content_type='application/json')
        else:
            return HttpResponse("")

class AddDeviceField(View):
    def post(self, request):
        dform = QueryDict(query_string=unicode(request.POST["form"]).encode('utf-8'))
        classname = dform["classname"]
        if classname == "manufacturer":
            form = modelform_factory(Manufacturer, form=AddForm)(dform)
        elif classname == "devicetype":
            form = modelform_factory(Type, form=AddForm)(dform)
        elif classname == "room":
            form = modelform_factory(Room, form=AddForm)(dform)
        elif classname == "group":
            form = modelform_factory(Devicegroup, form=AddForm)(dform)
        else:
            return HttpResponse("")
        data = {}
        if form.is_valid():
            if request.user.is_staff:
                classname = form.cleaned_data["classname"]
                if classname == "manufacturer":
                    newitem = Manufacturer()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                elif classname == "devicetype":
                    newitem = Type()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                elif classname == "room":
                    newitem = Room()
                    newitem.name = form.cleaned_data["name"]
                    newitem.building = form.cleaned_data["building"]
                    newitem.section = form.cleaned_data["section"]
                    newitem.save()
                elif classname == "group":
                    newitem = Devicegroup()
                    newitem.name = form.cleaned_data["name"]
                    newitem.save()
                data["id"] = newitem.pk
                data["name"] = newitem.name
                data["classname"] = classname
        else:
            print form.errors
            data["error"] = "Error: {0}".format(form.non_field_errors())
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadExtraform(View):
    def post(self, request):
        classname = request.POST["classname"]
        if classname == "manufacturer":
            form = modelform_factory(Manufacturer, form=AddForm)()
        elif classname == "devicetype":
            form = modelform_factory(Type, form=AddForm)()
        elif classname == "room":
            form = modelform_factory(Room, form=AddForm)()
        elif classname == "group":
            form = modelform_factory(Devicegroup, form=AddForm)()
        else:
            return HttpResponse("")

        return HttpResponse(render_to_string('snippets/formfields.html', {"form":form}))

class PreviewMail(View):
    def post(self, request):
        template = request.POST["template"]
        device = {
            "currentlending": request.POST.get("device[currentlending]", ""),
            "description": request.POST.get("device[description]", ""),
            "devicetype": request.POST.get("device[devicetype]", ""),
            "group": request.POST.get("device[group]", ""),
            "hostname": request.POST.get("device[hostname]", ""),
            "inventorynumber": request.POST.get("device[inventorynumber]", ""),
            "manufacturer": request.POST.get("device[manufacturer]", ""),
            "name": request.POST.get("device[name]", ""),
            "room": request.POST.get("device[room]", "") ,
            "serialnumber": request.POST.get("device[serialnumber]", ""),
            "templending": request.POST.get("device[templending]", ""),
            "webinterface": request.POST.get("device[webinterface]", "")
        }
        if template == "":
            return HttpResponse("")

        if device["manufacturer"] != "":
            device["manufacturer"] = get_object_or_404(Manufacturer, pk=device["manufacturer"])
        else:
            del device["manufacturer"]

        if device["devicetype"] != "":
            device["devicetype"] = get_object_or_404(Type, pk=device["devicetype"])
        else:
            del device["devicetype"]

        if device["room"] != "":
            device["room"] = get_object_or_404(Room, pk=device["room"])
        else:
            del device["room"]

        template = get_object_or_404(MailTemplate, pk=template)
        datadict = {}
        datadict["device"] = device
        datadict["user"] = {
            "username" : request.user.username,
            "first_name" : request.user.first_name,
            "last_name" : request.user.last_name
        }
        data = {}
        data["subject"] = pystache.render(template.subject, datadict)
        data["body"] = pystache.render(template.body, datadict).replace("\n", "
")
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadMailtemplate(View):
    def post(self, request):
        template = request.POST["template"]
        recipients = request.POST.get("recipients[]", [])
        if template == "":
            return HttpResponse("")
        template = get_object_or_404(MailTemplate, pk=template)
        data = {}
        data["subject"] = template.subject
        data["body"] = template.body
        if isinstance(recipients, unicode):
            recipients = [recipients]
        newrecipients = [obj for obj in recipients]
        newrecipients += [obj.content_type.name[0].lower()+str(obj.object_id) for obj in template.default_recipients.all()]
        newrecipients = list(set(newrecipients))
        data["recipients"] = newrecipients
        return HttpResponse(json.dumps(data), content_type='application/json')

class LoadSearchoptions(View):
    def post(self, request):
        term = request.POST["searchTerm"]
        if term[:4] == "not ":
            term = term[4:]
            invert = True
        else:
            invert = False
        facet = request.POST["facet"]
        if facet == "manufacturer":
            items = Manufacturer.objects.filter(name__icontains=term)
        elif facet == "devicetype":
            items = Type.objects.filter(name__icontains=term)
        elif facet == "room":
            items = Room.objects.filter(name__icontains=term)
        elif facet == "devicegroup":
            items = Devicegroup.objects.filter(name__icontains=term)
        elif facet == "user":
            items = Lageruser.objects.filter(username__icontains=term)
        elif facet == "tag":
            items = Devicetag.objects.filter(name__icontains=term)
        else:
            return HttpResponse("")
        if invert:
            data = [{"value": "not " + str(object.pk)+"-"+object.__unicode__(), "label" : "not " + object.__unicode__()}
                for object in items]
        else:
            data = [{"value": str(object.pk)+"-"+object.__unicode__(), "label" : object.__unicode__()}
                for object in items]
        return HttpResponse(json.dumps(data), content_type='application/json')

class AjaxSearch(View):
    def post(self, request):
        search = json.loads(request.POST["search"])
        searchdict = {}
        excludedict = {}
        textfilter = None
        statusfilter = None
        displayed_columns  = []
        searchvalues = ["id", "name", "inventorynumber", "serialnumber", "devicetype__name", "room__name", "room__building__name"]
        for searchitem in search:
            key, value = searchitem.items()[0]

            if value[:4] == "not ":
                value = value[4:]
                dictionary = excludedict
            else:
                dictionary = searchdict

            if key == "manufacturer":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if len(displayed_columns) < 8:
                    displayed_columns.append(("manufacturer", _("Manufacturer")))
                    searchvalues.append("manufacturer__name")
                if "manufacturer__in" in dictionary:
                    dictionary["manufacturer__in"].append(value)
                else:
                    dictionary["manufacturer__in"] = [value]

            elif key == "room":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if "room__in" in dictionary:
                    dictionary["room__in"].append(value)
                else:
                    dictionary["room__in"] = [value]

            elif key == "devicetype":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if "devicetype__in" in dictionary:
                    dictionary["devicetype__in"].append(value)
                else:
                    dictionary["devicetype__in"] = [value]

            elif key == "devicegroup":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if len(displayed_columns) < 8:
                    displayed_columns.append(("group", _("Group")))
                    searchvalues.append("group__name")
                if "group__in" in dictionary:
                    dictionary["group__in"].append(value)
                else:
                    dictionary["group__in"] = [value]

            elif key == "user":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if len(displayed_columns) < 8:
                    displayed_columns.append(("user", _("User")))
                    searchvalues.append("currentlending__owner__username")
                if value.lower() == "null":
                    dictionary["currentlending"] = None
                else:
                    dictionary["currentlending__owner__id"] = value

            elif key == "ipaddress":
                if len(displayed_columns) < 8:
                    displayed_columns.append(("ipaddress", _("IP-Address")))
                    searchvalues.append("ipaddress__address")
                if value.lower() == "null":
                    dictionary["ipaddress"] = None
                else:
                    dictionary["ipaddress__address__icontains"] = value

            elif key == "inventoried" or key == "trashed" or key == "archived":
                if value.startswith("before"):
                    value = value[7:]
                    modifier = "__lt"
                elif value.startswith("after"):
                    value = value[6:]
                    modifier = "__gt"
                else:
                    modifier = ""

                if len(displayed_columns) < 8:
                    displayed_columns.append((key, _("{0} on").format(key.capitalize())))
                    searchvalues.append(key)
                if key == "archived" or key == "inventoried":
                    statusfilter = "all"
                dictionary[key + modifier] = parse_date(value)

            elif key == "tag":
                value = value.split("-", 1)[0]
                try:
                    value = int(value)
                except:
                    break
                if "tags__in" in dictionary:
                    dictionary["tags__in"].append(value)
                else:
                    dictionary["tags__in"] = [value]

            elif key == "text":
                textfilter = value

            elif key == "status":
                statusfilter = value

            elif key == "id":
                try:
                    value = int(value)
                    context = {"device_list": Device.objects.filter(id=value).values("id", "name", "inventorynumber", "devicetype__name", "room__name", "room__building__name")}
                    return render_to_response('devices/searchresult.html', context, RequestContext(self.request))
                except:
                    return render_to_response('devices/searchempty.html', {}, RequestContext(self.request))
            elif key == "shortterm":
                if value.lower() == "yes":
                    dictionary["templending"] = True
                else:
                    dictionary["templending"] = False

        devices = Device.objects.filter(**searchdict)
        devices = devices.exclude(**excludedict)

        if statusfilter == "all":
                pass
        elif statusfilter == "available":
            devices = devices.filter(currentlending=None, archived=None, trashed=None)
        elif statusfilter == "unavailable":
            devices = devices.exclude(currentlending=None).filter(archived=None, trashed=None)
        elif statusfilter == "archived":
            devices = devices.exclude(archived=None)
        elif statusfilter == "trashed":
            devices = devices.exclude(trashed=None)
        else:
            devices = devices.filter(archived=None, trashed=None)

        if textfilter != None:
            if "text" in settings.SEARCHSTRIP:
                textfilter = textfilter.strip(settings.SEARCHSTRIP["text"]).strip()
            try:
                searchid = int(textfilter.replace(" ", ""))
                devices = devices.filter(Q(name__icontains=textfilter)|
                    Q(inventorynumber__icontains=textfilter.replace( " ", ""))|Q(serialnumber__icontains=textfilter.replace( " ", ""))|Q(id=searchid))
            except ValueError:
                devices = devices.filter(Q(name__icontains=textfilter)|
                    Q(inventorynumber__icontains=textfilter.replace( " ", ""))|Q(serialnumber__icontains=textfilter.replace( " ", "")))
        context = {
            "device_list": devices.values(*searchvalues),
            "columns": displayed_columns
        }
        return render_to_response('devices/searchresult.html', context, RequestContext(self.request))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

try:
    from cs import CloudStack, CloudStackException, read_config
    has_lib_cs = True
except ImportError:
    has_lib_cs = False

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')
        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
<commit_msg>cloudstack: get_vm(): fix missing zone<commit_after># -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

try:
    from cs import CloudStack, CloudStackException, read_config
    has_lib_cs = True
except ImportError:
    has_lib_cs = False

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
<|endoftext|>