|
| 1 | +""" |
| 2 | +This module needs to be able to do a few things using gRPC Openconfig modules. |
| 3 | +1. Get information on policy and bgp neighbors. |
| 4 | +2. Apple to apply single or multiple policies to an interfaces. |
| 5 | +3. Get a policy from box a and apply it to box b. |
| 6 | +4. It needs to use templating to create new policies and apply to upload them in the end. |
| 7 | +""" |
| 8 | +import json |
| 9 | +from collections import OrderedDict |
| 10 | +import sys |
| 11 | +sys.path.insert(0, '../') |
| 12 | +from lib.cisco_grpc_client import CiscoGRPCClient |
| 13 | + |
| 14 | + |
| 15 | +class RoutePolicy(object): |
| 16 | + """Class to manipulate route policy and bgp neighbors using openconfig |
| 17 | + """ |
| 18 | + def __init__(self, host, port, username, password): |
| 19 | + """This class creates a grpc client for the functions to use. |
| 20 | + :param host: The ip address for the device. |
| 21 | + :param port: The port for the device. |
| 22 | + :param user: Username for device login. |
| 23 | + :param password: Password for device login. |
| 24 | + :type host: str |
| 25 | + :type port: int |
| 26 | + :type password: str |
| 27 | + :type username: str |
| 28 | + """ |
| 29 | + self.client = CiscoGRPCClient(host, port, 10, username, password) |
| 30 | + |
| 31 | + def get_policies(self): |
| 32 | + """sends a gRPC request and returns openconfig json of policies |
| 33 | + :return: Json string of policies |
| 34 | + :rtype: json object |
| 35 | + """ |
| 36 | + path = '{"openconfig-routing-policy:routing-policy": [null]}' |
| 37 | + result = self.client.getconfig(path) |
| 38 | + policies = json.loads(result, object_pairs_hook=OrderedDict) |
| 39 | + return policies |
| 40 | + |
| 41 | + def list_policies(self): |
| 42 | + """Prints a list policies names for a router |
| 43 | + """ |
| 44 | + bgp_policies = self.get_policies() |
| 45 | + policy_definitions = bgp_policies['openconfig-routing-policy:routing-policy']['policy-definitions']['policy-definition'] |
| 46 | + print '\nPolicy Names:\n' |
| 47 | + for policy in policy_definitions: |
| 48 | + print 'Name: %s' % policy['name'] |
| 49 | + |
| 50 | + def detail_policy(self, policy_name): |
| 51 | + """Prints the full json of a policy in Openconfig and returns it |
| 52 | + :param policy_name: Policy Name on the box (case sensative). |
| 53 | + :type policy_name: str |
| 54 | + :return: Json string of policy |
| 55 | + :rtype: str |
| 56 | + """ |
| 57 | + bgp_policies = self.get_policies() |
| 58 | + policy_definitions = bgp_policies['openconfig-routing-policy:routing-policy']['policy-definitions']['policy-definition'] |
| 59 | + for policy in policy_definitions: |
| 60 | + if policy_name == policy['name']: |
| 61 | + print json.dumps(policy, indent=4, separators=(',', ': ')) |
| 62 | + inner = json.dumps(policy) |
| 63 | + template = '{"openconfig-routing-policy:routing-policy": {"policy-definitions": {"policy-definition": [%s]}}}' % inner |
| 64 | + return json.dumps(json.loads(template, object_pairs_hook=OrderedDict), indent=4, separators=(',', ': ')) |
| 65 | + |
| 66 | + def get_neighbors(self): |
| 67 | + """sends a gRPC request and returns openconfig json of BGP |
| 68 | + :return: Json string of policies |
| 69 | + :rtype: json object |
| 70 | + """ |
| 71 | + path = '{"openconfig-bgp:bgp": [null]}' |
| 72 | + result = self.client.getconfig(path) |
| 73 | + bgp = json.loads(result, object_pairs_hook=OrderedDict) |
| 74 | + return bgp |
| 75 | + |
| 76 | + def list_neighbors(self): |
| 77 | + """Prints a list bgp neighbors for a router |
| 78 | + """ |
| 79 | + bgp = self.get_neighbors() |
| 80 | + bgp_neighbors = bgp['openconfig-bgp:bgp']['neighbors']['neighbor'] |
| 81 | + print "\nNeighbor's\n" |
| 82 | + for neighbor in bgp_neighbors: |
| 83 | + print 'Neighbor: %s AS: %s' % (neighbor['neighbor-address'], neighbor['config']['peer-as']) |
| 84 | + |
| 85 | + def detail_neighbor(self, neighbor_address): |
| 86 | + """Prints the full json of a neighbor in Openconfig format |
| 87 | + :param policy_name: Neighbor Address on the box. |
| 88 | + :type policy_name: str |
| 89 | + """ |
| 90 | + bgp = self.get_neighbors() |
| 91 | + bgp_neighbors = bgp['openconfig-bgp:bgp']['neighbors']['neighbor'] |
| 92 | + for neighbor in bgp_neighbors: |
| 93 | + if neighbor_address == neighbor['neighbor-address']: |
| 94 | + print json.dumps(neighbor, indent=4, separators=(',', ': ')) |
| 95 | + inner = json.dumps(neighbor) |
| 96 | + template = '{"openconfig-bgp:bgp": {"neighbors": {"neighbor" : [%s]}}}' % inner |
| 97 | + return json.dumps(json.loads(template, object_pairs_hook=OrderedDict), indent=4, separators=(',', ': ')) |
| 98 | + |
| 99 | + def is_int(self, num): |
| 100 | + """Helper function to see if value is a integer. |
| 101 | + Used to figure if its an AS or Neighbor Address |
| 102 | + :param num: A number or str |
| 103 | + :type: int or str |
| 104 | + :rtype boolean |
| 105 | + """ |
| 106 | + try: |
| 107 | + int(num) |
| 108 | + return True |
| 109 | + except ValueError: |
| 110 | + return False |
| 111 | + |
| 112 | + def merge_config(self, config): |
| 113 | + """gRPC merge call to push config changes to Router |
| 114 | + :param config: yang structured config in json |
| 115 | + :type config: str |
| 116 | + :return error if applicable |
| 117 | + :rtype str |
| 118 | + """ |
| 119 | + try: |
| 120 | + response = self.client.mergeconfig(config) |
| 121 | + if response.errors: |
| 122 | + err = json.loads(response.errors) |
| 123 | + return json.dumps(err, indent=4, separators=(',', ': ')) |
| 124 | + except ValueError as err: |
| 125 | + return err |
| 126 | + |
| 127 | + def neighbor_policy(self, neighbor_address, policy, direction): |
| 128 | + """Function to update a neighbors or AS policy |
| 129 | + :param neighbor_address: neighbor address or AS for policy |
| 130 | + :param policy: name of policy to be applied |
| 131 | + :param direction: export-policy or import-policy |
| 132 | + :type neighbor_address: str or int |
| 133 | + :type policy: str |
| 134 | + :type direction: str |
| 135 | + :returns: Prints neighbors it is applied to, and new bgp neighbor config |
| 136 | + """ |
| 137 | + updated_neighbors = [] |
| 138 | + bgp = self.get_neighbors() |
| 139 | + bgp_neighbors = bgp['openconfig-bgp:bgp']['neighbors']['neighbor'] |
| 140 | + for neighbor in bgp_neighbors: |
| 141 | + if self.is_int(neighbor_address): |
| 142 | + val = neighbor['config']['peer-as'] |
| 143 | + else: |
| 144 | + val = neighbor['neighbor-address'] |
| 145 | + if val in neighbor_address: |
| 146 | + if len(policy) > 1 and isinstance(policy, list): |
| 147 | + policy = self.multiple_policies(policy, neighbor_address) |
| 148 | + # Change the policy to drop. |
| 149 | + ipvs = neighbor['afi-safis']['afi-safi'] |
| 150 | + for ipv in ipvs: |
| 151 | + curr_policy = ipv['apply-policy']['config'][direction] |
| 152 | + ipv['apply-policy']['config']['export-policy'] = policy |
| 153 | + ip_type = ipv['afi-safi-name'] |
| 154 | + # Add the removed neighbors to list. |
| 155 | + updated_neighbors.append((neighbor['neighbor-address'], ip_type, curr_policy)) |
| 156 | + updated_neighbors = json.dumps(updated_neighbors) |
| 157 | + print updated_neighbors |
| 158 | + bgp_config = json.dumps(bgp) |
| 159 | + err = self.merge_config(bgp_config) |
| 160 | + if not err: |
| 161 | + print err |
| 162 | + print '\nNew Neighbor Detail:\n' |
| 163 | + self.detail_neighbor(neighbor_address) |
| 164 | + |
| 165 | + def multiple_policies(self, policies, neighbor): |
| 166 | + """Creates a new policy that applies list of policies to it. |
| 167 | + :param policies: list of policies that you want applied to a single policy |
| 168 | + :param neighbor: the neighbor you are going to apply these policies (used for naming) |
| 169 | + :type policies: list |
| 170 | + :type neighbor: str |
| 171 | + :return: Name of the policy that is created |
| 172 | + :rtype: str |
| 173 | + """ |
| 174 | + policy_name = neighbor.replace('.', '_') |
| 175 | + policy_name = 'multi_policy_' + policy_name |
| 176 | + shell = '{"openconfig-routing-policy:routing-policy": {"policy-definitions": {"policy-definition": [{"name": "%s","statements": {"statement": []}}]}}}' % policy_name |
| 177 | + shell = json.loads(shell, object_pairs_hook=OrderedDict) |
| 178 | + conditions = shell['openconfig-routing-policy:routing-policy']['policy-definitions']['policy-definition'][0]['statements']['statement'] |
| 179 | + for policy in policies: |
| 180 | + policy_nm = 'Policy_' + policy |
| 181 | + json_policy = '{"name": "%s", "conditions": {"call-policy": "%s"}}' % (policy_nm, policy) |
| 182 | + json_policy = json.loads(json_policy, object_pairs_hook=OrderedDict) |
| 183 | + conditions.append(json_policy) |
| 184 | + multi_policy = json.dumps(shell) |
| 185 | + print self.merge_config(multi_policy) |
| 186 | + return policy_name |
| 187 | + |
| 188 | +def main(): |
| 189 | + """ |
| 190 | + Testing and demoing functions in class |
| 191 | + """ |
| 192 | + # Create a class object for route policy manipulation |
| 193 | + route = RoutePolicy('localhost', 57777, 'vagrant', 'vagrant') |
| 194 | + # List Policies from Router |
| 195 | + route.list_policies() |
| 196 | + print '\nnext function\n' |
| 197 | + # Get the full json object of the policy, allows for manipulation |
| 198 | + policy = route.detail_policy('TEST') |
| 199 | + print '\nnext function\n' |
| 200 | + # List BGP neighbors |
| 201 | + route.list_neighbors() |
| 202 | + print '\nnext function\n' |
| 203 | + # Get the full json object of the BGP neighbor, allows for manipulation |
| 204 | + route.detail_neighbor('11.1.1.2') |
| 205 | + print '\nnext function\n' |
| 206 | + # Apply multiple policies to a single neighbor interface |
| 207 | + route.neighbor_policy('11.1.1.2', ['TEST', 'SEND-MED-IGP'], 'export-policy') |
| 208 | + print '\nnext function\n' |
| 209 | + # Store policy in json file to modify |
| 210 | + policy_file = open('policy.json', 'w') |
| 211 | + policy_file.write(policy) |
| 212 | + policy_file.close() |
| 213 | + # Merge policy from file to router |
| 214 | + policy_file = open('policy.json', 'r') |
| 215 | + route.merge_config(policy_file.read()) |
| 216 | + policy_file.close() |
| 217 | + |
| 218 | +if __name__ == '__main__': |
| 219 | + main() |
0 commit comments