X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=juju%2Fmodel.py;h=df72eb22cae2f78fd90edcd0609495e41b7044aa;hb=df3aef1ed7daac491bcc135c33e9dfc3b5cd4eae;hp=b7318992351bbf51d2823a167ed9c823129ef35e;hpb=db399019ac73dfb980d48b231d69a267de27dd87;p=osm%2FN2VC.git diff --git a/juju/model.py b/juju/model.py index b731899..df72eb2 100644 --- a/juju/model.py +++ b/juju/model.py @@ -12,6 +12,7 @@ from theblues import charmstore from .client import client from .client import watcher from .client import connection +from .constraints import parse as parse_constraints from .delta import get_entity_delta from .delta import get_entity_class from .exceptions import DeadEntityException @@ -344,6 +345,7 @@ class Model(object): self.connection = None self.observers = weakref.WeakValueDictionary() self.state = ModelState(self) + self.info = None self._watcher_task = None self._watch_shutdown = asyncio.Event(loop=loop) self._watch_received = asyncio.Event(loop=loop) @@ -356,25 +358,31 @@ class Model(object): """ self.connection = await connection.Connection.connect(*args, **kw) - self._watch() - await self._watch_received.wait() + await self._after_connect() async def connect_current(self): """Connect to the current Juju model. """ self.connection = await connection.Connection.connect_current() - self._watch() - await self._watch_received.wait() + await self._after_connect() + + async def connect_model(self, model_name): + """Connect to a specific Juju model by name. - async def connect_model(self, arg): - """Connect to a specific Juju model. - :param arg: : + :param model_name: Format [controller:][user/]model + + """ + self.connection = await connection.Connection.connect_model(model_name) + await self._after_connect() + + async def _after_connect(self): + """Run initialization steps after connecting to websocket. """ - self.connection = await connection.Connection.connect_model(arg) self._watch() await self._watch_received.wait() + await self.get_info() async def disconnect(self): """Shut down the watcher task and close websockets. @@ -448,6 +456,27 @@ class Model(object): """ return self.state.units + async def get_info(self): + """Return a client.ModelInfo object for this Model. + + Retrieves latest info for this Model from the api server. The + return value is cached on the Model.info attribute so that the + valued may be accessed again without another api call, if + desired. + + This method is called automatically when the Model is connected, + resulting in Model.info being initialized without requiring an + explicit call to this method. + + """ + facade = client.ClientFacade() + facade.connect(self.connection) + + self.info = await facade.ModelInfo() + log.debug('Got ModelInfo: %s', vars(self.info)) + + return self.info + def add_observer( self, callable_, entity_type=None, action=None, entity_id=None, predicate=None): @@ -1206,6 +1235,36 @@ class Model(object): def charmstore(self): return self._charmstore + async def get_metrics(self, *tags): + """Retrieve metrics. + + :param str \*tags: Tags of entities from which to retrieve metrics. + No tags retrieves the metrics of all units in the model. + """ + log.debug("Retrieving metrics for %s", + ', '.join(tags) if tags else "all units") + + metrics_facade = client.MetricsDebugFacade() + metrics_facade.connect(self.connection) + + entities = [client.Entity(tag) for tag in tags] + metrics_result = await metrics_facade.GetMetrics(entities) + + metrics = collections.defaultdict(list) + + for entity_metrics in metrics_result.results: + error = entity_metrics.error + if error: + if "is not a valid tag" in error: + raise ValueError(error.message) + else: + raise Exception(error.message) + + for metric in entity_metrics.metrics: + metrics[metric.unit].append(vars(metric)) + + return metrics + class BundleHandler(object): """ @@ -1264,35 +1323,41 @@ class BundleHandler(object): await self.client_facade.AddCharm(None, entity_id) return entity_id - async def addMachines(self, series, constraints, container_type, - parent_id): - """ - :param series string: - Series holds the optional machine OS series. - - :param constraints string: - Constraints holds the optional machine constraints. - - :param Container_type string: - ContainerType optionally holds the type of the container (for - instance ""lxc" or kvm"). It is not specified for top level - machines. - - :param parent_id string: - ParentId optionally holds a placeholder pointing to another machine - change or to a unit change. This value is only specified in the - case this machine is a container, in which case also ContainerType - is set. - """ - params = client.AddMachineParams( - series=series, - constraints=constraints, - container_type=container_type, - parent_id=self.resolve(parent_id), - ) - results = await self.client_facade.AddMachines(params) - log.debug('Added new machine %s', results[0].machine) - return results[0].machine + async def addMachines(self, params=None): + """ + :param params dict: + Dictionary specifying the machine to add. All keys are optional. + Keys include: + + series: string specifying the machine OS series. + constraints: string holding machine constraints, if any. We'll + parse this into the json friendly dict that the juju api + expects. + container_type: string holding the type of the container (for + instance ""lxc" or kvm"). It is not specified for top level + machines. + parent_id: string holding a placeholder pointing to another + machine change or to a unit change. This value is only + specified in the case this machine is a container, in + which case also ContainerType is set. + """ + params = params or {} + + if 'parent_id' in params: + params['parent_id'] = self.resolve(params['parent_id']) + + params['constraints'] = parse_constraints( + params.get('constraints')) + params['jobs'] = params.get('jobs', ['JobHostUnits']) + + params = client.AddMachineParams(**params) + results = await self.client_facade.AddMachines([params]) + error = results.machines[0].error + if error: + raise ValueError("Error adding machine: %s", error.message) + machine = results.machines[0].machine + log.debug('Added new machine %s', machine) + return machine async def addRelation(self, endpoint1, endpoint2): """