Skip to content

Commit

Permalink
feat: added create/store bond api
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaile committed Feb 5, 2024
1 parent c714df7 commit 1500951
Showing 1 changed file with 146 additions and 10 deletions.
156 changes: 146 additions & 10 deletions packages/ns-api/files/ns.devices
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
#!/usr/bin/python3
import ipaddress
import json
import random
import re
import subprocess
import sys

from euci import EUci
from nethsec import utils, firewall


#
# Copyright (C) 2023 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

import json
import sys
import subprocess
import re
from euci import EUci
from nethsec import utils, firewall


def get_all_by_type_as_list(uci, config, utype):
all_as_dict = utils.get_all_by_type(uci, config, utype)
Expand Down Expand Up @@ -1098,6 +1101,128 @@ def list_zones_for_device_config():
return {'zones': zones}


def __available_bond_slaves():
"""
Returns a list of strings containing all the names of available slaves for a bond interface.
"""
available_devices = list_devices()
unconfigured_devices = [device['devices'] for device in available_devices['devices_by_zone']
if device['name'] == 'unassigned'][0]
slaves = []
for device in available_devices['all_devices']:
if 'link_type' in device and device['link_type'] == 'ether':
if device['name'] in unconfigured_devices:
slaves.append(device['name'])

return slaves


def store_bond():
"""
Create a bond interface.
"""
uci = EUci()
data = json.loads(sys.stdin.read())
if 'name' not in data:
raise utils.ValidationError('name', 'required')
if data['name'] == '':
raise utils.ValidationError('name', 'invalid', data['name'])
if data['name'] in uci.get_all('network').keys():
raise utils.ValidationError('name', 'unique', data['name'])

if 'slaves' not in data:
raise utils.ValidationError('slaves', 'required')
if not isinstance(data['slaves'], list):
raise utils.ValidationError('slaves', 'invalid', data['slaves'])
if len(data['slaves']) == 0:
raise utils.ValidationError('slaves', 'required', data['slaves'])
for slave in data['slaves']:
available_slaves = __available_bond_slaves()
if slave not in available_slaves:
raise utils.ValidationError('slaves', 'invalid', data['devices'])

if 'ip_addr' not in data:
raise utils.ValidationError('ip_addr', 'required')
try:
ip_address = ipaddress.IPv4Network(data['ip_addr'], strict=False)
except ipaddress.AddressValueError or ipaddress.NetmaskValueError:
raise utils.ValidationError('ip_addr', 'invalid', data['ip_addr'])

if 'bonding_policy' not in data:
raise utils.ValidationError('bonding_policy', 'required')
match data['bonding_policy']:
case 'balance-rr':
additional_policy_config = {
'packets_per_slave': '1'
}
case 'active-backup':
additional_policy_config = {
'primary': data['device'][0],
'primary_reselect': 'always',
'fail_over_mac': 'none',
'num_grat_arp__num_unsol_na': '1'
}
case 'balance-xor':
additional_policy_config = {
'primary': '',
'xmit_hash_policy': 'layer2'
}
case 'broadcast':
additional_policy_config = {
'primary': ''
}
case '802.3ad':
additional_policy_config = {
'min_links': '0',
'ad_actor_sys_prio': '65535',
'ad_select': 'stable',
'lacp_rate': 'slow',
'xmit_hash_policy': 'layer2',
'primary': ''
}
case 'balance-tlb':
additional_policy_config = {
'primary': data['device'][0],
'primary_reselect': 'always',
'lp_interval': '1',
'tlb_dynamic_lb': '1',
'xmit_hash_policy': 'layer2'
}
case 'balance-alb':
additional_policy_config = {
'primary': data['device'][0],
'primary_reselect': 'always',
'lp_interval': '1',
'xmit_hash_policy': 'layer2',
'resend_igmp': '1'
}
case _:
raise utils.ValidationError('bonding_policy', 'invalid', data['bonding_policy'])

uci.set('network', data['name'], 'interface')
uci.set('network', data['name'], 'proto', 'bonding')
uci.set('network', data['name'], 'slaves', data['slaves'])
uci.set('network', data['name'], 'ipaddr', str(ip_address.network_address))
uci.set('network', data['name'], 'netmask', str(ip_address.netmask))
uci.set('network', data['name'], 'bonding_policy', data['bonding_policy'])
for key, value in additional_policy_config.items():
uci.set('network', data['name'], key, value)

uci.save('network')


def create_bond():
"""
Returns to the UI the fields needed to create a bond interface.
:return: dict
"""
return {
'name': utils.get_random_id(),
'slaves': __available_bond_slaves(),
'ip_addr': '.'.join(str(random.randint(0, 255)) for _ in range(4)) + '/30',
}


cmd = sys.argv[1]

if cmd == 'list':
Expand Down Expand Up @@ -1155,7 +1280,15 @@ if cmd == 'list':
'delete-device': {
'device_name': 'string'
},
'list-zones-for-device-config': {}
'list-zones-for-device-config': {},
'store-bond': {
'name': '',
'slaves': [],
'ip_addr': '',
'netmask': '',
'bonding_policy': ''
},
'create-bond': {}
}))
elif cmd == 'call':
action = sys.argv[2]
Expand Down Expand Up @@ -1194,10 +1327,13 @@ elif cmd == 'call':
elif action == 'list-zones-for-device-config':
zones = list_zones_for_device_config()
print(json.dumps(zones))
elif action == 'store-bond':
store_bond()
print(json.dumps({'message': 'success'}))
elif action == 'create-bond':
print(json.dumps({'values': create_bond()}))
else:
print(json.dumps(utils.generic_error(f'invalid method {action}')))
except KeyError as e:
print(json.dumps(utils.validation_error(e.args[0], 'required')))
except json.JSONDecodeError:
print(json.dumps(utils.generic_error("json given is invalid")))
except utils.ValidationError as e:
Expand Down

0 comments on commit 1500951

Please sign in to comment.