From 58a82a7e0a5bc010236991061aa959e55672028c Mon Sep 17 00:00:00 2001 From: Krzysztof Opasiak Date: Wed, 25 Aug 2021 17:44:25 +0200 Subject: [PATCH] [TEST] Unwind master back to 27.07.2020 Unwind master due to regression in few tests. Issue-ID: OOM-1 Change-Id: I3b759a5928aeb47fc7fddeaa5998aa090d085c5c Signed-off-by: Krzysztof Opasiak --- setup.cfg | 1 - .../cds_resource_resolution_settings.py | 77 ------ src/onaptests/configuration/pnf_macro_settings.py | 13 +- src/onaptests/configuration/settings.py | 3 +- src/onaptests/scenario/cds_resource_resolution.py | 96 -------- src/onaptests/steps/base.py | 4 +- src/onaptests/steps/instantiate/msb_k8s.py | 38 +-- src/onaptests/steps/onboard/cds.py | 46 +--- src/onaptests/steps/onboard/msb_k8s.py | 16 +- src/onaptests/steps/simulator/cds_mockserver.py | 62 ----- .../simulator/pnf_simulator_cnf/pnf_register.py | 4 +- .../cds-resource-resolution/cds-mock-server.tar.gz | Bin 2376 -> 0 bytes .../artifacts/cds-resource-resolution/dd.json | 260 --------------------- .../resource-resolution.zip | Bin 6121 -> 0 bytes 14 files changed, 27 insertions(+), 593 deletions(-) delete mode 100644 src/onaptests/configuration/cds_resource_resolution_settings.py delete mode 100644 src/onaptests/scenario/cds_resource_resolution.py delete mode 100644 src/onaptests/steps/simulator/cds_mockserver.py delete mode 100644 src/onaptests/templates/artifacts/cds-resource-resolution/cds-mock-server.tar.gz delete mode 100644 src/onaptests/templates/artifacts/cds-resource-resolution/dd.json delete mode 100644 src/onaptests/templates/artifacts/cds-resource-resolution/resource-resolution.zip diff --git a/setup.cfg b/setup.cfg index 5b3c6bf..3b9c670 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,4 +54,3 @@ xtesting.testcase = basic_onboard = onaptests.scenario.basic_onboard:BasicOnboard pnf_macro = onaptests.scenario.pnf_macro:PnfMacro basic_clamp = onaptests.scenario.basic_clamp:BasicClamp - cds_resource_resolution = onaptests.scenario.cds_resource_resolution:CDSResourceResolution diff --git a/src/onaptests/configuration/cds_resource_resolution_settings.py b/src/onaptests/configuration/cds_resource_resolution_settings.py deleted file mode 100644 index c7687b5..0000000 --- a/src/onaptests/configuration/cds_resource_resolution_settings.py +++ /dev/null @@ -1,77 +0,0 @@ -from pathlib import Path -from uuid import uuid4 - -from .settings import * # pylint: disable=W0614 - -CLOUD_REGION_CLOUD_OWNER = "basicnf-owner" # must not contain _ -CLOUD_REGION_ID = "k8sregion" -CLOUD_REGION_TYPE = "k8s" -CLOUD_REGION_VERSION = "1.0" -CLOUD_OWNER_DEFINED_TYPE = "N/A" -COMPLEX_PHYSICAL_LOCATION_ID = "sdktests" - -PNF_DEFINITION_ATRIFACT_FILE_PATH = Path(Path(__file__).parent.parent, - "templates/artifacts/cds-resource-resolution/cds-mock-server.tar.gz") -PNF_RB_NAME = f"cds-ms-rb-{str(uuid4())}" -PNF_RB_VERSION = "v1" -PNF_PROFILE_ARTIFACT_FILE_PATH = Path(Path(__file__).parent.parent, - "templates/artifacts/profile.tar.gz") -PNF_PROFILE_NAME = f"cds-ms-prof-{str(uuid4())}" -K8S_VERSION = "1.0" -K8S_CONFIG = str(Path(Path(__file__).parent.parent, "templates/artifacts/config")) -CDS_MOCKSERVER_EXPECTATIONS = [ - { - "method": "GET", - "path": "/resource-resolution/get", - "response": '{"value": "A046E51D-44DC-43AE-BBA2-86FCA86C5265"}' - }, - { - "method": "GET", - "path": "/resource-resolution/get/A046E51D-44DC-43AE-BBA2-86FCA86C5265/id", - "response": '{"value": "74FE67C6-50F5-4557-B717-030D79903908"}' - }, - { - "method": "POST", - "path": "/resource-resolution/post", - "response": '{"value": "post:ok"}' - }, - { - "method": "PUT", - "path": "/resource-resolution/put", - "response": '{"value": "put:ok"}' - }, - { - "method": "PATCH", - "path": "/resource-resolution/patch", - "response": '{"value": "patch:ok"}' - }, - { - "method": "DELETE", - "path": "/resource-resolution/delete", - "response": '{"value": "delete:ok"}' - } -] - -CDS_DD_FILE = Path(Path(__file__).parent.parent, "templates/artifacts/cds-resource-resolution/dd.json") -CDS_CBA_UNENRICHED = Path(Path(__file__).parent.parent, "templates/artifacts/cds-resource-resolution/resource-resolution.zip") -CDS_CBA_ENRICHED = "/tmp/resource-resolution-enriched.zip" -CDS_WORKFLOW_NAME = "resource-resolution" -CDS_WORKFLOW_INPUT = { - "template-prefix": [ - "helloworld-velocity", - "helloworld-jinja" - ], - "resolution-key": "regression-test", - "resource-resolution-properties": { - "v_input": "ok", - "j_input": "ok" - } -} -CDS_WORKFLOW_EXPECTED_OUTPUT = { - "resource-resolution-response": { - "meshed-template": { - "helloworld-velocity": "{\n \"default\": \"ok\",\n \"input\": \"ok\",\n \"script\": {\n \"python\": \"ok\",\n \"kotlin\": \"ok\"\n },\n \"db\": \"ok\",\n \"rest\": {\n \"GET\": \"A046E51D-44DC-43AE-BBA2-86FCA86C5265\",\n \"POST\": \"post:ok\",\n \"PUT\": \"put:ok\",\n \"PATCH\": \"patch:ok\",\n \"DELETE\": \"delete:ok\"\n }\n}\n", - "helloworld-jinja": "{\n \"default\": \"ok\",\n \"input\": \"ok\",\n \"script\": {\n \"python\": \"ok\",\n \"kotlin\": {\n \"base\": \"ok\"\n \"from suspend function\": \"ok\"\n }\n },\n \"db\": \"ok\",\n \"rest\": {\n \"GET\": \"A046E51D-44DC-43AE-BBA2-86FCA86C5265\",\n \"GET_ID\": \"74FE67C6-50F5-4557-B717-030D79903908\",\n \"POST\": \"post:ok\",\n \"PUT\": \"put:ok\",\n \"PATCH\": \"patch:ok\",\n \"DELETE\": \"delete:ok\"\n }\n}\n" - } - } -} diff --git a/src/onaptests/configuration/pnf_macro_settings.py b/src/onaptests/configuration/pnf_macro_settings.py index 6f59893..d9ccb1b 100644 --- a/src/onaptests/configuration/pnf_macro_settings.py +++ b/src/onaptests/configuration/pnf_macro_settings.py @@ -33,18 +33,15 @@ PLATFORM = "pnf_macro_platform" INSTANTIATION_TIMEOUT = 600 -MSB_K8S_CLEANUP_WAIT_TIME = 60 -MSB_K8S_DEFINITION_ATRIFACT_FILE_PATH = Path(Path(__file__).parent.parent, +PNF_DEFINITION_ATRIFACT_FILE_PATH = Path(Path(__file__).parent.parent, "templates/artifacts/pnf-simulator.tar.gz") -MSB_K8S_RESOURCE_NAME_PREFIX = "pnf-cnf" -MSB_K8S_RB_NAME = f"{MSB_K8S_RESOURCE_NAME_PREFIX}-rb-{str(uuid4())}" -MSB_K8S_RB_VERSION = "v1" -MSB_K8S_PROFILE_ARTIFACT_FILE_PATH = Path(Path(__file__).parent.parent, +PNF_RB_NAME = f"pnf-cnf-rb-{str(uuid4())}" +PNF_RB_VERSION = "v1" +PNF_PROFILE_ARTIFACT_FILE_PATH = Path(Path(__file__).parent.parent, "templates/artifacts/profile.tar.gz") -MSB_K8S_PROFILE_NAME = f"{MSB_K8S_RESOURCE_NAME_PREFIX}-profile-{str(uuid4())}" +PNF_PROFILE_NAME = f"pnf-cnf-profile-{str(uuid4())}" K8S_VERSION = "1.0" K8S_CONFIG = str(Path(Path(__file__).parent.parent, "templates/artifacts/config")) -K8S_ADDITIONAL_RESOURCES_NAMESPACE = "onap-tests" SERVICE_INSTANCE_NAME = "TestPNFMacroInstantiation" diff --git a/src/onaptests/configuration/settings.py b/src/onaptests/configuration/settings.py index e6f894d..491e5e0 100644 --- a/src/onaptests/configuration/settings.py +++ b/src/onaptests/configuration/settings.py @@ -43,8 +43,7 @@ REPORTING_FILE_PATH = "/tmp/reporting.html" K8S_REGION_TYPE = "k8s" TILLER_HOST = "localhost" K8S_CONFIG = None # None means it will use default config (~/.kube/config) -K8S_ONAP_NAMESPACE = "onap" # ONAP Kubernetes namespace -K8S_ADDITIONAL_RESOURCES_NAMESPACE = K8S_ONAP_NAMESPACE # Resources created on tests namespace +K8S_NAMESPACE = "onap" # Kubernetes namespace #SOCK_HTTP = "socks5h://127.0.0.1:8091" ORCHESTRATION_REQUEST_TIMEOUT = 60.0 * 15 # 15 minutes in seconds diff --git a/src/onaptests/scenario/cds_resource_resolution.py b/src/onaptests/scenario/cds_resource_resolution.py deleted file mode 100644 index 11df77f..0000000 --- a/src/onaptests/scenario/cds_resource_resolution.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -"""CDS resource resolution test scenario.""" - -import logging -import time - -from onapsdk.configuration import settings -from onapsdk.exceptions import SDKException -from xtesting.core import testcase - -from onaptests.steps.base import BaseStep -from onaptests.steps.onboard.cds import CbaProcessStep -from onaptests.steps.simulator.cds_mockserver import CdsMockserverCnfConfigureStep -from onaptests.utils.exceptions import OnapTestException - - -class CDSResourceResolutionStep(BaseStep): - """Step created to run scenario and generate report.""" - - def __init__(self, cleanup=False): - """Initialize step. - - Substeps: - - CdsMockserverCnfConfigureStep, - - CbaProcessStep. - """ - super().__init__(cleanup=cleanup) - self.add_step(CdsMockserverCnfConfigureStep( - cleanup=settings.CLEANUP_FLAG - )) - self.add_step(CbaProcessStep( - cleanup=settings.CLEANUP_FLAG - )) - - @property - def description(self) -> str: - """Step description. - - Used for reports - - Returns: - str: Step description - - """ - return "CDS resource-resoulution base step" - - @property - def component(self) -> str: - """Component name. - - Name of the component this step relates to. - Usually the name of ONAP component. - - Returns: - str: Component name - - """ - return "PythonSDK-tests" - - -class CDSResourceResolution(testcase.TestCase): - """Enrich simple blueprint using CDS blueprintprocessor.""" - - __logger = logging.getLogger(__name__) - - def __init__(self, **kwargs): - """Init CDS resource resolution use case.""" - if "case_name" not in kwargs: - kwargs["case_name"] = 'basic_cds' - super().__init__(**kwargs) - self.__logger.debug("CDS resource resolution initialization") - self.test = CDSResourceResolutionStep( - cleanup=settings.CLEANUP_FLAG) - self.start_time = None - self.stop_time = None - self.result = 0 - - def run(self): - self.__logger.debug("CDS resource resolution run") - self.start_time = time.time() - try: - self.test.execute() - self.result = 100 - except OnapTestException as exc: - self.result = 0 - self.__logger.error(exc.error_message) - except SDKException: - self.result = 0 - self.__logger.error("SDK Exception") - finally: - self.stop_time = time.time() - - def clean(self): - """Clean Additional resources if needed.""" - self.__logger.info("Generate Test report") - self.test.reports_collection.generate_report() diff --git a/src/onaptests/steps/base.py b/src/onaptests/steps/base.py index 744fc7b..6d43fbc 100644 --- a/src/onaptests/steps/base.py +++ b/src/onaptests/steps/base.py @@ -126,8 +126,8 @@ class BaseStep(ABC): if self._cleanup: if self._cleanup_report: yield self._cleanup_report - for step in self._steps: - yield from step.cleanup_reports + for step in self._steps: + yield from step.cleanup_reports @property def name(self) -> str: diff --git a/src/onaptests/steps/instantiate/msb_k8s.py b/src/onaptests/steps/instantiate/msb_k8s.py index ba1b732..e6186f5 100644 --- a/src/onaptests/steps/instantiate/msb_k8s.py +++ b/src/onaptests/steps/instantiate/msb_k8s.py @@ -1,6 +1,4 @@ """MSB k8s instantiation module.""" -import time - from onapsdk.configuration import settings from onapsdk.msb.k8s import Instance @@ -8,35 +6,6 @@ from onaptests.steps.base import BaseStep from onaptests.steps.onboard.msb_k8s import CreateProfileStep -class InstancesCleanup(BaseStep): - """Delete old instances which were not cleaned up properly.""" - - @property - def description(self) -> str: - """Step description.""" - return ("Delete old instances which were created using same MSB_K8S_RESOURCE_NAME_PREFIX" - " and were not cleaned up.") - - @property - def component(self) -> str: - """Component name.""" - return "K8S plugin" - - @BaseStep.store_state - def execute(self) -> None: - """Create instance using MSB K8S plugin.""" - super().execute() - self._logger.debug("Delete all instances which are created using definition with same prefix ") - any_deleted: bool = False - for instance in Instance.get_all(): - if instance.request.profile_name.startswith(settings.MSB_K8S_RESOURCE_NAME_PREFIX): - self._logger.debug("Delete %s instance", instance.instance_id) - instance.delete() - any_deleted = True - if any_deleted: - time.sleep(settings.MSB_K8S_CLEANUP_WAIT_TIME) # Give it some time to delete k8s resources (pods, services, deployments...) - - class CreateInstanceStep(BaseStep): """Create MSB k8s instance step.""" @@ -47,7 +16,6 @@ class CreateInstanceStep(BaseStep): - CreateProfileStep. """ super().__init__(cleanup=cleanup) - self.add_step(InstancesCleanup()) self.add_step(CreateProfileStep(cleanup=cleanup)) self.instance: Instance = None @@ -66,9 +34,9 @@ class CreateInstanceStep(BaseStep): """Create instance using MSB K8S plugin.""" super().execute() self.instance = Instance.create(cloud_region_id=settings.CLOUD_REGION_ID, - profile_name=settings.MSB_K8S_PROFILE_NAME, - rb_name=settings.MSB_K8S_RB_NAME, - rb_version=settings.MSB_K8S_RB_VERSION) + profile_name=settings.PNF_PROFILE_NAME, + rb_name=settings.PNF_RB_NAME, + rb_version=settings.PNF_RB_VERSION) @BaseStep.store_state(cleanup=True) def cleanup(self) -> None: diff --git a/src/onaptests/steps/onboard/cds.py b/src/onaptests/steps/onboard/cds.py index 7f91d56..cbd69ce 100644 --- a/src/onaptests/steps/onboard/cds.py +++ b/src/onaptests/steps/onboard/cds.py @@ -8,7 +8,6 @@ from typing import Any, Dict from kubernetes import client, config from kubernetes.client.exceptions import ApiException from onapsdk.cds import Blueprint, DataDictionarySet -from onapsdk.cds.blueprint import Workflow from onapsdk.cds.blueprint_processor import Blueprintprocessor from onapsdk.configuration import settings import urllib3 @@ -54,7 +53,7 @@ class ExposeCDSBlueprintprocessorNodePortStep(CDSBaseStep): try: service_data: Dict[str, Any] = self.k8s_client.read_namespaced_service( self.service_name, - settings.K8S_ONAP_NAMESPACE + settings.K8S_NAMESPACE ) return service_data.spec.type == "NodePort" except ApiException: @@ -67,7 +66,7 @@ class ExposeCDSBlueprintprocessorNodePortStep(CDSBaseStep): Use settings values: - K8S_CONFIG, - - K8S_ONAP_NAMESPACE. + - K8S_NAMESPACE. """ super().execute() @@ -75,7 +74,7 @@ class ExposeCDSBlueprintprocessorNodePortStep(CDSBaseStep): try: self.k8s_client.patch_namespaced_service( self.service_name, - settings.K8S_ONAP_NAMESPACE, + settings.K8S_NAMESPACE, {"spec": {"ports": [{"port": 8080, "nodePort": 30449}], "type": "NodePort"}} ) except ApiException: @@ -97,7 +96,7 @@ class ExposeCDSBlueprintprocessorNodePortStep(CDSBaseStep): try: self.k8s_client.patch_namespaced_service( self.service_name, - settings.K8S_ONAP_NAMESPACE, + settings.K8S_NAMESPACE, [ { "op": "remove", @@ -223,45 +222,12 @@ class CbaPublishStep(CDSBaseStep): @BaseStep.store_state def execute(self) -> None: - """Publish CBA file. + """Enrich CBA file. Use settings values: - - CDS_CBA_ENRICHED. + - CDS_DD_FILE. """ super().execute() blueprint: Blueprint = Blueprint.load_from_file(settings.CDS_CBA_ENRICHED) blueprint.publish() - - -class CbaProcessStep(CDSBaseStep): - """Process CBA step.""" - - def __init__(self, cleanup=False) -> None: - """Initialize CBA process step.""" - super().__init__(cleanup=cleanup) - self.add_step(CbaPublishStep(cleanup=cleanup)) - - @property - def description(self) -> str: - """Step description.""" - return "Process CBA file." - - @BaseStep.store_state - def execute(self) -> None: - """Process CBA file. - - Check if output is equal to expected - - Use settings values: - - CDS_CBA_ENRICHED, - - CDS_WORKFLOW_NAME, - - CDS_WORKFLOW_INPUT - - """ - super().execute() - blueprint: Blueprint = Blueprint.load_from_file(settings.CDS_CBA_ENRICHED) - workflow: Workflow = blueprint.get_workflow_by_name(settings.CDS_WORKFLOW_NAME) - output: Dict[str, Any] = workflow.execute(settings.CDS_WORKFLOW_INPUT) - if not output == settings.CDS_WORKFLOW_EXPECTED_OUTPUT: - raise OnapTestException("Response is not equal to the expected one") diff --git a/src/onaptests/steps/onboard/msb_k8s.py b/src/onaptests/steps/onboard/msb_k8s.py index 503a886..cad471b 100644 --- a/src/onaptests/steps/onboard/msb_k8s.py +++ b/src/onaptests/steps/onboard/msb_k8s.py @@ -36,9 +36,9 @@ class CreateDefinitionStep(BaseStep): def execute(self) -> None: """Create definition.""" super().execute() - with open(settings.MSB_K8S_DEFINITION_ATRIFACT_FILE_PATH, "rb") as definition_file: - self.definition = Definition.create(rb_name=settings.MSB_K8S_RB_NAME, - rb_version=settings.MSB_K8S_RB_VERSION) + with open(settings.PNF_DEFINITION_ATRIFACT_FILE_PATH, "rb") as definition_file: + self.definition = Definition.create(rb_name=settings.PNF_RB_NAME, + rb_version=settings.PNF_RB_VERSION) self.definition.upload_artifact(definition_file.read()) @@ -70,10 +70,10 @@ class CreateProfileStep(BaseStep): """Create profile.""" super().execute() definition: Definition = Definition.get_definition_by_name_version(\ - rb_name=settings.MSB_K8S_RB_NAME, - rb_version=settings.MSB_K8S_RB_VERSION) - with open(settings.MSB_K8S_PROFILE_ARTIFACT_FILE_PATH, "rb") as profile_file: - self.profile = definition.create_profile(profile_name=settings.MSB_K8S_PROFILE_NAME, - namespace=settings.K8S_ADDITIONAL_RESOURCES_NAMESPACE, + rb_name=settings.PNF_RB_NAME, + rb_version=settings.PNF_RB_VERSION) + with open(settings.PNF_PROFILE_ARTIFACT_FILE_PATH, "rb") as profile_file: + self.profile = definition.create_profile(profile_name=settings.PNF_PROFILE_NAME, + namespace=settings.K8S_NAMESPACE, kubernetes_version=settings.K8S_VERSION) self.profile.upload_artifact(profile_file.read()) diff --git a/src/onaptests/steps/simulator/cds_mockserver.py b/src/onaptests/steps/simulator/cds_mockserver.py deleted file mode 100644 index 6933fa0..0000000 --- a/src/onaptests/steps/simulator/cds_mockserver.py +++ /dev/null @@ -1,62 +0,0 @@ -# http://www.apache.org/licenses/LICENSE-2.0 -"""CDS mockserver registration module.""" - -import time - -import requests -from onapsdk.configuration import settings - -from onaptests.steps.base import BaseStep -from onaptests.steps.instantiate.msb_k8s import CreateInstanceStep -from onaptests.utils.exceptions import OnapTestException - - -class CdsMockserverCnfConfigureStep(BaseStep): - """Configure cds mockserver expectations.""" - - def __init__(self, cleanup: bool = False) -> None: - """Initialize step. - - Substeps: - - CreateInstanceStep. - """ - super().__init__(cleanup=cleanup) - self.add_step(CreateInstanceStep(cleanup=cleanup)) - - @property - def description(self) -> str: - """Step description.""" - return "Configure cds-mockserver." - - @property - def component(self) -> str: - """Component name.""" - return "Environment" - - @BaseStep.store_state - def execute(self) -> None: - """Create mockserver expectations. - - Use settings values: - - CDS_MOCKSERVER_EXPECTATIONS. - """ - super().execute() - time.sleep(60) # Wait for mockserver - for expectation in settings.CDS_MOCKSERVER_EXPECTATIONS: - try: - response = requests.put( - "http://portal.api.simpledemo.onap.org:30726/mockserver/expectation", - json={ - "httpRequest" : { - "method": expectation["method"], - "path": expectation["path"] - }, - "httpResponse" : { - "body": expectation["response"] - } - } - ) - response.raise_for_status() - except (requests.ConnectionError, requests.HTTPError) as http_error: - self._logger.debug(f"Can't register cds-mockserver expectation: {str(http_error)}") - raise OnapTestException("CDS mockserver not configured") diff --git a/src/onaptests/steps/simulator/pnf_simulator_cnf/pnf_register.py b/src/onaptests/steps/simulator/pnf_simulator_cnf/pnf_register.py index 1960659..caeb20b 100644 --- a/src/onaptests/steps/simulator/pnf_simulator_cnf/pnf_register.py +++ b/src/onaptests/steps/simulator/pnf_simulator_cnf/pnf_register.py @@ -51,7 +51,7 @@ class PnfSimulatorCnfRegisterStep(BaseStep): k8s_watch: "Watch" = watch.Watch() try: for event in k8s_watch.stream(k8s_client.list_namespaced_pod, - namespace=settings.K8S_ADDITIONAL_RESOURCES_NAMESPACE, + namespace=settings.K8S_NAMESPACE, timeout_seconds=timeout_seconds): if event["object"].metadata.name == "pnf-macro-test-simulator": if not event["object"].status.phase in ["Pending", "Running"]: @@ -76,7 +76,7 @@ class PnfSimulatorCnfRegisterStep(BaseStep): config.load_kube_config(settings.K8S_CONFIG) k8s_client: "CoreV1API" = client.CoreV1Api() try: - for service in k8s_client.list_namespaced_service(namespace=settings.K8S_ONAP_NAMESPACE).items: + for service in k8s_client.list_namespaced_service(namespace=settings.K8S_NAMESPACE).items: if service.metadata.name == settings.DCAE_VES_COLLECTOR_POD_NAME: return service.spec.cluster_ip, service.spec.ports[0].port raise EnvironmentPreparationException("Couldn't get VES ip and port") diff --git a/src/onaptests/templates/artifacts/cds-resource-resolution/cds-mock-server.tar.gz b/src/onaptests/templates/artifacts/cds-resource-resolution/cds-mock-server.tar.gz deleted file mode 100644 index ad428fbcf85d5211debb12174046195c8f316da5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2376 zcmV-O3AgqiiwFSxX7ykI1MOOEPa`=Jp3nU&NL3jUNA$ltIO;V2m-ME4*cluh^bfj6$k**19v;4dqc3933KdGT9Nc}Q!?)%A z|0zYw@jtm`xo$7mEO|v86ySdUaAW)_<`ncGWZK;Sr^*YM$Q(3RTBK7rIUT{(yU{g7EMsGl2rak@Qa0m5SWc(l zt`yyEciNrcTTj;06IxmScPuHmdgTJJW&L;i-9cIZd%Z!A)_=d}*Z)_cw{Rk}MJ}ed z8g`;xIOrU7;3u9WaKcg+v*2y;7T$}9r;5i=q%jr)eaqoEV-fyyZ}z|y5jbdfV22n& z_bS}|CrVhz0%mLhsnk#?jv|DDiAXr``-o>6L<*6dWr<*E#9=PPER@d0xX?@PD(%Br zHyO}jEQRoR1ZSfVemWkVjrLH+Z)ey4zPP!D-;S@Yj?b^p-i_em3QjK0PtUH;F3$1p zXE;9p9ez1GKivb4&O$Z(KFi5|G*XZwc}!j#alT}LDdc#sG9HPEh;Ux%v|v*XQ;7v2 zr2?Pvd?plyQ=xe=N=d{_Xr|4RCzEOiftaC$Av6j}qX`7lM2=Z93;^mTk@G28ixQjt z0?_-JR6@&qF@%)g7pm8mDa$Bw!!8vI{~y(5TW6mK2DbG7L3hw!j{iZY*X#KH|2630VgHA~g8&Z#J{tu5u>bfN1P>1_ zh_Rie7<`Q7Or~vW`U+joLuerjfa76BqM?1o6V7n_dC3LsU+qUos%&F4qi76IV{=Vf zFlzzc-DhZ;F@uy)9$-e8Eew2njgKcq8j;1I*$0lvmdhFXn^WlYTms9d#f+y~!H!6k zh%t9CG-d?L7rUnY1GJzWW6BQEfIE@Plt#c#d$)S4iV4{{1Qy3A!QGM$XPI@*2H!N@ z@)x7%$J#wsb%UYQ^<$;>sZ&Wx+ivBMTkU=6ReW%PLC$Hme}c}@L(uP;OEDW+kzvai z_3!{W2>+^D<}ajti;^O)Ez9jX2#p!LPHidwxj3y0+}c2#ufNj*71!I(#kD!7tp(Yk{F9d z&TYxCmC_XZ9W1A`CQh+@Auk1#YS1oex>58EVb>I_dZ&yVI(I9_4kZwT)^#C-v&E>q zMAxE(-4_;zS1ALfeOptr4&XDz;3dgdW$=s3LoH*Ix9fG}yly;f3=gaI5;_yAkT}bC zHDi}T}4(`NCbP%bQz2yb~Z zE1$Gl#v(1X4EIPBA`h%S*iCX)OQrA1xxiX=%g~sRxLXxlNCmC5s9Y`<(v*2Mmqmh^ zkg7R0%Q1^SSQ+8w`czec+VvBWE6g+e%H;PW^tP?OOMMes-CbDY%qQYaNa_SzDoZG{ zkrVE^M09_e?`!JL;oor`{Sj_!9UJ1RWa7eFHf$Hh8C7rEMQH2*lug`hN*d5oW@*hs zQ7)7PYA(O`a9CksoI^#jOz0U$52QlP%3HhlIx58|;=sJ(EA>I1>dhIfM)18FNgct+uJ~xe=9Mo+#cJ zUe&3mQQGX_T38w{rwu%tHxB6iO@Z6UF`=Y%Da|k|cWg&BV8Tu#kCv((x=C?udeN~% zmDJ;8?Y&ZywZV;36#A7sYBH`#Aom(U0B>8=eKc9d8)BF_y`b_QLb%PHgjfZerm62e^H71m1^+6ErFKqtMv&f{{ zJzC4&*jsY^2^%9Uv7c;c%EnRE0Ih&9Y%$V(FhQtZaMi zbtRJobK)k$$ZIn^of$CX?$|5UheDCl_t_M zrQ(6XtQEI5C*vC3TaOkS9)6Y+Q>nyL-OjQGcbXcmonDRa$*%o|I{SU__!zE=x@?@6 zHuxPDuTN7OCY3ymmrEwn(PqPkc0&S*D}LrLTxuD~WC+(Mm+l$X$|=|XONY1P%~`?(_{Xg?2+JQ_ zh1KBtcUn(T%<^xk)*zV`26JlpWDCpjEZb{0CY=c{$=}+Duzu|_W*OLjVxoR}l6gSo u$&)8fo;-Q-Oxm51oAT`<#Hw$P$54Q;hK56l{ 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 -- 2.16.6