Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config Deployment and Remediation #533

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1647ee9
Adds ConfigPlan model and features (#521)
joewesch Jul 19, 2023
c9817c5
Config Remediation (#512)
mzbroch Jul 19, 2023
de7cb78
Run Black and update dependencies (#532)
itdependsnetworks Jul 19, 2023
758170e
Split Migrations (#534)
itdependsnetworks Jul 20, 2023
be3eb57
Fixes field name to be singular
joewesch Jul 20, 2023
33a4890
Add Config Set modal to list view, copy button on modal/detail view, …
itdependsnetworks Jul 21, 2023
a839773
Move job and form toggle to JS and add job_result/cm url to model
itdependsnetworks Jul 24, 2023
117afb2
Add customer filter, minor updates
itdependsnetworks Jul 24, 2023
9f753c0
Apply suggestions from code review
itdependsnetworks Jul 24, 2023
235b707
Merge branch 'next' into config-working-group-temp
itdependsnetworks Jul 26, 2023
d8c157f
just pushing new poetry lock after merge conflicts were fixed
jeffkala Jul 26, 2023
513c2f6
Fixes device serializer to return names
joewesch Jul 27, 2023
dc319da
Removes unused import
joewesch Jul 27, 2023
ce1343b
Fixes working group tests (#548)
joewesch Aug 21, 2023
58fb4aa
Adds config deployment job and UI elements (#545)
joewesch Aug 21, 2023
4ae180c
add back locations to form
itdependsnetworks Aug 23, 2023
685b0d6
fix default for control_url
jeffkala Aug 25, 2023
72e4195
update js job stat poller to support more job state
jeffkala Aug 25, 2023
fe8583a
Merge branch 'config-working-group-temp' into job_via_js
jeffkala Aug 25, 2023
b1f7d21
fixes ci testing
jeffkala Aug 25, 2023
3f9c54d
undo a messed up merge conflict resolution and pylint
jeffkala Aug 25, 2023
739e1eb
config plan fixes
jeffkala Aug 26, 2023
27e075e
config plan fixes
jeffkala Aug 26, 2023
f1b7239
Update nautobot_golden_config/forms.py
jeffkala Aug 26, 2023
1f4d250
Merge pull request #537 from itdependsnetworks/job_via_js
jeffkala Aug 26, 2023
8c2969e
Remediation Updates (#546)
pato23arg Aug 28, 2023
b62ccd2
Minor changes and fixes (#554)
joewesch Aug 28, 2023
328d874
Merge branch 'next' into config-working-group-temp-3
itdependsnetworks Aug 29, 2023
e31153e
generate poetry lock
itdependsnetworks Aug 29, 2023
0c0ea48
Merge pull request #556 from itdependsnetworks/fix-conflicts
itdependsnetworks Aug 29, 2023
87f3fa4
Apply suggestions from code review
joewesch Sep 1, 2023
513e768
rerun migrations to fix name and multifiles (#559)
jeffkala Sep 1, 2023
dabbbf3
Fixes screenshots and fixes title for config plan edit view (#561)
joewesch Sep 1, 2023
5f43d69
Update docs/user/app_feature_config_plans.md
jeffkala Sep 1, 2023
15f35d6
Apply suggestions from code review
jeffkala Sep 1, 2023
721af8e
Apply suggestions from code review
jeffkala Sep 5, 2023
d3fdf3e
Adds message if Job is not enabled (#562)
joewesch Sep 5, 2023
fb458d9
additional pr review updates for remediation feature (#564)
jeffkala Sep 6, 2023
c1da463
Merge branch 'next' into config-working-group-temp
jeffkala Sep 6, 2023
a252dcb
Update nautobot_golden_config/forms.py
jeffkala Sep 6, 2023
841f63d
run black after import conflicct
jeffkala Sep 6, 2023
248f052
fix imports again after merge conflict
jeffkala Sep 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ creds.env
nautobot_golden_config/transposer.py
docker-compose.override.yml
packages/
invoke.yml

# Ansible Retry Files
*.retry
Expand Down
Binary file added docs/images/config_plan-edit.png
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/config_plan-generate-filters.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/config_plan-generate-manual.png
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/config_plan-generate-missing.png
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/config_plan-view.png
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/remediation_hier_edit_options.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/remediation_validate_feature.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions docs/user/app_feature_config_plans.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Navigating Config Plans

The natural progression for the Golden Config application is providing the ability to execute config deployments. One specific example is to work toward making one or more devices configuration compliant. To aid in this effort, the Golden Config application has the ability to generate plans containing sets of configuration commands from various sources with the intent of deploying them to devices.

The current sources of these plans (i.e. plan types) are as follows:

- The **Intended** configuration(s) of Compliance Feature(s)
- The **Missing** configuration(s) of Compliance Feature(s)
- The **Remediation** configuration(s) of Compliance Feature(s) (*)
- A **Manual** set of configuration commands

!!! note
The Intended, Missing and Remediation configuration come from the [Configuration Compliance](./app_feature_compliance.md#compliance-details-view) object that is created when you run the [Perform Configuration Compliance Job](./app_feature_compliance.md#starting-a-compliance-job).

Much like a Configuration Compliance object, each Config Plan is tied directly to a single Device.

## Viewing a Config Plan

You can view a plan by navigating to **Golden Config -> Config Plans** and choosing a generated plan from the list. A Config Plan comprises of the following fields:

- **Device**: The device the plan is to be deployed to.
- **Date Created**: The date the plan was generated.
- **Plan Type**: The type of plan used to generate it.
- **Config Set**: The set of commands to be deployed.
- **Feature** (If Applicable): The Compliance Feature the config set was generated from.
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
- **Change Control ID** (Optional): A text field that be used for grouping and filtering plans.
joewesch marked this conversation as resolved.
Show resolved Hide resolved
- **Change Control URL** (Optional): A URL field that can be used to link to an external system tracking change controls.
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
- **Status**: The status of the plan.

![Config Plan View](../images/config_plan-view.png)

## Generating Config Plans

In order to generate a plan, navigate to **Golden Config -> Config Plans** and hit the **Add** button. After choosing the type of plan you want to generate, you can then filter the list of devices you want to generate a Config Plan for by selecting either the list of devices themselves or a by choosing one or more related items such as Location or Status. If you select a plan type that is derived from a Configuration Compliance object, you will have the ability to only generate plans for one or more features, but selecting no features will generate plans for all applicable features.

In addition, you have the ability to specify a Change Control ID that can be associated with all of the plans that will be generated. This can come in handy when it comes to filtering the list of plans to ultimately deploy.
jeffkala marked this conversation as resolved.
Show resolved Hide resolved

Once you have selected the appropriate options, you can click the **Generate** button which will start a Job to generate the plans. You will automatically be forwarded to the Job Result output page so you can monitor the progress.
jeffkala marked this conversation as resolved.
Show resolved Hide resolved

### Screenshots

![Config Plan Generate Missing](../images/config_plan-generate-missing.png)

![Config Plan Generate Filters](../images/config_plan-generate-filters.png)

![Config Plan Generate Manual](../images/config_plan-generate-manual.png)

### Generating Config Plans via API

The HTTP(S) POST method is not currently enabled for the Config Plan serializer to create plans directly via API. Instead you may run the **GenerateConfigPlans** Job directly via the `plugins/nautobot_golden_config.jobs/GenerateConfigPlans` API endpoint.

## Editing a Config Plan

After a Config Plan is generated you have the ability to edit (or bulk edit) the following fields:

- Change Control ID
joewesch marked this conversation as resolved.
Show resolved Hide resolved
- Change Control URL
- Status
- Notes
- Tags

!!! note
You will not be able to modify the Config Set after generation. If it does not contain the desired commands, you will need to delete the plan and recreate it after ensuring the source of the generated commands has been updated.

![Config Plan Edit](../images/config_plan-edit.png)
69 changes: 69 additions & 0 deletions docs/user/app_feature_remediation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Navigating Configuration Remediation

Automated network configuration remediation is a systematic approach that leverages technology and processes to address and rectify configuration issues in network devices.
It involves the use of the Golden Configuration plugin to understand the current configuration state, compare it against the intended configuration state, and automatically generate remediation data.
Automated network configuration remediation improves efficiency by eliminating manual efforts and reducing the risk of human errors. It enables rapid response to security vulnerabilities, minimizes downtime, and enhances compliance with regulatory and industry standards.


The current sources of data to generate remediating configuration are as follows:

- The **Intended** configuration of a specific Compliance Feature
- The **Missing** configuration of a specific Compliance Feature
- The **Extra** configuration of a specific Compliance Feature

Based on this information, Golden Configuration will create a remediating configuration (if enabled for that particular platform and compliance feature). This configuration snippet will be represented as a "Remediating Configuration" field in the compliance detailed view:

- The **Remediation** configuration of a specific Compliance Feature


!!! note
The Intended, Missing and Extra configuration come from the [Configuration Compliance](./app_feature_compliance.md#compliance-details-view) object that is created when you run the [Perform Configuration Compliance Job](./app_feature_compliance.md#starting-a-compliance-job).


## Setting up Configuration Remediation

The type of remediation to be performed in a particular platform is defined by navigating to **Golden Config -> Remediation Settings**.
Network device operating systems (Nautobot Platforms) can consume two different types of remediation, namely:

- **HIERCONFIG remediation (CLI - hierarchical)**
- **Custom Remediation**

![Remediation Platform Settings](../images/remediation_settings_per_platform.png)

### Hier Config Remediation Type

Hier Config is a python library that is able to take a running configuration of a network device, compare it to its intended configuration, and build the remediation steps necessary to bring a device into spec with its intended configuration. Hier Config has been used extensively on:

- Cisco IOS
- Cisco IOSXR
- Cisco NXOS
- Arista EOS
- Ruckus FastIron

However, any Network Operating System (NOS) that utilizes a CLI syntax that is structured in a similar fashion to Cisco IOS should work mostly out of the box.
Default Hier config options can be used or customized on a per platform basis, as shown below:

![Hier Options Customization](../images/remediation_hier_edit_options.png)

For additional information on how to customize Hier Config options, please refer to the Hierarchical Configuration development guide:
https://netdevops.io/hier_config/advanced-topics/

### Custom Config Remediation Type

When a Network Operating System delivers configuration data in a format that is not CLI/Hierarchical, we can still perform remediation by using the Custom Remediation options. Custom Remediation is defined within a Python function that takes as input a Configuration Compliance object and returns a Remediation Field.
Custom remediation performs a call to the remediation function every time a Compliance Job runs. Custom Remediation allows the user to control the configuration comparison process (between intended and actual configuration) and use additional Nautobot or external data to produce the remediation plan. Custom remediation functions need to be defined in PLUGIN_CONFIG for `nautobot_plugin_golden_config` the nautobot_config.py file, as show below:

![Custom Remediation Function Setup](../images/remediation_custom_function_setup.png)

## Enabling Configuration Remediation

Once remediation settings are configured for a particular platform, remediation can be enabled on a per compliance feature basis. In order to enable configuration remediation for a particular feature, navigate to **Golden Config -> Compliance Rules**, and choose a rule for a platform that has remediation settings set up. Edit the compliance rule and check the box "Enable Remediation". This action effectively enables remediation for that particular Platform/Feature pair.
jeffkala marked this conversation as resolved.
Show resolved Hide resolved

![Enable Configuration Remediation per Feature](../images/remediation_enable_compliance_rule_feature.png)


## Validating Configuration Remediation

Once remediation is configured for a particular Platform/Feature pair, it is possible to validate remediation operations by running a compliance job. Navigate to **Jobs -> Run Configuration Compliance** and run a compliance job for a device that has remediation enabled. Verify that remediation data has been generated by navigating to **Golden Config -> Compliance**, select the device and check the compliance status for the feature with remediation enabled and the "Remediating Configuration" field, as shown below:
jeffkala marked this conversation as resolved.
Show resolved Hide resolved

![Validate Configuration Remediation](../images/remediation_validate_feature.png)
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ nav:
- Navigate Intended: "user/app_feature_intended.md"
- Navigate SoT Agg: "user/app_feature_sotagg.md"
- Navigate Configuration Post-Processing: "user/app_feature_config_postprocessing.md"
- Navigate Config Plans: "user/app_feature_config_plans.md"
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
- Navigate Remediation: "user/app_feature_remediation.md"
- Getting Started: "user/app_getting_started.md"
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
- Frequently Asked Questions: "user/app_faq.md"
- External Interactions: "user/app_external_interactions.md"
Expand Down
11 changes: 10 additions & 1 deletion nautobot_golden_config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
__version__ = metadata.version(__name__)

from django.db.models.signals import post_migrate
from nautobot.core.signals import nautobot_database_ready
from nautobot.extras.plugins import PluginConfig


Expand Down Expand Up @@ -39,7 +40,15 @@ def ready(self):
"""Register custom signals."""
from nautobot_golden_config.models import ConfigCompliance # pylint: disable=import-outside-toplevel

from .signals import config_compliance_platform_cleanup # pylint: disable=import-outside-toplevel
# pylint: disable=import-outside-toplevel
from .signals import (
config_compliance_platform_cleanup,
post_migrate_create_statuses,
post_migrate_create_job_button,
)

nautobot_database_ready.connect(post_migrate_create_statuses, sender=self)
nautobot_database_ready.connect(post_migrate_create_job_button, sender=self)

super().ready()
post_migrate.connect(config_compliance_platform_cleanup, sender=ConfigCompliance)
Expand Down
48 changes: 47 additions & 1 deletion nautobot_golden_config/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
# pylint: disable=too-many-ancestors
from rest_framework import serializers

from nautobot.apps.api import WritableNestedSerializer
from nautobot.extras.api.fields import StatusSerializerField
from nautobot.extras.api.serializers import TaggedObjectSerializer
from nautobot.extras.api.nested_serializers import NestedDynamicGroupSerializer
from nautobot.extras.models import Status
from nautobot.dcim.api.nested_serializers import NestedDeviceSerializer
from nautobot.dcim.api.serializers import DeviceSerializer
from nautobot.dcim.models import Device
from nautobot.extras.api.serializers import NautobotModelSerializer
from nautobot.extras.api.serializers import NautobotModelSerializer, StatusModelSerializerMixin


from nautobot_golden_config import models
Expand Down Expand Up @@ -149,3 +153,45 @@ def get_config(self, obj):

config_details = models.GoldenConfig.objects.get(device=obj)
return get_config_postprocessing(config_details, request)


class RemediationSettingSerializer(NautobotModelSerializer, TaggedObjectSerializer):
"""Serializer for RemediationSetting object."""

url = serializers.HyperlinkedIdentityField(
view_name="plugins-api:nautobot_golden_config-api:remediationsetting-detail"
)

class Meta:
"""Set Meta Data for RemediationSetting, will serialize all fields."""

model = models.RemediationSetting
choices_fields = ["remediation_type"]
fields = "__all__"


class ConfigPlanSerializer(NautobotModelSerializer, TaggedObjectSerializer, StatusModelSerializerMixin):
"""Serializer for ConfigPlan object."""

url = serializers.HyperlinkedIdentityField(view_name="plugins-api:nautobot_golden_config-api:configplan-detail")
device = NestedDeviceSerializer(required=False)
status = StatusSerializerField(required=False, queryset=Status.objects.all())

class Meta:
"""Set Meta Data for ConfigPlan, will serialize all fields."""

model = models.ConfigPlan
fields = "__all__"
read_only_fields = ["device", "plan_type", "feature", "config_set"]


class NestedConfigPlanSerializer(WritableNestedSerializer):
"""Nested serializer for ConfigPlan object."""

url = serializers.HyperlinkedIdentityField(view_name="plugins-api:nautobot_golden_config-api:configplan-detail")

class Meta:
"""Set Meta Data for ConfigPlan, will serialize brief fields."""

model = models.ConfigPlan
fields = ["id", "url", "device", "plan_type"]
2 changes: 2 additions & 0 deletions nautobot_golden_config/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
router.register("golden-config-settings", views.GoldenConfigSettingViewSet)
router.register("config-remove", views.ConfigRemoveViewSet)
router.register("config-replace", views.ConfigReplaceViewSet)
router.register("remediation-setting", views.RemediationSettingViewSet)
router.register("config-postprocessing", views.ConfigToPushViewSet)
router.register("config-plan", views.ConfigPlanViewSet)
urlpatterns = router.urls
urlpatterns.append(
path(
Expand Down
19 changes: 19 additions & 0 deletions nautobot_golden_config/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,22 @@ class ConfigToPushViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
permission_classes = [IsAuthenticated & ConfigPushPermissions]
queryset = Device.objects.all()
serializer_class = serializers.ConfigToPushSerializer


class RemediationSettingViewSet(NautobotModelViewSet): # pylint:disable=too-many-ancestors
"""API viewset for interacting with ConfigRemove objects."""

queryset = models.RemediationSetting.objects.all()
serializer_class = serializers.RemediationSettingSerializer
filterset_class = filters.RemediationSettingFilterSet


class ConfigPlanViewSet(NautobotModelViewSet): # pylint:disable=too-many-ancestors
"""API viewset for interacting with ConfigPlan objects."""

queryset = models.ConfigPlan.objects.all()
serializer_class = serializers.ConfigPlanSerializer
filterset_class = filters.ConfigPlanFilterSet

# Disabling POST as these should only be created via Job.
http_method_names = ["get", "put", "patch", "delete", "head", "options"]
28 changes: 28 additions & 0 deletions nautobot_golden_config/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,31 @@ class ComplianceRuleConfigTypeChoice(ChoiceSet):
(TYPE_CLI, "CLI"),
(TYPE_JSON, "JSON"),
)


class RemediationTypeChoice(ChoiceSet):
"""Choiceset used by RemediationSetting."""

TYPE_HIERCONFIG = "hierconfig"
TYPE_CUSTOM = "custom_remediation"

CHOICES = (
(TYPE_HIERCONFIG, "HIERCONFIG"),
(TYPE_CUSTOM, "CUSTOM_REMEDIATION"),
)


class ConfigPlanTypeChoice(ChoiceSet):
"""Choiceset used by ConfigPlan."""

TYPE_INTENDED = "intended"
TYPE_MISSING = "missing"
TYPE_REMEDIATION = "remediation"
TYPE_MANUAL = "manual"

CHOICES = (
(TYPE_INTENDED, "Intended"),
(TYPE_MISSING, "Missing"),
(TYPE_REMEDIATION, "Remediation"),
(TYPE_MANUAL, "Manual"),
)
23 changes: 23 additions & 0 deletions nautobot_golden_config/filter_extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Custom filter to extend base API for filterform use case."""
import django_filters
from nautobot.apps.filters import FilterExtension


def config_plan_null_search(queryset, name, value): # pylint: disable=unused-argument
"""Query to ensure config plans are not empty."""
return queryset.filter(config_plan__isnull=False).distinct()


class JobResultFilterExtension(FilterExtension):
"""Filter provided to be used in select2 query for only jobs that were used by ConfigPlan."""

model = "extras.jobresult"

filterset_fields = {
"nautobot_golden_config_config_plan_null": django_filters.BooleanFilter(
label="Is FK to ConfigPlan Model", method=config_plan_null_search
)
}


filter_extensions = [JobResultFilterExtension]
Loading