Skip to content
Snippets Groups Projects
Commit ea68d1a0 authored by geant-release-service's avatar geant-release-service
Browse files

Finished release 2.28.

parents 2fd3c507 91b42671
No related branches found
No related tags found
No related merge requests found
Pipeline #90961 passed
Showing
with 266 additions and 60 deletions
......@@ -61,7 +61,7 @@ build-documentation:
lint-documentation:
stage: documentation
image:
name: jdkato/vale:latest
name: jdkato/vale:v3.9.1
entrypoint: [""]
tags:
- docker-executor
......
# Changelog
## [2.28] - 2024-12-13
- Fix an issue where LSO could receive special characters from a workflow state.
- Unify GÉANT service ID resource type naming, and add sequence to the database for generating them.
- Fix a bug in the Edge Port validation workflow
- Add support for DCN VLAN numbering in the LAN Switch Interconnect product.
- Auto-generate IP resources when creating a new LAN Switch Interconnect based on the Site it's in.
- Update documentation.
## [2.27] - 2024-12-03
- Add support for VRFs
- Rework the way code documentation is built and pulished
......
FROM python:3.12-alpine
FROM python:3.12.7-alpine
WORKDIR /app
ARG ARTIFACT_VERSION
......
......@@ -5,7 +5,7 @@ set -o nounset
export OSS_PARAMS_FILENAME=../gso/oss-params-example.json
export TESTING=true
pip install pyyaml mkdocstrings-python mkdocs_gen_files mkdocs-material mkdocs-literate-nav mkdocs-redirects
pip install pyyaml mkdocstrings-python mkdocs_gen_files mkdocs-material mkdocs-literate-nav mkdocs-redirects mkdocs-open-in-new-tab
pip install -e ..
python ./scripts/gen_wf_redirects.py
......
......@@ -5,6 +5,7 @@
*[CFS]: Customer Facing Service
*[CIDR]: Classless Inter-Domain Routing
*[DCIM]: Datacenter Infrastructure Manager
*[DCN]: Datacenter Network
*[DHCP]: Dynamic Host Configuration Protocol
*[DNS]: Domain Name System
*[DTAP]: Development, Testing, Acceptance, and Production
......
......@@ -29,10 +29,11 @@ with Path.open(root / "docs" / "wf_redirects.yaml", "w") as redirect_file:
file_content = {
"plugins": [
"search",
{"open-in-new-tab": {"add_icon": True}},
{"gen-files": {"scripts": ["scripts/gen_ref_pages.py"]}},
{"redirects": {"redirect_maps": redirect_map}},
{"literate-nav": {"nav_file": "SUMMARY.md"}},
"mkdocstrings",
{"mkdocstrings": {"handlers": {"python": {"options": {"filters": [], "members_order": "source"}}}}},
]
}
yaml.dump(file_content, redirect_file)
......@@ -22,7 +22,7 @@ classDiagram
A node consists of one or more routers, a switch, and a terminal server.
In general -- as laid out more extensively
<a href="https://wiki.geant.org/display/NETENG/001+-+Topology+and+physical+layout" target="_blank">here</a>
[here](https://wiki.geant.org/display/NETENG/001+-+Topology%2C+physical+layout+and+site+design)
(behind login) -- a PoP consists of:
* One or two routers
......
# Components of GAP
As stated before, GAP is a platform and not a monolithic piece of software. GAP interacts with different OSS/BSS
systems already present in GÉANT and these are tightly integrated with the automation platform.
systems already present in GÉANT and these are integrated with the automation platform.
From a high level point of view, GAP can be seen as the sum of the following parts:
......
......@@ -18,11 +18,9 @@ library dependencies.
## Inner workings
LSO uses <a href="https://ansible.readthedocs.io/projects/runner/en/latest/"
target="_blank">`ansible-runner`</a> for the execution of Ansible playbooks.
This package fully dictates the way in which GAP interacts with Ansible itself.
LSO only introduces an API with a single REST endpoint that exposes its
functionality.
LSO uses [`ansible-runner`](https://ansible.readthedocs.io/projects/runner/en/latest/) for the execution of Ansible
playbooks. This package fully dictates the way in which GAP interacts with Ansible itself. LSO only introduces an API
with a single REST endpoint that exposes its functionality.
In the case of GAP, all Ansible playbooks operate without an inventory that
contains all relevant `group_vars` and `host_vars`. The inventory is passed to
......@@ -148,15 +146,12 @@ full-fledged API request to LSO, an example call is given.
## Code documentation
Code documentation for LSO can be found
<a href="https://workfloworchestrator.org/lso" target="_blank">here</a>.
Code documentation for LSO can be found [here](https://workfloworchestrator.org/lso).
## Deployment within GÉANT
For the deployment in GÉANT, LSO runs inside a Docker container. The Dockerfile
used to build this container is available <a href=
"https://gitlab.software.geant.org/goat/gap/lso/-/blob/develop/Dockerfile"
target="_blank">here</a>.
For the deployment in GÉANT, LSO runs inside a Docker container. A Dockerfile that can be used to build this container
is available [here](https://github.com/workfloworchestrator/lso/blob/main/Dockerfile.example).
When building the Docker image, some Ansible roles and collections are installed
that are required for interacting with Juniper and Nokia equipment. For another
......
......@@ -57,16 +57,12 @@ configuration backups of routers, switches, and any other network devices that a
More detailed information about this integration is available in the
[LibreNMS integration module](../admin_guide/oss_bss/librenms.md).
### Kentik (planned)
### Kentik
Kentik is a Network Observability tool which collects various data points from deployed PE routers.
For this reason it is not in scope for PHASE1.
### Inventory provider (planned)
### Inventory provider
At the time of writing, the Inventory Provider gets the list of routers from the network engineering SOT servers.
This will change and Inventory Provider is then able to directly query CoreDB.
## Interaction with a technical domain: IP/MPLS
TBA
......@@ -33,10 +33,10 @@ Microsoft.SentenceLength = NO
; This statement ignores the names of parameters in docstrings, the four spaces that prepend it and the one following it
; are necessary.
TokenIgnores = \S+:
TokenIgnores = (?: *\S+: ), (?:`\S+`)
[*.md]
BasedOnStyles = Vale, proselint, Microsoft
[formats]
py = rst
py = md
......@@ -11,6 +11,7 @@ CIDR
CFS
CNAME
DCIM
DCN
DDI
DHCP
DNS
......@@ -90,3 +91,4 @@ WFO
dry_run
eBGP
iBGP
disable?[sd]
......@@ -64,7 +64,7 @@ class IptrunkBlock(OrchestratorBaseModel):
iptrunk_capacity: str
iptrunk_isis_metric: int
iptrunk_sides: list[IptrunkSideBlock]
geant_s_sid: str
gs_id: str
class IptrunkSchema(OrchestratorBaseModel):
......@@ -109,7 +109,7 @@ def network_topology() -> NetworkTopologyDomainModelSchema:
"iptrunk_capacity": _calculate_iptrunk_capacity(
extended_model["iptrunk"]["iptrunk_sides"], extended_model["iptrunk"]["iptrunk_speed"]
),
"geant_s_sid": extended_model["iptrunk"]["geant_s_sid"],
"gs_id": extended_model["iptrunk"]["gs_id"],
"iptrunk_sides": [
{
"subscription_instance_id": side["subscription_instance_id"],
......
......@@ -43,7 +43,7 @@ def ensure_openid_config_loaded(func: Callable) -> Callable:
class OIDCAuthentication(OIDCAuth):
"""OIDCUser class extends the ``HTTPBearer`` class to do extra verification.
"""OIDCUser class extends the `HTTPBearer` class to do extra verification.
The class will act as follows: Validate the Credentials at the AAI proxy by calling the UserInfo endpoint.
"""
......
......@@ -40,20 +40,20 @@ from gso.services.subscriptions import (
)
from gso.utils.shared_enums import SBPType, Vendor
from gso.utils.types.base_site import BaseSiteValidatorModel
from gso.utils.types.geant_ids import IMPORTED_GA_ID, IMPORTED_GS_ID
from gso.utils.types.interfaces import BandwidthString, LAGMember, LAGMemberList, PhysicalPortCapacity
from gso.utils.types.ip_address import (
AddressSpace,
IPAddress,
IPv4AddressType,
IPV4Netmask,
IPv4NetworkType,
IPv4Netmask,
IPv6AddressType,
IPV6Netmask,
IPv6Netmask,
PortNumber,
)
from gso.utils.types.virtual_identifiers import VC_ID, VLAN_ID
app: typer.Typer = typer.Typer()
IMPORT_WAIT_MESSAGE = "Waiting for the dust to settle before importing new products..."
class CreatePartner(BaseModel):
......@@ -64,11 +64,11 @@ class CreatePartner(BaseModel):
class SiteImportModel(BaseSiteValidatorModel):
"""The required input for importing an existing ``gso.products.product_types.site``."""
"""The required input for importing an existing `gso.products.product_types.site`."""
class RouterImportModel(BaseModel):
"""Required fields for importing an existing ``gso.product.product_types.router``."""
"""Required fields for importing an existing `gso.product.product_types.router`."""
partner: str
router_site: str
......@@ -82,7 +82,7 @@ class RouterImportModel(BaseModel):
class SwitchImportModel(BaseModel):
"""Required fields for importing an existing ``gso.product.product_types.switch``."""
"""Required fields for importing an existing `gso.product.product_types.switch`."""
fqdn: str
ts_port: PortNumber
......@@ -92,7 +92,7 @@ class SwitchImportModel(BaseModel):
class SuperPopSwitchImportModel(BaseModel):
"""Required fields for importing an existing ``gso.product.product_types.super_pop_switch``."""
"""Required fields for importing an existing `gso.product.product_types.super_pop_switch`."""
partner: str
super_pop_switch_site: str
......@@ -102,7 +102,7 @@ class SuperPopSwitchImportModel(BaseModel):
class OfficeRouterImportModel(BaseModel):
"""Required fields for importing an existing ``gso.product.product_types.office_router``."""
"""Required fields for importing an existing `gso.product.product_types.office_router`."""
partner: str
office_router_site: str
......@@ -113,10 +113,10 @@ class OfficeRouterImportModel(BaseModel):
class IptrunkImportModel(BaseModel):
"""Required fields for importing an existing ``gso.products.product_types.iptrunk``."""
"""Required fields for importing an existing `gso.products.product_types.iptrunk`."""
partner: str
geant_s_sid: str | None
gs_id: IMPORTED_GS_ID | None
iptrunk_type: IptrunkType
iptrunk_description: str | None = None
iptrunk_speed: PhysicalPortCapacity
......@@ -124,11 +124,11 @@ class IptrunkImportModel(BaseModel):
iptrunk_isis_metric: int
side_a_node_id: str
side_a_ae_iface: str
side_a_ae_geant_a_sid: str | None
side_a_ga_id: IMPORTED_GA_ID | None
side_a_ae_members: LAGMemberList[LAGMember]
side_b_node_id: str
side_b_ae_iface: str
side_b_ae_geant_a_sid: str | None
side_b_ga_id: IMPORTED_GA_ID | None
side_b_ae_members: LAGMemberList[LAGMember]
iptrunk_ipv4_network: ipaddress.IPv4Network
......@@ -178,7 +178,7 @@ class IptrunkImportModel(BaseModel):
class OpenGearImportModel(BaseModel):
"""Required fields for importing an existing ``gso.products.product_types.opengear``."""
"""Required fields for importing an existing `gso.products.product_types.opengear`."""
partner: str
opengear_site: str
......@@ -189,7 +189,7 @@ class OpenGearImportModel(BaseModel):
class EdgePortImportModel(BaseModel):
"""Required fields for importing an existing ``gso.products.product_types.edge_port``."""
"""Required fields for importing an existing `gso.products.product_types.edge_port`."""
node: str
service_type: EdgePortType
......@@ -197,7 +197,7 @@ class EdgePortImportModel(BaseModel):
encapsulation: EncapsulationType
name: str
minimum_links: int
geant_ga_id: str | None
ga_id: IMPORTED_GA_ID | None
mac_address: str | None
partner: str
enable_lacp: bool
......@@ -269,15 +269,15 @@ class L3CoreServiceImportModel(BaseModel):
edge_port: str
ap_type: str
geant_sid: str
gs_id: IMPORTED_GS_ID
sbp_type: SBPType = SBPType.L3
is_tagged: bool = False
vlan_id: VLAN_ID
custom_firewall_filters: bool = False
ipv4_address: IPv4AddressType
ipv4_mask: IPV4Netmask
ipv4_mask: IPv4Netmask
ipv6_address: IPv6AddressType
ipv6_mask: IPV6Netmask
ipv6_mask: IPv6Netmask
is_multi_hop: bool = True
bgp_peers: list["L3CoreServiceImportModel.BaseBGPPeer"]
v4_bfd_settings: "L3CoreServiceImportModel.BFDSettingsModel"
......@@ -315,7 +315,6 @@ class LanSwitchInterconnectRouterSideImportModel(BaseModel):
node: UUIDstr
ae_iface: str
ae_members: LAGMemberList[LAGMember]
ipv4_address: IPv4AddressType
class LanSwitchInterconnectSwitchSideImportModel(BaseModel):
......@@ -324,15 +323,12 @@ class LanSwitchInterconnectSwitchSideImportModel(BaseModel):
switch: UUIDstr
ae_iface: str
ae_members: LAGMemberList[LAGMember]
ipv4_address: IPv4AddressType
class LanSwitchInterconnectImportModel(BaseModel):
"""Import LAN Switch Interconnect model."""
lan_switch_interconnect_description: str
lan_switch_interconnect_ip_network: IPv4NetworkType | None
address_space: AddressSpace
minimum_links: int
router_side: LanSwitchInterconnectRouterSideImportModel
switch_side: LanSwitchInterconnectSwitchSideImportModel
......@@ -349,7 +345,7 @@ class Layer2CircuitServiceImportModel(BaseModel):
service_type: Layer2CircuitServiceType
partner: str
geant_sid: str
gs_id: IMPORTED_GS_ID
vc_id: VC_ID
layer_2_circuit_side_a: ServiceBindingPortInput
layer_2_circuit_side_b: ServiceBindingPortInput
......@@ -456,7 +452,7 @@ def _generic_import_product(
except ValidationError as e:
typer.echo(f"Validation error: {e}")
typer.echo("Waiting for the dust to settle before moving on the importing new products...")
typer.echo(IMPORT_WAIT_MESSAGE)
time.sleep(1)
# Migrate new products from imported to "full" counterpart.
......@@ -544,7 +540,7 @@ def import_edge_port(filepath: str = common_filepath_option) -> None:
except ValidationError as e:
typer.echo(f"Validation error: {e}")
typer.echo("Waiting for the dust to settle before moving on the importing new products...")
typer.echo(IMPORT_WAIT_MESSAGE)
time.sleep(1)
edge_port_ids = get_subscriptions(
......@@ -592,7 +588,7 @@ def import_iptrunks(filepath: str = common_filepath_option) -> None:
try:
initial_data = IptrunkImportModel(
partner="GEANT",
geant_s_sid=trunk["id"],
gs_id=trunk["id"],
iptrunk_type=trunk["config"]["common"]["type"],
iptrunk_description=trunk["config"]["common"].get("description", ""),
iptrunk_speed=trunk["config"]["common"]["link_speed"],
......@@ -600,11 +596,11 @@ def import_iptrunks(filepath: str = common_filepath_option) -> None:
iptrunk_isis_metric=trunk["config"]["common"]["isis_metric"],
side_a_node_id=_get_router_subscription_id(trunk["config"]["nodeA"]["name"]) or "",
side_a_ae_iface=trunk["config"]["nodeA"]["ae_name"],
side_a_ae_geant_a_sid=trunk["config"]["nodeA"]["port_sid"],
side_a_ga_id=trunk["config"]["nodeA"]["port_ga_id"],
side_a_ae_members=trunk["config"]["nodeA"]["members"],
side_b_node_id=_get_router_subscription_id(trunk["config"]["nodeB"]["name"]) or "",
side_b_ae_iface=trunk["config"]["nodeB"]["ae_name"],
side_b_ae_geant_a_sid=trunk["config"]["nodeB"]["port_sid"],
side_b_ga_id=trunk["config"]["nodeB"]["port_ga_id"],
side_b_ae_members=trunk["config"]["nodeB"]["members"],
iptrunk_ipv4_network=iptrunk_ipv4_network,
iptrunk_ipv6_network=iptrunk_ipv6_network,
......@@ -615,7 +611,7 @@ def import_iptrunks(filepath: str = common_filepath_option) -> None:
except ValidationError as e:
typer.echo(f"Validation error: {e}")
typer.echo("Waiting for the dust to settle before moving on the importing new products...")
typer.echo(IMPORT_WAIT_MESSAGE)
time.sleep(1)
trunk_ids = get_subscriptions(
......@@ -675,7 +671,7 @@ def import_l3_core_service(filepath: str = common_filepath_option) -> None:
except ValidationError as e:
typer.echo(f"Validation error: {e}")
typer.echo("Waiting for the dust to settle before importing new products...")
typer.echo(IMPORT_WAIT_MESSAGE)
time.sleep(1)
# Migrate new products from imported to "full" counterpart.
......@@ -733,7 +729,7 @@ def import_layer_2_circuit_service(filepath: str = common_filepath_option) -> No
)
except ValidationError as e:
typer.echo(f"Validation error: {e}")
typer.echo("Waiting for the dust to settle before importing new products...")
typer.echo(IMPORT_WAIT_MESSAGE)
time.sleep(1)
# Migrate new products from imported to "full" counterpart.
......
"""Update LAN Switch Interconnect.
Revision ID: e854e0c35e20
Revises: 79192e72131c
Create Date: 2024-11-04 17:21:14.612740
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'e854e0c35e20'
down_revision = '79192e72131c'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))
"""))
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space'))
"""))
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('address_space', 'lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM resource_types WHERE resource_types.resource_type IN ('address_space', 'lan_switch_interconnect_ip_network')
"""))
def downgrade() -> None:
conn = op.get_bind()
"""Clean up deprecated references to NREN-names products
Revision ID: 79a76b22ca53
Revises: e854e0c35e20
Create Date: 2024-12-03 17:01:33.752966
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '79a76b22ca53'
down_revision = 'e854e0c35e20'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('GÉANT IP', 'IAS', 'Imported GÉANT IP', 'Imported IAS')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('NRENL3CoreServiceBlock'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('NRENL3CoreServiceBlock', 'NRENAccessPort'))
"""))
conn.execute(sa.text("""
DELETE FROM product_blocks WHERE product_blocks.name IN ('NRENL3CoreServiceBlock', 'NRENAccessPort')
"""))
def downgrade() -> None:
conn = op.get_bind()
"""Rename iptrunk and iptrunk side sid and gid.
Revision ID: e358efe9ab03
Revises: 28c1723c6a00
Create Date: 2024-12-04 15:05:46.356709
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'e358efe9ab03'
down_revision = '28c1723c6a00'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='ga_id' WHERE resource_types.resource_type = 'iptrunk_side_ae_geant_a_sid'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='gs_id' WHERE resource_types.resource_type = 'geant_s_sid'
"""))
def downgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='iptrunk_side_ae_geant_a_sid' WHERE resource_types.resource_type = 'ga_id'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='geant_s_sid' WHERE resource_types.resource_type = 'gs_id'
"""))
"""Rename edgeport and sbp gs and ga id.
Revision ID: e36b3bd8a45c
Revises: e358efe9ab03
Create Date: 2024-12-04 15:08:30.512126
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'e36b3bd8a45c'
down_revision = 'e358efe9ab03'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE product_block_resource_types SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ga_id')) WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock')) AND product_block_resource_types.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_ga_id'))
"""))
conn.execute(sa.text("""
UPDATE product_block_resource_types SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id')) WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')) AND product_block_resource_types.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_sid'))
"""))
conn.execute(sa.text("""
UPDATE subscription_instance_values SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ga_id')) WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock'))) AND subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_ga_id'))
"""))
conn.execute(sa.text("""
UPDATE subscription_instance_values SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id')) WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort'))) AND subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_sid'))
"""))
def downgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE product_block_resource_types SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_ga_id')) WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock')) AND product_block_resource_types.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ga_id'))
"""))
conn.execute(sa.text("""
UPDATE product_block_resource_types SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_sid')) WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')) AND product_block_resource_types.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id'))
"""))
conn.execute(sa.text("""
UPDATE subscription_instance_values SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_ga_id')) WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock'))) AND subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ga_id'))
"""))
conn.execute(sa.text("""
UPDATE subscription_instance_values SET resource_type_id=(SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('geant_sid')) WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort'))) AND subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id'))
"""))
"""Add optical equipment attribute to Site.
Revision ID: fc7bd696014e
Revises: 79a76b22ca53
Create Date: 2024-12-04 10:15:40.529552
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'fc7bd696014e'
down_revision = '79a76b22ca53'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
INSERT INTO resource_types (resource_type, description) VALUES ('site_contains_optical_equipment', 'Whether a site contains optical equipment') RETURNING resource_types.resource_type_id
"""))
conn.execute(sa.text("""
INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('site_contains_optical_equipment')))
"""))
conn.execute(sa.text("""
WITH rt_id AS (SELECT resource_type_id FROM resource_types WHERE resource_type = 'site_contains_optical_equipment') INSERT INTO subscription_instance_values (subscription_instance_id, resource_type_id, value) SELECT subscription_instance_id, rt_id.resource_type_id, 'True' FROM rt_id, subscription_instances WHERE product_block_id = (SELECT product_block_id FROM product_blocks WHERE name = 'SiteBlock');
"""))
def downgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('site_contains_optical_equipment'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SiteBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('site_contains_optical_equipment'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('site_contains_optical_equipment'))
"""))
conn.execute(sa.text("""
DELETE FROM resource_types WHERE resource_types.resource_type IN ('site_contains_optical_equipment')
"""))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment