Ejemplo n.º 1
0
    def __init__(self, *args, **kwargs):

        if kwargs.get('gce_driver'):
            self.gce = kwargs['gce_driver']
        else:
            self.gce = GCENodeDriver(*args, **kwargs)

        self.connection = self.gce.connection
Ejemplo n.º 2
0
 def setUp(self):
     GCEMockHttp.test = self
     GCENodeDriver.connectionCls.conn_classes = (GCEMockHttp, GCEMockHttp)
     GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
                                              GoogleAuthMockHttp)
     GCEMockHttp.type = None
     kwargs = GCE_KEYWORD_PARAMS.copy()
     kwargs['auth_type'] = 'IA'
     kwargs['datacenter'] = self.datacenter
     self.driver = GCENodeDriver(*GCE_PARAMS, **kwargs)
Ejemplo n.º 3
0
    def __init__(self, *args, **kwargs):

        if kwargs.get('gce_driver'):
            self.gce = kwargs['gce_driver']
        else:
            self.gce = GCENodeDriver(*args, **kwargs)

        self.connection = self.gce.connection
Ejemplo n.º 4
0
 def __init__(self, config, gcp_credentials_file):
     self.config = config
     creds = read_json(gcp_credentials_file)
     self._compute = GCENodeDriver(
         creds['client_email'],
         gcp_credentials_file,
         project=creds['project_id'],
         datacenter=self.config['gcp_compute_zone'],
         timeout=self.config['compute_timeout'])
     self._dns = GoogleDNSDriver(creds['client_email'],
                                 gcp_credentials_file,
                                 project=creds['project_id'])
     self._dns_zone = self._dns.get_zone(config['gcp_dns_zone'])
     self.log = logging.getLogger(__name__ + '.' + self._name)
Ejemplo n.º 5
0
class GCELBDriver(Driver):
    connectionCls = GCEConnection
    apiname = 'googleapis'
    name = 'Google Compute Engine Load Balancer'
    website = 'https://cloud.google.com/'

    _VALUE_TO_ALGORITHM_MAP = {'RANDOM': Algorithm.RANDOM}

    def __init__(self, *args, **kwargs):

        if kwargs.get('gce_driver'):
            self.gce = kwargs['gce_driver']
        else:
            self.gce = GCENodeDriver(*args, **kwargs)

        self.connection = self.gce.connection

    def _get_node_from_ip(self, ip):
        """
        Return the node object that matches a given public IP address.

        :param  ip: Public IP address to search for
        :type   ip: ``str``

        :return:  Node object that has the given IP, or None if not found.
        :rtype:   :class:`Node` or None
        """
        all_nodes = self.gce.list_nodes(ex_zone='all')
        for node in all_nodes:
            if ip in node.public_ips:
                return node
        return None

    def list_protocols(self):
        """
        Return a list of supported protocols.

        For GCE, this is simply a hardcoded list.

        :rtype: ``list`` of ``str``
        """
        return ['TCP', 'UDP']

    def list_balancers(self, ex_region=None):
        """
        List all loadbalancers

        :keyword  ex_region: The region to return balancers from.  If None,
                             will default to self.region.  If 'all', will
                             return all balancers.
        :type     ex_region: ``str`` or :class:`GCERegion` or ``None``

        :rtype: ``list`` of :class:`LoadBalancer`
        """
        balancers = []
        for fwr in self.gce.ex_list_forwarding_rules(region=ex_region):
            balancers.append(self._forwarding_rule_to_loadbalancer(fwr))
        return balancers

    def create_balancer(self,
                        name,
                        port,
                        protocol,
                        algorithm,
                        members,
                        ex_region=None,
                        ex_healthchecks=None,
                        ex_address=None,
                        ex_session_affinity=None):
        """
        Create a new load balancer instance.

        For GCE, this means creating a forwarding rule and a matching target
        pool, then adding the members to the target pool.

        :param  name: Name of the new load balancer (required)
        :type   name: ``str``

        :param  port: Port or range of ports the load balancer should listen
                      on, defaults to all ports.  Examples: '80', '5000-5999'
        :type   port: ``str``

        :param  protocol: Load balancer protocol.  Should be 'tcp' or 'udp',
                          defaults to 'tcp'.
        :type   protocol: ``str``

        :param  members: List of Members to attach to balancer.  Can be Member
                         objects or Node objects.  Node objects are preferred
                         for GCE, but Member objects are accepted to comply
                         with the established libcloud API.  Note that the
                         'port' attribute of the members is ignored.
        :type   members: ``list`` of :class:`Member` or :class:`Node`

        :param  algorithm: Load balancing algorithm.  Ignored for GCE which
                           uses a hashing-based algorithm.
        :type   algorithm: :class:`Algorithm` or ``None``

        :keyword  ex_region:  Optional region to create the load balancer in.
                              Defaults to the default region of the GCE Node
                              Driver.
        :type     ex_region:  C{GCERegion} or ``str``

        :keyword  ex_healthchecks: Optional list of healthcheck objects or
                                   names to add to the load balancer.
        :type     ex_healthchecks: ``list`` of :class:`GCEHealthCheck` or
                                   ``list`` of ``str``

        :keyword  ex_address: Optional static address object to be assigned to
                              the load balancer.
        :type     ex_address: C{GCEAddress}

        :keyword  ex_session_affinity: Optional algorithm to use for session
                                       affinity.  This will modify the hashing
                                       algorithm such that a client will tend
                                       to stick to a particular Member.
        :type     ex_session_affinity: ``str``

        :return:  LoadBalancer object
        :rtype:   :class:`LoadBalancer`
        """
        node_list = []
        for member in members:
            # Member object
            if hasattr(member, 'ip'):
                if member.extra.get('node'):
                    node_list.append(member.extra['node'])
                else:
                    node_list.append(self._get_node_from_ip(member.ip))
            # Node object
            elif hasattr(member, 'name'):
                node_list.append(member)
            # Assume it's a node name otherwise
            else:
                node_list.append(self.gce.ex_get_node(member, 'all'))

        # Create Target Pool
        tp_name = '%s-tp' % name
        targetpool = self.gce.ex_create_targetpool(
            tp_name,
            region=ex_region,
            healthchecks=ex_healthchecks,
            nodes=node_list,
            session_affinity=ex_session_affinity)

        # Create the Forwarding rule, but if it fails, delete the target pool.
        try:
            forwarding_rule = self.gce.ex_create_forwarding_rule(
                name,
                targetpool,
                region=ex_region,
                protocol=protocol,
                port_range=port,
                address=ex_address)
        except:
            targetpool.destroy()
            raise

        # Reformat forwarding rule to LoadBalancer object
        return self._forwarding_rule_to_loadbalancer(forwarding_rule)

    def destroy_balancer(self, balancer):
        """
        Destroy a load balancer.

        For GCE, this means destroying the associated forwarding rule, then
        destroying the target pool that was attached to the forwarding rule.

        :param  balancer: LoadBalancer which should be used
        :type   balancer: :class:`LoadBalancer`

        :return:  True if successful
        :rtype:   ``bool``
        """
        destroy = balancer.extra['forwarding_rule'].destroy()
        if destroy:
            tp_destroy = balancer.extra['targetpool'].destroy()
            return tp_destroy
        else:
            return destroy

    def get_balancer(self, balancer_id):
        """
        Return a :class:`LoadBalancer` object.

        :param  balancer_id: Name of load balancer you wish to fetch.  For GCE,
                             this is the name of the associated forwarding
                             rule.
        :param  balancer_id: ``str``

        :rtype: :class:`LoadBalancer`
        """
        fwr = self.gce.ex_get_forwarding_rule(balancer_id)
        return self._forwarding_rule_to_loadbalancer(fwr)

    def balancer_attach_compute_node(self, balancer, node):
        """
        Attach a compute node as a member to the load balancer.

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param node: Node to join to the balancer
        :type  node: :class:`Node`

        :return: Member after joining the balancer.
        :rtype: :class:`Member`
        """
        add_node = balancer.extra['targetpool'].add_node(node)
        if add_node:
            return self._node_to_member(node, balancer)

    def balancer_attach_member(self, balancer, member):
        """
        Attach a member to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member to join to the balancer
        :type member: :class:`Member`

        :return: Member after joining the balancer.
        :rtype: :class:`Member`
        """
        node = member.extra.get('node') or self._get_node_from_ip(member.ip)
        add_node = balancer.extra['targetpool'].add_node(node)
        if add_node:
            return self._node_to_member(node, balancer)

    def balancer_detach_member(self, balancer, member):
        """
        Detach member from balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member which should be used
        :type member: :class:`Member`

        :return: True if member detach was successful, otherwise False
        :rtype: ``bool``
        """
        node = member.extra.get('node') or self._get_node_from_ip(member.ip)
        remove_node = balancer.extra['targetpool'].remove_node(node)
        return remove_node

    def balancer_list_members(self, balancer):
        """
        Return list of members attached to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :rtype: ``list`` of :class:`Member`
        """
        return [
            self._node_to_member(n, balancer)
            for n in balancer.extra['targetpool'].nodes
        ]

    def ex_create_healthcheck(self, *args, **kwargs):
        return self.gce.ex_create_healthcheck(*args, **kwargs)

    def ex_list_healthchecks(self):
        return self.gce.ex_list_healthchecks()

    def ex_balancer_attach_healthcheck(self, balancer, healthcheck):
        """
        Attach a healthcheck to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param healthcheck: Healthcheck to add
        :type  healthcheck: :class:`GCEHealthCheck`

        :return: True if successful
        :rtype:  ``bool``
        """
        return balancer.extra['targetpool'].add_healthcheck(healthcheck)

    def ex_balancer_detach_healthcheck(self, balancer, healthcheck):
        """
        Detach healtcheck from balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param healthcheck: Healthcheck to remove
        :type  healthcheck: :class:`GCEHealthCheck`

        :return: True if successful
        :rtype: ``bool``
        """
        return balancer.extra['targetpool'].remove_healthcheck(healthcheck)

    def ex_balancer_list_healthchecks(self, balancer):
        """
        Return list of healthchecks attached to balancer

        :param  balancer: LoadBalancer which should be used
        :type   balancer: :class:`LoadBalancer`

        :rtype: ``list`` of :class:`HealthChecks`
        """
        return balancer.extra['healthchecks']

    def _node_to_member(self, node, balancer):
        """
        Return a Member object based on a Node.

        :param  node: Node object
        :type   node: :class:`Node`

        :keyword  balancer: The balancer the member is attached to.
        :type     balancer: :class:`LoadBalancer`

        :return:  Member object
        :rtype:   :class:`Member`
        """
        # A balancer can have a node as a member, even if the node doesn't
        # exist.  In this case, 'node' is simply a string to where the resource
        # would be found if it was there.
        if hasattr(node, 'name'):
            member_id = node.name
        else:
            member_id = node

        if hasattr(node, 'public_ips') and len(node.public_ips) > 0:
            member_ip = node.public_ips[0]
        else:
            member_ip = None

        extra = {'node': node}
        return Member(id=member_id,
                      ip=member_ip,
                      port=balancer.port,
                      balancer=balancer,
                      extra=extra)

    def _forwarding_rule_to_loadbalancer(self, forwarding_rule):
        """
        Return a Load Balancer object based on a GCEForwardingRule object.

        :param  forwarding_rule: ForwardingRule object
        :type   forwarding_rule: :class:`GCEForwardingRule`

        :return:  LoadBalancer object
        :rtype:   :class:`LoadBalancer`
        """
        extra = {}
        extra['forwarding_rule'] = forwarding_rule
        extra['targetpool'] = forwarding_rule.targetpool
        extra['healthchecks'] = forwarding_rule.targetpool.healthchecks

        return LoadBalancer(id=forwarding_rule.id,
                            name=forwarding_rule.name,
                            state=None,
                            ip=forwarding_rule.address,
                            port=forwarding_rule.extra['portRange'],
                            driver=self,
                            extra=extra)
Ejemplo n.º 6
0
class GCELBDriver(Driver):
    connectionCls = GCEConnection
    apiname = 'googleapis'
    name = 'Google Compute Engine Load Balancer'
    website = 'https://cloud.google.com/'

    _VALUE_TO_ALGORITHM_MAP = {
        'RANDOM': Algorithm.RANDOM
    }

    def __init__(self, *args, **kwargs):

        if kwargs.get('gce_driver'):
            self.gce = kwargs['gce_driver']
        else:
            self.gce = GCENodeDriver(*args, **kwargs)

        self.connection = self.gce.connection

    def _get_node_from_ip(self, ip):
        """
        Return the node object that matches a given public IP address.

        :param  ip: Public IP address to search for
        :type   ip: ``str``

        :return:  Node object that has the given IP, or None if not found.
        :rtype:   :class:`Node` or None
        """
        all_nodes = self.gce.list_nodes(ex_zone='all')
        for node in all_nodes:
            if ip in node.public_ips:
                return node
        return None

    def list_protocols(self):
        """
        Return a list of supported protocols.

        For GCE, this is simply a hardcoded list.

        :rtype: ``list`` of ``str``
        """
        return ['TCP', 'UDP']

    def list_balancers(self, ex_region=None):
        """
        List all loadbalancers

        :keyword  ex_region: The region to return balancers from.  If None,
                             will default to self.region.  If 'all', will
                             return all balancers.
        :type     ex_region: ``str`` or :class:`GCERegion` or ``None``

        :rtype: ``list`` of :class:`LoadBalancer`
        """
        balancers = []
        for fwr in self.gce.ex_list_forwarding_rules(region=ex_region):
            balancers.append(self._forwarding_rule_to_loadbalancer(fwr))
        return balancers

    def create_balancer(self, name, port, protocol, algorithm, members,
                        ex_region=None, ex_healthchecks=None, ex_address=None,
                        ex_session_affinity=None):
        """
        Create a new load balancer instance.

        For GCE, this means creating a forwarding rule and a matching target
        pool, then adding the members to the target pool.

        :param  name: Name of the new load balancer (required)
        :type   name: ``str``

        :param  port: Port or range of ports the load balancer should listen
                      on, defaults to all ports.  Examples: '80', '5000-5999'
        :type   port: ``str``

        :param  protocol: Load balancer protocol.  Should be 'tcp' or 'udp',
                          defaults to 'tcp'.
        :type   protocol: ``str``

        :param  members: List of Members to attach to balancer.  Can be Member
                         objects or Node objects.  Node objects are preferred
                         for GCE, but Member objects are accepted to comply
                         with the established libcloud API.  Note that the
                         'port' attribute of the members is ignored.
        :type   members: ``list`` of :class:`Member` or :class:`Node`

        :param  algorithm: Load balancing algorithm.  Ignored for GCE which
                           uses a hashing-based algorithm.
        :type   algorithm: :class:`Algorithm` or ``None``

        :keyword  ex_region:  Optional region to create the load balancer in.
                              Defaults to the default region of the GCE Node
                              Driver.
        :type     ex_region:  C{GCERegion} or ``str``

        :keyword  ex_healthchecks: Optional list of healthcheck objects or
                                   names to add to the load balancer.
        :type     ex_healthchecks: ``list`` of :class:`GCEHealthCheck` or
                                   ``list`` of ``str``

        :keyword  ex_address: Optional static address object to be assigned to
                              the load balancer.
        :type     ex_address: C{GCEAddress}

        :keyword  ex_session_affinity: Optional algorithm to use for session
                                       affinity.  This will modify the hashing
                                       algorithm such that a client will tend
                                       to stick to a particular Member.
        :type     ex_session_affinity: ``str``

        :return:  LoadBalancer object
        :rtype:   :class:`LoadBalancer`
        """
        node_list = []
        for member in members:
            # Member object
            if hasattr(member, 'ip'):
                if member.extra.get('node'):
                    node_list.append(member.extra['node'])
                else:
                    node_list.append(self._get_node_from_ip(member.ip))
            # Node object
            elif hasattr(member, 'name'):
                node_list.append(member)
            # Assume it's a node name otherwise
            else:
                node_list.append(self.gce.ex_get_node(member, 'all'))

        # Create Target Pool
        tp_name = '%s-tp' % name
        targetpool = self.gce.ex_create_targetpool(
            tp_name, region=ex_region, healthchecks=ex_healthchecks,
            nodes=node_list, session_affinity=ex_session_affinity)

        # Create the Forwarding rule, but if it fails, delete the target pool.
        try:
            forwarding_rule = self.gce.ex_create_forwarding_rule(
                name, targetpool, region=ex_region, protocol=protocol,
                port_range=port, address=ex_address)
        except:
            targetpool.destroy()
            raise

        # Reformat forwarding rule to LoadBalancer object
        return self._forwarding_rule_to_loadbalancer(forwarding_rule)

    def destroy_balancer(self, balancer):
        """
        Destroy a load balancer.

        For GCE, this means destroying the associated forwarding rule, then
        destroying the target pool that was attached to the forwarding rule.

        :param  balancer: LoadBalancer which should be used
        :type   balancer: :class:`LoadBalancer`

        :return:  True if successful
        :rtype:   ``bool``
        """
        destroy = balancer.extra['forwarding_rule'].destroy()
        if destroy:
            tp_destroy = balancer.extra['targetpool'].destroy()
            return tp_destroy
        else:
            return destroy

    def get_balancer(self, balancer_id):
        """
        Return a :class:`LoadBalancer` object.

        :param  balancer_id: Name of load balancer you wish to fetch.  For GCE,
                             this is the name of the associated forwarding
                             rule.
        :param  balancer_id: ``str``

        :rtype: :class:`LoadBalancer`
        """
        fwr = self.gce.ex_get_forwarding_rule(balancer_id)
        return self._forwarding_rule_to_loadbalancer(fwr)

    def balancer_attach_compute_node(self, balancer, node):
        """
        Attach a compute node as a member to the load balancer.

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param node: Node to join to the balancer
        :type  node: :class:`Node`

        :return: Member after joining the balancer.
        :rtype: :class:`Member`
        """
        add_node = balancer.extra['targetpool'].add_node(node)
        if add_node:
            return self._node_to_member(node, balancer)

    def balancer_attach_member(self, balancer, member):
        """
        Attach a member to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member to join to the balancer
        :type member: :class:`Member`

        :return: Member after joining the balancer.
        :rtype: :class:`Member`
        """
        node = member.extra.get('node') or self._get_node_from_ip(member.ip)
        add_node = balancer.extra['targetpool'].add_node(node)
        if add_node:
            return self._node_to_member(node, balancer)

    def balancer_detach_member(self, balancer, member):
        """
        Detach member from balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member which should be used
        :type member: :class:`Member`

        :return: True if member detach was successful, otherwise False
        :rtype: ``bool``
        """
        node = member.extra.get('node') or self._get_node_from_ip(member.ip)
        remove_node = balancer.extra['targetpool'].remove_node(node)
        return remove_node

    def balancer_list_members(self, balancer):
        """
        Return list of members attached to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :rtype: ``list`` of :class:`Member`
        """
        return [self._node_to_member(n, balancer) for n in
                balancer.extra['targetpool'].nodes]

    def ex_create_healthcheck(self, *args, **kwargs):
        return self.gce.ex_create_healthcheck(*args, **kwargs)

    def ex_list_healthchecks(self):
        return self.gce.ex_list_healthchecks()

    def ex_balancer_attach_healthcheck(self, balancer, healthcheck):
        """
        Attach a healthcheck to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param healthcheck: Healthcheck to add
        :type  healthcheck: :class:`GCEHealthCheck`

        :return: True if successful
        :rtype:  ``bool``
        """
        return balancer.extra['targetpool'].add_healthcheck(healthcheck)

    def ex_balancer_detach_healthcheck(self, balancer, healthcheck):
        """
        Detach healtcheck from balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param healthcheck: Healthcheck to remove
        :type  healthcheck: :class:`GCEHealthCheck`

        :return: True if successful
        :rtype: ``bool``
        """
        return balancer.extra['targetpool'].remove_healthcheck(healthcheck)

    def ex_balancer_list_healthchecks(self, balancer):
        """
        Return list of healthchecks attached to balancer

        :param  balancer: LoadBalancer which should be used
        :type   balancer: :class:`LoadBalancer`

        :rtype: ``list`` of :class:`HealthChecks`
        """
        return balancer.extra['healthchecks']

    def _node_to_member(self, node, balancer):
        """
        Return a Member object based on a Node.

        :param  node: Node object
        :type   node: :class:`Node`

        :keyword  balancer: The balancer the member is attached to.
        :type     balancer: :class:`LoadBalancer`

        :return:  Member object
        :rtype:   :class:`Member`
        """
        # A balancer can have a node as a member, even if the node doesn't
        # exist.  In this case, 'node' is simply a string to where the resource
        # would be found if it was there.
        if hasattr(node, 'name'):
            member_id = node.name
        else:
            member_id = node

        if hasattr(node, 'public_ips') and len(node.public_ips) > 0:
            member_ip = node.public_ips[0]
        else:
            member_ip = None

        extra = {'node': node}
        return Member(id=member_id, ip=member_ip, port=balancer.port,
                      balancer=balancer, extra=extra)

    def _forwarding_rule_to_loadbalancer(self, forwarding_rule):
        """
        Return a Load Balancer object based on a GCEForwardingRule object.

        :param  forwarding_rule: ForwardingRule object
        :type   forwarding_rule: :class:`GCEForwardingRule`

        :return:  LoadBalancer object
        :rtype:   :class:`LoadBalancer`
        """
        extra = {}
        extra['forwarding_rule'] = forwarding_rule
        extra['targetpool'] = forwarding_rule.targetpool
        extra['healthchecks'] = forwarding_rule.targetpool.healthchecks

        return LoadBalancer(id=forwarding_rule.id,
                            name=forwarding_rule.name, state=None,
                            ip=forwarding_rule.address,
                            port=forwarding_rule.extra['portRange'],
                            driver=self, extra=extra)
Ejemplo n.º 7
0
class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
    """
    Google Compute Engine Test Class.
    """
    # Mock out a few specific calls that interact with the user, system or
    # environment.
    GoogleBaseConnection._get_token_info_from_file = lambda x: None
    GoogleBaseConnection._write_token_info_to_file = lambda x: None
    GoogleInstalledAppAuthConnection.get_code = lambda x: '1234'
    GCEZone._now = lambda x: datetime.datetime(2013, 6, 26, 19, 0, 0)
    datacenter = 'us-central1-a'

    def setUp(self):
        GCEMockHttp.test = self
        GCENodeDriver.connectionCls.conn_classes = (GCEMockHttp, GCEMockHttp)
        GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
                                                 GoogleAuthMockHttp)
        GCEMockHttp.type = None
        kwargs = GCE_KEYWORD_PARAMS.copy()
        kwargs['auth_type'] = 'IA'
        kwargs['datacenter'] = self.datacenter
        self.driver = GCENodeDriver(*GCE_PARAMS, **kwargs)

    def test_timestamp_to_datetime(self):
        timestamp1 = '2013-06-26T10:05:19.340-07:00'
        datetime1 = datetime.datetime(2013, 6, 26, 17, 5, 19)
        self.assertEqual(timestamp_to_datetime(timestamp1), datetime1)
        timestamp2 = '2013-06-26T17:43:15.000-00:00'
        datetime2 = datetime.datetime(2013, 6, 26, 17, 43, 15)
        self.assertEqual(timestamp_to_datetime(timestamp2), datetime2)

    def test_find_zone(self):
        zone1 = self.driver._find_zone('libcloud-demo-np-node', 'instances')
        self.assertEqual(zone1, 'us-central1-a')
        zone2 = self.driver._find_zone('libcloud-demo-europe-np-node',
                                       'instances')
        self.assertEqual(zone2, 'europe-west1-a')
        region = self.driver._find_zone('libcloud-demo-address',
                                        'addresses',
                                        region=True)
        self.assertEqual(region, 'us-central1')

    def test_match_images(self):
        project = 'debian-cloud'
        image = self.driver._match_images(project, 'debian-7')
        self.assertEqual(image.name, 'debian-7-wheezy-v20130617')
        image = self.driver._match_images(project, 'debian-6')
        self.assertEqual(image.name, 'debian-6-squeeze-v20130617')

    def test_ex_list_addresses(self):
        address_list = self.driver.ex_list_addresses()
        address_list_all = self.driver.ex_list_addresses('all')
        address_list_uc1 = self.driver.ex_list_addresses('us-central1')
        self.assertEqual(len(address_list), 2)
        self.assertEqual(len(address_list_all), 4)
        self.assertEqual(address_list[0].name, 'libcloud-demo-address')
        self.assertEqual(address_list_uc1[0].name, 'libcloud-demo-address')
        self.assertEqual(address_list_all[0].name, 'lcaddress')

    def test_ex_list_firewalls(self):
        firewalls = self.driver.ex_list_firewalls()
        self.assertEqual(len(firewalls), 4)
        self.assertEqual(firewalls[0].name, 'default-allow-internal')

    def test_list_images(self):
        local_images = self.driver.list_images()
        debian_images = self.driver.list_images(ex_project='debian-cloud')
        self.assertEqual(len(local_images), 1)
        self.assertEqual(len(debian_images), 10)
        self.assertEqual(local_images[0].name, 'debian-7-wheezy-v20130617')

    def test_list_locations(self):
        locations = self.driver.list_locations()
        self.assertEqual(len(locations), 5)
        self.assertEqual(locations[0].name, 'europe-west1-a')

    def test_ex_list_networks(self):
        networks = self.driver.ex_list_networks()
        self.assertEqual(len(networks), 3)
        self.assertEqual(networks[0].name, 'default')

    def test_list_nodes(self):
        nodes = self.driver.list_nodes()
        nodes_all = self.driver.list_nodes(ex_zone='all')
        nodes_uc1a = self.driver.list_nodes(ex_zone='us-central1-a')
        self.assertEqual(len(nodes), 5)
        self.assertEqual(len(nodes_all), 8)
        self.assertEqual(len(nodes_uc1a), 5)
        self.assertEqual(nodes[0].name, 'node-name')
        self.assertEqual(nodes_uc1a[0].name, 'node-name')
        self.assertEqual(nodes_all[0].name, 'libcloud-demo-persist-node')

    def test_list_sizes(self):
        sizes = self.driver.list_sizes()
        sizes_all = self.driver.list_sizes('all')
        self.assertEqual(len(sizes), 22)
        self.assertEqual(len(sizes_all), 100)
        self.assertEqual(sizes[0].name, 'f1-micro')
        self.assertEqual(sizes[0].extra['zone'].name, 'us-central1-a')
        self.assertEqual(sizes_all[0].name, 'n1-highmem-8')
        self.assertEqual(sizes_all[0].extra['zone'].name, 'us-central1-a')

    def test_list_volumes(self):
        volumes = self.driver.list_volumes()
        volumes_all = self.driver.list_volumes('all')
        volumes_uc1a = self.driver.list_volumes('us-central1-a')
        self.assertEqual(len(volumes), 3)
        self.assertEqual(len(volumes_all), 3)
        self.assertEqual(len(volumes_uc1a), 3)
        self.assertEqual(volumes[0].name, 'lcdisk')
        self.assertEqual(volumes_all[0].name, 'test-disk')
        self.assertEqual(volumes_uc1a[0].name, 'lcdisk')

    def test_ex_list_zones(self):
        zones = self.driver.ex_list_zones()
        self.assertEqual(len(zones), 5)
        self.assertEqual(zones[0].name, 'europe-west1-a')

    def test_ex_create_address(self):
        address_name = 'lcaddress'
        address = self.driver.ex_create_address(address_name)
        self.assertTrue(isinstance(address, GCEAddress))
        self.assertEqual(address.name, address_name)

    def test_ex_create_firewall(self):
        firewall_name = 'lcfirewall'
        allowed = [{'IPProtocol': 'tcp', 'ports': ['4567']}]
        source_tags = ['libcloud']
        firewall = self.driver.ex_create_firewall(firewall_name,
                                                  allowed,
                                                  source_tags=source_tags)
        self.assertTrue(isinstance(firewall, GCEFirewall))
        self.assertEqual(firewall.name, firewall_name)

    def test_ex_create_network(self):
        network_name = 'lcnetwork'
        cidr = '10.11.0.0/16'
        network = self.driver.ex_create_network(network_name, cidr)
        self.assertTrue(isinstance(network, GCENetwork))
        self.assertEqual(network.name, network_name)
        self.assertEqual(network.cidr, cidr)

    def test_create_node_req(self):
        image = self.driver.ex_get_image('debian-7')
        size = self.driver.ex_get_size('n1-standard-1')
        location = self.driver.zone
        network = self.driver.ex_get_network('default')
        tags = ['libcloud']
        metadata = [{'key': 'test_key', 'value': 'test_value'}]
        boot_disk = self.driver.ex_get_volume('lcdisk')
        node_request, node_data = self.driver._create_node_req(
            'lcnode', size, image, location, network, tags, metadata,
            boot_disk)
        self.assertEqual(node_request, '/zones/%s/instances' % location.name)
        self.assertEqual(node_data['metadata'][0]['key'], 'test_key')
        self.assertEqual(node_data['tags']['items'][0], 'libcloud')
        self.assertEqual(node_data['name'], 'lcnode')
        self.assertTrue(node_data['disks'][0]['boot'])

    def test_create_node(self):
        node_name = 'node-name'
        image = self.driver.ex_get_image('debian-7')
        size = self.driver.ex_get_size('n1-standard-1')
        node = self.driver.create_node(node_name, size, image)
        self.assertTrue(isinstance(node, Node))
        self.assertEqual(node.name, node_name)

    def test_create_node_existing(self):
        node_name = 'libcloud-demo-europe-np-node'
        image = self.driver.ex_get_image('debian-7')
        size = self.driver.ex_get_size('n1-standard-1', zone='europe-west1-a')
        self.assertRaises(ResourceExistsError,
                          self.driver.create_node,
                          node_name,
                          size,
                          image,
                          location='europe-west1-a')

    def test_ex_create_multiple_nodes(self):
        base_name = 'lcnode'
        image = self.driver.ex_get_image('debian-7')
        size = self.driver.ex_get_size('n1-standard-1')
        number = 2
        nodes = self.driver.ex_create_multiple_nodes(base_name, size, image,
                                                     number)
        self.assertEqual(len(nodes), 2)
        self.assertTrue(isinstance(nodes[0], Node))
        self.assertTrue(isinstance(nodes[1], Node))
        self.assertEqual(nodes[0].name, '%s-000' % base_name)
        self.assertEqual(nodes[1].name, '%s-001' % base_name)

    def test_create_volume(self):
        volume_name = 'lcdisk'
        size = 1
        volume = self.driver.create_volume(size, volume_name)
        self.assertTrue(isinstance(volume, StorageVolume))
        self.assertEqual(volume.name, volume_name)

    def test_ex_update_firewall(self):
        firewall_name = 'lcfirewall'
        firewall = self.driver.ex_get_firewall(firewall_name)
        firewall.source_ranges = ['10.0.0.0/16']
        firewall.source_tags = ['libcloud', 'test']
        firewall2 = self.driver.ex_update_firewall(firewall)
        self.assertTrue(isinstance(firewall2, GCEFirewall))

    def test_reboot_node(self):
        node = self.driver.ex_get_node('node-name')
        reboot = self.driver.reboot_node(node)
        self.assertTrue(reboot)

    def test_ex_set_node_tags(self):
        new_tags = ['libcloud']
        node = self.driver.ex_get_node('node-name')
        set_tags = self.driver.ex_set_node_tags(node, new_tags)
        self.assertTrue(set_tags)

    def test_attach_volume(self):
        volume = self.driver.ex_get_volume('lcdisk')
        node = self.driver.ex_get_node('node-name')
        attach = volume.attach(node)
        self.assertTrue(attach)

    def test_detach_volume(self):
        volume = self.driver.ex_get_volume('lcdisk')
        node = self.driver.ex_get_node('node-name')
        # This fails since the node is required
        detach = volume.detach()
        self.assertFalse(detach)
        # This should pass
        detach = self.driver.detach_volume(volume, node)
        self.assertTrue(detach)

    def test_ex_destroy_address(self):
        address = self.driver.ex_get_address('lcaddress')
        destroyed = address.destroy()
        self.assertTrue(destroyed)

    def test_ex_destroy_firewall(self):
        firewall = self.driver.ex_get_firewall('lcfirewall')
        destroyed = firewall.destroy()
        self.assertTrue(destroyed)

    def test_ex_destroy_network(self):
        network = self.driver.ex_get_network('lcnetwork')
        destroyed = network.destroy()
        self.assertTrue(destroyed)

    def test_destroy_node(self):
        node = self.driver.ex_get_node('node-name')
        destroyed = node.destroy()
        self.assertTrue(destroyed)

    def test_ex_destroy_multiple_nodes(self):
        nodes = []
        nodes.append(self.driver.ex_get_node('lcnode-000'))
        nodes.append(self.driver.ex_get_node('lcnode-001'))
        destroyed = self.driver.ex_destroy_multiple_nodes(nodes)
        for d in destroyed:
            self.assertTrue(d)

    def test_destroy_volume(self):
        address = self.driver.ex_get_address('lcaddress')
        destroyed = address.destroy()
        self.assertTrue(destroyed)

    def test_ex_get_address(self):
        address_name = 'lcaddress'
        address = self.driver.ex_get_address(address_name)
        self.assertEqual(address.name, address_name)
        self.assertEqual(address.address, '173.255.113.20')
        self.assertEqual(address.region, 'us-central1')
        self.assertEqual(address.extra['status'], 'RESERVED')

    def test_ex_get_firewall(self):
        firewall_name = 'lcfirewall'
        firewall = self.driver.ex_get_firewall(firewall_name)
        self.assertEqual(firewall.name, firewall_name)
        self.assertEqual(firewall.network.name, 'default')
        self.assertEqual(firewall.source_tags, ['libcloud'])

    def test_ex_get_image(self):
        partial_name = 'debian-7'
        image = self.driver.ex_get_image(partial_name)
        self.assertEqual(image.name, 'debian-7-wheezy-v20130617')
        # A 'debian-7' image exists in the local project
        self.assertTrue(image.extra['description'].startswith('Local'))

        partial_name = 'debian-6'
        image = self.driver.ex_get_image(partial_name)
        self.assertEqual(image.name, 'debian-6-squeeze-v20130617')
        self.assertTrue(image.extra['description'].startswith('Debian'))

    def test_ex_get_network(self):
        network_name = 'lcnetwork'
        network = self.driver.ex_get_network(network_name)
        self.assertEqual(network.name, network_name)
        self.assertEqual(network.cidr, '10.11.0.0/16')
        self.assertEqual(network.extra['gatewayIPv4'], '10.11.0.1')

    def test_ex_get_project(self):
        project = self.driver.ex_get_project()
        self.assertEqual(project.name, 'project_name')
        instances_quota = project.quotas[0]
        self.assertEqual(instances_quota['usage'], 7.0)
        self.assertEqual(instances_quota['limit'], 8.0)

    def test_ex_get_size(self):
        size_name = 'n1-standard-1'
        size = self.driver.ex_get_size(size_name)
        self.assertEqual(size.name, size_name)
        self.assertEqual(size.extra['zone'].name, 'us-central1-a')
        self.assertEqual(size.disk, 10)
        self.assertEqual(size.ram, 3840)
        self.assertEqual(size.extra['guestCpus'], 1)

    def test_ex_get_volume(self):
        volume_name = 'lcdisk'
        volume = self.driver.ex_get_volume(volume_name)
        self.assertEqual(volume.name, volume_name)
        self.assertEqual(volume.size, '1')
        self.assertEqual(volume.extra['status'], 'READY')

    def test_ex_get_zone(self):
        zone_name = 'us-central1-a'
        expected_time_until = datetime.timedelta(days=52)
        expected_duration = datetime.timedelta(days=15)
        zone = self.driver.ex_get_zone(zone_name)
        self.assertEqual(zone.name, zone_name)
        self.assertEqual(zone.time_until_mw, expected_time_until)
        self.assertEqual(zone.next_mw_duration, expected_duration)