Assume that you work for a service provider, and must develop a script that extracts customer data from a router configuration. The script should parse customer data, such as customer name, customer VLAN, and customer IP address, and return the data in the JavaScript Object Notation (JSON) format.
To simplify the task, the router configuration should already be in the config.txt file, located in the same folder as the script.
As a testing framework, you should use unittest.
By observing the configuration in the config.txt file, you verify that the required customer data is available in the configuration.
ip vrf CUSTOMER_A rd 65000:100 ! ip vrf CUSTOMER_B rd 65000:101 ! interface GigabitEthernet0/0.100 encapsulation dot1Q 100 ip vrf forwarding CUSTOMER_A ip address 10.10.100.1 255.255.255.0 no ip redirects ip nat outside ip virtual-reassembly in ! interface GigabitEthernet0/0.101 encapsulation dot1Q 101 ip vrf forwarding CUSTOMER_B ip address 10.10.101.1 255.255.255.0 no ip redirects ip nat outside ip virtual-reassembly in ! Interface IP-Address Embedded-Service-Engine0/0 unassigned GigabitEthernet0/0 192.168.254.2 GigabitEthernet0/0.100 10.10.100.1 GigabitEthernet0/0.101 10.10.101.1
Development Procedure
Before actual programming, you decide to build a script in multiple iterations. Each iteration will already fulfill part of the requirements, but the last iteration will group everything.
Iterations:
Iteration 1: Parse Customer Names
Iteration 2: Parse Customer IP Addresses
Iteration 3: Parse Customer VLANs
Iteration 4: Group All Together
Steps used for a specific iteration:
Write tests
Run tests (should fail)
Write code
Run tests (should pass)
Refactor code
Parse Customer Names
Step 1: Write tests.
During the first iteration, you decide to parse customer names from the configuration.
To follow the rules of the TDD, first create tests for parsing customer names.
You decide that the class responsible for configuration parsing will be called ConfigurationParser
. This class will contain all methods required for parsing customer data. Therefore, you can already instantiate the class in the test. You can also create this class, but without any functionality.
cp = ConfigurationParser()
Next, you decide that parsed customer names should be returned in a Python list.
expected_names = ['CUSTOMER_A', 'CUSTOMER_B']
The method that parses customer names is called parseCustomerNames()
.
parsed_names = cp.parseCustomerNames()
At the end of the test, assertions are being made to check the method output based on the input.
self.assertEqual(list, type(parsed_name)) self.assertEqual(expected_names, parsed_names)
Step 2: Run tests (should fail).
$ python3.7 -m unittest test_parser.py E ====================================================================== ERROR: test_parse_cust_names (test_parser.TestParse) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_parser.py," line 8, in test_parse_cust_names parsed_names = cp.parseCustomerNames() AttributeError: 'ConfigurationParser' object has no attribute 'parseCustomerNames' ---------------------------------------------------------------------- FAILED (errors=1)
No actual functionality exists, so running the test will fail.
Step 3: Write code.
Once tests are created, you should implement the actual functionality.
As specified in the requirements, the router configuration should already be present in the config.txt file.
You decide that the customer names will be parsed from the VRF configuration.
ip vrf CUSTOMER_A
To parse a certain substring from a configuration line, use regular expressions.
customerNamePattern = r'ip vrf ([a-zA-Z_]+)\n' customerNames = re.findall(customerNamePattern, deviceConfig)
The result of the re.findall
method is a list, which is what you need to return.
return customerNames
Step 4: Run tests (should pass).
$ python3.7 –m unittest test_parser.py
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
If the functionality is implemented correctly, tests should pass now.
Step 5: Refactor code.
The last step of the iteration is to refactor the code if needed. Because you will implement additional methods to parse customer VLANs and customer IP addresses, you decide to put the deviceConfig
variable out of the method so that other methods can use it, too.
The TDD iteration is now complete, and you can go on to the next iteration.
Parse Customer VLANs
Repeat Steps 1 to 5.
In the second iteration, you decide to parse the customer VLAN. Similar to the previous iteration, first you create tests where you define the following:
The method that will be used to parse the customer VLAN:
parseCustomerVLAN()
Parameters:
customer_name
The return type of the method (integer):
expected_vlan = 100
At the end of the test, check that the actual parsed data are the same as the expected data.
Repeat Steps 1 to 5.
You decide that the customer VLAN will be parsed from the interface configuration.
interface GigabitEthernet0/0.100
To parse a certain substring from a configuration line, use regular expressions.
intPattern = ( r"interface GigabitEthernet0\/0\.([0-9]+)\n encapsulation " r"dot1Q [0-9]+\n ip vrf forwarding %s" % (customerName) ) allCustomerSubInterfaces = re.search(intPattern, self.deviceConfig)
As expected in the test, you return the integer value of the VLAN.
return int(allCustomerSubInterfaces.group(1))
Parse Customer IP Addresses
Repeat Steps 1 to 5.
In the third iteration, you decide to parse the customer IP addresses. Similar to the previous iterations, first create tests where you define the following:
The method that will be used to parse the customer IP address:
parseCustomerIPAddress()
Parameters:
vlan
The return type of the method (string):
expected_ip = "10.10.100.1"
At the end of the test, check that the actual parsed data are the same as the expected data.
Repeat Steps 1 to 5.
You decide that the customer IP addresses will be parsed from the interface summary configuration.
GigabitEthernet0/0.100 10.10.100.1
To parse a certain substring from a configuration line, you use regular expressions.
customerIpPattern = r'GigabitEthernet0/0.%s[ ]+([0-9\.]+)' % (vlan) customerIpAddress = re.search(customerIpPattern, self.deviceConfig)
As expected in the test, you return the string value of the IP address.
return customerIpAddress.group(1)
Combine All Together
Repeat steps 1 to 5 (continued).
For the last iteration, you decide to combine all previously developed methods together. Similar to the previous iterations, first create tests where you define the following:
The method that will be used to parse customer data:
parseCustomerData()
The return type of the method (dictionary):
expected_data = { "CUSTOMER_A": [100, "10.10.100.1"] }
At the end of the test, check that the actual parsed data are the same as the expected data.
Repeat steps 1 to 5 (continued).
The parseCustomerData()
method combines all the previously developed methods and returns customer data inside a Python dictionary, which can be very easily converted to JSON format later if needed.
No comments:
Post a Comment