X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FN2VC.git;a=blobdiff_plain;f=n2vc%2Flibjuju.py;h=028cea820e6d5ef5414c7362253381a48f6302c0;hp=64276d6df19b8d511dc7cb6e4e76ab0bca270e7d;hb=7e887b22fdc176021b215c3b83a052276fdbeefc;hpb=eacf5a7724815fb33802ceb0af246dfc959eb021 diff --git a/n2vc/libjuju.py b/n2vc/libjuju.py index 64276d6..028cea8 100644 --- a/n2vc/libjuju.py +++ b/n2vc/libjuju.py @@ -33,6 +33,7 @@ from juju.controller import Controller from juju.client import client from juju import tag +from n2vc.definitions import Offer, RelationEndpoint from n2vc.juju_watcher import JujuModelWatcher from n2vc.provisioner import AsyncSSHProvisioner from n2vc.n2vc_conn import N2VCConnector @@ -94,7 +95,7 @@ class Libjuju: """ controller = None try: - controller = Controller(loop=self.loop) + controller = Controller() await asyncio.wait_for( controller.connect( endpoint=self.vca_connection.data.endpoints, @@ -558,7 +559,7 @@ class Libjuju: controller = await self.get_controller() model = await self.get_model(controller, model_name) try: - await model.deploy(uri) + await model.deploy(uri, trust=True) if wait: await JujuModelWatcher.wait_for_model(model, timeout=timeout) self.log.debug("All units active in model {}".format(model_name)) @@ -791,6 +792,13 @@ class Libjuju: raise JujuApplicationExists( "Application {} exists".format(application_name) ) + except juju.errors.JujuError as e: + if "already exists" in e.message: + raise JujuApplicationExists( + "Application {} exists".format(application_name) + ) + else: + raise e finally: await self.disconnect_model(model) await self.disconnect_controller(controller) @@ -1121,28 +1129,71 @@ class Libjuju: await self.disconnect_model(model) await self.disconnect_controller(controller) + async def offer(self, endpoint: RelationEndpoint) -> Offer: + """ + Create an offer from a RelationEndpoint + + :param: endpoint: Relation endpoint + + :return: Offer object + """ + model_name = endpoint.model_name + offer_name = f"{endpoint.application_name}-{endpoint.endpoint_name}" + controller = await self.get_controller() + model = None + try: + model = await self.get_model(controller, model_name) + await model.create_offer(endpoint.endpoint, offer_name=offer_name) + offer_list = await self._list_offers(model_name, offer_name=offer_name) + if offer_list: + return Offer(offer_list[0].offer_url) + else: + raise Exception("offer was not created") + except juju.errors.JujuError as e: + if "application offer already exists" not in e.message: + raise e + finally: + if model: + self.disconnect_model(model) + self.disconnect_controller(controller) + async def consume( self, - offer_url: str, model_name: str, - ): + offer: Offer, + provider_libjuju: "Libjuju", + ) -> str: """ - Adds a remote offer to the model. Relations can be created later using "juju relate". + Consumes a remote offer in the model. Relations can be created later using "juju relate". - :param: offer_url: Offer Url - :param: model_name: Model name + :param: model_name: Model name + :param: offer: Offer object to consume + :param: provider_libjuju: Libjuju object of the provider endpoint :raises ParseError if there's a problem parsing the offer_url :raises JujuError if remote offer includes and endpoint :raises JujuAPIError if the operation is not successful + + :returns: Saas name. It is the application name in the model that reference the remote application. """ + saas_name = f'{offer.name}-{offer.model_name.replace("-", "")}' + if offer.vca_id: + saas_name = f"{saas_name}-{offer.vca_id}" controller = await self.get_controller() - model = await controller.get_model(model_name) - + model = None + provider_controller = None try: - await model.consume(offer_url) + model = await controller.get_model(model_name) + provider_controller = await provider_libjuju.get_controller() + await model.consume( + offer.url, application_alias=saas_name, controller=provider_controller + ) + return saas_name finally: - await self.disconnect_model(model) + if model: + await self.disconnect_model(model) + if provider_controller: + await provider_libjuju.disconnect_controller(provider_controller) await self.disconnect_controller(controller) async def destroy_model(self, model_name: str, total_timeout: float = 1800): @@ -1203,6 +1254,10 @@ class Libjuju: raise Exception( "Timeout waiting for model {} to be destroyed".format(model_name) ) + except juju.errors.JujuError as e: + if any("has been removed" in error for error in e.errors): + return + raise e async def destroy_application( self, model_name: str, application_name: str, total_timeout: float @@ -1339,17 +1394,29 @@ class Libjuju: finally: await self.disconnect_controller(controller) - async def list_offers(self, model_name: str) -> QueryApplicationOffersResults: - """List models with certain names + async def _list_offers( + self, model_name: str, offer_name: str = None + ) -> QueryApplicationOffersResults: + """ + List offers within a model :param: model_name: Model name + :param: offer_name: Offer name to filter. - :return: Returns list of offers + :return: Returns application offers results in the model """ controller = await self.get_controller() try: - return await controller.list_offers(model_name) + offers = (await controller.list_offers(model_name)).results + if offer_name: + matching_offer = [] + for offer in offers: + if offer.offer_name == offer_name: + matching_offer.append(offer) + break + offers = matching_offer + return offers finally: await self.disconnect_controller(controller)