From bf404cac2cefcc99df9663ddf02419fe0e402ff5 Mon Sep 17 00:00:00 2001 From: coolyuvee Date: Thu, 9 Aug 2018 19:19:26 +0530 Subject: [PATCH 1/6] [docstring] Create docstring for CDN transport layer --- poppy/transport/pecan/controllers/v1/admin.py | 220 ++++++++++++++---- .../transport/pecan/controllers/v1/flavors.py | 33 +-- .../transport/pecan/controllers/v1/health.py | 47 ++-- poppy/transport/pecan/controllers/v1/home.py | 5 + poppy/transport/pecan/controllers/v1/ping.py | 4 + .../pecan/controllers/v1/services.py | 51 +++- .../pecan/controllers/v1/ssl_certificates.py | 22 +- poppy/transport/pecan/hooks/context.py | 6 + poppy/transport/pecan/hooks/error.py | 7 +- .../pecan/models/request/cachingrule.py | 11 + .../transport/pecan/models/request/domain.py | 11 + .../transport/pecan/models/request/flavor.py | 12 + .../pecan/models/request/log_delivery.py | 12 + .../transport/pecan/models/request/origin.py | 11 + .../pecan/models/request/provider_details.py | 13 ++ .../pecan/models/request/restriction.py | 12 + poppy/transport/pecan/models/request/rule.py | 12 + .../transport/pecan/models/request/service.py | 12 + .../pecan/models/request/ssl_certificate.py | 12 + .../pecan/models/response/cachingrules.py | 11 +- .../transport/pecan/models/response/domain.py | 13 +- .../transport/pecan/models/response/flavor.py | 10 + .../transport/pecan/models/response/health.py | 34 +++ poppy/transport/pecan/models/response/link.py | 9 +- .../pecan/models/response/log_delivery.py | 12 +- .../transport/pecan/models/response/origin.py | 13 +- .../pecan/models/response/restriction.py | 13 +- poppy/transport/pecan/models/response/rule.py | 13 +- .../pecan/models/response/service.py | 12 +- .../pecan/models/response/ssl_certificate.py | 12 +- 30 files changed, 573 insertions(+), 92 deletions(-) diff --git a/poppy/transport/pecan/controllers/v1/admin.py b/poppy/transport/pecan/controllers/v1/admin.py index cb94cc17..4add603a 100644 --- a/poppy/transport/pecan/controllers/v1/admin.py +++ b/poppy/transport/pecan/controllers/v1/admin.py @@ -50,6 +50,11 @@ def __init__(self, driver): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): + """ + Update the certificate status for the provider belonging to service and domain + :return: Pecan response with http status 202 or 404 + :rtype: pecan.Response + """ request_json = json.loads(pecan.request.body.decode('utf-8')) project_id = request_json.get('project_id', None) service_id = request_json.get('service_id', None) @@ -97,6 +102,14 @@ def __init__(self, driver): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def patch_one(self, service_id): + """ + + Update the access urls for service + :type service_id: str + :param service_id: + :return: Pecan response with http status code 201 or 202 400 or 404 + :rtype: pecan.Response + """ request_json = json.loads(pecan.request.body.decode('utf-8')) project_id = request_json.get('project_id', None) domain_name = request_json.get('domain_name', None) @@ -109,16 +122,16 @@ def patch_one(self, service_id): changes_made = False try: - changes_made = self._driver.manager.services_controller.\ + changes_made = self._driver.manager.services_controller. \ update_access_url_service( - project_id, - service_id, - access_url_changes={ - 'domain_name': domain_name, - 'operator_url': operator_url, - 'provider_url': provider_url - } - ) + project_id, + service_id, + access_url_changes={ + 'domain_name': domain_name, + 'operator_url': operator_url, + 'provider_url': provider_url + } + ) except errors.ServiceNotFound: pecan.abort(404, detail='Service {0} could not be found'.format( service_id)) @@ -148,6 +161,11 @@ def __init__(self, driver): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): + """ + Post a new background job + :return: Pecan response with json and http status 202 or 404 + :rtype: pecan.Response + """ request_json = json.loads(pecan.request.body.decode('utf-8')) job_type = request_json.pop('job_type') @@ -155,7 +173,7 @@ def post(self): ignored = [] try: - sent, ignored = self._driver.manager.background_job_controller.\ + sent, ignored = self._driver.manager.background_job_controller. \ post_job(job_type, request_json) except NotImplementedError as e: pecan.abort(400, str(e)) @@ -179,6 +197,12 @@ def __init__(self, driver): @pecan.expose('json') def get_all(self): + """ + + Get the list of SAN mapping + :return: A list of SAN mappings + :rtype: list + """ try: return ( self.manager.background_job_controller.get_san_mapping_list()) @@ -194,7 +218,10 @@ def get_all(self): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def put(self): - """The input of the queue data must be a list of dictionaries: + """ + Put the items in the SAN mapping list for the processing + + The input of the queue data must be a list of dictionaries: (after json loaded) [ @@ -202,14 +229,17 @@ def put(self): "san_cert_name": } ] + + :return Dictionary consisting of the new queue and deleted items + :rtype : dict """ try: san_mapping_list = json.loads(pecan.request.body.decode('utf-8')) res, deleted = ( self.manager.background_job_controller. - put_san_mapping_list(san_mapping_list)) + put_san_mapping_list(san_mapping_list)) # queue is the new queue, and deleted is deleted items - return {"queue": res, "deleted": deleted} + return {"queue": res, "deleted": deleted} except Exception as e: pecan.abort(400, str(e)) @@ -222,11 +252,17 @@ def __init__(self, driver): @pecan.expose('json') def get_all(self): + """ + + Get the mod-san Re-try list + :return: List of items in mod-san retry + :rtype: list + """ retry_list = None try: retry_list = ( self._driver.manager.ssl_certificate_controller. - get_san_retry_list()) + get_san_retry_list()) except Exception as e: pecan.abort(404, str(e)) @@ -234,10 +270,14 @@ def get_all(self): @pecan.expose('json') def post(self): - """Rerun retry-list mod-san requests.""" + """ + Rerun retry-list mod-san requests. + :return: Pecan response with JSON and http status code 202 or 404 + :rtype: pecan.Response + """ sent, ignored = None, None try: - sent, ignored = self._driver.manager.ssl_certificate_controller.\ + sent, ignored = self._driver.manager.ssl_certificate_controller. \ rerun_san_retry_list() except Exception as e: pecan.abort(404, str(e)) @@ -256,7 +296,9 @@ def post(self): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def put(self): - """The input of the queue data must be a list of dictionaries: + """ + Update the mod-san retry list + The input of the queue data must be a list of dictionaries: (after json loaded) [ @@ -265,14 +307,16 @@ def put(self): "flavor_id": } ] + :return: Dictionary consisting of new queue and deleted items + :rtype: dict """ try: queue_data = json.loads(pecan.request.body.decode('utf-8')) res, deleted = ( self._driver.manager.ssl_certificate_controller. - update_san_retry_list(queue_data)) + update_san_retry_list(queue_data)) # queue is the new queue, and deleted is deleted items - return {"queue": res, "deleted": deleted} + return {"queue": res, "deleted": deleted} except Exception as e: pecan.abort(400, str(e)) @@ -286,12 +330,20 @@ class AkamaiSanCertConfigController(base.Controller, hooks.HookController): helpers.is_valid_domain_by_name_or_akamai_setting(), helpers.abort_with_message)) def get_one(self, query): - + """ + Get SAN certificate hostname limit or the configuration depending on the query + :type query: str + :param query: Use value 'san_cert_hostname_limit' to get the hostname limit, + else configuration will be returned + :return: Dictionary with hostname limit or configuration if success, + else Pecan response with http status code 400 + :rtype: dict + """ if query == 'san_cert_hostname_limit': try: return ( self._driver.manager.ssl_certificate_controller. - get_san_cert_hostname_limit() + get_san_cert_hostname_limit() ) except Exception as e: pecan.abort(400, str(e)) @@ -299,7 +351,7 @@ def get_one(self, query): try: return ( self._driver.manager.ssl_certificate_controller. - get_san_cert_configuration(query) + get_san_cert_configuration(query) ) except Exception as e: pecan.abort(400, str(e)) @@ -316,6 +368,13 @@ def get_one(self, query): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self, query): + """ + Set the hostname limit or configuration for the SAN certificate depending on the query + :type query: str + :param query: Use 'san_cert_hostname_limit' to set the hostname limit + :return: Pecan response with http status 202 or 400 + :rtype: pecan.Response + """ request_json = json.loads(pecan.request.body.decode('utf-8')) if query == 'san_cert_hostname_limit': @@ -330,7 +389,7 @@ def post(self, query): try: res = ( self._driver.manager.ssl_certificate_controller. - update_san_cert_configuration(query, request_json)) + update_san_cert_configuration(query, request_json)) return res except Exception as e: pecan.abort(400, str(e)) @@ -344,11 +403,19 @@ class AkamaiSNICertConfigController(base.Controller, hooks.HookController): query=rule.Rule( helpers.is_valid_domain_by_name(), helpers.abort_with_message)) - def get_one(self, query): + def get_one(self, cert_name): + """ + + Get SNI certificate configuration + :type cert_name: str + :param cert_name: The name of the SNI certificate + :return: The SNI certificate details + :rtype: dict + """ try: return ( self._driver.manager.ssl_certificate_controller. - get_sni_cert_configuration(query) + get_sni_cert_configuration(cert_name) ) except Exception as e: pecan.abort(400, str(e)) @@ -364,12 +431,19 @@ def get_one(self, query): "sni_config", "POST")), helpers.abort_with_message, stoplight_helpers.pecan_getter)) - def post(self, query): + def post(self, cert_name): + """ + Update the configuration for SNI certificate + :type cert_name: str + :param cert_name: + :return: Updated configuration + :rtype: dict + """ request_json = json.loads(pecan.request.body.decode('utf-8')) try: res = ( self._driver.manager.ssl_certificate_controller. - update_sni_cert_configuration(query, request_json)) + update_sni_cert_configuration(cert_name, request_json)) return res except Exception as e: pecan.abort(400, str(e)) @@ -400,7 +474,6 @@ def __init__(self, driver): class OperatorServiceActionController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -415,7 +488,12 @@ def __init__(self, driver): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): + """ + Perform action on a list of services + :return: Pecan response with http status code 202 or 404 + :rtype: pecan.Response + """ service_state_json = json.loads(pecan.request.body.decode('utf-8')) service_action = service_state_json.get('action', None) project_id = service_state_json.get('project_id', None) @@ -429,15 +507,14 @@ def post(self): except Exception as e: pecan.abort(404, detail=( - 'Services action {0} on tenant: {1} failed, ' - 'Reason: {2}'.format(service_action, - project_id, str(e)))) + 'Services action {0} on tenant: {1} failed, ' + 'Reason: {2}'.format(service_action, + project_id, str(e)))) return pecan.Response(None, 202) class OperatorServiceLimitController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -456,7 +533,14 @@ def __init__(self, driver): helpers.abort_with_message) ) def put(self, project_id): - + """ + + Set limit on number of services per project + :type project_id: int + :param project_id: Id of the project to set the service limit + :return: Pecan response with http status code 201 or 404 + :rtype: pecan.Response + """ service_state_json = json.loads(pecan.request.body.decode('utf-8')) project_limit = service_state_json.get('limit', None) @@ -467,9 +551,9 @@ def put(self, project_id): project_limit) except Exception as e: pecan.abort(404, detail=( - 'Services limit {0} on tenant: {1} failed, ' - 'Reason: {2}'.format(project_limit, - project_id, str(e)))) + 'Services limit {0} on tenant: {1} failed, ' + 'Reason: {2}'.format(project_limit, + project_id, str(e)))) return pecan.Response(None, 201) @@ -480,6 +564,14 @@ def put(self, project_id): helpers.abort_with_message) ) def get_one(self, project_id): + """ + + Get currently set limit on number of services per this project id + :type project_id: int + :param project_id: Id of the Project to get the service limit + :return: Dictionary with the current value of limits + :rtype: dict + """ services_controller = self._driver.manager.services_controller service_limits = services_controller.get_services_limit( @@ -489,7 +581,6 @@ def get_one(self, project_id): class ServiceStatusController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -505,7 +596,12 @@ def __init__(self, driver): stoplight_helpers.pecan_getter) ) def post(self): + """ + Update or set status of the service with provider details + :return: Pecan response with http status code 201 or 202 or 404 + :rtype: pecan.Response + """ service_state_json = json.loads(pecan.request.body.decode('utf-8')) project_id = service_state_json['project_id'] service_id = service_state_json['service_id'] @@ -522,18 +618,17 @@ def post(self): ) except Exception as e: pecan.abort(404, detail=( - 'Setting state of service {0} on tenant: {1} ' - 'to {2} has failed, ' - 'Reason: {3}'.format(service_id, - project_id, - status, - str(e)))) + 'Setting state of service {0} on tenant: {1} ' + 'to {2} has failed, ' + 'Reason: {3}'.format(service_id, + project_id, + status, + str(e)))) return pecan.Response(None, status_code) class AdminCertController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -547,6 +642,12 @@ def __init__(self, driver): stoplight_helpers.pecan_getter) ) def get(self): + """ + + Get ssl certificates by status + :return: Pecan response with http status 200 and json + :rtype: pecan.Response + """ ssl_certificate_controller = ( self._driver.manager.ssl_certificate_controller ) @@ -576,7 +677,14 @@ def get(self): stoplight_helpers.pecan_getter) ) def patch_one(self, domain_name): + """ + Update certificate for a domain + :type domain_name: str + :param domain_name: Name of the domain for which the certificate needs to be updated + :return: Pecan response with http status code 204 or 400 or 404 + :rtype: pecan.Response + """ ssl_certificate_controller = ( self._driver.manager.ssl_certificate_controller ) @@ -600,7 +708,6 @@ def patch_one(self, domain_name): class AdminServiceController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -619,6 +726,12 @@ def __init__(self, driver): stoplight_helpers.pecan_getter) ) def get(self): + """ + + Get services by status + :return: JSON response with http status 200 + :rtype: pecan.Response + """ services_controller = self._driver.manager.services_controller call_args = getattr(pecan.request.context, @@ -632,7 +745,6 @@ def get(self): class DomainController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] def __init__(self, driver): @@ -647,13 +759,21 @@ def __init__(self, driver): helpers.abort_with_message) ) def get_one(self, domain_name): + """ + + Get services by domain name + :type domain_name: str + :param domain_name: Name of the domain + :return: JSON response model + :rtype: collections.OrderedDict + """ services_controller = self._driver.manager.services_controller try: service_obj = services_controller.get_service_by_domain_name( domain_name) except LookupError: pecan.abort(404, detail='Domain %s cannot be found' % - domain_name) + domain_name) # convert a service model into a response service model return resp_service_model.Model(service_obj, self) @@ -665,6 +785,12 @@ def get_one(self, domain_name): stoplight_helpers.pecan_getter) ) def get(self): + """ + + Get domains by provider url + :return: Pecan response with http status 200 and json + :rtype: pecan.Response + """ services_controller = self._driver.manager.services_controller call_args = getattr(pecan.request.context, diff --git a/poppy/transport/pecan/controllers/v1/flavors.py b/poppy/transport/pecan/controllers/v1/flavors.py index 7aefc3c6..8dd8e1d4 100644 --- a/poppy/transport/pecan/controllers/v1/flavors.py +++ b/poppy/transport/pecan/controllers/v1/flavors.py @@ -31,15 +31,17 @@ class FlavorsController(base.Controller, hooks.HookController): - __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] """Flavors Controller.""" @pecan.expose('json') def get_all(self): - """get all flavor list.""" - + """ + Get all the flavors + :return: List of available flavors in the system + :rtype: dict + """ flavors_controller = self.driver.manager.flavors_controller result = flavors_controller.list() @@ -57,10 +59,12 @@ def get_all(self): helpers.abort_with_message) ) def get_one(self, flavor_id): - """get_one - - :param flavor_model - :returns JSON flavor(HTTP 200) or HTTP 404 + """ + Get A flavor details by it's id + :param flavor_id: Flavor id + :type flavor_id: str + :return: Serialized response with the Flavor details wrapped + :rtype: collections.OrderedDict """ flavors_controller = self.driver.manager.flavors_controller try: @@ -78,9 +82,10 @@ def get_one(self, flavor_id): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): - """POST - - :returns JSON flavor(HTTP 200) or HTTP 400 + """ + Create a new flavor + :return: Newly created flavor + :rtype: collections.OrderedDict """ flavors_controller = self.driver.manager.flavors_controller flavor_json = json.loads(pecan.request.body.decode('utf-8')) @@ -104,10 +109,10 @@ def post(self): @pecan.expose('json') def delete(self, flavor_id): - """DELETE - - :param flavor_model - :returns HTTP 204 + """ + Delete an existing flavor + :param flavor_id: Flavor id to be deleted + :type flavor_id: str """ flavors_controller = self.driver.manager.flavors_controller flavors_controller.delete(flavor_id) diff --git a/poppy/transport/pecan/controllers/v1/health.py b/poppy/transport/pecan/controllers/v1/health.py index b39388a4..38141806 100644 --- a/poppy/transport/pecan/controllers/v1/health.py +++ b/poppy/transport/pecan/controllers/v1/health.py @@ -29,14 +29,14 @@ class DNSHealthController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self, dns_name): - """GET. + """ Returns the health of DNS Provider - - :param dns_name - :returns JSON storage model or HTTP 404 + :type dns_name: str + :param dns_name: Name of the DNS + :return: JSON dns model or HTTP 404 + :rtype: collections.OrderedDict """ - health_controller = self._driver.manager.health_controller try: @@ -54,14 +54,14 @@ class StorageHealthController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self, storage_name): - """GET. + """ Returns the health of storage - - :param storage_name - :returns JSON storage model or HTTP 404 + :type storage_name: str + :param storage_name: Name of the storage + :return: JSON storage model or HTTP 404 + :rtype: collections.OrderedDict """ - health_controller = self._driver.manager.health_controller try: @@ -79,14 +79,14 @@ class DistributedTaskHealthController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self, distributed_task_name): - """GET. + """ Returns the health of distributed task manager - - :param distributed_task_name - :returns JSON storage model or HTTP 404 + :type distributed_task_name: str + :param distributed_task_name: Name of the distributed task + :return: JSON Distributed task model or HTTP 404 + :rtype: collections.OrderedDict """ - health_controller = self._driver.manager.health_controller try: @@ -103,7 +103,15 @@ class ProviderHealthController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self, provider_name): - """Returns the health of provider.""" + """ + + Returns the health of provider + :type provider_name: str + :param provider_name: Name of the provider + :return: JSON provider model or HTTP 404 + :rtype: collections.OrderedDict + """ + health_controller = self._driver.manager.health_controller @@ -120,7 +128,12 @@ class HealthController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self): - """Returns the health of storage and providers.""" + """ + Returns the health of storage and providers + :return: JSON response or HTTP 503 + :rtype: collections.OrderedDict + """ + health_controller = self._driver.manager.health_controller diff --git a/poppy/transport/pecan/controllers/v1/home.py b/poppy/transport/pecan/controllers/v1/home.py index 57c2fb54..f5c0ead5 100644 --- a/poppy/transport/pecan/controllers/v1/home.py +++ b/poppy/transport/pecan/controllers/v1/home.py @@ -26,5 +26,10 @@ class HomeController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self): + """ + + Root path for urls '/home/' + :return: A router to route the urls further + """ home_controller = self._driver.manager.home_controller return home_controller.get() diff --git a/poppy/transport/pecan/controllers/v1/ping.py b/poppy/transport/pecan/controllers/v1/ping.py index abcf2c95..1501ffbb 100644 --- a/poppy/transport/pecan/controllers/v1/ping.py +++ b/poppy/transport/pecan/controllers/v1/ping.py @@ -28,6 +28,10 @@ class PingController(base.Controller, hooks.HookController): @pecan.expose('json') def get(self): + """ + Ping the health controller + :return: Pecan response with http status 204 or 503 + """ health_controller = self._driver.manager.health_controller health_map, is_alive = health_controller.ping_check() if is_alive: diff --git a/poppy/transport/pecan/controllers/v1/services.py b/poppy/transport/pecan/controllers/v1/services.py index 43ab6bb0..63c9e2b1 100644 --- a/poppy/transport/pecan/controllers/v1/services.py +++ b/poppy/transport/pecan/controllers/v1/services.py @@ -55,6 +55,14 @@ class ServiceAssetsController(base.Controller, hooks.HookController): helpers.abort_with_message) ) def delete(self, service_id): + """ + + Purge a service by id + :type service_id: str + :param service_id: Id of the service to delete + :return: Pecan's response with 202 status + :rtype: pecan.Response + """ purge_url = pecan.request.GET.get('url', '/*') purge_all = pecan.request.GET.get('all', False) hard = pecan.request.GET.get('hard', 'True') @@ -110,8 +118,14 @@ class ServicesAnalyticsController(base.Controller, hooks.HookController): stoplight_helpers.pecan_getter) ) def get(self, service_id): - '''Get Analytics By Domain Data''' - + """ + + Get metrics by domain of this service id + :type service_id: str + :param service_id: Id of the service to retrieve the metrics by domain + :return: Pecan response object with http status code 200 or 500 or 404 or 400 + :rtype: pecan.Response + """ call_args = getattr(pecan.request.context, "call_args") domain = call_args.pop('domain') @@ -153,6 +167,11 @@ def __init__(self, driver): @pecan.expose('json') def get_all(self): + """ + Get all the services available + :return: Dict of links and services + :rtype: dict + """ marker = pecan.request.GET.get('marker', None) limit = pecan.request.GET.get('limit', 10) try: @@ -202,6 +221,14 @@ def get_all(self): helpers.abort_with_message) ) def get_one(self, service_id): + """ + + Get a service by id + :type service_id: str + :param service_id: Id of the service to retrieve + :return: Service details + :rtype: collections.OrderedDict + """ services_controller = self._driver.manager.services_controller try: service_obj = services_controller.get_service( @@ -220,6 +247,11 @@ def get_one(self, service_id): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): + """ + Create a new service + :return: Pecan response with status 202 + :rtype: pecan.Response + """ services_controller = self._driver.manager.services_controller service_json_dict = json.loads(pecan.request.body.decode('utf-8')) service_id = None @@ -254,6 +286,13 @@ def post(self): helpers.abort_with_message) ) def delete(self, service_id): + """ + Delete a service by id + :type service_id: str + :param service_id: Id of the service to delete + :return: Pecan response with http status code 202 or 404 + :rtype: pecan.Response + """ services_controller = self._driver.manager.services_controller try: @@ -276,6 +315,14 @@ def delete(self, service_id): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def patch_one(self, service_id): + """ + + Update a service + :type service_id: str + :param service_id: Id of the service to update + :return: Pecan response with http status code 202 or 400 or 404 + :rtype: pecan.Response + """ service_updates = json.loads(pecan.request.body.decode('utf-8')) services_controller = self._driver.manager.services_controller diff --git a/poppy/transport/pecan/controllers/v1/ssl_certificates.py b/poppy/transport/pecan/controllers/v1/ssl_certificates.py index d45d05d0..35a86bcf 100644 --- a/poppy/transport/pecan/controllers/v1/ssl_certificates.py +++ b/poppy/transport/pecan/controllers/v1/ssl_certificates.py @@ -45,6 +45,12 @@ class SSLCertificateController(base.Controller, hooks.HookController): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): + """ + + Create a new SSL certificate + :return: Pecan response with http status 202 or 400 + :rtype: pecan.Response + """ ssl_certificate_controller = ( self._driver.manager.ssl_certificate_controller) @@ -72,6 +78,14 @@ def post(self): helpers.abort_with_message) ) def delete(self, domain_name): + """ + + Delete an SSL certificate for the domain + :type domain_name: str + :param domain_name: Name of the domain for which the certificates needs to be deleted + :return: Pecan response with http status code 202 or 400 + :rtype: pecan.Response + """ # For now we only support 'san' cert type cert_type = pecan.request.GET.get('cert_type', 'san') @@ -94,7 +108,13 @@ def delete(self, domain_name): helpers.abort_with_message) ) def get_one(self, domain_name): - + """ + Get certificates for a domain name + :type domain_name: str + :param domain_name: Name of the domain + :return: Pecan response with 404 or list of JSON certificate model + :rtype: collections.OrderedDict + """ certificate_controller = \ self._driver.manager.ssl_certificate_controller total_cert_info = [] diff --git a/poppy/transport/pecan/hooks/context.py b/poppy/transport/pecan/hooks/context.py index a9ac517f..5a821b94 100644 --- a/poppy/transport/pecan/hooks/context.py +++ b/poppy/transport/pecan/hooks/context.py @@ -32,6 +32,12 @@ def __init__(self, **kwargs): class ContextHook(hooks.PecanHook): def before(self, state): + """ + + Pre-request validations for Pecan requests + + :param state: Pecan request state + """ context_kwargs = {} if 'X-Project-ID' in state.request.headers: diff --git a/poppy/transport/pecan/hooks/error.py b/poppy/transport/pecan/hooks/error.py index f58b3188..681b5734 100644 --- a/poppy/transport/pecan/hooks/error.py +++ b/poppy/transport/pecan/hooks/error.py @@ -29,7 +29,9 @@ class ErrorHook(hooks.PecanHook): '''Intercepts all errors during request fulfillment and logs them.''' def on_error(self, state, exception): - '''Fires off when an error happens during a request. + """ + + Fires off when an error happens during a request. :param state: The Pecan state for the current request. :type state: pecan.core.state @@ -37,7 +39,8 @@ def on_error(self, state, exception): :type exception: Exception :returns: webob.Response -- JSON response with the error message. - ''' + :rtype: webob.Response + """ exception_payload = { 'status': 500, } diff --git a/poppy/transport/pecan/models/request/cachingrule.py b/poppy/transport/pecan/models/request/cachingrule.py index 35b5e6d4..c3c17df8 100644 --- a/poppy/transport/pecan/models/request/cachingrule.py +++ b/poppy/transport/pecan/models/request/cachingrule.py @@ -18,6 +18,17 @@ def load_from_json(json_data): + """ + Deserialize CachingRule object from JSON + Example : + from poppy.transport.pecan.models.request import cachingrule + CachingRule_obj = cachingrule.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of CachingRule object related key, values + :return: CachingRule object loaded from json_data + :rtype: CachingRule + """ name = json_data.get('name') ttl = json_data.get('ttl') rules = json_data.get('rules', []) diff --git a/poppy/transport/pecan/models/request/domain.py b/poppy/transport/pecan/models/request/domain.py index 944a99f7..8c51454d 100644 --- a/poppy/transport/pecan/models/request/domain.py +++ b/poppy/transport/pecan/models/request/domain.py @@ -18,6 +18,17 @@ def load_from_json(json_data): + """ + Deserialize Domain object from JSON + Example : + from poppy.transport.pecan.models.request import domain + Domain_obj = domain.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Domain object related key, values + :return: Domain object loaded from json_data + :rtype: Domain + """ domain_name = json_data.get('domain') protocol = json_data.get('protocol', 'http') certification_option = json_data.get('certificate', None) diff --git a/poppy/transport/pecan/models/request/flavor.py b/poppy/transport/pecan/models/request/flavor.py index 95638a25..4622e607 100644 --- a/poppy/transport/pecan/models/request/flavor.py +++ b/poppy/transport/pecan/models/request/flavor.py @@ -17,6 +17,18 @@ def load_from_json(json_data): + """ + + Deserialize Flavor object from JSON + Example : + from poppy.transport.pecan.models.request import flavor + Flavor_obj = flavor.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Flavor object related key, values + :return: Flavor object loaded from json_data + :rtype: Flavor + """ flavor_id = json_data['id'] providers = [] diff --git a/poppy/transport/pecan/models/request/log_delivery.py b/poppy/transport/pecan/models/request/log_delivery.py index e51a38d9..eee3d2cd 100644 --- a/poppy/transport/pecan/models/request/log_delivery.py +++ b/poppy/transport/pecan/models/request/log_delivery.py @@ -17,6 +17,18 @@ def load_from_json(json_data): + """ + + Deserialize LogDelivery object from JSON + Example : + from poppy.transport.pecan.models.request import log_delivery + LogDelivery_obj = log_delivery.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of LogDelivery object related key, values + :return: LogDelivery object loaded from json_data + :rtype: LogDelivery + """ enabled = json_data.get("enabled", False) result = ld.LogDelivery(enabled) return result diff --git a/poppy/transport/pecan/models/request/origin.py b/poppy/transport/pecan/models/request/origin.py index a1f2bfe3..678993b6 100644 --- a/poppy/transport/pecan/models/request/origin.py +++ b/poppy/transport/pecan/models/request/origin.py @@ -18,6 +18,17 @@ def load_from_json(json_data): + """ + Deserialize Origin object from JSON + Example : + from poppy.transport.pecan.models.request import origin + Origin_obj = origin.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Origin object related key, values + :return: Origin object loaded from json_data + :rtype: Origin + """ origin_name = json_data.get("origin") origin_name = origin_name.rstrip("/") hostheadertype = json_data.get("hostheadertype", "domain") diff --git a/poppy/transport/pecan/models/request/provider_details.py b/poppy/transport/pecan/models/request/provider_details.py index 55f21cb8..60b186b2 100644 --- a/poppy/transport/pecan/models/request/provider_details.py +++ b/poppy/transport/pecan/models/request/provider_details.py @@ -17,6 +17,19 @@ def load_from_json(json_data): + """ + + Deserialize ProviderDetail object from JSON + Example : + from poppy.transport.pecan.models.request import provider_details + ProviderDetail_obj = provider_details.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of ProviderDetail object related key, values + :return: ProviderDetail object loaded from json_data + :rtype: ProviderDetail + + """ access_urls = json_data.get("access_urls") error_info = json_data.get("error_info", ) provider_service_id = json_data.get("id") diff --git a/poppy/transport/pecan/models/request/restriction.py b/poppy/transport/pecan/models/request/restriction.py index 423472d1..2cf97f57 100644 --- a/poppy/transport/pecan/models/request/restriction.py +++ b/poppy/transport/pecan/models/request/restriction.py @@ -18,6 +18,18 @@ def load_from_json(json_data): + """ + + Deserialize Restriction object from JSON + Example : + from poppy.transport.pecan.models.request import restriction + Restriction_obj = restriction.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Restriction object related key, values + :return: Restriction object loaded from json_data + :rtype: Restriction + """ name = json_data.get('name') access = json_data.get('access', 'whitelist') res = restriction.Restriction(name, access) diff --git a/poppy/transport/pecan/models/request/rule.py b/poppy/transport/pecan/models/request/rule.py index 9a7bcc98..49944f4d 100644 --- a/poppy/transport/pecan/models/request/rule.py +++ b/poppy/transport/pecan/models/request/rule.py @@ -17,6 +17,18 @@ def load_from_json(json_data): + """ + + Deserialize Rule object from JSON + Example : + from poppy.transport.pecan.models.request import rule + Rule_obj = rule.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Rule object related key, values + :return: Rule object loaded from json_data + :rtype: Rule + """ name = json_data.get('name', None) res = rule.Rule(name) res.referrer = json_data.get('referrer', None) diff --git a/poppy/transport/pecan/models/request/service.py b/poppy/transport/pecan/models/request/service.py index 46a930eb..e93ccc99 100644 --- a/poppy/transport/pecan/models/request/service.py +++ b/poppy/transport/pecan/models/request/service.py @@ -24,6 +24,18 @@ def load_from_json(json_data): + """ + + Deserialize Service object from JSON + Example : + from poppy.transport.pecan.models.request import service + Service_obj = service.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of Service object related key, values + :return: Service object loaded from json_data + :rtype: Service + """ service_id = json_data.get('service_id', uuid.uuid4()) name = json_data.get("name") diff --git a/poppy/transport/pecan/models/request/ssl_certificate.py b/poppy/transport/pecan/models/request/ssl_certificate.py index 7a189a40..70d58abe 100644 --- a/poppy/transport/pecan/models/request/ssl_certificate.py +++ b/poppy/transport/pecan/models/request/ssl_certificate.py @@ -17,6 +17,18 @@ def load_from_json(json_data): + """ + + Deserialize SSLCertificate object from JSON + Example : + from poppy.transport.pecan.models.request import ssl_certificate + SSLCertificate_obj = ssl_certificate.load_from_json({}) + + :type json_data: dict + :param json_data: Dictionary consisting of SSLCertificate object related key, values + :return: SSLCertificate object loaded from json_data + :rtype: SSLCertificate + """ flavor_id = json_data.get("flavor_id") domain_name = json_data.get("domain_name") cert_type = json_data.get("cert_type") diff --git a/poppy/transport/pecan/models/response/cachingrules.py b/poppy/transport/pecan/models/response/cachingrules.py index 84327e5d..0461f41e 100644 --- a/poppy/transport/pecan/models/response/cachingrules.py +++ b/poppy/transport/pecan/models/response/cachingrules.py @@ -22,8 +22,17 @@ class Model(collections.OrderedDict): + """ + Serialize a cachingrule object into an OrderedDict. + Can be used to send the service details back to client. - 'response class for CachingRules' + Example : + from poppy.model.helpers import cachingrule + from poppy.transport.pecan.models.response import cachingrule as cachingrule_response + cachingrule_response_obj = cachingrule.CachingRule('name', 'ttl') + return cachingrule_response.Model(cachingrule_response_obj, self) + + """ def __init__(self, caching): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/domain.py b/poppy/transport/pecan/models/response/domain.py index 80a08b7f..03f7cf8f 100644 --- a/poppy/transport/pecan/models/response/domain.py +++ b/poppy/transport/pecan/models/response/domain.py @@ -21,8 +21,19 @@ class Model(collections.OrderedDict): + """ + + Serialize a domain object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model.helpers import domain + from poppy.transport.pecan.models.response import domain as domain_response + domain_response_obj = domain.Domain('domain') + return domain_response.Model(domain_response_obj, self) + + """ - 'response class for Domain.' def __init__(self, domain): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/flavor.py b/poppy/transport/pecan/models/response/flavor.py index 04cbfa44..fd477e42 100644 --- a/poppy/transport/pecan/models/response/flavor.py +++ b/poppy/transport/pecan/models/response/flavor.py @@ -22,7 +22,17 @@ class Model(collections.OrderedDict): + """ + Serialize a flavor object into an OrderedDict. + Can be used to send the flavor details back to client. + Example : + from poppy.model import flavor + from poppy.transport.pecan.models.response import flavor as flavor_response + flavor_obj = flavor.Flavor('Premium', []) + return flavor_response.Model(flavor_obj, self) + + """ def __init__(self, flavor, controller): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/health.py b/poppy/transport/pecan/models/response/health.py index 35ced4eb..46c17adb 100644 --- a/poppy/transport/pecan/models/response/health.py +++ b/poppy/transport/pecan/models/response/health.py @@ -22,6 +22,13 @@ class DNSModel(collections.OrderedDict): + """ + + Serialize DNS Model. + Example: + from poppy.transport.pecan.models.response import health + return health.DNSModel(True) + """ def __init__(self, is_alive): super(DNSModel, self).__init__() @@ -32,6 +39,13 @@ def __init__(self, is_alive): class StorageModel(collections.OrderedDict): + """ + + Serialize Storage Model. + Example: + from poppy.transport.pecan.models.response import health + return health.StorageModel(True) + """ def __init__(self, is_alive): super(StorageModel, self).__init__() @@ -42,6 +56,12 @@ def __init__(self, is_alive): class DistributedTaskModel(collections.OrderedDict): + """ + Serialize Distributed task Model. + Example: + from poppy.transport.pecan.models.response import health + return health.DistributedTaskModel(True) + """ def __init__(self, is_alive): super(DistributedTaskModel, self).__init__() @@ -52,6 +72,13 @@ def __init__(self, is_alive): class ProviderModel(collections.OrderedDict): + """ + + Serialize Provider Model. + Example: + from poppy.transport.pecan.models.response import health + return health.ProviderModel(True) + """ def __init__(self, is_alive): super(ProviderModel, self).__init__() @@ -62,6 +89,13 @@ def __init__(self, is_alive): class HealthModel(collections.OrderedDict): + """ + + Serialize Health Model. + Example: + from poppy.transport.pecan.models.response import health + return health.HealthModel(True) + """ def __init__(self, controller, health_map): super(HealthModel, self).__init__() diff --git a/poppy/transport/pecan/models/response/link.py b/poppy/transport/pecan/models/response/link.py index 5ccbe291..bcf50719 100644 --- a/poppy/transport/pecan/models/response/link.py +++ b/poppy/transport/pecan/models/response/link.py @@ -20,7 +20,14 @@ class Model(collections.OrderedDict): - 'response class for Link.' + """ + + Serialize links. + Example: + from poppy.transport.pecan.models.response import link + link_obj = link.Model('href', 'rel') + return link_obj + """ def __init__(self, href, rel): super(Model, self).__init__() self['href'] = href diff --git a/poppy/transport/pecan/models/response/log_delivery.py b/poppy/transport/pecan/models/response/log_delivery.py index dda782ca..c07bef7e 100644 --- a/poppy/transport/pecan/models/response/log_delivery.py +++ b/poppy/transport/pecan/models/response/log_delivery.py @@ -21,7 +21,17 @@ class Model(collections.OrderedDict): - 'Response class for Log Delivery' + """ + Serialize a log_delivery object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model import log_delivery + from poppy.transport.pecan.models.response import log_delivery as log_delivery_response + log_delivery_obj = log_delivery.LogDelivery() + return log_delivery_response.Model(log_delivery_obj, self) + + """ def __init__(self, log_delivery): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/origin.py b/poppy/transport/pecan/models/response/origin.py index c6d6bae9..e7f8a8cc 100644 --- a/poppy/transport/pecan/models/response/origin.py +++ b/poppy/transport/pecan/models/response/origin.py @@ -22,8 +22,19 @@ class Model(collections.OrderedDict): + """ + + Serialize an Origin object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model.helpers import origin + from poppy.transport.pecan.models.response import origin as origin_response + origin_response_obj = origin.Origin('origin') + return origin_response.Model(origin_response_obj, self) + + """ - 'response class for Origin' def __init__(self, origin): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/restriction.py b/poppy/transport/pecan/models/response/restriction.py index fd06b544..3ca63352 100644 --- a/poppy/transport/pecan/models/response/restriction.py +++ b/poppy/transport/pecan/models/response/restriction.py @@ -23,8 +23,19 @@ class Model(collections.OrderedDict): + """ + + Serialize a Restriction object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model.helpers import restriction + from poppy.transport.pecan.models.response import restriction as restriction_response + restriction_response_obj = restriction.Restriction('name') + return restriction_response.Model(restriction_response_obj, self) + + """ - 'response class for Restriction' def __init__(self, restriction): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/rule.py b/poppy/transport/pecan/models/response/rule.py index 68644a1c..4ab2c46b 100644 --- a/poppy/transport/pecan/models/response/rule.py +++ b/poppy/transport/pecan/models/response/rule.py @@ -22,7 +22,18 @@ class Model(collections.OrderedDict): - """response class for rule.""" + """ + + Serialize a Rule object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model.helpers import rule + from poppy.transport.pecan.models.response import rule as rule_response + rule_response_obj = rule.Rule('name') + return rule_response.Model(rule_response_obj, self) + + """ def __init__(self, rule): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/service.py b/poppy/transport/pecan/models/response/service.py index 7e1ac51b..67ae1aa4 100644 --- a/poppy/transport/pecan/models/response/service.py +++ b/poppy/transport/pecan/models/response/service.py @@ -30,7 +30,17 @@ class Model(collections.OrderedDict): - """Service Response Model.""" + """ + Serialize a service object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model import service + from poppy.transport.pecan.models.response import service as service_response + service_obj = service.Service('Service id', 'name', ...) + return service_response.Model(service_obj, self) + + """ def __init__(self, service_obj, controller): super(Model, self).__init__() diff --git a/poppy/transport/pecan/models/response/ssl_certificate.py b/poppy/transport/pecan/models/response/ssl_certificate.py index e6410c93..b19a06fd 100644 --- a/poppy/transport/pecan/models/response/ssl_certificate.py +++ b/poppy/transport/pecan/models/response/ssl_certificate.py @@ -22,7 +22,17 @@ class Model(collections.OrderedDict): - """response class for SSLCertificate.""" + """ + Serialize an ssl_certificate object into an OrderedDict. + Can be used to send the service details back to client. + + Example : + from poppy.model import ssl_certificate + from poppy.transport.pecan.models.response import ssl_certificate as ssl_certificate_response + ssl_certificate_obj = ssl_certificate.SSLCertificate('flavor id', 'domain', ...) + return ssl_certificate_response.Model(ssl_certificate_obj, self) + + """ def __init__(self, ssl_certificate): super(Model, self).__init__() From ba8769c3b57fce33639a96d6815e6b21ae4d9824 Mon Sep 17 00:00:00 2001 From: coolyuvee Date: Thu, 16 Aug 2018 17:47:57 +0530 Subject: [PATCH 2/6] [docstring] Create docstring for poppy manager layer, poppy transport validators --- poppy/manager/base/analytics.py | 25 +- poppy/manager/base/background_job.py | 8 +- poppy/manager/base/controller.py | 4 + poppy/manager/base/driver.py | 45 ++- poppy/manager/base/flavors.py | 40 ++- poppy/manager/base/health.py | 26 +- poppy/manager/base/home.py | 3 +- poppy/manager/base/notifications.py | 17 +- poppy/manager/base/providers.py | 86 ++++- poppy/manager/base/services.py | 105 ++++-- poppy/manager/base/ssl_certificate.py | 34 +- poppy/manager/default/analytics.py | 14 + poppy/manager/default/background_job.py | 34 ++ poppy/manager/default/driver.py | 41 +++ poppy/manager/default/flavors.py | 37 +- poppy/manager/default/health.py | 52 ++- poppy/manager/default/home.py | 6 +- poppy/manager/default/services.py | 315 ++++++++++++++++-- poppy/manager/default/ssl_certificate.py | 156 +++++++++ poppy/transport/validators/helpers.py | 227 ++++++++++++- poppy/transport/validators/schema_base.py | 16 +- .../validators/schemas/background_jobs.py | 2 +- .../validators/schemas/domain_migration.py | 2 +- poppy/transport/validators/schemas/service.py | 2 +- .../validators/schemas/service_limit.py | 2 +- .../validators/schemas/ssl_certificate.py | 2 +- .../transport/validators/stoplight/helpers.py | 2 +- poppy/transport/validators/stoplight/rule.py | 12 +- 28 files changed, 1137 insertions(+), 178 deletions(-) diff --git a/poppy/manager/base/analytics.py b/poppy/manager/base/analytics.py index 46ae6f1a..725d4590 100644 --- a/poppy/manager/base/analytics.py +++ b/poppy/manager/base/analytics.py @@ -30,22 +30,41 @@ def __init__(self, manager): @property def storage_controller(self): + """Return storage controller. + + :return: Storage controller + :rtype: poppy.storage.cassandra.services.ServicesController + """ return self.manager.storage.services_controller @property def providers(self): + """Return provider module. + + :return: Provider module + :rtype: poppy.provider.akamai + """ return self.manager.providers @property def metrics_controller(self): + """Return metrics controller. + + :return: Metrics controller + :rtype: poppy.metrics.blueflood.services.ServicesController + """ return self.manager.metrics.services_controller @abc.abstractmethod def get_metrics_by_domain(self, project_id, domain_name, **extras): - """get analytics metrics by domain + """Get analytics metrics by domain. + + :param project_id: The project id + :type project_id: int + + :param domain_name: The domain name + :type domain_name: str - :param project_id - :param domain_name :raises: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/background_job.py b/poppy/manager/base/background_job.py index 8bb27372..e1069853 100644 --- a/poppy/manager/base/background_job.py +++ b/poppy/manager/base/background_job.py @@ -31,6 +31,12 @@ def __init__(self, manager): def post_job(self, job_type, kwargs): """Posts a background job to the distributed task driver. - :raises: NotImplementedError + :param job_type: The job type + :type job_type: str + + :param kwargs: Extra arguments for the task engine + :type kwargs: dict + + :raise: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/controller.py b/poppy/manager/base/controller.py index cd8fad18..ef613006 100644 --- a/poppy/manager/base/controller.py +++ b/poppy/manager/base/controller.py @@ -32,4 +32,8 @@ def __init__(self, driver): @property def driver(self): + """Return the instance of the driver + + :return: Driver's instance + """ return self._driver diff --git a/poppy/manager/base/driver.py b/poppy/manager/base/driver.py index a1757939..f68ba3c8 100644 --- a/poppy/manager/base/driver.py +++ b/poppy/manager/base/driver.py @@ -33,47 +33,72 @@ def __init__(self, conf, storage, providers, dns, distributed_task, @property def conf(self): - """conf + """Return the configuration for the driver. - :returns conf + :return: Configuration + :rtype: dict """ return self._conf @property def storage(self): - """storage + """Return the storage module for the driver. - :returns storage + :return: Storage module + :rtype: poppy.storage.cassandra """ return self._storage @property def providers(self): - """providers + """Return the provider module for the driver. - :returns providers + :return: Providers module + :rtype: poppy.provider.akamai """ return self._providers @property def dns(self): + """Return the dns module for the driver. + + :return: DNS module + :rtype: poppy.provider.dns + """ return self._dns @property def distributed_task(self): + """Return the distributed_task module for the driver. + + :return: distributed_task module + :rtype: poppy.provider.distributed_task + """ return self._distributed_task @property def notification(self): + """Return the notification module for the driver. + + :return: notification module + :rtype: poppy.provider.notification + """ + return self._notification @property def metrics(self): + """Return the metrics module for the driver. + + :return: metrics module + :rtype: poppy.provider.metrics + """ + return self._metrics @abc.abstractproperty def analytics_controller(self): - """Returns the driver's analytics controller + """Return the driver's analytics controller. :raises NotImplementedError """ @@ -81,7 +106,7 @@ def analytics_controller(self): @abc.abstractproperty def services_controller(self): - """Returns the driver's services controller + """Return the driver's services controller. :raises NotImplementedError """ @@ -89,7 +114,7 @@ def services_controller(self): @abc.abstractproperty def flavors_controller(self): - """Returns the driver's flavors controller + """Return the driver's flavors controller. :raises NotImplementedError """ @@ -97,7 +122,7 @@ def flavors_controller(self): @abc.abstractproperty def health_controller(self): - """Returns the driver's health controller + """Return the driver's health controller. :raises NotImplementedError """ diff --git a/poppy/manager/base/flavors.py b/poppy/manager/base/flavors.py index e2a89bd3..9e88d80f 100644 --- a/poppy/manager/base/flavors.py +++ b/poppy/manager/base/flavors.py @@ -32,52 +32,60 @@ def __init__(self, manager): @property def storage(self): - """storage + """Return StorageController for this FlavorController. - :returns: the storage object + :return: The storage object """ return self._storage @property def providers(self): - """storage + """Get list of providers for this FlavorController. - :returns: the list of provider drivers + :return: The list of provider drivers """ return self._providers @abc.abstractmethod def list(self): - """list + """Get list of the supported flavors. - :raises: NotImplementedError + :raise: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def get(self, flavor_id): - """GET + """Get Flavor details for the given flavor id - :param flavor_id - :raises: NotImplementedError + :param flavor_id: Flavor id to get the flavor details + :type flavor_id: str + + :raise: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def add(self, flavor): - """POST + """Add a new flavor. + + :param flavor: Flavor details + :type flavor: poppy.model.flavor.Flavor - :param flavor - :raises: NotImplementedError + :raise: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def delete(self, flavor_id, provider_id): - """DELETE + """Delete a flavor. + + :param flavor_id: The flavor id to delete + :type flavor_id: str + + :param provider_id: The provider id of the flavor + :type provider_id: str - :param flavor_id - :param provider_id - :raises: NotImplementedError + :raise: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/health.py b/poppy/manager/base/health.py index 5e9369bc..0d88130c 100644 --- a/poppy/manager/base/health.py +++ b/poppy/manager/base/health.py @@ -34,7 +34,7 @@ def __init__(self, manager): @abc.abstractmethod def health(self): - """Returns the health of storage and providers + """Returns the health of storage and providers. :raises: NotImplementedError """ @@ -42,36 +42,44 @@ def health(self): @abc.abstractmethod def is_provider_alive(self, provider_name): - """Returns the health of provider + """Returns the health of provider. + + :param provider_name: The provider name + :type provider_name: str - :param provider_name :raises: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def is_distributed_task_alive(self, distributed_task_name): - """Returns the health of distributed_task + """Returns the health of distributed_task. + + :param distributed_task_name: The task name + :type distributed_task_name: str - :param distributed_task_name :raises: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def is_dns_alive(self, dns_name): - """Returns the health of DNS Provider + """Returns the health of DNS Provider. + + :param dns_name: The DNS name + :type dns_name: str - :param dns_name :raises: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def is_storage_alive(self, storage_name): - """Returns the health of storage + """Returns the health of storage. + + :param storage_name: The name of the storage + :type storage_name: str - :param storage_name :raises: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/home.py b/poppy/manager/base/home.py index 5f037090..616420b8 100644 --- a/poppy/manager/base/home.py +++ b/poppy/manager/base/home.py @@ -29,9 +29,8 @@ def __init__(self, manager): @abc.abstractmethod def get(self): - """get + """Get JSON text with all resources info. - :param self :raises: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/notifications.py b/poppy/manager/base/notifications.py index 64f3121e..e119a24b 100644 --- a/poppy/manager/base/notifications.py +++ b/poppy/manager/base/notifications.py @@ -18,12 +18,19 @@ class NotificationWrapper(object): """"ProviderWrapper class.""" def send(self, ext, subject, mail_content): - """Send notifications with subject and mail_content + """Send notifications with subject and mail_content. - :param ext - :param subject - :param mail_content - :returns: ext.obj.services_controller.send(subject, mail_content) + :param ext: + :type ext: + + :param subject: The subject text + :type subject: str + + :param mail_content: The mail content + :type mail_content: str + + :return: ext.obj.services_controller.send(subject, mail_content) + :rtype: ext.obj.services_controller.send """ return ext.obj.services_controller.send(subject, mail_content) diff --git a/poppy/manager/base/providers.py b/poppy/manager/base/providers.py index e831c7bc..d1049170 100644 --- a/poppy/manager/base/providers.py +++ b/poppy/manager/base/providers.py @@ -20,21 +20,34 @@ class ProviderWrapper(object): """"ProviderWrapper class.""" def create(self, ext, service_obj): - """Create a provider + """Create a provider. - :param ext - :param service_obj - :returns: ext.obj.service_controller.create(service_obj) + :param ext: + :type ext: obj + + :param service_obj: The service details + :type service_obj: dict + + :return: ext.obj.service_controller.create(service_obj) + :rtype: ext.obj.service_controller.create """ return ext.obj.service_controller.create(service_obj) def update(self, ext, provider_details, service_obj): - """Update a provider + """Update a provider. + + :param ext: + :type ext: object - :param ext - :param provider_details - :param service_obj + :param provider_details: The provider details + :type provider_details: dict + + :param service_obj: The service details + :type service_obj: dict + + :return: Updated service details + :rtype: dict """ try: @@ -48,6 +61,17 @@ def update(self, ext, provider_details, service_obj): provider_service_id, service_obj) def delete(self, ext, provider_details, project_id): + """Delete a service. + + :param ext: + :type ext: object + + :param provider_details: The provider details + :type provider_details: dict + + :param project_id: The project id + :type project_id: int + """ try: provider_detail = provider_details[ext.obj.provider_name] except KeyError: @@ -60,6 +84,26 @@ def delete(self, ext, provider_details, project_id): def purge(self, ext, service_obj, provider_details, hard=False, purge_url=None): + """Purge a service. + + :param ext: + :type ext: object + + :param service_obj: The service details to purge + :type service_obj: dict + + :param provider_details: The provider details + :type provider_details: dict + + :param hard: (Default False) Provider will be set to status + 'update_in_progress' if set to True + :type hard: bool + + :param purge_url: The purge url + :type purge_url: str + + :raise: BadProviderDetail if service has not been created + """ try: provider_detail = provider_details[ext.obj.provider_name] except KeyError: @@ -76,11 +120,19 @@ def purge(self, ext, service_obj, provider_details, def create_certificate(self, ext, cert_obj, enqueue, https_upgrade): """Create a certificate. - :param ext - :param cert_obj - :param enqueue - :param https_upgrade - :returns: ext.obj.certificate_controller.create_certificate(cert_obj, + :param ext: + :type ext: object + + :param cert_obj: The certificate details + :type cert_obj: dict + + :param enqueue: The enqueue option + :type enqueue: bool + + :param https_upgrade: The upgrade option to https + :type https_upgrade: bool + + :return: ext.obj.certificate_controller.create_certificate(cert_obj, enqueue, https_upgrade) """ @@ -93,8 +145,12 @@ def create_certificate(self, ext, cert_obj, enqueue, https_upgrade): def delete_certificate(self, ext, cert_obj): """Delete a certificate. - :param ext - :param cert_obj + :param ext: + :type ext: object + + :param cert_obj: The certificate details + :type cert_obj: dict + :returns: ext.obj.service_controller.delete_certificate(cert_obj) """ diff --git a/poppy/manager/base/services.py b/poppy/manager/base/services.py index 12bbb2ab..eb4fb456 100644 --- a/poppy/manager/base/services.py +++ b/poppy/manager/base/services.py @@ -36,10 +36,16 @@ def __init__(self, manager): def get_services(self, project_id, marker=None, limit=None): """Get a list of services. - :param project_id - :param marker - :param limit - :raises: NotImplementedError + :param project_id: The project id + :type project_id: str + + :param marker: The marker indicator + :type marker: int + + :param limit: No of services to fetch + :type limit: int + + :raise: NotImplementedError """ raise NotImplementedError @@ -47,20 +53,30 @@ def get_services(self, project_id, marker=None, limit=None): def get_service(self, project_id, service_id): """Get a service. - :param project_id - :param service_id - :raises: NotImplementedError + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :raise: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def create_service(self, project_id, auth_token, service_obj): - """Create a service. + """Create a new service. + + :param project_id: The project id + :type project_id: int - :param project_id - :param auth_token - :param service_obj - :raises: NotImplementedError + :param auth_token: Token for the authorization + :type auth_token: str + + :param service_obj: The new service details + :type service_obj: dict + + :raise: NotImplementedError """ raise NotImplementedError @@ -69,32 +85,52 @@ def update_service(self, project_id, service_id, auth_token, service_updates, force_update=False): """Update a service. - :param project_id - :param service_id - :param auth_token - :param service_updates - :param force_update - :raises: NotImplementedError + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param auth_token: Token for the authorization + :type auth_token: str + + :param service_updates: To be updated details + :type service_updates: dict + + :param force_update: (Default False) Force update + :type force_update: bool + + :raise: NotImplementedError """ raise NotImplementedError @abc.abstractmethod def services_action(self, project_id, action, domain=None): - """services_action + """Perform an action on services. + + :param project_id: The project id + :type project_id: int - :param project_id - :param action - :param domain - :raises ValueError + :param action: Choose from 'delete', 'enable', 'disable' + :type action: str + + :param domain: The domain name + :type domain: str + + :raise: ValueError """ @abc.abstractmethod def delete_service(self, project_id, service_id): """Delete a service. - :param project_id - :param service_id - :raises: NotImplementedError + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :raise: NotImplementedError """ raise NotImplementedError @@ -104,9 +140,18 @@ def purge(self, project_id, service_id, hard=False, purge_url=None): If purge_url is none, all content of this service will be purged. - :param project_id - :param service_id - :param hard - :param purge_url + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param hard: (Default False) Changes provider's status to + 'update_in_progress' if True + + :param purge_url: The purge URL + :type purge_url: str + + :raise: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/base/ssl_certificate.py b/poppy/manager/base/ssl_certificate.py index d773c15a..82eaf8d9 100644 --- a/poppy/manager/base/ssl_certificate.py +++ b/poppy/manager/base/ssl_certificate.py @@ -31,9 +31,13 @@ def __init__(self, manager): def create_ssl_certificate(self, project_id, cert_obj): """Create ssl certificate. - :param project_id - :param cert_obj - :raises: NotImplementedError + :param project_id: The project id + :type int + + :param cert_obj: The certificate details + :type cert_obj: dict + + :raise: NotImplementedError """ raise NotImplementedError @@ -41,10 +45,16 @@ def create_ssl_certificate(self, project_id, cert_obj): def delete_ssl_certificate(self, project_id, domain_name, cert_type): """Delete ssl certificate. - :param project_id - :param domain_name - :param cert_type - :raises: NotImplementedError + :param project_id: The project id + :type project_id: int + + :param domain_name: The domain name + :type domain_name: str + + :param cert_type: Type of the certificate + :type cert_type: str + + :raise: NotImplementedError """ raise NotImplementedError @@ -52,8 +62,12 @@ def delete_ssl_certificate(self, project_id, domain_name, cert_type): def get_certs_info_by_domain(self, domain_name, project_id): """Get ssl certificate by domain. - :param domain_name: - :param project_id: - :raises: NotImplementedError + :param domain_name: The domain name + :type domain_name: str + + :param project_id: The project id + :type project_id: int + + :raise: NotImplementedError """ raise NotImplementedError diff --git a/poppy/manager/default/analytics.py b/poppy/manager/default/analytics.py index 5eb60817..5c8fd5d2 100644 --- a/poppy/manager/default/analytics.py +++ b/poppy/manager/default/analytics.py @@ -23,6 +23,20 @@ class AnalyticsController(base.AnalyticsController): def get_metrics_by_domain(self, project_id, domain_name, **extras): + """Get metrics information by domain. + + :param project_id: The project id + :type project_id: int + + :param domain_name: The domain name + :type domain_name: str + + :param extras: kwargs + :type extras: dict + + :return: Metrics captured for this domain + :rtype: dict + """ storage_controller = self.storage_controller try: diff --git a/poppy/manager/default/background_job.py b/poppy/manager/default/background_job.py index 760f34cd..fbb74769 100644 --- a/poppy/manager/default/background_job.py +++ b/poppy/manager/default/background_job.py @@ -50,6 +50,21 @@ def __init__(self, manager): self.service_storage = self._driver.storage.services_controller def post_job(self, job_type, kwargs): + """Post job to the TaskFlow engine. + + :param job_type: Type of the job. Valid types are : . + 'akamai_check_and_update_cert_status' or + 'akamai_update_papi_property_for_mod_san' or + 'akamai_update_papi_property_for_mod_sni' + :type job_type: str + + :param kwargs: Additional arguments for the task engine + :type kwargs: dict + + :return: Tuple of run_list and ignore_list + :rtype: (list, list) + :raise: NotImplementedError if the job_type is not supported + """ queue_data = [] run_list = [] @@ -445,6 +460,11 @@ def post_job(self, job_type, kwargs): ) def get_san_mapping_list(self): + """Get items from SAN mapping list. + + :return: List of SAN mapping items + :rtype: list + """ res = [] if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj @@ -455,6 +475,15 @@ def get_san_mapping_list(self): return res def put_san_mapping_list(self, san_mapping_list): + """Populate Akamai's san_mapping_queue with san_mapping_list. + + :param san_mapping_list: The items to put into + Akamai's san_mapping_queue + :type san_mapping_list: list + + :return: Tuple of old and new items in the Akamai san_mapping_queue + :rtype: (list, list) + """ new_queue_data = [json.dumps(r) for r in san_mapping_list] res, deleted = [], [] if 'akamai' in self._driver.providers: @@ -473,6 +502,11 @@ def put_san_mapping_list(self, san_mapping_list): return res, deleted def delete_http_policy(self): + """Delete old http policies from Akamai's http_policy_queue. + + :return: Tuple of deleted and ignored policies + :rtype: (list, list) + """ http_policies = [] run_list = [] ignore_list = [] diff --git a/poppy/manager/default/driver.py b/poppy/manager/default/driver.py index 4e356bce..c4af7d08 100644 --- a/poppy/manager/default/driver.py +++ b/poppy/manager/default/driver.py @@ -31,28 +31,69 @@ def __init__(self, conf, storage, providers, dns, distributed_task, @decorators.lazy_property(write=True) def analytics_controller(self): + """Return the driver's analytics controller. + + :return: Analytics controller + :rtype: poppy.manager.default.analytics.AnalyticsController + """ return controllers.Analytics(self) @decorators.lazy_property(write=True) def services_controller(self): + """Return the driver's services controller. + + :return: Services controller + :rtype: poppy.manager.default.services.DefaultServiceController + """ + return controllers.Services(self) @decorators.lazy_property(write=False) def home_controller(self): + """Return the driver's home controller. + + :return: Home controller + :rtype: poppy.manager.default.home.DefaultHomeController + """ + return controllers.Home(self) @decorators.lazy_property(write=False) def flavors_controller(self): + """Return the driver's flavors controller. + + :return: Flavors controller + :rtype: poppy.manager.default.flavors.DefaultFlavorsController + """ + return controllers.Flavors(self) @decorators.lazy_property(write=False) def health_controller(self): + """Return the driver's health controller. + + :return: Health controller + :rtype: poppy.manager.default.health.DefaultHealthController + """ + return controllers.Health(self) @decorators.lazy_property(write=False) def background_job_controller(self): + """Return the driver's background controller. + + :return: Background Job controller + :rtype: poppy.manager.default.background_job.BackgroundJobController + """ + return controllers.BackgroundJob(self) @decorators.lazy_property(write=False) def ssl_certificate_controller(self): + """Return the driver's SSL Certificate controller. + + :return: SSL Certificate controller + :rtype: poppy.manager.default.ssl_certificate.DefaultSSLCertificateController + """ + return controllers.SSLCertificate(self) diff --git a/poppy/manager/default/flavors.py b/poppy/manager/default/flavors.py index 2adf6845..f4ae2efb 100644 --- a/poppy/manager/default/flavors.py +++ b/poppy/manager/default/flavors.py @@ -24,28 +24,36 @@ def __init__(self, manager): super(DefaultFlavorsController, self).__init__(manager) def list(self): - """list. + """Get list of the supported flavors. - :return list + :return: List of the supported flavors. + :rtype: list """ return self.storage.list() def get(self, flavor_id): - """get. + """Return flavor details. - ":param flavor_id - :return flavor id + :param flavor_id: The flavor id + :type flavor_id: str + + :return: Flavor details + :rtype: poppy.model.flavor.Flavor """ return self.storage.get(flavor_id) def add(self, new_flavor): - """add. + """Add a new flavor. + + :param new_flavor: The new flavor details + :type new_flavor: poppy.model.flavor.Flavor - :param new_flavor - :return new flavor - :raise LookupError + :return: The newly added flavor details with + provider details + :rtype: poppy.model.flavor.Flavor + + :raise: ValueError if the flavor already exists """ - # is this a valid flavor? provider_list = self.driver.conf[bootstrap._DRIVER_GROUP].providers for provider in new_flavor.providers: @@ -58,9 +66,12 @@ def add(self, new_flavor): return self.storage.add(new_flavor) def delete(self, flavor_id): - """delete. + """Delete an existing flavor. + + :param flavor_id: The flavor id to delete + :type flavor_id: str - :param flavor_id - :return flavor_id + :return: None + :rtype: None """ return self.storage.delete(flavor_id) diff --git a/poppy/manager/default/health.py b/poppy/manager/default/health.py index 9657e58b..279f4c45 100644 --- a/poppy/manager/default/health.py +++ b/poppy/manager/default/health.py @@ -23,9 +23,10 @@ def __init__(self, manager): super(DefaultHealthController, self).__init__(manager) def health(self): - """health. + """Return the health of storage and providers. - :returns is_alive, health_map + :return: Tuple with is_alive and health_map info + :rtype:(bool, dict) """ health_map, is_alive = self.ping_check() @@ -53,6 +54,11 @@ def health(self): return is_alive, health_map def ping_check(self): + """Get the health_map and is_alive info. + + :return: Tuple with is_alive and health_map info + :rtype:(bool, dict) + """ health_map = {} is_alive = True @@ -78,12 +84,28 @@ def ping_check(self): return health_map, is_alive def is_provider_alive(self, provider_name): - """Returns the health of provider.""" + """Returns the health of provider. + + :param provider_name: The name of the provider + :type provider_name: str + + :return: Whether the provider is alive + :rtype: bool + """ return self._providers[provider_name].obj.is_alive() def is_distributed_task_alive(self, distributed_task_name): - """Returns the health of distributed_task.""" + """Returns the health of distributed_task. + + :param distributed_task_name: The name of the dist task + :type distributed_task_name: str + + :return: Whether the distributed task is alive + :rtype: bool + + :raise: KeyError if the distributed_task_name is not same as vendor name + """ if distributed_task_name == self._distributed_task.vendor_name.lower(): return self._distributed_task.is_alive() @@ -91,7 +113,16 @@ def is_distributed_task_alive(self, distributed_task_name): raise KeyError def is_storage_alive(self, storage_name): - """Returns the health of storage.""" + """Returns the health of storage. + + :param storage_name: The name of the storage + :type storage_name: str + + :return: Whether storage is alive + :rtype: bool + + :raise: KeyError if storage_name is not same as underlying storage + """ if storage_name == self._storage.storage_name.lower(): return self._storage.is_alive() @@ -99,7 +130,16 @@ def is_storage_alive(self, storage_name): raise KeyError def is_dns_alive(self, dns_name): - """Returns the health of DNS Provider.""" + """Returns the health of DNS Provider. + + :param dns_name: The name of the DNS + :type dns_name: str + + :return: Whether the DNS is alive + :rtype: bool + + :raise: KeyError if dns_name is not same as underlying DNS + """ if dns_name == self._dns.dns_name.lower(): return self._dns.is_alive() diff --git a/poppy/manager/default/home.py b/poppy/manager/default/home.py index 579248f5..b34bc5eb 100644 --- a/poppy/manager/default/home.py +++ b/poppy/manager/default/home.py @@ -80,8 +80,10 @@ def __init__(self, manager): self.JSON_HOME = JSON_HOME def get(self): - """get. + """Get JSON text with all resources info. - :return json home + :return: The JSON text with services, flavors, + health and ping information + :rtype: dict[str, dict] """ return self.JSON_HOME diff --git a/poppy/manager/default/services.py b/poppy/manager/default/services.py index c3ac0ae4..2beba2f5 100644 --- a/poppy/manager/default/services.py +++ b/poppy/manager/default/services.py @@ -63,6 +63,11 @@ def __init__(self, manager): self.provider_conf = self.driver.conf[PROVIDER_GROUP] def determine_sleep_times(self): + """Calculate sleep times used in rebind. + + :return: Time in seconds to sleep + :rtype: list + """ determined_sleep_time = \ random.randrange(self.dns_conf.min_backoff_range, @@ -74,6 +79,19 @@ def determine_sleep_times(self): return backoff def _get_provider_details(self, project_id, service_id): + """Return Provider details. + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :return: Provider details for the given project and service ids + :rtype: Dict[str, poppy.model.helpers.provider_details.ProviderDetail] + + :raise: LookupError if the service does not exists + """ try: provider_details = self.storage_controller.get_provider_details( project_id, @@ -84,6 +102,17 @@ def _get_provider_details(self, project_id, service_id): return provider_details def get_service_by_domain_name(self, domain_name): + """Return service details for the domain. + + :param domain_name: The domain name + :type domain_name: str + + :return: The service details + :rtype: poppy.model.service.Service + + :raise: LookupError if the domain does not exists + or service details can not be found for the domain + """ try: service_details = self.storage_controller\ .get_service_details_by_domain_name(domain_name) @@ -98,6 +127,14 @@ def get_service_by_domain_name(self, domain_name): return service_details def get_services_by_status(self, status): + """Return the services by status + + :param status: The status of the service + :type status: str + + :return: List of services + :rtype: list + """ services_project_ids = \ self.storage_controller.get_services_by_status(status) @@ -105,6 +142,14 @@ def get_services_by_status(self, status): return services_project_ids def get_domains_by_provider_url(self, provider_url): + """Return domains by Provider url + + :param provider_url: The provider URL + :type provider_url: str + + :return: List of domains + :rtype: list + """ domains = \ self.storage_controller.get_domains_by_provider_url(provider_url) @@ -112,6 +157,17 @@ def get_domains_by_provider_url(self, provider_url): return domains def _append_defaults(self, service_json, operation='create'): + """ Add defaults to the service. + + If the service rules are None or Empty, add default rule. + If the operation is 'create', add default ttl and cache. + + :param service_json: Service details in JSON form + :type service_json: dict + + :param operation: (Default 'create') Name of operation + :type operation: str + """ # default origin rule for origin in service_json.get('origins', []): if origin.get('rules') is None: @@ -151,29 +207,52 @@ def _append_defaults(self, service_json, operation='create'): def get_services(self, project_id, marker=None, limit=None): """Get a list of services. - :param project_id - :param marker - :param limit - :return list + :param project_id: The project id + :type project_id: int + + :param marker: The marker indicator + :type marker: int + + :param limit: No of services to fetch + :type limit: int + + :return: List of services + :rtype: list(poppy.model.service.Service) """ return self.storage_controller.get_services(project_id, marker, limit) def get_service(self, project_id, service_id): - """get. + """Get a service object. - :param project_id - :param service_id - :return controller + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :return: The service object + :rtype: poppy.model.services.Service """ return self.storage_controller.get_service(project_id, service_id) def create_service(self, project_id, auth_token, service_json): - """create. + """Create a new service. + + :param project_id: The project id + :type project_id: int + + :param auth_token: Token for authorization + :type auth_token: str + + :param service_json: The service details to create + :type service_json: dict + + :return: The new service object created + :rtype: poppy.model.service.Service - :param project_id - :param auth_token - :param service_json - :raises LookupError, ValueError + :raise: + LookupError if the flavor does not exists, + ValueError if shared domain retry ops has problem """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) @@ -250,14 +329,30 @@ def create_service(self, project_id, auth_token, service_json): def update_service(self, project_id, service_id, auth_token, service_updates, force_update=False): - """update. - - :param project_id - :param service_id - :param auth_token - :param service_updates - :param force_update - :raises LookupError, ValueError + """Update a service. + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param auth_token: Token for authorization + :type auth_token: str + + :param service_updates: The service details + :type service_updates: dict + + :param force_update: (Default False) Forceful update \ + of the service + :type force_update: bool + + :raise: + ServiceNotFound if service not found, + ServiceStatusDisabled if service id disabled, + ServiceStatusNeitherDeployedNorFailed + if service is neither deployed not failed + """ # get the current service object try: @@ -476,12 +571,37 @@ def update_service(self, project_id, service_id, return def services_limit(self, project_id, limit): + """Set limit for no of services. + + :param project_id: The project id + :type project_id: int + + :param limit: No of services + :type limit: int + """ self.storage_controller.set_service_limit( project_id=project_id, project_limit=limit) def set_service_provider_details(self, project_id, service_id, auth_token, status): + """Set provider details for service. + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param auth_token: Token for authorization + :type auth_token: str + + :param status: Status of the service + :type status: str + + :return: Http status code 201 or 202 + :rtype: int + """ old_service = self.storage_controller.get_service( project_id, service_id @@ -502,6 +622,14 @@ def set_service_provider_details(self, project_id, service_id, return 201 def get_services_limit(self, project_id): + """Get the currently set limit on services. + + :param project_id: The project id + :type project_id: int + + :return: The dict with limit's value + :rtype: dict[str, int] + """ limit = self.storage_controller.get_service_limit( project_id=project_id) @@ -510,6 +638,17 @@ def get_services_limit(self, project_id): } def _action_per_service_obj(self, project_id, action, service_obj): + """Perform an action on the service. + + :param project_id: The project id + :type project_id: int + + :param action: Choose from 'delete', 'enable', 'disable' + :type action: str + + :param service_obj: The service object + :type service_obj: dict + """ kwargs = { 'project_id': project_id, @@ -546,13 +685,16 @@ def _action_per_service_obj(self, project_id, action, service_obj): str(e))) def services_action(self, project_id, action, domain=None): - """perform action on services + """perform action on services present in this domain. - :param project_id - :param action - :param domain + :param project_id: The project id + :type project_id: int - :raises ValueError + :param action: Choose from 'delete', 'enable', 'disable' + :type action: str + + :param domain: The name of the domain + :type domain: str """ # list all the services of for this project_id @@ -587,11 +729,13 @@ def services_action(self, project_id, action, domain=None): return def delete_service(self, project_id, service_id): - """delete. + """Delete a service. + + :param project_id: The project id + :type project_id: int - :param project_id - :param service_id - :raises LookupError + :param service_id: The service id + :type service_id: str """ service_obj = self.storage_controller.get_service( project_id, service_id) @@ -625,7 +769,25 @@ def delete_service(self, project_id, service_id): return def purge(self, project_id, service_id, hard=False, purge_url=None): - """If purge_url is none, all content of this service will be purge.""" + """Purge a service. + + If purge_url is none, all content of this service will be purge. + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param hard: (Default False) Mark 'update_in_progress' \ + if set to True + :type hard: bool + + :param purge_url: The purge url + :type purge_url: str + + :raise: ServiceStatusNotDeployed if service not yet deployed + """ try: service_obj = self.storage_controller.get_service( project_id, @@ -673,6 +835,19 @@ def purge(self, project_id, service_id, hard=False, purge_url=None): return def _generate_shared_ssl_domain(self, domain_name, store): + """Generate shared ssl domain. + + :param domain_name: The domain name + :type domain_name: str + + :param store: The store object + :type store: object + + :return: Shared ssl domain + :rtype: str + + :raise: SharedShardsExhausted if domain has already been taken + """ try: if not hasattr(self, store): gen_store = { @@ -700,6 +875,20 @@ def _generate_shared_ssl_domain(self, domain_name, store): 'Domain {0} has already been taken'.format(domain_name)) def _pick_shared_ssl_domain(self, domain, service_id, store): + """Select an available shared ssl domain. + + :param domain: The domain name + :type domain: str + + :param service_id: The service id + :type service_id: str + + :param store: The store object + :type store: object + + :return: The available domain + :rtype: str + """ shared_ssl_domain = self._generate_shared_ssl_domain( domain, store) @@ -712,6 +901,22 @@ def _pick_shared_ssl_domain(self, domain, service_id, store): return shared_ssl_domain def _shard_retry(self, project_id, service_obj, store=None): + """Retry to get available ssl domain and add to service. + + :param project_id: The project id + :type project_id: int + + :param service_obj: the service object + :type service_obj: dict + + :param store: The store object + :type store: object + + :return: The service object with shared ssl domain added + :rtype: dict + + :raise: ValueError + """ # deal with shared ssl domains try: for domain in service_obj.domains: @@ -735,6 +940,23 @@ def _shard_retry(self, project_id, service_obj, store=None): def migrate_domain(self, project_id, service_id, domain_name, new_cert, cert_status='deployed'): + """Migrate domain + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param domain_name: The domain name + :type domain_name: str + + :param new_cert: The new certificate + :type new_cert: str + + :param cert_status: (Default 'deployed')Certificate status + :type cert_status: str + """ dns_controller = self.dns_controller storage_controller = self.storage_controller @@ -802,6 +1024,17 @@ def migrate_domain(self, project_id, service_id, domain_name, new_cert, ) def _detect_upgrade_http_to_https(self, old_domains, new_domain): + """Determine whether upgrade is required from http to https. + + :param old_domains: List of old domains + :type old_domains: list + + :param new_domain: New domain + :type new_domain: str + + :return: Boolean value indicating upgrade is required or not + :rtype: bool + """ is_upgrade = False for old_domain in old_domains: if old_domain.domain == new_domain.domain: @@ -815,6 +1048,26 @@ def _detect_upgrade_http_to_https(self, old_domains, new_domain): def update_access_url_service( self, project_id, service_id, access_url_changes): + """Update access url of a service. + + :param project_id: The project id + :type project_id: int + + :param service_id: The service id + :type service_id: str + + :param access_url_changes: The changes to update the access url + :type access_url_changes: dict + + :return: Whether or not the access url details updated + :rtype: bool + + :raise: + ValueError if the domain could not found on the service, + ServiceNotFound if service is not found, + InvalidOperation if the domain is shared ssl, + InvalidResourceName if the access url or domain is invalid + """ try: service_old = self.storage_controller.get_service( project_id, diff --git a/poppy/manager/default/ssl_certificate.py b/poppy/manager/default/ssl_certificate.py index 9820ac87..0aef4c2a 100644 --- a/poppy/manager/default/ssl_certificate.py +++ b/poppy/manager/default/ssl_certificate.py @@ -45,6 +45,23 @@ def __init__(self, manager): def create_ssl_certificate( self, project_id, cert_obj, https_upgrade=False): + """Create a new certificate. + + :param project_id: The project id + :type project_id: int + + :param cert_obj: The certificate details + :type cert_obj: dict + + :param https_upgrade: (Default False) + Whether ot not to be upgraded to https + :type https_upgrade: bool + + :return: kwargs with wrapped arguments + :rtype: dict + + :raise: ValueError if if domain is not a valid non-root domain + """ if (not validators.is_valid_domain_name(cert_obj.domain_name)) or \ (validators.is_root_domain( @@ -86,6 +103,22 @@ def create_ssl_certificate( return kwargs def delete_ssl_certificate(self, project_id, domain_name, cert_type): + """Delete an SSL certificate. + + :param project_id: The project id + :type project_id: int + + :param domain_name: The name of the domain + :type domain_name: str + + :param cert_type: Type of the certificate to be deleted + :type cert_type: str + + :return: kwargs with cert, provider and other original inputs + :rtype: dict + + :raise: LookupError if the flavor is not found + """ cert_obj = self.storage.get_certs_by_domain( domain_name, cert_type=cert_type) @@ -110,6 +143,19 @@ def delete_ssl_certificate(self, project_id, domain_name, cert_type): return kwargs def get_certs_info_by_domain(self, domain_name, project_id): + """Get the certificates information by domain. + + :param domain_name: Name of the domain + :type domain_name: str + + :param project_id: The project id + :type project_id: int + + :return: SSLCertificate object + :rtype: poppy.model.ssl_certificate.SSLCertificate + + :raise: ValueError if cert info is not found + """ try: certs_info = self.storage.get_certs_by_domain( domain_name=domain_name, @@ -124,6 +170,11 @@ def get_certs_info_by_domain(self, domain_name, project_id): raise e def get_san_retry_list(self): + """Get items from san_mapping_queue for Akamai provider. + + :return: san_mapping_queue of Akamai provider + :rtype: dict + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj res = akamai_driver.mod_san_queue.traverse_queue() @@ -142,6 +193,17 @@ def get_san_retry_list(self): ] def update_san_retry_list(self, queue_data_list): + """Update Akamai mod_san_queue with new items. + + :param queue_data_list: The list of new items to update to. + :type queue_data_list: list + + :return: tuple of lists of old and new items in Akamai mod_san_queue + :rtype: (list, list) + :raise: + LookupError if domain does not exists, + ValueError if certificate already exists + """ for r in queue_data_list: service_obj = self.service_storage\ .get_service_details_by_domain_name(r['domain_name']) @@ -180,6 +242,13 @@ def update_san_retry_list(self, queue_data_list): return res, deleted def rerun_san_retry_list(self): + """Rerun the mod_san_queue. + + :return: Tuple of lists containing the run items and ignored items + :rtype: (list, list) + + :raise: LookupError if the flavor is not found + """ run_list = [] ignore_list = [] if 'akamai' in self._driver.providers: @@ -310,6 +379,16 @@ def rerun_san_retry_list(self): return run_list, ignore_list def get_san_cert_configuration(self, san_cert_name): + """Get SAN certificate configuration. + + :param san_cert_name: Name of the SAN certificate + :type san_cert_name: str + + :return: Configuration of the certificate + :rtype: dict + + :raise: ValueError if not a valid certificate name + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj if san_cert_name not in akamai_driver.san_cert_cnames: @@ -326,6 +405,21 @@ def get_san_cert_configuration(self, san_cert_name): return res def update_san_cert_configuration(self, san_cert_name, new_cert_config): + """Update configuration of SAN certificate. + + :param san_cert_name: Name of the SAN certificate + :type san_cert_name: str + + :param new_cert_config: New configuration to update with. + :type new_cert_config: dict + + :return: The updated values of the certificate + :rtype: dict + + :raise: + ValueError if not a valid certificate, + RuntimeError if unable to call SPS api + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj if san_cert_name not in akamai_driver.san_cert_cnames: @@ -361,6 +455,14 @@ def update_san_cert_configuration(self, san_cert_name, new_cert_config): return res def get_sni_cert_configuration(self, cert_name): + """Get configuration for SNI certificates. + + :param cert_name: The name of the SNI certificate + :type cert_name: str + + :return: SNI certificate configuration + :rtype: dict + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj self._validate_sni_cert_name(akamai_driver, cert_name) @@ -372,6 +474,17 @@ def get_sni_cert_configuration(self, cert_name): return res def update_sni_cert_configuration(self, cert_name, new_cert_config): + """Update SNI certificate's configuration. + + :param cert_name: SNI certificate name + :type cert_name: str + + :param new_cert_config: New configuration to update with + :type new_cert_config: dict + + :return: The updated configuration of the certificate + :rtype: dict + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj self._validate_sni_cert_name(akamai_driver, cert_name) @@ -386,6 +499,11 @@ def update_sni_cert_configuration(self, cert_name, new_cert_config): return res def get_san_cert_hostname_limit(self): + """Get SAN certificate hostname limit. + + :return: Limit for the SAN cert hostnames + :rtype: int + """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj res = akamai_driver.cert_info_storage.get_san_cert_hostname_limit() @@ -398,6 +516,16 @@ def get_san_cert_hostname_limit(self): @staticmethod def _validate_sni_cert_name(provider_driver, cert_name): + """Check whether a valid SNI certificate name. + + :param provider_driver: The provider driver + :type provider_driver: object + + :param cert_name: The name of the SNI certificate + :type cert_name: str + + :raise: ValueError if not a valid SNI certificate name + """ if cert_name not in provider_driver.sni_cert_cnames: raise ValueError( "{0} is not a valid sni cert, " @@ -405,6 +533,14 @@ def _validate_sni_cert_name(provider_driver, cert_name): cert_name, provider_driver.sni_cert_cnames)) def set_san_cert_hostname_limit(self, request_json): + """Set hostname limit for SAN certificate. + + :param request_json: Dict containing the required limit + :type request_json: dict + + :return: The updated limit value + :rtype: int + """ if 'akamai' in self._driver.providers: try: new_limit = request_json['san_cert_hostname_limit'] @@ -425,11 +561,31 @@ def set_san_cert_hostname_limit(self, request_json): return res def get_certs_by_status(self, status): + """Get certificates by their status + + :param status: Status of the certficate to look for + :type status: str + + :return: Certificate details + :rtype: dict + """ certs_by_status = self.storage.get_certs_by_status(status) return certs_by_status def update_certificate_status(self, domain_name, certificate_updates): + """Update a certificate status. + + :param domain_name: The domain name + :type domain_name: str + + :param certificate_updates: New status of the cert to update with + :type certificate_updates: dict + + :raise: + ValueError if no certificate found, + CertificateStatusUpdateError if unable to update certificate status + """ certificate_old = self.storage.get_certs_by_domain(domain_name) if not certificate_old: raise ValueError( diff --git a/poppy/transport/validators/helpers.py b/poppy/transport/validators/helpers.py index f3a97bf5..6b0177e8 100644 --- a/poppy/transport/validators/helpers.py +++ b/poppy/transport/validators/helpers.py @@ -37,19 +37,34 @@ def req_accepts_json_pecan(request, desired_content_type='application/json'): - # Assume the transport is pecan for now - # for falcon the syntax should actually be: - # request.accept('application/json') + """Check the request's contentType is accepted or not. + + pecan is the currently implemented web framework. + For Falcon use 'application/json' + + :param request: Web request + :type request: pecan.request + + :param desired_content_type: The contentType of the request + :type desired_content_type: str + + :raise: ValidationFailed if the contentType is not accepted + """ + if not request.accept(desired_content_type): raise exceptions.ValidationFailed('Invalid Accept Header') def custom_abort_pecan(errors_info): - """Error_handler for with_schema + """Error_handler for with_schema. Meant to be used with pecan transport. - param errors: a list of validation exceptions + :param errors_info: a list of validation exceptions + :type errors_info: list + + :return: Pecan abort response with http status code 400 + :rtype: pecan.abort """ # TODO(tonytan4ever): gettext support details = dict(errors=[{'message': str(getattr(error, "message", error))} @@ -66,11 +81,19 @@ def with_schema_pecan(request, schema=None, handler=custom_abort_pecan, """Decorate a Pecan/Flask style controller form validation. For an HTTP POST or PUT (RFC2616 unsafe methods) request, the schema is - used to validate the request body. + used to validate the request body + + :param request: Web request + :type request: pecan.request + + :param schema: JSON schema + :type schema: object - :param request: request object - :param schema: A JSON schema. - :param handler: A Function (Error_handler) + :param handler: Error_handler function + :type handler: function + + :return: Decorator to validate the input JSON schema + :rtype: function """ def decorator(f): @@ -104,12 +127,31 @@ def wrapped(*args, **kwargs): def json_matches_service_schema(input_schema): + """Check whether input JSON schema matches with service schema. + + :param input_schema: Input schema dictionary + :type input_schema: Dict[str, bool] + + :return: New function with partial application of + this function and parameters + :rtype: functools.partial + """ return functools.partial( json_matches_service_schema_inner, schema=input_schema) def json_matches_service_schema_inner(request, schema=None): + """Check whether input JSON schema matches with service schema. + + :param request: Web request + :type request: pecan.request + + :param schema: Schema to verify + :type schema: dict + + :raise: ValidationFailed if not a valid JSON string + """ try: data = json.loads(request.body.decode('utf-8')) except ValueError: @@ -119,12 +161,31 @@ def json_matches_service_schema_inner(request, schema=None): def json_matches_flavor_schema(input_schema): + """Check whether input JSON schema matches with flavor schema. + + :param input_schema: Schema to verify + :type input_schema: dict + + :return: New function with partial application of + this function and parameters + :rtype: functools.partial + """ return functools.partial( json_matches_flavor_schema_inner, schema=input_schema) def json_matches_flavor_schema_inner(request, schema=None): + """Check whether input JSON schema matches with flavor schema. + + :param request: Web request + :type request: pecan.request + + :param schema: Schema to verify + :type schema: dict + + :raise: ValidationFailed if not a valid JSON schema + """ try: data = json.loads(request.body.decode('utf-8')) except ValueError: @@ -134,11 +195,27 @@ def json_matches_flavor_schema_inner(request, schema=None): def is_valid_shared_ssl_domain_name(domain_name): + """Check whether the domain name is a valid ssl shared domain. + + :param domain_name: Name of the domain to check + :type domain_name: str + + :return: True if valid, else False + :rtype: bool + """ shared_ssl_domain_regex = '^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]?$' return re.match(shared_ssl_domain_regex, domain_name) is not None def is_valid_tld(domain_name): + """Check whether the domain name is a valid top level dir. + + :param domain_name: Name of the domain to check + :type domain_name: str + + :return: True if valid tld, else False + :rtype: bool + """ try: status = whois.whois(domain_name)['status'] if status is not None or status != '': @@ -158,6 +235,15 @@ def is_valid_tld(domain_name): def is_valid_domain_name(domain_name): + """Check whether the domain name is a valid domain name. + + :param domain_name: Name of the domain to check + :type domain_name: str + + :return: True if valid name, else False + :rtype: bool + """ + # only allow ascii domain_regex = ('^((?=[a-z0-9-]{1,63}\.)[a-z0-9]+' '(-[a-z0-9]+)*\.)+[a-z]{2,63}$') @@ -168,6 +254,14 @@ def is_valid_domain_name(domain_name): def is_valid_domain(domain): + """Check whether the domain is a valid domain. + + :param domain: Domain to check + :type domain: poppy.model.helpers.domain.Domain + + :return: True if valid domain, else False + :rtype: bool + """ domain_name = domain.get('domain') if (domain.get('protocol') == 'https' and domain['certificate'] == u'shared'): @@ -177,6 +271,14 @@ def is_valid_domain(domain): def is_valid_ip_address(ip_address): + """Check whether ip_address is valid + + :param ip_address: IP address to check + :type ip_address: str + + :return: True if valid IP, else False + :rtype: bool + """ ipv4_regex = ('^(((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]' '|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$') # ipv6 is not used to validate origin since akamai does not support ipv6 @@ -199,12 +301,27 @@ def is_valid_ip_address(ip_address): def is_valid_origin(origin): + """Check whether origin is valid + + :param origin: Origin to check + :type origin: poppy.model.helpers.origin.Origin + + :return: True if valid Origin, else False + :rtype: bool + """ return (is_valid_domain_name(origin.get('origin')) or is_valid_ip_address(origin.get('origin'))) @decorators.validation_function def is_valid_project_id(project_id): + """Check whether Project id is valid + + :param project_id: Project id to check + :type project_id: str + + :raise: ValidationFailed if not a valid Project id + """ project_id_regex = '^[a-zA-Z0-9_\\-\\.]{1,256}$' if not re.match(project_id_regex, project_id): raise exceptions.ValidationFailed('Invalid ' @@ -214,6 +331,13 @@ def is_valid_project_id(project_id): @decorators.validation_function def is_valid_akamai_setting(setting): + """Check whether setting is valid Akamai setting + + :param setting: setting to check + :type setting: str + + :raise: ValidationFailed if not a valid setting + """ if setting not in ['san_cert_hostname_limit']: raise exceptions.ValidationFailed( 'Invalid akamai setting : {0}'.format(setting) @@ -222,6 +346,13 @@ def is_valid_akamai_setting(setting): @decorators.validation_function def is_valid_domain_by_name_or_akamai_setting(query): + """Check whether Valid domain by name ot setting. + + :param query: The query to check + :type query: str + + :raise: ValidationFailed if not valid + """ valid_domain = True domain_exc = None valid_setting = True @@ -244,6 +375,14 @@ def is_valid_domain_by_name_or_akamai_setting(query): def is_root_domain(domain): + """Check whether the domain is root domain. + + :param domain: Domain to check + :type domain: poppy.model.helpers.domain.Domain + + :return: True if root domain else False + :rtype: bool + """ domain_name = domain.get('domain') # if the domain contains four or more segments, it a not a root domain @@ -275,6 +414,16 @@ def is_root_domain(domain): def is_valid_service_configuration(service, schema): + """Check whether the service configuration is valid. + + :param service: The service object + :type service: poppy.model.helpers.domain.Domain + + :param schema: JSON schema + :type schema: dict + + :raise: ValidationFailed if not a valid configuration + """ errors_list = list() if schema is not None: errors_list = list( @@ -492,6 +641,13 @@ def is_valid_service_configuration(service, schema): @decorators.validation_function def is_valid_service_id(service_id): + """Check service id is valid or not. + + :param service_id: Service id to check + :type service_id: str + + :raise: ValidationFailed if not a valid service id + """ try: uuid.UUID(service_id) except ValueError: @@ -500,6 +656,13 @@ def is_valid_service_id(service_id): @decorators.validation_function def is_valid_domain_by_name(domain_name): + """Check domain is valid by name + + :param domain_name: Name of the domain + :type domain_name: str + + :raise: ValidationFailed if not a valid domain + """ domain_regex = ('^((?=[a-z0-9-]{1,63}\.)[a-z0-9]+' '(-[a-z0-9]+)*\.)+[a-z]{2,63}$') # allow Punycode @@ -525,6 +688,13 @@ def is_valid_domain_by_name(domain_name): @decorators.validation_function def is_valid_provider_url(request): + """Check Provider url is valid or not. + + :param request: Web request + :type request: pecan.request + + :raise: ValidationFailed if not a valid provider url + """ provider_url = request.GET.get("provider_url", None) if not provider_url: @@ -548,6 +718,16 @@ def is_valid_provider_url(request): def is_valid_flavor_configuration(flavor, schema): + """Check Flavor configuration is valid or not. + + :param flavor: Flavor details + :type flavor: poppy.model.flavor.Flavor + + :param schema: JSON schema + :type schema: dict + + :raise: ValidationFailed if not a valid flavor configuration + """ if schema is not None: errors_list = list( jsonschema.Draft3Validator(schema).iter_errors(flavor)) @@ -571,6 +751,13 @@ def is_valid_flavor_id(flavor_id): @decorators.validation_function def is_valid_analytics_request(request): + """Check Analytics request is valid or not. + + :param request: Web request + :type request: pecan.request + + :raise: ValidationFailed if not a valid analytics request + """ default_end_time = datetime.datetime.utcnow() default_start_time = (datetime.datetime.utcnow() - datetime.timedelta(days=1)) @@ -630,6 +817,13 @@ def is_valid_analytics_request(request): @decorators.validation_function def is_valid_service_status(request): + """Check service status is valid or not. + + :param request: Web request + :type request: pecan.request + + :raise: ValidationFailed if not a valid service status + """ status = request.GET.get('status', "") # NOTE(TheSriram): The statuses listed below are the currently @@ -655,6 +849,13 @@ def is_valid_service_status(request): @decorators.validation_function def is_valid_certificate_status(request): + """Check certificate status is valid or not. + + :param request: Web request + :type request: pecan.request + + :raise: ValidationFailed if not a valid certificate status + """ status = request.GET.get('status', "") # NOTE(TheSriram): The statuses listed below are the currently @@ -679,6 +880,14 @@ def is_valid_certificate_status(request): def abort_with_message(error_info): + """Pecan's abort web response utility. + + :param error_info: Error information + :type error_info: dict + + :return: Pecan web response with http status code 400 + :rtype: pecan.abort + """ pecan.abort(400, detail=util.help_escape( getattr(error_info, "message", "")), headers={'Content-Type': "application/json"}) diff --git a/poppy/transport/validators/schema_base.py b/poppy/transport/validators/schema_base.py index dd48621b..e30aeaf5 100644 --- a/poppy/transport/validators/schema_base.py +++ b/poppy/transport/validators/schema_base.py @@ -23,22 +23,22 @@ class SchemaBase(object): @classmethod def get_schema(cls, resource_name, operation): - """Returns the schema for an operation + """Returns the schema for an operation. :param resource_name: Operation for which resource need - to be validated. - :type operation: `six.text_type` + to be validated + :type resource_name: str :param operation: Operation for which params need - to be validated. + to be validated. :type operation: `six.text_type` - :returns: Operation's schema + :return: Operation's schema :rtype: dict - :raises: `errors.InvalidResource` if the resource - does not exist and `errors.InvalidOperation` if the operation - does not exist + :raise: + InvalidResource if the resource does not exist, + InvalidOperation if the operation does not exist """ try: resource_schemas = cls.schema[resource_name] diff --git a/poppy/transport/validators/schemas/background_jobs.py b/poppy/transport/validators/schemas/background_jobs.py index 426ae7b0..983370ee 100644 --- a/poppy/transport/validators/schemas/background_jobs.py +++ b/poppy/transport/validators/schemas/background_jobs.py @@ -18,7 +18,7 @@ class BackgroundJobSchema(schema_base.SchemaBase): - """JSON Schema validation for /admin/provider/akamai/background_job""" + """JSON Schema validation for /admin/provider/akamai/background_job.""" schema = { 'background_jobs': { diff --git a/poppy/transport/validators/schemas/domain_migration.py b/poppy/transport/validators/schemas/domain_migration.py index be397fdb..cafe5778 100644 --- a/poppy/transport/validators/schemas/domain_migration.py +++ b/poppy/transport/validators/schemas/domain_migration.py @@ -18,7 +18,7 @@ class DomainMigrationServiceSchema(schema_base.SchemaBase): - '''JSON Schema validation for /admin/provider/akamai/service''' + """JSON Schema validation for /admin/provider/akamai/service.""" schema = { 'domain_migration': { diff --git a/poppy/transport/validators/schemas/service.py b/poppy/transport/validators/schemas/service.py index 237cb904..fb91bad4 100644 --- a/poppy/transport/validators/schemas/service.py +++ b/poppy/transport/validators/schemas/service.py @@ -20,7 +20,7 @@ class ServiceSchema(schema_base.SchemaBase): - '''JSON Schema validation for /service.''' + """JSON Schema validation for /service.""" schema = { 'service': { diff --git a/poppy/transport/validators/schemas/service_limit.py b/poppy/transport/validators/schemas/service_limit.py index aecc7b4c..b9ae7582 100644 --- a/poppy/transport/validators/schemas/service_limit.py +++ b/poppy/transport/validators/schemas/service_limit.py @@ -18,7 +18,7 @@ class ServiceLimitSchema(schema_base.SchemaBase): - '''JSON Schema validation for /admin/limits/{project_id}.''' + """JSON Schema validation for /admin/limits/{project_id}.""" schema = { 'service_limit': { diff --git a/poppy/transport/validators/schemas/ssl_certificate.py b/poppy/transport/validators/schemas/ssl_certificate.py index 533c7cfa..4f9683db 100644 --- a/poppy/transport/validators/schemas/ssl_certificate.py +++ b/poppy/transport/validators/schemas/ssl_certificate.py @@ -20,7 +20,7 @@ class SSLCertificateSchema(schema_base.SchemaBase): - '''JSON Schema validation for /ssl_certificate.''' + """JSON Schema validation for /ssl_certificate.""" schema = { 'ssl_certificate': { diff --git a/poppy/transport/validators/stoplight/helpers.py b/poppy/transport/validators/stoplight/helpers.py index e013d493..6efdf35c 100644 --- a/poppy/transport/validators/stoplight/helpers.py +++ b/poppy/transport/validators/stoplight/helpers.py @@ -19,6 +19,6 @@ def pecan_getter(parm): - """pecan getter.""" + """pecan getter""" pecan_module = __import__('pecan', globals(), locals(), ['request']) return getattr(pecan_module, 'request') diff --git a/poppy/transport/validators/stoplight/rule.py b/poppy/transport/validators/stoplight/rule.py index f9bef8a1..40d71567 100644 --- a/poppy/transport/validators/stoplight/rule.py +++ b/poppy/transport/validators/stoplight/rule.py @@ -28,9 +28,17 @@ def Rule(vfunc, on_error, getter=None): to happen. :param vfunc: The function used to validate this param + :type vfunc: function + :param on_error: The function to call when an error is detected - :param value_src: The source from which the value can be + :type on_error: function + + :param getter: The source from which the value can be This function should take a value as a field name - as a single param. + as a single param + :type getter: function + + :return: A Validation rule object to validate + :rtype: ValidationRule """ return ValidationRule(vfunc=vfunc, errfunc=on_error, getter=getter) From 2563a76a0f0e87beff8515ac96e86d0753a28997 Mon Sep 17 00:00:00 2001 From: coolyuvee Date: Wed, 22 Aug 2018 20:17:51 +0530 Subject: [PATCH 3/6] [docstring] Update docstring for default manager --- poppy/manager/default/analytics.py | 26 ++- poppy/manager/default/background_job.py | 47 ++-- poppy/manager/default/flavors.py | 13 +- poppy/manager/default/health.py | 53 +++-- poppy/manager/default/services.py | 273 ++++++++--------------- poppy/manager/default/ssl_certificate.py | 124 ++++------ 6 files changed, 226 insertions(+), 310 deletions(-) diff --git a/poppy/manager/default/analytics.py b/poppy/manager/default/analytics.py index 5c8fd5d2..17cea45a 100644 --- a/poppy/manager/default/analytics.py +++ b/poppy/manager/default/analytics.py @@ -23,19 +23,29 @@ class AnalyticsController(base.AnalyticsController): def get_metrics_by_domain(self, project_id, domain_name, **extras): - """Get metrics information by domain. + """Gets metrics information for a domain. - :param project_id: The project id - :type project_id: int + The below keys are expected in **extras along with their values. + - metricType + - startTime + - endTime + - metrics_controller - :param domain_name: The domain name - :type domain_name: str + Example return: + ``{'provider': '', 'flavor':'', 'domain':'', 'metricType': {}}`` - :param extras: kwargs - :type extras: dict + :param unicode project_id: The project id + :param unicode domain_name: The domain name + :param dict extras: Additional arguments to supply - :return: Metrics captured for this domain + :return: A dictionary containing domain name and its metrics :rtype: dict + + :raises ServiceNotFound: if domain not present under project id + :raises ServiceProviderDetailsNotFound: if no provider details + exists for the service id and project id combo + :raises ProviderDetailsIncomplete: if provider is not found + for the domain """ storage_controller = self.storage_controller diff --git a/poppy/manager/default/background_job.py b/poppy/manager/default/background_job.py index fbb74769..42639915 100644 --- a/poppy/manager/default/background_job.py +++ b/poppy/manager/default/background_job.py @@ -50,20 +50,28 @@ def __init__(self, manager): self.service_storage = self._driver.storage.services_controller def post_job(self, job_type, kwargs): - """Post job to the TaskFlow engine. + """Submit a task to the TaskFlow engine. - :param job_type: Type of the job. Valid types are : . - 'akamai_check_and_update_cert_status' or - 'akamai_update_papi_property_for_mod_san' or - 'akamai_update_papi_property_for_mod_sni' - :type job_type: str + Iterate over each certificate in san mapping queue. + For each job type, build run_list and ignore_list of certificates + based on some logic while submitting jobs for those certificates + belonging to the run_list. San mapping queue may not be intact depending + on the job type. Return both run_list and ignore_list. - :param kwargs: Additional arguments for the task engine - :type kwargs: dict + Valid job_type includes. + - akamai_check_and_update_cert_status + - akamai_update_papi_property_for_mod_san + - akamai_update_papi_property_for_mod_sni + + Example return : ``([run_list],[ignore_list])`` + + :param str job_type: Type of the job + :param dict kwargs: Additional arguments :return: Tuple of run_list and ignore_list :rtype: (list, list) - :raise: NotImplementedError if the job_type is not supported + + :raises NotImplementedError: if the job_type is not supported """ queue_data = [] @@ -460,7 +468,7 @@ def post_job(self, job_type, kwargs): ) def get_san_mapping_list(self): - """Get items from SAN mapping list. + """Get items from SAN mapping queue. :return: List of SAN mapping items :rtype: list @@ -475,13 +483,11 @@ def get_san_mapping_list(self): return res def put_san_mapping_list(self, san_mapping_list): - """Populate Akamai's san_mapping_queue with san_mapping_list. + """Populate san_mapping_queue with new data. - :param san_mapping_list: The items to put into - Akamai's san_mapping_queue - :type san_mapping_list: list + :param list san_mapping_list: The new data - :return: Tuple of old and new items in the Akamai san_mapping_queue + :return: Tuple of old and new items in the san_mapping_queue :rtype: (list, list) """ new_queue_data = [json.dumps(r) for r in san_mapping_list] @@ -502,7 +508,16 @@ def put_san_mapping_list(self, san_mapping_list): return res, deleted def delete_http_policy(self): - """Delete old http policies from Akamai's http_policy_queue. + """Submit task to delete Provider's(Akamai's) obsolete http policies. + + Get all the http_policies available from the provider's policy queue + by consuming the queue (meaning queue will be empty after consume). For + each consumed policy, get certificates by domain. If the certificates are + empty for a domain, add that policy to ignore_list. If the certificate + status is not yet deployed, then add that policy to ignore_list and put it + back to policy queue so that it can be consumed in a later trial. Put rest + of the policies into run_list and submit a task to delete these policies. + Return ignored_list and run_list. :return: Tuple of deleted and ignored policies :rtype: (list, list) diff --git a/poppy/manager/default/flavors.py b/poppy/manager/default/flavors.py index f4ae2efb..26fa997d 100644 --- a/poppy/manager/default/flavors.py +++ b/poppy/manager/default/flavors.py @@ -26,7 +26,7 @@ def __init__(self, manager): def list(self): """Get list of the supported flavors. - :return: List of the supported flavors. + :return: List of the supported flavors :rtype: list """ return self.storage.list() @@ -34,8 +34,7 @@ def list(self): def get(self, flavor_id): """Return flavor details. - :param flavor_id: The flavor id - :type flavor_id: str + :param unicode flavor_id: The flavor id :return: Flavor details :rtype: poppy.model.flavor.Flavor @@ -52,7 +51,7 @@ def add(self, new_flavor): provider details :rtype: poppy.model.flavor.Flavor - :raise: ValueError if the flavor already exists + :raises ValueError: if the flavor already exists """ provider_list = self.driver.conf[bootstrap._DRIVER_GROUP].providers @@ -68,10 +67,6 @@ def add(self, new_flavor): def delete(self, flavor_id): """Delete an existing flavor. - :param flavor_id: The flavor id to delete - :type flavor_id: str - - :return: None - :rtype: None + :param unicode flavor_id: The flavor id to delete """ return self.storage.delete(flavor_id) diff --git a/poppy/manager/default/health.py b/poppy/manager/default/health.py index 279f4c45..a803d129 100644 --- a/poppy/manager/default/health.py +++ b/poppy/manager/default/health.py @@ -23,9 +23,11 @@ def __init__(self, manager): super(DefaultHealthController, self).__init__(manager) def health(self): - """Return the health of storage and providers. + """Returns health status of all the modules. - :return: Tuple with is_alive and health_map info + :return: A health_map dict with health information about dns, providers, + distributed_task and storage modules along with is_alive indicator( + ``True if all the modules are alive, False if one of the module is not alive.``) :rtype:(bool, dict) """ @@ -54,9 +56,14 @@ def health(self): return is_alive, health_map def ping_check(self): - """Get the health_map and is_alive info. + """Get health for storage and distributed_task. - :return: Tuple with is_alive and health_map info + Get the health_map dict and is_alive info for distributed_task + and storage modules. + + :return: A health_map dict with health information about distributed_task + and storage modules along with is_alive indicator( + ``True if all the modules are alive, False if one of the module is not alive.``) :rtype:(bool, dict) """ health_map = {} @@ -84,27 +91,23 @@ def ping_check(self): return health_map, is_alive def is_provider_alive(self, provider_name): - """Returns the health of provider. - - :param provider_name: The name of the provider - :type provider_name: str + """Check a provide is alive or not. - :return: Whether the provider is alive + :param str provider_name: The name of the provider + :return: True if alive, otherwise False :rtype: bool """ return self._providers[provider_name].obj.is_alive() def is_distributed_task_alive(self, distributed_task_name): - """Returns the health of distributed_task. - - :param distributed_task_name: The name of the dist task - :type distributed_task_name: str + """Check distributed_task is alive or not. - :return: Whether the distributed task is alive + :param str distributed_task_name: The name of the dist task + :return: True if alive, otherwise False :rtype: bool - :raise: KeyError if the distributed_task_name is not same as vendor name + :raises KeyError: if the distributed_task_name is not same as vendor name """ if distributed_task_name == self._distributed_task.vendor_name.lower(): @@ -113,15 +116,13 @@ def is_distributed_task_alive(self, distributed_task_name): raise KeyError def is_storage_alive(self, storage_name): - """Returns the health of storage. + """Check storage is alive or not. - :param storage_name: The name of the storage - :type storage_name: str - - :return: Whether storage is alive + :param str storage_name: The name of the storage + :return: True if alive, otherwise False :rtype: bool - :raise: KeyError if storage_name is not same as underlying storage + :raises KeyError: if storage_name is not same as underlying storage """ if storage_name == self._storage.storage_name.lower(): @@ -130,15 +131,13 @@ def is_storage_alive(self, storage_name): raise KeyError def is_dns_alive(self, dns_name): - """Returns the health of DNS Provider. - - :param dns_name: The name of the DNS - :type dns_name: str + """Check DNS is alive or not. - :return: Whether the DNS is alive + :param str dns_name: The name of the DNS + :return: True if alive, otherwise False :rtype: bool - :raise: KeyError if dns_name is not same as underlying DNS + :raise KeyError: if dns_name is not same as underlying DNS """ if dns_name == self._dns.dns_name.lower(): diff --git a/poppy/manager/default/services.py b/poppy/manager/default/services.py index 2beba2f5..8d809186 100644 --- a/poppy/manager/default/services.py +++ b/poppy/manager/default/services.py @@ -65,7 +65,7 @@ def __init__(self, manager): def determine_sleep_times(self): """Calculate sleep times used in rebind. - :return: Time in seconds to sleep + :return: Time to sleep :rtype: list """ @@ -79,18 +79,14 @@ def determine_sleep_times(self): return backoff def _get_provider_details(self, project_id, service_id): - """Return Provider details. - - :param project_id: The project id - :type project_id: int - - :param service_id: The service id - :type service_id: str + """Return Provider details for given project and service ids. + :param unicode project_id: The project id + :param unicode service_id: The service id :return: Provider details for the given project and service ids :rtype: Dict[str, poppy.model.helpers.provider_details.ProviderDetail] - :raise: LookupError if the service does not exists + :raises LookupError: if the service does not exists """ try: provider_details = self.storage_controller.get_provider_details( @@ -102,15 +98,13 @@ def _get_provider_details(self, project_id, service_id): return provider_details def get_service_by_domain_name(self, domain_name): - """Return service details for the domain. - - :param domain_name: The domain name - :type domain_name: str + """Return service details for given domain name. + :param unicode domain_name: The domain name :return: The service details :rtype: poppy.model.service.Service - :raise: LookupError if the domain does not exists + :raises LookupError: if the domain does not exists or service details can not be found for the domain """ try: @@ -127,11 +121,9 @@ def get_service_by_domain_name(self, domain_name): return service_details def get_services_by_status(self, status): - """Return the services by status - - :param status: The status of the service - :type status: str + """Return the services by status. + :param unicode status: The status of the service :return: List of services :rtype: list """ @@ -142,11 +134,9 @@ def get_services_by_status(self, status): return services_project_ids def get_domains_by_provider_url(self, provider_url): - """Return domains by Provider url - - :param provider_url: The provider URL - :type provider_url: str + """Return domains by Provider url. + :param unicode provider_url: The provider URL :return: List of domains :rtype: list """ @@ -157,16 +147,13 @@ def get_domains_by_provider_url(self, provider_url): return domains def _append_defaults(self, service_json, operation='create'): - """ Add defaults to the service. + """Add defaults to the service. If the service rules are None or Empty, add default rule. If the operation is 'create', add default ttl and cache. - :param service_json: Service details in JSON form - :type service_json: dict - - :param operation: (Default 'create') Name of operation - :type operation: str + :param dict service_json: Service details in JSON form + :param str operation: (Default 'create') Name of operation """ # default origin rule for origin in service_json.get('origins', []): @@ -205,16 +192,11 @@ def _append_defaults(self, service_json, operation='create'): caching_entry['rules'].append(default_rule.to_dict()) def get_services(self, project_id, marker=None, limit=None): - """Get a list of services. - - :param project_id: The project id - :type project_id: int + """Get list of services. - :param marker: The marker indicator - :type marker: int - - :param limit: No of services to fetch - :type limit: int + :param unicode project_id: The project id + :param int marker: The marker indicator + :param int limit: No of services to fetch :return: List of services :rtype: list(poppy.model.service.Service) @@ -224,11 +206,8 @@ def get_services(self, project_id, marker=None, limit=None): def get_service(self, project_id, service_id): """Get a service object. - :param project_id: The project id - :type project_id: int - - :param service_id: The service id - :type service_id: str + :param unicode project_id: The project id + :param unicode service_id: The service id :return: The service object :rtype: poppy.model.services.Service @@ -238,21 +217,15 @@ def get_service(self, project_id, service_id): def create_service(self, project_id, auth_token, service_json): """Create a new service. - :param project_id: The project id - :type project_id: int - - :param auth_token: Token for authorization - :type auth_token: str - - :param service_json: The service details to create - :type service_json: dict + :param unicode project_id: The project id + :param unicode auth_token: Token for authorization + :param dict service_json: The service details to create :return: The new service object created :rtype: poppy.model.service.Service - :raise: - LookupError if the flavor does not exists, - ValueError if shared domain retry ops has problem + :raises LookupError: if the flavor does not exists + :raises ValueError: if shared domain retry ops has problem """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) @@ -331,28 +304,17 @@ def update_service(self, project_id, service_id, auth_token, service_updates, force_update=False): """Update a service. - :param project_id: The project id - :type project_id: int - - :param service_id: The service id - :type service_id: str - - :param auth_token: Token for authorization - :type auth_token: str - - :param service_updates: The service details - :type service_updates: dict - - :param force_update: (Default False) Forceful update \ + :param unicode project_id: The project id + :param unicode service_id: The service id + :param unicode auth_token: Token for authorization + :param dict service_updates: The service details + :param bool force_update: (Default False) Forceful update \ of the service - :type force_update: bool - - :raise: - ServiceNotFound if service not found, - ServiceStatusDisabled if service id disabled, - ServiceStatusNeitherDeployedNorFailed - if service is neither deployed not failed + :raises ServiceNotFound: if service not found + :raises ServiceStatusDisabled: if service is disabled + :raises ServiceStatusNeitherDeployedNorFailed: if force_update + is False and service is neither deployed nor failed """ # get the current service object try: @@ -571,13 +533,10 @@ def update_service(self, project_id, service_id, return def services_limit(self, project_id, limit): - """Set limit for no of services. + """Set limit for number of services. - :param project_id: The project id - :type project_id: int - - :param limit: No of services - :type limit: int + :param unicode project_id: The project id + :param int limit: No of services """ self.storage_controller.set_service_limit( project_id=project_id, @@ -587,17 +546,10 @@ def set_service_provider_details(self, project_id, service_id, auth_token, status): """Set provider details for service. - :param project_id: The project id - :type project_id: int - - :param service_id: The service id - :type service_id: str - - :param auth_token: Token for authorization - :type auth_token: str - - :param status: Status of the service - :type status: str + :param unicode project_id: The project id + :param unicode service_id: The service id + :param unicode auth_token: Token for authorization + :param str status: Status of the service :return: Http status code 201 or 202 :rtype: int @@ -624,8 +576,7 @@ def set_service_provider_details(self, project_id, service_id, def get_services_limit(self, project_id): """Get the currently set limit on services. - :param project_id: The project id - :type project_id: int + :param unicode project_id: The project id :return: The dict with limit's value :rtype: dict[str, int] @@ -640,14 +591,14 @@ def get_services_limit(self, project_id): def _action_per_service_obj(self, project_id, action, service_obj): """Perform an action on the service. - :param project_id: The project id - :type project_id: int - - :param action: Choose from 'delete', 'enable', 'disable' - :type action: str + List of actions include + - delete + - enable + - disable - :param service_obj: The service object - :type service_obj: dict + :param unicode project_id: The project id + :param str action: Action needs to be done + :param dict service_obj: The service object """ kwargs = { @@ -687,14 +638,14 @@ def _action_per_service_obj(self, project_id, action, service_obj): def services_action(self, project_id, action, domain=None): """perform action on services present in this domain. - :param project_id: The project id - :type project_id: int + List of actions include + - delete + - enable + - disable - :param action: Choose from 'delete', 'enable', 'disable' - :type action: str - - :param domain: The name of the domain - :type domain: str + :param unicode project_id: The project id + :param str action: Action needs to done + :param unicode domain: The name of the domain """ # list all the services of for this project_id @@ -729,13 +680,13 @@ def services_action(self, project_id, action, domain=None): return def delete_service(self, project_id, service_id): - """Delete a service. + """Submit a task to delete a service. - :param project_id: The project id - :type project_id: int + Before deletion, update each of the provider's status in + service with 'delete_in_progress' - :param service_id: The service id - :type service_id: str + :param unicode project_id: The project id + :param unicode service_id: The service id """ service_obj = self.storage_controller.get_service( project_id, service_id) @@ -771,22 +722,19 @@ def delete_service(self, project_id, service_id): def purge(self, project_id, service_id, hard=False, purge_url=None): """Purge a service. - If purge_url is none, all content of this service will be purge. - - :param project_id: The project id - :type project_id: int + If purge_url is none, all content of this service will be purged. - :param service_id: The service id - :type service_id: str + If ``hard`` is False, status of the each provider details + will be set to 'update_in_progress' - :param hard: (Default False) Mark 'update_in_progress' \ + :param unicode project_id: The project id + :param unicode service_id: The service id + :param bool hard: (Default False) Mark 'update_in_progress' \ if set to True - :type hard: bool + :param unicode purge_url: The purge url - :param purge_url: The purge url - :type purge_url: str - - :raise: ServiceStatusNotDeployed if service not yet deployed + :raises ServiceStatusNotDeployed: if service not yet deployed + :raises LookupError: if the service does not exists """ try: service_obj = self.storage_controller.get_service( @@ -837,16 +785,13 @@ def purge(self, project_id, service_id, hard=False, purge_url=None): def _generate_shared_ssl_domain(self, domain_name, store): """Generate shared ssl domain. - :param domain_name: The domain name - :type domain_name: str - - :param store: The store object - :type store: object + :param unicode domain_name: The domain name + :param unicode store: uuid :return: Shared ssl domain - :rtype: str + :rtype: unicode - :raise: SharedShardsExhausted if domain has already been taken + :raises SharedShardsExhausted: if domain has already been taken """ try: if not hasattr(self, store): @@ -877,17 +822,12 @@ def _generate_shared_ssl_domain(self, domain_name, store): def _pick_shared_ssl_domain(self, domain, service_id, store): """Select an available shared ssl domain. - :param domain: The domain name - :type domain: str - - :param service_id: The service id - :type service_id: str - - :param store: The store object - :type store: object + :param unicode domain: The domain name + :param unicode service_id: The service id + :param unicode store: uuid :return: The available domain - :rtype: str + :rtype: unicode """ shared_ssl_domain = self._generate_shared_ssl_domain( domain, @@ -903,19 +843,14 @@ def _pick_shared_ssl_domain(self, domain, service_id, store): def _shard_retry(self, project_id, service_obj, store=None): """Retry to get available ssl domain and add to service. - :param project_id: The project id - :type project_id: int - - :param service_obj: the service object - :type service_obj: dict - - :param store: The store object - :type store: object + :param unicode project_id: The project id + :param dict service_obj: the service object + :param unicode store: The store object :return: The service object with shared ssl domain added :rtype: dict - :raise: ValueError + :raises ValueError: """ # deal with shared ssl domains try: @@ -940,22 +875,20 @@ def _shard_retry(self, project_id, service_obj, store=None): def migrate_domain(self, project_id, service_id, domain_name, new_cert, cert_status='deployed'): - """Migrate domain - - :param project_id: The project id - :type project_id: int + """Migrate domain. - :param service_id: The service id - :type service_id: str + Domain migration includes + - Set cert_status to all the provider's domain_certification + - Update the all the certificates with cert_status + - Update the access_url of all the providers with new_cert - :param domain_name: The domain name - :type domain_name: str + :param unicode project_id: The project id + :param unicode service_id: The service id + :param unicode domain_name: The domain name + :param unicode new_cert: The new certificate + :param unicode cert_status: (Default 'deployed')Certificate status - :param new_cert: The new certificate - :type new_cert: str - - :param cert_status: (Default 'deployed')Certificate status - :type cert_status: str + :raises ServiceNotFound: if the service not found """ dns_controller = self.dns_controller storage_controller = self.storage_controller @@ -1026,11 +959,8 @@ def migrate_domain(self, project_id, service_id, domain_name, new_cert, def _detect_upgrade_http_to_https(self, old_domains, new_domain): """Determine whether upgrade is required from http to https. - :param old_domains: List of old domains - :type old_domains: list - - :param new_domain: New domain - :type new_domain: str + :param list old_domains: List of old domains + :param unicode new_domain: New domain :return: Boolean value indicating upgrade is required or not :rtype: bool @@ -1050,14 +980,9 @@ def update_access_url_service( self, project_id, service_id, access_url_changes): """Update access url of a service. - :param project_id: The project id - :type project_id: int - - :param service_id: The service id - :type service_id: str - - :param access_url_changes: The changes to update the access url - :type access_url_changes: dict + :param unicode project_id: The project id + :param unicode service_id: The service id + :param dict access_url_changes: The changes to update the access url :return: Whether or not the access url details updated :rtype: bool diff --git a/poppy/manager/default/ssl_certificate.py b/poppy/manager/default/ssl_certificate.py index 0aef4c2a..6db2d011 100644 --- a/poppy/manager/default/ssl_certificate.py +++ b/poppy/manager/default/ssl_certificate.py @@ -47,20 +47,15 @@ def create_ssl_certificate( self, project_id, cert_obj, https_upgrade=False): """Create a new certificate. - :param project_id: The project id - :type project_id: int - - :param cert_obj: The certificate details - :type cert_obj: dict - - :param https_upgrade: (Default False) + :param unicode project_id: The project id + :param dict cert_obj: The certificate details + :param bool https_upgrade: (Default False) Whether ot not to be upgraded to https - :type https_upgrade: bool :return: kwargs with wrapped arguments :rtype: dict - :raise: ValueError if if domain is not a valid non-root domain + :raises ValueError: if if domain is not a valid non-root domain """ if (not validators.is_valid_domain_name(cert_obj.domain_name)) or \ @@ -105,19 +100,14 @@ def create_ssl_certificate( def delete_ssl_certificate(self, project_id, domain_name, cert_type): """Delete an SSL certificate. - :param project_id: The project id - :type project_id: int - - :param domain_name: The name of the domain - :type domain_name: str - - :param cert_type: Type of the certificate to be deleted - :type cert_type: str + :param unicode project_id: The project id + :param unicode domain_name: The name of the domain + :param unicode cert_type: Type of the certificate to be deleted - :return: kwargs with cert, provider and other original inputs + :return: dict with cert, provider and other original inputs :rtype: dict - :raise: LookupError if the flavor is not found + :raises LookupError: if the flavor is not found """ cert_obj = self.storage.get_certs_by_domain( domain_name, cert_type=cert_type) @@ -145,16 +135,13 @@ def delete_ssl_certificate(self, project_id, domain_name, cert_type): def get_certs_info_by_domain(self, domain_name, project_id): """Get the certificates information by domain. - :param domain_name: Name of the domain - :type domain_name: str - - :param project_id: The project id - :type project_id: int + :param unicode domain_name: Name of the domain + :param unicode project_id: The project id :return: SSLCertificate object :rtype: poppy.model.ssl_certificate.SSLCertificate - :raise: ValueError if cert info is not found + :raises ValueError: if cert info is not found """ try: certs_info = self.storage.get_certs_by_domain( @@ -170,9 +157,9 @@ def get_certs_info_by_domain(self, domain_name, project_id): raise e def get_san_retry_list(self): - """Get items from san_mapping_queue for Akamai provider. + """Get items from san_mapping_queue of provider. - :return: san_mapping_queue of Akamai provider + :return: san_mapping_queue of provider :rtype: dict """ if 'akamai' in self._driver.providers: @@ -193,16 +180,14 @@ def get_san_retry_list(self): ] def update_san_retry_list(self, queue_data_list): - """Update Akamai mod_san_queue with new items. - - :param queue_data_list: The list of new items to update to. - :type queue_data_list: list + """Update provider's mod_san_queue with new items. - :return: tuple of lists of old and new items in Akamai mod_san_queue + :param list queue_data_list: The list of new items to update with. + :return: tuple of lists of old and new items in mod_san_queue :rtype: (list, list) - :raise: - LookupError if domain does not exists, - ValueError if certificate already exists + + :raises LookupError: if domain does not exists + :raises ValueError: if certificate already exists """ for r in queue_data_list: service_obj = self.service_storage\ @@ -242,12 +227,18 @@ def update_san_retry_list(self, queue_data_list): return res, deleted def rerun_san_retry_list(self): - """Rerun the mod_san_queue. + """Retry the mod san queue. - :return: Tuple of lists containing the run items and ignored items + Obtain the items from mod san queue and build run_list and + ignore_list by iterating through this retry queue. For each + certificate in the run_list, submit a task to recreate the + ssl certificate. + + :return: list of certificates that were recreated and list of + certificates that were ignored :rtype: (list, list) - :raise: LookupError if the flavor is not found + :raises LookupError: if the flavor is not found """ run_list = [] ignore_list = [] @@ -381,13 +372,12 @@ def rerun_san_retry_list(self): def get_san_cert_configuration(self, san_cert_name): """Get SAN certificate configuration. - :param san_cert_name: Name of the SAN certificate - :type san_cert_name: str + :param unicode san_cert_name: Name of the SAN certificate :return: Configuration of the certificate :rtype: dict - :raise: ValueError if not a valid certificate name + :raises ValueError: if not a valid certificate name """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj @@ -407,18 +397,14 @@ def get_san_cert_configuration(self, san_cert_name): def update_san_cert_configuration(self, san_cert_name, new_cert_config): """Update configuration of SAN certificate. - :param san_cert_name: Name of the SAN certificate - :type san_cert_name: str - - :param new_cert_config: New configuration to update with. - :type new_cert_config: dict + :param unicode san_cert_name: Name of the SAN certificate + :param dict new_cert_config: New configuration to update with. :return: The updated values of the certificate :rtype: dict - :raise: - ValueError if not a valid certificate, - RuntimeError if unable to call SPS api + :raises ValueError: if not a valid certificate + :raises RuntimeError: if unable to call SPS api """ if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj @@ -457,9 +443,7 @@ def update_san_cert_configuration(self, san_cert_name, new_cert_config): def get_sni_cert_configuration(self, cert_name): """Get configuration for SNI certificates. - :param cert_name: The name of the SNI certificate - :type cert_name: str - + :param unicode cert_name: The name of the SNI certificate :return: SNI certificate configuration :rtype: dict """ @@ -476,11 +460,8 @@ def get_sni_cert_configuration(self, cert_name): def update_sni_cert_configuration(self, cert_name, new_cert_config): """Update SNI certificate's configuration. - :param cert_name: SNI certificate name - :type cert_name: str - - :param new_cert_config: New configuration to update with - :type new_cert_config: dict + :param unicode cert_name: SNI certificate name + :param dict new_cert_config: New configuration to update with :return: The updated configuration of the certificate :rtype: dict @@ -501,7 +482,7 @@ def update_sni_cert_configuration(self, cert_name, new_cert_config): def get_san_cert_hostname_limit(self): """Get SAN certificate hostname limit. - :return: Limit for the SAN cert hostnames + :return: Limit for the SAN cert host names :rtype: int """ if 'akamai' in self._driver.providers: @@ -518,13 +499,10 @@ def get_san_cert_hostname_limit(self): def _validate_sni_cert_name(provider_driver, cert_name): """Check whether a valid SNI certificate name. - :param provider_driver: The provider driver - :type provider_driver: object - - :param cert_name: The name of the SNI certificate - :type cert_name: str + :param object provider_driver: The provider driver + :param unicode cert_name: The name of the SNI certificate - :raise: ValueError if not a valid SNI certificate name + :raises ValueError: if not a valid SNI certificate name """ if cert_name not in provider_driver.sni_cert_cnames: raise ValueError( @@ -535,8 +513,7 @@ def _validate_sni_cert_name(provider_driver, cert_name): def set_san_cert_hostname_limit(self, request_json): """Set hostname limit for SAN certificate. - :param request_json: Dict containing the required limit - :type request_json: dict + :param dict request_json: Dict containing the required limit :return: The updated limit value :rtype: int @@ -563,8 +540,7 @@ def set_san_cert_hostname_limit(self, request_json): def get_certs_by_status(self, status): """Get certificates by their status - :param status: Status of the certficate to look for - :type status: str + :param unicode status: Status of the certificate to look for :return: Certificate details :rtype: dict @@ -576,15 +552,11 @@ def get_certs_by_status(self, status): def update_certificate_status(self, domain_name, certificate_updates): """Update a certificate status. - :param domain_name: The domain name - :type domain_name: str - - :param certificate_updates: New status of the cert to update with - :type certificate_updates: dict + :param unicode domain_name: The domain name + :param dict certificate_updates: New status of the cert to update with - :raise: - ValueError if no certificate found, - CertificateStatusUpdateError if unable to update certificate status + :raises ValueError: if no certificate found + :raises CertificateStatusUpdateError: if unable to update certificate status """ certificate_old = self.storage.get_certs_by_domain(domain_name) if not certificate_old: From e9b3c74cc7e981133968721843eca6e82c394ff8 Mon Sep 17 00:00:00 2001 From: coolyuvee Date: Fri, 24 Aug 2018 04:18:39 +0530 Subject: [PATCH 4/6] tmp commit --- doc/source/poppy.manager.rst | 11 ++++ poppy/manager/default/analytics.py | 2 +- poppy/manager/default/background_job.py | 31 ++++++++--- poppy/manager/default/health.py | 43 +++++++++------ poppy/manager/default/services.py | 73 ++++++++++++++++++------- 5 files changed, 111 insertions(+), 49 deletions(-) diff --git a/doc/source/poppy.manager.rst b/doc/source/poppy.manager.rst index b504c4ca..b9dfd717 100644 --- a/doc/source/poppy.manager.rst +++ b/doc/source/poppy.manager.rst @@ -65,3 +65,14 @@ :members: :undoc-members: :show-inheritance: + + +.. automodule:: poppy.manager.default.background_job + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: poppy.manager.default.health + :members: + :undoc-members: + :show-inheritance: diff --git a/poppy/manager/default/analytics.py b/poppy/manager/default/analytics.py index 17cea45a..0532b475 100644 --- a/poppy/manager/default/analytics.py +++ b/poppy/manager/default/analytics.py @@ -25,7 +25,7 @@ class AnalyticsController(base.AnalyticsController): def get_metrics_by_domain(self, project_id, domain_name, **extras): """Gets metrics information for a domain. - The below keys are expected in **extras along with their values. + The below keys are expected in ``extras`` along with their values. - metricType - startTime - endTime diff --git a/poppy/manager/default/background_job.py b/poppy/manager/default/background_job.py index 42639915..c94cdf06 100644 --- a/poppy/manager/default/background_job.py +++ b/poppy/manager/default/background_job.py @@ -50,10 +50,10 @@ def __init__(self, manager): self.service_storage = self._driver.storage.services_controller def post_job(self, job_type, kwargs): - """Submit a task to the TaskFlow engine. + """Submit a job to the Job board. Iterate over each certificate in san mapping queue. - For each job type, build run_list and ignore_list of certificates + For each job type, build ``run_list`` and ``ignore_list`` of certificates based on some logic while submitting jobs for those certificates belonging to the run_list. San mapping queue may not be intact depending on the job type. Return both run_list and ignore_list. @@ -63,13 +63,26 @@ def post_job(self, job_type, kwargs): - akamai_update_papi_property_for_mod_san - akamai_update_papi_property_for_mod_sni - Example return : ``([run_list],[ignore_list])`` + .. code-block:: python + + ( + [ + { + "domain_name": "one.example.com", + "project_id": "000", + "flavor_id": "cdn", + "cert_type": "sni" + } + ], + [] + ) + :param str job_type: Type of the job :param dict kwargs: Additional arguments :return: Tuple of run_list and ignore_list - :rtype: (list, list) + :rtype: tuple(list, list) :raises NotImplementedError: if the job_type is not supported """ @@ -483,12 +496,12 @@ def get_san_mapping_list(self): return res def put_san_mapping_list(self, san_mapping_list): - """Populate san_mapping_queue with new data. + """Override san_mapping_queue with new certificate objects. - :param list san_mapping_list: The new data + :param list san_mapping_list: New certificates - :return: Tuple of old and new items in the san_mapping_queue - :rtype: (list, list) + :return: Tuple of new and deleted certs in the san_mapping_queue + :rtype: tuple(list, list) """ new_queue_data = [json.dumps(r) for r in san_mapping_list] res, deleted = [], [] @@ -520,7 +533,7 @@ def delete_http_policy(self): Return ignored_list and run_list. :return: Tuple of deleted and ignored policies - :rtype: (list, list) + :rtype: tuple(list, list) """ http_policies = [] run_list = [] diff --git a/poppy/manager/default/health.py b/poppy/manager/default/health.py index a803d129..a3bd98b7 100644 --- a/poppy/manager/default/health.py +++ b/poppy/manager/default/health.py @@ -25,10 +25,20 @@ def __init__(self, manager): def health(self): """Returns health status of all the modules. - :return: A health_map dict with health information about dns, providers, - distributed_task and storage modules along with is_alive indicator( - ``True if all the modules are alive, False if one of the module is not alive.``) - :rtype:(bool, dict) + A health_map dict with health information about dns, providers, + distributed_task and storage modules along with is_alive indicator( + ``True if all the modules are alive,`` + ``False if any one of the module is not alive.``) + + This API may be slow in giving response depending on the provider + as it needs to hit the provider's API to fetch the health info. + + If you only need to check the status of ``distributed_task`` + and/or ``storage``, you can use :meth:`ping_check()`, as it provides + a subset of the ``health_map`` and is generally much faster. + + :return: is_alive and dict of health info + :rtype: tuple(bool, dict) """ health_map, is_alive = self.ping_check() @@ -58,13 +68,10 @@ def health(self): def ping_check(self): """Get health for storage and distributed_task. - Get the health_map dict and is_alive info for distributed_task - and storage modules. + For more details, refer to :meth:`health` . - :return: A health_map dict with health information about distributed_task - and storage modules along with is_alive indicator( - ``True if all the modules are alive, False if one of the module is not alive.``) - :rtype:(bool, dict) + :return: is_alive and health_map dict + :rtype: tuple(bool, dict) """ health_map = {} is_alive = True @@ -91,20 +98,20 @@ def ping_check(self): return health_map, is_alive def is_provider_alive(self, provider_name): - """Check a provide is alive or not. + """Check if provider is alive. :param str provider_name: The name of the provider - :return: True if alive, otherwise False + :return: ``True`` if alive, otherwise ``False`` :rtype: bool """ return self._providers[provider_name].obj.is_alive() def is_distributed_task_alive(self, distributed_task_name): - """Check distributed_task is alive or not. + """Check if distributed_task is alive. :param str distributed_task_name: The name of the dist task - :return: True if alive, otherwise False + :return: ``True`` if alive, otherwise ``False`` :rtype: bool :raises KeyError: if the distributed_task_name is not same as vendor name @@ -116,10 +123,10 @@ def is_distributed_task_alive(self, distributed_task_name): raise KeyError def is_storage_alive(self, storage_name): - """Check storage is alive or not. + """Check if storage is alive. :param str storage_name: The name of the storage - :return: True if alive, otherwise False + :return: ``True`` if alive, otherwise ``False`` :rtype: bool :raises KeyError: if storage_name is not same as underlying storage @@ -131,10 +138,10 @@ def is_storage_alive(self, storage_name): raise KeyError def is_dns_alive(self, dns_name): - """Check DNS is alive or not. + """Check if DNS is alive. :param str dns_name: The name of the DNS - :return: True if alive, otherwise False + :return: ``True`` if alive, otherwise ``False`` :rtype: bool :raise KeyError: if dns_name is not same as underlying DNS diff --git a/poppy/manager/default/services.py b/poppy/manager/default/services.py index 8d809186..fedbc257 100644 --- a/poppy/manager/default/services.py +++ b/poppy/manager/default/services.py @@ -65,7 +65,7 @@ def __init__(self, manager): def determine_sleep_times(self): """Calculate sleep times used in rebind. - :return: Time to sleep + :return: List of times to sleep using exponential backoff :rtype: list """ @@ -123,6 +123,13 @@ def get_service_by_domain_name(self, domain_name): def get_services_by_status(self, status): """Return the services by status. + Currently supported statuses include: + - create_in_progress + - deployed + - update_in_progress + - delete_in_progress + - failed + :param unicode status: The status of the service :return: List of services :rtype: list @@ -152,7 +159,7 @@ def _append_defaults(self, service_json, operation='create'): If the service rules are None or Empty, add default rule. If the operation is 'create', add default ttl and cache. - :param dict service_json: Service details in JSON form + :param dict service_json: Service object represented as a dict :param str operation: (Default 'create') Name of operation """ # default origin rule @@ -195,8 +202,8 @@ def get_services(self, project_id, marker=None, limit=None): """Get list of services. :param unicode project_id: The project id - :param int marker: The marker indicator - :param int limit: No of services to fetch + :param int marker: Starting position of the pagination + :param int limit: Number of services to fetch :return: List of services :rtype: list(poppy.model.service.Service) @@ -225,7 +232,7 @@ def create_service(self, project_id, auth_token, service_json): :rtype: poppy.model.service.Service :raises LookupError: if the flavor does not exists - :raises ValueError: if shared domain retry ops has problem + :raises ValueError: if shard domain retry operation has problem """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) @@ -307,7 +314,7 @@ def update_service(self, project_id, service_id, :param unicode project_id: The project id :param unicode service_id: The service id :param unicode auth_token: Token for authorization - :param dict service_updates: The service details + :param list service_updates: The service details :param bool force_update: (Default False) Forceful update \ of the service @@ -533,7 +540,7 @@ def update_service(self, project_id, service_id, return def services_limit(self, project_id, limit): - """Set limit for number of services. + """Set limit for number of services for a given project. :param unicode project_id: The project id :param int limit: No of services @@ -544,7 +551,7 @@ def services_limit(self, project_id, limit): def set_service_provider_details(self, project_id, service_id, auth_token, status): - """Set provider details for service. + """Set provider details for a given service. :param unicode project_id: The project id :param unicode service_id: The service id @@ -574,7 +581,7 @@ def set_service_provider_details(self, project_id, service_id, return 201 def get_services_limit(self, project_id): - """Get the currently set limit on services. + """Get the currently set limit on services for a given project. :param unicode project_id: The project id @@ -598,7 +605,8 @@ def _action_per_service_obj(self, project_id, action, service_obj): :param unicode project_id: The project id :param str action: Action needs to be done - :param dict service_obj: The service object + :param service_obj: The service object + :type service_obj: poppy.model.service.Service """ kwargs = { @@ -638,6 +646,10 @@ def _action_per_service_obj(self, project_id, action, service_obj): def services_action(self, project_id, action, domain=None): """perform action on services present in this domain. + If domain is None, The action is performed on all the + services for a project. Else, the action will be + performed on those services present in the given domain. + List of actions include - delete - enable @@ -720,7 +732,14 @@ def delete_service(self, project_id, service_id): return def purge(self, project_id, service_id, hard=False, purge_url=None): - """Purge a service. + """Purge the contents of a service. + + Example purge_urls (from least specific to most specific): + - Whole-site wildcard, such as ``/*`` + - URL path, such as ``/images/*`` + - Detailed URL path, such as ``/images/logos/partnerA/*`` + - File extension, such as ``/*.png`` + If purge_url is none, all content of this service will be purged. @@ -844,11 +863,13 @@ def _shard_retry(self, project_id, service_obj, store=None): """Retry to get available ssl domain and add to service. :param unicode project_id: The project id - :param dict service_obj: the service object + :param service_obj: The service object + :type service_obj: poppy.model.service.Service + :param unicode store: The store object :return: The service object with shared ssl domain added - :rtype: dict + :rtype: poppy.model.service.Service :raises ValueError: """ @@ -882,6 +903,12 @@ def migrate_domain(self, project_id, service_id, domain_name, new_cert, - Update the all the certificates with cert_status - Update the access_url of all the providers with new_cert + As of now, below are the supported statuses. + - create_in_progress + - deployed + - failed + - cancelled + :param unicode project_id: The project id :param unicode service_id: The service id :param unicode domain_name: The domain name @@ -959,8 +986,11 @@ def migrate_domain(self, project_id, service_id, domain_name, new_cert, def _detect_upgrade_http_to_https(self, old_domains, new_domain): """Determine whether upgrade is required from http to https. - :param list old_domains: List of old domains - :param unicode new_domain: New domain + :param old_domains: List of old domains + :type old_domains: list(poppy.model.helpers.domain.Domain) + + :param new_domain: New domain + :type new_domain: poppy.model.helpers.domain.Domain :return: Boolean value indicating upgrade is required or not :rtype: bool @@ -980,18 +1010,19 @@ def update_access_url_service( self, project_id, service_id, access_url_changes): """Update access url of a service. + This is typically called from admin APIs only. + :param unicode project_id: The project id :param unicode service_id: The service id :param dict access_url_changes: The changes to update the access url - :return: Whether or not the access url details updated + :return: ``True`` if the access urls is successfully updated :rtype: bool - :raise: - ValueError if the domain could not found on the service, - ServiceNotFound if service is not found, - InvalidOperation if the domain is shared ssl, - InvalidResourceName if the access url or domain is invalid + :raises ValueError: if the domain could not found on the service, + :raises ServiceNotFound: if service is not found, + :raises InvalidOperation: if the domain is shared ssl, + :raises InvalidResourceName: if the access url or domain is invalid """ try: service_old = self.storage_controller.get_service( From c2f346170774e729640ba8fedbf53fe817a3975e Mon Sep 17 00:00:00 2001 From: Yuvaraju Meenuga Date: Tue, 4 Dec 2018 14:06:34 +0530 Subject: [PATCH 5/6] tmp commit for doc strings --- .../pecan/controllers/v1/services.py | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/poppy/transport/pecan/controllers/v1/services.py b/poppy/transport/pecan/controllers/v1/services.py index 63c9e2b1..d59ddcd8 100644 --- a/poppy/transport/pecan/controllers/v1/services.py +++ b/poppy/transport/pecan/controllers/v1/services.py @@ -13,6 +13,38 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Pecan router to map service related urls. + +Each class in this module maps to different urls. + + - Purge service [:class:`ServiceAssetsController` ] + - Retrieve analytical metrics for domain [:class:`ServicesAnalyticsController` ] + - GET/Create/Delete/Update service [:class:`ServicesController` ] + +Mappings:- + +Each HTTP method is mapped to Pecan's method as shown below. + +Pecan Method HttpMethod/URL +------------- ------------------ +get_one -> GET /services/ +get_all -> GET /services/ +get -> GET /services/ or GET /services/ +post -> POST /services/ +put -> PUT /services/ +delete -> DELETE /services/ + +Example:- + + - The URL ``{{host}}/v1.0/services/ with HTTP POST`` will be received by + :py:func:`ServicesController.post`` + - The URL ``{{host}}/v1.0/services/ with HTTP POST`` will be received by + :py:func:`ServicesController.get_all`` + +For more details on how the top level URL mapping is done, refer to + :py:mod:`poppy/poppy/transport/pecan/driver.py` +""" + import ast import json import uuid @@ -148,6 +180,23 @@ def get(self, service_id): class ServicesController(base.Controller, hooks.HookController): + """Handles typical CRUD operations on Services. + + Enables Context Hook and Errors Hook. + `Context Hook` checks that `X-Project-ID` and `X-Auth-Token` + are present in the request payload and constructs `base_url`. + `Errors Hook` handles any errors during the request. + + Apart from Hooks, each CRUD operation will validate the + passed in request parameters. + + After doing the base level validations on the request + payload, calls will be delegated to Manager layer to + process the request. + + When Manager layer returns an output/ or any Exception + raised, It serializes the responses and returns to user. + """ __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] @@ -167,10 +216,18 @@ def __init__(self, driver): @pecan.expose('json') def get_all(self): - """ - Get all the services available - :return: Dict of links and services - :rtype: dict + """Get all the services in Poppy. + + There is a limit on number of services that can + be fetched at a time. This limit can be configured + through ``poppy.conf`` by setting an integer value + for ``max_services_per_page``. + + Example return: + + + + :return: """ marker = pecan.request.GET.get('marker', None) limit = pecan.request.GET.get('limit', 10) From 400660aa0a3fc199d66b2572ac8ec20bd024b7e6 Mon Sep 17 00:00:00 2001 From: Yuvaraju Meenuga Date: Tue, 4 Dec 2018 17:29:29 +0530 Subject: [PATCH 6/6] tmp commit 2 --- .../pecan/controllers/v1/services.py | 227 ++++++++++++++---- 1 file changed, 181 insertions(+), 46 deletions(-) diff --git a/poppy/transport/pecan/controllers/v1/services.py b/poppy/transport/pecan/controllers/v1/services.py index d59ddcd8..d8818115 100644 --- a/poppy/transport/pecan/controllers/v1/services.py +++ b/poppy/transport/pecan/controllers/v1/services.py @@ -41,6 +41,21 @@ - The URL ``{{host}}/v1.0/services/ with HTTP POST`` will be received by :py:func:`ServicesController.get_all`` +Each class in the module have Enabled Context Hook and Errors Hook. +`Context Hook` checks that `X-Project-ID` and `X-Auth-Token` +are present in the request payload and constructs `base_url`. +`Errors Hook` handles any errors during the request. + +``validate`` decorators are injected into each method of the class +to validate the payload and other dependencies. If any of the +validation fails, operation will be aborted and ``Errors Hook`` +will be responsible for sending error response to the user. + +After doing the base level validations on the request +payload, calls will be delegated to Manager layer to +process the request. The Default Manager layer has +various controllers to handle these requests. + For more details on how the top level URL mapping is done, refer to :py:mod:`poppy/poppy/transport/pecan/driver.py` """ @@ -77,6 +92,7 @@ class ServiceAssetsController(base.Controller, hooks.HookController): + """Controller to purge the contents of service""" __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] @@ -87,12 +103,27 @@ class ServiceAssetsController(base.Controller, hooks.HookController): helpers.abort_with_message) ) def delete(self, service_id): - """ + """Purge contents of a service. + + Purge the content when it is in the provider + network. Based on the ``purge_url`` in the request, + content will be purged. - Purge a service by id - :type service_id: str - :param service_id: Id of the service to delete - :return: Pecan's response with 202 status + For example: When the ``purge_url`` is set to + ``/images/*``, all the images present in the + the path will be purged. + + Note that if the ``purge_url`` is None, all the + contents of the service will be purged. + + The default manager will invoke ``purge taskflow`` + to do this operation. + + :param unicode service_id: ID of the service + :return: Pecan's 200 response if the ``purge taskflow`` + is successfully invoked. 404 if the service not + found. 400 response if the request payload is + incompatible. :rtype: pecan.Response """ purge_url = pecan.request.GET.get('url', '/*') @@ -136,6 +167,9 @@ def delete(self, service_id): class ServicesAnalyticsController(base.Controller, hooks.HookController): + """Controller to process and return Metrics for a given + service. + """ __hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()] @@ -150,12 +184,26 @@ class ServicesAnalyticsController(base.Controller, hooks.HookController): stoplight_helpers.pecan_getter) ) def get(self, service_id): - """ + """Get Metrics for a given service ID. + + The below keys are expected in the payload. + - metricType + - startTime + - endTime + - metrics_controller + + Example return: - Get metrics by domain of this service id - :type service_id: str - :param service_id: Id of the service to retrieve the metrics by domain - :return: Pecan response object with http status code 200 or 500 or 404 or 400 + Pecan will serialize the below dict and sends along + with 200 response. + + ``{'provider': '', 'flavor':'', 'domain':'', 'metricType': {}}`` + + :param unicode service_id: ID of the service + :return: Pecan's 200 response if successfully retrieved + analytics for the service. 404 response if the service + was not found or Provider details not found. 500 response + for general server side exceptions. :rtype: pecan.Response """ call_args = getattr(pecan.request.context, @@ -182,18 +230,6 @@ def get(self, service_id): class ServicesController(base.Controller, hooks.HookController): """Handles typical CRUD operations on Services. - Enables Context Hook and Errors Hook. - `Context Hook` checks that `X-Project-ID` and `X-Auth-Token` - are present in the request payload and constructs `base_url`. - `Errors Hook` handles any errors during the request. - - Apart from Hooks, each CRUD operation will validate the - passed in request parameters. - - After doing the base level validations on the request - payload, calls will be delegated to Manager layer to - process the request. - When Manager layer returns an output/ or any Exception raised, It serializes the responses and returns to user. """ @@ -218,16 +254,21 @@ def __init__(self, driver): def get_all(self): """Get all the services in Poppy. + Example URL to create service: + -``{{host}}/v1.0/services/`` with HTTP method as `GET` + There is a limit on number of services that can be fetched at a time. This limit can be configured through ``poppy.conf`` by setting an integer value for ``max_services_per_page``. - Example return: - - - - :return: + :return: Dictionary containing lists of links and + serialized Service objects + :rtype: dict + :raise ValueError: If the request `limit` value + is more than configured ``max_services_per_page`` + Or If the request `marker` is not + a valid UUID """ marker = pecan.request.GET.get('marker', None) limit = pecan.request.GET.get('limit', 10) @@ -278,13 +319,18 @@ def get_all(self): helpers.abort_with_message) ) def get_one(self, service_id): - """ + """Get a Service details by its ID. + + Example URL to get service: + -``{{host}}/v1.0/services/`` + with HTTP method as `GET` - Get a service by id - :type service_id: str - :param service_id: Id of the service to retrieve - :return: Service details + :param unicode service_id: Id of the service + + :return: Service object serialized into OrderedDict :rtype: collections.OrderedDict + :raise ValueError: If there was not any service + for the given ID. """ services_controller = self._driver.manager.services_controller try: @@ -304,9 +350,66 @@ def get_one(self, service_id): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def post(self): - """ - Create a new service - :return: Pecan response with status 202 + """Create a new service. + + Example URL to create service: + -``{{host}}/v1.0/services/`` with HTTP method as `POST` + + An example payload for this POST request: + + .. code-block:: python + + { + "name": "ServiceName0001", + "domains": + [ + { + "domain": "test.domain.com", + "protocol": "http" + } + ], + "origins": + [ + { + "origin": "www.example.com", + "port": 80, + "ssl": false + } + ], + "caching": [ + { + "name": "default", + "ttl": 3600 + } + ], + "flavor_id": "premium", + "restrictions": [ + ] + } + + The payload must have at least one domain and one origin. + The request to create a new service will be + delegated to Default Manager service controller + that does the below. + + - A service dictionary gets created in Cassandra + - Async tasks to create DNS mappings + - Async tasks to create provider policies + - Based on `Enqueue` flag, request to create SSL + certificate will be enqueued in mod_san_queue or + a certificate will be created for HTTPS domains. + Enqueue flag is set to `True` by default. + + Create service will be aborted if .. + - All the available shards are exhausted + - No flavor is created in Cassandra + - If the service name already exists + - Services count is exceeding the limit + + In all the above cases, Pecan sends a 400 error. + + :return: Pecan's 200 response if the service was + successfully created, 400 response otherwise. :rtype: pecan.Response """ services_controller = self._driver.manager.services_controller @@ -343,11 +446,20 @@ def post(self): helpers.abort_with_message) ) def delete(self, service_id): - """ - Delete a service by id - :type service_id: str - :param service_id: Id of the service to delete - :return: Pecan response with http status code 202 or 404 + """Delete a service for a given service ID. + + Example URL to create service: + -``{{host}}/v1.0/service/`` + with HTTP method as `DELETE` + + Deleting service will trigger the below tasks + - Deleting service dictionary from Cassandra + - Deleting DNS mappings + - Deleting associated certificates for each domain in the service + + :param unicode service_id: Id of the service to delete + :return: Pecan's 202 response if the delete operation was + successful. Else, Pecan's 400 response will be returned. :rtype: pecan.Response """ services_controller = self._driver.manager.services_controller @@ -372,12 +484,35 @@ def delete(self, service_id): helpers.abort_with_message, stoplight_helpers.pecan_getter)) def patch_one(self, service_id): - """ - - Update a service - :type service_id: str - :param service_id: Id of the service to update - :return: Pecan response with http status code 202 or 400 or 404 + """Update service. + + Example URL to create service: + -``{{host}}/v1.0/services/`` + with HTTP method as `PATCH` + + For payload, refer to :meth:`post()`. + + Updating service is two-step process. It filters out + the payload and detects newly added domains, deleted + old domains. For newly added domains, it follows the + :meth:`post()` workflow. For deleted domains it + follows :meth:`delete()` workflow. If the service + update involves anything other than domains (ex: + renaming the service etc..) it updated the service + dictionary in cassandra. + + The update operation will be aborted if .. + - If any validations failed in request Payload + - No flavor detected in cassandra + - No service found + - Conflict names while renaming service + - If the service is in invalid states + - Exhausted shard domains + + :param unicode service_id: ID of the service to update + :return: Pecan's 200 response if the service was updated + successfully. 404 response if the service was not found. + In other cases, 400 response will be returned. :rtype: pecan.Response """ service_updates = json.loads(pecan.request.body.decode('utf-8'))