blob: 92b58dac83e98b7f6d1293077eb5fa92d39d5922 [file] [log] [blame]
# 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.
import time
from libcloud.utils.py3 import httplib
try:
import simplejson as json
except ImportError:
import json
from libcloud.utils.misc import reverse_dict
from libcloud.common.types import LibcloudError
from libcloud.common.gogrid import GoGridConnection, GoGridResponse, BaseGoGridDriver
from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
from libcloud.loadbalancer.types import State, LibcloudLBImmutableError
class GoGridLBResponse(GoGridResponse):
def success(self):
if self.status == httplib.INTERNAL_SERVER_ERROR:
# Hack, but at least this error message is more useful than
# "unexpected server error"
body = json.loads(self.body)
if (
body["method"] == "/grid/loadbalancer/add"
and len(body["list"]) >= 1
and body["list"][0]["message"].find("unexpected server error") != -1
):
raise LibcloudError(
value="You mostly likely tried to add a member with an IP"
" address not assigned to your account",
driver=self,
)
return super(GoGridLBResponse, self).success()
class GoGridLBConnection(GoGridConnection):
"""
Connection class for the GoGrid load-balancer driver.
"""
responseCls = GoGridLBResponse
class GoGridLBDriver(BaseGoGridDriver, Driver):
connectionCls = GoGridLBConnection
api_name = "gogrid_lb"
name = "GoGrid LB"
website = "http://www.gogrid.com/"
LB_STATE_MAP = {"On": State.RUNNING, "Unknown": State.UNKNOWN}
_VALUE_TO_ALGORITHM_MAP = {
"round robin": Algorithm.ROUND_ROBIN,
"least connect": Algorithm.LEAST_CONNECTIONS,
}
_ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
def __init__(self, *args, **kwargs):
"""
@inherits: :class:`Driver.__init__`
"""
super(GoGridLBDriver, self).__init__(*args, **kwargs)
def list_protocols(self):
# GoGrid only supports http
return ["http"]
def list_balancers(self):
return self._to_balancers(
self.connection.request("/api/grid/loadbalancer/list").object
)
def ex_create_balancer_nowait(
self, name, members, protocol="http", port=80, algorithm=DEFAULT_ALGORITHM
):
"""
@inherits: :class:`Driver.create_balancer`
"""
algorithm = self._algorithm_to_value(algorithm)
params = {
"name": name,
"loadbalancer.type": algorithm,
"virtualip.ip": self._get_first_ip(),
"virtualip.port": port,
}
params.update(self._members_to_params(members))
resp = self.connection.request(
"/api/grid/loadbalancer/add", method="GET", params=params
)
return self._to_balancers(resp.object)[0]
def create_balancer(
self, name, members, protocol="http", port=80, algorithm=DEFAULT_ALGORITHM
):
balancer = self.ex_create_balancer_nowait(
name, members, protocol, port, algorithm
)
timeout = 60 * 20
waittime = 0
interval = 2 * 15
if balancer.id is not None:
return balancer
else:
while waittime < timeout:
balancers = self.list_balancers()
for i in balancers:
if i.name == balancer.name and i.id is not None:
return i
waittime += interval
time.sleep(interval)
raise Exception("Failed to get id")
def destroy_balancer(self, balancer):
try:
resp = self.connection.request(
"/api/grid/loadbalancer/delete",
method="POST",
params={"id": balancer.id},
)
except Exception as e:
if "Update request for LoadBalancer" in str(e):
raise LibcloudLBImmutableError(
"Cannot delete immutable object", GoGridLBDriver
)
else:
raise
return resp.status == 200
def get_balancer(self, **kwargs):
params = {}
try:
params["name"] = kwargs["ex_balancer_name"]
except KeyError:
balancer_id = kwargs["balancer_id"]
params["id"] = balancer_id
resp = self.connection.request("/api/grid/loadbalancer/get", params=params)
return self._to_balancers(resp.object)[0]
def balancer_attach_member(self, balancer, member):
members = self.balancer_list_members(balancer)
members.append(member)
params = {"id": balancer.id}
params.update(self._members_to_params(members))
resp = self._update_balancer(params)
return [
m
for m in self._to_members(resp.object["list"][0]["realiplist"], balancer)
if m.ip == member.ip
][0]
def balancer_detach_member(self, balancer, member):
members = self.balancer_list_members(balancer)
remaining_members = [n for n in members if n.id != member.id]
params = {"id": balancer.id}
params.update(self._members_to_params(remaining_members))
resp = self._update_balancer(params)
return resp.status == 200
def balancer_list_members(self, balancer):
resp = self.connection.request(
"/api/grid/loadbalancer/get", params={"id": balancer.id}
)
return self._to_members(resp.object["list"][0]["realiplist"], balancer)
def _update_balancer(self, params):
try:
return self.connection.request(
"/api/grid/loadbalancer/edit", method="POST", params=params
)
except Exception as e:
if "Update already pending" in str(e):
raise LibcloudLBImmutableError("Balancer is immutable", GoGridLBDriver)
raise LibcloudError(value="Exception: %s" % str(e), driver=self)
def _members_to_params(self, members):
"""
Helper method to convert list of :class:`Member` objects
to GET params.
"""
params = {}
i = 0
for member in members:
params["realiplist.%s.ip" % i] = member.ip
params["realiplist.%s.port" % i] = member.port
i += 1
return params
def _to_balancers(self, object):
return [self._to_balancer(el) for el in object["list"]]
def _to_balancer(self, el):
lb = LoadBalancer(
id=el.get("id"),
name=el["name"],
state=self.LB_STATE_MAP.get(el["state"]["name"], State.UNKNOWN),
ip=el["virtualip"]["ip"]["ip"],
port=el["virtualip"]["port"],
driver=self.connection.driver,
)
return lb
def _to_members(self, object, balancer=None):
return [self._to_member(el, balancer) for el in object]
def _to_member(self, el, balancer=None):
member = Member(
id=el["ip"]["id"], ip=el["ip"]["ip"], port=el["port"], balancer=balancer
)
return member