Skip to content
Snippets Groups Projects
Verified Commit f0f0113c authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

add iBGP mesh update workflow boilerplate

parent e5568160
No related branches found
No related tags found
1 merge request!126Add iBGP workflow and LibreNMS client
"""Add iBGP mesh workflow.
Revision ID: bacd55c26106
Revises: 815033570ad7
Create Date: 2023-12-18 17:58:29.581963
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'bacd55c26106'
down_revision = '815033570ad7'
branch_labels = None
depends_on = None
from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [
{
"name": "update_ibgp_mesh",
"target": "MODIFY",
"description": "Update iBGP mesh",
"product_type": "Router"
}
]
def upgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
create_workflow(conn, workflow)
def downgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
delete_workflow(conn, workflow["name"])
......@@ -41,6 +41,7 @@
"migrate_iptrunk": "Migrate IP Trunk",
"modify_isis_metric": "Modify the ISIS metric",
"modify_trunk_interface": "Modify IP Trunk interface",
"redeploy_base_config": "Redeploy base config"
"redeploy_base_config": "Redeploy base config",
"update_ibgp_mesh": "Update iBGP mesh"
}
}
......@@ -11,6 +11,7 @@ LazyWorkflowInstance("gso.workflows.iptrunk.terminate_iptrunk", "terminate_iptru
LazyWorkflowInstance("gso.workflows.router.create_router", "create_router")
LazyWorkflowInstance("gso.workflows.router.redeploy_base_config", "redeploy_base_config")
LazyWorkflowInstance("gso.workflows.router.terminate_router", "terminate_router")
LazyWorkflowInstance("gso.workflows.router.update_ibgp_mesh", "update_ibgp_mesh")
LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
LazyWorkflowInstance("gso.workflows.site.modify_site", "modify_site")
LazyWorkflowInstance("gso.workflows.site.terminate_site", "terminate_site")
......
from typing import Any
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State, UUIDstr
from orchestrator.workflow import StepList, done, init, step, workflow
from orchestrator.workflows.steps import resync, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from pydantic import root_validator
from gso.products.product_blocks.router import RouterRole
from gso.products.product_types.router import Router
from gso.services import provisioning_proxy, subscriptions
from gso.services.provisioning_proxy import pp_interaction, indifferent_pp_interaction
from gso.services.subscriptions import get_active_trunks_that_terminate_on_router
def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
subscription = Router.from_subscription(subscription_id)
class AddBGPSessionForm(FormPage):
class Config:
title = f"Add {subscription.router.router_fqdn} to the iBGP mesh?"
@root_validator
def router_has_a_trunk(cls, values: dict[str, Any]) -> dict[str, Any]:
if len(get_active_trunks_that_terminate_on_router(subscription_id)) == 0:
msg = "Selected router does not terminate any active IP trunks."
raise ValueError(msg)
return values
yield AddBGPSessionForm
return {"subscription": subscription}
@step("Calculate list of all active PE routers")
def calculate_pe_router_list() -> State:
all_routers = [Router.from_subscription(r["subscription_id"]) for r in
subscriptions.get_active_router_subscriptions()]
all_pe_routers = [router for router in all_routers if router.router.router_role == RouterRole.PE]
return {"pe_router_list": all_pe_routers}
def _generate_pe_inventory(pe_router_list: list[Router]) -> dict[str, Any]:
return {
"_meta": {
"vars": {
router.router.router_fqdn: {
"lo4": router.router.router_lo_ipv4_address,
"lo6": router.router.router_lo_ipv6_address,
"vendor": router.router.vendor,
}
} for router in pe_router_list
},
"all": {
"hosts": {
router.router.router_fqdn: None for router in pe_router_list
}
}
}
@step("[DRY RUN] Add P router to iBGP mesh")
def add_p_to_mesh_dry(subscription: Router, callback_route: str, pe_router_list: list[Router]) -> State:
extra_vars = {
"dry_run": True,
"subscription": subscription
}
provisioning_proxy.execute_playbook(
playbook_name="update_ibgp_mesh.yaml",
callback_route=callback_route,
inventory=_generate_pe_inventory(pe_router_list),
extra_vars=extra_vars,
)
return {"subscription": subscription}
@step("[FOR REAL] Add P router to iBGP mesh")
def add_p_to_mesh_real(subscription: Router, callback_route: str, pe_router_list: list[Router]) -> State:
extra_vars = {
"dry_run": False,
"subscription": subscription
}
provisioning_proxy.execute_playbook(
playbook_name="update_ibgp_mesh.yaml",
callback_route=callback_route,
inventory=_generate_pe_inventory(pe_router_list),
extra_vars=extra_vars,
)
return {"subscription": subscription}
@step("[DRY RUN] Add all PE routers to P router iBGP table")
def add_all_pe_to_p_dry(subscription: Router, pe_router_list: list[Router], callback_route: str) -> State:
extra_vars = {
"dry_run": True,
"pe_router_list": {
router.router.router_fqdn: {
"lo4": router.router.router_lo_ipv4_address,
"lo6": router.router.router_lo_ipv6_address,
"vendor": router.router.vendor,
} for router in pe_router_list
},
}
inventory = {
"all": {
"hosts": {
router.router.router_fqdn: None
} for router in pe_router_list
}
}
provisioning_proxy.execute_playbook(
playbook_name="update_ibgp_mesh.yaml",
callback_route=callback_route,
inventory=inventory,
extra_vars=extra_vars,
)
return {"subscription": subscription}
@step("[FOR REAL] Add all PE routers to P router iBGP table")
def add_all_pe_to_p_real(subscription: Router, pe_router_list: list[Router], callback_route: str) -> State:
extra_vars = {
"dry_run": False,
"pe_router_list": {
router.router.router_fqdn: {
"lo4": router.router.router_lo_ipv4_address,
"lo6": router.router.router_lo_ipv6_address,
"vendor": router.router.vendor,
} for router in pe_router_list
},
}
inventory = {
"all": {
"hosts": {
router.router.router_fqdn: None
} for router in pe_router_list
}
}
provisioning_proxy.execute_playbook(
playbook_name="update_ibgp_mesh.yaml",
callback_route=callback_route,
inventory=inventory,
extra_vars=extra_vars,
)
return {"subscription": subscription}
@step("Verify iBGP session health")
def check_ibgp_session(subscription: Router, callback_route: str) -> State:
inventory = {
"all": {
"hosts": {
subscription.router.router_fqdn: None
}
}
}
provisioning_proxy.execute_playbook(
playbook_name="check_ibgp.yaml",
callback_route=callback_route,
inventory=inventory,
extra_vars={},
)
return {"subscription": subscription}
@step("Add the router to LibreNMS")
def add_device_to_librenms(subscription: Router) -> State:
librenms_client.add_device(subscription)
return {"subscription": subscription}
@step("Update subscription model")
def update_subscription_model(subscription: Router) -> State:
subscription.router.router_access_via_ts = False
return {"subscription": subscription}
@workflow(
"Update iBGP mesh",
initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
target=Target.MODIFY,
)
def update_ibgp_mesh() -> StepList:
"""Update the iBGP mesh with a new P router
* Add the new P-router to all other PE-routers in the network, including a dry run.
* Add all PE-routers to the P-router, including a dry run.
* Verify that the iBGP session is up.
* Add the new P-router to LibreNMS.
* Update the subscription model.
"""
return (
init
>> store_process_subscription(Target.MODIFY)
>> unsync
>> calculate_pe_router_list
>> pp_interaction(add_p_to_mesh_dry)
>> pp_interaction(add_p_to_mesh_real)
>> pp_interaction(add_all_pe_to_p_dry)
>> pp_interaction(add_all_pe_to_p_real)
>> indifferent_pp_interaction(check_ibgp_session)
>> add_device_to_librenms
>> update_subscription_model
>> resync
>> done
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment