Exemplo n.º 1
0
def _build_networks() -> List[Dict]:
    """Build filtered JSON Structure of networks & devices for Jinja templates."""
    networks = []
    _networks = list(
        set({device.network.display_name
             for device in devices.objects}))

    for _network in _networks:
        network_def = {"display_name": _network, "locations": []}
        for device in devices.objects:
            if device.network.display_name == _network:
                network_def["locations"].append({
                    "_id":
                    device._id,
                    "name":
                    device.name,
                    "network":
                    device.network.display_name,
                    "vrfs": [
                        {
                            "_id": vrf._id,
                            "display_name": vrf.display_name,
                            "default": vrf.default,
                            "ipv4": True if vrf.ipv4 else False,  # noqa: IF100
                            "ipv6": True if vrf.ipv6 else False,  # noqa: IF100
                        } for vrf in device.vrfs
                    ],
                })
        networks.append(network_def)

    if not networks:
        raise ConfigError(
            error_msg="Unable to build network to device mapping")
    return networks
Exemplo n.º 2
0
    async def response(self):
        """Initiate query validation and execution."""
        device = getattr(devices, self.query_location)

        log.debug(f"Received query for {self.query_data}")
        log.debug(f"Matched device config: {device}")

        supported, transport = validate_nos(device.nos)

        connect = None
        output = params.messages.general
        connect = Connect(device, self.query_data, transport)

        if supported and transport == "rest":
            output = await connect.rest()

        elif supported and transport == "scrape":
            if device.proxy:
                output = await connect.scrape_proxied()
            else:
                output = await connect.scrape_direct()
        else:
            raise ConfigError('"{nos}" is not supported.', nos=device.nos)

        if output == "" or output == "\n":
            raise ResponseEmpty(params.messages.no_output,
                                device_name=device.display_name)

        log.debug(
            f"Output for query: {self.query_data.json()}:\n{repr(output)}")

        return output
Exemplo n.º 3
0
def _build_frontend_devices():
    """Build filtered JSON structure of devices for frontend.

    Schema:
    {
        "device.name": {
            "display_name": "device.display_name",
            "vrfs": [
                "Global",
                "vrf.display_name"
            ]
        }
    }

    Raises:
        ConfigError: Raised if parsing/building error occurs.

    Returns:
        {dict} -- Frontend devices
    """
    frontend_dict = {}
    for device in devices.objects:
        if device.name in frontend_dict:
            frontend_dict[device.name].update({
                "network":
                device.network.display_name,
                "display_name":
                device.display_name,
                "vrfs": [
                    {
                        "id": vrf.name,
                        "display_name": vrf.display_name,
                        "default": vrf.default,
                        "ipv4": True if vrf.ipv4 else False,  # noqa: IF100
                        "ipv6": True if vrf.ipv6 else False,  # noqa: IF100
                    } for vrf in device.vrfs
                ],
            })
        elif device.name not in frontend_dict:
            frontend_dict[device.name] = {
                "network":
                device.network.display_name,
                "display_name":
                device.display_name,
                "vrfs": [
                    {
                        "id": vrf.name,
                        "display_name": vrf.display_name,
                        "default": vrf.default,
                        "ipv4": True if vrf.ipv4 else False,  # noqa: IF100
                        "ipv6": True if vrf.ipv6 else False,  # noqa: IF100
                    } for vrf in device.vrfs
                ],
            }
    if not frontend_dict:
        raise ConfigError(
            error_msg="Unable to build network to device mapping")
    return frontend_dict
Exemplo n.º 4
0
def _config_required(config_path: Path) -> dict:
    try:
        with config_path.open("r") as cf:
            config = yaml.safe_load(cf)
            log.debug("Unvalidated data from file '{f}': {c}",
                      f=str(config_path),
                      c=config)
    except (yaml.YAMLError, yaml.MarkedYAMLError) as yaml_error:
        raise ConfigError(str(yaml_error))
    return config
Exemplo n.º 5
0
 def validate_address(cls, value, values):
     """Ensure a hostname is resolvable."""
     if not isinstance(value, (IPv4Address, IPv6Address)):
         if not any(resolve_hostname(value)):
             raise ConfigError(
                 "Device '{d}' has an address of '{a}', which is not resolvable.",
                 d=values["name"],
                 a=value,
             )
     return value
Exemplo n.º 6
0
def _config_required(config_path: Path) -> Dict:
    try:
        with config_path.open("r") as cf:
            config = yaml.safe_load(cf)

    except (yaml.YAMLError, yaml.MarkedYAMLError) as yaml_error:
        raise ConfigError(str(yaml_error))

    if config is None:
        log.critical("{} appears to be empty", str(config_path))
        raise ConfigMissing(missing_item=config_path.name)

    return config
Exemplo n.º 7
0
def _config_optional(config_path: Path) -> Dict:

    if config_path is None:
        config = {}

    else:
        try:
            with config_path.open("r") as cf:
                config = yaml.safe_load(cf) or {}

        except (yaml.YAMLError, yaml.MarkedYAMLError) as yaml_error:
            raise ConfigError(error_msg=str(yaml_error))

    return config
Exemplo n.º 8
0
def _config_optional(config_path: Path) -> dict:
    if config_path is None:
        config = {}
    else:
        try:
            with config_path.open("r") as cf:
                config = yaml.safe_load(cf) or {}
                log.debug(
                    "Unvalidated data from file '{f}': {c}",
                    f=str(config_path),
                    c=config,
                )
        except (yaml.YAMLError, yaml.MarkedYAMLError) as yaml_error:
            raise ConfigError(error_msg=str(yaml_error))
    return config
Exemplo n.º 9
0
    def validate_structured_output(cls, value: bool, values: Dict) -> bool:
        """Validate structured output is supported on the device & set a default."""

        if value is True and values["nos"] not in SUPPORTED_STRUCTURED_OUTPUT:
            raise ConfigError(
                "The 'structured_output' field is set to 'true' on device '{d}' with "
                + "NOS '{n}', which does not support structured output",
                d=values["name"],
                n=values["nos"],
            )

        elif value is None and values["nos"] in SUPPORTED_STRUCTURED_OUTPUT:
            value = True
        else:
            value = False

        return value
Exemplo n.º 10
0
def validate_nos_commands(all_nos: List[str], commands: Commands) -> bool:
    """Ensure defined devices have associated commands."""
    custom_commands = commands.dict().keys()

    for nos in all_nos:
        valid = False
        if nos in (*SUPPORTED_STRUCTURED_OUTPUT, *TRANSPORT_REST,
                   *custom_commands):
            valid = True

        if not valid:
            raise ConfigError(
                '"{nos}" is used on a device, ' +
                'but no command profile for "{nos}" is defined.',
                nos=nos,
            )

    return True
Exemplo n.º 11
0
def _build_frontend_networks():
    """Build filtered JSON structure of networks for frontend.

    Schema:
    {
        "device.network.display_name": {
            "device.name": {
                "display_name": "device.display_name",
                "vrfs": [
                    "Global",
                    "vrf.display_name"
                ]
            }
        }
    }

    Raises:
        ConfigError: Raised if parsing/building error occurs.

    Returns:
        {dict} -- Frontend networks
    """
    frontend_dict = {}
    for device in devices.routers:
        if device.network.display_name in frontend_dict:
            frontend_dict[device.network.display_name].update({
                device.name: {
                    "display_name": device.network.display_name,
                    "vrfs": [vrf.display_name for vrf in device.vrfs],
                }
            })
        elif device.network.display_name not in frontend_dict:
            frontend_dict[device.network.display_name] = {
                device.name: {
                    "display_name": device.network.display_name,
                    "vrfs": [vrf.display_name for vrf in device.vrfs],
                }
            }
    frontend_dict["default_vrf"] = devices.default_vrf
    if not frontend_dict:
        raise ConfigError(
            error_msg="Unable to build network to device mapping")
    return frontend_dict
Exemplo n.º 12
0
def _validate_nos_commands(all_nos, commands):
    nos_with_commands = commands.dict().keys()

    for nos in all_nos:
        valid = False
        if nos in SUPPORTED_STRUCTURED_OUTPUT:
            valid = True
        elif nos in TRANSPORT_REST:
            valid = True
        elif nos in nos_with_commands:
            valid = True

        if not valid:
            raise ConfigError(
                '"{nos}" is used on a device, ' +
                'but no command profile for "{nos}" is defined.',
                nos=nos,
            )

    return True
Exemplo n.º 13
0
    def validate_structured_output(cls, value, values):
        """Validate structured output is supported on the device & set a default.

        Raises:
            ConfigError: Raised if true on a device that doesn't support structured output.

        Returns:
            {bool} -- True if hyperglass should return structured output for this device.
        """
        if value is True and values["nos"] not in SUPPORTED_STRUCTURED_OUTPUT:
            raise ConfigError(
                "The 'structured_output' field is set to 'true' on device '{d}' with "
                + "NOS '{n}', which does not support structured output",
                d=values["name"],
                n=values["nos"],
            )

        elif value is None and values["nos"] in SUPPORTED_STRUCTURED_OUTPUT:
            value = True
        else:
            value = False

        return value
Exemplo n.º 14
0
def _build_networks():
    """Build filtered JSON Structure of networks & devices for Jinja templates.

    Raises:
        ConfigError: Raised if parsing/building error occurs.

    Returns:
        {dict} -- Networks & devices
    """
    networks = []
    _networks = list(
        set({device.network.display_name
             for device in devices.routers}))

    for _network in _networks:
        network_def = {"display_name": _network, "locations": []}
        for device in devices.routers:
            if device.network.display_name == _network:
                network_def["locations"].append({
                    "name":
                    device.name,
                    "display_name":
                    device.display_name,
                    "network":
                    device.network.display_name,
                    "vrfs": [{
                        "id": vrf.name,
                        "display_name": vrf.display_name
                    } for vrf in device.vrfs],
                })
        networks.append(network_def)

    if not networks:
        raise ConfigError(
            error_msg="Unable to build network to device mapping")
    return networks
Exemplo n.º 15
0
    def validate_vrfs(cls, value, values):
        """Validate VRF definitions.

          - Ensures source IP addresses are set for the default VRF
            (global routing table).
          - Initializes the default VRF with the DefaultVRF() class so
            that specific defaults can be set for the global routing
            table.
          - If the 'display_name' is not set for a non-default VRF, try
            to make one that looks pretty based on the 'name'.

        Arguments:
            value {list} -- List of VRFs
            values {dict} -- Other already-validated fields

        Raises:
            ConfigError: Raised if the VRF is missing a source address

        Returns:
            {list} -- List of valid VRFs
        """
        vrfs = []
        for vrf in value:
            vrf_name = vrf.get("name")

            for afi in ("ipv4", "ipv6"):
                vrf_afi = vrf.get(afi)

                # If AFI is actually defined (enabled), and if the
                # source_address field is not set, raise an error
                if vrf_afi is not None and vrf_afi.get(
                        "source_address") is None:
                    raise ConfigError(
                        ("VRF '{vrf}' in router '{router}' is missing a source "
                         "{afi} address."),
                        vrf=vrf.get("name"),
                        router=values.get("name"),
                        afi=afi.replace("ip", "IP"),
                    )

            # If no display_name is set for a non-default VRF, try
            # to make one by replacing non-alphanumeric characters
            # with whitespaces and using str.title() to make each
            # word look "pretty".
            if vrf_name != "default" and not isinstance(
                    vrf.get("display_name"), StrictStr):
                new_name = vrf["name"]
                new_name = re.sub(r"[^a-zA-Z0-9]", " ", new_name)
                new_name = re.split(" ", new_name)
                vrf["display_name"] = " ".join([w.title() for w in new_name])

                log.debug(
                    f'Field "display_name" for VRF "{vrf["name"]}" was not set. '
                    f"Generated '{vrf['display_name']}'")

            elif vrf_name == "default" and vrf.get("display_name") is None:
                vrf["display_name"] = "Global"

            # Validate the non-default VRF against the standard
            # Vrf() class.
            vrf = Vrf(**vrf)

            vrfs.append(vrf)
        return vrfs