Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • goat/gap/geant-service-orchestrator
1 result
Show changes
Commits on Source (7)
# Glossary of terms
{.glossary}
BGP
: Border Gateway Protocol: a path vector routing protocol described in
<a href="https://datatracker.ietf.org/doc/html/rfc4271" target="_blank">RFC 4271</a>.
CRUD
: Create, Read, Update, Delete
......@@ -26,5 +30,9 @@ LSO
NET
: Network Entity Title: used for {term}`IS-IS` routing.
SNMP
: Simple Network Management Protocol: a protocol that's used for gathering data, widely used for network management and
monitoring.
WFO
: <a href="https://workfloworchestrator.org/" target="_blank">Workflow Orchestrator</a>
......@@ -27,6 +27,7 @@ TokenIgnores = ({term}), (:param \S+:), (:type \S+:)
[*/glossary.md]
; Ignore acronyms being undefined in the file that defines all acronyms by definition.
Microsoft.Acronyms = NO
Microsoft.Passive = NO
[formats]
py = md
"""The product block that describes a site subscription."""
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
......@@ -5,6 +7,12 @@ from orchestrator.types import SubscriptionLifecycle, strEnum
class SiteTier(strEnum):
"""The tier of a site, ranging from 1 to 4.
A lower value generally corresponds to a larger amount of installed equipment, and a higher cardinality of the
connectivity to and from a site.
"""
TIER1 = 1
TIER2 = 2
TIER3 = 3
......@@ -12,6 +20,8 @@ class SiteTier(strEnum):
class SiteBlockInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="SiteBlock"):
"""A site that's currently inactive, see {class}`SiteBlock`."""
site_name: Optional[str] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
......@@ -24,6 +34,8 @@ class SiteBlockInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INIT
class SiteBlockProvisioning(SiteBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
"""A site that's currently being provisioned, see {class}`SiteBlock`."""
site_name: Optional[str] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
......@@ -36,12 +48,25 @@ class SiteBlockProvisioning(SiteBlockInactive, lifecycle=[SubscriptionLifecycle.
class SiteBlock(SiteBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
"""A site that's currently available for devices and services to be hosted at."""
site_name: str
"""The name of the site, that will dictate part of the {term}`FQDN` of devices that are hosted at this site. For
example: `device.X.Y.geant.net`, where X denotes the name of the site."""
site_city: str
"""The city at which the site is located."""
site_country: str
"""The country in which the site is located."""
site_country_code: str
"""The code of the corresponding country. This is also used for the {term}`FQDN`, following the example given for
the site name, the country code would end up in the Y position."""
site_latitude: float
"""The latitude of the site, used for {term}`SNMP` purposes."""
site_longitude: float
"""Similar to the latitude, the longitude of a site."""
site_internal_id: int
"""The internal ID used within GÉANT to denote a site."""
site_bgp_community_id: int
"""The {term}`BGP` community ID of a site, used to advertise routes learned at this site."""
site_tier: SiteTier
"""The tier of a site, as described in {class}`SiteTier`."""
"""Product types define the different products that are available.
Multiple product blocks may insist on a single product type. In that sense, a product type is a mode general description
of a product. For example, the product type {class}`Device` must either be a router or a switch, both being separate
product blocks.
"""
......@@ -5,6 +5,11 @@ from gso.products.product_blocks.device import DeviceBlock, DeviceBlockInactive,
class DeviceType(strEnum):
"""Enumerator for different device types.
A device can either be a router or a switch.
"""
router = "router"
switch = "switch"
......
......@@ -203,22 +203,25 @@ def _confirm_pp_results(state: State) -> FormGenerator:
:return: Confirmation from the user, when presented with the run results.
:rtype: {class}`orchestrator.types.FormGenerator`
"""
if "pp_run_results" not in state:
# FIXME: dirty hack that makes the skipping """work"""
return {"pp_did_succeed": True}
successful_run = state["pp_run_results"]["return_code"] == 0
class ConfirmRunPage(FormPage):
class Config:
title = (
f"Execution for "
f"{state['subscription']['product']['name']} "
f"completed, please confirm the results below."
f"Execution for {state['subscription']['product']['name']} completed, please confirm the results below."
)
if not successful_run:
pp_retry_label1: Label = (
"Provisioning Proxy - playbook execution failed: inspect the output before proceeding" # type: ignore
)
run_status: str = ReadOnlyField(state["pp_run_results"]["status"])
run_results: LongText = ReadOnlyField(f"{state['pp_run_results']['output']}")
run_results: LongText = ReadOnlyField(json.dumps(state["pp_run_results"]["output"], indent=4))
if not successful_run:
pp_retry_label: Label = (
"Click submit to retry. Otherwise, abort the workflow from the process tab." # type: ignore
......
......@@ -29,7 +29,9 @@
"iptrunk_sideB_ae_iface": "Aggregated Ethernet interface name",
"iptrunk_sideB_ae_geant_a_sid": "GÉANT A-SID",
"iptrunk_sideB_ae_members": "Aggregated Ethernet member interface names",
"iptrunk_sideB_ae_members_descriptions": "Aggregated Ethernet member interface descriptions"
"iptrunk_sideB_ae_members_descriptions": "Aggregated Ethernet member interface descriptions",
"remove_configuration": "Remove configuration from the device",
"clean_up_ipam": "Clean up related entries in IPAM"
}
},
"workflow": {
......
......@@ -5,7 +5,7 @@ from orchestrator.forms import FormPage
from orchestrator.forms.validators import Label
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import StepList, done, init, step, workflow
from orchestrator.workflow import StepList, conditional, done, init, step, workflow
from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
......@@ -20,14 +20,15 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
subscription = Device.from_subscription(subscription_id)
class TerminateForm(FormPage):
are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?" # type: ignore
termination_label: Label = (
f"Please confirm whether configuration should get removed from the {subscription.device_type}, and "
"whether IPAM resources should be released." # type: ignore
)
remove_configuration: bool = True
clean_up_ipam: bool = True
return TerminateForm # type: ignore
def _deprovision_in_user_management_system(fqdn: str) -> None:
logger.debug(fqdn)
pass
user_input = yield TerminateForm
return user_input.dict()
@step("Deprovision loopback IPs from IPAM/DNS")
......@@ -73,19 +74,28 @@ def deprovision_lt_ips(subscription: Device) -> dict[str, V4ServiceNetwork | V6S
}
@step("Remove configuration from device")
def remove_config_from_device() -> None:
pass
@workflow(
"Terminate device",
initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
target=Target.TERMINATE,
)
def terminate_device() -> StepList:
run_ipam_steps = conditional(lambda state: state.get("clean_up_ipam", True))
run_config_steps = conditional(lambda state: state.get("remove_configuration", True))
ipam_steps = StepList([deprovision_loopback_ips, deprovision_si_ips, deprovision_lt_ips])
return (
init
>> store_process_subscription(Target.TERMINATE)
>> unsync
>> deprovision_loopback_ips
>> deprovision_si_ips
>> deprovision_lt_ips
>> run_ipam_steps(ipam_steps)
>> run_config_steps(remove_config_from_device)
>> set_status(SubscriptionLifecycle.TERMINATED)
>> resync
>> done
......
# noinspection PyProtectedMember
import ipaddress
from orchestrator.forms import FormPage
from orchestrator.forms.validators import Label
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import StepList, done, init, step, workflow
from orchestrator.workflow import StepList, conditional, done, init, step, workflow
from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
......@@ -15,17 +14,21 @@ from gso.services.ipam import V4ServiceNetwork, V6ServiceNetwork
from gso.services.provisioning_proxy import pp_interaction
def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
subscription = Iptrunk.from_subscription(subscription_id)
def initial_input_form_generator() -> FormGenerator:
class TerminateForm(FormPage):
are_you_sure: Label = f"Are you sure you want to remove {subscription.description}?" # type: ignore
termination_label: Label = (
"Please confirm whether configuration should get removed from the A and B sides of the trunk, and whether "
"IPAM resources should be released." # type: ignore
)
remove_configuration: bool = True
clean_up_ipam: bool = True
return TerminateForm # type: ignore
user_input = yield TerminateForm
return user_input.dict()
@step("Set iptrunk ISIS metric to 9000")
def modify_iptrunk_subscription(subscription: Iptrunk) -> State:
def update_isis_metric(subscription: Iptrunk) -> State:
subscription.iptrunk.iptrunk_isis_metric = 9000
return {"subscription": subscription}
......@@ -87,15 +90,23 @@ def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> dict[str, V4ServiceNetwo
target=Target.TERMINATE,
)
def terminate_iptrunk() -> StepList:
run_config_steps = conditional(lambda state: state.get("remove_configuration", True))
run_ipam_steps = conditional(lambda state: state.get("clean_up_ipam", True))
config_steps = (
StepList([update_isis_metric])
>> pp_interaction(drain_traffic_from_ip_trunk, 3)
>> pp_interaction(deprovision_ip_trunk_dry, 3)
>> pp_interaction(deprovision_ip_trunk_real, 3)
)
ipam_steps = StepList([deprovision_ip_trunk_ipv4, deprovision_ip_trunk_ipv6])
return (
init
>> store_process_subscription(Target.TERMINATE)
>> unsync
>> modify_iptrunk_subscription
>> pp_interaction(drain_traffic_from_ip_trunk, 3)
>> pp_interaction(deprovision_ip_trunk_dry, 3)
>> deprovision_ip_trunk_ipv4
>> deprovision_ip_trunk_ipv6
>> run_config_steps(config_steps)
>> run_ipam_steps(ipam_steps)
>> set_status(SubscriptionLifecycle.TERMINATED)
>> resync
>> done
......