From ffd0c7ec44c46f00af45d197c4deebf01c4493cf Mon Sep 17 00:00:00 2001 From: Costya Date: Wed, 11 Sep 2024 23:14:04 -0500 Subject: [PATCH] adding subnets and inbound ports part 1 --- .../cp/gcp/actions/firewall_policy_actions.py | 5 +- .../cp/gcp/flows/deploy_instance/base_flow.py | 47 +++++++---- cloudshell/cp/gcp/handlers/vpc.py | 2 +- .../cp/gcp/helpers/network_tag_helper.py | 77 +++++++++++++++++++ cloudshell/cp/gcp/models/deploy_app.py | 18 ++++- 5 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 cloudshell/cp/gcp/helpers/network_tag_helper.py diff --git a/cloudshell/cp/gcp/actions/firewall_policy_actions.py b/cloudshell/cp/gcp/actions/firewall_policy_actions.py index 4d18250..ada7357 100644 --- a/cloudshell/cp/gcp/actions/firewall_policy_actions.py +++ b/cloudshell/cp/gcp/actions/firewall_policy_actions.py @@ -17,10 +17,7 @@ class FirewallPolicyActions: NSG_DENY_PRV_RULE_NAME_TPL = "deny_internet_traffic_to_priv_subnet_{subnet_cidr}" VM_NSG_NAME_TPL = "rule_{vm_name}" INBOUND_RULE_DIRECTION = "inbound" - CUSTOM_NSG_RULE_NAME_TPL = ( - "rule-{vm_name}-{dst_address}-" - "{dst_port_range}-{protocol}" - ) + NSG_ADD_MGMT_RULE_PRIORITY = 4000 NSG_ADD_MGMT_RULE_NAME_TPL = "allow-{mgmt_network}-to-{sandbox_cidr}" NSG_DENY_OTHER_SB_RULE_PRIORITY = 4090 diff --git a/cloudshell/cp/gcp/flows/deploy_instance/base_flow.py b/cloudshell/cp/gcp/flows/deploy_instance/base_flow.py index e84f4cf..fe48965 100644 --- a/cloudshell/cp/gcp/flows/deploy_instance/base_flow.py +++ b/cloudshell/cp/gcp/flows/deploy_instance/base_flow.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json import logging from abc import abstractmethod from typing import TYPE_CHECKING @@ -42,33 +43,35 @@ def __attrs_post_init__(self): def _prepare_vm_details_data( self, - deployed_vm_id: int, - deploy_app: BaseGCPDeployApp + deployed_vm: InstanceHandler, ) -> VmDetailsData: """Prepare CloudShell VM Details model.""" vm_details_actions = VMDetailsActions( - self.proxmox_api, - self._resource_config, - self._cancellation_manager, + config=self._resource_config, + logger=self._logger, ) - return vm_details_actions.create(deployed_vm_id, deploy_app) + return vm_details_actions.prepare_vm_details(deployed_vm) def _prepare_deploy_app_result( self, deploy_app: BaseGCPDeployApp, - instance_name: str, + instance_handler: InstanceHandler, ) -> DeployAppResult: vm_details_data = self._prepare_vm_details_data( - deployed_vm_id=deployed_vm_id, - deploy_app=deploy_app, + deployed_vm=instance_handler, ) logger.info(f"Prepared VM details: {vm_details_data}") return DeployAppResult( actionId=deploy_app.actionId, - vmUuid=str(deployed_vm_id), - vmName=instance_name, + vmUuid=json.dumps( + { + "instance_name": instance_handler.instance.name, + "zone": instance_handler.instance.zone + } + ), + vmName=instance_handler.instance.name, vmDetailsData=vm_details_data, deployedAppAdditionalData={ "ip_regex": deploy_app.ip_regex, @@ -76,7 +79,7 @@ def _prepare_deploy_app_result( "auto_power_off": deploy_app.auto_power_off, "auto_delete": deploy_app.auto_delete, }, - deployedAppAttributes=self._prepare_app_attrs(deploy_app, deployed_vm_id), + # deployedAppAttributes=self._prepare_app_attrs(deploy_app, deployed_vm_id), ) @abstractmethod @@ -88,11 +91,11 @@ def _create_instance(self, deploy_app: BaseGCPDeployApp, subnet_list: list[str]) def _deploy(self, request_actions: DeployVMRequestActions) -> DeployAppResult: """Deploy Proxmox Instance.""" # noinspection PyTypeChecker - + network_handler = self._get_network() deploy_app: BaseGCPDeployApp = request_actions.deploy_app subnet_list = [x.subnet_id for x in request_actions.connect_subnets] if not subnet_list: - subnet_list = [] + subnet_list = network_handler.get_subnets() with self.cancellation_manager: instance = self._create_instance( @@ -102,6 +105,8 @@ def _deploy(self, request_actions: DeployVMRequestActions) -> DeployAppResult: with self._rollback_manager: logger.info(f"Creating Instance {instance.name}") + # ToDo DeployInstanceCommand.execute() should return InstanceHandler + # ToDo additionally we need to pass deploy_app.network_tags.keys() deployed_instance = DeployInstanceCommand( instance=instance, credentials=self.resource_config.credentials, @@ -109,10 +114,17 @@ def _deploy(self, request_actions: DeployVMRequestActions) -> DeployAppResult: cancellation_manager=self.cancellation_manager, ).execute() + logger.info(f"Instance {deployed_instance.name} created") + + # ToDo I will add network tags over here. + # firewall_actions = FirewallActions(config=self.config, logger=self.logger) + # for tag_name, inbound_port in deploy_app.network_tags.items(): + # firewall_actions.create_inbound_port_rule(...) + logger.info(f"Preparing Deploy App result for the {deployed_instance.name}") return self._prepare_deploy_app_result( deploy_app=deploy_app, - instance_name=deployed_instance.name, + instance_handler=deployed_instance, ) def _get_network(self) -> VPCHandler: @@ -120,5 +132,6 @@ def _get_network(self) -> VPCHandler: Get Network. """ - network_handler = VPCHandler.get_vpc_by_sandbox_id(self.resource_config.reservation_info.reservation_id) - return network_handler.get_vpc_by_name(network_name) + return VPCHandler.get_vpc_by_sandbox_id( + self.resource_config.reservation_info.reservation_id + ) diff --git a/cloudshell/cp/gcp/handlers/vpc.py b/cloudshell/cp/gcp/handlers/vpc.py index 20c524e..8c8d3df 100644 --- a/cloudshell/cp/gcp/handlers/vpc.py +++ b/cloudshell/cp/gcp/handlers/vpc.py @@ -94,4 +94,4 @@ def delete(self) -> None: logger.info(f"VPC network '{self.network.name}' deleted successfully.") def get_subnets(self): - return self.network.subnetworks + return list(self.network.subnetworks) diff --git a/cloudshell/cp/gcp/helpers/network_tag_helper.py b/cloudshell/cp/gcp/helpers/network_tag_helper.py new file mode 100644 index 0000000..a0965b7 --- /dev/null +++ b/cloudshell/cp/gcp/helpers/network_tag_helper.py @@ -0,0 +1,77 @@ +import re + +from attr import define + +from cloudshell.cp.gcp.models.deploy_app import BaseGCPDeployApp + +DEFAULT_DESTINATION = "0.0.0.0/0" +DEFAULT_PROTOCOL = "tcp" + + +@define +class InboundPort: + port_range: str + src_address: str = DEFAULT_DESTINATION + protocol: str = DEFAULT_PROTOCOL + + +@define +class NetworkTagHelper: + PORT_DATA_MATCH = re.compile( + r"^(?P\d+)" + r"(-(?P\d+))?" + r"(:(?P(udp|tcp|icmp)))?" + r"(:(?P\S+))?$", + re.IGNORECASE, + ) + ICMP_PORT_DATA_MATCH = re.compile( + r"^(?Picmp)" r"(:(?P\S+))?$", + re.IGNORECASE, + ) + + + CUSTOM_NSG_RULE_NAME_TPL = ( + "rule-{vm_name}-{dst_address}-" + "{dst_port_range}-{protocol}" + ) + deploy_app: BaseGCPDeployApp + + def get_network_tags(self, app_name) -> dict[str,InboundPort]: + """Get network tags for VM. + + :param app_name: VM name + :return: Network tags + """ + # ToDo adjust this one for work in deploy app parser. + network_tags = {} + for network_tag in self.deploy_app.inbound_ports: + rule = self._parse_port_range(network_tag) + name = self.CUSTOM_NSG_RULE_NAME_TPL.format( + vm_name=app_name, + dst_address=rule.dst_address, + dst_port_range=rule.dst_port_range, + protocol=rule.protocol, + ) + network_tags[name] = self._parse_port_range(network_tag) + + return network_tags + + def _parse_port_range(self, port_data): + match = self.PORT_DATA_MATCH.search(port_data) + if match: + from_port = match.group("from_port") + to_port = match.group("to_port") + else: + # match = self.ICMP_PORT_DATA_MATCH.search(port_data) + # if match: + # from_port = to_port = "-1" + # else: + msg = f"The value '{port_data}' is not a valid ports rule" + raise ValueError(msg) + + destination = match.group("destination") or self.DEFAULT_DESTINATION + protocol = match.group("protocol") or self.DEFAULT_PROTOCOL + port = f"{from_port}" + if to_port: + port = f"{from_port}-{to_port}" + return InboundPort(port, protocol, destination) \ No newline at end of file diff --git a/cloudshell/cp/gcp/models/deploy_app.py b/cloudshell/cp/gcp/models/deploy_app.py index 398e494..99a223e 100644 --- a/cloudshell/cp/gcp/models/deploy_app.py +++ b/cloudshell/cp/gcp/models/deploy_app.py @@ -4,6 +4,7 @@ from cloudshell.cp.core.request_actions.models import DeployApp from cloudshell.cp.gcp.helpers import constants +from cloudshell.cp.gcp.helpers.network_tag_helper import NetworkTagHelper from cloudshell.cp.gcp.models.attributes import ( ResourceAttrRODeploymentPath, ResourceBoolAttrRODeploymentPath, @@ -14,6 +15,21 @@ GCPFromVMImageDeploymentAppAttributeNames, ) +CUSTOM_NSG_RULE_NAME_TPL = ( + "rule-{vm_name}-{dst_address}-" + "{dst_port_range}-{protocol}" +) + + +class InboundPortsAttrRO(ResourceAttrRODeploymentPath): + def __get__(self, instance, owner): + if instance is None: + return self + helper = NetworkTagHelper() + + attr = instance.attributes.get(self.get_key(instance), self.default) + return [port_data.strip() for port_data in attr.split(";") if port_data] + class BaseGCPDeployApp(DeployApp): _DO_NOT_EDIT_APP_NAME = True @@ -25,7 +41,7 @@ class BaseGCPDeployApp(DeployApp): maintenance = ResourceAttrRODeploymentPath(ATTR_NAMES.maintenance) auto_restart = ResourceBoolAttrRODeploymentPath(ATTR_NAMES.auto_restart) ip_forwarding = ResourceBoolAttrRODeploymentPath(ATTR_NAMES.ip_forwarding) - inbound_ports = ResourceAttrRODeploymentPath(ATTR_NAMES.inbound_ports) + inbound_ports = InboundPortsAttrRO(ATTR_NAMES.inbound_ports) custom_tags = ResourceAttrRODeploymentPath(ATTR_NAMES.custom_tags) wait_for_ip = ResourceBoolAttrRODeploymentPath(ATTR_NAMES.wait_for_ip) add_public_ip = ResourceBoolAttrRODeploymentPath(ATTR_NAMES.add_public_ip)