# ars_burt_sig_2000

import utils
import re
# from arsbase import RuleBase, RuleResult, Props
import pdb
from rulebase import RuleBase2
from configadvisor_rulebase import Rule
from result_ars import ARS_Result


class rule_3000(RuleBase2):
    """
    Checks to see if Open ACP cables are installed correctly and Open ACP is enabled on the storage
    controller
    """
    def __init__(self):
        # Do NOT edit this line #################
        super(self.__class__, self).__init__()  #
        #########################################

        # Rule properties
        self.RULE_ID = 3000
        self.PUBLIC = 'Y'               # MYASUP_PUBLIC
        self.CATEGORY = 'CA Sample Rule'              # MYASUP_CATEGORY
        self.RULE_TYPE = 4


    def preCheck(self, DM, **kwargs):
        preCheckFail = False
        if preCheckFail:
            return ARS_Result( status = ARS_Result.CNE, sub_id = sub_id,
                name='', description = '', target=utils.getHeaderValue(DM,'serial_num'),
                target_name=utils.getHeaderValue(DM, 'hostname'),
                notes=self.RULE_ERROR_TEMPLATE,
                rule_object=self)

        return None

    @property 
    def sections(self):
        return ['sysconfig -a', 'storage show acp -a', 'storage-acp.xml', 'system node run * sysconfig -a', \
               'storage shelf acp show -instance', 'system node run * storage show acp -a']

    def get_node(self, nodes, node_id):
        for node in nodes:
            if node.get('node_name')==node_id:
                return node
        for node in nodes:
            if node.get('system_id') and node['system_id']==node_id:
                return node        

    def run(self, asupid, DM):
        # for rules that evaluate multiple failure conditions, set rule_fail to True
        # so that an implicit Pass can be generated if no fail conditions are met.
        description = 'Checks to see if Open ACP cables are installed correctly and Open ACP is '\
            'enabled on the storage controller.'

        nodes = DM.get('nodes')
        if not nodes:
            yield Rule('Open ACP Cabled and Configured', '', '', description, Rule.CNE,
                       "Controller Nodes could not be identified.",
                       3000, Rule.MISSING_COMMAND_OUTPUT, \
                       rule_target="Controller")
            return

        node = nodes[0]
        if 'sysconfig -a' in node:
            for node in nodes:
                if 'sysconfig -a' not in node:
                    yield Rule('Open ACP Cabled and Configured', '', '', description, \
                               Rule.CNE, "Command output 'sysconfig -a' missing.",
                               3000, Rule.MISSING_COMMAND_OUTPUT, \
                               rule_target="Controller")
                    continue

                node_name = node['sysconfig -a'].get('hostname', '')
                system_serial = node['sysconfig -a'].get('serial')
                model = node['sysconfig -a'].get('model', '')

                if (not node_name or not system_serial or not model):
                    yield Rule('Open ACP Cabled and Configured', '', '', \
                               description, Rule.CNE,
                               "Unable to identify one or more of the following "\
                               "node attributes (Hostname, System Serial, Model).", \
                               3000, Rule.MISSING_MODEL_DATA, rule_target="Controller")
                    continue
            
                if model in ["FAS2020", "N3300",]:
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
                                   Rule.CNE, "Rule not applicable for FAS2020.",
                                   3000, Rule.NOT_APPLICABLE, \
                                   rule_target="Controller")
                    continue

# Temporarily removed a CNE condition
#                 sas_found = False
#                 for slot in node['sysconfig -a'].slots:
#                     if slot.slot_type in ["SAS", ] and list(slot.shelves):
#                         if model in ['FAS2040',] and slot.portname in ['0c', ]:
#                             continue
#                         sas_found = True
#                         break
                    
#                 if model in ['FAS2650', 'FAS2620', 'FAS2240-2', 'FAS2240-4', 'FAS2220', 'N3150', \
#                             'N3220', 'N3240', 'FAS2520', 'FAS2552', 'FAS2554',\
#                             'V2240-2', 'V2240-4', 'V2220', \
#                             'NS2520', 'NS2552', 'NS2554']:
#                     if not (node.has_key('environment status') and \
#                         node['environment status'].has_parser):
#                         yield Rule('Open ACP Cabled and Configured', '', '', \
#                                description, Rule.CNE, \
#                                "Command output 'environment status' missing.",
#                                3000, Rule.MISSING_COMMAND_OUTPUT, rule_target="Controller")
#                         return
                
#                     external_sas_found = False
#                     for channel in node['environment status'].channels:
#                         for shelf in list(channel.shelves):
#                             if str(shelf.a_module_square)!="Internal connector":
#                                 external_sas_found = True
#                                 break
                            
#                     if not external_sas_found:
#                         yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
#                                    Rule.CNE, "Rule applicable only when external SAS shelves are found.",
#                                    3000, Rule.NOT_APPLICABLE, \
#                                    rule_target="Controller")
#                         continue
                        
#                 if not sas_found:
#                     yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
#                                Rule.CNE, "Rule applicable only when external SAS shelves are found.",
#                                3000, Rule.NOT_APPLICABLE, \
#                                rule_target="Controller")
#                     continue

                if 'storage show acp -a' not in node:                    
                    if 'storage-acp.xml' not in node:
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
                               Rule.CNE, "Command output 'storage show acp -a' and 'storage-acp.xml' missing.",
                               3000, Rule.MISSING_COMMAND_OUTPUT, \
                               rule_target="Controller")
                        continue
                    
                if 'storage-acp.xml' in node:
                    try:
                        node_name, is_enabled, connection_status, error_type, error_text, \
                                   corrective_action = node['storage-acp.xml']['get_details']
                    except Exception as err:
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                   description, Rule.CNE,
                                   "Parser failed to extract Open ACP details.", \
                                   3000, Rule.MISSING_MODEL_DATA)
                        continue
                    if is_enabled in ['true', 'True'] and \
                           connection_status in ["active", "Active", "full-connectivity"]:
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                   description, "Pass", \
                                   "Open ACP Enabled, Active and Full Connectivity.", \
                                   3000, Rule.PASS)
                        continue
                    
                    message = ""
                    if is_enabled:
                        message = "Open ACP Enable Status : %s."%is_enabled
                    if connection_status:
                        if message:
                            message = message + ' '
                        message = message + "Open ACP Connection Status : %s."%connection_status
                    if error_text:
                        if message:
                            message = message + ' '
                        message = message + "Error Text : %s."%error_text
                    if corrective_action:
                        if message:
                            message = message + ' '
                        message = message + "Corrective Actions : %s."%corrective_action
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, "Fail", \
                               message, \
                               3000, 105, 'Info')
                    continue
                    
                node_acp = node['storage show acp -a']
                could_not_evaluate = False
                if node_acp.get('acp_enabled','') == 'Disabled':
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, "Fail", \
                               "Alternate Control Path (ACP) not enabled on this controller.", \
                               3000, 100, 'Info')
                elif node_acp.get('acp_enabled','') == 'Enabled':
                    if node_acp.get('acp_status','') == 'Inactive':
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                   description, "Fail", \
                                   "Open ACP Enabled, but Inactive. Alternate Control Path (ACP) "\
                                   "has been enabled, but there is no Open ACP connectivity to the "\
                                   "storage controller.", \
                                   3000, 101, 'Low')
                    elif node_acp.get('acp_status','') == 'Active':
                        if node_acp.get('acp_conn_status','') == 'No Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, but no cables are present. Alternate Control"\
                                       " Path (ACP) has been enabled, but there is no Open ACP "\
                                       "connectivity to the storage controller", \
                                       3000, 102, 'Low')
                        elif node_acp.get('acp_conn_status','') == 'Partial Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, partial connectivity. Alternate Control "\
                                       "Path (ACP) has been enabled, Fewer shelf IOMs are detected"\
                                       " out-of-band (through ACP) than in-band (through SAS).", \
                                       3000, 103, 'Low')
                        elif node_acp.get('acp_conn_status','') == 'Additional Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, additional connectivity. Alternate Control "\
                                       "Path (ACP) has been enabled, More shelf IOMs are detected "\
                                       "out-of-band (through ACP) than in-band (through SAS).", \
                                       3000, 104, 'Low')
                        elif node_acp.get('acp_conn_status','') == 'Full Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Pass", "Open ACP Enabled, Active and Full "\
                                       "Connectivity.", \
                                       3000, Rule.PASS)
                        else:
                            could_not_evaluate = True
                    else:
                        could_not_evaluate = True
                else:
                    could_not_evaluate = True

                if could_not_evaluate == True:
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, Rule.CNE,
                               "Parser failed to extract Open ACP details.", \
                               3000, Rule.MISSING_MODEL_DATA)
        elif 'system node run * sysconfig -a' in node and \
             'nodes' in node['system node run * sysconfig -a']:
            if 'system node run * storage show acp -a' not in node:
                if 'storage shelf acp show -instance' not in node:                
                    yield Rule('Open ACP Cabled and Configured', '', '', description, \
                               Rule.CNE, "Command output 'system node run * storage show acp -a' "\
                               "and 'storage shelf acp show -instance' "\
                               "missing.",
                               3000, Rule.MISSING_COMMAND_OUTPUT, \
                               rule_target="Controller")
                    return
                
            nodes = [node_match for node_match in node['system node run * sysconfig -a'].get('nodes', [])]
            if not nodes:
                yield Rule('Open ACP Cabled and Configured', '', '', \
                           description, Rule.CNE, \
                           "Nodes in the cluster could not be identified.", \
                           3000, Rule.MISSING_COMMAND_OUTPUT, \
                           rule_target="Controller")
                return

            for eachnode in nodes:
                model = eachnode.get('model')
                node_name = eachnode.get('node_name')
                release = eachnode.get('version_number')
                system_serial = eachnode.get('serial')
                
                if model in ["FAS2020", "N3300", ]:
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
                                Rule.CNE, "Rule not applicable for FAS2020.",
                                3000, Rule.NOT_APPLICABLE, \
                                rule_target="Controller")
                    continue

# Temporarily removed a CNE condition                    
#                 if model in ['FAS2650', 'FAS2620', 'FAS2240-2', 'FAS2240-4', 'FAS2220', 'N3150', \
#                             'N3220', 'N3240', 'FAS2520', 'FAS2552', 'FAS2554',\
#                             'V2240-2', 'V2240-4', 'V2220', \
#                             'NS2520', 'NS2552', 'NS2554']:
#                     if not (node.has_key('system node run * environment status') and \
#                         node['system node run * environment status'].has_parser):
#                         yield Rule('Open ACP Cabled and Configured', '', '', \
#                                description, Rule.CNE, \
#                                "Command output 'system node run * environment status' missing.",
#                                3000, Rule.MISSING_COMMAND_OUTPUT, rule_target="Controller")
#                         return
                
#                     external_sas_found = False
#                     environment_status = node['system node run * environment status'].get_node(node_name)
#                     for channel in environment_status.channels:
#                         for shelf in list(channel.shelves):
#                             if str(shelf.a_module_square)!="Internal connector":
#                                 external_sas_found = True
#                                 break
#                     if not external_sas_found:
#                         yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
#                                    Rule.CNE, "Rule applicable only when external SAS shelves are found.",
#                                    3000, Rule.NOT_APPLICABLE, \
#                                    rule_target="Controller")
#                         continue
                        
#                 sas_found = False
#                 for slot in eachnode.slots:
#                     if slot.slot_type in ["SAS", ] and list(slot.shelves):
#                         if model in ['FAS2040',] and slot.portname in ['0c', ]:
#                             continue
#                         sas_found = True
#                         break
                
#                 if not sas_found:
#                     yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
#                                Rule.CNE, "Rule applicable only when external SAS shelves are found.",
#                                3000, Rule.NOT_APPLICABLE, \
#                                rule_target="Controller")
#                     continue

                if 'storage shelf acp show -instance' in node:
                    for acpnode in node['storage shelf acp show -instance'].get('node_instances',[]):
                        if node_name not in acpnode.get('node_name'):
                            continue
                        break
                    else:
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, description, \
                               Rule.CNE, "Command 'storage shelf acp show -instance' does not contain node details.",
                               3000, Rule.MISSING_MODEL_DATA, \
                               rule_target="Controller")
                        continue
                    if acpnode.get('enable_status') in ['true', 'True'] and \
                           acpnode.get('connection_status') in ["active", "Active", "full-connectivity"]:
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                   description, "Pass", \
                                   "Open ACP Enabled, Active and Full Connectivity.", \
                                                 3000, Rule.PASS)
                        continue
                    
                    message = ""
                    if acpnode.get('enable_status'):
                        message = "Open ACP Enable Status : %s."%acpnode.get('enable_status')
                    if acpnode.get('connection_status'):
                        if message:
                            message = message + ' '
                        message = message + "Open ACP Connection Status : %s."%acpnode.get('connection_status')
                    if acpnode.get('error_text'):
                        if message:
                            message = message + ' '
                        message = message + "Error Text : %s."%acpnode.get('error_text')
                    if acpnode.get('corrective_action'):
                        if message:
                            message = message + ' '
                        message = message + "Corrective Actions : %s."%acpnode.get('corrective_action')

                        
                    impact = "Low"
                    rule_id = 105
                    
                    if acpnode.get('enable_status') in ['false', 'False'] and acpnode.get('connection_status') in ["disabled", "Disabled"]:
                        rule_id = 106
                        impact = "Info"
                
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, "Fail", \
                               message, \
                               3000, rule_id, impact)
                    continue

                node_acp = self.get_node(node['system node run * storage show acp -a'].get('nodes', []), node_name)
                could_not_evaluate = False

                if node_acp and node_acp.get('acp_enabled','') == 'Disabled':
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, "Fail", \
                               "Alternate Control Path (ACP) not enabled on this controller.", \
                               3000, 100, 'Info')
                elif node_acp and node_acp.get('acp_enabled','') == 'Enabled':
                    if node_acp.get('acp_status','') == 'Inactive':
                        yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                   description, "Fail", \
                                   "Open ACP Enabled, but Inactive. Alternate Control Path (ACP) has "\
                                   "been enabled, but there is no Open ACP connectivity to the storage"\
                                   " controller.", \
                                   3000, 101, 'Low')
                    elif node_acp.get('acp_status','') == 'Active':
                        if node_acp.get('acp_conn_status','') == 'No Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, but no cables are present. Alternate Control"\
                                       " Path (ACP) has been enabled, but there is no Open ACP "\
                                       "connectivity to the storage controller.", \
                                       3000, 102, 'Low')
                        elif node_acp.get('acp_conn_status','') == 'Partial Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, partial connectivity. Alternate Control Path"\
                                       " (ACP) has been enabled, Fewer shelf IOMs are detected "\
                                       "out-of-band (through ACP) than in-band (through SAS).", \
                                       3000, 103, 'Low')
                        elif node_acp.get('acp_conn_status') == 'Additional Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Fail", \
                                       "Open ACP Enabled, additional connectivity. Alternate Control "\
                                       "Path (ACP) has been enabled, More shelf IOMs are detected"\
                                       " out-of-band (through ACP) than in-band (through SAS).", \
                                       3000, 104, 'Low')
                        elif node_acp.get('acp_conn_status') == 'Full Connectivity':
                            yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                                       description, "Pass", \
                                       "Open ACP Enabled, Active and Full Connectivity.", \
                                       3000, Rule.PASS)
                        else:
                            could_not_evaluate = True
                    else:
                        could_not_evaluate = True
                else:
                    could_not_evaluate = True

                if could_not_evaluate == True:
                    yield Rule('Open ACP Cabled and Configured', system_serial, node_name, \
                               description, Rule.CNE,
                               "Parser failed to extract Open ACP details.", \
                               3000, Rule.MISSING_MODEL_DATA)
        else:
            yield Rule('Open ACP Cabled and Configured', '', '', description, Rule.CNE,
                       "Command output 'sysconfig -a' or 'system node run * sysconfig -a' is missing.", \
                       3000, Rule.MISSING_COMMAND_OUTPUT, \
                       rule_target="Controller")


def contents():
    return [{'class': rule_3000, 'iface_version': 1}]
