Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@
"cloudfeeds": {
"service": "cloudFeeds",
"tenant_id": "identity_admin_tenant",
"url": "https://cfurl.example.net/not/in/service/catalog"
},
"terminator": {
"interval": 300,
"tenant_id": "identity_admin_tenant",
"cf_cap_url": "https://cfurl.example.net/not/in/service/catalog"
"url": "http://cfurl.net/not/in/service/catalog"
},
"converger": {
"build_timeout": 3600,
Expand Down
4 changes: 2 additions & 2 deletions otter/cloud_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def concretize_service_request(
log = service_request.log

service_config = service_configs[service_request.service_type]
region = service_config['region']
service_name = service_config['name']

def got_auth((token, catalog)):
request_ = add_headers(otter_headers(token), request)
Expand All @@ -191,8 +193,6 @@ def got_auth((token, catalog)):
if 'url' in service_config:
request_ = add_bind_root(service_config['url'], request_)
else:
region = service_config['region']
service_name = service_config['name']
request_ = add_bind_service(
catalog, service_name, region, log, request_)
request_ = add_error_handling(
Expand Down
55 changes: 43 additions & 12 deletions otter/cloud_client/clb.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
from functools import partial
from operator import itemgetter
from urlparse import parse_qs, urlparse

import attr

from characteristic import Attribute, attributes

from effect import catch, raise_
from effect.do import do, do_return

import six

from toolz.functoolz import identity
from toolz.itertoolz import concat

from otter.cloud_client import cloudfeeds as cf
from otter.cloud_client import (
log_success_response,
match_errors,
only_json_api_errors,
regex,
service_request)
from otter.constants import ServiceType
from otter.indexer import atom
from otter.util.http import APIError, append_segments, try_json_with_keys
from otter.util.pure_http import has_code

Expand Down Expand Up @@ -368,27 +370,56 @@ def get_clbs():
success=lambda (response, body): body['loadBalancers'])


def get_clb_node_feed(lb_id, node_id):
def _node_feed_page(lb_id, node_id, params):
"""
Get the atom feed associated with a CLB node.
Return page of CLB node feed

:param int lb_id: Cloud Load balancer ID
:param int node_id: Node ID of in loadbalancer node
:param dict params: Request query parameters

:returns: Effect of ``list`` of atom entry :class:`Element`
:rtype: ``Effect``
:returns: Unparsed response body as string
:rtype: `str`
"""
return cf.read_entries(
ServiceType.CLOUD_LOAD_BALANCERS,
return service_request(
ServiceType.CLOUD_LOAD_BALANCERS, 'GET',
append_segments('loadbalancers', str(lb_id), 'nodes',
'{}.atom'.format(node_id)),
{},
cf.Direction.NEXT,
"request-get-clb-node-feed"
).on(itemgetter(0)).on(
params=params,
json_response=False
).on(
error=only_json_api_errors(
lambda c, b: _process_clb_api_error(c, b, lb_id))
)
).on(
log_success_response('request-get-clb-node-feed', identity)
).on(itemgetter(1))


@do
def get_clb_node_feed(lb_id, node_id):
"""
Get the atom feed associated with a CLB node.

:param int lb_id: Cloud Load balancer ID
:param int node_id: Node ID of in loadbalancer node

:returns: Effect of ``list`` of atom entry :class:`Element`
:rtype: ``Effect``
"""
all_entries = []
params = {}
while True:
feed_str = yield _node_feed_page(lb_id, node_id, params)
feed = atom.parse(feed_str)
entries = atom.entries(feed)
if entries == []:
break
all_entries.extend(entries)
next_link = atom.next_link(feed)
if not next_link:
break
params = parse_qs(urlparse(next_link).query)
yield do_return(all_entries)


def get_clb_health_monitor(lb_id):
Expand Down
66 changes: 1 addition & 65 deletions otter/cloud_client/cloudfeeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,8 @@
Cloud feeds related APIs
"""

from urlparse import parse_qs, urlparse

from effect.do import do, do_return

from toolz.functoolz import identity

from twisted.python.constants import NamedConstant, Names

from otter.cloud_client import log_success_response, service_request
from otter.cloud_client import service_request
from otter.constants import ServiceType
from otter.indexer import atom
from otter.util.http import append_segments
from otter.util.pure_http import has_code

Expand All @@ -31,58 +22,3 @@ def publish_autoscale_event(event, log=None):
'content-type': ['application/vnd.rackspace.atom+json']},
data=event, log=log, success_pred=has_code(201),
json_response=False)


class Direction(Names):
"""
Which direction to follow the feeds?
"""
PREVIOUS = NamedConstant()
NEXT = NamedConstant()


@do
def read_entries(service_type, url, params, direction, follow_limit=100,
log_msg_type=None):
"""
Read all feed entries and follow in given direction until it is empty

:param service_type: Service hosting the feed
:type service_type: A member of :class:`ServiceType`
:param str url: CF URL to append
:param dict params: HTTP parameters
:param direction: Where to continue fetching?
:type direction: A member of :class:`Direction`
:param int follow_limit: Maximum number of times to follow in given
direction

:return: (``list`` of :obj:`Element`, last fetched params) tuple
"""
if direction == Direction.PREVIOUS:
direction_link = atom.previous_link
elif direction == Direction.NEXT:
direction_link = atom.next_link
else:
raise ValueError("Invalid direction")

if log_msg_type is not None:
log_cb = log_success_response(log_msg_type, identity, False)
else:
log_cb = identity

all_entries = []
while follow_limit > 0:
resp, feed_str = yield service_request(
service_type, "GET", url, params=params,
json_response=False).on(log_cb)
feed = atom.parse(feed_str)
entries = atom.entries(feed)
if entries == []:
break
all_entries.extend(entries)
link = direction_link(feed)
if link is None:
break
params = parse_qs(urlparse(link).query)
follow_limit -= 1
yield do_return((all_entries, params))
31 changes: 8 additions & 23 deletions otter/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@


class ServiceType(Names):
"""
Constants representing Rackspace cloud services.

Note: CLOUD_FEEDS_CAP represents customer access policy events which like
CLOUD_FEEDS is fixed URL but different from CLOUD_FEEDS.
"""
"""Constants representing Rackspace cloud services."""
CLOUD_SERVERS = NamedConstant()
CLOUD_LOAD_BALANCERS = NamedConstant()
RACKCONNECT_V3 = NamedConstant()
CLOUD_METRICS_INGEST = NamedConstant()
CLOUD_FEEDS = NamedConstant()
CLOUD_FEEDS_CAP = NamedConstant()
CLOUD_ORCHESTRATION = NamedConstant()


Expand All @@ -34,23 +28,22 @@ def get_service_configs(config):
:param dict config: Config from file containing service names that will be
there in service catalog
"""
region = config['region']
configs = {
ServiceType.CLOUD_SERVERS: {
'name': config['cloudServersOpenStack'],
'region': region,
'region': config['region'],
},
ServiceType.CLOUD_LOAD_BALANCERS: {
'name': config['cloudLoadBalancers'],
'region': region,
'region': config['region'],
},
ServiceType.CLOUD_ORCHESTRATION: {
'name': config['cloudOrchestration'],
'region': region,
'region': config['region'],
},
ServiceType.RACKCONNECT_V3: {
'name': config['rackconnect'],
'region': region,
'region': config['region'],
}
}

Expand All @@ -59,18 +52,10 @@ def get_service_configs(config):
configs[ServiceType.CLOUD_METRICS_INGEST] = {
'name': metrics['service'], 'region': metrics['region']}

# {"cloudfeeds": {"url": "https://url"}} config is for service which
# is used to push scaling group events: `otter.log.cloudfeeds`.
cf = config.get('cloudfeeds')
if cf is not None:
configs[ServiceType.CLOUD_FEEDS] = {'url': cf['url']}

# "terminator" contains config to setup terminator service and
# cloud feed client to access customer access policy events. This and
# above cloudfeeds service have fixed URL as opposed to service being
# there in service catalog
term = config.get('terminator')
if term is not None:
configs[ServiceType.CLOUD_FEEDS_CAP] = {'url': term['cf_cap_url']}
configs[ServiceType.CLOUD_FEEDS] = {
'name': cf['service'], 'region': config['region'],
'url': cf['url']}

return configs
Loading