mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-11 12:06:26 +00:00
644843b892
Entirely mechanical, produced by `ruff check --fix --select F401,F541,E713` and reviewed rather than taken on faith: deleting an import is only safe if nothing imports it for its side effects or re-exports it. None of the 19 removed names is referenced anywhere in its file, none appears in any string literal, and none of the touched files uses eval, exec, globals() or __import__, so there is no dynamic lookup that could reach them.
726 lines
27 KiB
Python
726 lines
27 KiB
Python
#!/usr/bin/python
|
|
try:
|
|
import http.client as client
|
|
except ImportError:
|
|
import httplib as client
|
|
import base64
|
|
import ctypes
|
|
import ctypes.util
|
|
import os
|
|
import select
|
|
import socket
|
|
import subprocess
|
|
import ssl
|
|
import sys
|
|
import time
|
|
import re
|
|
import hashlib
|
|
try:
|
|
import json
|
|
import hmac
|
|
except ImportError:
|
|
json = None
|
|
hmac = None
|
|
|
|
# Ignore proxy-based environment variables
|
|
for proxy_var in ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'all_proxy', 'NO_PROXY', 'no_proxy']:
|
|
os.environ.pop(proxy_var, None)
|
|
|
|
class InvalidApiKey(Exception):
|
|
pass
|
|
|
|
def msg_align(len):
|
|
return (len + 3) & ~3
|
|
|
|
try:
|
|
_protoparm = ssl.PROTOCOL_TLS_CLIENT
|
|
except AttributeError:
|
|
_protoparm = ssl.PROTOCOL_SSLv23
|
|
cryptname = ctypes.util.find_library('crypt')
|
|
if not cryptname:
|
|
if os.path.exists('/lib64/libcrypt.so.2') or os.path.exists('/usr/lib64/libcrypt.so.2'):
|
|
cryptname = 'libcrypt.so.2'
|
|
else:
|
|
cryptname = 'libcrypt.so.1'
|
|
c_libcrypt = ctypes.CDLL(cryptname)
|
|
c_crypt = c_libcrypt.crypt
|
|
c_crypt.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
|
|
c_crypt.restype = ctypes.c_char_p
|
|
|
|
|
|
def get_my_addresses():
|
|
addrs = []
|
|
for ifa in get_ifaddrs():
|
|
if ifa[0] == 'ip':
|
|
addrs.append((ifa[1], ifa[2], ifa[3]))
|
|
return addrs
|
|
|
|
def get_mac_addresses():
|
|
macs = []
|
|
for ifa in get_ifaddrs():
|
|
if ifa[0] == 'ETHER':
|
|
macs.append((ifa[1], ifa[2]))
|
|
return macs
|
|
|
|
def get_ifaddrs():
|
|
class sockaddr(ctypes.Structure):
|
|
_fields_ = [
|
|
('sa_family', ctypes.c_uint16),
|
|
('sa_data', ctypes.c_ubyte * 14),
|
|
]
|
|
|
|
class sockaddr_in(ctypes.Structure):
|
|
_fields_ = [
|
|
('sin_family', ctypes.c_uint16),
|
|
('sin_port', ctypes.c_uint16),
|
|
('sin_addr', ctypes.c_ubyte * 4),
|
|
('sin_zero', ctypes.c_ubyte * 8),
|
|
]
|
|
|
|
class sockaddr_in6(ctypes.Structure):
|
|
_fields_ = [
|
|
('sin6_family', ctypes.c_uint16),
|
|
('sin6_port', ctypes.c_uint16),
|
|
('sin6_flowinfo', ctypes.c_uint32),
|
|
('sin6_addr', ctypes.c_ubyte * 16),
|
|
('sin6_scope_id', ctypes.c_uint32),
|
|
]
|
|
|
|
class sockaddr_ll(ctypes.Structure):
|
|
_fields_ = [
|
|
('sll_family', ctypes.c_uint16),
|
|
('sll_protocol', ctypes.c_uint16),
|
|
('sll_ifindex', ctypes.c_int32),
|
|
('sll_hatype', ctypes.c_uint16),
|
|
('sll_pkttype', ctypes.c_uint8),
|
|
('sll_halen', ctypes.c_uint8),
|
|
('sll_addr', ctypes.c_ubyte * 8),
|
|
]
|
|
|
|
class ifaddrs(ctypes.Structure):
|
|
pass
|
|
|
|
ifaddrs._fields_ = [
|
|
('ifa_next', ctypes.POINTER(ifaddrs)),
|
|
('ifa_name', ctypes.c_char_p),
|
|
('ifa_flags', ctypes.c_uint),
|
|
('ifa_addr', ctypes.POINTER(sockaddr)),
|
|
('ifa_netmask', ctypes.POINTER(sockaddr)),
|
|
('ifa_ifu', ctypes.POINTER(sockaddr)),
|
|
('ifa_data', ctypes.c_void_p),
|
|
]
|
|
|
|
libc = ctypes.CDLL(ctypes.util.find_library('c'))
|
|
libc.getifaddrs.argtypes = [ctypes.POINTER(ctypes.POINTER(ifaddrs))]
|
|
libc.getifaddrs.restype = ctypes.c_int
|
|
libc.freeifaddrs.argtypes = [ctypes.POINTER(ifaddrs)]
|
|
libc.freeifaddrs.restype = None
|
|
ifap = ctypes.POINTER(ifaddrs)()
|
|
result = libc.getifaddrs(ctypes.pointer(ifap))
|
|
if result != 0:
|
|
return []
|
|
addresses = []
|
|
ifa = ifap
|
|
try:
|
|
while ifa:
|
|
if ifa.contents.ifa_addr:
|
|
family = ifa.contents.ifa_addr.contents.sa_family
|
|
name = ifa.contents.ifa_name.decode('utf-8') if ifa.contents.ifa_name else None
|
|
if family in (socket.AF_INET, socket.AF_INET6):
|
|
# skip loopback and non-multicast interfaces
|
|
if ifa.contents.ifa_flags & 8 or not ifa.contents.ifa_flags & 0x1000:
|
|
ifa = ifa.contents.ifa_next
|
|
continue
|
|
if family == socket.AF_INET:
|
|
addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_in))
|
|
addr_bytes = bytes(addr_ptr.contents.sin_addr)
|
|
if_index = socket.if_nametoindex(name) if name else 0
|
|
addresses.append(('ip', family, addr_bytes, if_index))
|
|
elif family == socket.AF_INET6:
|
|
addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_in6))
|
|
addr_bytes = bytes(addr_ptr.contents.sin6_addr)
|
|
scope_id = addr_ptr.contents.sin6_scope_id
|
|
addresses.append(('ip', family, addr_bytes, scope_id))
|
|
elif family == socket.AF_PACKET:
|
|
addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_ll))
|
|
halen = addr_ptr.contents.sll_halen
|
|
if addr_ptr.contents.sll_hatype in (1, 32) and halen > 0: # ARPHRD_ETHER or ARPHRD_INFINIBAND
|
|
if addr_ptr.contents.sll_hatype == 1 and addr_ptr.contents.sll_addr[0] & 2: # skip locally administered MACs
|
|
ifa = ifa.contents.ifa_next
|
|
continue
|
|
mac_bytes = bytes(addr_ptr.contents.sll_addr[:halen])
|
|
macaddr = ':'.join('{:02x}'.format(b) for b in mac_bytes)
|
|
addresses.append(('ETHER', name, macaddr))
|
|
ifa = ifa.contents.ifa_next
|
|
finally:
|
|
libc.freeifaddrs(ifap)
|
|
|
|
return addresses
|
|
|
|
def scan_confluents(confuuid=None):
|
|
srvs = {}
|
|
s6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
|
|
try:
|
|
s6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
except Exception:
|
|
# REUSPORT is ideal, but if python is missing it, just live with the limitation
|
|
pass
|
|
try: # We would like to bind for firewall rules, however if not possible then leave it alone
|
|
s6.bind(('::', 1900))
|
|
except Exception:
|
|
pass
|
|
s4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
try:
|
|
s4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
except Exception:
|
|
# REUSPORT is ideal, but if python is missing it, just live with the limitation
|
|
pass
|
|
try:
|
|
s4.bind(('0.0.0.0', 1900))
|
|
except Exception:
|
|
pass
|
|
doneidxs = set([])
|
|
msg = 'M-SEARCH * HTTP/1.1\r\nST: urn:xcat.org:service:confluent:'
|
|
if not confuuid and os.path.exists('/etc/confluent/confluent.deploycfg'):
|
|
with open('/etc/confluent/confluent.deploycfg') as dcfg:
|
|
for line in dcfg.read().split('\n'):
|
|
if line.startswith('confluent_uuid:'):
|
|
confluentuuid = line.split(': ')[1]
|
|
msg += '/confluentuuid=' + confluentuuid
|
|
break
|
|
if not confuuid and os.path.exists('/confluent_uuid'):
|
|
with open('/confluent_uuid') as cuuidin:
|
|
confluentuuid = cuuidin.read().strip()
|
|
msg += '/confluentuuid=' + confluentuuid
|
|
try:
|
|
with open('/sys/devices/virtual/dmi/id/product_uuid') as uuidin:
|
|
msg += '/uuid=' + uuidin.read().strip()
|
|
except Exception:
|
|
pass
|
|
for iface, hwaddr in get_mac_addresses():
|
|
msg += '/mac=' + hwaddr
|
|
msg = msg.encode('utf8')
|
|
for addr in get_my_addresses():
|
|
if addr[0] == socket.AF_INET6:
|
|
if addr[-1] in doneidxs:
|
|
continue
|
|
try:
|
|
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1])
|
|
except TypeError:
|
|
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1].tobytes())
|
|
try:
|
|
s6.sendto(msg, ('ff02::c', 1900))
|
|
except OSError:
|
|
pass
|
|
doneidxs.add(addr[-1])
|
|
elif addr[0] == socket.AF_INET:
|
|
try:
|
|
s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1])
|
|
except TypeError:
|
|
s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1].tobytes())
|
|
try:
|
|
s4.sendto(msg, ('239.255.255.250', 1900))
|
|
except OSError:
|
|
pass
|
|
r = select.select((s4, s6), (), (), 4)
|
|
srvlist = []
|
|
if r:
|
|
r = r[0]
|
|
nodename = None
|
|
while r:
|
|
for s in r:
|
|
(rsp, peer) = s.recvfrom(9000)
|
|
rsp = rsp.split(b'\r\n')
|
|
current = None
|
|
for line in rsp:
|
|
if line.startswith(b'NODENAME: '):
|
|
nodename = line.replace(b'NODENAME: ', b'').strip().decode('utf8')
|
|
current = {}
|
|
elif line.startswith(b'DEFAULTNET: 1'):
|
|
current['isdefault'] = True
|
|
elif line.startswith(b'MGTIFACE: '):
|
|
current['mgtiface'] = line.replace(b'MGTIFACE: ', b'').strip().decode('utf8')
|
|
if len(peer) > 2:
|
|
current['myidx'] = peer[-1]
|
|
currip = peer[0]
|
|
if currip.startswith('fe80::') and '%' not in currip:
|
|
currip = '{0}%{1}'.format(currip, peer[-1])
|
|
srvs[currip] = current
|
|
if currip not in srvlist:
|
|
srvlist.append(currip)
|
|
r = select.select((s4, s6), (), (), 2)
|
|
if r:
|
|
r = r[0]
|
|
if not os.path.exists('/etc/confluent/confluent.info'):
|
|
with open('/etc/confluent/confluent.info', 'w+') as cinfo:
|
|
if nodename:
|
|
cinfo.write('NODENAME: {0}\n'.format(nodename))
|
|
for srv in srvlist:
|
|
cinfo.write('MANAGER: {0}\n'.format(srv))
|
|
return srvlist, srvs
|
|
|
|
|
|
def get_net_apikey(nodename, mgr, hmackey=None, confuuid=None):
|
|
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./'
|
|
newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))])
|
|
salt = '$5$' + ''.join([alpha[x >> 2] for x in bytearray(os.urandom(8))])
|
|
newpass = newpass.encode('utf8')
|
|
salt = salt.encode('utf8')
|
|
crypted = c_crypt(newpass, salt)
|
|
if hmackey:
|
|
hmacvalue = hmac.new(hmackey.encode('utf8'), crypted, hashlib.sha256).digest()
|
|
hmacvalue = base64.b64encode(hmacvalue).decode('utf8')
|
|
client = HTTPSClient(host=mgr, phmac=hmacvalue, nodename=nodename, confuuid=confuuid)
|
|
try:
|
|
status, rsp = client.grab_url_with_status('/confluent-api/self/registerapikey', data=crypted, returnrsp=True)
|
|
if status == 200:
|
|
return newpass.decode('utf8')
|
|
except Exception:
|
|
pass
|
|
for addrinfo in socket.getaddrinfo(mgr, 13001, 0, socket.SOCK_STREAM):
|
|
try:
|
|
clisock = socket.socket(addrinfo[0], addrinfo[1])
|
|
clisock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
if addrinfo[0] == socket.AF_INET:
|
|
cliaddr = ('0.0.0.0', 302)
|
|
else:
|
|
cliaddr = ('::', 302)
|
|
clisock.bind(cliaddr)
|
|
clisock.settimeout(1)
|
|
clisock.connect(addrinfo[-1])
|
|
rsp = clisock.recv(8)
|
|
if rsp != b'\xc2\xd1-\xa8\x80\xd8j\xba':
|
|
raise Exception('Unrecognized credential banner')
|
|
hellostr = bytearray([1, len(nodename)]) + bytearray(nodename.encode('utf8')) + bytearray(b'\x00\x00')
|
|
clisock.send(hellostr)
|
|
rsp = bytearray(clisock.recv(2))
|
|
if not rsp:
|
|
continue
|
|
if rsp[0] == 128:
|
|
continue
|
|
if rsp[0] == 2:
|
|
echotoken = clisock.recv(rsp[1])
|
|
clisock.recv(2) # drain \x00\x00
|
|
clisock.send(bytes(bytearray([3, rsp[1]])))
|
|
clisock.send(echotoken)
|
|
clisock.send(bytes(bytearray([4, len(crypted)])))
|
|
clisock.send(crypted)
|
|
clisock.send(b'\x00\x00')
|
|
rsp = bytearray(clisock.recv(2))
|
|
if rsp[0] == 5:
|
|
return newpass.decode('utf8')
|
|
finally:
|
|
clisock.close()
|
|
return ''
|
|
|
|
|
|
def get_apikey(nodename, hosts, errout=None, hmackey=None, confuuid=None):
|
|
apikey = ""
|
|
if os.path.exists('/etc/confluent/confluent.apikey'):
|
|
apikey = open('/etc/confluent/confluent.apikey').read().strip()
|
|
if apikey:
|
|
return apikey
|
|
while not apikey:
|
|
for host in hosts:
|
|
try:
|
|
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
|
except OSError:
|
|
apikey = None
|
|
if apikey:
|
|
break
|
|
else:
|
|
srvlist, _ = scan_confluents(confuuid=confuuid)
|
|
for host in srvlist:
|
|
try:
|
|
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
|
except OSError:
|
|
apikey = None
|
|
if apikey:
|
|
break
|
|
if not apikey:
|
|
errmsg = "Failed getting API token, check deployment.apiarmed attribute on {}\n".format(nodename)
|
|
sys.stderr.write(errmsg)
|
|
if errout:
|
|
errout.write(errmsg)
|
|
time.sleep(10)
|
|
with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
|
|
apiout.write(apikey)
|
|
apikey = apikey.strip()
|
|
os.chmod('/etc/confluent/confluent.apikey', 0o600)
|
|
return apikey
|
|
|
|
class HTTPSClient(client.HTTPConnection, object):
|
|
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False, hmackey=None, nodename=None, confuuid=None):
|
|
self.ignorehosts = set([])
|
|
self.phmac = phmac
|
|
self.hmackey = hmackey
|
|
self.confuuid = confuuid
|
|
self.errout = None
|
|
self.stdheaders = {}
|
|
if nodename:
|
|
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
|
if errout:
|
|
self.errout = open(errout, 'w')
|
|
self.errout.flush()
|
|
mgtiface = None
|
|
if usejson:
|
|
self.stdheaders['ACCEPT'] = 'application/json'
|
|
if host:
|
|
self.hosts = [host]
|
|
if not nodename:
|
|
with open('/etc/confluent/confluent.info') as cinfo:
|
|
info = cinfo.read().split('\n')
|
|
for line in info:
|
|
if line.startswith('NODENAME:'):
|
|
nodename = line.split(' ')[1]
|
|
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
|
else:
|
|
self.hosts = []
|
|
try:
|
|
info = open('/etc/confluent/confluent.info').read().split('\n')
|
|
except Exception:
|
|
info = []
|
|
havedefault = '0'
|
|
plainhost = ''
|
|
for line in info:
|
|
host = ''
|
|
if line.startswith('NODENAME:'):
|
|
nodename = line.split(' ')[1]
|
|
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
|
if line.startswith('MANAGER:') and not host:
|
|
host = line.split(' ')[1]
|
|
self.hosts.append(host)
|
|
if not plainhost:
|
|
plainhost = host
|
|
if line.startswith('EXTMGRINFO:'):
|
|
extinfo = line.split(' ')[1]
|
|
extinfo = extinfo.split('|')
|
|
if havedefault == '0' and extinfo[2] == '1':
|
|
host, mgtiface, havedefault = extinfo[:3]
|
|
if '%' in host:
|
|
ifidx = host.split('%', 1)[1]
|
|
with open('/tmp/confluent.ifidx', 'w+') as ifout:
|
|
ifout.write(ifidx)
|
|
if not mgtiface:
|
|
host, mgtiface, havedefault = extinfo[:3]
|
|
if host:
|
|
if havedefault == '0':
|
|
if '%' in host:
|
|
ifidx = host.split('%', 1)[1]
|
|
with open('/tmp/confluent.ifidx', 'w+') as ifout:
|
|
ifout.write(ifidx)
|
|
self.hosts.append(host)
|
|
try:
|
|
info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
|
|
except Exception:
|
|
info = None
|
|
if info:
|
|
for line in info:
|
|
if line.startswith('deploy_server: ') or line.startswith('deploy_server_v6: '):
|
|
self.hosts.append(line.split(': ', 1)[1])
|
|
if plainhost and not self.hosts:
|
|
self.hosts.append(plainhost)
|
|
if self.phmac:
|
|
self.stdheaders['CONFLUENT_CRYPTHMAC'] = self.phmac
|
|
elif not checkonly:
|
|
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(nodename, self.hosts, errout=self.errout, hmackey=hmackey, confuuid=self.confuuid)
|
|
if mgtiface:
|
|
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
|
|
self.port = port
|
|
self.host = None
|
|
self.node = nodename
|
|
host = self.check_connections()
|
|
client.HTTPConnection.__init__(self, host, port)
|
|
self.connect()
|
|
|
|
|
|
def set_header(self, key, val):
|
|
self.stdheaders[key] = val
|
|
|
|
def check_connections(self):
|
|
foundsrv = None
|
|
hosts = self.hosts
|
|
ctx = ssl.SSLContext(_protoparm)
|
|
ctx.load_verify_locations('/etc/confluent/ca.pem')
|
|
ctx.verify_mode = ssl.CERT_REQUIRED
|
|
ctx.check_hostname = True
|
|
for timeo in (0.1, 5):
|
|
for host in hosts:
|
|
if host in self.ignorehosts:
|
|
continue
|
|
try:
|
|
addrinf = socket.getaddrinfo(host, self.port)[0]
|
|
psock = socket.socket(addrinf[0])
|
|
psock.settimeout(timeo)
|
|
psock.connect(addrinf[4])
|
|
chost = host.split('%', 1)[0]
|
|
ctx.wrap_socket(psock, server_hostname=chost)
|
|
foundsrv = host
|
|
psock.close()
|
|
break
|
|
except (OSError, socket.error):
|
|
continue
|
|
except ssl.SSLError:
|
|
continue
|
|
except ssl.CertificateError:
|
|
continue
|
|
else:
|
|
continue
|
|
break
|
|
if not foundsrv:
|
|
srvlist, srvs = scan_confluents(self.confuuid)
|
|
hosts = []
|
|
for srv in srvlist:
|
|
if srvs[srv].get('isdefault', False):
|
|
hosts = [srv] + hosts
|
|
else:
|
|
hosts = hosts + [srv]
|
|
for host in hosts:
|
|
try:
|
|
addrinf = socket.getaddrinfo(host, self.port)[0]
|
|
psock = socket.socket(addrinf[0])
|
|
psock.settimeout(timeo)
|
|
psock.connect(addrinf[4])
|
|
foundsrv = host
|
|
psock.close()
|
|
break
|
|
except OSError:
|
|
continue
|
|
else:
|
|
raise Exception('Unable to reach any hosts')
|
|
return foundsrv
|
|
|
|
def connect(self, tries=3):
|
|
addrinf = socket.getaddrinfo(self.host, self.port)[0]
|
|
psock = socket.socket(addrinf[0])
|
|
psock.settimeout(15)
|
|
psock.connect(addrinf[4])
|
|
ctx = ssl.SSLContext(_protoparm)
|
|
ctx.load_verify_locations('/etc/confluent/ca.pem')
|
|
host = self.host.split('%', 1)[0]
|
|
if '[' not in host and ':' in host:
|
|
self.stdheaders['Host'] = '[{0}]'.format(host)
|
|
else:
|
|
self.stdheaders['Host'] = '{0}'.format(host)
|
|
ctx.verify_mode = ssl.CERT_REQUIRED
|
|
ctx.check_hostname = True
|
|
try:
|
|
self.sock = ctx.wrap_socket(psock, server_hostname=host)
|
|
except ssl.SSLError:
|
|
errmsg = 'Error validating certificate on deployer (try `osdeploy initialize -t` on the deployment server {0})\n'.format(host)
|
|
sys.stderr.write(errmsg)
|
|
if self.errout:
|
|
self.errout.write(errmsg)
|
|
self.errout.flush()
|
|
sys.exit(1)
|
|
except socket.timeout:
|
|
if not tries:
|
|
raise
|
|
return self.connect(tries=tries-1)
|
|
|
|
def grab_url(self, url, data=None, returnrsp=False):
|
|
return self.grab_url_with_status(url, data, returnrsp)[1]
|
|
|
|
def grab_url_with_status(self, url, data=None, returnrsp=False):
|
|
if data:
|
|
method = 'POST'
|
|
else:
|
|
method = 'GET'
|
|
authed = False
|
|
while not authed:
|
|
authed = True
|
|
self.request(method, url, data, headers=self.stdheaders)
|
|
rsp = self.getresponse()
|
|
if rsp.status >= 200 and rsp.status < 300:
|
|
if returnrsp:
|
|
return rsp.status, rsp
|
|
else:
|
|
return rsp.status, rsp.read()
|
|
if rsp.status == 401:
|
|
authed = False
|
|
rsp.read()
|
|
with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
|
|
akfile.write('')
|
|
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
|
|
self.node, [self.host], errout=self.errout, hmackey=self.hmackey, confuuid=self.confuuid)
|
|
if rsp.status == 503: # confluent is down, but the server running confluent is otherwise up
|
|
authed = False
|
|
self.ignorehosts.add(self.host)
|
|
host = self.check_connections()
|
|
client.HTTPConnection.__init__(self, host, self.port)
|
|
|
|
raise Exception(rsp.read())
|
|
|
|
def get_current_vmnic_vswitch():
|
|
uplinkmatch = re.compile(r'^\s*Uplinks:\s*(.*)')
|
|
switchmatch = re.compile(r'^\s*Name:\s*(.*)')
|
|
|
|
vswinfo = subprocess.check_output(['localcli', 'network', 'vswitch', 'standard', 'list']).decode()
|
|
|
|
vmnic = None
|
|
vswitch_name = None
|
|
|
|
for info in vswinfo.split('\n'):
|
|
name_match = switchmatch.match(info)
|
|
if name_match:
|
|
vswitch_name = name_match.group(1).strip()
|
|
|
|
upinfo = uplinkmatch.match(info)
|
|
if upinfo:
|
|
vmnic = upinfo.group(1).strip()
|
|
if vmnic and 'vusb0' not in vmnic:
|
|
return vswitch_name, vmnic
|
|
|
|
return vswitch_name, vmnic
|
|
|
|
def get_available_nics():
|
|
nicinfo = subprocess.check_output(['localcli', 'network', 'nic', 'list']).decode('utf8').split('\n')
|
|
available_nics = {}
|
|
|
|
# Skip headers and separators
|
|
parsing_started = False
|
|
for line in nicinfo:
|
|
if re.match(r'^-+', line):
|
|
parsing_started = True
|
|
continue
|
|
if not parsing_started or not line.strip():
|
|
continue
|
|
|
|
parts = re.split(r'\s{2,}', line.strip())
|
|
if len(parts) >= 5:
|
|
nic_name = parts[0]
|
|
nic_status = parts[4] # "Link Status" is the 5th field
|
|
available_nics[nic_name] = nic_status
|
|
|
|
return available_nics
|
|
|
|
def is_esxi():
|
|
return os.path.isdir("/etc/vmware")
|
|
|
|
def fix_vswitch():
|
|
if is_esxi():
|
|
start_time = time.time()
|
|
while True:
|
|
|
|
current_vswitch, current_vmnic = get_current_vmnic_vswitch()
|
|
if current_vswitch is None:
|
|
raise RuntimeError("Panic: current vswitch is None")
|
|
if current_vmnic is None:
|
|
raise RuntimeError("Panic: current vmnic is None")
|
|
|
|
available_nics = get_available_nics()
|
|
|
|
if current_vmnic and available_nics.get(current_vmnic) == 'Up':
|
|
break
|
|
|
|
new_vmnic = next((nic for nic, status in available_nics.items() if status == 'Up'), None)
|
|
|
|
if new_vmnic and new_vmnic != current_vmnic:
|
|
subprocess.check_call(['localcli', 'network', 'vswitch', 'standard', 'uplink', 'remove',
|
|
'--uplink-name', current_vmnic, '--vswitch-name', current_vswitch])
|
|
subprocess.check_call(['localcli', 'network', 'vswitch', 'standard', 'uplink', 'add',
|
|
'--uplink-name', new_vmnic, '--vswitch-name', current_vswitch])
|
|
elif not new_vmnic:
|
|
if time.time() - start_time > 300:
|
|
break
|
|
time.sleep(5)
|
|
|
|
time.sleep(5)
|
|
|
|
if __name__ == '__main__':
|
|
data = None
|
|
|
|
if '-f' in sys.argv:
|
|
try:
|
|
fix_vswitch()
|
|
except Exception as e:
|
|
print("fix_vswitch() error: {}".format(e))
|
|
sys.argv.remove('-f')
|
|
sys.exit(0)
|
|
usejson = False
|
|
if '-j' in sys.argv:
|
|
usejson = True
|
|
if len(sys.argv) == 1:
|
|
HTTPSClient()
|
|
sys.exit(0)
|
|
try:
|
|
outbin = sys.argv.index('-o')
|
|
sys.argv.pop(outbin)
|
|
outbin = sys.argv.pop(outbin)
|
|
except ValueError:
|
|
outbin = None
|
|
try:
|
|
waitfor = sys.argv.index('-w')
|
|
sys.argv.pop(waitfor)
|
|
waitfor = int(sys.argv.pop(waitfor))
|
|
except ValueError:
|
|
waitfor = None
|
|
try:
|
|
data = sys.argv.index('-d')
|
|
sys.argv.pop(data)
|
|
data = sys.argv.pop(data)
|
|
except ValueError:
|
|
data = None
|
|
try:
|
|
errout = sys.argv.index('-e')
|
|
sys.argv.pop(errout)
|
|
errout = sys.argv.pop(errout)
|
|
except ValueError:
|
|
errout = None
|
|
try:
|
|
phmac = sys.argv.index('-p')
|
|
sys.argv.pop(phmac)
|
|
phmac = sys.argv.pop(phmac)
|
|
with open(phmac, 'r') as hmacin:
|
|
phmac = hmacin.read()
|
|
except ValueError:
|
|
phmac = None
|
|
try:
|
|
identfile = sys.argv.index('-i')
|
|
sys.argv.pop(identfile)
|
|
identfile = sys.argv.pop(identfile)
|
|
with open(identfile) as idin:
|
|
data = idin.read()
|
|
identinfo = json.loads(data)
|
|
nodename = identinfo.get('nodename', None)
|
|
hmackey = identinfo.get('apitoken', None)
|
|
confuuid = identinfo.get('confluent_uuid', None)
|
|
except ValueError:
|
|
hmackey = None
|
|
nodename = None
|
|
confuuid = None
|
|
try:
|
|
checkonly = False
|
|
idxit = sys.argv.index('-c')
|
|
sys.argv.pop(idxit)
|
|
checkonly = True
|
|
except Exception:
|
|
pass
|
|
if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
|
|
data = open(sys.argv[-1]).read()
|
|
if outbin:
|
|
with open(outbin, 'ab+') as outf:
|
|
reader = HTTPSClient(usejson=usejson, errout=errout, hmackey=hmackey, nodename=nodename, confuuid=confuuid).grab_url(
|
|
sys.argv[1], data, returnrsp=True)
|
|
chunk = reader.read(16384)
|
|
while chunk:
|
|
outf.write(chunk)
|
|
chunk = reader.read(16384)
|
|
sys.exit(0)
|
|
|
|
mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly, hmackey=hmackey, nodename=nodename, confuuid=confuuid)
|
|
if waitfor:
|
|
status = 201
|
|
while status != waitfor:
|
|
status, rsp = mclient.grab_url_with_status(sys.argv[1], data)
|
|
sys.stdout.write(rsp.decode())
|
|
elif checkonly:
|
|
sys.stdout.write(mclient.check_connections())
|
|
else:
|
|
try:
|
|
sys.stdout.buffer.write(mclient.grab_url(sys.argv[1], data))
|
|
except AttributeError:
|
|
sys.stdout.write(mclient.grab_url(sys.argv[1], data))
|