def common(self, fun: Callable[[entities.Model], entities.Model], pbBefore: str, pbAfter: str, keepPriority: bool = False): with open(pbBefore, "r") as f: before = f.read() before: entities.Model = text_format.Parse(before, entities.Model()) after: entities.Model = fun(before) with open(pbAfter, "r") as f: expected = f.read() expected: entities.Model = text_format.Parse( expected, entities.Model()) before = getTrimmedRoutes(before.routes, keepPriority) after = getTrimmedRoutes(after.routes, keepPriority) expected = getTrimmedRoutes(expected.routes, keepPriority) print("=======after=========") print(after) print("=======expected=========") print(expected) self.assertEqual(after, expected)
def deriveAfterBgpMedChanged(_model: entities.Model, vpnTunnel: Union[entities.VPNTunnel, str], MED_diff: int) -> entities.Model: """ Will change the priority of all influenced routes, and propagate """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(vpnTunnel, str): vpnTunnel = findVpnTunnel(model, vpnTunnel) peerTunnel = findPeeringVpnTunnel(model, vpnTunnel) if not peerTunnel: return model routes = list(filter(lambda r: r.next_hop_tunnel == peerTunnel.url and r.from_local, model.routes)) for route in routes: derivedRoutes = FindDerivedRoutes(model, route) print("===========Changing Priorities of Routes============") derivedRoutes.append(route) print(derivedRoutes) for r in derivedRoutes: r.priority += MED_diff return model
def deriveAfterVpnTunnelRemoval(_model: entities.Model, vpnTunnel: Union[entities.VPNTunnel, str]) -> entities.Model: """ Will remove all learned routes and custom prefixes on this side, and propagate the removal """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(vpnTunnel, str): vpnTunnel = findVpnTunnel(model, vpnTunnel) peerTunnel = findPeeringVpnTunnel(model, vpnTunnel) if not peerTunnel: return model routes = list(filter(lambda r: r.next_hop_tunnel in [vpnTunnel.url, peerTunnel.url] and r.from_local, model.routes)) for route in routes: derivedRoutes = FindDerivedRoutes(model, route) print("===========Deleting Routes============") derivedRoutes.append(route) print(derivedRoutes) for r in derivedRoutes: model.routes.remove(r) model.vpn_tunnels.remove(vpnTunnel) return model
def deriveAfterSubnetRemoved(_model: entities.Model, subnet: Union[entities.Subnet, str]) -> entities.Model: """ the pre-requisite is that no instance is under this subnet. remove subnet routes in this network and peers, and remove a region, if this subnet is the last in the region """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(subnet, str): subnet = findSubnet(model, subnet) network = findNetwork(model, subnet.network) prefixes = [subnet.ipv4_range] + list(subnet.secondary_ranges) model.subnets.remove(subnet) if not next((s for s in model.subnets if s.network == subnet.network and s.region == subnet.region), None): network.regions.remove(subnet.region) routes = [] for route in model.routes: if route.route_type == rules.Route.RouteType.SUBNET and route.dest_range in prefixes: routes.append(route) print("===========Root Routes============") print(routes) for route in routes: derivedRoutes = FindDerivedRoutes(model, route) print("===========Deleting Routes============") derivedRoutes.append(route) print(derivedRoutes) for r in derivedRoutes: model.routes.remove(r) return model
def deriveAfterBgpWithdrawals(_model: entities.Model, vpnTunnel: Union[entities.VPNTunnel, str], prefixes: List[rules.Ipv4Range]) -> entities.Model: """ When the other side withdrawals some prefixes, delete the derived routes on this side. The prefixes should be existing, and no prefix aggregation is done inside this function. """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(vpnTunnel, str): vpnTunnel = findVpnTunnel(model, vpnTunnel) routes = [] for route in model.routes: if route.next_hop_tunnel == vpnTunnel.url and route.dest_range in prefixes and route.from_local: routes.append(route) print("===========Root Routes============") print(routes) for route in routes: derivedRoutes = FindDerivedRoutes(model, route) print("===========Deleting Routes============") derivedRoutes.append(route) print(derivedRoutes) for r in derivedRoutes: model.routes.remove(r) return model
def deriveAfterLearnedBgpAdvertisements(_model: entities.Model, vpnTunnel: Union[entities.VPNTunnel, str], prefixes: List[rules.Ipv4Range]) -> entities.Model: """ Some prefixes are advertised from the other end of the vpnTunnel. Derive routes based on that. """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(vpnTunnel, str): vpnTunnel = findVpnTunnel(model, vpnTunnel) gw = findVpnGateway(model, vpnTunnel.vpn_gateway) context = DestinationContext(network=gw.network, region=vpnTunnel.region, peer_info=vpnTunnel.url) routes = [] for prefix in prefixes: # reuse the existing route construction functions to build a correct route route = rules.Route(dest_range=prefix, region=vpnTunnel.region) route = deriveRoute(model, route, context, Destination.BGP_PEERS) routes.append(route) model.routes.append(route) Derive(model, routes) return model
def test_integrity(self): with open("test_data/big_project/big_project.pb", "r") as f: model = f.read() model: entities.Model = text_format.Parse(model, entities.Model()) rootRoutes = derivationFunctions.IdentifyRootRoutes(model) derivedRootRoutes = derivationFunctions.FindRootRoute( model, rootRoutes) self.assertEqual(derivedRootRoutes, rootRoutes) derived = derivationFunctions.Derive(model, rootRoutes) for routes in derived: for route in routes: self.assertTrue(route not in rootRoutes) flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x] allRoutes = rootRoutes + flatten(derived) self.assertEqual(getTrimmedRoutes(allRoutes), getTrimmedRoutes(model.routes))
def deriveAfterStaticRouteRemoved(_model: entities.Model, route: rules.Route) -> entities.Model: """ Remove the static route and propagate to peers """ model: entities.Model = entities.Model() model.CopyFrom(_model) for r in FindDerivedRoutes(model, route): model.routes.remove(r) model.routes.remove(route) return model
def deriveAfterStaticRouteAdded(_model: entities.Model, route: rules.Route) -> entities.Model: """ Add the static route and propagate to peers """ model: entities.Model = entities.Model() model.CopyFrom(_model) model.routes.append(route) Derive(model, [route]) return model
def test_findRoot(self): with open("test_data/big_project/big_project.pb", "r") as f: model = f.read() model: entities.Model = text_format.Parse(model, entities.Model()) root = derivationFunctions.FindRootRoute( model, text_format.Parse( """ id: "3679872279986331686" name: "reachability-e2e-test::peering-route-1426b0141f4aa8cc" priority: 0 dest_range { ip: 167903488 mask: 24 } instance_filter { network: "projects/reachability-e2e-test/global/networks/peer1-route-test" } next_hop_peering: "reachability-e2e-test::peer1-route-test::peering1-peer-route-test" url: "projects/reachability-e2e-test/global/routes/peering-route-1426b0141f4aa8cc" route_type: PEERING_SUBNET creation_timestamp: "2019-11-11T14:20:57.515-08:00" description: "Auto generated route via peering [peering1-peer-route-test]." """, rules.Route())) self.assertEqual( root, text_format.Parse( """ id: "6980694499687424046" name: "reachability-e2e-test::default-route-b56e783807428e99" priority: 0 dest_range { ip: 167903488 mask: 24 } next_hop_network: "projects/reachability-e2e-test/global/networks/route-test" instance_filter { network: "projects/reachability-e2e-test/global/networks/route-test" } url: "projects/reachability-e2e-test/global/routes/default-route-b56e783807428e99" route_type: SUBNET creation_timestamp: "2019-11-11T14:20:49.544-08:00" description: "Default local route to the subnetwork 10.2.1.0/24." """, rules.Route()))
def deriveAfterIpRangesAdded(_model: entities.Model, subnet: Union[entities.Subnet, str], ipRanges: List[Union[rules.Ipv4Range, str]]) -> entities.Model: """ Adding some secondary ipRanges to a subnet requires the range update in the subnet, and installing subnet derivation_rules to the network and peers. """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(subnet, str): subnet = findSubnet(model, subnet) network = findNetwork(model, subnet.network) projectName = ParseProjectFromUrl(subnet.url) routes = [] for prefix in ipRanges: if isinstance(prefix, str): prefix = ipv4StrToRange(prefix) subnet.secondary_ranges.append(prefix) hexId = genHex(16) # reuse the existing route construction functions to build a correct route route = rules.Route( id=genId(), name=projectName + "::default-route-" + hexId, priority=0, dest_range=prefix, next_hop_network=network.url, instance_filter=rules.InstanceFilter(network=network.url), url="projects/%s/global/routes/default-route-%s" % (projectName, hexId), route_type=rules.Route.RouteType.SUBNET ) routes.append(route) model.routes.append(route) Derive(model, routes) return model
def deriveAfterIpRangeEnlarged(_model: entities.Model, subnet: Union[entities.Subnet, str], ipRanges: List[Tuple[Union[rules.Ipv4Range, str], Union[rules.Ipv4Range, str]]] ) -> entities.Model: """ If ranges of a subnet is enlarged, then the corresponding routes are changed accordingly """ model: entities.Model = entities.Model() model.CopyFrom(_model) if isinstance(subnet, str): subnet = findSubnet(model, subnet) for range, enlargedRange in ipRanges: if isinstance(range, str): range = ipv4StrToRange(range) if isinstance(enlargedRange, str): enlargedRange = ipv4StrToRange(enlargedRange) if subnet.ipv4_range == range: subnet.ipv4_range = enlargedRange else: # this branch is not possible in the web interface. But we support this subnet.secondary_ranges.remove(range) subnet.secondary_ranges.append(enlargedRange) for route in model.routes: if route.route_type == rules.Route.RouteType.SUBNET and route.dest_range == range: derivedRoutes = FindDerivedRoutes(model, route) print("===========Enlarging Routes============") derivedRoutes.append(route) print(derivedRoutes) for r in derivedRoutes: r.dest_range.ip = enlargedRange.ip r.dest_range.mask = enlargedRange.mask break return model
def deriveAfterSubnetAdded(_model: entities.Model, subnet: entities.Subnet) -> entities.Model: """ When a subnet is added, the associated subnet route is created and propagated """ model: entities.Model = entities.Model() model.CopyFrom(_model) network = findNetwork(model, subnet.network) projectName = ParseProjectFromUrl(subnet.url) model.subnets.append(subnet) if subnet.region not in network.regions: network.regions.append(subnet.region) routes = [] prefixes = [subnet.ipv4_range] + list(subnet.secondary_ranges) for prefix in prefixes: hexId = genHex(16) # reuse the existing route construction functions to build a correct route route = rules.Route( id=genId(), name=projectName + "::default-route-" + hexId, priority=0, dest_range=prefix, next_hop_network=network.url, instance_filter=rules.InstanceFilter(network=network.url), url="projects/%s/global/routes/default-route-%s" % (projectName, hexId), route_type=rules.Route.RouteType.SUBNET ) routes.append(route) model.routes.append(route) Derive(model, routes) return model
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from google.protobuf import text_format import proto.cloud_network_model_pb2 as entities import proto.rules_pb2 as rules if __name__ == '__main__': with open("combined_model_0922_clear.pb", "r") as f: str = f.read() topo: entities.Model = text_format.Parse(str, entities.Model()) copy = entities.Model() copy.CopyFrom(topo) subnet = entities.Subnet() subnet.id = "Deep copy" copy.subnets.append(subnet) subnet.id = "Shallow copy" print(copy.subnets[-1].id) copy.subnets[-1].id = 'Should show this' print(copy.subnets[-1].id) subnet = entities.Subnet()
def justDerive(_model: entities.Model) -> entities.Model: model: entities.Model = entities.Model() model.CopyFrom(_model) derivationFunctions.Derive(model) return model