Skip to content
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

Add RoCE module #363

Merged
merged 8 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions plugins/httpapi/sonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"""

import json
import time

from ansible.module_utils._text import to_text
from ansible.module_utils.connection import ConnectionError
Expand Down Expand Up @@ -81,6 +82,18 @@ def edit_config(self, requests):
responses.append(response)
return responses

def edit_config_reboot(self, requests):
"""Send a list of http requests to remote device and allow time for reboot
"""
if requests is None:
raise ValueError("'requests' value is required")

for req in to_list(requests):
try:
response = self.send_request(**req)
except Exception as exc:
time.sleep(300)

def get_capabilities(self):
result = {}
result['rpc'] = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(self, **kwargs):
'stp',
'sflow',
'fips',
'roce',
'qos_buffer',
'qos_pfc',
'qos_maps',
Expand Down
Empty file.
50 changes: 50 additions & 0 deletions plugins/module_utils/network/sonic/argspec/roce/roce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
# -*- coding: utf-8 -*-
# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################

"""
The arg spec for the sonic_roce module
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type


class RoceArgs(object): # pylint: disable=R0903
"""The arg spec for the sonic_roce module
"""

def __init__(self, **kwargs):
pass

argument_spec = {
'config': {
'options': {
'pfc_priority': {'type': 'str'},
'roce_enable': {'type': 'bool'}
},
'type': 'dict'
},
'state': {
'choices': ['merged'], 'default': 'merged', 'type': 'str'
}
} # pylint: disable=C0301
Empty file.
176 changes: 176 additions & 0 deletions plugins/module_utils/network/sonic/config/roce/roce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#
# -*- coding: utf-8 -*-
# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The sonic_roce class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
get_diff,
remove_empties,
update_states
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
to_request,
edit_config_reboot
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import (
get_new_config,
get_formatted_config_diff
)


ROCE_PATH = '/operations/openconfig-qos-private:qos-roce-config'
POST = 'post'


class Roce(ConfigBase):
"""
The sonic_roce class
"""

gather_subset = [
'!all',
'!min',
]

gather_network_resources = [
'roce',
]

def __init__(self, module):
super(Roce, self).__init__(module)

def get_roce_facts(self):
""" Get the 'facts' (the current configuration)

:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
roce_facts = facts['ansible_network_resources'].get('roce')
stalabi1 marked this conversation as resolved.
Show resolved Hide resolved
if not roce_facts:
return {}
return roce_facts

def execute_module(self):
""" Execute the module

:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
warnings = []
commands = []

existing_roce_facts = self.get_roce_facts()
commands, requests = self.set_config(existing_roce_facts)
if commands and len(requests) > 0:
if not self._module.check_mode:
try:
edit_config_reboot(self._module, to_request(self._module, requests))
except ConnectionError as exc:
pass
result['changed'] = True
result['commands'] = commands
changed_roce_facts = self.get_roce_facts()

result['before'] = existing_roce_facts
if result['changed']:
result['after'] = changed_roce_facts

new_config = changed_roce_facts
old_config = existing_roce_facts
if self._module.check_mode:
result.pop('after', None)
new_config = get_new_config(commands, existing_roce_facts)
result['after(generated)'] = new_config
if self._module._diff:
result['diff'] = get_formatted_config_diff(old_config,
new_config,
self._module._verbosity)

result['warnings'] = warnings
return result

def set_config(self, existing_roce_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)

:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = remove_empties(self._module.params['config'])
stalabi1 marked this conversation as resolved.
Show resolved Hide resolved
have = existing_roce_facts
resp = self.set_state(want, have)
return to_list(resp)

def set_state(self, want, have):
""" Select the appropriate function based on the state provided

:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
requests = []
state = self._module.params['state']
diff = get_diff(want, have)

if state == 'merged':
commands, requests = self._state_merged(diff)
return commands, requests

def _state_merged(self, diff):
""" The command generator when state is merged

:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = diff
requests = self.get_modify_roce_request(commands)

if commands and len(requests) > 0:
commands = update_states(commands, 'merged')
else:
commands = []

return commands, requests

def get_modify_roce_request(self, commands):
request = None

if commands:
input_dict = {}
bool_dict = {True: 'ENABLE', False: 'DISABLE'}
roce_enable = commands.get('roce_enable')
pfc_priority = commands.get('pfc_priority')

if roce_enable is not None:
input_dict['operation'] = bool_dict[roce_enable]
if pfc_priority:
input_dict['pfc-priority'] = pfc_priority
if input_dict:
payload = {'openconfig-qos-private:input': input_dict}
request = {'path': ROCE_PATH, 'method': POST, 'data': payload}

return request
2 changes: 2 additions & 0 deletions plugins/module_utils/network/sonic/facts/facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.stp.stp import StpFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.sflow.sflow import SflowFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.fips.fips import FipsFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.roce.roce import RoceFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.qos_buffer.qos_buffer import Qos_bufferFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.qos_pfc.qos_pfc import Qos_pfcFacts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.qos_maps.qos_maps import Qos_mapsFacts
Expand Down Expand Up @@ -113,6 +114,7 @@
stp=StpFacts,
sflow=SflowFacts,
fips=FipsFacts,
roce=RoceFacts,
qos_buffer=Qos_bufferFacts,
qos_pfc=Qos_pfcFacts,
qos_maps=Qos_mapsFacts,
Expand Down
Empty file.
99 changes: 99 additions & 0 deletions plugins/module_utils/network/sonic/facts/roce/roce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#
# -*- coding: utf-8 -*-
# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The sonic roce fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from copy import deepcopy

from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
remove_empties
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.roce.roce import RoceArgs
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
to_request,
edit_config
)


class RoceFacts(object):
""" The sonic roce fact class
"""

def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = RoceArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec

self.generated_spec = utils.generate_dict(facts_argument_spec)

def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for roce
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""

objs = []

if not data:
cfg = self.get_config(self._module)
data = self.update_roce(cfg)
objs = data
facts = {}
if objs:
params = utils.validate_config(self.argument_spec, {'config': objs})
facts['roce'] = remove_empties(params['config'])
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts

def get_config(self, module):
cfg = None
get_path = '/data/sonic-switch:sonic-switch/SWITCH/SWITCH_LIST=switch'
request = {'path': get_path, 'method': 'get'}

try:
response = edit_config(module, to_request(self._module, request))
if 'sonic-switch:SWITCH_LIST' in response[0][1]:
cfg = response[0][1].get('sonic-switch:SWITCH_LIST')
except ConnectionError as exc:
module.fail_json(msg=str(exc), code=exc.code)

return cfg

def update_roce(self, cfg):
config_dict = {}

if cfg:
roce_enable = cfg[0].get('roce_enable')
pfc_priority = cfg[0].get('roce_pfc_priority')

if roce_enable:
config_dict['roce_enable'] = True
# Set roce_enable to false when none
else:
config_dict['roce_enable'] = False
if pfc_priority:
config_dict['pfc_priority'] = pfc_priority

return config_dict
14 changes: 14 additions & 0 deletions plugins/module_utils/network/sonic/sonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ def edit_config(module, commands, skip_code=None):
return connection.edit_config(commands)


def edit_config_reboot(module, commands, skip_code=None):
connection = get_connection(module)

# Start: This is to convert interface name from Eth1/1 to Eth1%2f1
for request in commands:
# This check is to differenciate between requests and commands
kerry-meyer marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(request, dict):
url = request.get("path", None)
if url:
request["path"] = update_url(url)
# End
connection.edit_config_reboot(commands)


def update_url(url):
match = re.search(STANDARD_ETH_REGEXP, url)
ret_url = url
Expand Down
Loading
Loading