| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """ |
| Rackspace driver |
| """ |
| from libcloud.compute.base import NodeLocation, VolumeSnapshot |
| from libcloud.compute.types import Provider, LibcloudError, VolumeSnapshotState |
| from libcloud.utils.iso8601 import parse_date |
| from libcloud.common.rackspace import AUTH_URL |
| from libcloud.compute.drivers.openstack import ( |
| OpenStack_1_0_Response, |
| OpenStack_1_0_Connection, |
| OpenStack_1_0_NodeDriver, |
| OpenStack_1_1_Connection, |
| OpenStack_1_1_NodeDriver, |
| ) |
| |
| SERVICE_TYPE = "compute" |
| SERVICE_NAME_GEN1 = "cloudServers" |
| SERVICE_NAME_GEN2 = "cloudServersOpenStack" |
| ENDPOINT_ARGS_MAP = { |
| "dfw": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "DFW"}, |
| "ord": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "ORD"}, |
| "iad": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "IAD"}, |
| "lon": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "LON"}, |
| "syd": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "SYD"}, |
| "hkg": {"service_type": SERVICE_TYPE, "name": SERVICE_NAME_GEN2, "region": "HKG"}, |
| } |
| |
| |
| class RackspaceFirstGenConnection(OpenStack_1_0_Connection): |
| """ |
| Connection class for the Rackspace first-gen driver. |
| """ |
| |
| responseCls = OpenStack_1_0_Response |
| XML_NAMESPACE = "http://docs.rackspacecloud.com/servers/api/v1.0" |
| auth_url = AUTH_URL |
| _auth_version = "2.0" |
| cache_busting = True |
| |
| def __init__(self, *args, **kwargs): |
| self.region = kwargs.pop("region", None) |
| super().__init__(*args, **kwargs) |
| |
| def get_endpoint(self): |
| if "2.0" in self._auth_version: |
| ep = self.service_catalog.get_endpoint( |
| service_type=SERVICE_TYPE, name=SERVICE_NAME_GEN1 |
| ) |
| else: |
| raise LibcloudError('Auth version "%s" not supported' % (self._auth_version)) |
| |
| public_url = ep.url |
| |
| if not public_url: |
| raise LibcloudError("Could not find specified endpoint") |
| |
| # This is a nasty hack, but it's required because of how the |
| # auth system works. |
| # Old US accounts can access UK API endpoint, but they don't |
| # have this endpoint in the service catalog. Same goes for the |
| # old UK accounts and US endpoint. |
| if self.region == "us": |
| # Old UK account, which only have uk endpoint in the catalog |
| public_url = public_url.replace("https://lon.servers.api", "https://servers.api") |
| elif self.region == "uk": |
| # Old US account, which only has us endpoints in the catalog |
| public_url = public_url.replace("https://servers.api", "https://lon.servers.api") |
| |
| return public_url |
| |
| def get_service_name(self): |
| return SERVICE_NAME_GEN1 |
| |
| |
| class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): |
| name = "Rackspace Cloud (First Gen)" |
| website = "http://www.rackspace.com" |
| connectionCls = RackspaceFirstGenConnection |
| type = Provider.RACKSPACE_FIRST_GEN |
| api_name = "rackspace" |
| |
| def __init__(self, key, secret=None, secure=True, host=None, port=None, region="us", **kwargs): |
| """ |
| @inherits: :class:`NodeDriver.__init__` |
| |
| :param region: Region ID which should be used |
| :type region: ``str`` |
| """ |
| if region not in ["us", "uk"]: |
| raise ValueError("Invalid region: %s" % (region)) |
| |
| super().__init__( |
| key=key, |
| secret=secret, |
| secure=secure, |
| host=host, |
| port=port, |
| region=region, |
| **kwargs, |
| ) |
| |
| def list_locations(self): |
| """ |
| Lists available locations |
| |
| Locations cannot be set or retrieved via the API, but currently |
| there are two locations, DFW and ORD. |
| |
| @inherits: :class:`OpenStack_1_0_NodeDriver.list_locations` |
| """ |
| if self.region == "us": |
| locations = [NodeLocation(0, "Rackspace DFW1/ORD1", "US", self)] |
| elif self.region == "uk": |
| locations = [NodeLocation(0, "Rackspace UK London", "UK", self)] |
| |
| return locations |
| |
| def _ex_connection_class_kwargs(self): |
| kwargs = self.openstack_connection_kwargs() |
| kwargs["region"] = self.region |
| return kwargs |
| |
| |
| class RackspaceConnection(OpenStack_1_1_Connection): |
| """ |
| Connection class for the Rackspace next-gen OpenStack base driver. |
| """ |
| |
| auth_url = AUTH_URL |
| _auth_version = "2.0" |
| |
| def __init__(self, *args, **kwargs): |
| self.region = kwargs.pop("region", None) |
| self.get_endpoint_args = kwargs.pop("get_endpoint_args", None) |
| super().__init__(*args, **kwargs) |
| |
| def get_service_name(self): |
| if not self.get_endpoint_args: |
| # if they used ex_force_base_url, assume the Rackspace default |
| return SERVICE_NAME_GEN2 |
| |
| return self.get_endpoint_args.get("name", SERVICE_NAME_GEN2) |
| |
| def get_endpoint(self): |
| if not self.get_endpoint_args: |
| raise LibcloudError("RackspaceConnection must have get_endpoint_args set") |
| |
| if "2.0" in self._auth_version: |
| ep = self.service_catalog.get_endpoint(**self.get_endpoint_args) |
| else: |
| raise LibcloudError('Auth version "%s" not supported' % (self._auth_version)) |
| |
| public_url = ep.url |
| |
| if not public_url: |
| raise LibcloudError("Could not find specified endpoint") |
| |
| return public_url |
| |
| |
| class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): |
| name = "Rackspace Cloud (Next Gen)" |
| website = "http://www.rackspace.com" |
| connectionCls = RackspaceConnection |
| type = Provider.RACKSPACE |
| |
| _networks_url_prefix = "/os-networksv2" |
| |
| def __init__( |
| self, |
| key, |
| secret=None, |
| secure=True, |
| host=None, |
| port=None, |
| region="dfw", |
| **kwargs, |
| ): |
| """ |
| @inherits: :class:`NodeDriver.__init__` |
| |
| :param region: ID of the region which should be used. |
| :type region: ``str`` |
| """ |
| valid_regions = ENDPOINT_ARGS_MAP.keys() |
| |
| if region not in valid_regions: |
| raise ValueError("Invalid region: %s" % (region)) |
| |
| if region == "lon": |
| self.api_name = "rackspacenovalon" |
| elif region == "syd": |
| self.api_name = "rackspacenovasyd" |
| else: |
| self.api_name = "rackspacenovaus" |
| |
| super().__init__( |
| key=key, |
| secret=secret, |
| secure=secure, |
| host=host, |
| port=port, |
| region=region, |
| **kwargs, |
| ) |
| |
| def _to_snapshot(self, api_node): |
| if "snapshot" in api_node: |
| api_node = api_node["snapshot"] |
| |
| extra = { |
| "volume_id": api_node["volumeId"], |
| "name": api_node["displayName"], |
| "created": api_node["createdAt"], |
| "description": api_node["displayDescription"], |
| "status": api_node["status"], |
| } |
| |
| state = self.SNAPSHOT_STATE_MAP.get(api_node["status"], VolumeSnapshotState.UNKNOWN) |
| |
| try: |
| created_td = parse_date(api_node["createdAt"]) |
| except ValueError: |
| created_td = None |
| |
| snapshot = VolumeSnapshot( |
| id=api_node["id"], |
| driver=self, |
| size=api_node["size"], |
| extra=extra, |
| created=created_td, |
| state=state, |
| name=api_node["displayName"], |
| ) |
| return snapshot |
| |
| def _ex_connection_class_kwargs(self): |
| endpoint_args = ENDPOINT_ARGS_MAP[self.region] |
| kwargs = self.openstack_connection_kwargs() |
| kwargs["region"] = self.region |
| kwargs["get_endpoint_args"] = endpoint_args |
| return kwargs |