-
Notifications
You must be signed in to change notification settings - Fork 1k
[dualtor] Implement y_cable_simulator client #2652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
bingwang-ms
merged 3 commits into
sonic-net:master
from
bingwang-ms:y_cable_simulator_client
Dec 15, 2020
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| --- | ||
| - fail: msg="There must be two duts in this testbed" | ||
| when: "testbed_facts['duts']|length != 2" | ||
|
|
||
| - fail: msg="The type of testbed must be dualtor" | ||
| when: "'dualtor' not in testbed_facts['topo']" | ||
|
|
||
| - fail: msg="The DUT you are trying to run test does not belongs to this testbed" | ||
| when: inventory_hostname not in testbed_facts['duts'] | ||
|
|
||
| - name: get host server address | ||
| vmhost_server_info: vmhost_server_name={{ testbed_facts['server'] }} vm_file='veos' | ||
| delegate_to: localhost | ||
|
|
||
| - name: set y cable simulator server address | ||
| set_fact: | ||
| mux_simulator_server: "{{ vmhost_server_address }}" | ||
|
|
||
| - name: set default y cable simulator server port | ||
| set_fact: | ||
| mux_simulator_port: 8080 | ||
| when: mux_simulator_port is not defined | ||
|
|
||
| - name: generate y cable simulator driver for {{ dut_name }} | ||
| include_tasks: "dualtor/y_cable_simulator_injector.yml" | ||
| loop: "{{ testbed_facts['duts'] }}" | ||
| loop_control: | ||
| loop_var: dut_name | ||
| run_once: True | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| from urllib import request, error | ||
| import json | ||
| from sonic_py_common import logger | ||
|
|
||
| DUTS_MAP = { | ||
| "{{ dual_tor_facts['positions']['upper'] }}": 0, | ||
| "{{ dual_tor_facts['positions']['lower'] }}": 1 | ||
| } | ||
|
|
||
| VM_SET = "{{ testbed_facts['group-name'] }}" | ||
|
|
||
| DUT_NAME = "{{ dut_name }}" | ||
|
|
||
| BASE_URL = "http://{{ mux_simulator_server }}:{{ mux_simulator_port }}/" | ||
|
|
||
| SYSLOG_IDENTIFIER = "y_cable_sim" | ||
| helper_logger = logger.Logger(SYSLOG_IDENTIFIER) | ||
|
|
||
| TOR_A = "tor_a" | ||
| TOR_B = "tor_b" | ||
|
|
||
| def _url(physical_port): | ||
| """ | ||
| Helper function to build an url for given physical_port | ||
|
|
||
| Args: | ||
| physical_port: physical port on switch, an integer starting from 1 | ||
| Returns: | ||
| str: The url for post/get. | ||
| """ | ||
| return BASE_URL + "/mux/{}/{}".format(VM_SET, physical_port - 1) | ||
|
|
||
| def _post(physical_port, data): | ||
| """ | ||
| Helper function for posting data to y_cable server. | ||
|
|
||
| Args: | ||
| physical_port: physical port on switch, an integer starting from 1 | ||
| data: data to post | ||
| Returns: | ||
| True if succeed. False otherwise | ||
| """ | ||
| data = json.dumps(data).encode(encoding='utf-8') | ||
| header = {'Accept': 'application/json', 'Content-Type': 'application/json'} | ||
| req = request.Request(url=_url(physical_port), data=data, headers=header) | ||
| try: | ||
| _ = request.urlopen(req) | ||
| except error.HTTPError as e: | ||
| try: | ||
| err_msg = json.loads(e.read().decode())['err_msg'] | ||
| helper_logger.log_warning("post request returns err. status_code = {} err_msg = {}".format(e.code, err_msg)) | ||
| except Exception: | ||
| helper_logger.log_warning("post request returns err. status_code = {}".format(e.code)) | ||
| return False | ||
| except error.URLError as e: | ||
| helper_logger.log_warning("post request returns err. err_msg = {}".format(str(e))) | ||
| return False | ||
| return True | ||
|
|
||
| def _get(physical_port): | ||
| """ | ||
| Helper function for polling status from y_cable server. | ||
|
|
||
| Args: | ||
| physical_port: physical port on switch, an integer starting from 1 | ||
| Returns: | ||
| dict: A dict decoded from server's response. | ||
| None: Returns None is error is detected. | ||
| """ | ||
| req = request.Request(url=_url(physical_port)) | ||
| try: | ||
| res = request.urlopen(req) | ||
| data = res.read() | ||
| return json.loads(data) | ||
| except error.HTTPError as e: | ||
| err_msg = json.loads(e.read().decode())['err_msg'] | ||
| helper_logger.log_warning("get request returns err. status_code = {} err_msg = {}".format(e.code, err_msg)) | ||
| except error.URLError as e: | ||
| helper_logger.log_warning("get request returns err. err_msg = {}".format(str(e))) | ||
| except json.decoder.JSONDecodeError as e: | ||
| helper_logger.log_warning("failed to parse response as json. err_msg = {}".format(str(e))) | ||
| except Exception as e: | ||
| helper_logger.log_warning("get request returns err. err_msg = {}".format(str(e))) | ||
| return None | ||
|
|
||
| def _toggle_to(physical_port, target): | ||
| """ | ||
| Helper function for toggling to certain TOR. | ||
|
|
||
| Args: | ||
| physical_port: physical port on switch, an integer starting from 1 | ||
| target: TOR_A / TOR_B | ||
| Returns: | ||
| True if succeed. False otherwise | ||
| """ | ||
| data = {"active_side": target} | ||
| helper_logger.log_info("physical_port {} toggle to {}".format(physical_port, target)) | ||
| return _post(physical_port, data) | ||
|
|
||
| def _get_side(physical_port): | ||
| """ | ||
| Retrieve the current active tor from y_cable simulator server. | ||
| Args: | ||
| physical_port: physical port on switch, an integer starting from 1 | ||
| Returns: | ||
| 1 if TOR_A is active | ||
| 2 if TOR_B is active | ||
| -1 for exception or inconstient status | ||
| """ | ||
| res = _get(physical_port) | ||
| if not res: | ||
| return -1 | ||
| active_side = res["active_side"] | ||
| if active_side == TOR_A: | ||
| return 1 | ||
| elif active_side == TOR_B: | ||
| return 2 | ||
| else: | ||
| return -1 | ||
|
|
||
| def toggle_mux_to_torA(physical_port): | ||
| return _toggle_to(physical_port, TOR_A) | ||
|
|
||
| def toggle_mux_to_torB(physical_port): | ||
| return _toggle_to(physical_port, TOR_B) | ||
|
|
||
| def check_read_side(physical_port): | ||
| return DUTS_MAP[DUT_NAME] + 1 | ||
|
|
||
| def check_mux_direction(physical_port): | ||
| return _get_side(physical_port) | ||
|
|
||
| def check_active_linked_tor_side(physical_port): | ||
| return _get_side(physical_port) | ||
|
|
||
| def check_if_link_is_active_for_NIC(physical_port): | ||
| """ | ||
| Checks if NIC side of the Y cable's link is active. | ||
| Always return True for now because all links in simulator are active. | ||
| """ | ||
| return True | ||
|
|
||
| def check_if_link_is_active_for_torA(physical_port): | ||
| """ | ||
| Checks if TOR_A side of the Y cable's link is active. | ||
| Always return True for now because all links in simulator are active. | ||
| """ | ||
| return True | ||
|
|
||
| def check_if_link_is_active_for_torB(physical_port): | ||
| """ | ||
| Checks if TOR_B side of the Y cable's link is active. | ||
| Always return True for now because all links in simulator are active. | ||
| """ | ||
| return True | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| - name: generate y_cable_simulator driver for {{ dut_name }} | ||
| template: src=dualtor/y_cable_simulator_client.j2 | ||
| dest=/tmp/y_cable_simulator_client.py | ||
| delegate_to: "{{ dut_name }}" | ||
|
|
||
| - name: inject y_cable_simulator to pmon container for {{ dut_name }} | ||
| shell: docker cp /tmp/y_cable_simulator_client.py pmon:/usr/lib/python3/dist-packages | ||
| delegate_to: "{{ dut_name }}" | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| mux_simulator_port: 8080 | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| #!/usr/bin/env python | ||
|
|
||
| from ansible.parsing.dataloader import DataLoader | ||
| from ansible.inventory.manager import InventoryManager | ||
|
|
||
| DOCUMENTATION = ''' | ||
| module: vmhost_server_info.py | ||
| short_description: Gather mgmt IP for given host server (like server_17) | ||
| Description: | ||
| This plugin will parse the input vm_file and return mgmt IP for given host server. | ||
| options: | ||
| vmhost_server_name: the name of vm_host server, like server_1; required: True | ||
| vm_file: the virtual machine file path ; default: 'veos' | ||
|
|
||
| Ansible_facts: | ||
| 'vmhost_server_address': the IPv4 address for given vmhost server | ||
|
|
||
| ''' | ||
|
|
||
| EXAMPLES = ''' | ||
| - name: gather vm_host server address | ||
| vmhost_server_info: vmhost_server_name='server_1' vm_file='veos' | ||
| ''' | ||
|
|
||
| # Here we assume that the group name of host server starts with 'vm_host_'. | ||
| VMHOST_PREFIX = "vm_host_" | ||
|
|
||
| VM_INV_FILE = 'veos' | ||
|
|
||
| def main(): | ||
| module = AnsibleModule( | ||
| argument_spec=dict( | ||
| vmhost_server_name=dict(required=True, type='str'), | ||
| vm_file=dict(default=VM_INV_FILE, type='str') | ||
| ), | ||
| supports_check_mode=True | ||
| ) | ||
| m_args = module.params | ||
| vmhost_group_name = VMHOST_PREFIX + m_args['vmhost_server_name'].split('_')[-1] | ||
| inv_mgr = InventoryManager(loader=DataLoader(), sources=m_args['vm_file']) | ||
| all_hosts = inv_mgr.get_hosts(pattern=vmhost_group_name) | ||
| if len(all_hosts) != 1: | ||
| module.fail_json(msg="{} host servers are found in {}, which should be 1".format(len(all_hosts), vmhost_group_name)) | ||
| else: | ||
| module.exit_json(ansible_facts={'vmhost_server_address':all_hosts[0].get_vars()['ansible_host']}) | ||
|
|
||
| from ansible.module_utils.basic import * | ||
| if __name__ == "__main__": | ||
| main() | ||
|
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we have mux_simulator per topology?