Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ services:
- MQ_PASS=${MQ_PASS:-guest}
# OXP settings.
- OXP_PROVISION_URL=${OXP_PROVISION_URL}
- OXP_PULL_URL=${OXP_PULL_URL}
- OXP_PULL_INTERVAL=${OXP_PULL_INTERVAL}
- OXP_TOPOLOGY_URL=${OXP_TOPOLOGY_URL}
- OXP_PULL_TOPOLOGY_INTERVAL=${OXP_PULL_TOPOLOGY_INTERVAL}
- OXP_CONNECTION_URL=${OXP_CONNECTION_URL}

volumes:
Expand Down
6 changes: 4 additions & 2 deletions env.template
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ MQ_PASS=guest

# Kytos/OESS API address
OXP_PROVISION_URL=http://192.168.201.205:8088/SDX-LC/1.0.0/provision
OXP_PULL_URL=http://192.168.201.205:8088/SDX-LC/1.0.0/topology
OXP_PULL_INTERVAL=180
OXP_TOPOLOGY_URL=http://192.168.201.205:8088/SDX-LC/1.0.0/topology
OXP_PULL_TOPOLOGY_INTERVAL=180
OXP_CONNECTION_URL=http://192.168.201.205:8088/SDX-LC/1.0.0/connection
OXP_LIST_CONNECTIONS_URL=http://192.168.201.205:8088/SDX-LC/1.0.0/connections

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to the L2VPN provisioning API, the same endpoint used for creating L2VPNs is used to list all L2VPNs. Thus, I believe we could have the same env var here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I was wrong, the endpoint for creating l2vpn is "1.0.0/connection", while the one list all connections is "1.0.0/connections"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was the earlier endpoint, but when we finished the spec for L2VPN provisioning API, we refactor the Kytos SDX Napp to support the new endpoints, which are both (for creating and listing) /l2vpn/1.0

see more: https://github.com/atlanticwave-sdx/sdx-end-to-end-tests/blob/e98cce8412ee820529187d0fbf478adbdcf63493/env/ampath-lc.env#L7

OXP_PULL_CONNECTIONS_INTERVAL=180
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependencies = [
"connexion[swagger-ui] == 2.14.2",
"asgiref >= 3.7.2",
"pymongo > 3.0",
"sdx-datamodel @ git+https://github.com/atlanticwave-sdx/datamodel@v3.2.1",
"sdx-datamodel @ git+https://github.com/atlanticwave-sdx/datamodel@v3.2.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Cong, can you please confirm this change? like using a older version?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow datamodel's most recent tag is 3.2.0 (https://github.com/atlanticwave-sdx/datamodel/tags). I'll check with Yufeng to make sure tags are consistent.

]

[project.optional-dependencies]
Expand Down
6 changes: 5 additions & 1 deletion sdx_lc/handlers/sdx_controller_msg_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ def process_sdx_controller_json_msg(self, msg):

if "link" in msg_json and ("endpoints" in msg_json["link"]):
service_id = msg_json.get("service_id")
if not service_id:
self.logger.info(f"Connection did not include service_id. Ignored.")
return

connection = msg_json.get("link")
self.db_instance.add_key_value_pair_to_db(self.message_id, connection)
self.db_instance.add_key_value_pair_to_db(service_id, connection)
self.logger.info("Save to database complete.")
self.logger.info("Message ID:" + str(self.message_id))
self.message_id += 1
Expand Down
112 changes: 112 additions & 0 deletions sdx_lc/jobs/pull_connection_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import json
import logging
import os.path
import sys
import time

import requests
from sdx_datamodel.constants import Constants, MessageQueueNames

# append abspath, so this file can import other modules from parent directory
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))
)

from messaging.rpc_queue_producer import RpcProducer
from utils.db_utils import DbUtils

SDXLC_DOMAIN = os.environ.get("SDXLC_DOMAIN")
OXP_LIST_CONNECTIONS_URL = os.environ.get("OXP_LIST_CONNECTIONS_URL")
OXP_PULL_CONNECTIONS_INTERVAL = os.environ.get("OXP_PULL_CONNECTIONS_INTERVAL")
PUB_QUEUE = MessageQueueNames.OXP_UPDATE
logger = logging.getLogger(__name__)


def main():
db_instance = DbUtils()
db_instance.initialize_db()
process_oxp_connections(db_instance)


# Periodically pull l2vpn (connection) status from OXP, and handle status change.
# Possible l2vpn status are:
# “up” if the L2VPN is operational,
# “down” if the L2VPN is not operational due to topology issues/lack of path, or endpoints being down,
# “error” when there is an error with the L2VPN,
# “under provisioning” when the L2VPN is still being provisioned by the OXPs,
# “maintenance” when the L2VPN is being affected by a network maintenance.
def process_oxp_connections(db_instance):
while True:
time.sleep(int(OXP_PULL_CONNECTIONS_INTERVAL))
try:
try:
response = requests.get(OXP_LIST_CONNECTIONS_URL, timeout=10)
connections = response.content
assert response.ok, response.text
except (requests.ConnectionError, requests.HTTPError) as err:
logger.error(f"Error connecting to OXP: {err}")
continue

logger.debug("Received connections from OXP.")

try:
connections_json = response.json()
except ValueError:
logger.debug("Cannot parse connections, invalid JSON.")
continue

if not connections_json:
logger.debug("No connections yet.")
continue

for service_id, connection in connections_json.items():
# Fetch existing connection from DB
existing_connection = db_instance.get_value_by_key(service_id)

if not existing_connection:
logger.debug(f"New connection {service_id}, ignored")
continue

try:
existing_connection_json = json.loads(existing_connection)
except ValueError:
logger.debug(f"Invalid JSON in DB for {service_id}")
continue

existing_connection_status = (
existing_connection_json.get("status")
if existing_connection_json
else None
)
new_status = connection.get("status")

if existing_connection_status == new_status:
logger.debug(f"Status unchanged for {service_id}")
continue

existing_connection_json["status"] = new_status
logger.info(
f"Status change for {service_id}: "
f"{existing_connection_status} changed to {new_status}"
)
db_instance.add_key_value_pair_to_db(
service_id, existing_connection_json
)
rpc_msg = {
"lc_domain": SDXLC_DOMAIN,
"msg_type": "oxp_conn_status_change",
"service_id": service_id,
"existing_status": existing_connection_status,
"new_status": new_status,
}
rpc_producer = RpcProducer(5, "", PUB_QUEUE)
rpc_producer.call(json.dumps(rpc_msg))
rpc_producer.stop()
except Exception:
logger.exception(
"Unexpected error while processing OXP connections; Retrying."
)


if __name__ == "__main__":
main()
Comment thread
congwang09 marked this conversation as resolved.
8 changes: 4 additions & 4 deletions sdx_lc/jobs/pull_topo_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

OXPO_USER = os.environ.get("OXPO_USER", None)
OXPO_PASS = os.environ.get("OXPO_PASS", None)
OXP_PULL_URL = os.environ.get("OXP_PULL_URL")
OXP_PULL_INTERVAL = os.environ.get("OXP_PULL_INTERVAL")
OXP_TOPOLOGY_URL = os.environ.get("OXP_TOPOLOGY_URL")
OXP_PULL_TOPOLOGY_INTERVAL = os.environ.get("OXP_PULL_TOPOLOGY_INTERVAL")
PUB_QUEUE = MessageQueueNames.OXP_UPDATE
logger = logging.getLogger(__name__)

Expand All @@ -32,7 +32,7 @@ def main():

def process_domain_controller_topo(db_instance):
while True:
time.sleep(int(OXP_PULL_INTERVAL))
time.sleep(int(OXP_PULL_TOPOLOGY_INTERVAL))
latest_topology_exists = False
latest_topology = db_instance.read_from_db(Constants.LATEST_TOPOLOGY)

Expand All @@ -55,7 +55,7 @@ def process_domain_controller_topo(db_instance):
logger.debug("Latest topology does not exist")

try:
response = requests.get(OXP_PULL_URL, auth=(OXPO_USER, OXPO_PASS))
response = requests.get(OXP_TOPOLOGY_URL, auth=(OXPO_USER, OXPO_PASS))
pulled_topology = response.content
except (requests.ConnectionError, requests.HTTPError):
logger.debug("Error connecting to domain controller...")
Expand Down
Loading