2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-25 11:01:09 +00:00

Add multi-address registration

This enables scanning a range or subnet
This commit is contained in:
Jarrod Johnson 2022-11-18 14:14:31 -05:00
parent 2c1e88d927
commit 9837ad7932
3 changed files with 126 additions and 18 deletions

View File

@ -51,16 +51,27 @@ columnmapping = {
#TODO: add chassis uuid
def register_endpoint(options, session, addr):
neednewline = False
current = 0
for rsp in session.update('/discovery/register', {'addresses': addr}):
if 'count' in rsp:
total = rsp['count']
elif total > 1:
current += 1
sys.stdout.write('\x1b[2K\r{0:.1f}% Scanned'.format(100 * current / total))
sys.stdout.flush()
neednewline = True
if 'created' in rsp:
if neednewline:
print('')
neednewline = False
print('Registered: {0}'.format(rsp['created']))
else:
sys.stderr.write(repr(rsp))
sys.stderr.write('\n')
if neednewline:
print('')
def subscribe_discovery(options, session, subscribe):
def subscribe_discovery(options, session, subscribe, targ):
keyn = 'subscribe' if subscribe else 'unsubscribe'
payload = {keyn: options.node}
payload = {keyn: targ}
if subscribe:
for rsp in session.update('/discovery/subscriptions/{0}'.format(options.node), payload):
if 'status' in rsp:
@ -383,6 +394,12 @@ def main():
if len(args) == 0 or args[0] not in ('list', 'assign', 'reassign', 'rescan', 'clear', 'subscribe', 'unsubscribe', 'register'):
parser.print_help()
sys.exit(1)
if args[0] == 'register' and len(args) != 2:
sys.stderr.write('Register requires target address or range (e.g. 192.168.2.30, 192.168.2.0/24, or 192.168.2.1-192.168.2.30)')
sys.exit(1)
if 'subscribe' in args[0] and len(args) != 2:
sys.stderr.write('subscribe/unsubscribe subcommands require switch name as argument')
sys.exit(1)
session = client.Command()
if args[0] == 'list':
list_discovery(options, session)
@ -395,9 +412,9 @@ def main():
if args[0] == 'register':
register_endpoint(options, session, args[1])
if args[0] == 'subscribe':
subscribe_discovery(options, session, True)
subscribe_discovery(options, session, True, args[1])
if args[0] == 'unsubscribe':
subscribe_discovery(options, session, False)
subscribe_discovery(options, session, False, args[1])
if args[0] == 'rescan':
blocking_scan(session)
print("Rescan complete")

View File

@ -83,6 +83,7 @@ import json
import eventlet
import traceback
import shlex
import struct
import eventlet.green.socket as socket
import socket as nsocket
import eventlet.green.subprocess as subprocess
@ -385,6 +386,59 @@ single_selectors = set([
])
def addr_to_number(addr):
addr = socket.inet_pton(socket.AF_INET, addr)
num = struct.unpack('!I', addr)[0]
return num
def number_to_addr(number):
addr = struct.pack('!I', number)
addr = socket.inet_ntop(socket.AF_INET, addr)
return addr
def iterate_addrs(addrs, countonly=False):
if '.' not in addrs:
raise exc.InvalidArgumentException('IPv4 only supported')
if '-' in addrs:
first, last = addrs.split('-', 1)
currn = addr_to_number(first)
last = addr_to_number(last)
if last < currn:
tm = currn
currn = last
currn = tm
if (last - currn) > 65538:
raise exc.InvalidArgumentException("Too many ip addresses")
if countonly:
yield last - currn + 1
return
while currn <= last:
yield number_to_addr(currn)
currn += 1
elif '/' in addrs:
first, plen = addrs.split('/', 1)
plen = int(plen)
if plen > 32:
raise exc.InvalidArgumentException("Invalid prefix length")
mask = (2**32 - 1) ^ (2**(32 - plen) - 1)
currn = addr_to_number(first)
currn = currn & mask
numips = 2**(32 - plen)
if numips > 65538:
raise exc.InvalidArgumentException("Too many ip addresses")
if countonly:
yield numips
return
while numips > 0:
yield number_to_addr(currn)
currn += 1
numips -= 1
else:
if countonly:
yield 1
return
yield addrs
def _parameterize_path(pathcomponents):
listrequested = False
childcoll = True
@ -442,6 +496,30 @@ def save_subscriptions(subs):
with open('/etc/confluent/discovery_subscriptions.json', 'w') as dso:
dso.write(json.dumps(subs))
def register_remote_addrs(addresses, configmanager):
def register_remote_addr(addr):
nd = {
'addresses': [(addr, 443)]
}
sd = ssdp.check_fish(('/DeviceDescription.json', nd))
if not sd:
return addr, False
sd['hwaddr'] = sd['attributes']['mac-address']
nh = xcc.NodeHandler(sd, configmanager)
nh.scan()
detected(nh.info)
return addr, True
rpool = eventlet.greenpool.GreenPool(512)
for count in iterate_addrs(addresses, True):
yield msg.ConfluentResourceCount(count)
for result in rpool.imap(register_remote_addr, iterate_addrs(addresses)):
if result[1]:
yield msg.CreatedResource(result[0])
else:
yield msg.ConfluentResourceNotFound(result[0])
def handle_api_request(configmanager, inputdata, operation, pathcomponents):
if pathcomponents == ['discovery', 'autosense']:
return handle_autosense_config(operation, inputdata)
@ -477,17 +555,7 @@ def handle_api_request(configmanager, inputdata, operation, pathcomponents):
if pathcomponents == ['discovery', 'register']:
if 'addresses' not in inputdata:
raise exc.InvalidArgumentException('Missing address in input')
nd = {
'addresses': [(inputdata['addresses'], 443)]
}
sd = ssdp.check_fish(('/DeviceDescription.json', nd))
if not sd:
raise exc.InvalidArgumentException('Target address is not detected or is not a supported device for remote discovery registration')
sd['hwaddr'] = sd['attributes']['mac-address']
nh = xcc.NodeHandler(sd, configmanager)
nh.scan()
detected(nh.info)
return [msg.CreatedResource(inputdata['address'])]
return register_remote_addrs(inputdata['addresses'], configmanager)
if 'node' not in inputdata:
raise exc.InvalidArgumentException('Missing node name in input')
mac = _get_mac_from_query(pathcomponents)

View File

@ -309,6 +309,29 @@ class DeletedResource(ConfluentMessage):
pass
class ConfluentResourceNotFound(ConfluentMessage):
notnode = True
apicode = 404
def __init__(self, resource):
self.myargs = [resource]
self.desc = 'Not Found'
self.kvpairs = {'missing': resource}
def strip_node(self, node):
pass
class ConfluentResourceCount(ConfluentMessage):
notnode = True
def __init__(self, count):
self.myargs = [count]
self.desc = 'Resource Count'
self.kvpairs = {'count': count}
def strip_node(self, node):
pass
class CreatedResource(ConfluentMessage):
notnode = True
readonly = True