libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From erjoh...@apache.org
Subject libcloud git commit: [google compute] Support HTTP-LB in GCE NodeDriver
Date Thu, 08 Jan 2015 14:46:29 GMT
Repository: libcloud
Updated Branches:
  refs/heads/trunk 62477a421 -> ecd36734f


[google compute] Support HTTP-LB in GCE NodeDriver

Closes #429
Closes LIBCLOUD-605

Signed-off-by: Eric Johnson <erjohnso@google.com>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/ecd36734
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/ecd36734
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/ecd36734

Branch: refs/heads/trunk
Commit: ecd36734f0c258f7225ddd4ad10d465e93da0d37
Parents: 62477a4
Author: Lee Verberne <verb@google.com>
Authored: Tue Dec 23 13:50:24 2014 -0800
Committer: Eric Johnson <erjohnso@google.com>
Committed: Thu Jan 8 14:45:06 2015 +0000

----------------------------------------------------------------------
 CHANGES.rst                                     |   4 +
 libcloud/common/google.py                       |   2 +-
 libcloud/compute/drivers/gce.py                 | 528 +++++++++++++++++--
 .../gce/global_backendServices-empty.json       |   5 +
 .../gce/global_backendServices-web-service.json |  38 ++
 .../gce/global_backendServices_no_backends.json |  15 +
 .../gce/global_backendServices_post.json        |  13 +
 .../gce/global_backendServices_web_service.json |  31 ++
 ...obal_backendServices_web_service_delete.json |  14 +
 .../fixtures/gce/global_forwardingRules.json    |  29 +
 .../gce/global_forwardingRules_http_rule.json   |  12 +
 ...global_forwardingRules_http_rule_delete.json |  14 +
 .../gce/global_forwardingRules_post.json        |  13 +
 .../fixtures/gce/global_targetHttpProxies.json  |  23 +
 .../gce/global_targetHttpProxies_post.json      |  13 +
 .../gce/global_targetHttpProxies_web_proxy.json |   8 +
 ...obal_targetHttpProxies_web_proxy_delete.json |  14 +
 .../compute/fixtures/gce/global_urlMaps.json    |  16 +
 .../fixtures/gce/global_urlMaps_post.json       |  13 +
 .../fixtures/gce/global_urlMaps_web_map.json    |   9 +
 .../gce/global_urlMaps_web_map_delete.json      |  14 +
 ...s_operation_global_backendServices_post.json |  15 +
 ...obal_backendServices_web_service_delete.json |  15 +
 ...global_forwardingRules_http_rule_delete.json |  15 +
 ...s_operation_global_forwardingRules_post.json |  15 +
 ...operation_global_targetHttpProxies_post.json |  15 +
 ...obal_targetHttpProxies_web_proxy_delete.json |  15 +
 ...perations_operation_global_urlMaps_post.json |  15 +
 ...operation_global_urlMaps_web_map_delete.json |  15 +
 libcloud/test/compute/test_gce.py               | 295 ++++++++++-
 30 files changed, 1198 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 4037b87..d773d74 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -16,6 +16,10 @@ General
 Compute
 ~~~~~~~
 
+- GCE driver support for HTTP load-balancer resources
+  (LIBCLOUD-605, GITHUB-429)
+  [Lee Verberne]
+
 - GCE driver updated to make better use of GCEDiskTypes
   (GITHUB-428)
   [Eric Johnson]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/common/google.py
----------------------------------------------------------------------
diff --git a/libcloud/common/google.py b/libcloud/common/google.py
index ebb9fdf..7e3962a 100644
--- a/libcloud/common/google.py
+++ b/libcloud/common/google.py
@@ -730,7 +730,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection):
 
         In many places, the Google API returns a full URL to a resource.
         This will strip the scheme and host off of the path and just return
-        the request.  Otherwise, it will append the base request_path to
+        the request.  Otherwise, it will prepend the base request_path to
         the action.
 
         :param  action: The action to be called in the http request

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py
index 254d95a..b4cdbb4 100644
--- a/libcloud/compute/drivers/gce.py
+++ b/libcloud/compute/drivers/gce.py
@@ -136,6 +136,37 @@ class GCEAddress(UuidMixin):
             (hasattr(self.region, "name") and self.region.name or self.region))
 
 
+class GCEBackendService(UuidMixin):
+    """A GCE Backend Service."""
+
+    def __init__(self, id, name, backends, healthchecks, port, port_name,
+                 protocol, timeout, driver, extra=None):
+        self.id = str(id)
+        self.name = name
+        self.backends = backends or []
+        self.healthchecks = healthchecks or []
+        self.port = port
+        self.port_name = port_name
+        self.protocol = protocol
+        self.timeout = timeout
+        self.driver = driver
+        self.extra = extra or {}
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return '<GCEBackendService id="%s" name="%s">' % (
+            self.id, self.name)
+
+    def destroy(self):
+        """
+        Destroy this Backend Service.
+
+        :return: True if successful
+        :rtype:  ``bool``
+        """
+        return self.driver.ex_destroy_backendservice(backendservice=self)
+
+
 class GCEFailedDisk(object):
     """Dummy Node object for disks that are not created."""
     def __init__(self, name, error, code):
@@ -173,7 +204,7 @@ class GCEHealthCheck(UuidMixin):
         self.unhealthy_threshold = unhealthy_threshold
         self.healthy_threshold = healthy_threshold
         self.driver = driver
-        self.extra = extra
+        self.extra = extra or {}
         UuidMixin.__init__(self)
 
     def destroy(self):
@@ -245,6 +276,8 @@ class GCEForwardingRule(UuidMixin):
         self.region = region
         self.address = address
         self.protocol = protocol
+        # TODO: 'targetpool' should more correctly be 'target' since a
+        # forwarding rule's target can be something besides a targetpool
         self.targetpool = targetpool
         self.driver = driver
         self.extra = extra
@@ -455,6 +488,29 @@ class GCESnapshot(VolumeSnapshot):
         super(GCESnapshot, self).__init__(id, driver, size, extra)
 
 
+class GCETargetHttpProxy(UuidMixin):
+    def __init__(self, id, name, urlmap, driver, extra=None):
+        self.id = str(id)
+        self.name = name
+        self.urlmap = urlmap
+        self.driver = driver
+        self.extra = extra or {}
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return '<GCETargetHttpProxy id="%s" name="%s">' % (
+            self.id, self.name)
+
+    def destroy(self):
+        """
+        Destroy this Target HTTP Proxy.
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        return self.driver.ex_destroy_targethttpproxy(targethttpproxy=self)
+
+
 class GCETargetInstance(UuidMixin):
     def __init__(self, id, name, zone, node, driver, extra=None):
         self.id = str(id)
@@ -590,6 +646,35 @@ class GCETargetPool(UuidMixin):
             self.id, self.name, self.region.name)
 
 
+class GCEUrlMap(UuidMixin):
+    """A GCE URL Map."""
+
+    def __init__(self, id, name, default_service, host_rules, path_matchers,
+                 tests, driver, extra=None):
+        self.id = str(id)
+        self.name = name
+        self.default_service = default_service
+        self.host_rules = host_rules or []
+        self.path_matchers = path_matchers or []
+        self.tests = tests or []
+        self.driver = driver
+        self.extra = extra or {}
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return '<GCEUrlMap id="%s" name="%s">' % (
+            self.id, self.name)
+
+    def destroy(self):
+        """
+        Destroy this URL Map
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        return self.driver.ex_destroy_urlmap(urlmap=self)
+
+
 class GCEZone(NodeLocation):
     """Subclass of NodeLocation to provide additional information."""
     def __init__(self, id, name, status, maintenance_windows, deprecated,
@@ -1076,6 +1161,22 @@ class GCENodeDriver(NodeDriver):
                                   response['items']]
         return list_addresses
 
+    def ex_list_backendservices(self):
+        """
+        Return a list of backend services.
+
+        :return: A list of backend service objects.
+        :rtype: ``list`` of :class:`GCEBackendService`
+        """
+        list_backendservices = []
+        response = self.connection.request('/global/backendServices',
+                                           method='GET').object
+
+        list_backendservices = [self._to_backendservice(d) for d in
+                                response.get('items', [])]
+
+        return list_backendservices
+
     def ex_list_healthchecks(self):
         """
         Return the list of health checks.
@@ -1104,7 +1205,7 @@ class GCENodeDriver(NodeDriver):
                           response.get('items', [])]
         return list_firewalls
 
-    def ex_list_forwarding_rules(self, region=None):
+    def ex_list_forwarding_rules(self, region=None, global_rules=False):
         """
         Return the list of forwarding rules for a region or all.
 
@@ -1112,23 +1213,33 @@ class GCENodeDriver(NodeDriver):
                           example: 'us-central1'.  If None, will return
                           forwarding rules from the region of self.region
                           (which is based on self.zone).  If 'all', will
-                          return all forwarding rules.
+                          return forwarding rules for all regions, which does
+                          not include the global forwarding rules.
         :type     region: ``str`` or :class:`GCERegion` or ``None``
 
+        :keyword  global_rules: List global forwarding rules instead of
+                                per-region rules.  Setting True will cause
+                                'region' parameter to be ignored.
+        :type     global_rules: ``bool``
+
         :return: A list of forwarding rule objects.
         :rtype: ``list`` of :class:`GCEForwardingRule`
         """
         list_forwarding_rules = []
-        region = self._set_region(region)
-        if region is None:
-            request = '/aggregated/forwardingRules'
+        if global_rules:
+            region = None
+            request = '/global/forwardingRules'
         else:
-            request = '/regions/%s/forwardingRules' % (region.name)
+            region = self._set_region(region)
+            if region is None:
+                request = '/aggregated/forwardingRules'
+            else:
+                request = '/regions/%s/forwardingRules' % (region.name)
         response = self.connection.request(request, method='GET').object
 
         if 'items' in response:
             # The aggregated result returns dictionaries for each region
-            if region is None:
+            if not global_rules and region is None:
                 for v in response['items'].values():
                     region_forwarding_rules = [self._to_forwarding_rule(f) for
                                                f in v.get('forwardingRules',
@@ -1349,6 +1460,18 @@ class GCENodeDriver(NodeDriver):
                           response.get('items', [])]
         return list_snapshots
 
+    def ex_list_targethttpproxies(self):
+        """
+        Return the list of target HTTP proxies.
+
+        :return:  A list of target http proxy objects
+        :rtype:   ``list`` of :class:`GCETargetHttpProxy`
+        """
+        request = '/global/targetHttpProxies'
+        response = self.connection.request(request, method='GET').object
+        return [self._to_targethttpproxy(u) for u in
+                response.get('items', [])]
+
     def ex_list_targetinstances(self, zone=None):
         """
         Return the list of target instances.
@@ -1403,6 +1526,17 @@ class GCENodeDriver(NodeDriver):
                                     response['items']]
         return list_targetpools
 
+    def ex_list_urlmaps(self):
+        """
+        Return the list of URL Maps in the project.
+
+        :return:  A list of url map objects
+        :rtype:   ``list`` of :class:`GCEUrlMap`
+        """
+        request = '/global/urlMaps'
+        response = self.connection.request(request, method='GET').object
+        return [self._to_urlmap(u) for u in response.get('items', [])]
+
     def list_volumes(self, ex_zone=None):
         """
         Return a list of volumes for a zone or all.
@@ -1491,6 +1625,33 @@ class GCENodeDriver(NodeDriver):
                                       data=address_data)
         return self.ex_get_address(name, region=region)
 
+    def ex_create_backendservice(self, name, healthchecks):
+        """
+        Create a global backend service.
+
+        :param  name: Name of the backend service
+        :type   name: ``str``
+
+        :keyword  healthchecks: A list of HTTP Health Checks to use for this
+                                service.  There must be at least one.
+        :type     healthchecks: ``list`` of (``str`` or
+                                :class:`GCEHealthCheck`)
+
+        :return:  A Backend Service object
+        :rtype:   :class:`GCEBackendService`
+        """
+        backendservice_data = {'name': name, 'healthChecks': []}
+
+        for hc in healthchecks:
+            if not hasattr(hc, 'extra'):
+                hc = self.ex_get_healthcheck(name=hc)
+            backendservice_data['healthChecks'].append(hc.extra['selfLink'])
+
+        request = '/global/backendServices'
+        self.connection.async_request(request, method='POST',
+                                      data=backendservice_data)
+        return self.ex_get_backendservice(name)
+
     def ex_create_healthcheck(self, name, host=None, path=None, port=None,
                               interval=None, timeout=None,
                               unhealthy_threshold=None,
@@ -1622,27 +1783,37 @@ class GCENodeDriver(NodeDriver):
                                       data=firewall_data)
         return self.ex_get_firewall(name)
 
-    def ex_create_forwarding_rule(self, name, targetpool, region=None,
+    def ex_create_forwarding_rule(self, name, target=None, region=None,
                                   protocol='tcp', port_range=None,
-                                  address=None, description=None):
+                                  address=None, description=None,
+                                  global_rule=False, targetpool=None):
         """
         Create a forwarding rule.
 
         :param  name: Name of forwarding rule to be created
         :type   name: ``str``
 
-        :param  targetpool: Target pool to apply the rule to
-        :param  targetpool: ``str`` or :class:`GCETargetPool`
+        :keyword  target: The target of this forwarding rule.  For global
+                          forwarding rules this must be a global
+                          TargetHttpProxy. For regional rules this may be
+                          either a TargetPool or TargetInstance. If passed
+                          a string instead of the object, it will be the name
+                          of a TargetHttpProxy for global rules or a
+                          TargetPool for regional rules.  A TargetInstance
+                          must be passed by object. (required)
+        :type     target: ``str`` or :class:`GCETargetHttpProxy` or
+                          :class:`GCETargetInstance` or :class:`GCETargetPool`
 
         :keyword  region: Region to create the forwarding rule in.  Defaults to
-                          self.region
+                          self.region.  Ignored if global_rule is True.
         :type     region: ``str`` or :class:`GCERegion`
 
         :keyword  protocol: Should be 'tcp' or 'udp'
         :type     protocol: ``str``
 
-        :keyword  port_range: Optional single port number or range separated
-                              by a dash.  Examples: '80', '5000-5999'.
+        :keyword  port_range: Single port number or range separated by a dash.
+                              Examples: '80', '5000-5999'.  Required for global
+                              forwarding rules, optional for regional rules.
         :type     port_range: ``str``
 
         :keyword  address: Optional static address for forwarding rule. Must be
@@ -1653,35 +1824,49 @@ class GCENodeDriver(NodeDriver):
                                Defaults to None.
         :type     description: ``str`` or ``None``
 
+        :keyword  targetpool: Deprecated parameter for backwards compatibility.
+                              Use target instead.
+        :type     targetpool: ``str`` or :class:`GCETargetPool`
+
         :return:  Forwarding Rule object
         :rtype:   :class:`GCEForwardingRule`
         """
-        forwarding_rule_data = {}
-        region = region or self.region
-        if not hasattr(region, 'name'):
-            region = self.ex_get_region(region)
-        if not hasattr(targetpool, 'name'):
-            targetpool = self.ex_get_targetpool(targetpool, region)
+        forwarding_rule_data = {'name': name}
+        if global_rule:
+            if not hasattr(target, 'name'):
+                target = self.ex_get_targethttpproxy(target)
+        else:
+            region = region or self.region
+            if not hasattr(region, 'name'):
+                region = self.ex_get_region(region)
+            forwarding_rule_data['region'] = region.extra['selfLink']
+
+            if not target:
+                target = targetpool  # Backwards compatibility
+            if not hasattr(target, 'name'):
+                target = self.ex_get_targetpool(target, region)
 
-        forwarding_rule_data['name'] = name
-        forwarding_rule_data['region'] = region.extra['selfLink']
-        forwarding_rule_data['target'] = targetpool.extra['selfLink']
+        forwarding_rule_data['target'] = target.extra['selfLink']
         forwarding_rule_data['IPProtocol'] = protocol.upper()
         if address:
             if not hasattr(address, 'name'):
-                address = self.ex_get_address(address, region)
+                address = self.ex_get_address(
+                    address, 'global' if global_rule else region)
             forwarding_rule_data['IPAddress'] = address.address
         if port_range:
             forwarding_rule_data['portRange'] = port_range
         if description:
             forwarding_rule_data['description'] = description
 
-        request = '/regions/%s/forwardingRules' % (region.name)
+        if global_rule:
+            request = '/global/forwardingRules'
+        else:
+            request = '/regions/%s/forwardingRules' % (region.name)
 
         self.connection.async_request(request, method='POST',
                                       data=forwarding_rule_data)
 
-        return self.ex_get_forwarding_rule(name)
+        return self.ex_get_forwarding_rule(name, global_rule=global_rule)
 
     def ex_create_image(self, name, volume, description=None,
                         use_existing=True, wait_for_completion=True):
@@ -2229,6 +2414,32 @@ n
             node_list.append(status['node'])
         return node_list
 
+    def ex_create_targethttpproxy(self, name, urlmap):
+        """
+        Create a target HTTP proxy.
+
+        :param  name: Name of target HTTP proxy
+        :type   name: ``str``
+
+        :keyword  urlmap: URL map defining the mapping from URl to the
+                           backendservice.
+        :type     healthchecks: ``str`` or :class:`GCEUrlMap`
+
+        :return:  Target Pool object
+        :rtype:   :class:`GCETargetPool`
+        """
+        targetproxy_data = {'name': name}
+
+        if not hasattr(urlmap, 'name'):
+            urlmap = self.ex_get_urlmap(urlmap)
+        targetproxy_data['urlMap'] = urlmap.extra['selfLink']
+
+        request = '/global/targetHttpProxies'
+        self.connection.async_request(request, method='POST',
+                                      data=targetproxy_data)
+
+        return self.ex_get_targethttpproxy(name)
+
     def ex_create_targetinstance(self, name, zone=None, node=None,
                                  description=None, nat_policy="NO_NAT"):
         """
@@ -2345,6 +2556,32 @@ n
 
         return self.ex_get_targetpool(name, region)
 
+    def ex_create_urlmap(self, name, default_service):
+        """
+        Create a URL Map.
+
+        :param  name: Name of the URL Map.
+        :type   name: ``str``
+
+        :keyword  default_service: Default backend service for the map.
+        :type     default_service: ``str`` or :class:`GCEBackendService`
+
+        :return:  URL Map object
+        :rtype:   :class:`GCEUrlMap`
+        """
+        urlmap_data = {'name': name}
+
+        # TODO: support hostRules, pathMatchers, tests
+        if not hasattr(default_service, 'name'):
+            default_service = self.ex_get_backendservice(default_service)
+        urlmap_data['defaultService'] = default_service.extra['selfLink']
+
+        request = '/global/urlMaps'
+        self.connection.async_request(request, method='POST',
+                                      data=urlmap_data)
+
+        return self.ex_get_urlmap(name)
+
     def create_volume(self, size, name, location=None, snapshot=None,
                       image=None, use_existing=True,
                       ex_disk_type='pd-standard'):
@@ -3037,6 +3274,21 @@ n
         self.connection.async_request(request, method='DELETE')
         return True
 
+    def ex_destroy_backendservice(self, backendservice):
+        """
+        Destroy a Backend Service.
+
+        :param  backendservice: BackendService object to destroy
+        :type   backendservice: :class:`GCEBackendService`
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        request = '/global/backendServices/%s' % backendservice.name
+
+        self.connection.async_request(request, method='DELETE')
+        return True
+
     def ex_delete_image(self, image):
         """
         Delete a specific image resource.
@@ -3157,8 +3409,11 @@ n
         :return:  True if successful
         :rtype:   ``bool``
         """
-        request = '/regions/%s/forwardingRules/%s' % (
-            forwarding_rule.region.name, forwarding_rule.name)
+        if forwarding_rule.region:
+            request = '/regions/%s/forwardingRules/%s' % (
+                forwarding_rule.region.name, forwarding_rule.name)
+        else:
+            request = '/global/forwardingRules/%s' % forwarding_rule.name
         self.connection.async_request(request, method='DELETE')
         return True
 
@@ -3318,6 +3573,20 @@ n
             success.append(s)
         return success
 
+    def ex_destroy_targethttpproxy(self, targethttpproxy):
+        """
+        Destroy a target HTTP proxy.
+
+        :param  targethttpproxy: TargetHttpProxy object to destroy
+        :type   targethttpproxy: :class:`GCETargetHttpProxy`
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        request = '/global/targetHttpProxies/%s' % targethttpproxy.name
+        self.connection.async_request(request, method='DELETE')
+        return True
+
     def ex_destroy_targetinstance(self, targetinstance):
         """
         Destroy a target instance.
@@ -3349,6 +3618,21 @@ n
         self.connection.async_request(request, method='DELETE')
         return True
 
+    def ex_destroy_urlmap(self, urlmap):
+        """
+        Destroy a URL map.
+
+        :param  urlmap: UrlMap object to destroy
+        :type   urlmap: :class:`GCEUrlMap`
+
+        :return:  True if successful
+        :rtype:   ``bool``
+        """
+        request = '/global/urlMaps/%s' % urlmap.name
+
+        self.connection.async_request(request, method='DELETE')
+        return True
+
     def destroy_volume(self, volume):
         """
         Destroy a volume.
@@ -3443,6 +3727,20 @@ n
         response = self.connection.request(request, method='GET').object
         return self._to_address(response)
 
+    def ex_get_backendservice(self, name):
+        """
+        Return a Backend Service object based on name
+
+        :param  name: The name of the backend service
+        :type   name: ``str``
+
+        :return:  A BackendService object for the backend service
+        :rtype:   :class:`GCEBackendService`
+        """
+        request = '/global/backendServices/%s' % name
+        response = self.connection.request(request, method='GET').object
+        return self._to_backendservice(response)
+
     def ex_get_healthcheck(self, name):
         """
         Return a HealthCheck object based on the healthcheck name.
@@ -3471,7 +3769,7 @@ n
         response = self.connection.request(request, method='GET').object
         return self._to_firewall(response)
 
-    def ex_get_forwarding_rule(self, name, region=None):
+    def ex_get_forwarding_rule(self, name, region=None, global_rule=False):
         """
         Return a Forwarding Rule object based on the forwarding rule name.
 
@@ -3482,12 +3780,21 @@ n
                           to search all regions).
         :type     region: ``str`` or ``None``
 
+        :keyword  global_rule: Set to True to get a global forwarding rule.
+                                Region will be ignored if True.
+        :type     global_rule: ``bool``
+
         :return:  A GCEForwardingRule object
         :rtype:   :class:`GCEForwardingRule`
         """
-        region = self._set_region(region) or self._find_zone_or_region(
-            name, 'forwardingRules', region=True, res_name='ForwardingRule')
-        request = '/regions/%s/forwardingRules/%s' % (region.name, name)
+        if global_rule:
+            request = '/global/forwardingRules/%s' % name
+        else:
+            region = self._set_region(region) or self._find_zone_or_region(
+                name, 'forwardingRules', region=True,
+                res_name='ForwardingRule')
+            request = '/regions/%s/forwardingRules/%s' % (region.name, name)
+
         response = self.connection.request(request, method='GET').object
         return self._to_forwarding_rule(response)
 
@@ -3652,6 +3959,20 @@ n
         response = self.connection.request(request, method='GET').object
         return self._to_region(response)
 
+    def ex_get_targethttpproxy(self, name):
+        """
+        Return a Target HTTP Proxy object based on its name.
+
+        :param  name: The name of the target HTTP proxy.
+        :type   name: ``str``
+
+        :return:  A Target HTTP Proxy object for the pool
+        :rtype:   :class:`GCETargetHttpProxy`
+        """
+        request = '/global/targetHttpProxies/%s' % name
+        response = self.connection.request(request, method='GET').object
+        return self._to_targethttpproxy(response)
+
     def ex_get_targetinstance(self, name, zone=None):
         """
         Return a TargetInstance object based on a name and optional zone.
@@ -3692,6 +4013,20 @@ n
         response = self.connection.request(request, method='GET').object
         return self._to_targetpool(response)
 
+    def ex_get_urlmap(self, name):
+        """
+        Return a URL Map object based on name
+
+        :param  name: The name of the url map
+        :type   name: ``str``
+
+        :return:  A URL Map object for the backend service
+        :rtype:   :class:`GCEUrlMap`
+        """
+        request = '/global/urlMaps/%s' % name
+        response = self.connection.request(request, method='GET').object
+        return self._to_urlmap(response)
+
     def ex_get_zone(self, name):
         """
         Return a Zone object based on the zone name.
@@ -3786,7 +4121,7 @@ n
         :type   path: ``str``
 
         :return:  Dictionary containing name and zone/region of resource
-        :rtype    ``dict``
+        :rtype:   ``dict``
         """
         region = None
         zone = None
@@ -3802,6 +4137,26 @@ n
 
         return {'name': name, 'region': region, 'zone': zone, 'global': glob}
 
+    def _get_object_by_kind(self, url):
+        """
+        Fetch a resource and return its object representation by mapping its
+        'kind' parameter to the appropriate class.  Returns ``None`` if url is
+        ``None``
+
+        :param  url: fully qualified URL of the resource to request from GCE
+        :type   url: ``str``
+
+        :return:  Object representation of the requested resource.
+        "rtype:   :class:`object` or ``None``
+        """
+        if not url:
+            return None
+
+        # Relies on GoogleBaseConnection.morph_action_hook to rewrite
+        # the URL to a request
+        response = self.connection.request(url, method='GET').object
+        return GCENodeDriver.KIND_METHOD_MAP[response['kind']](self, response)
+
     def _get_region_from_zone(self, zone):
         """
         Return the Region object that contains the given Zone object.
@@ -4409,6 +4764,36 @@ n
                           address=address['address'],
                           region=region, driver=self, extra=extra)
 
+    def _to_backendservice(self, backendservice):
+        """
+        Return a Backend Service object from the json-response dictionary.
+
+        :param  backendservice: The dictionary describing the backend service.
+        :type   backendservice: ``dict``
+
+        :return: BackendService object
+        :rtype: :class:`GCEBackendService`
+        """
+        extra = {}
+
+        for extra_key in ('selfLink', 'creationTimestamp', 'fingerprint',
+                          'description'):
+            extra[extra_key] = backendservice.get(extra_key)
+
+        backends = backendservice.get('backends', [])
+        healthchecks = [self._get_object_by_kind(h) for h in
+                        backendservice.get('healthChecks', [])]
+
+        return GCEBackendService(id=backendservice['id'],
+                                 name=backendservice['name'],
+                                 backends=backends,
+                                 healthchecks=healthchecks,
+                                 port=backendservice['port'],
+                                 port_name=backendservice['portName'],
+                                 protocol=backendservice['protocol'],
+                                 timeout=backendservice['timeoutSec'],
+                                 driver=self, extra=extra)
+
     def _to_healthcheck(self, healthcheck):
         """
         Return a HealthCheck object from the json-response dictionary.
@@ -4479,16 +4864,16 @@ n
         extra['creationTimestamp'] = forwarding_rule.get('creationTimestamp')
         extra['description'] = forwarding_rule.get('description')
 
-        region = self.ex_get_region(forwarding_rule['region'])
-        targetpool = self.ex_get_targetpool(
-            self._get_components_from_path(forwarding_rule['target'])['name'])
+        region = forwarding_rule.get('region')
+        if region:
+            region = self.ex_get_region(region)
+        target = self._get_object_by_kind(forwarding_rule['target'])
 
         return GCEForwardingRule(id=forwarding_rule['id'],
                                  name=forwarding_rule['name'], region=region,
                                  address=forwarding_rule.get('IPAddress'),
                                  protocol=forwarding_rule.get('IPProtocol'),
-                                 targetpool=targetpool,
-                                 driver=self, extra=extra)
+                                 targetpool=target, driver=self, extra=extra)
 
     def _to_network(self, network):
         """
@@ -4787,6 +5172,25 @@ n
         return StorageVolume(id=volume['id'], name=volume['name'],
                              size=volume['sizeGb'], driver=self, extra=extra)
 
+    def _to_targethttpproxy(self, targethttpproxy):
+        """
+        Return a Target HTTP Proxy object from the json-response dictionary.
+
+        :param  targethttpproxy: The dictionary describing the proxy.
+        :type   targethttpproxy: ``dict``
+
+        :return: Target HTTP Proxy object
+        :rtype:  :class:`GCETargetHttpProxy`
+        """
+        extra = dict([(k, targethttpproxy.get(k)) for k in (
+            'creationTimestamp', 'description', 'selfLink')])
+
+        urlmap = self._get_object_by_kind(targethttpproxy.get('urlMap'))
+
+        return GCETargetHttpProxy(id=targethttpproxy['id'],
+                                  name=targethttpproxy['name'],
+                                  urlmap=urlmap, driver=self, extra=extra)
+
     def _to_targetinstance(self, targetinstance):
         """
         Return a Target Instance object from the json-response dictionary.
@@ -4938,6 +5342,31 @@ n
             raise ValueError("Unsupported metadata format.")
         return md
 
+    def _to_urlmap(self, urlmap):
+        """
+        Return a UrlMap object from the json-response dictionary.
+
+        :param  zone: The dictionary describing the url-map.
+        :type   zone: ``dict``
+
+        :return: Zone object
+        :rtype: :class:`GCEUrlMap`
+        """
+        extra = dict([(k, urlmap.get(k)) for k in (
+            'creationTimestamp', 'description', 'fingerprint', 'selfLink')])
+
+        default_service = self._get_object_by_kind(
+            urlmap.get('defaultService'))
+
+        host_rules = urlmap.get('hostRules', [])
+        path_matchers = urlmap.get('pathMatchers', [])
+        tests = urlmap.get('tests', [])
+
+        return GCEUrlMap(id=urlmap['id'], name=urlmap['name'],
+                         default_service=default_service,
+                         host_rules=host_rules, path_matchers=path_matchers,
+                         tests=tests, driver=self, extra=extra)
+
     def _to_zone(self, zone):
         """
         Return a Zone object from the json-response dictionary.
@@ -5042,3 +5471,24 @@ n
             return_list.append(self.ex_get_license(project=lic_proj,
                                                    name=lic_name))
         return return_list
+
+    KIND_METHOD_MAP = {
+        'compute#address': _to_address,
+        'compute#backendService': _to_backendservice,
+        'compute#disk': _to_storage_volume,
+        'compute#firewall': _to_firewall,
+        'compute#forwardingRule': _to_forwarding_rule,
+        'compute#httpHealthCheck': _to_healthcheck,
+        'compute#image': _to_node_image,
+        'compute#instance': _to_node,
+        'compute#machineType': _to_node_size,
+        'compute#network': _to_network,
+        'compute#project': _to_project,
+        'compute#region': _to_region,
+        'compute#snapshot': _to_snapshot,
+        'compute#targetHttpProxy': _to_targethttpproxy,
+        'compute#targetInstance': _to_targetinstance,
+        'compute#targetPool': _to_targetpool,
+        'compute#urlMap': _to_urlmap,
+        'compute#zone': _to_zone,
+    }

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices-empty.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices-empty.json b/libcloud/test/compute/fixtures/gce/global_backendServices-empty.json
new file mode 100644
index 0000000..de09f6b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices-empty.json
@@ -0,0 +1,5 @@
+{
+ "kind": "compute#backendServiceList",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices",
+ "id": "projects/project_name/global/backendServices"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices-web-service.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices-web-service.json b/libcloud/test/compute/fixtures/gce/global_backendServices-web-service.json
new file mode 100644
index 0000000..1006258
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices-web-service.json
@@ -0,0 +1,38 @@
+{
+ "kind": "compute#backendServiceList",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices",
+ "id": "projects/project_name/global/backendServices",
+ "items": [
+  {
+   "kind": "compute#backendService",
+   "id": "12158223670162062306",
+   "creationTimestamp": "2014-08-14T14:37:36.728-07:00",
+   "name": "web-service",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+   "backends": [
+    {
+     "description": "",
+     "group": "https://www.googleapis.com/resourceviews/v1beta1/projects/project_name/zones/us-central1-b/resourceViews/us-resources",
+     "balancingMode": "RATE",
+     "maxRate": 100,
+     "capacityScaler": 1.0
+    },
+    {
+     "description": "",
+     "group": "https://www.googleapis.com/resourceviews/v1beta1/projects/project_name/zones/europe-west1-b/resourceViews/eu-resources",
+     "balancingMode": "RATE",
+     "maxRate": 100,
+     "capacityScaler": 1.0
+    }
+   ],
+   "healthChecks": [
+    "https://www.googleapis.com/compute/v1/projects/project_name/global/httpHealthChecks/basic-check"
+   ],
+   "timeoutSec": 30,
+   "port": 80,
+   "protocol": "HTTP",
+   "fingerprint": "Do4_wUywpJU=",
+   "portName": ""
+  }
+ ]
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices_no_backends.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices_no_backends.json b/libcloud/test/compute/fixtures/gce/global_backendServices_no_backends.json
new file mode 100644
index 0000000..be34cd8
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices_no_backends.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#backendService",
+ "id": "12158223690162062306",
+ "creationTimestamp": "2014-08-14T14:37:36.728-07:00",
+ "name": "web-service",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "healthChecks": [
+  "https://www.googleapis.com/compute/v1/projects/project_name/global/httpHealthChecks/basic-check"
+ ],
+ "timeoutSec": 30,
+ "port": 80,
+ "protocol": "HTTP",
+ "fingerprint": "5qm-QyYGyzw=",
+ "portName": ""
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices_post.json b/libcloud/test/compute/fixtures/gce/global_backendServices_post.json
new file mode 100644
index 0000000..efe43c7
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices_post.json
@@ -0,0 +1,13 @@
+{
+ "kind": "compute#operation",
+ "id": "8150500075597870926",
+ "name": "operation-global_backendServices_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-10T16:39:17.206-07:00",
+ "startTime": "2014-10-10T16:39:17.613-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation-global_backendServices_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices_web_service.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices_web_service.json b/libcloud/test/compute/fixtures/gce/global_backendServices_web_service.json
new file mode 100644
index 0000000..e9af3b4
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices_web_service.json
@@ -0,0 +1,31 @@
+{
+ "kind": "compute#backendService",
+ "id": "1814698108461677231",
+ "creationTimestamp": "2014-08-15T16:14:43.729-07:00",
+ "name": "web-service",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "backends": [
+  {
+   "description": "",
+   "group": "https://www.googleapis.com/resourceviews/v1beta1/projects/project_name/zones/us-central1-b/resourceViews/us-resources",
+   "balancingMode": "RATE",
+   "maxRate": 100,
+   "capacityScaler": 1.0
+  },
+  {
+   "description": "",
+   "group": "https://www.googleapis.com/resourceviews/v1beta1/projects/project_name/zones/europe-west1-b/resourceViews/eu-resources",
+   "balancingMode": "RATE",
+   "maxRate": 150,
+   "capacityScaler": 1.0
+  }
+ ],
+ "healthChecks": [
+  "https://www.googleapis.com/compute/v1/projects/project_name/global/httpHealthChecks/basic-check"
+ ],
+ "timeoutSec": 30,
+ "port": 80,
+ "protocol": "HTTP",
+ "fingerprint": "ha9VAg-MJ5M=",
+ "portName": ""
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_backendServices_web_service_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_backendServices_web_service_delete.json b/libcloud/test/compute/fixtures/gce/global_backendServices_web_service_delete.json
new file mode 100644
index 0000000..f528326
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_backendServices_web_service_delete.json
@@ -0,0 +1,14 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_backendServices_web_service_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "targetId": "15555555555223232737",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-28T12:51:20.402-07:00",
+ "startTime": "2014-10-28T12:51:20.623-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_backendServices_web_service_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_forwardingRules.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_forwardingRules.json b/libcloud/test/compute/fixtures/gce/global_forwardingRules.json
new file mode 100644
index 0000000..b16eaa5
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_forwardingRules.json
@@ -0,0 +1,29 @@
+{
+ "kind": "compute#forwardingRuleList",
+ "id": "projects/project_name/global/forwardingRules",
+ "items": [
+  {
+   "kind": "compute#forwardingRule",
+   "id": "16224943838916174114",
+   "creationTimestamp": "2014-08-22T11:15:26.174-07:00",
+   "name": "http-rule",
+   "IPAddress": "192.0.2.1",
+   "IPProtocol": "TCP",
+   "portRange": "80-80",
+   "target": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule"
+  },
+  {
+   "kind": "compute#forwardingRule",
+   "id": "16224943838916174115",
+   "creationTimestamp": "2014-08-22T11:15:26.174-07:00",
+   "name": "http-rule2",
+   "IPAddress": "192.0.2.2",
+   "IPProtocol": "TCP",
+   "portRange": "80-80",
+   "target": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule2"
+  }
+ ],
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule.json b/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule.json
new file mode 100644
index 0000000..e706195
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule.json
@@ -0,0 +1,12 @@
+{
+ "kind": "compute#forwardingRule",
+ "id": "16224243838919174114",
+ "creationTimestamp": "2014-08-22T11:15:26.174-07:00",
+ "name": "http-rule",
+ "IPAddress": "192.0.2.1",
+ "IPProtocol": "TCP",
+ "portRange": "80-80",
+ "target": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "description": "global forwarding rule",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule_delete.json b/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule_delete.json
new file mode 100644
index 0000000..ca87cec
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_forwardingRules_http_rule_delete.json
@@ -0,0 +1,14 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_forwardingRules_http_rule_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule-test",
+ "targetId": "16224243838919174114",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-28T10:53:13.433-07:00",
+ "startTime": "2014-10-28T10:53:13.723-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_forwardingRules_http_rule_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_forwardingRules_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_forwardingRules_post.json b/libcloud/test/compute/fixtures/gce/global_forwardingRules_post.json
new file mode 100644
index 0000000..dad5448
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_forwardingRules_post.json
@@ -0,0 +1,13 @@
+{
+ "kind": "compute#operation",
+ "id": "33333333333333333333",
+ "name": "operation_global_forwardingRules_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-27T17:10:54.102-07:00",
+ "startTime": "2014-10-27T17:10:54.531-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_forwardingRules_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_targetHttpProxies.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_targetHttpProxies.json b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies.json
new file mode 100644
index 0000000..a3bfe8e
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies.json
@@ -0,0 +1,23 @@
+{
+ "kind": "compute#targetHttpProxyList",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies",
+ "id": "projects/project_name/global/targetHttpProxies",
+ "items": [
+  {
+   "kind": "compute#targetHttpProxy",
+   "id": "2276970411930672658",
+   "creationTimestamp": "2014-08-22T09:47:35.425-07:00",
+   "name": "web-proxy",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+   "urlMap": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map"
+  },
+  {
+   "kind": "compute#targetHttpProxy",
+   "id": "2276970411930672659",
+   "creationTimestamp": "2014-08-22T09:47:35.425-07:00",
+   "name": "web-proxy2",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy2",
+   "urlMap": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map"
+  }
+ ]
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_post.json b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_post.json
new file mode 100644
index 0000000..8002298
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_post.json
@@ -0,0 +1,13 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_targetHttpProxies_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-27T16:22:40.726-07:00",
+ "startTime": "2014-10-27T16:22:41.027-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_targetHttpProxies_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy.json b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy.json
new file mode 100644
index 0000000..edd9b08
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy.json
@@ -0,0 +1,8 @@
+{
+ "kind": "compute#targetHttpProxy",
+ "id": "2276970411950672658",
+ "creationTimestamp": "2014-08-22T09:47:35.425-07:00",
+ "name": "web-proxy",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "urlMap": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy_delete.json b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy_delete.json
new file mode 100644
index 0000000..29754c0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_targetHttpProxies_web_proxy_delete.json
@@ -0,0 +1,14 @@
+{
+ "kind": "compute#operation",
+ "id": "33333333333333333333",
+ "name": "operation_global_targetHttpProxies_web_proxy_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "targetId": "5243939392541625113",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-28T12:21:47.406-07:00",
+ "startTime": "2014-10-28T12:21:47.666-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_targetHttpProxies_web_proxy_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_urlMaps.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_urlMaps.json b/libcloud/test/compute/fixtures/gce/global_urlMaps.json
new file mode 100644
index 0000000..0d16265
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_urlMaps.json
@@ -0,0 +1,16 @@
+{
+ "kind": "compute#urlMapList",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps",
+ "id": "projects/project_name/global/urlMaps",
+ "items": [
+  {
+   "kind": "compute#urlMap",
+   "id": "4266107551250249032",
+   "creationTimestamp": "2014-08-15T16:16:54.084-07:00",
+   "name": "web-map",
+   "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map",
+   "defaultService": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+   "fingerprint": "JiV2ACVOAlg="
+  }
+ ]
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_urlMaps_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_urlMaps_post.json b/libcloud/test/compute/fixtures/gce/global_urlMaps_post.json
new file mode 100644
index 0000000..d63a9aa
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_urlMaps_post.json
@@ -0,0 +1,13 @@
+{
+ "kind": "compute#operation",
+ "id": "14014132704089638847",
+ "name": "operation-global_urlMaps_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/test-map2",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-27T15:21:17.438-07:00",
+ "startTime": "2014-10-27T15:21:17.631-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation-global_urlMaps_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map.json b/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map.json
new file mode 100644
index 0000000..a17c8da
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map.json
@@ -0,0 +1,9 @@
+{
+ "kind": "compute#urlMap",
+ "id": "4266107551250249032",
+ "creationTimestamp": "2014-08-15T16:16:54.084-07:00",
+ "name": "web-map",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map",
+ "defaultService": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "fingerprint": "JiV2ACVOAlg="
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map_delete.json b/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map_delete.json
new file mode 100644
index 0000000..ace6f65
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/global_urlMaps_web_map_delete.json
@@ -0,0 +1,14 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_urlMaps_web_map_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map",
+ "targetId": "1955555555986139870",
+ "status": "PENDING",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 0,
+ "insertTime": "2014-10-28T12:36:28.927-07:00",
+ "startTime": "2014-10-28T12:36:29.146-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_urlMaps_web_map_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_post.json
new file mode 100644
index 0000000..64a7fba
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_post.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "8150500072597970926",
+ "name": "operation_global_backendServices_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "targetId": "4582947879210482708",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-10T16:39:17.206-07:00",
+ "startTime": "2014-10-10T16:39:17.613-07:00",
+ "endTime": "2014-10-10T16:39:18.330-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_backendServices_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_web_service_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_web_service_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_web_service_delete.json
new file mode 100644
index 0000000..c2e1fa1
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_backendServices_web_service_delete.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_backendServices_web_service_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/web-service",
+ "targetId": "15555555555223232737",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-28T12:51:20.402-07:00",
+ "startTime": "2014-10-28T12:51:20.623-07:00",
+ "endTime": "2014-10-28T12:51:21.218-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_backendServices_web_service_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_http_rule_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_http_rule_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_http_rule_delete.json
new file mode 100644
index 0000000..52946c4
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_http_rule_delete.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "4444444444444444444",
+ "name": "operation_global_forwardingRules_http_rule_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule-test",
+ "targetId": "16224243838919174114",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-28T10:53:13.433-07:00",
+ "startTime": "2014-10-28T10:53:13.723-07:00",
+ "endTime": "2014-10-28T10:53:16.304-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_forwardingRules_http_rule_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_post.json
new file mode 100644
index 0000000..e809a99
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_forwardingRules_post.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "44444444444444444444",
+ "name": "operation_global_forwardingRules_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/forwardingRules/http-rule",
+ "targetId": "16224243838919174114",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-27T17:10:54.102-07:00",
+ "startTime": "2014-10-27T17:10:54.531-07:00",
+ "endTime": "2014-10-27T17:10:59.466-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_forwardingRules_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_post.json
new file mode 100644
index 0000000..dcb9f01
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_post.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "7514147734702416613",
+ "name": "operation_global_targetHttpProxies_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "targetId": "5242670162541625113",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-27T16:22:40.726-07:00",
+ "startTime": "2014-10-27T16:22:41.027-07:00",
+ "endTime": "2014-10-27T16:22:41.657-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_targetHttpProxies_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_web_proxy_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_web_proxy_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_web_proxy_delete.json
new file mode 100644
index 0000000..d0e81d8
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_targetHttpProxies_web_proxy_delete.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "12025659947133083605",
+ "name": "operation_global_targetHttpProxies_web_proxy_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/targetHttpProxies/web-proxy",
+ "targetId": "5243939392541625113",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-28T12:21:47.406-07:00",
+ "startTime": "2014-10-28T12:21:47.666-07:00",
+ "endTime": "2014-10-28T12:21:48.419-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_targetHttpProxies_web_proxy_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_post.json
new file mode 100644
index 0000000..a16fe25
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_post.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "14014131794489638887",
+ "name": "operation-global_urlMaps_post",
+ "operationType": "insert",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map",
+ "targetId": "4266107551250249032",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-27T15:21:17.438-07:00",
+ "startTime": "2014-10-27T15:21:17.631-07:00",
+ "endTime": "2014-10-27T15:21:18.422-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation-global_urlMaps_post"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_web_map_delete.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_web_map_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_web_map_delete.json
new file mode 100644
index 0000000..102d79f
--- /dev/null
+++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_urlMaps_web_map_delete.json
@@ -0,0 +1,15 @@
+{
+ "kind": "compute#operation",
+ "id": "3333333333333333333",
+ "name": "operation_global_urlMaps_web_map_delete",
+ "operationType": "delete",
+ "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/urlMaps/web-map",
+ "targetId": "1955555555986139870",
+ "status": "DONE",
+ "user": "user@developer.gserviceaccount.com",
+ "progress": 100,
+ "insertTime": "2014-10-28T12:36:28.927-07:00",
+ "startTime": "2014-10-28T12:36:29.146-07:00",
+ "endTime": "2014-10-28T12:36:29.693-07:00",
+ "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation_global_urlMaps_web_map_delete"
+}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/ecd36734/libcloud/test/compute/test_gce.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_gce.py b/libcloud/test/compute/test_gce.py
index d1f2b75..f9c86e9 100644
--- a/libcloud/test/compute/test_gce.py
+++ b/libcloud/test/compute/test_gce.py
@@ -22,12 +22,12 @@ import datetime
 from libcloud.utils.py3 import httplib
 from libcloud.compute.drivers.gce import (GCENodeDriver, API_VERSION,
                                           timestamp_to_datetime,
-                                          GCEAddress, GCEHealthCheck,
+                                          GCEAddress, GCEBackendService,
                                           GCEFirewall, GCEForwardingRule,
-                                          GCENetwork,
-                                          GCEZone,
-                                          GCENodeImage,
-                                          GCERoute)
+                                          GCEHealthCheck, GCENetwork,
+                                          GCENodeImage, GCERoute,
+                                          GCETargetHttpProxy, GCEUrlMap,
+                                          GCEZone)
 from libcloud.common.google import (GoogleBaseAuthConnection,
                                     GoogleInstalledAppAuthConnection,
                                     GoogleBaseConnection,
@@ -77,6 +77,16 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         datetime2 = datetime.datetime(2013, 6, 26, 17, 43, 15)
         self.assertEqual(timestamp_to_datetime(timestamp2), datetime2)
 
+    def test_get_object_by_kind(self):
+        obj = self.driver._get_object_by_kind(None)
+        self.assertIsNone(obj)
+        obj = self.driver._get_object_by_kind('')
+        self.assertIsNone(obj)
+        obj = self.driver._get_object_by_kind(
+            'https://www.googleapis.com/compute/v1/projects/project_name/'
+            'global/targetHttpProxies/web-proxy')
+        self.assertEquals(obj.name, 'web-proxy')
+
     def test_get_region_from_zone(self):
         zone1 = self.driver.ex_get_zone('us-central1-a')
         expected_region1 = 'us-central1'
@@ -125,6 +135,18 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         names = [a.name for a in address_list_all]
         self.assertTrue('libcloud-demo-address' in names)
 
+    def test_ex_list_backendservices(self):
+        self.backendservices_mock = 'empty'
+        backendservices_list = self.driver.ex_list_backendservices()
+        self.assertListEqual(backendservices_list, [])
+
+        self.backendservices_mock = 'web-service'
+        backendservices_list = self.driver.ex_list_backendservices()
+        web_service = backendservices_list[0]
+        self.assertEqual(web_service.name, 'web-service')
+        self.assertEqual(len(web_service.healthchecks), 1)
+        self.assertEqual(len(web_service.backends), 2)
+
     def test_ex_list_healthchecks(self):
         healthchecks = self.driver.ex_list_healthchecks()
         self.assertEqual(len(healthchecks), 3)
@@ -147,6 +169,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         names = [f.name for f in forwarding_rules_all]
         self.assertTrue('lcforwardingrule' in names)
 
+    def test_ex_list_forwarding_rules_global(self):
+        forwarding_rules = self.driver.ex_list_forwarding_rules(global_rules=True)
+        self.assertEqual(len(forwarding_rules), 2)
+        self.assertEqual(forwarding_rules[0].name, 'http-rule')
+        names = [f.name for f in forwarding_rules]
+        self.assertListEqual(names, ['http-rule', 'http-rule2'])
+
     def test_list_images(self):
         local_images = self.driver.list_images()
         all_deprecated_images = self.driver.list_images(ex_include_deprecated=True)
@@ -196,6 +225,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(len(snapshots), 2)
         self.assertEqual(snapshots[0].name, 'lcsnapshot')
 
+    def test_ex_list_targethttpproxies(self):
+        target_proxies = self.driver.ex_list_targethttpproxies()
+        self.assertEqual(len(target_proxies), 2)
+        self.assertEqual(target_proxies[0].name, 'web-proxy')
+        names = [t.name for t in target_proxies]
+        self.assertListEqual(names, ['web-proxy', 'web-proxy2'])
+
     def test_ex_list_targetinstances(self):
         target_instances = self.driver.ex_list_targetinstances()
         target_instances_all = self.driver.ex_list_targetinstances('all')
@@ -248,6 +284,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertTrue('pd-standard' in names)
         self.assertTrue('local-ssd' in names)
 
+    def test_ex_list_urlmaps(self):
+        urlmaps_list = self.driver.ex_list_urlmaps()
+        web_map = urlmaps_list[0]
+        self.assertEqual(web_map.name, 'web-map')
+        self.assertEqual(len(web_map.host_rules), 0)
+        self.assertEqual(len(web_map.path_matchers), 0)
+        self.assertEqual(len(web_map.tests), 0)
+
     def test_list_volumes(self):
         volumes = self.driver.list_volumes()
         volumes_all = self.driver.list_volumes('all')
@@ -278,6 +322,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertTrue(isinstance(address, GCEAddress))
         self.assertEqual(address.name, address_name)
 
+    def test_ex_create_backendservice(self):
+        backendservice_name = 'web-service'
+        backendservice = self.driver.ex_create_backendservice(
+            name=backendservice_name,
+            healthchecks=['lchealthcheck'])
+        self.assertTrue(isinstance(backendservice, GCEBackendService))
+        self.assertEqual(backendservice.name, backendservice_name)
+
     def test_ex_create_healthcheck(self):
         healthcheck_name = 'lchealthcheck'
         kwargs = {'host': 'lchost',
@@ -317,10 +369,51 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         fwr_name = 'lcforwardingrule'
         targetpool = 'lctargetpool'
         region = 'us-central1'
+        address = 'lcaddress'
         port_range = '8000-8500'
         description = 'test forwarding rule'
         fwr = self.driver.ex_create_forwarding_rule(fwr_name, targetpool,
                                                     region=region,
+                                                    address=address,
+                                                    port_range=port_range,
+                                                    description=description)
+        self.assertTrue(isinstance(fwr, GCEForwardingRule))
+        self.assertEqual(fwr.name, fwr_name)
+        self.assertEqual(fwr.region.name, region)
+        self.assertEqual(fwr.protocol, 'TCP')
+        self.assertEqual(fwr.extra['portRange'], port_range)
+        self.assertEqual(fwr.extra['description'], description)
+
+    def test_ex_create_forwarding_rule_global(self):
+        fwr_name = 'http-rule'
+        target_name = 'web-proxy'
+        address = 'lcaddressglobal'
+        port_range = '80-80'
+        description = 'global forwarding rule'
+        for target in (target_name,
+                       self.driver.ex_get_targethttpproxy(target_name)):
+            fwr = self.driver.ex_create_forwarding_rule(fwr_name, target,
+                                                        global_rule=True,
+                                                        address=address,
+                                                        port_range=port_range,
+                                                        description=description)
+            self.assertTrue(isinstance(fwr, GCEForwardingRule))
+            self.assertEqual(fwr.name, fwr_name)
+            self.assertEqual(fwr.extra['portRange'], port_range)
+            self.assertEqual(fwr.extra['description'], description)
+
+    def test_ex_create_forwarding_rule_targetpool_keyword(self):
+        """Test backwards compatibility with the targetpool kwarg."""
+        fwr_name = 'lcforwardingrule'
+        targetpool = 'lctargetpool'
+        region = 'us-central1'
+        address = self.driver.ex_get_address('lcaddress')
+        port_range = '8000-8500'
+        description = 'test forwarding rule'
+        fwr = self.driver.ex_create_forwarding_rule(fwr_name,
+                                                    targetpool=targetpool,
+                                                    region=region,
+                                                    address=address,
                                                     port_range=port_range,
                                                     description=description)
         self.assertTrue(isinstance(fwr, GCEForwardingRule))
@@ -562,6 +655,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(nodes[0].name, '%s-000' % base_name)
         self.assertEqual(nodes[1].name, '%s-001' % base_name)
 
+    def test_ex_create_targethttpproxy(self):
+        proxy_name = 'web-proxy'
+        urlmap_name = 'web-map'
+        for urlmap in (urlmap_name, self.driver.ex_get_urlmap(urlmap_name)):
+            proxy = self.driver.ex_create_targethttpproxy(proxy_name, urlmap)
+            self.assertTrue(isinstance(proxy, GCETargetHttpProxy))
+            self.assertEqual(proxy_name, proxy.name)
+
     def test_ex_create_targetinstance(self):
         targetinstance_name = 'lctargetinstance'
         zone = 'us-central1-a'
@@ -598,6 +699,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(targetpool.extra.get('sessionAffinity'),
                          session_affinity)
 
+    def test_ex_create_urlmap(self):
+        urlmap_name = 'web-map'
+        for service in ('web-service',
+                        self.driver.ex_get_backendservice('web-service')):
+            urlmap = self.driver.ex_create_urlmap(urlmap_name, service)
+            self.assertTrue(isinstance(urlmap, GCEUrlMap))
+            self.assertEqual(urlmap_name, urlmap.name)
+
     def test_ex_create_volume_snapshot(self):
         snapshot_name = 'lcsnapshot'
         volume = self.driver.ex_get_volume('lcdisk')
@@ -738,6 +847,11 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         destroyed = address.destroy()
         self.assertTrue(destroyed)
 
+    def test_ex_destroy_backendservice(self):
+        backendservice = self.driver.ex_get_backendservice('web-service')
+        destroyed = backendservice.destroy()
+        self.assertTrue(destroyed)
+
     def test_ex_destroy_healthcheck(self):
         hc = self.driver.ex_get_healthcheck('lchealthcheck')
         destroyed = hc.destroy()
@@ -772,6 +886,11 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         destroyed = fwr.destroy()
         self.assertTrue(destroyed)
 
+    def test_ex_destroy_forwarding_rule_global(self):
+        fwr = self.driver.ex_get_forwarding_rule('http-rule', global_rule=True)
+        destroyed = fwr.destroy()
+        self.assertTrue(destroyed)
+
     def test_ex_destroy_route(self):
         route = self.driver.ex_get_route('lcdemoroute')
         destroyed = route.destroy()
@@ -795,6 +914,11 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         for d in destroyed:
             self.assertTrue(d)
 
+    def test_destroy_targethttpproxy(self):
+        proxy = self.driver.ex_get_targethttpproxy('web-proxy')
+        destroyed = proxy.destroy()
+        self.assertTrue(destroyed)
+
     def test_destroy_targetinstance(self):
         targetinstance = self.driver.ex_get_targetinstance('lctargetinstance')
         self.assertEqual(targetinstance.name, 'lctargetinstance')
@@ -806,6 +930,11 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         destroyed = targetpool.destroy()
         self.assertTrue(destroyed)
 
+    def test_destroy_urlmap(self):
+        urlmap = self.driver.ex_get_urlmap('web-map')
+        destroyed = urlmap.destroy()
+        self.assertTrue(destroyed)
+
     def test_destroy_volume(self):
         disk = self.driver.ex_get_volume('lcdisk')
         destroyed = disk.destroy()
@@ -839,6 +968,26 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(address.region.name, 'us-central1')
         self.assertEqual(address.extra['status'], 'RESERVED')
 
+    def test_ex_get_backendservice(self):
+        web_service = self.driver.ex_get_backendservice('web-service')
+        self.assertEqual(web_service.name, 'web-service')
+        self.assertEqual(web_service.protocol, 'HTTP')
+        self.assertEqual(web_service.port, 80)
+        self.assertEqual(web_service.timeout, 30)
+        self.assertEqual(web_service.healthchecks[0].name, 'basic-check')
+        self.assertEqual(len(web_service.healthchecks), 1)
+        backends = web_service.backends
+        self.assertEqual(len(backends), 2)
+        self.assertEqual(backends[0]['balancingMode'], 'RATE')
+        self.assertEqual(backends[0]['maxRate'], 100)
+        self.assertEqual(backends[0]['capacityScaler'], 1.0)
+
+        web_service = self.driver.ex_get_backendservice('no-backends')
+        self.assertEqual(web_service.name, 'web-service')
+        self.assertEqual(web_service.healthchecks[0].name, 'basic-check')
+        self.assertEqual(len(web_service.healthchecks), 1)
+        self.assertEqual(len(web_service.backends), 0)
+
     def test_ex_get_healthcheck(self):
         healthcheck_name = 'lchealthcheck'
         healthcheck = self.driver.ex_get_healthcheck(healthcheck_name)
@@ -861,6 +1010,16 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(fwr.targetpool.name, 'lctargetpool')
         self.assertEqual(fwr.protocol, 'TCP')
 
+    def test_ex_get_forwarding_rule_global(self):
+        fwr_name = 'http-rule'
+        fwr = self.driver.ex_get_forwarding_rule(fwr_name, global_rule=True)
+        self.assertEqual(fwr.name, fwr_name)
+        self.assertEqual(fwr.extra['portRange'], '80-80')
+        self.assertEqual(fwr.targetpool.name, 'web-proxy')
+        self.assertEqual(fwr.protocol, 'TCP')
+        self.assertEqual(fwr.address, '192.0.2.1')
+        self.assertEqual(fwr.targetpool.name, 'web-proxy')
+
     def test_ex_get_image_license(self):
         image = self.driver.ex_get_image('sles-12-v20141023')
         self.assertTrue('licenses' in image.extra)
@@ -1063,6 +1222,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(size.ram, 3840)
         self.assertEqual(size.extra['guestCpus'], 1)
 
+    def test_ex_get_targethttpproxy(self):
+        targethttpproxy_name = 'web-proxy'
+        targethttpproxy = self.driver.ex_get_targethttpproxy(
+            targethttpproxy_name)
+        self.assertEqual(targethttpproxy.name, targethttpproxy_name)
+        self.assertEqual(targethttpproxy.urlmap.name, 'web-map')
+
     def test_ex_get_targetinstance(self):
         targetinstance_name = 'lctargetinstance'
         targetinstance = self.driver.ex_get_targetinstance(targetinstance_name)
@@ -1083,6 +1249,12 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin):
         self.assertEqual(snapshot.size, '10')
         self.assertEqual(snapshot.status, 'READY')
 
+    def test_ex_get_urlmap(self):
+        urlmap_name = 'web-map'
+        urlmap = self.driver.ex_get_urlmap(urlmap_name)
+        self.assertEqual(urlmap.name, urlmap_name)
+        self.assertEqual(urlmap.default_service.name, 'web-service')
+
     def test_ex_get_volume(self):
         volume_name = 'lcdisk'
         volume = self.driver.ex_get_volume(volume_name)
@@ -1178,6 +1350,40 @@ class GCEMockHttp(MockHttpTestCase):
         body = self.fixtures.load('aggregated_targetPools.json')
         return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
 
+    def _global_backendServices(self, method, url, body, headers):
+        if method == 'POST':
+            body = self.fixtures.load('global_backendServices_post.json')
+        else:
+            body = self.fixtures.load('global_backendServices-%s.json' %
+                                      self.test.backendservices_mock)
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_backendServices_no_backends(self, method, url, body, headers):
+        body = self.fixtures.load('global_backendServices_no_backends.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_backendServices_web_service(self, method, url, body, headers):
+        if method == 'DELETE':
+            body = self.fixtures.load(
+                'global_backendServices_web_service_delete.json')
+        else:
+            body = self.fixtures.load('global_backendServices_web_service.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_forwardingRules(self, method, url, body, headers):
+        if method == 'POST':
+            body = self.fixtures.load('global_forwardingRules_post.json')
+        else:
+            body = self.fixtures.load('global_forwardingRules.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_forwardingRules_http_rule(self, method, url, body, headers):
+        if method == 'DELETE':
+            body = self.fixtures.load('global_forwardingRules_http_rule_delete.json')
+        else:
+            body = self.fixtures.load('global_forwardingRules_http_rule.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
     def _global_httpHealthChecks(self, method, url, body, headers):
         if method == 'POST':
             body = self.fixtures.load('global_httpHealthChecks_post.json')
@@ -1313,6 +1519,31 @@ class GCEMockHttp(MockHttpTestCase):
             'operations_operation_setCommonInstanceMetadata.json')
         return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
 
+    def _global_operations_operation_global_backendServices_post(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_backendServices_post.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_backendServices_web_service_delete(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_backendServices_web_service_delete'
+            '.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_forwardingRules_http_rule_delete(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_forwardingRules_http_rule_delete.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_forwardingRules_post(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_forwardingRules_post.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
     def _global_operations_operation_global_httpHealthChecks_lchealthcheck_delete(
             self, method, url, body, headers):
         body = self.fixtures.load(
@@ -1397,6 +1628,60 @@ class GCEMockHttp(MockHttpTestCase):
             'operations_operation_global_addresses_lcaddressglobal_delete.json')
         return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
 
+    def _global_operations_operation_global_targetHttpProxies_post(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_targetHttpProxies_post.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_targetHttpProxies_web_proxy_delete(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_targetHttpProxies_web_proxy_delete'
+            '.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_urlMaps_post(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_urlMaps_post.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_operations_operation_global_urlMaps_web_map_delete(
+            self, method, url, body, headers):
+        body = self.fixtures.load(
+            'operations_operation_global_urlMaps_web_map_delete.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_targetHttpProxies(self, method, url, body, headers):
+        if method == 'POST':
+            body = self.fixtures.load('global_targetHttpProxies_post.json')
+        else:
+            body = self.fixtures.load('global_targetHttpProxies.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_targetHttpProxies_web_proxy(self, method, url, body, headers):
+        if method == 'DELETE':
+            body = self.fixtures.load(
+                'global_targetHttpProxies_web_proxy_delete.json')
+        else:
+            body = self.fixtures.load('global_targetHttpProxies_web_proxy.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_urlMaps(self, method, url, body, headers):
+        if method == 'POST':
+            body = self.fixtures.load('global_urlMaps_post.json')
+        else:
+            body = self.fixtures.load('global_urlMaps.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
+    def _global_urlMaps_web_map(self, method, url, body, headers):
+        if method == 'DELETE':
+            body = self.fixtures.load('global_urlMaps_web_map_delete.json')
+        else:
+            body = self.fixtures.load('global_urlMaps_web_map.json')
+        return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK])
+
     def _regions_us_central1_operations_operation_regions_us_central1_addresses_lcaddress_delete(
             self, method, url, body, headers):
         body = self.fixtures.load(


Mime
View raw message