def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) _WriteSwitchDef(flags.autogen_root, flags.output_dir, script_name, config.GetSwitchChips())
def AddDependencies(task_mgr): """Add bootload dependencies given unicast routes in the network config.""" config = network_config.NetworkConfig() path_finder = network_util.PathFinder(config.GetSwitches(), config.all_messages) destinations = ['aio_nodes.' + t.GetTarget() for t in task_mgr.GetTasks()] paths = path_finder.GetHops(None, 'aio_nodes.operator', destinations, unicast=True) for path in paths: for ingress_index, _ in enumerate(path[:-1]): ingress_all = path_finder.GetAttachedNodes(path[ingress_index]) ingress_tms570 = [n for n in ingress_all if config.GetAioNode(n).tms570_node] for egress_index in range(ingress_index + 1, len(path)): egress_all = path_finder.GetAttachedNodes(path[egress_index]) egress_tms570 = [n for n in egress_all if config.GetAioNode(n).tms570_node] for egress in egress_tms570: for ingress in ingress_tms570: task_mgr.AddDependency(egress, ingress) # Configuration tasks depend on the corresponding application tasks. for target in [task.GetTarget() for task in task_mgr.GetTasks()]: task_mgr.AddInternalDependencies(target)
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) _WriteMessageStats(flags.autogen_root, flags.output_dir, script_name, config.aio_messages)
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) locations = node_locations.GetNodeLocations(config) _WriteAioNode(flags.autogen_root, flags.output_dir, script_name, config, locations)
def GenerateBootloaderImage(boot_info, elf_file, bin_file): """Generate a bootloader image given an elf file. Args: boot_info: A dictionary containing the bootloader configuration. In the current implementation, possible options include 'aio_node' (defaults to kAioNodeUnknown), 'ip_address' (defaults the address corresponding to the kAioNode), and 'hardware_type' (defaults to kHardwareTypeUnknown). elf_file: Full path to input elf file. bin_file: Full path to output bin file. """ _ConvertElfToBin(elf_file, bin_file) # Determine IP address from the given information. if boot_info.get('ip_address'): ip = int(boot_info['ip_address'].split('.')[3]) elif boot_info.get('aio_node'): aio_node = network_config.NetworkConfig().GetAioNode( boot_info['aio_node']) ip = aio_node.ip_octet else: ip = network_config.NetworkConfig().GetAioNode('unknown').ip_octet # Determine hardware type. if not boot_info.get('hardware_type'): boot_info['hardware_type'] = 'kHardwareTypeUnknown' hardware_type = hardware_type_helper.Value(boot_info['hardware_type']) boot_config = ldscript.BootConfig() boot_config.version = _BOOT_CONFIG_VERSION_FLAG | _BOOT_CONFIG_VERSION boot_config.ip_address[0] = 192 boot_config.ip_address[1] = 168 boot_config.ip_address[2] = 1 boot_config.ip_address[3] = ip boot_config.mac_address[0] = 0x02 boot_config.mac_address[1] = 0x00 boot_config.mac_address[2] = 0x00 boot_config.mac_address[3] = 0x00 boot_config.mac_address[4] = 0x00 boot_config.mac_address[5] = ip boot_config.hardware_type = hardware_type boot_config.unused = 0 # Previously used as AppType. boot_config.unused_2 = 0 # Previously used as AIO node index. _WriteBootConfig(boot_config, bin_file, bin_file)
def __init__(self, minimum_stale_timeout_seconds, stale_timeout_in_periods, receiver_idle_timeout_seconds, message_types, timeout, aio_loop_duration, process_duration, receiver_aio_node, network_yaml_file, aio_message_sequence_bits, populate_from_sim): """Initializes the AIO receiver. Args: minimum_stale_timeout_seconds: Default time period in which the buffered message remains live. [s] stale_timeout_in_periods: Time period in which the buffered message remains live, in number of message transfering periods. receiver_idle_timeout_seconds: Duration to stay live since the last request. [s] Once due, the thread exits the main loop and stops receiving messages. message_types: The list of message types to receive. timeout: The timeout when receiving messages. [s] aio_loop_duration: Time to continuously receive AIO messages. [s] process_duration: Time to process other requests. [s] receiver_aio_node: Name of the AIO node used to receive messages. network_yaml_file: The YAML file that defines nodes and messages on the AIO network. aio_message_sequence_bits: The number of bits in the AIO message sequence. populate_from_sim: Whether or not the receiver should populate telemtry from the simulator. """ super(AioReceiver, self).__init__(minimum_stale_timeout_seconds, stale_timeout_in_periods, receiver_idle_timeout_seconds, message_types, network_yaml_file, aio_message_sequence_bits) self._timeout_us = long(timeout * 1e6) self._aio_loop_us = long(aio_loop_duration * 1e6) assert self._timeout_us < self._aio_loop_us self._process_duration = process_duration self._receiver_aio_node = receiver_aio_node self._message_types = message_types # _message_cache is accessed only by one thread, the AioReceiver, so # there is no need to guard it with locks. self._message_cache = collections.defaultdict(dict) self._populate_from_sim = populate_from_sim config = network_config.NetworkConfig(network_yaml_file) self._sources_by_message = {} for m in config.all_messages: if m.name in _MESSAGE_TYPE_HELPER: msg_enum = _MESSAGE_TYPE_HELPER.Value(m.name) self._sources_by_message[msg_enum] = [ _AIO_NODE_HELPER.Value(sender.camel_name) for sender in m.all_senders ] # The lock ensures that the AIO snapshot is produced right after # the AIO loop phase, without interruption from other threads. self._snapshot_lock = threading.Lock() self._snapshot = None
def __init__(self, system_name, yaml_file=None, net_config=None): if net_config: self._net_config = net_config else: self._net_config = network_config.NetworkConfig() y = SystemConfig._LoadSystemYaml(yaml_file) nodes, self.config, self.calib = self._LoadSystem(y, system_name) self.nodes = sorted(nodes, key=lambda x: x.snake_name)
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) _WriteForwardingMapHeader(flags.autogen_root, flags.output_dir, script_name, config.GetSwitches()) _WriteForwardingMapSource(flags.autogen_root, flags.output_dir, script_name, config)
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) _WriteMessageType(flags.autogen_root, script_name, config.aio_messages, flags.output_dir, 'message_type') _WriteMessageType(flags.autogen_root, script_name, config.eop_messages, flags.output_dir, 'eop_message_type') _WriteMessageType(flags.autogen_root, script_name, config.winch_messages, flags.output_dir, 'winch_message_type')
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) source_file = os.path.join(flags.output_dir, 'switch_links.c') header_file = os.path.join(flags.output_dir, 'switch_links.h') rel_header_file = os.path.relpath(header_file, flags.autogen_root) with open(source_file, 'w') as f: f.write(_GenerateSource(rel_header_file, config)) with open(header_file, 'w') as f: f.write(_GenerateHeader(rel_header_file))
def __init__(self, minimum_stale_timeout_seconds, stale_timeout_in_periods, receiver_idle_timeout_seconds, message_types, network_yaml_file, aio_message_sequence_bits): """A thread that continuously receive messages. Args: minimum_stale_timeout_seconds: Default time period in which the buffered message remains live. [s] stale_timeout_in_periods: Time period in which the buffered message remains live, in number of message transfering periods. receiver_idle_timeout_seconds: Duration to stay live since the last request. [s] Once due, the thread exits the main loop and stops receiving messages. message_types: The list of message types to receive. network_yaml_file: The YAML file that defines nodes and messages on the AIO network. aio_message_sequence_bits: The number of bits in the AIO message sequence. """ super(DataThread, self).__init__() # Data specific attributes. self._message_types = copy.copy(message_types) # The buffer time by message type name. self._buffer_time = {} config = network_config.NetworkConfig(network_yaml_file) message_frequencies = { m.name: m.frequency_hz for m in config.all_messages } # Determine the buffer time for each message type according to network # configuration. If not found, use minimum_stale_timeout_seconds. for message_type in _MESSAGE_TYPE_HELPER.Names(): assert message_type in _MESSAGE_TYPE_HELPER short_name = _MESSAGE_TYPE_HELPER.ShortName(message_type) if short_name not in message_frequencies: self._buffer_time[message_type] = minimum_stale_timeout_seconds else: frequency = message_frequencies[short_name] if frequency == 0: self._buffer_time[ message_type] = minimum_stale_timeout_seconds else: self._buffer_time[message_type] = max( 1.0 / frequency * stale_timeout_in_periods, minimum_stale_timeout_seconds) self._idle_timeout = receiver_idle_timeout_seconds self._latest_access_time = self._CurrentTimeSec() # The overflow value of message sequence numbers. self._sequence_period = 2**aio_message_sequence_bits
def GenerateApplicationImage(app_info, elf_file, bin_file): """Generate an application image given an elf file. Args: app_info: A dictionary containing the application configuration. In the current implementation, the application configuration only requires the aio_node (specified as a full or short name). elf_file: Full path to input elf file. bin_file: Full path to output bin file. """ aio_node = network_config.NetworkConfig().GetAioNode(app_info['aio_node']) _ConvertElfToBin(elf_file, bin_file) app_config = ldscript.AppConfig() app_config.aio_node = aio_node.enum_value app_config.node_index = aio_node.label_value _WriteAppConfig(app_config, bin_file, bin_file)
def GetAvionicsServoLimits(): """Get the avionics servo mechanical limits for the current system.""" sys_conf = system_config.SystemConfig.GetSystemConfigBySerial( _CONFIG['system']['wing_serial']) config_file = os.path.join(makani.HOME, 'avionics/servo/firmware/config_params.yaml') net_conf = network_config.NetworkConfig() yaml_keys = [ sys_conf.config[net_conf.GetAioNode('servo_%s' % s.lower())] for s in _SERVOS ] limits = [codec.DecodeYamlFile(config_file, key) for key in yaml_keys] return { _SERVOS[i]: (limits[i].servo_min_limit, limits[i].servo_max_limit) for i in range(len(_SERVOS)) }
def Initialize(self): config = network_config.NetworkConfig(settings.NETWORK_YAML) message_types = config.all_messages path_finder = network_util.PathFinder(config.GetSwitches(), message_types) self._node_stats = {} for tag, stats in network_util.GetNodeBandwidthStatistics( path_finder, message_types).iteritems(): self._node_stats[self._ParseNetworkConfigTag(tag)[1]] = stats self._link_stats = network_bandwidth.GetLinkBandwidthStatistics(config) self._freq_by_message_type = {} self._status_message_by_aio_node = {} for m in message_types: self._freq_by_message_type[m.name] = m.frequency_hz if m.name in ['CoreSwitchSlowStatus', 'SlowStatus']: for sender in m.all_senders: self._status_message_by_aio_node[ sender.camel_name] = m.name
def GetTargetInfo(name): """Gets info about our target board. Args: name: The target name (e.g., CS_A). Returns: {node_name: AioNode enum name. node_value: AioNode enum value. node_index: e.g. This is the return of IdentityGetIndex(). ip_address: As packed binary. } Raises: ValueError: if the name of the target looks wrong. """ config = network_config.NetworkConfig() node = config.GetAioNode(name.lower()) return { 'node_name': node.camel_name, 'node_value': node.enum_value, 'node_index': node.label_value, 'ip_address': node.ip}
def SynthesizeMessages(message_names=None, sequence=0, fail_silently=False): """Synthesize fake AIO messages. Args: message_names: The set of message type names. If None, all messages are synthesized. sequence: The sequence number of all the messages. fail_silently: If True, operations over the returned StructTree return None rather than raising exceptions. Returns: A StructTree object as a nested dict of messages, indexed first by message types and then AIO nodes. """ # TODO: Clean-up nonstandard messages. excluded = ['Stdio', 'ControlDebug'] messages = {} message_types = network_config.NetworkConfig( settings.NETWORK_YAML).all_messages for m in message_types: if m.name not in _MESSAGE_TYPE_HELPER or m.name in excluded: continue if message_names and m.name not in message_names: continue msg_enum = _MESSAGE_TYPE_HELPER.Value(m.name) messages[m.name] = {} for sender in m.all_senders: aio_node = sender.camel_name message = FakeAioMessage(msg_enum, aio_node, sequence) if message.IsValid(): messages[m.name][aio_node] = message.Get(readonly=True) messages['filtered'] = SynthesizeFilteredData() return struct_tree.StructTree(messages, fail_silently=fail_silently, readonly=False)
def __init__(self, for_log): self._items_to_check = [] config = network_config.NetworkConfig( os.path.join(makani.HOME, 'avionics/network/network.yaml')) for m in config.all_messages: if m.name == 'TetherDown': for sender in m.all_senders: sender_name = sender.camel_name if sender_name.startswith('CsGs'): self._items_to_check.append( FrameUpdateIntervalCheck( for_log, m.name, sender_name, tether_message.TETHER_RADIO_DECIMATION, m.frequency_hz / tether_message.TETHER_RADIO_DECIMATION)) else: self._items_to_check.append( FrameUpdateIntervalCheck(for_log, m.name, sender_name, 1, m.frequency_hz)) elif m.name == 'TetherUp': for sender in m.all_senders: sender_name = sender.camel_name if not sender_name.startswith('CsGs'): self._items_to_check.append( FrameUpdateIntervalCheck( for_log, m.name, sender_name, tether_message.TETHER_RADIO_DECIMATION, m.frequency_hz / tether_message.TETHER_RADIO_DECIMATION)) else: self._items_to_check.append( FrameUpdateIntervalCheck(for_log, m.name, sender_name, 1, m.frequency_hz))
_MON_BY_MESSAGE_TYPE = { 'BatteryStatus': 'aio_mon', 'CoreSwitchStatus': 'cs_mon', 'DrumSensorsMonitor': 'aio_mon', 'FlightComputerSensor': 'aio_mon', 'GpsStatus': 'aio_mon', 'GroundStationPlcMonitorStatus': 'aio_mon', 'JoystickMonitorStatus': 'aio_mon', 'Loadcell': 'aio_mon', 'PlatformSensorsMonitor': 'aio_mon', 'RecorderStatus': 'aio_mon', 'ServoStatus': 'aio_mon', } _SOURCES_BY_MESSAGE_TYPE = {} for m in network_config.NetworkConfig(settings.NETWORK_YAML).all_messages: _SOURCES_BY_MESSAGE_TYPE[m.name] = [ s.camel_name for s in m.all_senders ] _DEFAULT_TEMPERATURE_LIMITS = _MONITOR_PARAMS.thermal.aiomon_default def WindWsToWindG(velocity, pqr, dcm_g2p): """Convert wind measurement from wind sensor to ground frame.""" wind_g = sensor_util.Vec3() wind_ws = sensor_util.Vec3() wind_ws.x = velocity[0] wind_ws.y = velocity[1] wind_ws.z = velocity[2] wind_sensor_params = ctypes.cast(
def _GetMinMessageFrequency(): """Get the minimum frequency across all message types.""" config = network_config.NetworkConfig(settings.NETWORK_YAML) return min(m.frequency_hz for m in config.all_messages if m.frequency_hz > 0)
def __init__(self, for_log): self._items_to_check = [] net_conf = network_config.NetworkConfig() node_messages = { 'batt': 'BatteryStatus', 'drum': 'DrumSensorsMonitor', 'flight_computer': 'FlightComputerSensor', 'gps': 'GpsStatus', 'plc_gs02': 'GroundStationPlcMonitorStatus', 'joystick': 'JoystickMonitorStatus', 'loadcell_node': 'Loadcell', 'mvlv': 'MvlvStatus', 'platform': 'PlatformSensorsMonitor', 'recorder_tms570': 'RecorderStatus', 'servo': 'ServoStatus', 'short_stack': 'ShortStackStatus' } for label, message_type in node_messages.iteritems(): nodes = [ node.camel_name for node in net_conf.GetAioNodesByLabel(label) ] # TODO: Specify thresholds for different boards once thermo # characterization is completed. self._items_to_check += [ avionics_checks.AioMonTemperatureChecker( for_log, message_type, node, name='%s.AioMon.Temperature' % node, normal_ranges=check_range.Interval([0, 75]), warning_ranges=check_range.Interval([0, 85])) for node in nodes ] self._items_to_check += [ avionics_checks.AioMonHumidityChecker( for_log, message_type, node, name='%s.AioMon.Humidity' % node, normal_ranges=check_range.Interval([0, 40]), warning_ranges=check_range.Interval([0, 70])) for node in nodes ] self._items_to_check += [ avionics_checks.AioMonBusVoltageChecker( for_log, message_type, node, name='%s.AioMon.BusVoltage' % node) for node in nodes ] self._items_to_check += [ avionics_checks.AioMonBusCurrentChecker( for_log, message_type, node, name='%s.AioMon.BusCurrent' % node) for node in nodes ] self._items_to_check += [ avionics_checks.AioMonWarningCheck(for_log, message_type, node, name='%s.AioMon.Warnings' % node) for node in nodes ]
import tempfile import time import gflags import makani from makani.avionics.bootloader import generate_image from makani.avionics.common import aio from makani.avionics.common import aio_version from makani.avionics.linux.provision import process_helper from makani.avionics.linux.provision import ui_helper from makani.avionics.network import network_config from makani.lib.bazel import bazel_util from makani.lib.python import c_helpers import program _NETWORK_CONFIG = network_config.NetworkConfig() def IpToAioNode(ip): assert ip.startswith('192.168.1.'), ('Ip {} not in AIO range ' '192.168.1.0/24.'.format(ip)) final_octet = int(ip.split('.')[-1]) node_num = aio.aio_node_to_ip_address.FinalOctetToAioNode(final_octet) return _NETWORK_CONFIG.aio_nodes[node_num] def DetectAio(timeout=1.1): """Detect AIO nodes on the network, present all options if none detected.""" sources = aio.aio_node_helper.Names() types = aio.message_type_helper.Names() client = aio.AioClient(types, timeout=0.1, allowed_sources=sources)
def setUp(self): filename = os.path.join(makani.HOME, 'avionics/network/network.yaml') self._network_config = network_config.NetworkConfig(filename)
# tricky because overrides need to propagate to all of the wrapped C/C++ # libraries. POPULATE_MESSAGES_FROM_SIM = (os.environ['MAKANI_WEBMON_FROM_SIM'] == 'True' if 'MAKANI_WEBMON_FROM_SIM' in os.environ else False) # A third-party CSS/JS library divides a webpage into logical a grid, where we # can put views and widgets inside a block of grid cells. # We use a setup that has 12 columns. CSS_GRID_COLUMNS = 12 # Number of bits used for AIO message sequence. AIO_MESSAGE_SEQUENCE_BITS = 16 # Path to the network configuration file. NETWORK_YAML = os.path.join(makani.HOME, 'avionics/network/network.yaml') NETWORK_CONFIGS = network_config.NetworkConfig(NETWORK_YAML) # Number of seconds a layout's memory gets cached without further access. # TODO: Develop a better way to handle stale data. MEMORY_STALE_TIMEOUT_S = 36000 # Maximum number of clients. MAX_CLIENT_COUNT = 100 # The max number of data points per line per plot. MAX_DATA_POINTS_PER_LOG_FIELD = 6000 # Server configurations. logging.basicConfig(level=logging.ERROR) DEBUG = True ALLOWED_HOSTS = ['*']
'bytes_per_sec': stat.bytes_per_sec, 'packets_per_sec': stat.packets_per_sec, 'bytes_peak': stat.bytes_peak, 'packets_peak': stat.packets_peak, 'segment_utilization': stat.utilization }) print json.dumps(stats, sort_keys=True, indent=2) def main(argv): try: argv = FLAGS(argv) except gflags.FlagsError, e: sys.stderr.write('\nError: %s\n\nUsage: %s ARGS\n%s\n' % (e, argv[0], FLAGS)) sys.exit(1) config = network_config.NetworkConfig(FLAGS.network_file) if FLAGS.print_bandwidth_stats: _PrintBandwidthStatistics(config) if FLAGS.validate_bandwidth_stats: _ValidateBandwidthStatistics(config) if FLAGS.export_json: _ExportBandwidthStatistics(config) if __name__ == '__main__': main(sys.argv)
"""Set a mask to disable specific ports on a core switch.""" try: argv = FLAGS(argv) except gflags.FlagsError, e: sys.stderr.write('\nError: %s\n\nUsage: %s ARGS\n%s\n' % (e, argv[0], FLAGS)) sys.exit(1) disable_port_mask = 0 for port in FLAGS.disable_ports.split(','): if port: if int(port) < 0: raise ValueError('Invalid port: %s.' % port) disable_port_mask |= 1 << int(port) config = network_config.NetworkConfig() target_node = config.GetAioNode(FLAGS.node) if target_node.label_name != 'core_switch': raise ValueError('Only core switches supported in switch_client.') if (target_node.snake_name != 'cs_gs_a' and target_node.snake_name != 'cs_gs_b' and not FLAGS.i_am_not_breaking_the_wing): raise ValueError( 'Can only disable ports on cs_gs_a or cs_gs_b by default.' ' If you know what you are doing please specify ' '--i_am_not_breaking_the_wing to modify the wing core ' 'switches. Incorrect usage can break the wing (requiring ' 'power cycle to restore connection).') if (disable_port_mask & (1 << 25)) != 0: raise ValueError('Cannot disable the command center port.')
def __init__(self, definitions, network_yaml_file): """Loads the gradebook as a JSON object. Args: definitions: A dictionary of check definitions formated as: { 'imports': {<short_name>: <path_to_the_module>}, // Optional 'checks': { // Required. <message_type> : { // This can be a name or a regex. The regex should be // wrapped by parentheses. // <aio_node>: { // The nest struct reflects the C message. // Indices can apply if the attribute is a dict/list. // Example: array[:], array[1], dict[:], dict[key] // <attribute>: { <sub_attribute>: { // Ranges are list of pairs or scalars, where // a pair denotes lower and upper bounds, and // a scalar denotes an exact match. // Ranges can also be "any", meaning // any value falls into the range. // Ranges can also be an empty list, meaning // no value falls into the range. // "normal_ranges": [], "warning_ranges": [], "normal_std_dev_ranges": [], "warning_std_dev_ranges": [], "normal_norm_ranges": [], "warning_norm_ranges": [], // Callback to preprocess the data. "callback": "<module_short_name>.<function>", "callback_args": [<constant_values>], "name": Name of the field, } } } } } } network_yaml_file: The YAML file that defines nodes and messages on the AIO network. Raises: GradebookParserError: Raised if the gradebook is invalid. """ self._imports = self._ImportModules(definitions.get('imports', {})) self._checks = {} message_types = network_config.NetworkConfig( network_yaml_file).all_messages sources_by_message_type = { m.name: {s.camel_name for s in m.all_senders} for m in message_types } if 'checks' not in definitions: raise GradebookParserError('Found no checks in gradebook.') for message_type, aio_nodes in definitions['checks'].iteritems(): if message_type not in sources_by_message_type: raise GradebookParserError( 'Invalid message type "%s" in gradebook.' % message_type) self._checks[message_type] = {} # Use regular expression to pick AIO nodes to check. # wildcard_info[regex] = checks_to_perform wildcard_info = {} for aio_node, info in aio_nodes.iteritems(): # Names wrapped in parentheses are wildcards for batch-selection. if aio_node.startswith('(') and aio_node.endswith(')'): wildcard_info[aio_node[1:-1]] = info else: self._checks[message_type][ aio_node] = self._InitializeNodes(info) # Expand regular expressions such as '.*' if there is any. for pattern, info in wildcard_info.iteritems(): regex = re.compile(pattern) for aio_node in sources_by_message_type[message_type]: if regex.match(aio_node): if aio_node not in self._checks[message_type]: self._checks[message_type][aio_node] = ( self._InitializeNodes(info)) else: raise GradebookParserError( 'Regex %s matches previously added node %s for ' 'message type %s.' % (pattern, aio_node, message_type))
def GetMessageInfoMapByNetworkFile(network_file=os.path.join( makani.HOME, 'avionics/network/network.yaml')): """Returns a map from MessageType to type info.""" config = network_config.NetworkConfig(network_file) return GetMessageInfoMapByNetworkConfig(config)
def main(argv): flags, argv = network_config.ParseGenerationFlags(argv) config = network_config.NetworkConfig(flags.network_file) script_name = os.path.basename(argv[0]) _WriteIpAddresses(flags.output_dir, script_name, config.aio_nodes)