def get_tree_root_mac(self, db_topology): # the root of the tree should be the main switch. # we analyse the neighbors of this server: # - if we find one, we return it # - if we find several ones, we favor the ones in # walt-net or walt-adm (thus we discard neighbors found from walt-out) # - if we still find several ones, we favor the ones which type is known # as a switch server_mac = get_mac_address(const.WALT_INTF) unknown_neighbors = [] for port, neighbor_mac, neighbor_port, confirmed in \ db_topology.get_neighbors(server_mac): info = self.devices.get_complete_device_info(neighbor_mac) if info.ip is None: continue # ignore this device if not (ip_in_walt_network(info.ip) or ip_in_walt_adm_network(info.ip)): continue # ignore this device if info.type == 'unknown': unknown_neighbors.append(info.name) continue # possibly the switch we are looking for if info.type == 'switch': return (True, neighbor_mac) # Found it! # if we are here we did not find what we want out = MSG_UNKNOWN_TOPOLOGY if len(unknown_neighbors) > 0: out += format_sentence("Note: %s was(were) detected, but its(their) type is unknown.\n" + "If it(one of them) is a switch, use:\n" + "$ walt device config <device> type=switch\n", unknown_neighbors, None, 'device', 'devices') return (False, out)
def reboot_nodes_after_image_change(self, requester, task_callback, image_fullname): nodes = self.nodes.get_nodes_using_image(image_fullname) if len(nodes) == 0: task_callback(None) # nothing to do return requester.stdout.write( format_sentence('Trying to reboot %s using this image...\n', [n.name for n in nodes], None, 'node', 'nodes')) self.nodes.reboot_nodes(requester, task_callback, nodes, False)
def applies_to_nodes(self, requester, device_infos, setting_name, setting_value, all_settings): not_nodes = [di for di in device_infos if di.type != "node"] if len(not_nodes) > 0: msg = format_sentence( "Failed: %s is(are) not a() node(nodes), " "so it(they) does(do) not support the '" + setting_name + "' setting.\n", [d.name for d in not_nodes], None, 'Device', 'Devices') requester.stderr.write(msg) return False return True
def applies_to_virtual_nodes(self, requester, device_infos, setting_name, setting_value, all_settings): if not self.applies_to_nodes(requester, device_infos, setting_name, setting_value, all_settings): return False not_virtual_node = [di for di in device_infos if not di.virtual] if len(not_virtual_node) > 0: msg = format_sentence( "Failed: %s is(are) not virtual, " "so it(they) does(do) not support the '" + setting_name + "' setting.\n", [d.name for d in not_virtual_node], None, 'Node', 'Nodes') requester.stderr.write(msg) return False return True
def applies_to_switches(self, requester, device_infos, setting_name, setting_value, all_settings): # if we have another setting 'type=switch', then we are expecting unknown devices, and this # will be verified by the appropriate check for this other setting. if all_settings.get('type') == 'switch': return True # ok for us not_switches = [di for di in device_infos if di.type != "switch"] if len(not_switches) > 0: msg = format_sentence( "Failed: %s is(are) not a() switch(switches), " "so '" + setting_name + "' setting cannot be applied.\n", [d.name for d in not_switches], None, 'Device', 'Devices') requester.stderr.write(msg) return False return True
def netsetup_handler(self, requester, device_set, netsetup_value): # Interpret the node set, some of them may be strict devices device_infos = self.devices.parse_device_set(requester, device_set) if device_infos is None: yield False # Check the node set not_nodes = [di for di in device_infos if di.type != "node"] if len(not_nodes) > 0: msg = format_sentence( "%s is(are) not a() node(nodes), " "so it(they) does(do) not support the 'netsetup' setting.\n", [d.name for d in not_nodes], None, 'Device', 'Devices') requester.stderr.write(msg) yield False # Interpret the value new_netsetup_state = None try: new_netsetup_state = NetSetup(netsetup_value) except ValueError: requester.stderr.write( "'%s' is not a valid setting value for netsetup." % (netsetup_value)) yield False # Yield information that all things have ran correctly yield True # Effectively configure nodes for node_info in device_infos: if node_info.netsetup == new_netsetup_state: # skip this node: already configured continue # Update the database self.db.update("nodes", "mac", mac=node_info.mac, netsetup=new_netsetup_state) # Update iptables do("iptables %(action)s WALT --source '%(ip)s' --jump ACCEPT" % dict(ip=node_info.ip, action="--insert" if new_netsetup_state == NetSetup.NAT else "--delete")) # Validate the modifications self.db.commit()
def verify_compatibility_issue(image_store, requester, clonable_link, ws_image_fullname, remote_image_fullname, docker, target_node_models, nodes_manager, **args): ws_image = image_store[ws_image_fullname] if not ws_image.in_use: return # no problem # there is a risk of overwritting the mounted ws image with # a target image that is incompatible. nodes = nodes_manager.get_nodes_using_image(ws_image_fullname) needed_models = set(node.model for node in nodes) image_compatible_models = set(target_node_models) incompatible_models = needed_models - image_compatible_models if len(incompatible_models) > 0: sentence = format_sentence(MSG_INCOMPATIBLE_MODELS, incompatible_models, None, 'node model', 'node models') requester.stderr.write(sentence) return False # give up
def includes_devices_not_owned(self, requester, device_set, warn): username = requester.get_username() if not username: return False # client already disconnected, give up devices = self.parse_device_set(requester, device_set) if devices is None: return None not_owned = [d for d in devices if not (d.type == "node" and (d.image.startswith(username + '/') or d.image.startswith('waltplatform/')) or d.type != "node")] if len(not_owned) == 0: return False else: if warn: requester.stderr.write(format_sentence( 'Warning: %s seems(seem) to be used by another(other) user(users).', [d.name for d in not_owned], "No device", "Device", "Devices") + '\n') return True
def netsetup_handler(self, requester, device_set, netsetup_value): # Interpret the node set, some of them may be strict devices device_infos = self.devices.parse_device_set(requester, device_set) if device_infos is None: yield False # Check the node set not_nodes = filter(lambda di: di.type != "node", device_infos) if len(not_nodes) > 0: msg = format_sentence("%s is(are) not a() node(nodes), " "so it(they) does(do) not support the 'netsetup' setting.\n", [d.name for d in not_nodes], None, 'Device', 'Devices') requester.stderr.write(msg) yield False # Interpret the value new_netsetup_state = None try: new_netsetup_state = NetSetup(netsetup_value) except ValueError: requester.stderr.write( "'%s' is not a valid setting value for netsetup." % (netsetup_value)) yield False # Yield information that all things have ran correctly yield True # Effectively configure nodes for node_info in device_infos: if node_info.netsetup == new_netsetup_state: # skip this node: already configured continue # Update the database self.db.update("nodes", "mac", mac=node_info.mac, netsetup=new_netsetup_state) # Update iptables do("iptables %(action)s WALT --source '%(ip)s' --jump ACCEPT" % dict(ip=node_info.ip, action="--insert" if new_netsetup_state == NetSetup.NAT else "--delete")) # Validate the modifications self.db.commit()
def set_image(self, requester, nodes, image_name): # if image tag is specified, let's get its fullname if image_name != 'default': image = self.store.get_user_image_from_name(requester, image_name) if image == None: return False image_compatible_models = set(image.get_node_models()) node_models = set(node.model for node in nodes) incompatible_models = node_models - image_compatible_models if len(incompatible_models) > 0: sentence = format_sentence(MSG_INCOMPATIBLE_MODELS, incompatible_models, None, 'node model', 'node models') requester.stderr.write(sentence) return False image_fullnames = {node.mac: image.fullname for node in nodes} else: image_fullnames = {} # since the 'default' keyword was specified, we might have to associate # different images depending on the type of each WalT node. # we compute the appropriate image fullname here. for node in nodes: image_fullnames[ node.mac] = self.store.get_default_image_fullname( node.model) # let's update the database about which node is mounting what for node_mac, image_fullname in image_fullnames.items(): self.db.update('nodes', 'mac', mac=node_mac, image=image_fullname) self.store.update_image_mounts(requester=requester) tftp.update(self.db, self.store) self.db.commit() self.dhcpd.update() # inform requester if image_name == 'default': sentence = MSG_BOOT_DEFAULT_IMAGE else: sentence = '%s will now boot ' + image_name + '.' requester.stdout.write( format_sentence_about_nodes(sentence, [n.name for n in nodes]) + '\n') return True
def set_image(self, requester, nodes, image_name): # if image tag is specified, let's get its fullname if image_name != 'default': image = self.store.get_user_image_from_name(requester, image_name) if image == None: return False image_compatible_models = set(image.get_node_models()) node_models = set(node.model for node in nodes) incompatible_models = node_models - image_compatible_models if len(incompatible_models) > 0: sentence = format_sentence(MSG_INCOMPATIBLE_MODELS, incompatible_models, None, 'node model', 'node models') requester.stderr.write(sentence) return False image_fullnames = { node.mac: image.fullname for node in nodes } else: image_fullnames = {} # since the 'default' keyword was specified, we might have to associate # different images depending on the type of each WalT node. # we compute the appropriate image fullname here. for node in nodes: image_fullnames[node.mac] = self.store.get_default_image_fullname(node.model) # let's update the database about which node is mounting what for node_mac, image_fullname in image_fullnames.items(): self.db.update('nodes', 'mac', mac=node_mac, image=image_fullname) self.store.update_image_mounts(requester = requester) tftp.update(self.db) self.db.commit() self.dhcpd.update() # inform requester if image_name == 'default': sentence = MSG_BOOT_DEFAULT_IMAGE else: sentence = '%s will now boot ' + image_name + '.' requester.stdout.write(format_sentence_about_nodes( sentence, [n.name for n in nodes]) + '\n') return True
def get_device_config(self, requester, device_set): # ensure the device set is correct device_infos = self.server.devices.parse_device_set( requester, device_set) if device_infos is None: return # issue already reported configs = defaultdict(lambda: defaultdict(list)) for device_info in device_infos: # check device category secondary_category = None if device_info.type == 'unknown': category = 'unknown-devices' elif device_info.type == 'switch': category = 'switches' elif device_info.type == 'node': if device_info.virtual: category = 'virtual-nodes' secondary_category = 'nodes' else: category = 'nodes' elif device_info.type == 'server': category = 'server' else: raise NotImplementedError( 'Unexpected device type in get_device_config().') # retrieve device settings settings = dict(device_info.conf) # add default value of unspecified settings for setting_name, setting_info in self.settings_table.items(): if setting_info['category'] in (category, secondary_category) \ and setting_name not in settings: settings[setting_name] = setting_info['default'] # append to the list of devices having this same config sorted_config = tuple(sorted(settings.items())) configs[category][sorted_config].append(device_info.name) # print groups of devices having the same config parts = [] for category, category_labels in (('nodes', ('Node', 'Nodes')), ('virtual-nodes', ('Virtual node', 'Virtual nodes')), ('switches', ('Switch', 'Switches')), ('server', None), ('unknown-devices', ('Unknown device', 'Unknown devices'))): if len(configs[category]) == 0: continue for sorted_config, device_names in configs[category].items(): if category == 'server': sentence_start = 'Server has' else: sentence_start = format_sentence('%s has(have)', device_names, None, category_labels[0], category_labels[1]) if len(sorted_config) == 0: parts.append(sentence_start + ' no config option available.\n') continue msg = sentence_start + ' the following config applied:\n' for setting_name, setting_value in sorted_config: if setting_value is None: pprinted_value = '<unspecified>' else: pprint = self.settings_table[setting_name].get( 'pretty_print') if pprint is None: pprinted_value = str(setting_value) else: pprinted_value = pprint(setting_value) msg += '%s=%s\n' % (setting_name, pprinted_value) parts.append(msg) requester.stdout.write('\n\n'.join(parts) + '\n')