def test_compile_url_compiler_http(self): """Test that the url compiler produces a http request when http is specified.""" myclient = SuiteRuntimeServiceClient("test-suite", host=get_host(), port=80) myclient.comms1[SuiteSrvFilesManager.KEY_COMMS_PROTOCOL] = 'http' self.assertEqual( 'http://%s:80/test_command?apples=False&oranges=True' % get_host(), myclient._call_server_get_url("test_command", apples="False", oranges="True"))
def test_url_compiler_https(self): """Tests that the url parser works for a single url and command using https""" myclient = SuiteRuntimeServiceClient("test-suite", host=get_host(), port=80) myclient.comms1[SuiteSrvFilesManager.KEY_COMMS_PROTOCOL] = 'https' self.assertEqual( 'https://%s:80/test_command?apples=False&oranges=True' % get_host(), myclient._call_server_get_url("test_command", apples="False", oranges="True"))
def test_compile_url_compiler_none_specified(self): """Test that the url compiler produces a http request when none is specified. This should retrieve it from the global config.""" myclient = SuiteRuntimeServiceClient("test-suite", host=get_host(), port=80) url = myclient._call_server_get_url("test_command", apples="False", oranges="True") # Check that the url has had http (or https) appended # to it. (If it does not start with "http*" then something # has gone wrong.) self.assertTrue(url.startswith("http"))
def _scan_item(timeout, my_uuid, srv_files_mgr, item): """Connect to item host:port (item) to get suite identify.""" host, port = item host_anon = host if is_remote_host(host): try: host_anon = get_host_ip_by_name(host) # IP reduces DNS traffic except socket.error as exc: if cylc.flags.debug: raise sys.stderr.write("ERROR: %s: %s\n" % (exc, host)) return (host, port, None) client = SuiteRuntimeServiceClient( None, host=host_anon, port=port, my_uuid=my_uuid, timeout=timeout, auth=SuiteRuntimeServiceClient.ANON_AUTH) try: result = client.identify() except ClientTimeout: return (host, port, MSG_TIMEOUT) except ClientError: return (host, port, None) else: owner = result.get(KEY_OWNER) name = result.get(KEY_NAME) states = result.get(KEY_STATES, None) if cylc.flags.debug: sys.stderr.write(' suite: %s %s\n' % (name, owner)) if states is None: # This suite keeps its state info private. # Try again with the passphrase if I have it. try: pphrase = srv_files_mgr.get_auth_item( srv_files_mgr.FILE_BASE_PASSPHRASE, name, owner, host, content=True) except SuiteServiceFileError: pass else: if pphrase: client.suite = name client.owner = owner client.auth = None try: result = client.identify() except ClientError: # Nope (private suite, wrong passphrase). if cylc.flags.debug: sys.stderr.write(' (wrong passphrase)\n') else: if cylc.flags.debug: sys.stderr.write( ' (got states with passphrase)\n') return (host, port, result)
def _send_by_remote_port(self, messages): """Send message by talking to the daemon (remote?) port.""" from cylc.network.httpclient import (SuiteRuntimeServiceClient, ClientError, ClientInfoError) # Convert time/duration into appropriate units retry_intvl = float( self.env_map.get(SuiteSrvFilesManager.KEY_TASK_MSG_RETRY_INTVL, self.MSG_RETRY_INTVL)) max_tries = int( self.env_map.get(SuiteSrvFilesManager.KEY_TASK_MSG_MAX_TRIES, self.MSG_MAX_TRIES)) client = SuiteRuntimeServiceClient( self.suite, owner=self.env_map.get(SuiteSrvFilesManager.KEY_OWNER), host=self.env_map.get(SuiteSrvFilesManager.KEY_HOST), port=self.env_map.get(SuiteSrvFilesManager.KEY_PORT), timeout=float( self.env_map.get(SuiteSrvFilesManager.KEY_TASK_MSG_TIMEOUT, self.MSG_TIMEOUT)), comms_protocol=self.env_map.get( SuiteSrvFilesManager.KEY_COMMS_PROTOCOL)) for i in range(1, max_tries + 1): # 1..max_tries inclusive try: for message in messages: client.put_message(self.task_id, self.severity, message) except ClientError as exc: sys.stderr.write( "%s WARNING - Send message: try %s of %s failed: %s\n" % (get_current_time_string(), i, max_tries, exc)) # Break if: # * Exhausted number of tries. # * Contact info file not found, suite probably not running. # Don't bother with retry, suite restart will poll any way. if i >= max_tries or isinstance(exc, ClientInfoError): # Issue a warning and let the task carry on sys.stderr.write("%s WARNING - MESSAGE SEND FAILED\n" % (get_current_time_string())) else: sys.stderr.write( " retry in %s seconds, timeout is %s\n" % (retry_intvl, client.timeout)) sleep(retry_intvl) # Reset in case contact info or passphrase change client.host = None client.port = None client.auth = None else: if i > 1: # Continue to write to STDERR, so users can easily see that # it has recovered from previous failures. sys.stderr.write( "%s INFO - Send message: try %s of %s succeeded\n" % (get_current_time_string(), i, max_tries)) break
def record_messages(suite, task_job, messages): """Record task job messages. Print the messages according to their severity. Write the messages in the job status file. Send the messages to the suite, if possible. Arguments: suite (str): Suite name. task_job (str): Task job identifier "CYCLE/TASK_NAME/SUBMIT_NUM". messages (list): List of messages "[[severity, message], ...]". """ # Record the event time, in case the message is delayed in some way. event_time = get_current_time_string( override_use_utc=(os.getenv('CYLC_UTC') == 'True')) # Print to stdout/stderr for severity, message in messages: if severity in STDERR_LEVELS: handle = sys.stderr else: handle = sys.stdout handle.write('%s %s - %s\n' % (event_time, severity, message)) handle.flush() # Write to job.status _append_job_status_file(suite, task_job, event_time, messages) # Send messages client = SuiteRuntimeServiceClient(suite) try: client.put_messages({ 'task_job': task_job, 'event_time': event_time, 'messages': messages }) except ClientInfoError: # Backward communication not possible if cylc.flags.debug: import traceback traceback.print_exc()
def record_messages(suite, task_job, messages): """Record task job messages. Print the messages according to their severity. Write the messages in the job status file. Send the messages to the suite, if possible. Arguments: suite (str): Suite name. task_job (str): Task job identifier "CYCLE/TASK_NAME/SUBMIT_NUM". messages (list): List of messages "[[severity, message], ...]". """ # Record the event time, in case the message is delayed in some way. event_time = get_current_time_string( override_use_utc=(os.getenv('CYLC_UTC') == 'True')) # Print to stdout/stderr for severity, message in messages: if severity in STDERR_LEVELS: handle = sys.stderr else: handle = sys.stdout handle.write('%s %s - %s\n' % (event_time, severity, message)) handle.flush() # Write to job.status _append_job_status_file(suite, task_job, event_time, messages) # Send messages client = SuiteRuntimeServiceClient(suite) try: client.put_messages({ 'task_job': task_job, 'event_time': event_time, 'messages': messages}) except ClientInfoError: # Backward communication not possible if cylc.flags.debug: import traceback traceback.print_exc()
def _scan_item(timeout, my_uuid, srv_files_mgr, item): """Connect to item host:port (item) to get suite identify.""" host, port = item host_anon = host if is_remote_host(host): host_anon = get_host_ip_by_name(host) # IP reduces DNS traffic client = SuiteRuntimeServiceClient( None, host=host_anon, port=port, my_uuid=my_uuid, timeout=timeout, auth=SuiteRuntimeServiceClient.ANON_AUTH) try: result = client.identify() except ClientTimeout: return (host, port, MSG_TIMEOUT) except ClientError: return (host, port, None) else: owner = result.get(KEY_OWNER) name = result.get(KEY_NAME) states = result.get(KEY_STATES, None) if cylc.flags.debug: sys.stderr.write(' suite: %s %s\n' % (name, owner)) if states is None: # This suite keeps its state info private. # Try again with the passphrase if I have it. try: pphrase = srv_files_mgr.get_auth_item( srv_files_mgr.FILE_BASE_PASSPHRASE, name, owner, host, content=True) except SuiteServiceFileError: pass else: if pphrase: client.suite = name client.owner = owner client.auth = None try: result = client.identify() except ClientError: # Nope (private suite, wrong passphrase). if cylc.flags.debug: sys.stderr.write(' (wrong passphrase)\n') else: if cylc.flags.debug: sys.stderr.write( ' (got states with passphrase)\n') return (host, port, result)
class Updater(threading.Thread): """Retrieve information about the running or stopped suite.""" def __init__(self, app): super(Updater, self).__init__() self.quit = False self.app_window = app.window self.cfg = app.cfg self.info_bar = app.info_bar self.full_mode = True self.err_log_lines = [] self.task_list = [] self.state_summary = {} self.full_state_summary = {} self.fam_state_summary = {} self.full_fam_state_summary = {} self.all_families = {} self.global_summary = {} self.ancestors = {} self.ancestors_pruned = {} self.descendants = {} self.stop_summary = None self.mode = "waiting..." self.update_time_str = "waiting..." self.last_update_time = time() self.update_interval = 1.0 self.max_update_interval = gcfg.get(['maximum update interval']) self.status = SUITE_STATUS_NOT_CONNECTED self.is_reloading = False self.connected = False self.no_update_event = threading.Event() self.ns_defn_order = [] self.dict_ns_defn_order = {} self.restricted_display = app.restricted_display self.filter_name_string = '' self.filter_states_excl = [] self.kept_task_ids = set() self.filt_task_ids = set() self.version_mismatch_warned = False self.client = None # Report sign-out on exit. atexit.register(self.signout) def signout(self): """Sign out the client, if possible.""" if self.client is not None: try: self.client.signout() except ClientError: pass def set_stopped(self): """Reset data and clients when suite is stopped.""" if cylc.flags.debug: sys.stderr.write("%s NOT CONNECTED\n" % get_current_time_string()) self.full_mode = True self.connected = False self.set_status(SUITE_STATUS_STOPPED) self.update_interval += 1.0 if self.update_interval > self.max_update_interval: self.update_interval = self.max_update_interval self.state_summary = {} self.full_state_summary = {} self.fam_state_summary = {} self.full_fam_state_summary = {} self.all_families = {} self.global_summary = {} self.cfg.port = None self.client = None gobject.idle_add(self.app_window.set_title, str(self.cfg.suite)) # Use info bar to display stop summary if available. # Otherwise, just display the reconnect count down. if self.cfg.suite and self.stop_summary is None: stop_summary = get_stop_state_summary( cat_state(self.cfg.suite, self.cfg.host, self.cfg.owner)) if stop_summary != self.stop_summary: self.stop_summary = stop_summary self.status = SUITE_STATUS_STOPPED gobject.idle_add( self.info_bar.set_stop_summary, stop_summary) self.last_update_time = time() try: update_time_str = time2str(self.stop_summary[0]["last_updated"]) except (AttributeError, IndexError, KeyError, TypeError): update_time_str = None gobject.idle_add( self.info_bar.set_update_time, update_time_str, self.info_bar.DISCONNECTED_TEXT) gobject.idle_add(self.info_bar.prog_bar_stop) def set_status(self, status=None): """Update status bar.""" if status == self.status: return if status is not None: self.status = status gobject.idle_add(self.info_bar.set_status, self.status) def warn(self, msg): """Pop up a warning dialog; call on idle_add!""" warning_dialog(msg, self.info_bar.get_toplevel()).warn() return False def filter_by_name(self, states): """Filter by name string.""" return dict( (i, j) for i, j in states.items() if self.filter_name_string in j['name'] or re.search(self.filter_name_string, j['name'])) def filter_by_state(self, states): """Filter by state key.""" return dict( (i, j) for i, j in states.items() if j['state'] not in self.filter_states_excl) def filter_families(self, families): """Remove family summaries if no members are present.""" fam_states = {} for fam_id, summary in families.items(): name, point_string = TaskID.split(fam_id) for mem in self.descendants[name]: mem_id = TaskID.get(mem, point_string) if mem_id in self.state_summary: fam_states[fam_id] = summary break return fam_states @classmethod def filter_for_restricted_display(cls, states): """Filter for legal restricted states.""" return dict((i, j) for i, j in states.items() if j['state'] in TASK_STATUSES_RESTRICTED) def refilter(self): """filter from the full state summary""" if self.filter_name_string or self.filter_states_excl: states = self.full_state_summary all_ids = set(states.keys()) if self.filter_name_string: states = self.filter_by_name(states) if self.filter_states_excl: states = self.filter_by_state(states) self.state_summary = states fam_states = self.full_fam_state_summary self.fam_state_summary = self.filter_families(fam_states) self.kept_task_ids = set(states.keys()) self.filt_task_ids = all_ids - self.kept_task_ids else: self.state_summary = self.full_state_summary self.fam_state_summary = self.full_fam_state_summary self.filt_task_ids = set() self.kept_task_ids = set(self.state_summary) self.task_list = list( set(t['name'] for t in self.state_summary.values())) self.task_list.sort() def stop(self): """Tell self.run to exit.""" self.quit = True def run(self): """Start the thread.""" prev_update_time = time() while not self.quit: now = time() if self.no_update_event.is_set(): pass elif now > prev_update_time + self.update_interval: self.update() prev_update_time = time() else: duration = round(prev_update_time + self.update_interval - now) if self.update_interval >= self.max_update_interval: self.info_bar.set_update_time(None, duration2str(duration)) sleep(1) def update(self): """Call suite for an update.""" if self.client is None: self.client = SuiteRuntimeServiceClient( self.cfg.suite, self.cfg.owner, self.cfg.host, self.cfg.port, self.cfg.comms_timeout, self.cfg.my_uuid) try: my_state = self.client.get_latest_state(full_mode=self.full_mode) except ClientError: # Bad credential, suite not running, starting up or just stopped? if cylc.flags.debug: try: traceback.print_exc() except IOError: pass # Cannot print to terminal (session may be closed). self.set_stopped() return # OK if cylc.flags.debug: sys.stderr.write("%s CONNECTED - suite cylc version=%s\n" % ( get_current_time_string(), my_state['cylc_version'])) self.info_bar.set_update_time(None, None) if my_state['full_mode']: gobject.idle_add( self.app_window.set_title, "%s - %s:%s" % ( self.cfg.suite, self.client.host, self.client.port)) # Connected. self.full_mode = False self.connected = True # This status will be very transient: self.set_status(SUITE_STATUS_CONNECTED) if my_state['cylc_version'] != CYLC_VERSION: gobject.idle_add(self.warn, ( "Warning: cylc version mismatch!\n\n" "Suite running with %r.\ngcylc at %r.\n" ) % (my_state['cylc_version'], CYLC_VERSION)) self.stop_summary = None self.err_log_lines[:] = [] is_updated = False if 'err_content' in my_state and 'err_size' in my_state: self._update_err_log(my_state) is_updated = True if 'ancestors' in my_state: self.ancestors = my_state['ancestors'] is_updated = True if 'ancestors_pruned' in my_state: self.ancestors_pruned = my_state['ancestors_pruned'] is_updated = True if 'descendants' in my_state: self.descendants = my_state['descendants'] self.all_families = list(self.descendants) is_updated = True if 'summary' in my_state and my_state['summary'][0]: self._update_state_summary(my_state) is_updated = True if self.status in [SUITE_STATUS_INITIALISING, SUITE_STATUS_STOPPING]: gobject.idle_add(self.info_bar.prog_bar_start, self.status) elif self.is_reloading: gobject.idle_add(self.info_bar.prog_bar_start, "reloading") else: gobject.idle_add(self.info_bar.prog_bar_stop) # Adjust next update duration: # If there is an update, readjust to 1.0s or the mean duration of the # last 10 main loop. If there is no update, it should be less frequent # than the last update duration. The maximum duration is # max_update_interval seconds. This should allow the GUI to update # more while the main loop is turning around events quickly, but less # frequently during quiet time or when the main loop is busy. if is_updated: self.update_interval = 1.0 self.last_update_time = time() elif time() - self.last_update_time > self.update_interval: self.update_interval += 1.0 if ('mean_main_loop_interval' in my_state and my_state['mean_main_loop_interval'] > self.update_interval): self.update_interval = my_state['mean_main_loop_interval'] if self.update_interval > self.max_update_interval: self.update_interval = self.max_update_interval def _update_err_log(self, my_state): """Display suite err log info if necessary.""" self.err_log_lines += my_state['err_content'].splitlines() self.err_log_lines = self.err_log_lines[-10:] gobject.idle_add( self.info_bar.set_log, "\n".join(self.err_log_lines), my_state['err_size']) def _update_state_summary(self, my_state): """Display suite summary.""" glbl, states, fam_states = my_state['summary'] self.mode = glbl['run_mode'] if self.cfg.use_defn_order: nsdo = glbl['namespace definition order'] if self.ns_defn_order != nsdo: self.ns_defn_order = nsdo self.dict_ns_defn_order = dict(zip(nsdo, range(0, len(nsdo)))) self.update_time_str = time2str(glbl['last_updated']) self.global_summary = glbl if self.restricted_display: states = self.filter_for_restricted_display(states) self.full_state_summary = states self.full_fam_state_summary = fam_states self.refilter() self.status = glbl['status_string'] self.is_reloading = glbl['reloading'] gobject.idle_add( self.info_bar.set_state, self.global_summary.get("states", [])) gobject.idle_add(self.info_bar.set_mode, self.mode) gobject.idle_add(self.info_bar.set_update_time, self.update_time_str) gobject.idle_add(self.info_bar.set_status, self.status)
def update(self): """Call suite for an update.""" if self.client is None: self.client = SuiteRuntimeServiceClient( self.cfg.suite, self.cfg.owner, self.cfg.host, self.cfg.port, self.cfg.comms_timeout, self.cfg.my_uuid) try: my_state = self.client.get_latest_state(full_mode=self.full_mode) except ClientError: # Bad credential, suite not running, starting up or just stopped? if cylc.flags.debug: try: traceback.print_exc() except IOError: pass # Cannot print to terminal (session may be closed). self.set_stopped() return # OK if cylc.flags.debug: sys.stderr.write("%s CONNECTED - suite cylc version=%s\n" % ( get_current_time_string(), my_state['cylc_version'])) self.info_bar.set_update_time(None, None) if my_state['full_mode']: gobject.idle_add( self.app_window.set_title, "%s - %s:%s" % ( self.cfg.suite, self.client.host, self.client.port)) # Connected. self.full_mode = False self.connected = True # This status will be very transient: self.set_status(SUITE_STATUS_CONNECTED) if my_state['cylc_version'] != CYLC_VERSION: gobject.idle_add(self.warn, ( "Warning: cylc version mismatch!\n\n" "Suite running with %r.\ngcylc at %r.\n" ) % (my_state['cylc_version'], CYLC_VERSION)) self.stop_summary = None self.err_log_lines[:] = [] is_updated = False if 'err_content' in my_state and 'err_size' in my_state: self._update_err_log(my_state) is_updated = True if 'ancestors' in my_state: self.ancestors = my_state['ancestors'] is_updated = True if 'ancestors_pruned' in my_state: self.ancestors_pruned = my_state['ancestors_pruned'] is_updated = True if 'descendants' in my_state: self.descendants = my_state['descendants'] self.all_families = list(self.descendants) is_updated = True if 'summary' in my_state and my_state['summary'][0]: self._update_state_summary(my_state) is_updated = True if self.status in [SUITE_STATUS_INITIALISING, SUITE_STATUS_STOPPING]: gobject.idle_add(self.info_bar.prog_bar_start, self.status) elif self.is_reloading: gobject.idle_add(self.info_bar.prog_bar_start, "reloading") else: gobject.idle_add(self.info_bar.prog_bar_stop) # Adjust next update duration: # If there is an update, readjust to 1.0s or the mean duration of the # last 10 main loop. If there is no update, it should be less frequent # than the last update duration. The maximum duration is # max_update_interval seconds. This should allow the GUI to update # more while the main loop is turning around events quickly, but less # frequently during quiet time or when the main loop is busy. if is_updated: self.update_interval = 1.0 self.last_update_time = time() elif time() - self.last_update_time > self.update_interval: self.update_interval += 1.0 if ('mean_main_loop_interval' in my_state and my_state['mean_main_loop_interval'] > self.update_interval): self.update_interval = my_state['mean_main_loop_interval'] if self.update_interval > self.max_update_interval: self.update_interval = self.max_update_interval