diff options
Diffstat (limited to 'relationships/relationshipplugin')
-rw-r--r-- | relationships/relationshipplugin/__init__.py | 23 | ||||
-rw-r--r-- | relationships/relationshipplugin/discovery.py | 84 | ||||
-rw-r--r-- | relationships/relationshipplugin/tasks.py | 131 |
3 files changed, 238 insertions, 0 deletions
diff --git a/relationships/relationshipplugin/__init__.py b/relationships/relationshipplugin/__init__.py new file mode 100644 index 0000000..259e52c --- /dev/null +++ b/relationships/relationshipplugin/__init__.py @@ -0,0 +1,23 @@ +# ============LICENSE_START======================================================= +# org.onap.dcae +# ================================================================================ +# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# ================================================================================ +# Licensed 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. +# ============LICENSE_END========================================================= +# +# ECOMP is a trademark and service mark of AT&T Intellectual Property. + + +from .tasks import add_relationship, remove_relationship, \ + forward_destination_info diff --git a/relationships/relationshipplugin/discovery.py b/relationships/relationshipplugin/discovery.py new file mode 100644 index 0000000..bd0e369 --- /dev/null +++ b/relationships/relationshipplugin/discovery.py @@ -0,0 +1,84 @@ +# ============LICENSE_START======================================================= +# org.onap.dcae +# ================================================================================ +# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# ================================================================================ +# Licensed 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. +# ============LICENSE_END========================================================= +# +# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +from urlparse import urlparse +import json +import consul + + +class DiscoveryError(RuntimeError): + pass + +def _create_rel_key(service_component_name): + return "{0}:rel".format(service_component_name) + +def _parse_host(host): + """Parse host string + + Returns: + Tuple of the hostname and port to use to connect to Consul + """ + def parse_urlparse_result(pr): + if not pr.hostname: + raise DiscoveryError("Invalid Consul host provided: {0}".format(host)) + + try: + # Port 8500 is the Consul default + return (pr.hostname, pr.port if pr.port else 8500) + except ValueError as e: + # Something bad happened with port + raise DiscoveryError("Invalid Consul host provided: {0}".format(host)) + + pr = urlparse(host) + + # urlparse requires scheme to be set in order to be useful + if pr.scheme and pr.netloc: + return parse_urlparse_result(pr) + else: + return parse_urlparse_result(urlparse("http://{0}".format(host))) + + +def create_kv_conn(host): + """Create connection to key-value store + + Returns a Consul client to the specified Consul host + """ + (hostname, port) = _parse_host(host) + return consul.Consul(host=hostname, port=port) + +def store_relationship(kv_conn, source_name, target_name): + # TODO: Rel entry may already exist in a one-to-many situation. Need to + # support that. + rel_key = _create_rel_key(source_name) + rel_value = [target_name] if target_name else [] + + kv_conn.kv.put(rel_key, json.dumps(rel_value)) + print("Added relationship for {0}".format(rel_key)) + +def delete_relationship(kv_conn, service_component_name): + rel_key = _create_rel_key(service_component_name) + index, rels = kv_conn.kv.get(rel_key) + + if rels: + rels = json.loads(rels["Value"].decode("utf-8")) + kv_conn.kv.delete(rel_key) + return rels + else: + return [] diff --git a/relationships/relationshipplugin/tasks.py b/relationships/relationshipplugin/tasks.py new file mode 100644 index 0000000..b17403c --- /dev/null +++ b/relationships/relationshipplugin/tasks.py @@ -0,0 +1,131 @@ +# ============LICENSE_START======================================================= +# org.onap.dcae +# ================================================================================ +# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# ================================================================================ +# Licensed 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. +# ============LICENSE_END========================================================= +# +# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +import json +from cloudify import ctx +from cloudify.decorators import operation +from cloudify.exceptions import NonRecoverableError +from relationshipplugin import discovery as dis +import requests + + +SERVICE_COMPONENT_NAME = "service_component_name" +SELECTED_CONTAINER_DESTINATION = "selected_container_destination" +CONSUL_HOST = "consul_host" + +CONSUL_HOSTNAME = "localhost" + + +# Lifecycle interface calls for component_connect_to + +# NOTE: ctx.source and ctx.target are RelationshipSubjectContext +# Order of operation of relationships is bit confusing. These operations are +# implemented for `target_interfaces`. By observation, the target node processed, +# then the source is created, the relationship is run then the source is started. +# http://getcloudify.org/guide/3.1/dsl-spec-relationships.html#relationship-interfaces + +@operation +def add_relationship(**kwargs): + """Adds target to the source relationship list""" + try: + conn = dis.create_kv_conn(CONSUL_HOSTNAME) + + source_name = ctx.source.instance.runtime_properties[SERVICE_COMPONENT_NAME] + # The use case for using the target name override is for the platform + # blueprint where the cdap broker needs to connect to a cdap cluster but + # the cdap cluster does not not use the component plugins so the name is + # not generated. + # REVIEW: Re-review this + target_name = kwargs["target_name_override"] \ + if "target_name_override" in kwargs \ + else ctx.target.instance.runtime_properties[SERVICE_COMPONENT_NAME] + + dis.store_relationship(conn, source_name, target_name) + ctx.logger.info("Created relationship: {0} to {1}".format(source_name, + target_name)) + except Exception as e: + ctx.logger.error("Unexpected error while adding relationship: {0}" + .format(str(e))) + raise NonRecoverableError(e) + +@operation +def remove_relationship(**kwargs): + """Removes target from the source relationship list""" + try: + conn = dis.create_kv_conn(CONSUL_HOSTNAME) + + source_name = ctx.source.instance.runtime_properties[SERVICE_COMPONENT_NAME] + dis.delete_relationship(conn, source_name) + ctx.logger.info("Removed relationship: {0}".format(source_name)) + except Exception as e: + ctx.logger.error("Unexpected error while removing relationship: {0}" + .format(str(e))) + raise NonRecoverableError(e) + + +# Lifecycle interface calls for component_contained_in + +@operation +def forward_destination_info(**kwargs): + try: + selected_target = ctx.target.instance.runtime_properties[SERVICE_COMPONENT_NAME] + ctx.source.instance.runtime_properties[SELECTED_CONTAINER_DESTINATION] = selected_target + ctx.logger.info("Forwarding selected target: {0}".format(ctx.source.instance.id)) + except Exception as e: + ctx.logger.error("Unexpected error while forwarding selected target: {0}" + .format(str(e))) + raise NonRecoverableError(e) + +@operation +def registered_to(**kwargs): + """ + Intended to be used in platform blueprints, but possible to be reused elsewhere + """ + ctx.logger.info(str(kwargs)) + address = kwargs["address_to_register"] + name = kwargs["name_to_register"] + port = kwargs["port_to_register"] + + (consul_host, consul_port) = (CONSUL_HOSTNAME, 8500) + #Storing in source because that's who is getting registered + ctx.source.instance.runtime_properties[CONSUL_HOST] = "http://{0}:{1}".format(consul_host, consul_port) + ctx.source.instance.runtime_properties["name_to_register"] = name #careful! delete does not have access to inputs + + try: + response = requests.put(url = "{0}/v1/agent/service/register".format(ctx.source.instance.runtime_properties[CONSUL_HOST]), + json = { + "name" : name, + "Address" : address, + "Port" : int(port) + }, + headers={'Content-Type': 'application/json'}) + response.raise_for_status() #bomb if not 2xx + except Exception as e: + ctx.logger.error("Error while registering: {0}".format(str(e))) + raise NonRecoverableError(e) + +@operation +def registered_to_delete(**kwargs): + """ + The deletion/opposite of registered_to + """ + requests.put(url = "{0}/v1/agent/service/deregister/{1}".format(ctx.source.instance.runtime_properties[CONSUL_HOST], ctx.source.instance.runtime_properties["name_to_register"]), + headers={'Content-Type': 'Content-Type: application/json'}) + #this is on delete so do not do any checking |