def test_stop(self): """Executing the stop transition, results in the workflow going to the down state. """ file_path = self.makeFile() self.write_hook("install", "#!/bin/bash\necho installed >> %s\n" % file_path) self.write_hook("start", "#!/bin/bash\necho start >> %s\n" % file_path) self.write_hook("stop", "#!/bin/bash\necho stop >> %s\n" % file_path) result = yield self.workflow.fire_transition("install") result = yield self.workflow.fire_transition("start") result = yield self.workflow.fire_transition("stop") self.assertTrue(result) current_state = yield self.workflow.get_state() self.assertEqual(current_state, "stopped") f_state, history, zk_state = yield self.read_persistent_state() self.assertEqual(f_state, zk_state) self.assertEqual(f_state, {"state": "stopped", "state_variables": {}}) workflow_client = WorkflowStateClient(self.client, self.states["unit"]) value = yield workflow_client.get_state() self.assertEqual(value, "stopped") self.assertEqual(history, [{ "state": "installed", "state_variables": {} }, { "state": "started", "state_variables": {} }, { "state": "stopped", "state_variables": {} }])
def _process_unit_relations(self, service, unit, relations, rel_svc_map): """Collect UnitRelationState information per relation and per unit. Includes information under each unit for its relations including its relation state and information about any possible errors. see `_process_relations` for argument information """ u = self.unit_data[unit.unit_name] relation_errors = {} for relation in relations: try: relation_unit = yield relation.get_unit_state(unit) except UnitRelationStateNotFound: # This exception will occur when relations are # established between services without service # units, and therefore never have any # corresponding service relation units. # UPDATE: common with subordinate services, and # some testing scenarios. continue relation_workflow_client = WorkflowStateClient( self.client, relation_unit) workflow_state = yield relation_workflow_client.get_state() rel_svc_name = rel_svc_map.get(relation.internal_relation_id) if rel_svc_name and workflow_state not in ("up", None): relation_errors.setdefault( relation.relation_name, set()).add(rel_svc_name) if relation_errors: # Normalize sets and store. u["relation-errors"] = dict( [(r, sorted(relation_errors[r])) for r in relation_errors])
def test_stop(self): """Executing the stop transition, results in the workflow going to the down state. """ file_path = self.makeFile() self.write_hook( "install", "#!/bin/bash\necho installed >> %s\n" % file_path) self.write_hook( "start", "#!/bin/bash\necho start >> %s\n" % file_path) self.write_hook( "stop", "#!/bin/bash\necho stop >> %s\n" % file_path) result = yield self.workflow.fire_transition("install") result = yield self.workflow.fire_transition("start") result = yield self.workflow.fire_transition("stop") self.assertTrue(result) current_state = yield self.workflow.get_state() self.assertEqual(current_state, "stopped") f_state, history, zk_state = yield self.read_persistent_state() self.assertEqual(f_state, zk_state) self.assertEqual(f_state, {"state": "stopped", "state_variables": {}}) workflow_client = WorkflowStateClient(self.client, self.states["unit"]) value = yield workflow_client.get_state() self.assertEqual(value, "stopped") self.assertEqual(history, [{"state": "installed", "state_variables": {}}, {"state": "started", "state_variables": {}}, {"state": "stopped", "state_variables": {}}])
def test_client_read_only(self): workflow_client = WorkflowStateClient( self.client, self.states["unit_relation"]) with (yield workflow_client.lock()): yield self.assertFailure( workflow_client.set_state("up"), NotImplementedError)
def test_client_readonly(self): yield self.workflow.fire_transition("install") workflow_client = WorkflowStateClient(self.client, self.states["unit"]) self.assertEqual((yield workflow_client.get_state()), "installed") yield self.assertFailure(workflow_client.set_state("started"), NotImplementedError) self.assertEqual((yield workflow_client.get_state()), "installed")
def test_client_readonly(self): with (yield self.workflow.lock()): yield self.workflow.fire_transition("install") workflow_client = WorkflowStateClient( self.client, self.states["unit"]) self.assertEqual( (yield workflow_client.get_state()), "started") with (yield workflow_client.lock()): yield self.assertFailure( workflow_client.set_state("stopped"), NotImplementedError) self.assertEqual( (yield workflow_client.get_state()), "started")
def test_client_read_state(self): """The relation workflow client can read the state of a unit relation.""" yield self.workflow.fire_transition("start") yield self.assertState(self.workflow, "up") self.write_hook("%s-relation-changed" % self.relation_name, "#!/bin/bash\necho hello\n") wait_on_hook = self.wait_on_hook("app-relation-changed") yield self.add_opposite_service_unit(self.states) yield wait_on_hook workflow = WorkflowStateClient( self.client, self.states["unit_relation"]) self.assertEqual("up", (yield workflow.get_state()))
def test_client_read_state(self): """The relation workflow client can read the state of a unit relation.""" yield self.workflow.fire_transition("start") yield self.assertState(self.workflow, "up") self.write_hook("%s-relation-changed" % self.relation_name, "#!/bin/bash\necho hello\n") wait_on_hook = self.wait_on_hook("app-relation-changed") yield self.add_opposite_service_unit(self.states) yield wait_on_hook workflow = WorkflowStateClient(self.client, self.states["unit_relation"]) self.assertEqual("up", (yield workflow.get_state()))
def _process_unit(self, service, unit, relations, rel_svc_map): """ Generate unit info for a single unit of a single service. `unit`: ServiceUnitState see `_process_units` for an explanation of other arguments. """ u = self.unit_data[unit.unit_name] = dict() container = yield unit.get_container() if container: u["container"] = container.unit_name self.subordinates.setdefault(unit.service_name, set()).add(container.service_name) machine_id = yield unit.get_assigned_machine_id() u["machine"] = machine_id unit_workflow_client = WorkflowStateClient(self.client, unit) unit_state = yield unit_workflow_client.get_state() if not unit_state: u["agent-state"] = "pending" else: unit_connected = yield unit.has_agent() u["agent-state"] = unit_state.replace("_", "-") \ if unit_connected else "down" exposed = self.service_data[service.service_name].get("exposed") if exposed: open_ports = yield unit.get_open_ports() u["open-ports"] = ["{port}/{proto}".format(**port_info) for port_info in open_ports] u["public-address"] = yield unit.get_public_address() # indicate we should include information about this # machine later self.seen_machines.add(machine_id) # collect info on each relation for the service unit yield self._process_unit_relations(service, unit, relations, rel_svc_map)
def _process_machine(self, machine_state): """ `machine_state`: MachineState instance """ instance_id = yield machine_state.get_instance_id() m = {"instance-id": instance_id \ if instance_id is not None else "pending"} if instance_id is not None: try: pm = yield self.provider.get_machine(instance_id) m["dns-name"] = pm.dns_name m["instance-state"] = pm.state if (yield machine_state.has_agent()): # if the agent's connected, we're fine m["agent-state"] = "running" else: units = ( yield machine_state.get_all_service_unit_states()) for unit in units: unit_workflow_client = WorkflowStateClient( self.client, unit) if (yield unit_workflow_client.get_state()): # for unit to have a state, its agent must # have run, which implies the machine agent # must have been running correctly at some # point in the past m["agent-state"] = "down" break else: # otherwise we're probably just still waiting m["agent-state"] = "not-started" except ProviderError: # The provider doesn't have machine information self.log.error( "Machine provider information missing: machine %s" % ( machine_state.id)) self.machine_data[machine_state.id] = m
def test_client_read_only(self): workflow_client = WorkflowStateClient(self.client, self.states["unit_relation"]) yield self.assertFailure(workflow_client.set_state("up"), NotImplementedError)
def test_client_read_none(self): workflow = WorkflowStateClient( self.client, self.states["unit_relation"]) self.assertEqual(None, (yield workflow.get_state()))
def test_client_with_state(self): with (yield self.workflow.lock()): yield self.workflow.fire_transition("install") workflow_client = WorkflowStateClient(self.client, self.states["unit"]) self.assertEqual( (yield workflow_client.get_state()), "started")
def test_client_with_no_state(self): workflow_client = WorkflowStateClient(self.client, self.states["unit"]) state = yield workflow_client.get_state() self.assertEqual(state, None)
def test_client_read_none(self): workflow = WorkflowStateClient(self.client, self.states["unit_relation"]) self.assertEqual(None, (yield workflow.get_state()))
def test_client_with_state(self): yield self.workflow.fire_transition("install") workflow_client = WorkflowStateClient(self.client, self.states["unit"]) self.assertEqual((yield workflow_client.get_state()), "installed")
def collect(scope, machine_provider, client, log): """Extract status information into nested dicts for rendering. `scope`: an optional list of name specifiers. Globbing based wildcards supported. Defaults to all units, services and relations. `machine_provider`: machine provider for the environment `client`: ZK client connection `log`: a Python stdlib logger. """ service_manager = ServiceStateManager(client) relation_manager = RelationStateManager(client) machine_manager = MachineStateManager(client) charm_manager = CharmStateManager(client) service_data = {} machine_data = {} state = dict(services=service_data, machines=machine_data) seen_machines = set() filter_services, filter_units = digest_scope(scope) services = yield service_manager.get_all_service_states() for service in services: if len(filter_services): found = False for filter_service in filter_services: if fnmatch(service.service_name, filter_service): found = True break if not found: continue unit_data = {} relation_data = {} charm_id = yield service.get_charm_id() charm = yield charm_manager.get_charm_state(charm_id) service_data[service.service_name] = dict(units=unit_data, charm=charm.id, relations=relation_data) exposed = yield service.get_exposed_flag() if exposed: service_data[service.service_name].update(exposed=exposed) units = yield service.get_all_unit_states() unit_matched = False relations = yield relation_manager.get_relations_for_service(service) for unit in units: if len(filter_units): found = False for filter_unit in filter_units: if fnmatch(unit.unit_name, filter_unit): found = True break if not found: continue u = unit_data[unit.unit_name] = dict() machine_id = yield unit.get_assigned_machine_id() u["machine"] = machine_id unit_workflow_client = WorkflowStateClient(client, unit) unit_state = yield unit_workflow_client.get_state() if not unit_state: u["state"] = "pending" else: unit_connected = yield unit.has_agent() u["state"] = unit_state if unit_connected else "down" if exposed: open_ports = yield unit.get_open_ports() u["open-ports"] = ["{port}/{proto}".format(**port_info) for port_info in open_ports] u["public-address"] = yield unit.get_public_address() # indicate we should include information about this # machine later seen_machines.add(machine_id) unit_matched = True # collect info on each relation for the service unit relation_status = {} for relation in relations: try: relation_unit = yield relation.get_unit_state(unit) except UnitRelationStateNotFound: # This exception will occur when relations are # established between services without service # units, and therefore never have any # corresponding service relation units. This # scenario does not occur in actual deployments, # but can happen in test circumstances. In # particular, it will happen with a misconfigured # provider, which exercises this codepath. continue # should not occur, but status should not fail relation_workflow_client = WorkflowStateClient( client, relation_unit) relation_workflow_state = \ yield relation_workflow_client.get_state() relation_status[relation.relation_name] = dict( state=relation_workflow_state) u["relations"] = relation_status # after filtering units check if any matched or remove the # service from the output if filter_units and not unit_matched: del service_data[service.service_name] continue for relation in relations: rel_services = yield relation.get_service_states() # A single related service implies a peer relation. More # imply a bi-directional provides/requires relationship. # In the later case we omit the local side of the relation # when reporting. if len(rel_services) > 1: # Filter out self from multi-service relations. rel_services = [ rsn for rsn in rel_services if rsn.service_name != service.service_name] if len(rel_services) > 1: raise ValueError("Unexpected relationship with more " "than 2 endpoints") rel_service = rel_services[0] relation_data[relation.relation_name] = rel_service.service_name machines = yield machine_manager.get_all_machine_states() for machine_state in machines: if (filter_services or filter_units) and \ machine_state.id not in seen_machines: continue instance_id = yield machine_state.get_instance_id() m = {"instance-id": instance_id \ if instance_id is not None else "pending"} if instance_id is not None: try: pm = yield machine_provider.get_machine(instance_id) m["dns-name"] = pm.dns_name m["instance-state"] = pm.state if (yield machine_state.has_agent()): # if the agent's connected, we're fine m["state"] = "running" else: units = (yield machine_state.get_all_service_unit_states()) for unit in units: unit_workflow_client = WorkflowStateClient(client, unit) if (yield unit_workflow_client.get_state()): # for unit to have a state, its agent must have # run, which implies the machine agent must have # been running correctly at some point in the past m["state"] = "down" break else: # otherwise we're probably just still waiting m["state"] = "not-started" except ProviderError: # The provider doesn't have machine information log.error( "Machine provider information missing: machine %s" % ( machine_state.id)) machine_data[machine_state.id] = m returnValue(state)
def collect(scope, machine_provider, client, log): """Extract status information into nested dicts for rendering. `scope`: an optional list of name specifiers. Globbing based wildcards supported. Defaults to all units, services and relations. `machine_provider`: machine provider for the environment `client`: ZK client connection `log`: a Python stdlib logger. """ service_manager = ServiceStateManager(client) relation_manager = RelationStateManager(client) machine_manager = MachineStateManager(client) charm_manager = CharmStateManager(client) service_data = {} machine_data = {} state = dict(services=service_data, machines=machine_data) seen_machines = set() filter_services, filter_units = digest_scope(scope) services = yield service_manager.get_all_service_states() for service in services: if len(filter_services): found = False for filter_service in filter_services: if fnmatch(service.service_name, filter_service): found = True break if not found: continue unit_data = {} relation_data = {} charm_id = yield service.get_charm_id() charm = yield charm_manager.get_charm_state(charm_id) service_data[service.service_name] = dict(units=unit_data, charm=charm.id, relations=relation_data) exposed = yield service.get_exposed_flag() if exposed: service_data[service.service_name].update(exposed=exposed) units = yield service.get_all_unit_states() unit_matched = False relations = yield relation_manager.get_relations_for_service(service) for unit in units: if len(filter_units): found = False for filter_unit in filter_units: if fnmatch(unit.unit_name, filter_unit): found = True break if not found: continue u = unit_data[unit.unit_name] = dict() machine_id = yield unit.get_assigned_machine_id() u["machine"] = machine_id unit_workflow_client = WorkflowStateClient(client, unit) unit_state = yield unit_workflow_client.get_state() if not unit_state: u["state"] = "pending" else: unit_connected = yield unit.has_agent() u["state"] = unit_state if unit_connected else "down" if exposed: open_ports = yield unit.get_open_ports() u["open-ports"] = [ "{port}/{proto}".format(**port_info) for port_info in open_ports ] u["public-address"] = yield unit.get_public_address() # indicate we should include information about this # machine later seen_machines.add(machine_id) unit_matched = True # collect info on each relation for the service unit relation_status = {} for relation in relations: try: relation_unit = yield relation.get_unit_state(unit) except UnitRelationStateNotFound: # This exception will occur when relations are # established between services without service # units, and therefore never have any # corresponding service relation units. This # scenario does not occur in actual deployments, # but can happen in test circumstances. In # particular, it will happen with a misconfigured # provider, which exercises this codepath. continue # should not occur, but status should not fail relation_workflow_client = WorkflowStateClient( client, relation_unit) relation_workflow_state = \ yield relation_workflow_client.get_state() relation_status[relation.relation_name] = dict( state=relation_workflow_state) u["relations"] = relation_status # after filtering units check if any matched or remove the # service from the output if filter_units and not unit_matched: del service_data[service.service_name] continue for relation in relations: rel_services = yield relation.get_service_states() # A single related service implies a peer relation. More # imply a bi-directional provides/requires relationship. # In the later case we omit the local side of the relation # when reporting. if len(rel_services) > 1: # Filter out self from multi-service relations. rel_services = [ rsn for rsn in rel_services if rsn.service_name != service.service_name ] if len(rel_services) > 1: raise ValueError("Unexpected relationship with more " "than 2 endpoints") rel_service = rel_services[0] relation_data[relation.relation_name] = rel_service.service_name machines = yield machine_manager.get_all_machine_states() for machine_state in machines: if (filter_services or filter_units) and \ machine_state.id not in seen_machines: continue instance_id = yield machine_state.get_instance_id() m = {"instance-id": instance_id \ if instance_id is not None else "pending"} if instance_id is not None: try: pm = yield machine_provider.get_machine(instance_id) m["dns-name"] = pm.dns_name m["instance-state"] = pm.state if (yield machine_state.has_agent()): # if the agent's connected, we're fine m["state"] = "running" else: units = (yield machine_state.get_all_service_unit_states()) for unit in units: unit_workflow_client = WorkflowStateClient( client, unit) if (yield unit_workflow_client.get_state()): # for unit to have a state, its agent must have # run, which implies the machine agent must have # been running correctly at some point in the past m["state"] = "down" break else: # otherwise we're probably just still waiting m["state"] = "not-started" except ProviderError: # The provider doesn't have machine information log.error("Machine provider information missing: machine %s" % (machine_state.id)) machine_data[machine_state.id] = m returnValue(state)