237 lines
8.2 KiB
Python
237 lines
8.2 KiB
Python
# 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
|