Beispiel #1
0
    def validate(self):
        if not self.query():
            Log.error(
                '{}[{}].validate : can\'t fetch attributes - downing port',
                self.node.name, self.index)
            self.active = False
            return False

        #
        # Get the node's view of its remote neighbor
        #
        attr_uid = self.current['Oem']['Hpe']['RemoteComponentID']['UID']
        attr_port = self.current['Oem']['Hpe']['RemoteComponentID']['Port']

        #
        # Compare it to our view.
        #
        status = (attr_uid == self.remote_uid) and (attr_port
                                                    == self.remote_port)
        if not status:
            Log.error('{}[{}] didn\'t validate: 0x{:X}:{:<2} vs 0x{:X}:{:<2}',
                      self.node.name, self.index, self.remote_uid,
                      self.remote_port, attr_uid, attr_port)
            self.active = False
            return False

        return True
Beispiel #2
0
def zfm_load_configuration(config_file, zfm_config):

    #
    # Open the file for reading.
    #
    try:
        f = open(config_file)
    except FileNotFound:
        Log.error('no such file: {}', config_file)
        return False

    #
    # Read the file line by line.  Strip out the comments and then split the line into key and value.
    #
    errors = False
    for line in f:
        kv_line, _, _ = line.partition('#')
        kv_line = kv_line.strip()
        if len(kv_line) == 0: continue

        tokens = kv_line.split()
        if len(tokens) == 2:
            key, value = tokens
            zfm_config[key] = value
        else:
            Log.error('invalid line: {}', line.rstrip())
            errors = True

    return not errors
Beispiel #3
0
    def check(self):

        #
        # Get the port metrics.
        #
        status, attr = self.get()
        if not status:
            Log.error('can\'t fetch port metrics for sweep of remote node')
            return status

        #
        # First time through.  Nothing to compare.
        #
        if not self.data:
            self.data = copy.deepcopy(attr)
            return True

        #
        # Check the error counters and flag those that are increasing.
        #
        for s in attr['Gen-Z']:
            v_curr =      attr['Gen-Z'][s]
            v_last = self.data['Gen-Z'][s]
            if v_curr != v_last:
                Log.error('{:<20} port {:<2} : {:<25} : {} -> {}', self.node.name, self.port.index, s, v_last, v_curr)

        self.data = copy.deepcopy(attr)
        return status
Beispiel #4
0
    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)
Beispiel #5
0
    def do_PUT(self):
        Log.info('PUT {}:{}', self.server.node_address, self.path)

        #
        # We don't support this function.
        #
        self.reply(405)
Beispiel #6
0
    def sweep(self):

        #
        # Read the port metrics.
        #
        status = self.metrics.check()
        if not status:
            Log.error('{}[{}].sweep : can\'t read metrics - downing port',
                      self.node.name, self.index)
            self.active = False
            return status

        #
        # Check the port state.  If the link state is good and the interface state is bad, then it is
        # possible that someone is resetting some metrics.  (You do this by resetting the interface.)
        # So in this case, we sleep for two seconds to allow the interface to be good again.
        #
        link_state, if_state = self.link_interface_state()

        if (link_state == 'Enabled') and (if_state == 'Disabled'):
            time.sleep(2)
            link_state, if_state = self.link_interface_state()

        status = (link_state == 'Enabled') and (if_state == 'Enabled')
        if not status:
            Log.error(
                '{}[{}].sweep : link/interface in bad state - downing port',
                self.node.name, self.index)
            self.active = False

        return status
Beispiel #7
0
    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)
Beispiel #8
0
    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)
Beispiel #9
0
    def query(self):
        #
        # Get the port attribute.  Update the current view.
        #
        status, attr = self.node.get(self.name)
        if status:
            self.current = attr
        else:
            Log.error('failed to get {}/{} attributes', self.node.name,
                      self.index)

        return status
Beispiel #10
0
    def reply(self, status, headers=None, data=None):
        try:
            self.send_response(status)
            if headers:
                for key,value in headers.items():
                    self.send_header(key, value)
            self.end_headers()

            if data:
                encoded_data = data.encode()
                self.wfile.write(encoded_data)
        except:
            Log.error('can\'t reply to requester')
Beispiel #11
0
    def load(self):
        status = True
        if self.lprt: status &= self.lprt.patch()
        if self.mprt: status &= self.mprt.patch()
        if self.vcat: status &= self.vcat.patch()
        if self.metrics: status &= self.metrics.reset()

        if not status:
            Log.error('{}[{}].load : can\'t load attributes - downing port',
                      self.node.name, self.index)
            self.active = False

        return status
Beispiel #12
0
    def is_enabled(self):

        #
        #
        # Make sure that this port is enabled.
        #
        if not self.query():
            Log.error('{}[{}].enable : can\'t read status', self.node.name,
                      self.index)
            self.active = False
            return False

        state, health = self.current['Status']['State'], self.current[
            'Status']['Health']
        link_state = self.current['LinkState']
        if_state = self.current['InterfaceState']

        if health != 'OK':
            Log.error('{}[{}].enable : port health {} is bad', self.node.name,
                      self.index, health)
            port.active = False
            return False

        if state != 'Enabled':
            Log.debug('{}[{}].enable : state not yet ready {}/{}',
                      self.node.name, self.index, state, health)
            return False

        if if_state != 'Enabled':
            Log.debug('{}[{}].enable : interface not yet enabled {}',
                      self.node.name, self.index, if_state)
            return False

        return True
Beispiel #13
0
    def GET_port(self, parameters):
        node = self.node

        #
        # Fetch the port attribute.
        #
        metric_attr = self.query_all()
        if not metric_attr:
            Log.error('can\'t retrieve port attribute for {}', self.name)
            return 404, None

        #
        # We now have the port attribute.
        #
        oem_data = self.current['Oem']['Hpe']

        data = {
            'DataType':
            'PORT',
            'Timestamp':
            datetime.datetime.now().isoformat(),
            'Node':
            node.profile['name'],
            'Hostname':
            node.name,
            'Index':
            self.index,
            'ConfigState':
            'Enabled' if self.active else 'Disabled',
            'Status':
            '{}/{}'.format(self.current['Status']['State'],
                           self.current['Status']['Health']),
            'LinkState':
            self.current['LinkState'],
            'InterfaceState':
            self.current['InterfaceState'],
            'Remote':
            '0x{:08X}/{:<2}'.format(oem_data['RemoteComponentID']['UID'],
                                    oem_data['RemoteComponentID']['Port']),
            'Metrics':
            self.current_metrics
        }

        return 200, data
Beispiel #14
0
    def locate_node(self, node_id):
        node_hex_id = -1

        if type(node_id) is int:
            node_hex_id = node_id
        elif type(node_id) is str and node_id.isdigit():
            node_hex_id = int(node_id, 0)

        for name, node in self.nodes.items():
            if name == node_id:
                return node
            elif node.topoid == node_id:
                return node
            elif node.geoid == node_id:
                return node
            elif node.uid == node_hex_id:
                return node

        Log.error('invalid node identifier {}', node_id)
        return None
Beispiel #15
0
    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
Beispiel #16
0
    def __init__(self, hostname, fabric):

        #
        # Resolve the hostname.
        #
        hostname, delimiter, hostport = hostname.partition(':')
        if not hostport: hostport = '60000'

        try:
            hostname = socket.gethostbyname(hostname)
        except:
            Log.error('can\'t resolve the ZFM server address {}', hostname)
            sys.exit(0)

        #
        # Setup the server environment.
        #
        self.address = hostname
        self.port = int(hostport)
        self.server = None

        try:
            self.server = HTTPServer((self.address, self.port), GenZHandler)
        except:
            output = subprocess.check_output('lsof -i:{}'.format(self.port), shell=True)
            Log.error('can\'t create HTTP server')
            Log.error(output.decode('utf-8'))
            sys.exit(0)

        self.server.formatter = ZFMFormatter()
        self.server.node_address = hostname
        self.server.redfish_base = os.path.join('redfish', 'v1')
        self.server.fabric = fabric
Beispiel #17
0
    def get(node, attribute):

        #
        # Send the REST command to the server.
        #
        headers = { "Accept": "application/json", "Content-Type": "application/json" }
        url = 'http://{address}{attribute}'.format(address=node.address, attribute=attribute)

        status, reply = Rest._rest_function(requests.get, url, headers=headers)

        if status != 200:
            Log.error('Rest(GET): {} failed with status {}', url,status)
            return False, None

        #
        # Good reply - need to convert from string -> data
        #
        if reply.startswith('<pre>') : reply = reply[5:]
        if reply.endswith('</pre>')  : reply = reply[:-6]

        try:
            status,data = True,json.loads(reply)
        except:
            Log.error('Rest.get() : invalid JSON returned.')
            Log.error('Data={}', reply)
            status,data = False,None

        return status, data
Beispiel #18
0
    def verify_fabric_health(self):
        error_count = 0
        for name, node in self.nodes.items():
            if not node.active: continue

            for port in node.ports:
                if port.active:
                    remote_node = self.locate_node(port.remote_uid)
                    if not remote_node:
                        Log.error('failed node connectivity test : {}/{}',
                                  name, port.index)
                        error_count += 1
                        continue

                    remote_port = remote_node.ports[port.remote_port]
                    if not remote_port:
                        Log.error('failed port connectivity test : {}/{}',
                                  name, port.index)
                        error_count += 1
                        continue

                    if remote_port.current['Status']['Health'] != 'OK':
                        Log.error('pair not healthy : {}/{} <-> {}/{}', name,
                                  port.index, remote_node.name,
                                  remote_port.index)
                        error_count += 1
                        continue

                    if remote_port.current['InterfaceState'] != 'Enabled':
                        Log.error('pair not enabled : {}/{} <-> {}/{}', name,
                                  port.index, remote_node.name,
                                  remote_port.index)
                        error_count += 1
                        continue

        if error_count > 0:
            Log.error('can\'t verify remote port state')

        return True if error_count == 0 else False
Beispiel #19
0
    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
Beispiel #20
0
    def format(self, data, consumer):
        data_type = data.get('DataType', None)

        if consumer not in self.delimiters:
            Log.error('invalid consumer ({}) specified for formatter',
                      consumer)
            return None
        elif not data_type:
            Log.error('data does not contain the "DataType" key')
            return None
        elif data_type not in self.funcs:
            Log.error('invalid type ({}) specified for formatter',
                      data['DataType'])
            return None

        #
        # We have a proper call, execute the formatter.
        #
        return self.funcs[data_type][consumer](data, consumer,
                                               self.delimiters[consumer])
Beispiel #21
0
    def is_trained(self):
        #
        #
        # Make sure that this port is enabled.
        #
        if not self.query():
            Log.error('{}[{}].train : can\'t read status', self.node.name,
                      self.index)
            self.active = False
            return False

        state, health = self.current['Status']['State'], self.current[
            'Status']['Health']

        #
        # Check the port health.
        #
        if health != 'OK':
            Log.error('{}[{}].train : port health {} is bad', self.node.name,
                      self.index, health)
            self.active = False
            return False

        #
        # Check the state.
        #
        if state == 'Disabled':
            return False
        elif state == 'Starting':
            return False
        elif state == 'StandbyOffline':
            return True
        elif state == 'Enabled':
            return True
        else:
            Log.error('{}[{}].train : port state {} is bad', self.node.name,
                      self.index, state)
            self.active = False
            return False
Beispiel #22
0
    def patch(node, attribute, value):

        #
        # Convert the input to JSON format.
        #
        try:
            data = json.dumps(value)
        except:
            Log.error('Rest.(PATCH) : could not convert input to JSON.')
            Log.error('Input={}', value)
            return False,None

        #
        # Send the REST command to the server.
        #
        headers = { "Accept": "application/json", "Content-Type": "application/json" }
        url = 'http://{address}{attribute}'.format(address=node.address, attribute=attribute)

        status, _ = Rest._rest_function(requests.patch, url, headers=headers, data=data)

        if status != 204:
            Log.error('Rest(PATCH): {} failed with status {}', url,status)

        return status == 204, None
Beispiel #23
0
                        default=socket.gethostname())
    parser.add_argument('-l',
                        '--log',
                        help='log level',
                        required=False,
                        default='warning')
    parser.add_argument('-s',
                        '--sweep',
                        help='sweep type',
                        required=False,
                        default='light')

    args = vars(parser.parse_args())
    args['conf'] = 'zfm.conf'

    Log.Init(args['log'])

    #
    # Chdir to the config directory.
    #
    try:
        os.chdir(args['dir'])
    except:
        Log.error('{} is not accessible', args['dir'])
        sys.exit(1)

    zfm_config_file = args['conf']
    zfm_sweep_type = args['sweep']
    hostname = args['host']

    #
Beispiel #24
0
 def run(self):
     Log.info('starting ZFM server @ {}:{}', self.address, self.port)
     self.server.serve_forever()
Beispiel #25
0
 def train_ports(self):
     status = self.wait_for('train', [], {})
     if not status:
         Log.error('ports not trained')
     return status
Beispiel #26
0
 def validate_ports(self):
     status = self.wait_for('validate', [], {})
     if not status:
         Log.error('ports not validated')
     return status
Beispiel #27
0
 def load_nodes(self):
     status = self.wait_for('load', [], {})
     if not status:
         Log.error('ports not loaded')
     return status
Beispiel #28
0
 def enable_ports(self):
     status = self.wait_for('enable', [], {})
     if not status:
         Log.error('ports not enabled')
     return status
Beispiel #29
0
    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)
Beispiel #30
0
 def sweep(self):
     status = self.wait_for('sweep', [self.sweep_type], {})
     if not status:
         Log.error('nodes not swept')
     return status