def do_DELETE(self): Log.info('DELETE {}:{}', self.server.node_address, self.path) path = self.normalize_path(self.path) parent_path = path.rsplit(os.sep, 1)[0] # # If the resource doesn't exist, then 404. # If the parent doesn't exist, then 405. # If the parent isn't a collection, then 405. # Otherwise, 204 # if path not in self.server.cache: status = 404 elif parent_path not in self.server.cache: status = 405 elif 'Members' not in self.server.cache[parent_path]: status = 405 else: status = 204 del self.server.cache[path] for i,m in enumerate(self.server.cache[parent_path]['Members']): if m['@odata.id'] == self.path: del self.server.cache[parent_path]['Members'][i] self.server.cache[parent_path]['*****@*****.**'] -= 1 break self.reply(status)
def do_PUT(self): Log.info('PUT {}:{}', self.server.node_address, self.path) # # We don't support this function. # self.reply(405)
def do_PATCH(self): Log.info('PATCH {}:{}', self.server.node_address, self.path) path = self.normalize_path(self.path) data_length = int(self.headers["content-length"]) data = json.loads(self.rfile.read(data_length).decode("utf-8")) data_id = data['@odata.id'] # # If the resource doesn't exist, then 404. # If the resource is a collection, then 405. # Otherwise, 204. # if path not in self.server.cache: self.reply(404) elif 'Members' in self.server.cache[path]: self.reply(405) else: # # Update the resource. # update_resource(self.server.cache[path], data) # # Reply to the user. # headers = { "Location" : data_id, "Content-Length" : "0" } self.reply(204, headers)
def do_POST(self): Log.info('POST {}:{}', self.server.node_address, self.path) path = self.normalize_path(self.path) data_length = int(self.headers["content-length"]) data = json.loads(self.rfile.read(data_length).decode("utf-8")) data_id = data['@odata.id'] # # If the resource doesn't exist, then 404. # If the resource isn't a collection, then 405. # Otherwise, 204 # if path not in self.server.cache: self.reply(404) elif 'Members' not in self.server.cache[path]: self.reply(405) else: # # Find a resource id for the new entry. # resource = self.server.cache[path] members = resource['Members'] members_id = sorted([ int(x.get('@odata.id').rsplit(os.sep,1)[1]) for x in members ]) last = members_id[0] for x in members_id[1:]: if x != last+1: break last = x # # Name the new entry. # new_id = last + 1 data_id = data_id.rsplit(os.sep, 1)[0] data_id = os.path.join(data_id, str(new_id)) data['@odata.id'] = data_id # # Update the resource to include the new entry. # resource['Members'].append({'@odata.id' : data_id }) resource['*****@*****.**'] += 1 # # Put the new entry into the tree. (The resource name doesn't have a leading '/'.) # new_id = data_id if new_id[0] == os.sep: new_id = new_id[1:] self.server.cache[new_id] = data # # Reply to the user. # headers = { "Location" : data_id, "Content-Length" : "0" } self.reply(204, headers)
def initialize(self): # # Validate the sweep type. # if self.sweep_type not in Fabric.sweep_types: Log.error('invalid sweep type {}', self.sweep_type) return False # # Read the node profiles. # self.nodes = {} for filename in self.node_file_list: Log.info('reading {}', filename) try: with open(filename) as f: node_profiles = json.load(f) for name, profile in node_profiles.items(): node_type = profile['type'] self.nodes[name] = Fabric.name_classes[node_type]( name, profile) except: Log.error('error reading {}', filename) return False # # There are 5 steps for node initialization: # 1) load the GCIDs and UID into the endpoints # 2) train the port links # 3) validate that all ports are wired correctly # 4) load the node attributes # 5) enable the port interfaces # status = True if status: status = self.init_ids() if status: status = self.train_ports() if status: status = self.validate_ports() if status: status = self.load_nodes() if status: status = self.enable_ports() # # Verify the state of all connected ports. # if status: status = self.verify_fabric_health() Log.debug('fabric initialization status = {}', status) return status
def wait_for(self, command, args, kwargs): Log.info('starting {}...', command) kwargs['retries'] = self.timers.get(command, self.timers['default']) verify_name = '{}_done'.format(command) # # Tell the node threads to start the command. # active_nodes = [node for node in self.nodes.values() if node.active] for node in active_nodes: node.enqueue(command, args, kwargs) # # Wait for all of the node threads to complete. # cycles = 0 node_statuses = [-1] while (self.wait_status(node_statuses) < 0) and (cycles < self.timers[command]): time.sleep(1) cycles += 1 node_statuses = [ getattr(node, verify_name)() for node in active_nodes ] if (cycles % 60) == 0: self.wait_display(command, cycles, node_statuses) # # Check for node timeouts. # if self.wait_status(node_statuses) < 0: self.wait_display(command, cycles, node_statuses) for i in range(len(node_statuses)): if node_statuses[i] == -1: Log.error('{} : {} timed out', command, active_nodes[i].name) # # Display the command status. # command_status = self.wait_status(node_statuses) == 1 Log.info('{} done ... status = {} cycles = {}', command, command_status, cycles) return command_status
# # Create the fabric and server. # fabric = Fabric(zfm_sweep_type, timers, config_files) server = Server(hostname, fabric) # # Initialize the fabric. # try: if not fabric.initialize(): Log.error('fabric initialization failed') sys.exit(1) except KeyboardInterrupt: Log.info('user interrupt caught - exiting') sys.exit(1) # # Start the HTTP server. # server_thread = Thread(target=server.run, daemon=True) server_thread.start() # # Monitor the fabric. # try: while True: fabric.sweep() time.sleep(timers['sweep'])
def run(self): Log.info('starting ZFM server @ {}:{}', self.address, self.port) self.server.serve_forever()
def do_GET(self): if 'favicon.ico' in self.path: return 404, None Log.info('GET {}:{}', self.server.node_address, self.path) parsed_url = parse.urlsplit(self.path) tokens = parsed_url.path.split('/') query = parsed_url.query parameters = parse.parse_qs(query) if 'format' not in parameters: parameters['format'] = ['BROWSER'] # # Remove empty strings from the parsed URL. # while (len(tokens) > 0) and (tokens[0] == ''): del tokens[0] while (len(tokens) > 0) and (tokens[-1] == ''): del tokens[-1] if (len(tokens) > 0) and (tokens[-1] == 'favicon.ico'): return 404, None # # Valid requests are: # 1) [] -> fabric request # 2) [name] -> node request # 3) [name,port] -> port request # status,data = 404,None fabric = self.server.fabric node = None port = None # # Validate that the parameters are in range and correct. # if len(tokens) > 2: Log.error('too many fields in URL {}', self.path) return 404, None if len(tokens) == 2 and not tokens[1].isdigit(): Log.error('invalid URL (port incorrect) {}', self.path) return 404, None if len(tokens) >= 1: node = self.server.fabric.locate_node(tokens[0]) if not node: Log.error('invalid URL (node incorrect) {}', self.path) return 404, None if len(tokens) == 2: value = int(tokens[1]) if not (node.profile['portStart'] <= value < node.profile['portEnd']): Log.error('invalid URL (port out of range) {}', self.path) return 404, None port = node.ports[value] # # Execute the command at the appropriate level. # if len(tokens) == 0: status, data = fabric.GET(parameters) elif len(tokens) == 1: status, data = node.GET(parameters) elif len(tokens) == 2: status, data = port.GET(parameters) if status == 200: data['ZFM'] = self.server.node_address output = self.server.formatter.format(data, parameters['format'][0]) # # Send the reply back to the requester. # headers = {'Content-type' : 'text/html', 'Cache-Control' : 'no-cache, no-store, must-revalidate', 'Pragma' : 'no-cache', 'Expires' : '0' } payload = output if status == 200 else None self.reply(status, headers, payload)
def wait_display(self, command, cycles, statuses): s = {-1: '-', 0: '0', 1: '1'} status_str = ''.join(s[x] for x in statuses) Log.info('{} : cycles={:<4} status={:<}', command, cycles, status_str)