def remove_existing_charms(self, charms): existing_charms = [CharmStoreID(s.charm_source).as_str_without_rev() for s in self.placement_controller.bundle.services] return [c for c in charms if CharmStoreID(c['Id']).as_str_without_rev() not in existing_charms]
def __init__(self, service_name, charm_source, summary_future, constraints, depends, conflicts, allowed_assignment_types, num_units, options, allow_multi_units, subordinate, required, relations, placement_spec): self.service_name = service_name self.charm_source = charm_source self.csid = CharmStoreID(charm_source) self.charm_name = self.csid.name self.summary_future = summary_future self._summary = "Loading summary…" self.constraints = constraints self.depends = depends self.conflicts = conflicts self.allowed_assignment_types = allowed_assignment_types self.num_units = num_units self.orig_num_units = num_units self.options = options self.allow_multi_units = allow_multi_units self.subordinate = subordinate self.is_core = required self.isolate = True if not subordinate else False self.relations = relations self.placement_spec = placement_spec self.resources = None
def services_with_charm_id(self, charm_id): l = [] csid = CharmStoreID(charm_id) id_no_rev = csid.as_str_without_rev() for service in self.services: if service.csid.as_str_without_rev() == id_no_rev: l.append(service) return l
def done_cb(f): csid = CharmStoreID(charm_dict['Id']) id_no_rev = csid.as_str_without_rev() info = self.metadata_controller.get_charm_info(id_no_rev) is_subordinate = info["Meta"]["charm-metadata"].get( "Subordinate", False) service_name = self.placement_controller.add_new_service( charm_name, charm_dict, is_subordinate=is_subordinate) self.frame.focus_position = 'body' self.columns.focus_position = 0 self.update() self.services_column.select_service(service_name)
async def deploy_service(service, default_series, msg_cb): """Juju deploy service. If the service's charm ID doesn't have a revno, will query charm store to get latest revno for the charm. If the service's charm ID has a series, use that, otherwise use the provided default series. Arguments: service: Service to deploy msg_cb: message callback exc_cb: exception handler callback Returns a future that will be completed after the deploy has been submitted to juju """ name = service.service_name if not events.AppMachinesCreated.is_set(name): # block until we have machines await events.AppMachinesCreated.wait(name) app.log.debug('Machines for {} are ready'.format(name)) if service.csid.rev == "": id_no_rev = service.csid.as_str_without_rev() mc = app.metadata_controller futures.wait([mc.metadata_future]) info = mc.get_charm_info(id_no_rev, lambda _: None) service.csid = CharmStoreID(info["Id"]) deploy_args = {} deploy_args = dict( entity_url=service.csid.as_str(), application_name=service.service_name, num_units=service.num_units, constraints=service.constraints, to=service.placement_spec, config=service.options, ) msg = 'Deploying {}...'.format(service.service_name) app.log.info(msg) msg_cb(msg) from pprint import pformat app.log.debug(pformat(deploy_args)) app_inst = await app.juju.client.deploy(**deploy_args) if service.expose: msg = 'Exposing {}.'.format(service.service_name) app.log.info(msg) msg_cb(msg) await app_inst.expose() msg = '{}: deployed, installing.'.format(service.service_name) app.log.info(msg) msg_cb(msg) events.AppDeployed.set(service.service_name)
def _deploy_async(): if service.csid.rev == "": id_no_rev = service.csid.as_str_without_rev() mc = app.metadata_controller futures.wait([mc.metadata_future]) info = mc.get_charm_info(id_no_rev, lambda _: None) service.csid = CharmStoreID(info["Id"]) # Add charm to Juju this.CLIENT.Client(request="AddCharm", params={"url": service.csid.as_str()}) # We must load any resources prior to deploying resources = app.metadata_controller.get_resources( service.csid.as_str_without_rev()) app.log.debug("Resources: {}".format(resources)) if resources: params = { "tag": "application-{}".format(service.csid.name), "url": service.csid.as_str(), "resources": resources } app.log.debug("Adding pending resources: {}".format(params)) resource_ids = this.CLIENT.Resources(request="AddPendingResources", params=params) app.log.debug("Pending resources IDs: {}".format(resource_ids)) application_to_resource_map = {} for idx, resource in enumerate(resources): pid = resource_ids['pending-ids'][idx] application_to_resource_map[resource['Name']] = pid service.resources = application_to_resource_map app_params = {"applications": [service.as_deployargs()]} app.log.debug("Deploying {}: {}".format(service, app_params)) deploy_message = "Deploying application: {}".format( service.service_name) if msg_cb: msg_cb("{}".format(deploy_message)) this.CLIENT.Application(request="Deploy", params=app_params) if msg_cb: msg_cb("{}...done.".format(deploy_message))
def _deploy_async(): if service.csid.rev == "": id_no_rev = service.csid.as_str_without_rev() mc = app.metadata_controller futures.wait([mc.metadata_future]) info = mc.get_charm_info(id_no_rev, lambda _: None) service.csid = CharmStoreID(info["Id"]) app.log.debug("Adding Charm {}".format(service.csid.as_str())) rv = this.CLIENT.Client(request="AddCharm", params={"url": service.csid.as_str()}) app.log.debug("AddCharm returned {}".format(rv)) charm_id = service.csid.as_str() resources = app.metadata_controller.get_resources(charm_id) app.log.debug("Resources for charm id '{}': {}".format( charm_id, resources)) if resources: params = { "tag": "application-{}".format(service.csid.name), "url": service.csid.as_str(), "resources": resources } app.log.debug("AddPendingResources: {}".format(params)) resource_ids = this.CLIENT.Resources(request="AddPendingResources", params=params) app.log.debug( "AddPendingResources returned: {}".format(resource_ids)) application_to_resource_map = {} for idx, resource in enumerate(resources): pid = resource_ids['pending-ids'][idx] application_to_resource_map[resource['Name']] = pid service.resources = application_to_resource_map deploy_args = service.as_deployargs() deploy_args['series'] = service.csid.series app_params = {"applications": [deploy_args]} app.log.debug("Deploying {}: {}".format(service, app_params)) deploy_message = "Deploying {}... ".format(service.service_name) if msg_cb: msg_cb("{}".format(deploy_message)) rv = this.CLIENT.Application(request="Deploy", params=app_params) app.log.debug("Deploy returned {}".format(rv)) for result in rv.get('results', []): if 'error' in result: raise Exception("Error deploying: {}".format( result['error'].get('message', 'error'))) if msg_cb: msg_cb("{}: deployed, installing.".format(service.service_name)) if service.expose: expose_params = {"application": service.service_name} app.log.debug("Expose: {}".format(expose_params)) rv = this.CLIENT.Application(request="Expose", params=expose_params) app.log.debug("Expose returned: {}".format(rv))
class Service: def __init__(self, service_name, charm_source, summary_future, constraints, depends, conflicts, allowed_assignment_types, num_units, options, allow_multi_units, subordinate, required, relations, placement_spec): self.service_name = service_name self.charm_source = charm_source self.csid = CharmStoreID(charm_source) self.charm_name = self.csid.name self.summary_future = summary_future self._summary = "Loading summary…" self.constraints = constraints self.depends = depends self.conflicts = conflicts self.allowed_assignment_types = allowed_assignment_types self.num_units = num_units self.orig_num_units = num_units self.options = options self.allow_multi_units = allow_multi_units self.subordinate = subordinate self.is_core = required self.isolate = True if not subordinate else False self.relations = relations self.placement_spec = placement_spec self.resources = None @property def summary(self): if self.summary_future and self.summary_future.done(): self._summary = self.summary_future.result() return self._summary @property def display_name(self): return "{} ({})".format(self.service_name, self.charm_source) def _format_scope_directive(self, placement): if ':' in placement: scope, directive = placement.split(':') if scope == 'lxc': scope = 'lxd' return {"scope": scope, "directive": str(directive)} else: # Assume that the placement is to a machine number scope = '#' directive = placement return {"scope": scope, "directive": str(directive)} def _prepare_placement(self, placement): new_placements = [] if isinstance(placement, list): for p in placement: new_placements.append(self._format_scope_directive(p)) else: new_placements.append(self._format_scope_directive(placement)) return new_placements def as_deployargs(self): rd = {"charm-url": self.csid.as_str(), "application": self.service_name, "num-units": self.num_units, "constraints": self.constraints} if self.resources: rd['resources'] = self.resources if len(self.options) > 0: config_dict = {self.service_name: self.options} config_yaml = yaml.dump(config_dict, default_flow_style=False) rd["config-yaml"] = config_yaml if self.placement_spec: specs = self._prepare_placement(self.placement_spec) rd["placement"] = specs return rd def required_num_units(self): return self.num_units def __repr__(self): return "<Service {}>".format(self.service_name) def __eq__(self, other): me = self.charm_source + self.service_name them = other.charm_source + other.service_name return me == them def __hash__(self): """We assume that we won't instantiate multiple Charm objects for the same class that have different properties. """ return hash(self.charm_source + self.service_name)
def _graph_string_for_bundle(bundle, mc): s = [] graph = defaultdict(list) svc_requires = {} svc_provides = {} for svc, sd in bundle._bundle['services'].items(): csid = CharmStoreID(sd['charm']) info = mc.get_charm_info(csid.as_str_without_rev()) if info is None: md = {} else: md = info['Meta']['charm-metadata'] svc_requires[svc] = md.get("Requires", {}) svc_provides[svc] = md.get("Provides", {}) services_seen = set() for rel_src, rel_dst in bundle._bundle['relations']: def do_split(rel): ss = rel.split(":") if len(ss) == 1: return rel, "" else: return ss[0], ss[1] src, s_relname = do_split(rel_src) dst, d_relname = do_split(rel_dst) services_seen.add(src) services_seen.add(dst) if src not in svc_provides or src not in svc_requires or \ dst not in svc_provides or dst not in svc_requires: continue src_provides = set( [v['Interface'] for v in svc_provides[src].values()]) src_requires = set( [v['Interface'] for v in svc_requires[src].values()]) dst_provides = set( [v['Interface'] for v in svc_provides[dst].values()]) dst_requires = set( [v['Interface'] for v in svc_requires[dst].values()]) provides_intersection = src_provides.intersection(dst_requires) is_provides = len(provides_intersection) == 1 requires_intersection = src_requires.intersection(dst_provides) is_requires = len(requires_intersection) == 1 srcunits = bundle._bundle['services'][src].get('num_units', 1) src_with_units = "{} \N{MULTIPLICATION SIGN} {}".format(src, srcunits) dstunits = bundle._bundle['services'][dst].get('num_units', 1) dst_with_units = "{} \N{MULTIPLICATION SIGN} {}".format(dst, dstunits) if is_provides: if s_relname == "": s_relname = [ k for k, v in svc_provides[src].items() if v['Interface'] == list(provides_intersection)[0] ][0] if d_relname == "": d_relname = [ k for k, v in svc_requires[dst].items() if v['Interface'] == list(provides_intersection)[0] ][0] if s_relname != d_relname: relname = s_relname + " \N{RIGHTWARDS ARROW} " + d_relname else: relname = s_relname line = "[{}] - {} -> [{}]".format(src_with_units, relname, dst_with_units) graph[src_with_units].append(dst_with_units) elif is_requires: if s_relname == "": s_relname = [ k for k, v in svc_requires[src].items() if v['Interface'] == list(requires_intersection)[0] ][0] if d_relname == "": d_relname = [ k for k, v in svc_provides[dst].items() if v['Interface'] == list(requires_intersection)[0] ][0] if s_relname != d_relname: relname = d_relname + " \N{RIGHTWARDS ARROW} " + s_relname else: relname = s_relname line = "[{}] - {} -> [{}]".format(dst_with_units, relname, src_with_units) graph[dst_with_units].append(src_with_units) else: line = ("[{}] <- {} \N{LEFT RIGHT ARROW} {} -> " "[{}]").format(dst_with_units, s_relname, d_relname, src_with_units) graph[dst_with_units].append(src_with_units) graph[src_with_units].append(dst_with_units) s.append(line) for svc in bundle._bundle['services'].keys(): if svc not in services_seen: s.append("[{}]".format(svc)) return "\n".join(s), graph
def _graph_string_for_bundle(bundle, mc): s = [] graph = defaultdict(list) svc_requires = {} svc_provides = {} for svc, sd in bundle._bundle['services'].items(): csid = CharmStoreID(sd['charm']) info = mc.get_charm_info(csid.as_str_without_rev()) if info is None: md = {} else: md = info['Meta']['charm-metadata'] svc_requires[svc] = md.get("Requires", {}) svc_provides[svc] = md.get("Provides", {}) services_seen = set() for rel_src, rel_dst in bundle._bundle['relations']: def do_split(rel): ss = rel.split(":") if len(ss) == 1: return rel, "" else: return ss[0], ss[1] src, s_relname = do_split(rel_src) dst, d_relname = do_split(rel_dst) services_seen.add(src) services_seen.add(dst) if src not in svc_provides or src not in svc_requires or \ dst not in svc_provides or dst not in svc_requires: continue src_provides = set([v['Interface'] for v in svc_provides[src].values()]) src_requires = set([v['Interface'] for v in svc_requires[src].values()]) dst_provides = set([v['Interface'] for v in svc_provides[dst].values()]) dst_requires = set([v['Interface'] for v in svc_requires[dst].values()]) provides_intersection = src_provides.intersection(dst_requires) is_provides = len(provides_intersection) == 1 requires_intersection = src_requires.intersection(dst_provides) is_requires = len(requires_intersection) == 1 srcunits = bundle._bundle['services'][src].get('num_units', 1) src_with_units = "{} \N{MULTIPLICATION SIGN} {}".format(src, srcunits) dstunits = bundle._bundle['services'][dst].get('num_units', 1) dst_with_units = "{} \N{MULTIPLICATION SIGN} {}".format(dst, dstunits) if is_provides: if s_relname == "": s_relname = [k for k, v in svc_provides[src].items() if v['Interface'] == list(provides_intersection)[0]][0] if d_relname == "": d_relname = [k for k, v in svc_requires[dst].items() if v['Interface'] == list(provides_intersection)[0]][0] if s_relname != d_relname: relname = s_relname + " \N{RIGHTWARDS ARROW} " + d_relname else: relname = s_relname line = "[{}] - {} -> [{}]".format(src_with_units, relname, dst_with_units) graph[src_with_units].append(dst_with_units) elif is_requires: if s_relname == "": s_relname = [k for k, v in svc_requires[src].items() if v['Interface'] == list(requires_intersection)[0]][0] if d_relname == "": d_relname = [k for k, v in svc_provides[dst].items() if v['Interface'] == list(requires_intersection)[0]][0] if s_relname != d_relname: relname = d_relname + " \N{RIGHTWARDS ARROW} " + s_relname else: relname = s_relname line = "[{}] - {} -> [{}]".format(dst_with_units, relname, src_with_units) graph[dst_with_units].append(src_with_units) else: line = ("[{}] <- {} \N{LEFT RIGHT ARROW} {} -> " "[{}]").format(dst_with_units, s_relname, d_relname, src_with_units) graph[dst_with_units].append(src_with_units) graph[src_with_units].append(dst_with_units) s.append(line) for svc in bundle._bundle['services'].keys(): if svc not in services_seen: s.append("[{}]".format(svc)) return "\n".join(s), graph
#!/usr/bin/python3 from bundleplacer.charmstore_api import CharmStoreID ids = ['mysql', 'mysql-9', 'nova-compute', 'nova-cloud-controller', 'cs:~adam-stokes/trusty/ghost', 'cs:~openstack-charmers-next/xenial/lxd', 'cs:~openstack-charmers-next/xenial/nova-compute-12', 'cs:~openstack-charmers-next/xenial/nova-compute', 'cs:bundle/openstack-base-40', 'cs:~containers/easyrsa-2'] for i in ids: print(80*'-') print(i) csid = CharmStoreID(i) print(repr(csid)) print(csid.as_str_without_rev()) print(csid.as_str())