def clear_broadcast(self, point_strings=None, namespaces=None, cancel_settings=None): """Clear broadcasts globally, or for listed namespaces and/or points. Return a tuple (modified_settings, bad_options), where: * modified_settings is similar to the return value of the "put" method, but for removed broadcasts. * bad_options is a dict in the form: {"point_strings": ["20020202", ..."], ...} The dict is only populated if there are options not associated with previous broadcasts. The keys can be: * point_strings: a list of bad point strings. * namespaces: a list of bad namespaces. * cancel: a list of tuples. Each tuple contains the keys of a bad setting. """ # If cancel_settings defined, only clear specific broadcasts cancel_keys_list = self._settings_to_keys_list(cancel_settings) # Clear broadcasts modified_settings = [] with self.lock: for point_string, point_string_settings in self.broadcasts.items(): if point_strings and point_string not in point_strings: continue for namespace, namespace_settings in ( point_string_settings.items()): if namespaces and namespace not in namespaces: continue stuff_stack = [([], namespace_settings)] while stuff_stack: keys, stuff = stuff_stack.pop() for key, value in stuff.items(): if isinstance(value, dict): stuff_stack.append((keys + [key], value)) elif (not cancel_keys_list or keys + [key] in cancel_keys_list): stuff[key] = None setting = {key: value} for rkey in reversed(keys): setting = {rkey: setting} modified_settings.append( (point_string, namespace, setting)) # Prune any empty branches bad_options = self._get_bad_options(self._prune(), point_strings, namespaces, cancel_keys_list) # Log the broadcast self.suite_db_mgr.put_broadcast(modified_settings, is_cancel=True) LOG.info(get_broadcast_change_report(modified_settings, is_cancel=True)) if bad_options: LOG.error(get_broadcast_bad_options_report(bad_options)) if modified_settings: self.data_store_mgr.delta_broadcast() return modified_settings, bad_options
def clear_broadcast( self, point_strings=None, namespaces=None, cancel_settings=None): """Clear broadcasts globally, or for listed namespaces and/or points. Return a tuple (modified_settings, bad_options), where: * modified_settings is similar to the return value of the "put" method, but for removed broadcasts. * bad_options is a dict in the form: {"point_strings": ["20020202", ..."], ...} The dict is only populated if there are options not associated with previous broadcasts. The keys can be: * point_strings: a list of bad point strings. * namespaces: a list of bad namespaces. * cancel: a list of tuples. Each tuple contains the keys of a bad setting. """ # If cancel_settings defined, only clear specific broadcasts cancel_keys_list = self._settings_to_keys_list(cancel_settings) # Clear broadcasts modified_settings = [] with self.lock: for point_string, point_string_settings in self.broadcasts.items(): if point_strings and point_string not in point_strings: continue for namespace, namespace_settings in ( point_string_settings.items()): if namespaces and namespace not in namespaces: continue stuff_stack = [([], namespace_settings)] while stuff_stack: keys, stuff = stuff_stack.pop() for key, value in stuff.items(): if isinstance(value, dict): stuff_stack.append((keys + [key], value)) elif (not cancel_keys_list or keys + [key] in cancel_keys_list): stuff[key] = None setting = {key: value} for rkey in reversed(keys): setting = {rkey: setting} modified_settings.append( (point_string, namespace, setting)) # Prune any empty branches bad_options = self._get_bad_options( self._prune(), point_strings, namespaces, cancel_keys_list) # Log the broadcast self.suite_db_mgr.put_broadcast(modified_settings, is_cancel=True) LOG.info( get_broadcast_change_report(modified_settings, is_cancel=True)) if bad_options: LOG.error(get_broadcast_bad_options_report(bad_options)) return modified_settings, bad_options
def put_broadcast(self, point_strings=None, namespaces=None, settings=None): """Add new broadcast settings (server side interface). Return a tuple (modified_settings, bad_options) where: modified_settings is list of modified settings in the form: [("20200202", "foo", {"script": "true"}, ...] bad_options is as described in the docstring for self.clear(). """ modified_settings = [] bad_point_strings = [] bad_namespaces = [] with self.lock: for setting in settings: for point_string in point_strings: # Standardise the point and check its validity. bad_point = False try: point_string = standardise_point_string(point_string) except PointParsingError: if point_string != '*': bad_point_strings.append(point_string) bad_point = True if not bad_point and point_string not in self.broadcasts: self.broadcasts[point_string] = {} for namespace in namespaces: if namespace not in self.linearized_ancestors: bad_namespaces.append(namespace) elif not bad_point: if namespace not in self.broadcasts[point_string]: self.broadcasts[point_string][namespace] = {} addict(self.broadcasts[point_string][namespace], setting) modified_settings.append( (point_string, namespace, setting)) # Log the broadcast self.suite_db_mgr.put_broadcast(modified_settings) LOG.info(get_broadcast_change_report(modified_settings)) bad_options = {} if bad_point_strings: bad_options["point_strings"] = bad_point_strings if bad_namespaces: bad_options["namespaces"] = bad_namespaces if modified_settings: self.data_store_mgr.delta_broadcast() return modified_settings, bad_options
def put_broadcast( self, point_strings=None, namespaces=None, settings=None): """Add new broadcast settings (server side interface). Return a tuple (modified_settings, bad_options) where: modified_settings is list of modified settings in the form: [("20200202", "foo", {"script": "true"}, ...] bad_options is as described in the docstring for self.clear(). """ modified_settings = [] bad_point_strings = [] bad_namespaces = [] with self.lock: for setting in settings: for point_string in point_strings: # Standardise the point and check its validity. bad_point = False try: point_string = standardise_point_string(point_string) except PointParsingError: if point_string != '*': bad_point_strings.append(point_string) bad_point = True if not bad_point and point_string not in self.broadcasts: self.broadcasts[point_string] = {} for namespace in namespaces: if namespace not in self.linearized_ancestors: bad_namespaces.append(namespace) elif not bad_point: if namespace not in self.broadcasts[point_string]: self.broadcasts[point_string][namespace] = {} self._addict( self.broadcasts[point_string][namespace], setting) modified_settings.append( (point_string, namespace, setting)) # Log the broadcast self.suite_db_mgr.put_broadcast(modified_settings) LOG.info(get_broadcast_change_report(modified_settings)) bad_options = {} if bad_point_strings: bad_options["point_strings"] = bad_point_strings if bad_namespaces: bad_options["namespaces"] = bad_namespaces return modified_settings, bad_options
def main(_, options, suite): """Implement cylc broadcast.""" pclient = SuiteRuntimeClient( suite, options.owner, options.host, options.port, options.comms_timeout) if options.show or options.showtask: if options.showtask: try: TaskID.split(options.showtask) except ValueError: raise UserInputError("TASKID must be " + TaskID.SYNTAX) settings = pclient('get_broadcast', {'task_id': options.showtask}) padding = get_padding(settings) * ' ' if options.raw: print(str(settings)) else: print_tree(settings, padding, options.unicode) sys.exit(0) if options.clear: modified_settings, bad_options = pclient( 'clear_broadcast', {'point_strings': options.point_strings, 'namespaces': options.namespaces} ) if modified_settings: print(get_broadcast_change_report( modified_settings, is_cancel=True)) sys.exit(report_bad_options(bad_options)) if options.expire: modified_settings, bad_options = pclient( 'expire_broadcast', {'cutoff': options.expire} ) if modified_settings: print(get_broadcast_change_report( modified_settings, is_cancel=True)) sys.exit(report_bad_options(bad_options)) # implement namespace and cycle point defaults here namespaces = options.namespaces if not namespaces: namespaces = ["root"] point_strings = options.point_strings if not point_strings: point_strings = ["*"] if options.cancel or options.cancel_files: settings = [] for option_item in options.cancel: if "=" in option_item: raise UserInputError( "--cancel=[SEC]ITEM does not take a value") option_item = option_item.strip() setting = get_rdict(option_item) settings.append(setting) files_to_settings(settings, options.cancel_files, options.cancel) modified_settings, bad_options = pclient( 'clear_broadcast', {'point_strings': point_strings, 'namespaces': namespaces, 'cancel_settings': settings} ) if modified_settings: print(get_broadcast_change_report( modified_settings, is_cancel=True)) sys.exit(report_bad_options(bad_options)) if options.settings or options.setting_files: settings = [] for option_item in options.settings: if "=" not in option_item: raise UserInputError( "--set=[SEC]ITEM=VALUE requires a value") lhs, rhs = [s.strip() for s in option_item.split("=", 1)] setting = get_rdict(lhs, rhs) settings.append(setting) files_to_settings(settings, options.setting_files) modified_settings, bad_options = pclient( 'put_broadcast', {'point_strings': point_strings, 'namespaces': namespaces, 'settings': settings } ) print(get_broadcast_change_report(modified_settings)) sys.exit(report_bad_options(bad_options, is_set=True))
def main(_, options, suite): """Implement cylc broadcast.""" pclient = SuiteRuntimeClient(suite, timeout=options.comms_timeout) mutation_kwargs = { 'request_string': MUTATION, 'variables': { 'wFlows': [suite], 'bMode': 'Set', 'cPoints': options.point_strings, 'nSpaces': options.namespaces, 'bSettings': options.settings, 'bCutoff': options.expire, } } query_kwargs = { 'request_string': QUERY, 'variables': { 'wFlows': [suite], 'nIds': [] } } if options.show or options.showtask: if options.showtask: try: task, point = TaskID.split(options.showtask) query_kwargs['variables']['nIds'] = [ f'{point}{ID_DELIM}{task}' ] except ValueError: raise UserInputError("TASKID must be " + TaskID.SYNTAX) result = pclient('graphql', query_kwargs) for wflow in result['workflows']: settings = wflow['broadcasts'] padding = get_padding(settings) * ' ' if options.raw: print(str(settings)) else: print_tree(settings, padding, options.unicode) sys.exit(0) report_cancel = True report_set = False if options.clear: mutation_kwargs['variables']['bMode'] = 'Clear' if options.expire: mutation_kwargs['variables']['bMode'] = 'Expire' # implement namespace and cycle point defaults here namespaces = options.namespaces if not namespaces: namespaces = ["root"] point_strings = options.point_strings if not point_strings: point_strings = ["*"] if options.cancel or options.cancel_files: settings = [] for option_item in options.cancel: if "=" in option_item: raise UserInputError( "--cancel=[SEC]ITEM does not take a value") option_item = option_item.strip() setting = get_rdict(option_item) settings.append(setting) files_to_settings(settings, options.cancel_files, options.cancel) mutation_kwargs['variables'].update({ 'bMode': 'Clear', 'cPoints': point_strings, 'nSpaces': namespaces, 'bSettings': settings, }) if options.settings or options.setting_files: settings = [] for option_item in options.settings: if "=" not in option_item: raise UserInputError("--set=[SEC]ITEM=VALUE requires a value") lhs, rhs = [s.strip() for s in option_item.split("=", 1)] setting = get_rdict(lhs, rhs) settings.append(setting) files_to_settings(settings, options.setting_files) mutation_kwargs['variables'].update({ 'bMode': 'Set', 'cPoints': point_strings, 'nSpaces': namespaces, 'bSettings': settings, }) report_cancel = False report_set = True results = pclient('graphql', mutation_kwargs) for result in results['broadcast']['result']: modified_settings = result['response'][0] bad_options = result['response'][1] if modified_settings: print( get_broadcast_change_report(modified_settings, is_cancel=report_cancel)) sys.exit(report_bad_options(bad_options, is_set=report_set))
async def run(options: 'Values', workflow_id): """Implement cylc broadcast.""" pclient = get_client(workflow_id, timeout=options.comms_timeout) ret: Dict[str, Any] = { 'stdout': [], 'stderr': [], 'exit': 0, } mutation_kwargs: Dict[str, Any] = { 'request_string': MUTATION, 'variables': { 'wFlows': [workflow_id], 'bMode': 'Set', 'cPoints': options.point_strings, 'nSpaces': options.namespaces, 'bSettings': options.settings, 'bCutoff': options.expire, } } query_kwargs: Dict[str, Any] = { 'request_string': QUERY, 'variables': { 'wFlows': [workflow_id], 'nIds': [] } } if options.show or options.showtask: if options.showtask: try: query_kwargs['variables']['nIds'] = [options.showtask] except ValueError: # TODO validate showtask? raise UserInputError( 'TASK_ID_GLOB must be in the format: cycle/task') result = await pclient.async_request('graphql', query_kwargs) for wflow in result['workflows']: settings = wflow['broadcasts'] padding = get_padding(settings) * ' ' if options.raw: ret['stdout'].append(str(settings)) else: ret['stdout'].extend( get_tree(settings, padding, options.unicode)) return ret report_cancel = True report_set = False if options.clear: mutation_kwargs['variables']['bMode'] = 'Clear' if options.expire: mutation_kwargs['variables']['bMode'] = 'Expire' # implement namespace and cycle point defaults here namespaces = options.namespaces if not namespaces: namespaces = ["root"] point_strings = options.point_strings if not point_strings: point_strings = ["*"] if options.cancel or options.cancel_files: settings = [] for option_item in options.cancel: if "=" in option_item: raise UserInputError( "--cancel=[SEC]ITEM does not take a value") option_item = option_item.strip() setting = get_rdict(option_item) settings.append(setting) files_to_settings(settings, options.cancel_files, options.cancel) mutation_kwargs['variables'].update({ 'bMode': 'Clear', 'cPoints': point_strings, 'nSpaces': namespaces, 'bSettings': settings, }) if options.settings or options.setting_files: settings = [] for option_item in options.settings: if "=" not in option_item: raise UserInputError("--set=[SEC]ITEM=VALUE requires a value") lhs, rhs = [s.strip() for s in option_item.split("=", 1)] setting = get_rdict(lhs, rhs) settings.append(setting) files_to_settings(settings, options.setting_files) mutation_kwargs['variables'].update({ 'bMode': 'Set', 'cPoints': point_strings, 'nSpaces': namespaces, 'bSettings': settings, }) report_cancel = False report_set = True results = await pclient.async_request('graphql', mutation_kwargs) for result in results['broadcast']['result']: modified_settings = result['response'][0] bad_options = result['response'][1] if modified_settings: ret['stdout'].append( get_broadcast_change_report( modified_settings, is_cancel=report_cancel, )) bad_opts = report_bad_options(bad_options, is_set=report_set) if bad_opts: ret['stderr'].append(f'ERROR: {bad_opts}') ret['exit'] = 1 return ret