diff --git a/cicd/test_configs/fabric_native_aws/config.fab b/cicd/test_configs/fabric_native_aws/config.fab index 50b3d04f..950e5807 100644 --- a/cicd/test_configs/fabric_native_aws/config.fab +++ b/cicd/test_configs/fabric_native_aws/config.fab @@ -59,5 +59,6 @@ resource: device_name: agg3.ashb local_name: TenGigE0/0/0/11/3 site: AWS + labels: ['fabric_network@network'] producer: fabric consumer: aws diff --git a/examples/demos/cloudlab/clem/config.fab b/examples/demos/cloudlab/clem/config.fab index 3a479cbe..d3ba343b 100644 --- a/examples/demos/cloudlab/clem/config.fab +++ b/examples/demos/cloudlab/clem/config.fab @@ -1,6 +1,6 @@ variable: - vlan: - default: 3100 + default: 3200 provider: - cloudlab: @@ -11,6 +11,10 @@ provider: - fabric_provider: credential_file: ~/.fabfed/fabfed_credentials.yml profile: fabric + - janus: + - janus_provider: + credential_file: ~/.fabfed/fabfed_credentials.yml + profile: janus config: - layer3: @@ -24,8 +28,15 @@ resource: - cnet: provider: '{{cloudlab.cloudlab_provider }}' layer3: "{{ layer3.my_layer }}" + profile: clem-profile interface: - vlan: '{{ var.vlan }}' + - fabric_network: + provider: '{{ fabric.fabric_provider }}' + layer3: "{{ layer3.my_layer }}" + stitch_with: '{{ network.cnet }}' + stitch_option: + device_name: CloudLab-Clemson - node: - cloudlab_node: provider: '{{ cloudlab.cloudlab_provider }}' @@ -38,11 +49,11 @@ resource: nic_model: NIC_Basic network: "{{ network.fabric_network }}" count: 1 - - network: - - fabric_network: - provider: '{{ fabric.fabric_provider }}' - layer3: "{{ layer3.my_layer }}" - stitch_with: '{{ network.cnet }}' - stitch_option: - device_name: CloudLab-Clemson - count: 1 + - service: + - dtn_service: + provider: '{{ janus.janus_provider }}' + node: [ '{{ node.cloudlab_node }}', '{{ node.fabric_node }}' ] + controller: '{{ node.fabric_node }}' + image: dtnaas/tools + profile: fabfed + count: 0 diff --git a/examples/demos/native-gcp-aws/config.fab b/examples/demos/native-gcp-aws/config.fab new file mode 100644 index 00000000..1b23815e --- /dev/null +++ b/examples/demos/native-gcp-aws/config.fab @@ -0,0 +1,68 @@ +provider: + - gcp: + - gcp_provider: + - credential_file: ~/.fabfed/fabfed_credentials.yml + profile: gcp + - aws: + - aws_provider: + - credential_file: ~/.fabfed/fabfed_credentials.yml + profile: aws + - fabric: + - fabric_provider: + credential_file: ~/.fabfed/fabfed_credentials.yml + profile: fabric +config: + - layer3: + - gcp_layer: + subnet: 10.200.1.0/24 # subnet.cidr and vpc.cidr + - aws_layer: + subnet: 10.0.1.0/24 # 10.200.1.0/24 # subnet.cidr and vpc.cidr + - peering: + - gcp_peering: + # FOR GCP + cloud_region: "us-east4" + cloud_vpc: "vpc-69acc1d9-8c24-47cd-90b8-33be57167dbf" + # cloud_vlan: + + # FOR GCP AND FABRIC. + # local_asn: 55038 # customer_asn + remote_asn: 16550 # google_asn + + # FOR FABRIC + local_address: "192.168.1.1/30" # customer_ip + remote_address: "192.168.1.2/30" # google_ip + - aws_peering: + cloud_account: "296256999979" + cloud_vpc: "vpc-0936b973cf039f794" + cloud_region: "us-east-1" + # cloud_vlan: + + remote_asn: 64512 # amazon_asn + local_asn: 55038 # customer_asn + local_address: "192.168.1.1/30" # customer_ip + remote_address: "192.168.1.2/30" # amazon_ip + +resource: + - network: + - gcp_net: + provider: '{{ gcp.gcp_provider }}' + name: gcp-net + layer3: "{{ layer3.gcp_layer }}" + peering: "{{ peering.gcp_peering }}" + + - fabric_network: + provider: '{{ fabric.fabric_provider }}' + peering: [ "{{ peering.gcp_peering }}", "{{ peering.aws_peering }}" ] + count: 1 + stitch_with: + - network: '{{ network.gcp_net }}' + stitch_option: + group_name: GCP + - network: '{{ network.aws_net }}' + stitch_option: + device_name: agg3.ashb + + - aws_net: + provider: '{{ aws.aws_provider }}' + layer3: "{{ layer3.aws_layer }}" + peering: "{{ peering.aws_peering }}" diff --git a/fabfed/controller/controller.py b/fabfed/controller/controller.py index 1e438895..f1329b51 100644 --- a/fabfed/controller/controller.py +++ b/fabfed/controller/controller.py @@ -127,6 +127,23 @@ def init(self, *, session: str, provider_factory: ProviderFactory, provider_stat for networks_with_same_layer3 in layer3_to_network_mapping.values(): partition_layer3_config(networks=networks_with_same_layer3) + # Handle peering labels. The labels are inserted here to match it to a stitch port + for network in networks: + peering_list = network.attributes.get(Constants.RES_PEERING) + + if not peering_list: + continue + + if not isinstance(peering_list, list): + peering_list = [peering_list] + + for peering in peering_list: + if Constants.LABELS not in peering.attributes: + peering.attributes[Constants.LABELS] = [] + + peering.attributes[Constants.LABELS].append(network.label) + peering.attributes[Constants.LABELS] = sorted(peering.attributes["labels"]) + peering_to_network_mapping = {} for network in networks: @@ -134,9 +151,16 @@ def init(self, *, session: str, provider_factory: ProviderFactory, provider_stat continue network.attributes[Constants.RES_PEER_LAYER3] = [] - peering = network.attributes.get(Constants.RES_PEERING) - if peering: + peering_list = network.attributes.get(Constants.RES_PEERING) + + if not peering_list: + continue + + if not isinstance(peering_list, list): + peering_list = [peering_list] + + for peering in peering_list: if peering.label in peering_to_network_mapping: peering_to_network_mapping.get(peering.label).append(network) else: @@ -150,7 +174,6 @@ def init(self, *, session: str, provider_factory: ProviderFactory, provider_stat for network in [net for net in networks if net.attributes.get(Constants.RES_STITCH_INFO)]: stitch_info = network.attributes.get(Constants.RES_STITCH_INFO) - self.logger.info(f"{network}: stitch_info={stitch_info}") def plan(self, provider_states: List[ProviderState]): @@ -427,7 +450,7 @@ def destroy(self, *, provider_states: List[ProviderState]): try: provider.delete_resource(resource=resource.attributes) except Exception as e: - self.logger.warning(f"Exception occurred while deleting resource: {e} using {provider_label}") + self.logger.warning(f"Exception occurred while deleting resource: {e} using {provider_label}", exc_info=True) remaining_resources.append(resource) skip_resources.update([external_state.label for external_state in external_states]) exceptions.append(e) diff --git a/fabfed/policy/policy_helper.py b/fabfed/policy/policy_helper.py index 7770a5fa..c347db39 100644 --- a/fabfed/policy/policy_helper.py +++ b/fabfed/policy/policy_helper.py @@ -543,8 +543,9 @@ def handle_stitch_info(config, policy, resources): stitch_info.stitch_port['peer'] = dict() stitch_info.stitch_port['peer'].update(peer1) + # Handle stitch port labels. The labels are inserted here to match it to a peering config + stitch_info.stitch_port[Constants.LABELS] = sorted([network.label, other_network.label]) network.attributes[Constants.RES_STITCH_INFO].append(stitch_info) - stitch_info = StitchInfo(stitch_port=dict(), producer=stitch_info.producer, consumer=stitch_info.consumer) @@ -556,6 +557,8 @@ def handle_stitch_info(config, policy, resources): stitch_info.stitch_port.update(peer2) stitch_info.stitch_port['peer'] = dict() stitch_info.stitch_port['peer'].update(peer1) + # Handle stitch port labels. The labels are inserted here to match it to a peering config + stitch_info.stitch_port[Constants.LABELS] = sorted([network.label, other_network.label]) other_network.attributes[Constants.RES_STITCH_INFO].append(stitch_info) for network in [resource for resource in resources if resource.is_network]: diff --git a/fabfed/provider/api/provider.py b/fabfed/provider/api/provider.py index 5eebe4ec..b06fb9dd 100644 --- a/fabfed/provider/api/provider.py +++ b/fabfed/provider/api/provider.py @@ -329,7 +329,8 @@ def create_resource(self, *, resource: dict): self.add_resource(resource=internal_dependency) except Exception as e2: self.logger.warning( - f"Adding no longer pending internally {internal_dependency_label} failed using {e2}") + f"Adding no longer pending internally {internal_dependency_label} failed using {e2}", + exc_info=True) if label in self._added: self.logger.info(f"Create: {label} using {self.label}: {self._added}") diff --git a/fabfed/provider/fabric/fabric_network.py b/fabfed/provider/fabric/fabric_network.py index 2e263bb2..5ba28bdb 100644 --- a/fabfed/provider/fabric/fabric_network.py +++ b/fabfed/provider/fabric/fabric_network.py @@ -31,9 +31,17 @@ def __init__(self, *, label, delegate: NetworkService, layer3: Config, peering: self.interface = [] # TODO This is only needed for sense-aws and aws - if self.peering and Constants.RES_CLOUD_ACCOUNT in self.peering.attributes: - account_id = self.peering.attributes[Constants.RES_CLOUD_ACCOUNT] - key = self.slice_name + "-" + account_id + if self.peering and isinstance(self.peering, list): + for peering in self.peering: + if Constants.RES_CLOUD_ACCOUNT in peering.attributes: + # account_id = peering.attributes[Constants.RES_CLOUD_ACCOUNT] + # key = self.slice_name + "-" + account_id + key = self.slice_name + self.interface.append(dict(id=key, provider="fabric", password='0xzsEwC7xk6c1fK_h.xHyAdx')) + elif self.peering and Constants.RES_CLOUD_ACCOUNT in self.peering.attributes: + # account_id = self.peering.attributes[Constants.RES_CLOUD_ACCOUNT] + # key = self.slice_name + "-" + account_id + key = self.slice_name self.interface.append(dict(id=key, provider="fabric", password='0xzsEwC7xk6c1fK_h.xHyAdx')) for key, iface in ns.interfaces.items(): @@ -156,89 +164,119 @@ def __init__(self, label, provider: FabricProvider, slice_object: Slice, name, r logger.info( f'{self.net_name}:vlan={self.vlan},stitch_port={self.stitch_port},device={self.device},site={self.site}') + def _get_stitch_info_index(self, peering, stitch_ports): + for idx, stitch_port in enumerate(stitch_ports): + if peering.attributes[Constants.LABELS] == stitch_port[Constants.LABELS]: + return idx + + return -1 + def handle_facility_port(self, *, sites): from fim.slivers.capacities_labels import Labels, Capacities self.sites.update(sites) if self.peering: - cloud = self.peering.attributes.get(Constants.RES_CLOUD_FACILITY) - asn = self.peering.attributes.get(Constants.RES_REMOTE_ASN) - account_id = self.peering.attributes.get(Constants.RES_CLOUD_ACCOUNT) + if not isinstance(self.peering, list): + self.peering = [self.peering] - if account_id is None: - account_id = self.discovered_stitch_infos[0].get("id") # GCP + if not isinstance(self.stitch_port, list): + self.stitch_port = [self.stitch_port] - subnet = self.peering.attributes.get(Constants.RES_LOCAL_ADDRESS) - peer_subnet = self.peering.attributes.get(Constants.RES_REMOTE_ADDRESS) - region = self.peering.attributes.get(Constants.RES_CLOUD_REGION) - device = self.peering.attributes.get(Constants.RES_LOCAL_DEVICE) - port = self.peering.attributes.get(Constants.RES_LOCAL_PORT) - vlan = self.peering.attributes.get('cloud_vlan') - bw = self.peering.attributes.get('cloud_bw', 50) + for peering in self.peering: + idx = self._get_stitch_info_index(peering, self.stitch_port) + cloud = peering.attributes.get(Constants.RES_CLOUD_FACILITY) - if not device: - device = self.stitch_port.get(Constants.STITCH_PORT_DEVICE_NAME) + if not cloud: + cloud = self.stitch_port[idx].get(Constants.STITCH_PORT_SITE) + peering.attributes[Constants.RES_CLOUD_FACILITY] = cloud - if not port: - port = self.stitch_port.get(Constants.STITCH_PORT_LOCAL_NAME) + # TODO This is no needed at all with 1.7.3 and did not help ith 1.6 + if self.peering[0].attributes[Constants.RES_CLOUD_FACILITY] == "GCP": + self.peering.reverse() - if not region: - region = self.stitch_port.get(Constants.STITCH_PORT_REGION) + for peering in self.peering: + idx = self._get_stitch_info_index(peering, self.stitch_port) + cloud = peering.attributes.get(Constants.RES_CLOUD_FACILITY) + asn = peering.attributes.get(Constants.RES_REMOTE_ASN) + account_id = peering.attributes.get(Constants.RES_CLOUD_ACCOUNT) - if not cloud: - cloud = self.stitch_port.get(Constants.STITCH_PORT_SITE) - self.peering.attributes[Constants.RES_CLOUD_FACILITY] = cloud # TODO WORKAROUND FOR NOW + if account_id is None: + account_id = self.discovered_stitch_infos[0].get("id") # GCP - if not vlan: - vlan = self.stitch_port.get('vlan') + subnet = peering.attributes.get(Constants.RES_LOCAL_ADDRESS) + peer_subnet = peering.attributes.get(Constants.RES_REMOTE_ADDRESS) + region = peering.attributes.get(Constants.RES_CLOUD_REGION) + device = peering.attributes.get(Constants.RES_LOCAL_DEVICE) + port = peering.attributes.get(Constants.RES_LOCAL_PORT) + vlan = peering.attributes.get('cloud_vlan') + bw = peering.attributes.get('cloud_bw', 50) - labels = Labels(ipv4_subnet=subnet) + if not device: + device = self.stitch_port[idx].get(Constants.STITCH_PORT_DEVICE_NAME) - if vlan: - labels = Labels.update(labels, vlan=str(vlan)) + if not port: + port = self.stitch_port[idx].get(Constants.STITCH_PORT_LOCAL_NAME) - # if region: - # labels = Labels.update(labels, region=region) + if not region: + region = self.stitch_port[idx].get(Constants.STITCH_PORT_REGION) - if device: - labels = Labels.update(labels, device_name=device) - if port: - labels = Labels.update(labels, local_name=port) + if not cloud: + cloud = self.stitch_port[idx].get(Constants.STITCH_PORT_SITE) + # peering.attributes[Constants.RES_CLOUD_FACILITY] = cloud # TODO WORKAROUND FOR NOW - # TODO: al2s remote_name depends on the cloud provider - if cloud == "GCP": - peer_labels = Labels(ipv4_subnet=peer_subnet, - asn=str(asn), - bgp_key='0xzsEwC7xk6c1fK_h.xHyAdx', - account_id=account_id, - local_name='Google Cloud Platform') - else: - peer_labels = Labels(ipv4_subnet=peer_subnet, - asn=str(asn), - bgp_key='0xzsEwC7xk6c1fK_h.xHyAdx', - account_id=account_id, - region=region, - local_name=cloud) - - logger.info(f"Creating Facility Port:Labels: {labels}") - logger.info(f"Creating Facility Port:PeerLabels: {peer_labels}") - - if cloud == "GCP": - mtu_size = 1460 - else: - mtu_size = 9001 - - facility_port = self.slice_object.add_facility_port( - name='Cloud-Facility-' + cloud, - site=cloud, - labels=labels, - peer_labels=peer_labels, - capacities=Capacities(bw=int(bw), mtu=mtu_size)) + if not vlan: + vlan = self.stitch_port[idx].get('vlan') + + labels = Labels(ipv4_subnet=subnet) + + if vlan: + labels = Labels.update(labels, vlan=str(vlan)) + + # if region: + # labels = Labels.update(labels, region=region) + + if device: + labels = Labels.update(labels, device_name=device) + if port: + labels = Labels.update(labels, local_name=port) + + # TODO: al2s remote_name depends on the cloud provider + if cloud == "GCP": + peer_labels = Labels(ipv4_subnet=peer_subnet, + asn=str(asn), + bgp_key='0xzsEwC7xk6c1fK_h.xHyAdx', + account_id=account_id, + local_name='Google Cloud Platform') + else: + peer_labels = Labels(ipv4_subnet=peer_subnet, + asn=str(asn), + bgp_key='0xzsEwC7xk6c1fK_h.xHyAdx', + account_id=account_id, + region=region, + local_name=cloud) + + logger.info(f"*******************************:BEGIN:" + cloud) + logger.info(f"Creating Facility Port:Labels: {labels}") + logger.info(f"Creating Facility Port:PeerLabels: {peer_labels}") + + if cloud == "GCP": + mtu_size = 1460 + else: + mtu_size = 9001 + + facility_port = self.slice_object.add_facility_port( + name='Cloud-Facility-' + cloud, + site=cloud, + labels=labels, + peer_labels=peer_labels, + capacities=Capacities(bw=int(bw), mtu=mtu_size)) + + facility_port_interface = facility_port.get_interfaces()[0] + self.facility_port_interfaces.append(facility_port_interface) + logger.info("CreatedFacilityPort:" + facility_port.toJson()) + logger.info(f"*******************************:END:" + cloud) - facility_port_interface = facility_port.get_interfaces()[0] - self.facility_port_interfaces.append(facility_port_interface) - logger.info("CreatedFacilityPort:" + facility_port.toJson()) elif self.discovered_stitch_infos: for discovered_stitch_info in self.discovered_stitch_infos: logger.info( @@ -288,15 +326,17 @@ def handle_network(self, nodes): net_type = 'L2STS' else: net_type = 'L2Bridge' + logger.info( f"Adding Network:{self.net_name} to slice:ifaces={[i.get_name() for i in interfaces]}:type={net_type}") if net_type == "L3VPN": - self.net: NetworkService = self.slice_object.add_l3network(name=self.net_name, - interfaces=interfaces) + self.net: NetworkService = self.slice_object.add_l3network(name=self.net_name) else: - self.net: NetworkService = self.slice_object.add_l2network(name=self.net_name, - interfaces=interfaces) + self.net: NetworkService = self.slice_object.add_l2network(name=self.net_name) + + for itf in interfaces: + self.net.add_interface(itf) return tech = 'AL2S' diff --git a/fabfed/provider/fabric/fabric_slice.py b/fabfed/provider/fabric/fabric_slice.py index 17a715ed..1685c3bd 100644 --- a/fabfed/provider/fabric/fabric_slice.py +++ b/fabfed/provider/fabric/fabric_slice.py @@ -524,6 +524,7 @@ def create_resource(self, *, resource: dict): return self.logger.info(f"Submitting request for slice {self.name}") + # self.slice_object.validate() slice_id = self.slice_object.submit(wait=False) self.logger.info(f"Done Submitting request for slice {self.name}:{slice_id}") self.submitted = True diff --git a/fabfed/provider/gcp/gcp_utils.py b/fabfed/provider/gcp/gcp_utils.py index 728a05fc..11fe2dab 100644 --- a/fabfed/provider/gcp/gcp_utils.py +++ b/fabfed/provider/gcp/gcp_utils.py @@ -19,7 +19,7 @@ logger = get_logger() -GCP_REQUEST_RETRY_MAX = 3 +GCP_REQUEST_RETRY_MAX = 10 def find_vpc(*, service_key_path, project, vpc): credentials = service_account.Credentials.from_service_account_file(service_key_path) diff --git a/fabfed/util/constants.py b/fabfed/util/constants.py index 436c28ba..be19f356 100644 --- a/fabfed/util/constants.py +++ b/fabfed/util/constants.py @@ -111,3 +111,4 @@ class Constants: RUN_SSH_TESTER = True COPY_TOKENS = False PROVIDER_STATE = 'provider_state' + LABELS = "labels"