" % (ensure_new_type(self.faultCode),
+ ensure_new_type(self.faultString))
+
+# --------------------------------------------------------------------
+# Special values
+
+##
+# Backwards compatibility
+
+boolean = Boolean = bool
+
+##
+# Wrapper for XML-RPC DateTime values. This converts a time value to
+# the format used by XML-RPC.
+#
+# The value can be given as a datetime object, as a string in the
+# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
+# time.localtime()), or an integer value (as returned by time.time()).
+# The wrapper uses time.localtime() to convert an integer to a time
+# tuple.
+#
+# @param value The time, given as a datetime object, an ISO 8601 string,
+# a time tuple, or an integer time value.
+
+
+### For Python-Future:
+def _iso8601_format(value):
+ return "%04d%02d%02dT%02d:%02d:%02d" % (
+ value.year, value.month, value.day,
+ value.hour, value.minute, value.second)
+###
+# Issue #13305: different format codes across platforms
+# _day0 = datetime(1, 1, 1)
+# if _day0.strftime('%Y') == '0001': # Mac OS X
+# def _iso8601_format(value):
+# return value.strftime("%Y%m%dT%H:%M:%S")
+# elif _day0.strftime('%4Y') == '0001': # Linux
+# def _iso8601_format(value):
+# return value.strftime("%4Y%m%dT%H:%M:%S")
+# else:
+# def _iso8601_format(value):
+# return value.strftime("%Y%m%dT%H:%M:%S").zfill(17)
+# del _day0
+
+
+def _strftime(value):
+ if isinstance(value, datetime):
+ return _iso8601_format(value)
+
+ if not isinstance(value, (tuple, time.struct_time)):
+ if value == 0:
+ value = time.time()
+ value = time.localtime(value)
+
+ return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
+
+class DateTime(object):
+ """DateTime wrapper for an ISO 8601 string or time tuple or
+ localtime integer value to generate 'dateTime.iso8601' XML-RPC
+ value.
+ """
+
+ def __init__(self, value=0):
+ if isinstance(value, str):
+ self.value = value
+ else:
+ self.value = _strftime(value)
+
+ def make_comparable(self, other):
+ if isinstance(other, DateTime):
+ s = self.value
+ o = other.value
+ elif isinstance(other, datetime):
+ s = self.value
+ o = _iso8601_format(other)
+ elif isinstance(other, str):
+ s = self.value
+ o = other
+ elif hasattr(other, "timetuple"):
+ s = self.timetuple()
+ o = other.timetuple()
+ else:
+ otype = (hasattr(other, "__class__")
+ and other.__class__.__name__
+ or type(other))
+ raise TypeError("Can't compare %s and %s" %
+ (self.__class__.__name__, otype))
+ return s, o
+
+ def __lt__(self, other):
+ s, o = self.make_comparable(other)
+ return s < o
+
+ def __le__(self, other):
+ s, o = self.make_comparable(other)
+ return s <= o
+
+ def __gt__(self, other):
+ s, o = self.make_comparable(other)
+ return s > o
+
+ def __ge__(self, other):
+ s, o = self.make_comparable(other)
+ return s >= o
+
+ def __eq__(self, other):
+ s, o = self.make_comparable(other)
+ return s == o
+
+ def __ne__(self, other):
+ s, o = self.make_comparable(other)
+ return s != o
+
+ def timetuple(self):
+ return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
+
+ ##
+ # Get date/time value.
+ #
+ # @return Date/time value, as an ISO 8601 string.
+
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return "" % (ensure_new_type(self.value), id(self))
+
+ def decode(self, data):
+ self.value = str(data).strip()
+
+ def encode(self, out):
+ out.write("")
+ out.write(self.value)
+ out.write("\n")
+
+def _datetime(data):
+ # decode xml element contents into a DateTime structure.
+ value = DateTime()
+ value.decode(data)
+ return value
+
+def _datetime_type(data):
+ return datetime.strptime(data, "%Y%m%dT%H:%M:%S")
+
+##
+# Wrapper for binary data. This can be used to transport any kind
+# of binary data over XML-RPC, using BASE64 encoding.
+#
+# @param data An 8-bit string containing arbitrary data.
+
+class Binary(object):
+ """Wrapper for binary data."""
+
+ def __init__(self, data=None):
+ if data is None:
+ data = b""
+ else:
+ if not isinstance(data, (bytes, bytearray)):
+ raise TypeError("expected bytes or bytearray, not %s" %
+ data.__class__.__name__)
+ data = bytes(data) # Make a copy of the bytes!
+ self.data = data
+
+ ##
+ # Get buffer contents.
+ #
+ # @return Buffer contents, as an 8-bit string.
+
+ def __str__(self):
+ return str(self.data, "latin-1") # XXX encoding?!
+
+ def __eq__(self, other):
+ if isinstance(other, Binary):
+ other = other.data
+ return self.data == other
+
+ def __ne__(self, other):
+ if isinstance(other, Binary):
+ other = other.data
+ return self.data != other
+
+ def decode(self, data):
+ self.data = base64.decodebytes(data)
+
+ def encode(self, out):
+ out.write("\n")
+ encoded = base64.encodebytes(self.data)
+ out.write(encoded.decode('ascii'))
+ out.write("\n")
+
+def _binary(data):
+ # decode xml element contents into a Binary structure
+ value = Binary()
+ value.decode(data)
+ return value
+
+WRAPPERS = (DateTime, Binary)
+
+# --------------------------------------------------------------------
+# XML parsers
+
+class ExpatParser(object):
+ # fast expat parser for Python 2.0 and later.
+ def __init__(self, target):
+ self._parser = parser = expat.ParserCreate(None, None)
+ self._target = target
+ parser.StartElementHandler = target.start
+ parser.EndElementHandler = target.end
+ parser.CharacterDataHandler = target.data
+ encoding = None
+ target.xml(encoding, None)
+
+ def feed(self, data):
+ self._parser.Parse(data, 0)
+
+ def close(self):
+ self._parser.Parse("", 1) # end of data
+ del self._target, self._parser # get rid of circular references
+
+# --------------------------------------------------------------------
+# XML-RPC marshalling and unmarshalling code
+
+##
+# XML-RPC marshaller.
+#
+# @param encoding Default encoding for 8-bit strings. The default
+# value is None (interpreted as UTF-8).
+# @see dumps
+
+class Marshaller(object):
+ """Generate an XML-RPC params chunk from a Python data structure.
+
+ Create a Marshaller instance for each set of parameters, and use
+ the "dumps" method to convert your data (represented as a tuple)
+ to an XML-RPC params chunk. To write a fault response, pass a
+ Fault instance instead. You may prefer to use the "dumps" module
+ function for this purpose.
+ """
+
+ # by the way, if you don't understand what's going on in here,
+ # that's perfectly ok.
+
+ def __init__(self, encoding=None, allow_none=False):
+ self.memo = {}
+ self.data = None
+ self.encoding = encoding
+ self.allow_none = allow_none
+
+ dispatch = {}
+
+ def dumps(self, values):
+ out = []
+ write = out.append
+ dump = self.__dump
+ if isinstance(values, Fault):
+ # fault instance
+ write("\n")
+ dump({'faultCode': values.faultCode,
+ 'faultString': values.faultString},
+ write)
+ write("\n")
+ else:
+ # parameter block
+ # FIXME: the xml-rpc specification allows us to leave out
+ # the entire block if there are no parameters.
+ # however, changing this may break older code (including
+ # old versions of xmlrpclib.py), so this is better left as
+ # is for now. See @XMLRPC3 for more information. /F
+ write("\n")
+ for v in values:
+ write("\n")
+ dump(v, write)
+ write("\n")
+ write("\n")
+ result = "".join(out)
+ return str(result)
+
+ def __dump(self, value, write):
+ try:
+ f = self.dispatch[type(ensure_new_type(value))]
+ except KeyError:
+ # check if this object can be marshalled as a structure
+ if not hasattr(value, '__dict__'):
+ raise TypeError("cannot marshal %s objects" % type(value))
+ # check if this class is a sub-class of a basic type,
+ # because we don't know how to marshal these types
+ # (e.g. a string sub-class)
+ for type_ in type(value).__mro__:
+ if type_ in self.dispatch.keys():
+ raise TypeError("cannot marshal %s objects" % type(value))
+ # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
+ # for the p3yk merge, this should probably be fixed more neatly.
+ f = self.dispatch["_arbitrary_instance"]
+ f(self, value, write)
+
+ def dump_nil (self, value, write):
+ if not self.allow_none:
+ raise TypeError("cannot marshal None unless allow_none is enabled")
+ write("")
+ dispatch[type(None)] = dump_nil
+
+ def dump_bool(self, value, write):
+ write("")
+ write(value and "1" or "0")
+ write("\n")
+ dispatch[bool] = dump_bool
+
+ def dump_long(self, value, write):
+ if value > MAXINT or value < MININT:
+ raise OverflowError("long int exceeds XML-RPC limits")
+ write("")
+ write(str(int(value)))
+ write("\n")
+ dispatch[int] = dump_long
+
+ # backward compatible
+ dump_int = dump_long
+
+ def dump_double(self, value, write):
+ write("")
+ write(repr(ensure_new_type(value)))
+ write("\n")
+ dispatch[float] = dump_double
+
+ def dump_unicode(self, value, write, escape=escape):
+ write("")
+ write(escape(value))
+ write("\n")
+ dispatch[str] = dump_unicode
+
+ def dump_bytes(self, value, write):
+ write("\n")
+ encoded = base64.encodebytes(value)
+ write(encoded.decode('ascii'))
+ write("\n")
+ dispatch[bytes] = dump_bytes
+ dispatch[bytearray] = dump_bytes
+
+ def dump_array(self, value, write):
+ i = id(value)
+ if i in self.memo:
+ raise TypeError("cannot marshal recursive sequences")
+ self.memo[i] = None
+ dump = self.__dump
+ write("\n")
+ for v in value:
+ dump(v, write)
+ write("\n")
+ del self.memo[i]
+ dispatch[tuple] = dump_array
+ dispatch[list] = dump_array
+
+ def dump_struct(self, value, write, escape=escape):
+ i = id(value)
+ if i in self.memo:
+ raise TypeError("cannot marshal recursive dictionaries")
+ self.memo[i] = None
+ dump = self.__dump
+ write("\n")
+ for k, v in value.items():
+ write("\n")
+ if not isinstance(k, str):
+ raise TypeError("dictionary key must be string")
+ write("%s\n" % escape(k))
+ dump(v, write)
+ write("\n")
+ write("\n")
+ del self.memo[i]
+ dispatch[dict] = dump_struct
+
+ def dump_datetime(self, value, write):
+ write("")
+ write(_strftime(value))
+ write("\n")
+ dispatch[datetime] = dump_datetime
+
+ def dump_instance(self, value, write):
+ # check for special wrappers
+ if value.__class__ in WRAPPERS:
+ self.write = write
+ value.encode(self)
+ del self.write
+ else:
+ # store instance attributes as a struct (really?)
+ self.dump_struct(value.__dict__, write)
+ dispatch[DateTime] = dump_instance
+ dispatch[Binary] = dump_instance
+ # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
+ # for the p3yk merge, this should probably be fixed more neatly.
+ dispatch["_arbitrary_instance"] = dump_instance
+
+##
+# XML-RPC unmarshaller.
+#
+# @see loads
+
+class Unmarshaller(object):
+ """Unmarshal an XML-RPC response, based on incoming XML event
+ messages (start, data, end). Call close() to get the resulting
+ data structure.
+
+ Note that this reader is fairly tolerant, and gladly accepts bogus
+ XML-RPC data without complaining (but not bogus XML).
+ """
+
+ # and again, if you don't understand what's going on in here,
+ # that's perfectly ok.
+
+ def __init__(self, use_datetime=False, use_builtin_types=False):
+ self._type = None
+ self._stack = []
+ self._marks = []
+ self._data = []
+ self._methodname = None
+ self._encoding = "utf-8"
+ self.append = self._stack.append
+ self._use_datetime = use_builtin_types or use_datetime
+ self._use_bytes = use_builtin_types
+
+ def close(self):
+ # return response tuple and target method
+ if self._type is None or self._marks:
+ raise ResponseError()
+ if self._type == "fault":
+ raise Fault(**self._stack[0])
+ return tuple(self._stack)
+
+ def getmethodname(self):
+ return self._methodname
+
+ #
+ # event handlers
+
+ def xml(self, encoding, standalone):
+ self._encoding = encoding
+ # FIXME: assert standalone == 1 ???
+
+ def start(self, tag, attrs):
+ # prepare to handle this element
+ if tag == "array" or tag == "struct":
+ self._marks.append(len(self._stack))
+ self._data = []
+ self._value = (tag == "value")
+
+ def data(self, text):
+ self._data.append(text)
+
+ def end(self, tag):
+ # call the appropriate end tag handler
+ try:
+ f = self.dispatch[tag]
+ except KeyError:
+ pass # unknown tag ?
+ else:
+ return f(self, "".join(self._data))
+
+ #
+ # accelerator support
+
+ def end_dispatch(self, tag, data):
+ # dispatch data
+ try:
+ f = self.dispatch[tag]
+ except KeyError:
+ pass # unknown tag ?
+ else:
+ return f(self, data)
+
+ #
+ # element decoders
+
+ dispatch = {}
+
+ def end_nil (self, data):
+ self.append(None)
+ self._value = 0
+ dispatch["nil"] = end_nil
+
+ def end_boolean(self, data):
+ if data == "0":
+ self.append(False)
+ elif data == "1":
+ self.append(True)
+ else:
+ raise TypeError("bad boolean value")
+ self._value = 0
+ dispatch["boolean"] = end_boolean
+
+ def end_int(self, data):
+ self.append(int(data))
+ self._value = 0
+ dispatch["i4"] = end_int
+ dispatch["i8"] = end_int
+ dispatch["int"] = end_int
+
+ def end_double(self, data):
+ self.append(float(data))
+ self._value = 0
+ dispatch["double"] = end_double
+
+ def end_string(self, data):
+ if self._encoding:
+ data = data.decode(self._encoding)
+ self.append(data)
+ self._value = 0
+ dispatch["string"] = end_string
+ dispatch["name"] = end_string # struct keys are always strings
+
+ def end_array(self, data):
+ mark = self._marks.pop()
+ # map arrays to Python lists
+ self._stack[mark:] = [self._stack[mark:]]
+ self._value = 0
+ dispatch["array"] = end_array
+
+ def end_struct(self, data):
+ mark = self._marks.pop()
+ # map structs to Python dictionaries
+ dict = {}
+ items = self._stack[mark:]
+ for i in range(0, len(items), 2):
+ dict[items[i]] = items[i+1]
+ self._stack[mark:] = [dict]
+ self._value = 0
+ dispatch["struct"] = end_struct
+
+ def end_base64(self, data):
+ value = Binary()
+ value.decode(data.encode("ascii"))
+ if self._use_bytes:
+ value = value.data
+ self.append(value)
+ self._value = 0
+ dispatch["base64"] = end_base64
+
+ def end_dateTime(self, data):
+ value = DateTime()
+ value.decode(data)
+ if self._use_datetime:
+ value = _datetime_type(data)
+ self.append(value)
+ dispatch["dateTime.iso8601"] = end_dateTime
+
+ def end_value(self, data):
+ # if we stumble upon a value element with no internal
+ # elements, treat it as a string element
+ if self._value:
+ self.end_string(data)
+ dispatch["value"] = end_value
+
+ def end_params(self, data):
+ self._type = "params"
+ dispatch["params"] = end_params
+
+ def end_fault(self, data):
+ self._type = "fault"
+ dispatch["fault"] = end_fault
+
+ def end_methodName(self, data):
+ if self._encoding:
+ data = data.decode(self._encoding)
+ self._methodname = data
+ self._type = "methodName" # no params
+ dispatch["methodName"] = end_methodName
+
+## Multicall support
+#
+
+class _MultiCallMethod(object):
+ # some lesser magic to store calls made to a MultiCall object
+ # for batch execution
+ def __init__(self, call_list, name):
+ self.__call_list = call_list
+ self.__name = name
+ def __getattr__(self, name):
+ return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
+ def __call__(self, *args):
+ self.__call_list.append((self.__name, args))
+
+class MultiCallIterator(object):
+ """Iterates over the results of a multicall. Exceptions are
+ raised in response to xmlrpc faults."""
+
+ def __init__(self, results):
+ self.results = results
+
+ def __getitem__(self, i):
+ item = self.results[i]
+ if isinstance(type(item), dict):
+ raise Fault(item['faultCode'], item['faultString'])
+ elif type(item) == type([]):
+ return item[0]
+ else:
+ raise ValueError("unexpected type in multicall result")
+
+class MultiCall(object):
+ """server -> a object used to boxcar method calls
+
+ server should be a ServerProxy object.
+
+ Methods can be added to the MultiCall using normal
+ method call syntax e.g.:
+
+ multicall = MultiCall(server_proxy)
+ multicall.add(2,3)
+ multicall.get_address("Guido")
+
+ To execute the multicall, call the MultiCall object e.g.:
+
+ add_result, address = multicall()
+ """
+
+ def __init__(self, server):
+ self.__server = server
+ self.__call_list = []
+
+ def __repr__(self):
+ return "" % id(self)
+
+ __str__ = __repr__
+
+ def __getattr__(self, name):
+ return _MultiCallMethod(self.__call_list, name)
+
+ def __call__(self):
+ marshalled_list = []
+ for name, args in self.__call_list:
+ marshalled_list.append({'methodName' : name, 'params' : args})
+
+ return MultiCallIterator(self.__server.system.multicall(marshalled_list))
+
+# --------------------------------------------------------------------
+# convenience functions
+
+FastMarshaller = FastParser = FastUnmarshaller = None
+
+##
+# Create a parser object, and connect it to an unmarshalling instance.
+# This function picks the fastest available XML parser.
+#
+# return A (parser, unmarshaller) tuple.
+
+def getparser(use_datetime=False, use_builtin_types=False):
+ """getparser() -> parser, unmarshaller
+
+ Create an instance of the fastest available parser, and attach it
+ to an unmarshalling object. Return both objects.
+ """
+ if FastParser and FastUnmarshaller:
+ if use_builtin_types:
+ mkdatetime = _datetime_type
+ mkbytes = base64.decodebytes
+ elif use_datetime:
+ mkdatetime = _datetime_type
+ mkbytes = _binary
+ else:
+ mkdatetime = _datetime
+ mkbytes = _binary
+ target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault)
+ parser = FastParser(target)
+ else:
+ target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
+ if FastParser:
+ parser = FastParser(target)
+ else:
+ parser = ExpatParser(target)
+ return parser, target
+
+##
+# Convert a Python tuple or a Fault instance to an XML-RPC packet.
+#
+# @def dumps(params, **options)
+# @param params A tuple or Fault instance.
+# @keyparam methodname If given, create a methodCall request for
+# this method name.
+# @keyparam methodresponse If given, create a methodResponse packet.
+# If used with a tuple, the tuple must be a singleton (that is,
+# it must contain exactly one element).
+# @keyparam encoding The packet encoding.
+# @return A string containing marshalled data.
+
+def dumps(params, methodname=None, methodresponse=None, encoding=None,
+ allow_none=False):
+ """data [,options] -> marshalled data
+
+ Convert an argument tuple or a Fault instance to an XML-RPC
+ request (or response, if the methodresponse option is used).
+
+ In addition to the data object, the following options can be given
+ as keyword arguments:
+
+ methodname: the method name for a methodCall packet
+
+ methodresponse: true to create a methodResponse packet.
+ If this option is used with a tuple, the tuple must be
+ a singleton (i.e. it can contain only one element).
+
+ encoding: the packet encoding (default is UTF-8)
+
+ All byte strings in the data structure are assumed to use the
+ packet encoding. Unicode strings are automatically converted,
+ where necessary.
+ """
+
+ assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
+ if isinstance(params, Fault):
+ methodresponse = 1
+ elif methodresponse and isinstance(params, tuple):
+ assert len(params) == 1, "response tuple must be a singleton"
+
+ if not encoding:
+ encoding = "utf-8"
+
+ if FastMarshaller:
+ m = FastMarshaller(encoding)
+ else:
+ m = Marshaller(encoding, allow_none)
+
+ data = m.dumps(params)
+
+ if encoding != "utf-8":
+ xmlheader = "\n" % str(encoding)
+ else:
+ xmlheader = "\n" # utf-8 is default
+
+ # standard XML-RPC wrappings
+ if methodname:
+ # a method call
+ if not isinstance(methodname, str):
+ methodname = methodname.encode(encoding)
+ data = (
+ xmlheader,
+ "\n"
+ "", methodname, "\n",
+ data,
+ "\n"
+ )
+ elif methodresponse:
+ # a method response, or a fault structure
+ data = (
+ xmlheader,
+ "\n",
+ data,
+ "\n"
+ )
+ else:
+ return data # return as is
+ return str("").join(data)
+
+##
+# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
+# represents a fault condition, this function raises a Fault exception.
+#
+# @param data An XML-RPC packet, given as an 8-bit string.
+# @return A tuple containing the unpacked data, and the method name
+# (None if not present).
+# @see Fault
+
+def loads(data, use_datetime=False, use_builtin_types=False):
+ """data -> unmarshalled data, method name
+
+ Convert an XML-RPC packet to unmarshalled data plus a method
+ name (None if not present).
+
+ If the XML-RPC packet represents a fault condition, this function
+ raises a Fault exception.
+ """
+ p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
+ p.feed(data)
+ p.close()
+ return u.close(), u.getmethodname()
+
+##
+# Encode a string using the gzip content encoding such as specified by the
+# Content-Encoding: gzip
+# in the HTTP header, as described in RFC 1952
+#
+# @param data the unencoded data
+# @return the encoded data
+
+def gzip_encode(data):
+ """data -> gzip encoded data
+
+ Encode data using the gzip content encoding as described in RFC 1952
+ """
+ if not gzip:
+ raise NotImplementedError
+ f = BytesIO()
+ gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
+ gzf.write(data)
+ gzf.close()
+ encoded = f.getvalue()
+ f.close()
+ return encoded
+
+##
+# Decode a string using the gzip content encoding such as specified by the
+# Content-Encoding: gzip
+# in the HTTP header, as described in RFC 1952
+#
+# @param data The encoded data
+# @return the unencoded data
+# @raises ValueError if data is not correctly coded.
+
+def gzip_decode(data):
+ """gzip encoded data -> unencoded data
+
+ Decode data using the gzip content encoding as described in RFC 1952
+ """
+ if not gzip:
+ raise NotImplementedError
+ f = BytesIO(data)
+ gzf = gzip.GzipFile(mode="rb", fileobj=f)
+ try:
+ decoded = gzf.read()
+ except IOError:
+ raise ValueError("invalid data")
+ f.close()
+ gzf.close()
+ return decoded
+
+##
+# Return a decoded file-like object for the gzip encoding
+# as described in RFC 1952.
+#
+# @param response A stream supporting a read() method
+# @return a file-like object that the decoded data can be read() from
+
+class GzipDecodedResponse(gzip.GzipFile if gzip else object):
+ """a file-like object to decode a response encoded with the gzip
+ method, as described in RFC 1952.
+ """
+ def __init__(self, response):
+ #response doesn't support tell() and read(), required by
+ #GzipFile
+ if not gzip:
+ raise NotImplementedError
+ self.io = BytesIO(response.read())
+ gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
+
+ def close(self):
+ gzip.GzipFile.close(self)
+ self.io.close()
+
+
+# --------------------------------------------------------------------
+# request dispatcher
+
+class _Method(object):
+ # some magic to bind an XML-RPC method to an RPC server.
+ # supports "nested" methods (e.g. examples.getStateName)
+ def __init__(self, send, name):
+ self.__send = send
+ self.__name = name
+ def __getattr__(self, name):
+ return _Method(self.__send, "%s.%s" % (self.__name, name))
+ def __call__(self, *args):
+ return self.__send(self.__name, args)
+
+##
+# Standard transport class for XML-RPC over HTTP.
+#
+# You can create custom transports by subclassing this method, and
+# overriding selected methods.
+
+class Transport(object):
+ """Handles an HTTP transaction to an XML-RPC server."""
+
+ # client identifier (may be overridden)
+ user_agent = "Python-xmlrpc/%s" % __version__
+
+ #if true, we'll request gzip encoding
+ accept_gzip_encoding = True
+
+ # if positive, encode request using gzip if it exceeds this threshold
+ # note that many server will get confused, so only use it if you know
+ # that they can decode such a request
+ encode_threshold = None #None = don't encode
+
+ def __init__(self, use_datetime=False, use_builtin_types=False):
+ self._use_datetime = use_datetime
+ self._use_builtin_types = use_builtin_types
+ self._connection = (None, None)
+ self._extra_headers = []
+
+ ##
+ # Send a complete request, and parse the response.
+ # Retry request if a cached connection has disconnected.
+ #
+ # @param host Target host.
+ # @param handler Target PRC handler.
+ # @param request_body XML-RPC request body.
+ # @param verbose Debugging flag.
+ # @return Parsed response.
+
+ def request(self, host, handler, request_body, verbose=False):
+ #retry request once if cached connection has gone cold
+ for i in (0, 1):
+ try:
+ return self.single_request(host, handler, request_body, verbose)
+ except socket.error as e:
+ if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
+ raise
+ except http_client.BadStatusLine: #close after we sent request
+ if i:
+ raise
+
+ def single_request(self, host, handler, request_body, verbose=False):
+ # issue XML-RPC request
+ try:
+ http_conn = self.send_request(host, handler, request_body, verbose)
+ resp = http_conn.getresponse()
+ if resp.status == 200:
+ self.verbose = verbose
+ return self.parse_response(resp)
+
+ except Fault:
+ raise
+ except Exception:
+ #All unexpected errors leave connection in
+ # a strange state, so we clear it.
+ self.close()
+ raise
+
+ #We got an error response.
+ #Discard any response data and raise exception
+ if resp.getheader("content-length", ""):
+ resp.read()
+ raise ProtocolError(
+ host + handler,
+ resp.status, resp.reason,
+ dict(resp.getheaders())
+ )
+
+
+ ##
+ # Create parser.
+ #
+ # @return A 2-tuple containing a parser and a unmarshaller.
+
+ def getparser(self):
+ # get parser and unmarshaller
+ return getparser(use_datetime=self._use_datetime,
+ use_builtin_types=self._use_builtin_types)
+
+ ##
+ # Get authorization info from host parameter
+ # Host may be a string, or a (host, x509-dict) tuple; if a string,
+ # it is checked for a "user:pw@host" format, and a "Basic
+ # Authentication" header is added if appropriate.
+ #
+ # @param host Host descriptor (URL or (URL, x509 info) tuple).
+ # @return A 3-tuple containing (actual host, extra headers,
+ # x509 info). The header and x509 fields may be None.
+
+ def get_host_info(self, host):
+
+ x509 = {}
+ if isinstance(host, tuple):
+ host, x509 = host
+
+ auth, host = urllib_parse.splituser(host)
+
+ if auth:
+ auth = urllib_parse.unquote_to_bytes(auth)
+ auth = base64.encodebytes(auth).decode("utf-8")
+ auth = "".join(auth.split()) # get rid of whitespace
+ extra_headers = [
+ ("Authorization", "Basic " + auth)
+ ]
+ else:
+ extra_headers = []
+
+ return host, extra_headers, x509
+
+ ##
+ # Connect to server.
+ #
+ # @param host Target host.
+ # @return An HTTPConnection object
+
+ def make_connection(self, host):
+ #return an existing connection if possible. This allows
+ #HTTP/1.1 keep-alive.
+ if self._connection and host == self._connection[0]:
+ return self._connection[1]
+ # create a HTTP connection object from a host descriptor
+ chost, self._extra_headers, x509 = self.get_host_info(host)
+ self._connection = host, http_client.HTTPConnection(chost)
+ return self._connection[1]
+
+ ##
+ # Clear any cached connection object.
+ # Used in the event of socket errors.
+ #
+ def close(self):
+ if self._connection[1]:
+ self._connection[1].close()
+ self._connection = (None, None)
+
+ ##
+ # Send HTTP request.
+ #
+ # @param host Host descriptor (URL or (URL, x509 info) tuple).
+ # @param handler Target RPC handler (a path relative to host)
+ # @param request_body The XML-RPC request body
+ # @param debug Enable debugging if debug is true.
+ # @return An HTTPConnection.
+
+ def send_request(self, host, handler, request_body, debug):
+ connection = self.make_connection(host)
+ headers = self._extra_headers[:]
+ if debug:
+ connection.set_debuglevel(1)
+ if self.accept_gzip_encoding and gzip:
+ connection.putrequest("POST", handler, skip_accept_encoding=True)
+ headers.append(("Accept-Encoding", "gzip"))
+ else:
+ connection.putrequest("POST", handler)
+ headers.append(("Content-Type", "text/xml"))
+ headers.append(("User-Agent", self.user_agent))
+ self.send_headers(connection, headers)
+ self.send_content(connection, request_body)
+ return connection
+
+ ##
+ # Send request headers.
+ # This function provides a useful hook for subclassing
+ #
+ # @param connection httpConnection.
+ # @param headers list of key,value pairs for HTTP headers
+
+ def send_headers(self, connection, headers):
+ for key, val in headers:
+ connection.putheader(key, val)
+
+ ##
+ # Send request body.
+ # This function provides a useful hook for subclassing
+ #
+ # @param connection httpConnection.
+ # @param request_body XML-RPC request body.
+
+ def send_content(self, connection, request_body):
+ #optionally encode the request
+ if (self.encode_threshold is not None and
+ self.encode_threshold < len(request_body) and
+ gzip):
+ connection.putheader("Content-Encoding", "gzip")
+ request_body = gzip_encode(request_body)
+
+ connection.putheader("Content-Length", str(len(request_body)))
+ connection.endheaders(request_body)
+
+ ##
+ # Parse response.
+ #
+ # @param file Stream.
+ # @return Response tuple and target method.
+
+ def parse_response(self, response):
+ # read response data from httpresponse, and parse it
+ # Check for new http response object, otherwise it is a file object.
+ if hasattr(response, 'getheader'):
+ if response.getheader("Content-Encoding", "") == "gzip":
+ stream = GzipDecodedResponse(response)
+ else:
+ stream = response
+ else:
+ stream = response
+
+ p, u = self.getparser()
+
+ while 1:
+ data = stream.read(1024)
+ if not data:
+ break
+ if self.verbose:
+ print("body:", repr(data))
+ p.feed(data)
+
+ if stream is not response:
+ stream.close()
+ p.close()
+
+ return u.close()
+
+##
+# Standard transport class for XML-RPC over HTTPS.
+
+class SafeTransport(Transport):
+ """Handles an HTTPS transaction to an XML-RPC server."""
+
+ # FIXME: mostly untested
+
+ def make_connection(self, host):
+ if self._connection and host == self._connection[0]:
+ return self._connection[1]
+
+ if not hasattr(http_client, "HTTPSConnection"):
+ raise NotImplementedError(
+ "your version of http.client doesn't support HTTPS")
+ # create a HTTPS connection object from a host descriptor
+ # host may be a string, or a (host, x509-dict) tuple
+ chost, self._extra_headers, x509 = self.get_host_info(host)
+ self._connection = host, http_client.HTTPSConnection(chost,
+ None, **(x509 or {}))
+ return self._connection[1]
+
+##
+# Standard server proxy. This class establishes a virtual connection
+# to an XML-RPC server.
+#
+# This class is available as ServerProxy and Server. New code should
+# use ServerProxy, to avoid confusion.
+#
+# @def ServerProxy(uri, **options)
+# @param uri The connection point on the server.
+# @keyparam transport A transport factory, compatible with the
+# standard transport class.
+# @keyparam encoding The default encoding used for 8-bit strings
+# (default is UTF-8).
+# @keyparam verbose Use a true value to enable debugging output.
+# (printed to standard output).
+# @see Transport
+
+class ServerProxy(object):
+ """uri [,options] -> a logical connection to an XML-RPC server
+
+ uri is the connection point on the server, given as
+ scheme://host/target.
+
+ The standard implementation always supports the "http" scheme. If
+ SSL socket support is available (Python 2.0), it also supports
+ "https".
+
+ If the target part and the slash preceding it are both omitted,
+ "/RPC2" is assumed.
+
+ The following options can be given as keyword arguments:
+
+ transport: a transport factory
+ encoding: the request encoding (default is UTF-8)
+
+ All 8-bit strings passed to the server proxy are assumed to use
+ the given encoding.
+ """
+
+ def __init__(self, uri, transport=None, encoding=None, verbose=False,
+ allow_none=False, use_datetime=False, use_builtin_types=False):
+ # establish a "logical" server connection
+
+ # get the url
+ type, uri = urllib_parse.splittype(uri)
+ if type not in ("http", "https"):
+ raise IOError("unsupported XML-RPC protocol")
+ self.__host, self.__handler = urllib_parse.splithost(uri)
+ if not self.__handler:
+ self.__handler = "/RPC2"
+
+ if transport is None:
+ if type == "https":
+ handler = SafeTransport
+ else:
+ handler = Transport
+ transport = handler(use_datetime=use_datetime,
+ use_builtin_types=use_builtin_types)
+ self.__transport = transport
+
+ self.__encoding = encoding or 'utf-8'
+ self.__verbose = verbose
+ self.__allow_none = allow_none
+
+ def __close(self):
+ self.__transport.close()
+
+ def __request(self, methodname, params):
+ # call a method on the remote server
+
+ request = dumps(params, methodname, encoding=self.__encoding,
+ allow_none=self.__allow_none).encode(self.__encoding)
+
+ response = self.__transport.request(
+ self.__host,
+ self.__handler,
+ request,
+ verbose=self.__verbose
+ )
+
+ if len(response) == 1:
+ response = response[0]
+
+ return response
+
+ def __repr__(self):
+ return (
+ "" %
+ (self.__host, self.__handler)
+ )
+
+ __str__ = __repr__
+
+ def __getattr__(self, name):
+ # magic method dispatcher
+ return _Method(self.__request, name)
+
+ # note: to call a remote object with an non-standard name, use
+ # result getattr(server, "strange-python-name")(args)
+
+ def __call__(self, attr):
+ """A workaround to get special attributes on the ServerProxy
+ without interfering with the magic __getattr__
+ """
+ if attr == "close":
+ return self.__close
+ elif attr == "transport":
+ return self.__transport
+ raise AttributeError("Attribute %r not found" % (attr,))
+
+# compatibility
+
+Server = ServerProxy
+
+# --------------------------------------------------------------------
+# test code
+
+if __name__ == "__main__":
+
+ # simple test program (from the XML-RPC specification)
+
+ # local server, available from Lib/xmlrpc/server.py
+ server = ServerProxy("http://localhost:8000")
+
+ try:
+ print(server.currentTime.getCurrentTime())
+ except Error as v:
+ print("ERROR", v)
+
+ multi = MultiCall(server)
+ multi.getData()
+ multi.pow(2,9)
+ multi.add(1,2)
+ try:
+ for response in multi():
+ print(response)
+ except Error as v:
+ print("ERROR", v)
diff --git a/venv/lib/python3.10/site-packages/future/backports/xmlrpc/server.py b/venv/lib/python3.10/site-packages/future/backports/xmlrpc/server.py
new file mode 100644
index 0000000..28072bf
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/backports/xmlrpc/server.py
@@ -0,0 +1,999 @@
+r"""
+Ported using Python-Future from the Python 3.3 standard library.
+
+XML-RPC Servers.
+
+This module can be used to create simple XML-RPC servers
+by creating a server and either installing functions, a
+class instance, or by extending the SimpleXMLRPCServer
+class.
+
+It can also be used to handle XML-RPC requests in a CGI
+environment using CGIXMLRPCRequestHandler.
+
+The Doc* classes can be used to create XML-RPC servers that
+serve pydoc-style documentation in response to HTTP
+GET requests. This documentation is dynamically generated
+based on the functions and methods registered with the
+server.
+
+A list of possible usage patterns follows:
+
+1. Install functions:
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_function(pow)
+server.register_function(lambda x,y: x+y, 'add')
+server.serve_forever()
+
+2. Install an instance:
+
+class MyFuncs:
+ def __init__(self):
+ # make all of the sys functions available through sys.func_name
+ import sys
+ self.sys = sys
+ def _listMethods(self):
+ # implement this method so that system.listMethods
+ # knows to advertise the sys methods
+ return list_public_methods(self) + \
+ ['sys.' + method for method in list_public_methods(self.sys)]
+ def pow(self, x, y): return pow(x, y)
+ def add(self, x, y) : return x + y
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_introspection_functions()
+server.register_instance(MyFuncs())
+server.serve_forever()
+
+3. Install an instance with custom dispatch method:
+
+class Math:
+ def _listMethods(self):
+ # this method must be present for system.listMethods
+ # to work
+ return ['add', 'pow']
+ def _methodHelp(self, method):
+ # this method must be present for system.methodHelp
+ # to work
+ if method == 'add':
+ return "add(2,3) => 5"
+ elif method == 'pow':
+ return "pow(x, y[, z]) => number"
+ else:
+ # By convention, return empty
+ # string if no help is available
+ return ""
+ def _dispatch(self, method, params):
+ if method == 'pow':
+ return pow(*params)
+ elif method == 'add':
+ return params[0] + params[1]
+ else:
+ raise ValueError('bad method')
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_introspection_functions()
+server.register_instance(Math())
+server.serve_forever()
+
+4. Subclass SimpleXMLRPCServer:
+
+class MathServer(SimpleXMLRPCServer):
+ def _dispatch(self, method, params):
+ try:
+ # We are forcing the 'export_' prefix on methods that are
+ # callable through XML-RPC to prevent potential security
+ # problems
+ func = getattr(self, 'export_' + method)
+ except AttributeError:
+ raise Exception('method "%s" is not supported' % method)
+ else:
+ return func(*params)
+
+ def export_add(self, x, y):
+ return x + y
+
+server = MathServer(("localhost", 8000))
+server.serve_forever()
+
+5. CGI script:
+
+server = CGIXMLRPCRequestHandler()
+server.register_function(pow)
+server.handle_request()
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+from future.builtins import int, str
+
+# Written by Brian Quinlan (brian@sweetapp.com).
+# Based on code written by Fredrik Lundh.
+
+from future.backports.xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode
+from future.backports.http.server import BaseHTTPRequestHandler
+import future.backports.http.server as http_server
+from future.backports import socketserver
+import sys
+import os
+import re
+import pydoc
+import inspect
+import traceback
+try:
+ import fcntl
+except ImportError:
+ fcntl = None
+
+def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
+ """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
+
+ Resolves a dotted attribute name to an object. Raises
+ an AttributeError if any attribute in the chain starts with a '_'.
+
+ If the optional allow_dotted_names argument is false, dots are not
+ supported and this function operates similar to getattr(obj, attr).
+ """
+
+ if allow_dotted_names:
+ attrs = attr.split('.')
+ else:
+ attrs = [attr]
+
+ for i in attrs:
+ if i.startswith('_'):
+ raise AttributeError(
+ 'attempt to access private attribute "%s"' % i
+ )
+ else:
+ obj = getattr(obj,i)
+ return obj
+
+def list_public_methods(obj):
+ """Returns a list of attribute strings, found in the specified
+ object, which represent callable attributes"""
+
+ return [member for member in dir(obj)
+ if not member.startswith('_') and
+ callable(getattr(obj, member))]
+
+class SimpleXMLRPCDispatcher(object):
+ """Mix-in class that dispatches XML-RPC requests.
+
+ This class is used to register XML-RPC method handlers
+ and then to dispatch them. This class doesn't need to be
+ instanced directly when used by SimpleXMLRPCServer but it
+ can be instanced when used by the MultiPathXMLRPCServer
+ """
+
+ def __init__(self, allow_none=False, encoding=None,
+ use_builtin_types=False):
+ self.funcs = {}
+ self.instance = None
+ self.allow_none = allow_none
+ self.encoding = encoding or 'utf-8'
+ self.use_builtin_types = use_builtin_types
+
+ def register_instance(self, instance, allow_dotted_names=False):
+ """Registers an instance to respond to XML-RPC requests.
+
+ Only one instance can be installed at a time.
+
+ If the registered instance has a _dispatch method then that
+ method will be called with the name of the XML-RPC method and
+ its parameters as a tuple
+ e.g. instance._dispatch('add',(2,3))
+
+ If the registered instance does not have a _dispatch method
+ then the instance will be searched to find a matching method
+ and, if found, will be called. Methods beginning with an '_'
+ are considered private and will not be called by
+ SimpleXMLRPCServer.
+
+ If a registered function matches a XML-RPC request, then it
+ will be called instead of the registered instance.
+
+ If the optional allow_dotted_names argument is true and the
+ instance does not have a _dispatch method, method names
+ containing dots are supported and resolved, as long as none of
+ the name segments start with an '_'.
+
+ *** SECURITY WARNING: ***
+
+ Enabling the allow_dotted_names options allows intruders
+ to access your module's global variables and may allow
+ intruders to execute arbitrary code on your machine. Only
+ use this option on a secure, closed network.
+
+ """
+
+ self.instance = instance
+ self.allow_dotted_names = allow_dotted_names
+
+ def register_function(self, function, name=None):
+ """Registers a function to respond to XML-RPC requests.
+
+ The optional name argument can be used to set a Unicode name
+ for the function.
+ """
+
+ if name is None:
+ name = function.__name__
+ self.funcs[name] = function
+
+ def register_introspection_functions(self):
+ """Registers the XML-RPC introspection methods in the system
+ namespace.
+
+ see http://xmlrpc.usefulinc.com/doc/reserved.html
+ """
+
+ self.funcs.update({'system.listMethods' : self.system_listMethods,
+ 'system.methodSignature' : self.system_methodSignature,
+ 'system.methodHelp' : self.system_methodHelp})
+
+ def register_multicall_functions(self):
+ """Registers the XML-RPC multicall method in the system
+ namespace.
+
+ see http://www.xmlrpc.com/discuss/msgReader$1208"""
+
+ self.funcs.update({'system.multicall' : self.system_multicall})
+
+ def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
+ """Dispatches an XML-RPC method from marshalled (XML) data.
+
+ XML-RPC methods are dispatched from the marshalled (XML) data
+ using the _dispatch method and the result is returned as
+ marshalled data. For backwards compatibility, a dispatch
+ function can be provided as an argument (see comment in
+ SimpleXMLRPCRequestHandler.do_POST) but overriding the
+ existing method through subclassing is the preferred means
+ of changing method dispatch behavior.
+ """
+
+ try:
+ params, method = loads(data, use_builtin_types=self.use_builtin_types)
+
+ # generate response
+ if dispatch_method is not None:
+ response = dispatch_method(method, params)
+ else:
+ response = self._dispatch(method, params)
+ # wrap response in a singleton tuple
+ response = (response,)
+ response = dumps(response, methodresponse=1,
+ allow_none=self.allow_none, encoding=self.encoding)
+ except Fault as fault:
+ response = dumps(fault, allow_none=self.allow_none,
+ encoding=self.encoding)
+ except:
+ # report exception back to server
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ response = dumps(
+ Fault(1, "%s:%s" % (exc_type, exc_value)),
+ encoding=self.encoding, allow_none=self.allow_none,
+ )
+
+ return response.encode(self.encoding)
+
+ def system_listMethods(self):
+ """system.listMethods() => ['add', 'subtract', 'multiple']
+
+ Returns a list of the methods supported by the server."""
+
+ methods = set(self.funcs.keys())
+ if self.instance is not None:
+ # Instance can implement _listMethod to return a list of
+ # methods
+ if hasattr(self.instance, '_listMethods'):
+ methods |= set(self.instance._listMethods())
+ # if the instance has a _dispatch method then we
+ # don't have enough information to provide a list
+ # of methods
+ elif not hasattr(self.instance, '_dispatch'):
+ methods |= set(list_public_methods(self.instance))
+ return sorted(methods)
+
+ def system_methodSignature(self, method_name):
+ """system.methodSignature('add') => [double, int, int]
+
+ Returns a list describing the signature of the method. In the
+ above example, the add method takes two integers as arguments
+ and returns a double result.
+
+ This server does NOT support system.methodSignature."""
+
+ # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
+
+ return 'signatures not supported'
+
+ def system_methodHelp(self, method_name):
+ """system.methodHelp('add') => "Adds two integers together"
+
+ Returns a string containing documentation for the specified method."""
+
+ method = None
+ if method_name in self.funcs:
+ method = self.funcs[method_name]
+ elif self.instance is not None:
+ # Instance can implement _methodHelp to return help for a method
+ if hasattr(self.instance, '_methodHelp'):
+ return self.instance._methodHelp(method_name)
+ # if the instance has a _dispatch method then we
+ # don't have enough information to provide help
+ elif not hasattr(self.instance, '_dispatch'):
+ try:
+ method = resolve_dotted_attribute(
+ self.instance,
+ method_name,
+ self.allow_dotted_names
+ )
+ except AttributeError:
+ pass
+
+ # Note that we aren't checking that the method actually
+ # be a callable object of some kind
+ if method is None:
+ return ""
+ else:
+ return pydoc.getdoc(method)
+
+ def system_multicall(self, call_list):
+ """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
+[[4], ...]
+
+ Allows the caller to package multiple XML-RPC calls into a single
+ request.
+
+ See http://www.xmlrpc.com/discuss/msgReader$1208
+ """
+
+ results = []
+ for call in call_list:
+ method_name = call['methodName']
+ params = call['params']
+
+ try:
+ # XXX A marshalling error in any response will fail the entire
+ # multicall. If someone cares they should fix this.
+ results.append([self._dispatch(method_name, params)])
+ except Fault as fault:
+ results.append(
+ {'faultCode' : fault.faultCode,
+ 'faultString' : fault.faultString}
+ )
+ except:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ results.append(
+ {'faultCode' : 1,
+ 'faultString' : "%s:%s" % (exc_type, exc_value)}
+ )
+ return results
+
+ def _dispatch(self, method, params):
+ """Dispatches the XML-RPC method.
+
+ XML-RPC calls are forwarded to a registered function that
+ matches the called XML-RPC method name. If no such function
+ exists then the call is forwarded to the registered instance,
+ if available.
+
+ If the registered instance has a _dispatch method then that
+ method will be called with the name of the XML-RPC method and
+ its parameters as a tuple
+ e.g. instance._dispatch('add',(2,3))
+
+ If the registered instance does not have a _dispatch method
+ then the instance will be searched to find a matching method
+ and, if found, will be called.
+
+ Methods beginning with an '_' are considered private and will
+ not be called.
+ """
+
+ func = None
+ try:
+ # check to see if a matching function has been registered
+ func = self.funcs[method]
+ except KeyError:
+ if self.instance is not None:
+ # check for a _dispatch method
+ if hasattr(self.instance, '_dispatch'):
+ return self.instance._dispatch(method, params)
+ else:
+ # call instance method directly
+ try:
+ func = resolve_dotted_attribute(
+ self.instance,
+ method,
+ self.allow_dotted_names
+ )
+ except AttributeError:
+ pass
+
+ if func is not None:
+ return func(*params)
+ else:
+ raise Exception('method "%s" is not supported' % method)
+
+class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
+ """Simple XML-RPC request handler class.
+
+ Handles all HTTP POST requests and attempts to decode them as
+ XML-RPC requests.
+ """
+
+ # Class attribute listing the accessible path components;
+ # paths not on this list will result in a 404 error.
+ rpc_paths = ('/', '/RPC2')
+
+ #if not None, encode responses larger than this, if possible
+ encode_threshold = 1400 #a common MTU
+
+ #Override form StreamRequestHandler: full buffering of output
+ #and no Nagle.
+ wbufsize = -1
+ disable_nagle_algorithm = True
+
+ # a re to match a gzip Accept-Encoding
+ aepattern = re.compile(r"""
+ \s* ([^\s;]+) \s* #content-coding
+ (;\s* q \s*=\s* ([0-9\.]+))? #q
+ """, re.VERBOSE | re.IGNORECASE)
+
+ def accept_encodings(self):
+ r = {}
+ ae = self.headers.get("Accept-Encoding", "")
+ for e in ae.split(","):
+ match = self.aepattern.match(e)
+ if match:
+ v = match.group(3)
+ v = float(v) if v else 1.0
+ r[match.group(1)] = v
+ return r
+
+ def is_rpc_path_valid(self):
+ if self.rpc_paths:
+ return self.path in self.rpc_paths
+ else:
+ # If .rpc_paths is empty, just assume all paths are legal
+ return True
+
+ def do_POST(self):
+ """Handles the HTTP POST request.
+
+ Attempts to interpret all HTTP POST requests as XML-RPC calls,
+ which are forwarded to the server's _dispatch method for handling.
+ """
+
+ # Check that the path is legal
+ if not self.is_rpc_path_valid():
+ self.report_404()
+ return
+
+ try:
+ # Get arguments by reading body of request.
+ # We read this in chunks to avoid straining
+ # socket.read(); around the 10 or 15Mb mark, some platforms
+ # begin to have problems (bug #792570).
+ max_chunk_size = 10*1024*1024
+ size_remaining = int(self.headers["content-length"])
+ L = []
+ while size_remaining:
+ chunk_size = min(size_remaining, max_chunk_size)
+ chunk = self.rfile.read(chunk_size)
+ if not chunk:
+ break
+ L.append(chunk)
+ size_remaining -= len(L[-1])
+ data = b''.join(L)
+
+ data = self.decode_request_content(data)
+ if data is None:
+ return #response has been sent
+
+ # In previous versions of SimpleXMLRPCServer, _dispatch
+ # could be overridden in this class, instead of in
+ # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
+ # check to see if a subclass implements _dispatch and dispatch
+ # using that method if present.
+ response = self.server._marshaled_dispatch(
+ data, getattr(self, '_dispatch', None), self.path
+ )
+ except Exception as e: # This should only happen if the module is buggy
+ # internal error, report as HTTP server error
+ self.send_response(500)
+
+ # Send information about the exception if requested
+ if hasattr(self.server, '_send_traceback_header') and \
+ self.server._send_traceback_header:
+ self.send_header("X-exception", str(e))
+ trace = traceback.format_exc()
+ trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
+ self.send_header("X-traceback", trace)
+
+ self.send_header("Content-length", "0")
+ self.end_headers()
+ else:
+ self.send_response(200)
+ self.send_header("Content-type", "text/xml")
+ if self.encode_threshold is not None:
+ if len(response) > self.encode_threshold:
+ q = self.accept_encodings().get("gzip", 0)
+ if q:
+ try:
+ response = gzip_encode(response)
+ self.send_header("Content-Encoding", "gzip")
+ except NotImplementedError:
+ pass
+ self.send_header("Content-length", str(len(response)))
+ self.end_headers()
+ self.wfile.write(response)
+
+ def decode_request_content(self, data):
+ #support gzip encoding of request
+ encoding = self.headers.get("content-encoding", "identity").lower()
+ if encoding == "identity":
+ return data
+ if encoding == "gzip":
+ try:
+ return gzip_decode(data)
+ except NotImplementedError:
+ self.send_response(501, "encoding %r not supported" % encoding)
+ except ValueError:
+ self.send_response(400, "error decoding gzip content")
+ else:
+ self.send_response(501, "encoding %r not supported" % encoding)
+ self.send_header("Content-length", "0")
+ self.end_headers()
+
+ def report_404 (self):
+ # Report a 404 error
+ self.send_response(404)
+ response = b'No such page'
+ self.send_header("Content-type", "text/plain")
+ self.send_header("Content-length", str(len(response)))
+ self.end_headers()
+ self.wfile.write(response)
+
+ def log_request(self, code='-', size='-'):
+ """Selectively log an accepted request."""
+
+ if self.server.logRequests:
+ BaseHTTPRequestHandler.log_request(self, code, size)
+
+class SimpleXMLRPCServer(socketserver.TCPServer,
+ SimpleXMLRPCDispatcher):
+ """Simple XML-RPC server.
+
+ Simple XML-RPC server that allows functions and a single instance
+ to be installed to handle requests. The default implementation
+ attempts to dispatch XML-RPC calls to the functions or instance
+ installed in the server. Override the _dispatch method inherited
+ from SimpleXMLRPCDispatcher to change this behavior.
+ """
+
+ allow_reuse_address = True
+
+ # Warning: this is for debugging purposes only! Never set this to True in
+ # production code, as will be sending out sensitive information (exception
+ # and stack trace details) when exceptions are raised inside
+ # SimpleXMLRPCRequestHandler.do_POST
+ _send_traceback_header = False
+
+ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
+ logRequests=True, allow_none=False, encoding=None,
+ bind_and_activate=True, use_builtin_types=False):
+ self.logRequests = logRequests
+
+ SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
+ socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
+
+ # [Bug #1222790] If possible, set close-on-exec flag; if a
+ # method spawns a subprocess, the subprocess shouldn't have
+ # the listening socket open.
+ if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
+ flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
+ flags |= fcntl.FD_CLOEXEC
+ fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
+
+class MultiPathXMLRPCServer(SimpleXMLRPCServer):
+ """Multipath XML-RPC Server
+ This specialization of SimpleXMLRPCServer allows the user to create
+ multiple Dispatcher instances and assign them to different
+ HTTP request paths. This makes it possible to run two or more
+ 'virtual XML-RPC servers' at the same port.
+ Make sure that the requestHandler accepts the paths in question.
+ """
+ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
+ logRequests=True, allow_none=False, encoding=None,
+ bind_and_activate=True, use_builtin_types=False):
+
+ SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
+ encoding, bind_and_activate, use_builtin_types)
+ self.dispatchers = {}
+ self.allow_none = allow_none
+ self.encoding = encoding or 'utf-8'
+
+ def add_dispatcher(self, path, dispatcher):
+ self.dispatchers[path] = dispatcher
+ return dispatcher
+
+ def get_dispatcher(self, path):
+ return self.dispatchers[path]
+
+ def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
+ try:
+ response = self.dispatchers[path]._marshaled_dispatch(
+ data, dispatch_method, path)
+ except:
+ # report low level exception back to server
+ # (each dispatcher should have handled their own
+ # exceptions)
+ exc_type, exc_value = sys.exc_info()[:2]
+ response = dumps(
+ Fault(1, "%s:%s" % (exc_type, exc_value)),
+ encoding=self.encoding, allow_none=self.allow_none)
+ response = response.encode(self.encoding)
+ return response
+
+class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
+ """Simple handler for XML-RPC data passed through CGI."""
+
+ def __init__(self, allow_none=False, encoding=None, use_builtin_types=False):
+ SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
+
+ def handle_xmlrpc(self, request_text):
+ """Handle a single XML-RPC request"""
+
+ response = self._marshaled_dispatch(request_text)
+
+ print('Content-Type: text/xml')
+ print('Content-Length: %d' % len(response))
+ print()
+ sys.stdout.flush()
+ sys.stdout.buffer.write(response)
+ sys.stdout.buffer.flush()
+
+ def handle_get(self):
+ """Handle a single HTTP GET request.
+
+ Default implementation indicates an error because
+ XML-RPC uses the POST method.
+ """
+
+ code = 400
+ message, explain = BaseHTTPRequestHandler.responses[code]
+
+ response = http_server.DEFAULT_ERROR_MESSAGE % \
+ {
+ 'code' : code,
+ 'message' : message,
+ 'explain' : explain
+ }
+ response = response.encode('utf-8')
+ print('Status: %d %s' % (code, message))
+ print('Content-Type: %s' % http_server.DEFAULT_ERROR_CONTENT_TYPE)
+ print('Content-Length: %d' % len(response))
+ print()
+ sys.stdout.flush()
+ sys.stdout.buffer.write(response)
+ sys.stdout.buffer.flush()
+
+ def handle_request(self, request_text=None):
+ """Handle a single XML-RPC request passed through a CGI post method.
+
+ If no XML data is given then it is read from stdin. The resulting
+ XML-RPC response is printed to stdout along with the correct HTTP
+ headers.
+ """
+
+ if request_text is None and \
+ os.environ.get('REQUEST_METHOD', None) == 'GET':
+ self.handle_get()
+ else:
+ # POST data is normally available through stdin
+ try:
+ length = int(os.environ.get('CONTENT_LENGTH', None))
+ except (ValueError, TypeError):
+ length = -1
+ if request_text is None:
+ request_text = sys.stdin.read(length)
+
+ self.handle_xmlrpc(request_text)
+
+
+# -----------------------------------------------------------------------------
+# Self documenting XML-RPC Server.
+
+class ServerHTMLDoc(pydoc.HTMLDoc):
+ """Class used to generate pydoc HTML document for a server"""
+
+ def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
+ """Mark up some plain text, given a context of symbols to look for.
+ Each context dictionary maps object names to anchor names."""
+ escape = escape or self.escape
+ results = []
+ here = 0
+
+ # XXX Note that this regular expression does not allow for the
+ # hyperlinking of arbitrary strings being used as method
+ # names. Only methods with names consisting of word characters
+ # and '.'s are hyperlinked.
+ pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
+ r'RFC[- ]?(\d+)|'
+ r'PEP[- ]?(\d+)|'
+ r'(self\.)?((?:\w|\.)+))\b')
+ while 1:
+ match = pattern.search(text, here)
+ if not match: break
+ start, end = match.span()
+ results.append(escape(text[here:start]))
+
+ all, scheme, rfc, pep, selfdot, name = match.groups()
+ if scheme:
+ url = escape(all).replace('"', '"')
+ results.append('%s' % (url, url))
+ elif rfc:
+ url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
+ results.append('%s' % (url, escape(all)))
+ elif pep:
+ url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
+ results.append('%s' % (url, escape(all)))
+ elif text[end:end+1] == '(':
+ results.append(self.namelink(name, methods, funcs, classes))
+ elif selfdot:
+ results.append('self.%s' % name)
+ else:
+ results.append(self.namelink(name, classes))
+ here = end
+ results.append(escape(text[here:]))
+ return ''.join(results)
+
+ def docroutine(self, object, name, mod=None,
+ funcs={}, classes={}, methods={}, cl=None):
+ """Produce HTML documentation for a function or method object."""
+
+ anchor = (cl and cl.__name__ or '') + '-' + name
+ note = ''
+
+ title = '%s' % (
+ self.escape(anchor), self.escape(name))
+
+ if inspect.ismethod(object):
+ args = inspect.getfullargspec(object)
+ # exclude the argument bound to the instance, it will be
+ # confusing to the non-Python user
+ argspec = inspect.formatargspec (
+ args.args[1:],
+ args.varargs,
+ args.varkw,
+ args.defaults,
+ annotations=args.annotations,
+ formatvalue=self.formatvalue
+ )
+ elif inspect.isfunction(object):
+ args = inspect.getfullargspec(object)
+ argspec = inspect.formatargspec(
+ args.args, args.varargs, args.varkw, args.defaults,
+ annotations=args.annotations,
+ formatvalue=self.formatvalue)
+ else:
+ argspec = '(...)'
+
+ if isinstance(object, tuple):
+ argspec = object[0] or argspec
+ docstring = object[1] or ""
+ else:
+ docstring = pydoc.getdoc(object)
+
+ decl = title + argspec + (note and self.grey(
+ '%s' % note))
+
+ doc = self.markup(
+ docstring, self.preformat, funcs, classes, methods)
+ doc = doc and '%s' % doc
+ return '- %s
%s
\n' % (decl, doc)
+
+ def docserver(self, server_name, package_documentation, methods):
+ """Produce HTML documentation for an XML-RPC server."""
+
+ fdict = {}
+ for key, value in methods.items():
+ fdict[key] = '#-' + key
+ fdict[value] = fdict[key]
+
+ server_name = self.escape(server_name)
+ head = '%s' % server_name
+ result = self.heading(head, '#ffffff', '#7799ee')
+
+ doc = self.markup(package_documentation, self.preformat, fdict)
+ doc = doc and '%s' % doc
+ result = result + '%s
\n' % doc
+
+ contents = []
+ method_items = sorted(methods.items())
+ for key, value in method_items:
+ contents.append(self.docroutine(value, key, funcs=fdict))
+ result = result + self.bigsection(
+ 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
+
+ return result
+
+class XMLRPCDocGenerator(object):
+ """Generates documentation for an XML-RPC server.
+
+ This class is designed as mix-in and should not
+ be constructed directly.
+ """
+
+ def __init__(self):
+ # setup variables used for HTML documentation
+ self.server_name = 'XML-RPC Server Documentation'
+ self.server_documentation = \
+ "This server exports the following methods through the XML-RPC "\
+ "protocol."
+ self.server_title = 'XML-RPC Server Documentation'
+
+ def set_server_title(self, server_title):
+ """Set the HTML title of the generated server documentation"""
+
+ self.server_title = server_title
+
+ def set_server_name(self, server_name):
+ """Set the name of the generated HTML server documentation"""
+
+ self.server_name = server_name
+
+ def set_server_documentation(self, server_documentation):
+ """Set the documentation string for the entire server."""
+
+ self.server_documentation = server_documentation
+
+ def generate_html_documentation(self):
+ """generate_html_documentation() => html documentation for the server
+
+ Generates HTML documentation for the server using introspection for
+ installed functions and instances that do not implement the
+ _dispatch method. Alternatively, instances can choose to implement
+ the _get_method_argstring(method_name) method to provide the
+ argument string used in the documentation and the
+ _methodHelp(method_name) method to provide the help text used
+ in the documentation."""
+
+ methods = {}
+
+ for method_name in self.system_listMethods():
+ if method_name in self.funcs:
+ method = self.funcs[method_name]
+ elif self.instance is not None:
+ method_info = [None, None] # argspec, documentation
+ if hasattr(self.instance, '_get_method_argstring'):
+ method_info[0] = self.instance._get_method_argstring(method_name)
+ if hasattr(self.instance, '_methodHelp'):
+ method_info[1] = self.instance._methodHelp(method_name)
+
+ method_info = tuple(method_info)
+ if method_info != (None, None):
+ method = method_info
+ elif not hasattr(self.instance, '_dispatch'):
+ try:
+ method = resolve_dotted_attribute(
+ self.instance,
+ method_name
+ )
+ except AttributeError:
+ method = method_info
+ else:
+ method = method_info
+ else:
+ assert 0, "Could not find method in self.functions and no "\
+ "instance installed"
+
+ methods[method_name] = method
+
+ documenter = ServerHTMLDoc()
+ documentation = documenter.docserver(
+ self.server_name,
+ self.server_documentation,
+ methods
+ )
+
+ return documenter.page(self.server_title, documentation)
+
+class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
+ """XML-RPC and documentation request handler class.
+
+ Handles all HTTP POST requests and attempts to decode them as
+ XML-RPC requests.
+
+ Handles all HTTP GET requests and interprets them as requests
+ for documentation.
+ """
+
+ def do_GET(self):
+ """Handles the HTTP GET request.
+
+ Interpret all HTTP GET requests as requests for server
+ documentation.
+ """
+ # Check that the path is legal
+ if not self.is_rpc_path_valid():
+ self.report_404()
+ return
+
+ response = self.server.generate_html_documentation().encode('utf-8')
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.send_header("Content-length", str(len(response)))
+ self.end_headers()
+ self.wfile.write(response)
+
+class DocXMLRPCServer( SimpleXMLRPCServer,
+ XMLRPCDocGenerator):
+ """XML-RPC and HTML documentation server.
+
+ Adds the ability to serve server documentation to the capabilities
+ of SimpleXMLRPCServer.
+ """
+
+ def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
+ logRequests=True, allow_none=False, encoding=None,
+ bind_and_activate=True, use_builtin_types=False):
+ SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
+ allow_none, encoding, bind_and_activate,
+ use_builtin_types)
+ XMLRPCDocGenerator.__init__(self)
+
+class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
+ XMLRPCDocGenerator):
+ """Handler for XML-RPC data and documentation requests passed through
+ CGI"""
+
+ def handle_get(self):
+ """Handles the HTTP GET request.
+
+ Interpret all HTTP GET requests as requests for server
+ documentation.
+ """
+
+ response = self.generate_html_documentation().encode('utf-8')
+
+ print('Content-Type: text/html')
+ print('Content-Length: %d' % len(response))
+ print()
+ sys.stdout.flush()
+ sys.stdout.buffer.write(response)
+ sys.stdout.buffer.flush()
+
+ def __init__(self):
+ CGIXMLRPCRequestHandler.__init__(self)
+ XMLRPCDocGenerator.__init__(self)
+
+
+if __name__ == '__main__':
+ import datetime
+
+ class ExampleService:
+ def getData(self):
+ return '42'
+
+ class currentTime:
+ @staticmethod
+ def getCurrentTime():
+ return datetime.datetime.now()
+
+ server = SimpleXMLRPCServer(("localhost", 8000))
+ server.register_function(pow)
+ server.register_function(lambda x,y: x+y, 'add')
+ server.register_instance(ExampleService(), allow_dotted_names=True)
+ server.register_multicall_functions()
+ print('Serving XML-RPC on localhost port 8000')
+ print('It is advisable to run this example server within a secure, closed network.')
+ try:
+ server.serve_forever()
+ except KeyboardInterrupt:
+ print("\nKeyboard interrupt received, exiting.")
+ server.server_close()
+ sys.exit(0)
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__init__.py b/venv/lib/python3.10/site-packages/future/builtins/__init__.py
new file mode 100644
index 0000000..1734cd4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/__init__.py
@@ -0,0 +1,51 @@
+"""
+A module that brings in equivalents of the new and modified Python 3
+builtins into Py2. Has no effect on Py3.
+
+See the docs `here `_
+(``docs/what-else.rst``) for more information.
+
+"""
+
+from future.builtins.iterators import (filter, map, zip)
+# The isinstance import is no longer needed. We provide it only for
+# backward-compatibility with future v0.8.2. It will be removed in future v1.0.
+from future.builtins.misc import (ascii, chr, hex, input, isinstance, next,
+ oct, open, pow, round, super, max, min)
+from future.utils import PY3
+
+if PY3:
+ import builtins
+ bytes = builtins.bytes
+ dict = builtins.dict
+ int = builtins.int
+ list = builtins.list
+ object = builtins.object
+ range = builtins.range
+ str = builtins.str
+ __all__ = []
+else:
+ from future.types import (newbytes as bytes,
+ newdict as dict,
+ newint as int,
+ newlist as list,
+ newobject as object,
+ newrange as range,
+ newstr as str)
+from future import utils
+
+
+if not utils.PY3:
+ # We only import names that shadow the builtins on Py2. No other namespace
+ # pollution on Py2.
+
+ # Only shadow builtins on Py2; no new names
+ __all__ = ['filter', 'map', 'zip',
+ 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow',
+ 'round', 'super',
+ 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', 'max', 'min'
+ ]
+
+else:
+ # No namespace pollution on Py3
+ __all__ = []
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..4737101
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/disabled.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/disabled.cpython-310.pyc
new file mode 100644
index 0000000..c2cadef
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/disabled.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/iterators.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/iterators.cpython-310.pyc
new file mode 100644
index 0000000..aa95575
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/iterators.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/misc.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/misc.cpython-310.pyc
new file mode 100644
index 0000000..fb2dc37
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/misc.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/new_min_max.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/new_min_max.cpython-310.pyc
new file mode 100644
index 0000000..e268a83
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/new_min_max.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newnext.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newnext.cpython-310.pyc
new file mode 100644
index 0000000..f81a6d1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newnext.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newround.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newround.cpython-310.pyc
new file mode 100644
index 0000000..1a02e7e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newround.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newsuper.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newsuper.cpython-310.pyc
new file mode 100644
index 0000000..0d30278
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/builtins/__pycache__/newsuper.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/builtins/disabled.py b/venv/lib/python3.10/site-packages/future/builtins/disabled.py
new file mode 100644
index 0000000..f6d6ea9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/disabled.py
@@ -0,0 +1,66 @@
+"""
+This disables builtin functions (and one exception class) which are
+removed from Python 3.3.
+
+This module is designed to be used like this::
+
+ from future.builtins.disabled import *
+
+This disables the following obsolete Py2 builtin functions::
+
+ apply, cmp, coerce, execfile, file, input, long,
+ raw_input, reduce, reload, unicode, xrange
+
+We don't hack __builtin__, which is very fragile because it contaminates
+imported modules too. Instead, we just create new functions with
+the same names as the obsolete builtins from Python 2 which raise
+NameError exceptions when called.
+
+Note that both ``input()`` and ``raw_input()`` are among the disabled
+functions (in this module). Although ``input()`` exists as a builtin in
+Python 3, the Python 2 ``input()`` builtin is unsafe to use because it
+can lead to shell injection. Therefore we shadow it by default upon ``from
+future.builtins.disabled import *``, in case someone forgets to import our
+replacement ``input()`` somehow and expects Python 3 semantics.
+
+See the ``future.builtins.misc`` module for a working version of
+``input`` with Python 3 semantics.
+
+(Note that callable() is not among the functions disabled; this was
+reintroduced into Python 3.2.)
+
+This exception class is also disabled:
+
+ StandardError
+
+"""
+
+from __future__ import division, absolute_import, print_function
+
+from future import utils
+
+
+OBSOLETE_BUILTINS = ['apply', 'chr', 'cmp', 'coerce', 'execfile', 'file',
+ 'input', 'long', 'raw_input', 'reduce', 'reload',
+ 'unicode', 'xrange', 'StandardError']
+
+
+def disabled_function(name):
+ '''
+ Returns a function that cannot be called
+ '''
+ def disabled(*args, **kwargs):
+ '''
+ A function disabled by the ``future`` module. This function is
+ no longer a builtin in Python 3.
+ '''
+ raise NameError('obsolete Python 2 builtin {0} is disabled'.format(name))
+ return disabled
+
+
+if not utils.PY3:
+ for fname in OBSOLETE_BUILTINS:
+ locals()[fname] = disabled_function(fname)
+ __all__ = OBSOLETE_BUILTINS
+else:
+ __all__ = []
diff --git a/venv/lib/python3.10/site-packages/future/builtins/iterators.py b/venv/lib/python3.10/site-packages/future/builtins/iterators.py
new file mode 100644
index 0000000..dff651e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/iterators.py
@@ -0,0 +1,52 @@
+"""
+This module is designed to be used as follows::
+
+ from future.builtins.iterators import *
+
+And then, for example::
+
+ for i in range(10**15):
+ pass
+
+ for (a, b) in zip(range(10**15), range(-10**15, 0)):
+ pass
+
+Note that this is standard Python 3 code, plus some imports that do
+nothing on Python 3.
+
+The iterators this brings in are::
+
+- ``range``
+- ``filter``
+- ``map``
+- ``zip``
+
+On Python 2, ``range`` is a pure-Python backport of Python 3's ``range``
+iterator with slicing support. The other iterators (``filter``, ``map``,
+``zip``) are from the ``itertools`` module on Python 2. On Python 3 these
+are available in the module namespace but not exported for * imports via
+__all__ (zero no namespace pollution).
+
+Note that these are also available in the standard library
+``future_builtins`` module on Python 2 -- but not Python 3, so using
+the standard library version is not portable, nor anywhere near complete.
+"""
+
+from __future__ import division, absolute_import, print_function
+
+import itertools
+from future import utils
+
+if not utils.PY3:
+ filter = itertools.ifilter
+ map = itertools.imap
+ from future.types import newrange as range
+ zip = itertools.izip
+ __all__ = ['filter', 'map', 'range', 'zip']
+else:
+ import builtins
+ filter = builtins.filter
+ map = builtins.map
+ range = builtins.range
+ zip = builtins.zip
+ __all__ = []
diff --git a/venv/lib/python3.10/site-packages/future/builtins/misc.py b/venv/lib/python3.10/site-packages/future/builtins/misc.py
new file mode 100644
index 0000000..f86ce5f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/misc.py
@@ -0,0 +1,135 @@
+"""
+A module that brings in equivalents of various modified Python 3 builtins
+into Py2. Has no effect on Py3.
+
+The builtin functions are:
+
+- ``ascii`` (from Py2's future_builtins module)
+- ``hex`` (from Py2's future_builtins module)
+- ``oct`` (from Py2's future_builtins module)
+- ``chr`` (equivalent to ``unichr`` on Py2)
+- ``input`` (equivalent to ``raw_input`` on Py2)
+- ``next`` (calls ``__next__`` if it exists, else ``next`` method)
+- ``open`` (equivalent to io.open on Py2)
+- ``super`` (backport of Py3's magic zero-argument super() function
+- ``round`` (new "Banker's Rounding" behaviour from Py3)
+- ``max`` (new default option from Py3.4)
+- ``min`` (new default option from Py3.4)
+
+``isinstance`` is also currently exported for backwards compatibility
+with v0.8.2, although this has been deprecated since v0.9.
+
+
+input()
+-------
+Like the new ``input()`` function from Python 3 (without eval()), except
+that it returns bytes. Equivalent to Python 2's ``raw_input()``.
+
+Warning: By default, importing this module *removes* the old Python 2
+input() function entirely from ``__builtin__`` for safety. This is
+because forgetting to import the new ``input`` from ``future`` might
+otherwise lead to a security vulnerability (shell injection) on Python 2.
+
+To restore it, you can retrieve it yourself from
+``__builtin__._old_input``.
+
+Fortunately, ``input()`` seems to be seldom used in the wild in Python
+2...
+
+"""
+
+from future import utils
+
+
+if utils.PY2:
+ from io import open
+ from future_builtins import ascii, oct, hex
+ from __builtin__ import unichr as chr, pow as _builtin_pow
+ import __builtin__
+
+ # Only for backward compatibility with future v0.8.2:
+ isinstance = __builtin__.isinstance
+
+ # Warning: Python 2's input() is unsafe and MUST not be able to be used
+ # accidentally by someone who expects Python 3 semantics but forgets
+ # to import it on Python 2. Versions of ``future`` prior to 0.11
+ # deleted it from __builtin__. Now we keep in __builtin__ but shadow
+ # the name like all others. Just be sure to import ``input``.
+
+ input = raw_input
+
+ from future.builtins.newnext import newnext as next
+ from future.builtins.newround import newround as round
+ from future.builtins.newsuper import newsuper as super
+ from future.builtins.new_min_max import newmax as max
+ from future.builtins.new_min_max import newmin as min
+ from future.types.newint import newint
+
+ _SENTINEL = object()
+
+ def pow(x, y, z=_SENTINEL):
+ """
+ pow(x, y[, z]) -> number
+
+ With two arguments, equivalent to x**y. With three arguments,
+ equivalent to (x**y) % z, but may be more efficient (e.g. for ints).
+ """
+ # Handle newints
+ if isinstance(x, newint):
+ x = long(x)
+ if isinstance(y, newint):
+ y = long(y)
+ if isinstance(z, newint):
+ z = long(z)
+
+ try:
+ if z == _SENTINEL:
+ return _builtin_pow(x, y)
+ else:
+ return _builtin_pow(x, y, z)
+ except ValueError:
+ if z == _SENTINEL:
+ return _builtin_pow(x+0j, y)
+ else:
+ return _builtin_pow(x+0j, y, z)
+
+
+ # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this:
+ # callable = __builtin__.callable
+
+ __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct',
+ 'open', 'pow', 'round', 'super', 'max', 'min']
+
+else:
+ import builtins
+ ascii = builtins.ascii
+ chr = builtins.chr
+ hex = builtins.hex
+ input = builtins.input
+ next = builtins.next
+ # Only for backward compatibility with future v0.8.2:
+ isinstance = builtins.isinstance
+ oct = builtins.oct
+ open = builtins.open
+ pow = builtins.pow
+ round = builtins.round
+ super = builtins.super
+ if utils.PY34_PLUS:
+ max = builtins.max
+ min = builtins.min
+ __all__ = []
+ else:
+ from future.builtins.new_min_max import newmax as max
+ from future.builtins.new_min_max import newmin as min
+ __all__ = ['min', 'max']
+
+ # The callable() function was removed from Py3.0 and 3.1 and
+ # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever
+ # did, we'd add this:
+ # try:
+ # callable = builtins.callable
+ # except AttributeError:
+ # # Definition from Pandas
+ # def callable(obj):
+ # return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+ # __all__.append('callable')
diff --git a/venv/lib/python3.10/site-packages/future/builtins/new_min_max.py b/venv/lib/python3.10/site-packages/future/builtins/new_min_max.py
new file mode 100644
index 0000000..6f0c2a8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/new_min_max.py
@@ -0,0 +1,59 @@
+import itertools
+
+from future import utils
+if utils.PY2:
+ from __builtin__ import max as _builtin_max, min as _builtin_min
+else:
+ from builtins import max as _builtin_max, min as _builtin_min
+
+_SENTINEL = object()
+
+
+def newmin(*args, **kwargs):
+ return new_min_max(_builtin_min, *args, **kwargs)
+
+
+def newmax(*args, **kwargs):
+ return new_min_max(_builtin_max, *args, **kwargs)
+
+
+def new_min_max(_builtin_func, *args, **kwargs):
+ """
+ To support the argument "default" introduced in python 3.4 for min and max
+ :param _builtin_func: builtin min or builtin max
+ :param args:
+ :param kwargs:
+ :return: returns the min or max based on the arguments passed
+ """
+
+ for key, _ in kwargs.items():
+ if key not in set(['key', 'default']):
+ raise TypeError('Illegal argument %s', key)
+
+ if len(args) == 0:
+ raise TypeError
+
+ if len(args) != 1 and kwargs.get('default', _SENTINEL) is not _SENTINEL:
+ raise TypeError
+
+ if len(args) == 1:
+ iterator = iter(args[0])
+ try:
+ first = next(iterator)
+ except StopIteration:
+ if kwargs.get('default', _SENTINEL) is not _SENTINEL:
+ return kwargs.get('default')
+ else:
+ raise ValueError('{}() arg is an empty sequence'.format(_builtin_func.__name__))
+ else:
+ iterator = itertools.chain([first], iterator)
+ if kwargs.get('key') is not None:
+ return _builtin_func(iterator, key=kwargs.get('key'))
+ else:
+ return _builtin_func(iterator)
+
+ if len(args) > 1:
+ if kwargs.get('key') is not None:
+ return _builtin_func(args, key=kwargs.get('key'))
+ else:
+ return _builtin_func(args)
diff --git a/venv/lib/python3.10/site-packages/future/builtins/newnext.py b/venv/lib/python3.10/site-packages/future/builtins/newnext.py
new file mode 100644
index 0000000..097638a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/newnext.py
@@ -0,0 +1,70 @@
+'''
+This module provides a newnext() function in Python 2 that mimics the
+behaviour of ``next()`` in Python 3, falling back to Python 2's behaviour for
+compatibility if this fails.
+
+``newnext(iterator)`` calls the iterator's ``__next__()`` method if it exists. If this
+doesn't exist, it falls back to calling a ``next()`` method.
+
+For example:
+
+ >>> class Odds(object):
+ ... def __init__(self, start=1):
+ ... self.value = start - 2
+ ... def __next__(self): # note the Py3 interface
+ ... self.value += 2
+ ... return self.value
+ ... def __iter__(self):
+ ... return self
+ ...
+ >>> iterator = Odds()
+ >>> next(iterator)
+ 1
+ >>> next(iterator)
+ 3
+
+If you are defining your own custom iterator class as above, it is preferable
+to explicitly decorate the class with the @implements_iterator decorator from
+``future.utils`` as follows:
+
+ >>> @implements_iterator
+ ... class Odds(object):
+ ... # etc
+ ... pass
+
+This next() function is primarily for consuming iterators defined in Python 3
+code elsewhere that we would like to run on Python 2 or 3.
+'''
+
+_builtin_next = next
+
+_SENTINEL = object()
+
+def newnext(iterator, default=_SENTINEL):
+ """
+ next(iterator[, default])
+
+ Return the next item from the iterator. If default is given and the iterator
+ is exhausted, it is returned instead of raising StopIteration.
+ """
+
+ # args = []
+ # if default is not _SENTINEL:
+ # args.append(default)
+ try:
+ try:
+ return iterator.__next__()
+ except AttributeError:
+ try:
+ return iterator.next()
+ except AttributeError:
+ raise TypeError("'{0}' object is not an iterator".format(
+ iterator.__class__.__name__))
+ except StopIteration as e:
+ if default is _SENTINEL:
+ raise e
+ else:
+ return default
+
+
+__all__ = ['newnext']
diff --git a/venv/lib/python3.10/site-packages/future/builtins/newround.py b/venv/lib/python3.10/site-packages/future/builtins/newround.py
new file mode 100644
index 0000000..b06c116
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/newround.py
@@ -0,0 +1,105 @@
+"""
+``python-future``: pure Python implementation of Python 3 round().
+"""
+
+from __future__ import division
+from future.utils import PYPY, PY26, bind_method
+
+# Use the decimal module for simplicity of implementation (and
+# hopefully correctness).
+from decimal import Decimal, ROUND_HALF_EVEN
+
+
+def newround(number, ndigits=None):
+ """
+ See Python 3 documentation: uses Banker's Rounding.
+
+ Delegates to the __round__ method if for some reason this exists.
+
+ If not, rounds a number to a given precision in decimal digits (default
+ 0 digits). This returns an int when called with one argument,
+ otherwise the same type as the number. ndigits may be negative.
+
+ See the test_round method in future/tests/test_builtins.py for
+ examples.
+ """
+ return_int = False
+ if ndigits is None:
+ return_int = True
+ ndigits = 0
+ if hasattr(number, '__round__'):
+ return number.__round__(ndigits)
+
+ exponent = Decimal('10') ** (-ndigits)
+
+ # Work around issue #24: round() breaks on PyPy with NumPy's types
+ # Also breaks on CPython with NumPy's specialized int types like uint64
+ if 'numpy' in repr(type(number)):
+ number = float(number)
+
+ if isinstance(number, Decimal):
+ d = number
+ else:
+ if not PY26:
+ d = Decimal.from_float(number)
+ else:
+ d = from_float_26(number)
+
+ if ndigits < 0:
+ result = newround(d / exponent) * exponent
+ else:
+ result = d.quantize(exponent, rounding=ROUND_HALF_EVEN)
+
+ if return_int:
+ return int(result)
+ else:
+ return float(result)
+
+
+### From Python 2.7's decimal.py. Only needed to support Py2.6:
+
+def from_float_26(f):
+ """Converts a float to a decimal number, exactly.
+
+ Note that Decimal.from_float(0.1) is not the same as Decimal('0.1').
+ Since 0.1 is not exactly representable in binary floating point, the
+ value is stored as the nearest representable value which is
+ 0x1.999999999999ap-4. The exact equivalent of the value in decimal
+ is 0.1000000000000000055511151231257827021181583404541015625.
+
+ >>> Decimal.from_float(0.1)
+ Decimal('0.1000000000000000055511151231257827021181583404541015625')
+ >>> Decimal.from_float(float('nan'))
+ Decimal('NaN')
+ >>> Decimal.from_float(float('inf'))
+ Decimal('Infinity')
+ >>> Decimal.from_float(-float('inf'))
+ Decimal('-Infinity')
+ >>> Decimal.from_float(-0.0)
+ Decimal('-0')
+
+ """
+ import math as _math
+ from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3)
+
+ if isinstance(f, (int, long)): # handle integer inputs
+ return Decimal(f)
+ if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float
+ return Decimal(repr(f))
+ if _math.copysign(1.0, f) == 1.0:
+ sign = 0
+ else:
+ sign = 1
+ n, d = abs(f).as_integer_ratio()
+ # int.bit_length() method doesn't exist on Py2.6:
+ def bit_length(d):
+ if d != 0:
+ return len(bin(abs(d))) - 2
+ else:
+ return 0
+ k = bit_length(d) - 1
+ result = _dec_from_triple(sign, str(n*5**k), -k)
+ return result
+
+
+__all__ = ['newround']
diff --git a/venv/lib/python3.10/site-packages/future/builtins/newsuper.py b/venv/lib/python3.10/site-packages/future/builtins/newsuper.py
new file mode 100644
index 0000000..3e8cc80
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/builtins/newsuper.py
@@ -0,0 +1,113 @@
+'''
+This module provides a newsuper() function in Python 2 that mimics the
+behaviour of super() in Python 3. It is designed to be used as follows:
+
+ from __future__ import division, absolute_import, print_function
+ from future.builtins import super
+
+And then, for example:
+
+ class VerboseList(list):
+ def append(self, item):
+ print('Adding an item')
+ super().append(item) # new simpler super() function
+
+Importing this module on Python 3 has no effect.
+
+This is based on (i.e. almost identical to) Ryan Kelly's magicsuper
+module here:
+
+ https://github.com/rfk/magicsuper.git
+
+Excerpts from Ryan's docstring:
+
+ "Of course, you can still explicitly pass in the arguments if you want
+ to do something strange. Sometimes you really do want that, e.g. to
+ skip over some classes in the method resolution order.
+
+ "How does it work? By inspecting the calling frame to determine the
+ function object being executed and the object on which it's being
+ called, and then walking the object's __mro__ chain to find out where
+ that function was defined. Yuck, but it seems to work..."
+'''
+
+from __future__ import absolute_import
+import sys
+from types import FunctionType
+
+from future.utils import PY3, PY26
+
+
+_builtin_super = super
+
+_SENTINEL = object()
+
+def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
+ '''Like builtin super(), but capable of magic.
+
+ This acts just like the builtin super() function, but if called
+ without any arguments it attempts to infer them at runtime.
+ '''
+ # Infer the correct call if used without arguments.
+ if typ is _SENTINEL:
+ # We'll need to do some frame hacking.
+ f = sys._getframe(framedepth)
+
+ try:
+ # Get the function's first positional argument.
+ type_or_obj = f.f_locals[f.f_code.co_varnames[0]]
+ except (IndexError, KeyError,):
+ raise RuntimeError('super() used in a function with no args')
+
+ try:
+ typ = find_owner(type_or_obj, f.f_code)
+ except (AttributeError, RuntimeError, TypeError):
+ # see issues #160, #267
+ try:
+ typ = find_owner(type_or_obj.__class__, f.f_code)
+ except AttributeError:
+ raise RuntimeError('super() used with an old-style class')
+ except TypeError:
+ raise RuntimeError('super() called outside a method')
+
+ # Dispatch to builtin super().
+ if type_or_obj is not _SENTINEL:
+ return _builtin_super(typ, type_or_obj)
+ return _builtin_super(typ)
+
+
+def find_owner(cls, code):
+ '''Find the class that owns the currently-executing method.
+ '''
+ for typ in cls.__mro__:
+ for meth in typ.__dict__.values():
+ # Drill down through any wrappers to the underlying func.
+ # This handles e.g. classmethod() and staticmethod().
+ try:
+ while not isinstance(meth,FunctionType):
+ if isinstance(meth, property):
+ # Calling __get__ on the property will invoke
+ # user code which might throw exceptions or have
+ # side effects
+ meth = meth.fget
+ else:
+ try:
+ meth = meth.__func__
+ except AttributeError:
+ meth = meth.__get__(cls, typ)
+ except (AttributeError, TypeError):
+ continue
+ if meth.func_code is code:
+ return typ # Aha! Found you.
+ # Not found! Move onto the next class in MRO.
+
+ raise TypeError
+
+
+def superm(*args, **kwds):
+ f = sys._getframe(1)
+ nm = f.f_code.co_name
+ return getattr(newsuper(framedepth=2),nm)(*args, **kwds)
+
+
+__all__ = ['newsuper']
diff --git a/venv/lib/python3.10/site-packages/future/moves/__init__.py b/venv/lib/python3.10/site-packages/future/moves/__init__.py
new file mode 100644
index 0000000..0cd60d3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/__init__.py
@@ -0,0 +1,8 @@
+# future.moves package
+from __future__ import absolute_import
+import sys
+__future_module__ = True
+from future.standard_library import import_top_level_modules
+
+if sys.version_info[0] >= 3:
+ import_top_level_modules()
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..b60b48a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/_dummy_thread.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_dummy_thread.cpython-310.pyc
new file mode 100644
index 0000000..0e71703
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_dummy_thread.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/_markupbase.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_markupbase.cpython-310.pyc
new file mode 100644
index 0000000..64e3059
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_markupbase.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/_thread.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_thread.cpython-310.pyc
new file mode 100644
index 0000000..60c13f7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/_thread.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/builtins.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/builtins.cpython-310.pyc
new file mode 100644
index 0000000..ecc4ff3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/builtins.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/collections.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/collections.cpython-310.pyc
new file mode 100644
index 0000000..4e891e8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/collections.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/configparser.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/configparser.cpython-310.pyc
new file mode 100644
index 0000000..d20c6f5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/configparser.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/copyreg.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/copyreg.cpython-310.pyc
new file mode 100644
index 0000000..ee9ef70
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/copyreg.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/itertools.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/itertools.cpython-310.pyc
new file mode 100644
index 0000000..7657870
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/itertools.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/multiprocessing.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/multiprocessing.cpython-310.pyc
new file mode 100644
index 0000000..eeb32b0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/multiprocessing.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/pickle.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/pickle.cpython-310.pyc
new file mode 100644
index 0000000..4c1db9a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/pickle.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/queue.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/queue.cpython-310.pyc
new file mode 100644
index 0000000..5a6e207
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/queue.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/reprlib.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/reprlib.cpython-310.pyc
new file mode 100644
index 0000000..bbd8538
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/reprlib.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/socketserver.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/socketserver.cpython-310.pyc
new file mode 100644
index 0000000..3949918
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/socketserver.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/subprocess.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/subprocess.cpython-310.pyc
new file mode 100644
index 0000000..7233d2f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/subprocess.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/sys.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/sys.cpython-310.pyc
new file mode 100644
index 0000000..5b90110
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/sys.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/__pycache__/winreg.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/__pycache__/winreg.cpython-310.pyc
new file mode 100644
index 0000000..0b64b87
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/__pycache__/winreg.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/_dummy_thread.py b/venv/lib/python3.10/site-packages/future/moves/_dummy_thread.py
new file mode 100644
index 0000000..6633f42
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/_dummy_thread.py
@@ -0,0 +1,13 @@
+from __future__ import absolute_import
+from future.utils import PY3, PY39_PLUS
+
+
+if PY39_PLUS:
+ # _dummy_thread and dummy_threading modules were both deprecated in
+ # Python 3.7 and removed in Python 3.9
+ from _thread import *
+elif PY3:
+ from _dummy_thread import *
+else:
+ __future_module__ = True
+ from dummy_thread import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/_markupbase.py b/venv/lib/python3.10/site-packages/future/moves/_markupbase.py
new file mode 100644
index 0000000..f9fb4bb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/_markupbase.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from _markupbase import *
+else:
+ __future_module__ = True
+ from markupbase import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/_thread.py b/venv/lib/python3.10/site-packages/future/moves/_thread.py
new file mode 100644
index 0000000..c68018b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/_thread.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from _thread import *
+else:
+ __future_module__ = True
+ from thread import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/builtins.py b/venv/lib/python3.10/site-packages/future/moves/builtins.py
new file mode 100644
index 0000000..e4b6221
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/builtins.py
@@ -0,0 +1,10 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from builtins import *
+else:
+ __future_module__ = True
+ from __builtin__ import *
+ # Overwrite any old definitions with the equivalent future.builtins ones:
+ from future.builtins import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/collections.py b/venv/lib/python3.10/site-packages/future/moves/collections.py
new file mode 100644
index 0000000..664ee6a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/collections.py
@@ -0,0 +1,18 @@
+from __future__ import absolute_import
+import sys
+
+from future.utils import PY2, PY26
+__future_module__ = True
+
+from collections import *
+
+if PY2:
+ from UserDict import UserDict
+ from UserList import UserList
+ from UserString import UserString
+
+if PY26:
+ from future.backports.misc import OrderedDict, Counter
+
+if sys.version_info < (3, 3):
+ from future.backports.misc import ChainMap, _count_elements
diff --git a/venv/lib/python3.10/site-packages/future/moves/configparser.py b/venv/lib/python3.10/site-packages/future/moves/configparser.py
new file mode 100644
index 0000000..33d9cf9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/configparser.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+
+from future.utils import PY2
+
+if PY2:
+ from ConfigParser import *
+else:
+ from configparser import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/copyreg.py b/venv/lib/python3.10/site-packages/future/moves/copyreg.py
new file mode 100644
index 0000000..9d08cdc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/copyreg.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ import copyreg, sys
+ # A "*" import uses Python 3's copyreg.__all__ which does not include
+ # all public names in the API surface for copyreg, this avoids that
+ # problem by just making our module _be_ a reference to the actual module.
+ sys.modules['future.moves.copyreg'] = copyreg
+else:
+ __future_module__ = True
+ from copy_reg import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/__init__.py b/venv/lib/python3.10/site-packages/future/moves/dbm/__init__.py
new file mode 100644
index 0000000..626b406
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/dbm/__init__.py
@@ -0,0 +1,20 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from dbm import *
+else:
+ __future_module__ = True
+ from whichdb import *
+ from anydbm import *
+
+# Py3.3's dbm/__init__.py imports ndbm but doesn't expose it via __all__.
+# In case some (badly written) code depends on dbm.ndbm after import dbm,
+# we simulate this:
+if PY3:
+ from dbm import ndbm
+else:
+ try:
+ from future.moves.dbm import ndbm
+ except ImportError:
+ ndbm = None
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..5a3690b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/dumb.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/dumb.cpython-310.pyc
new file mode 100644
index 0000000..0850d14
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/dumb.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/gnu.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/gnu.cpython-310.pyc
new file mode 100644
index 0000000..45d4eaf
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/gnu.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-310.pyc
new file mode 100644
index 0000000..06e21e1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/dumb.py b/venv/lib/python3.10/site-packages/future/moves/dbm/dumb.py
new file mode 100644
index 0000000..528383f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/dbm/dumb.py
@@ -0,0 +1,9 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from dbm.dumb import *
+else:
+ __future_module__ = True
+ from dumbdbm import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/gnu.py b/venv/lib/python3.10/site-packages/future/moves/dbm/gnu.py
new file mode 100644
index 0000000..68ccf67
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/dbm/gnu.py
@@ -0,0 +1,9 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from dbm.gnu import *
+else:
+ __future_module__ = True
+ from gdbm import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/dbm/ndbm.py b/venv/lib/python3.10/site-packages/future/moves/dbm/ndbm.py
new file mode 100644
index 0000000..8c6fff8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/dbm/ndbm.py
@@ -0,0 +1,9 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from dbm.ndbm import *
+else:
+ __future_module__ = True
+ from dbm import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/__init__.py b/venv/lib/python3.10/site-packages/future/moves/html/__init__.py
new file mode 100644
index 0000000..22ed6e7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/html/__init__.py
@@ -0,0 +1,31 @@
+from __future__ import absolute_import
+from future.utils import PY3
+__future_module__ = True
+
+if PY3:
+ from html import *
+else:
+ # cgi.escape isn't good enough for the single Py3.3 html test to pass.
+ # Define it inline here instead. From the Py3.4 stdlib. Note that the
+ # html.escape() function from the Py3.3 stdlib is not suitable for use on
+ # Py2.x.
+ """
+ General functions for HTML manipulation.
+ """
+
+ def escape(s, quote=True):
+ """
+ Replace special characters "&", "<" and ">" to HTML-safe sequences.
+ If the optional flag quote is true (the default), the quotation mark
+ characters, both double quote (") and single quote (') characters are also
+ translated.
+ """
+ s = s.replace("&", "&") # Must be done first!
+ s = s.replace("<", "<")
+ s = s.replace(">", ">")
+ if quote:
+ s = s.replace('"', """)
+ s = s.replace('\'', "'")
+ return s
+
+ __all__ = ['escape']
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..eb5b5db
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/entities.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/entities.cpython-310.pyc
new file mode 100644
index 0000000..1f5c9d1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/entities.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/parser.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/parser.cpython-310.pyc
new file mode 100644
index 0000000..9ce0e98
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/html/__pycache__/parser.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/entities.py b/venv/lib/python3.10/site-packages/future/moves/html/entities.py
new file mode 100644
index 0000000..56a8860
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/html/entities.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from html.entities import *
+else:
+ __future_module__ = True
+ from htmlentitydefs import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/html/parser.py b/venv/lib/python3.10/site-packages/future/moves/html/parser.py
new file mode 100644
index 0000000..a6115b5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/html/parser.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+__future_module__ = True
+
+if PY3:
+ from html.parser import *
+else:
+ from HTMLParser import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__init__.py b/venv/lib/python3.10/site-packages/future/moves/http/__init__.py
new file mode 100644
index 0000000..917b3d7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/http/__init__.py
@@ -0,0 +1,4 @@
+from future.utils import PY3
+
+if not PY3:
+ __future_module__ = True
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..cda6ad4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/client.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/client.cpython-310.pyc
new file mode 100644
index 0000000..8875438
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/client.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookiejar.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookiejar.cpython-310.pyc
new file mode 100644
index 0000000..8937c41
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookiejar.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookies.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookies.cpython-310.pyc
new file mode 100644
index 0000000..a433779
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/cookies.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/server.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/server.cpython-310.pyc
new file mode 100644
index 0000000..dfe6bb3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/http/__pycache__/server.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/client.py b/venv/lib/python3.10/site-packages/future/moves/http/client.py
new file mode 100644
index 0000000..55f9c9c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/http/client.py
@@ -0,0 +1,8 @@
+from future.utils import PY3
+
+if PY3:
+ from http.client import *
+else:
+ from httplib import *
+ from httplib import HTTPMessage
+ __future_module__ = True
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/cookiejar.py b/venv/lib/python3.10/site-packages/future/moves/http/cookiejar.py
new file mode 100644
index 0000000..ea00df7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/http/cookiejar.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from http.cookiejar import *
+else:
+ __future_module__ = True
+ from cookielib import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/cookies.py b/venv/lib/python3.10/site-packages/future/moves/http/cookies.py
new file mode 100644
index 0000000..1b74fe2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/http/cookies.py
@@ -0,0 +1,9 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from http.cookies import *
+else:
+ __future_module__ = True
+ from Cookie import *
+ from Cookie import Morsel # left out of __all__ on Py2.7!
diff --git a/venv/lib/python3.10/site-packages/future/moves/http/server.py b/venv/lib/python3.10/site-packages/future/moves/http/server.py
new file mode 100644
index 0000000..4e75cc1
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/http/server.py
@@ -0,0 +1,20 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from http.server import *
+else:
+ __future_module__ = True
+ from BaseHTTPServer import *
+ from CGIHTTPServer import *
+ from SimpleHTTPServer import *
+ try:
+ from CGIHTTPServer import _url_collapse_path # needed for a test
+ except ImportError:
+ try:
+ # Python 2.7.0 to 2.7.3
+ from CGIHTTPServer import (
+ _url_collapse_path_split as _url_collapse_path)
+ except ImportError:
+ # Doesn't exist on Python 2.6.x. Ignore it.
+ pass
diff --git a/venv/lib/python3.10/site-packages/future/moves/itertools.py b/venv/lib/python3.10/site-packages/future/moves/itertools.py
new file mode 100644
index 0000000..e5eb20d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/itertools.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+
+from itertools import *
+try:
+ zip_longest = izip_longest
+ filterfalse = ifilterfalse
+except NameError:
+ pass
diff --git a/venv/lib/python3.10/site-packages/future/moves/multiprocessing.py b/venv/lib/python3.10/site-packages/future/moves/multiprocessing.py
new file mode 100644
index 0000000..a871b67
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/multiprocessing.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+from multiprocessing import *
+if not PY3:
+ __future_module__ = True
+ from multiprocessing.queues import SimpleQueue
diff --git a/venv/lib/python3.10/site-packages/future/moves/pickle.py b/venv/lib/python3.10/site-packages/future/moves/pickle.py
new file mode 100644
index 0000000..c53d693
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/pickle.py
@@ -0,0 +1,11 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from pickle import *
+else:
+ __future_module__ = True
+ try:
+ from cPickle import *
+ except ImportError:
+ from pickle import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/queue.py b/venv/lib/python3.10/site-packages/future/moves/queue.py
new file mode 100644
index 0000000..1cb1437
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/queue.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from queue import *
+else:
+ __future_module__ = True
+ from Queue import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/reprlib.py b/venv/lib/python3.10/site-packages/future/moves/reprlib.py
new file mode 100644
index 0000000..a313a13
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/reprlib.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from reprlib import *
+else:
+ __future_module__ = True
+ from repr import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/socketserver.py b/venv/lib/python3.10/site-packages/future/moves/socketserver.py
new file mode 100644
index 0000000..062e084
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/socketserver.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from socketserver import *
+else:
+ __future_module__ = True
+ from SocketServer import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/subprocess.py b/venv/lib/python3.10/site-packages/future/moves/subprocess.py
new file mode 100644
index 0000000..43ffd2a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/subprocess.py
@@ -0,0 +1,11 @@
+from __future__ import absolute_import
+from future.utils import PY2, PY26
+
+from subprocess import *
+
+if PY2:
+ __future_module__ = True
+ from commands import getoutput, getstatusoutput
+
+if PY26:
+ from future.backports.misc import check_output
diff --git a/venv/lib/python3.10/site-packages/future/moves/sys.py b/venv/lib/python3.10/site-packages/future/moves/sys.py
new file mode 100644
index 0000000..1293bcb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/sys.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+
+from future.utils import PY2
+
+from sys import *
+
+if PY2:
+ from __builtin__ import intern
diff --git a/venv/lib/python3.10/site-packages/future/moves/test/__init__.py b/venv/lib/python3.10/site-packages/future/moves/test/__init__.py
new file mode 100644
index 0000000..5cf428b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/test/__init__.py
@@ -0,0 +1,5 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if not PY3:
+ __future_module__ = True
diff --git a/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..dbaa3c8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/support.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/support.cpython-310.pyc
new file mode 100644
index 0000000..94f7427
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/test/__pycache__/support.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/test/support.py b/venv/lib/python3.10/site-packages/future/moves/test/support.py
new file mode 100644
index 0000000..f70c9d7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/test/support.py
@@ -0,0 +1,19 @@
+from __future__ import absolute_import
+
+import sys
+
+from future.standard_library import suspend_hooks
+from future.utils import PY3
+
+if PY3:
+ from test.support import *
+ if sys.version_info[:2] >= (3, 10):
+ from test.support.os_helper import (
+ EnvironmentVarGuard,
+ TESTFN,
+ )
+ from test.support.warnings_helper import check_warnings
+else:
+ __future_module__ = True
+ with suspend_hooks():
+ from test.test_support import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__init__.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/__init__.py
new file mode 100644
index 0000000..e408296
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/__init__.py
@@ -0,0 +1,27 @@
+from __future__ import absolute_import
+from future.utils import PY3
+__future_module__ = True
+
+if not PY3:
+ from Tkinter import *
+ from Tkinter import (_cnfmerge, _default_root, _flatten,
+ _support_default_root, _test,
+ _tkinter, _setit)
+
+ try: # >= 2.7.4
+ from Tkinter import (_join)
+ except ImportError:
+ pass
+
+ try: # >= 2.7.4
+ from Tkinter import (_stringify)
+ except ImportError:
+ pass
+
+ try: # >= 2.7.9
+ from Tkinter import (_splitdict)
+ except ImportError:
+ pass
+
+else:
+ from tkinter import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..42a1649
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-310.pyc
new file mode 100644
index 0000000..5ac772f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-310.pyc
new file mode 100644
index 0000000..2c49954
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/constants.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/constants.cpython-310.pyc
new file mode 100644
index 0000000..45ffa58
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/constants.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-310.pyc
new file mode 100644
index 0000000..c4342ca
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-310.pyc
new file mode 100644
index 0000000..08d830f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-310.pyc
new file mode 100644
index 0000000..103cddf
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/font.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/font.cpython-310.pyc
new file mode 100644
index 0000000..c8a0ecb
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/font.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-310.pyc
new file mode 100644
index 0000000..b1aa7fc
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-310.pyc
new file mode 100644
index 0000000..b1c625a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-310.pyc
new file mode 100644
index 0000000..620d0f6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/tix.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/tix.cpython-310.pyc
new file mode 100644
index 0000000..ffdb744
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/tix.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-310.pyc
new file mode 100644
index 0000000..1e53811
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/colorchooser.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/colorchooser.py
new file mode 100644
index 0000000..6dde6e8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/colorchooser.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.colorchooser import *
+else:
+ try:
+ from tkColorChooser import *
+ except ImportError:
+ raise ImportError('The tkColorChooser module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/commondialog.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/commondialog.py
new file mode 100644
index 0000000..eb7ae8d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/commondialog.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.commondialog import *
+else:
+ try:
+ from tkCommonDialog import *
+ except ImportError:
+ raise ImportError('The tkCommonDialog module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/constants.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/constants.py
new file mode 100644
index 0000000..ffe0981
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/constants.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.constants import *
+else:
+ try:
+ from Tkconstants import *
+ except ImportError:
+ raise ImportError('The Tkconstants module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/dialog.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/dialog.py
new file mode 100644
index 0000000..113370c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/dialog.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.dialog import *
+else:
+ try:
+ from Dialog import *
+ except ImportError:
+ raise ImportError('The Dialog module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/dnd.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/dnd.py
new file mode 100644
index 0000000..1ab4379
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/dnd.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.dnd import *
+else:
+ try:
+ from Tkdnd import *
+ except ImportError:
+ raise ImportError('The Tkdnd module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/filedialog.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/filedialog.py
new file mode 100644
index 0000000..6a6f03c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/filedialog.py
@@ -0,0 +1,18 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.filedialog import *
+else:
+ try:
+ from FileDialog import *
+ except ImportError:
+ raise ImportError('The FileDialog module is missing. Does your Py2 '
+ 'installation include tkinter?')
+
+ try:
+ from tkFileDialog import *
+ except ImportError:
+ raise ImportError('The tkFileDialog module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/font.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/font.py
new file mode 100644
index 0000000..628f399
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/font.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.font import *
+else:
+ try:
+ from tkFont import *
+ except ImportError:
+ raise ImportError('The tkFont module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/messagebox.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/messagebox.py
new file mode 100644
index 0000000..b43d870
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/messagebox.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.messagebox import *
+else:
+ try:
+ from tkMessageBox import *
+ except ImportError:
+ raise ImportError('The tkMessageBox module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/scrolledtext.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/scrolledtext.py
new file mode 100644
index 0000000..1c69db6
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/scrolledtext.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.scrolledtext import *
+else:
+ try:
+ from ScrolledText import *
+ except ImportError:
+ raise ImportError('The ScrolledText module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/simpledialog.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/simpledialog.py
new file mode 100644
index 0000000..dba93fb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/simpledialog.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.simpledialog import *
+else:
+ try:
+ from SimpleDialog import *
+ except ImportError:
+ raise ImportError('The SimpleDialog module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/tix.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/tix.py
new file mode 100644
index 0000000..8d1718a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/tix.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.tix import *
+else:
+ try:
+ from Tix import *
+ except ImportError:
+ raise ImportError('The Tix module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/tkinter/ttk.py b/venv/lib/python3.10/site-packages/future/moves/tkinter/ttk.py
new file mode 100644
index 0000000..081c1b4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/tkinter/ttk.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from future.utils import PY3
+
+if PY3:
+ from tkinter.ttk import *
+else:
+ try:
+ from ttk import *
+ except ImportError:
+ raise ImportError('The ttk module is missing. Does your Py2 '
+ 'installation include tkinter?')
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__init__.py b/venv/lib/python3.10/site-packages/future/moves/urllib/__init__.py
new file mode 100644
index 0000000..5cf428b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/__init__.py
@@ -0,0 +1,5 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if not PY3:
+ __future_module__ = True
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..52117b7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/error.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/error.cpython-310.pyc
new file mode 100644
index 0000000..451b919
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/error.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/parse.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/parse.cpython-310.pyc
new file mode 100644
index 0000000..1a581f0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/parse.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/request.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/request.cpython-310.pyc
new file mode 100644
index 0000000..d2cc0c9
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/request.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/response.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/response.cpython-310.pyc
new file mode 100644
index 0000000..b22bcce
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/response.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-310.pyc
new file mode 100644
index 0000000..1d828ca
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/error.py b/venv/lib/python3.10/site-packages/future/moves/urllib/error.py
new file mode 100644
index 0000000..7d8ada7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/error.py
@@ -0,0 +1,16 @@
+from __future__ import absolute_import
+from future.standard_library import suspend_hooks
+
+from future.utils import PY3
+
+if PY3:
+ from urllib.error import *
+else:
+ __future_module__ = True
+
+ # We use this method to get at the original Py2 urllib before any renaming magic
+ # ContentTooShortError = sys.py2_modules['urllib'].ContentTooShortError
+
+ with suspend_hooks():
+ from urllib import ContentTooShortError
+ from urllib2 import URLError, HTTPError
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/parse.py b/venv/lib/python3.10/site-packages/future/moves/urllib/parse.py
new file mode 100644
index 0000000..9074b81
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/parse.py
@@ -0,0 +1,28 @@
+from __future__ import absolute_import
+from future.standard_library import suspend_hooks
+
+from future.utils import PY3
+
+if PY3:
+ from urllib.parse import *
+else:
+ __future_module__ = True
+ from urlparse import (ParseResult, SplitResult, parse_qs, parse_qsl,
+ urldefrag, urljoin, urlparse, urlsplit,
+ urlunparse, urlunsplit)
+
+ # we use this method to get at the original py2 urllib before any renaming
+ # quote = sys.py2_modules['urllib'].quote
+ # quote_plus = sys.py2_modules['urllib'].quote_plus
+ # unquote = sys.py2_modules['urllib'].unquote
+ # unquote_plus = sys.py2_modules['urllib'].unquote_plus
+ # urlencode = sys.py2_modules['urllib'].urlencode
+ # splitquery = sys.py2_modules['urllib'].splitquery
+
+ with suspend_hooks():
+ from urllib import (quote,
+ quote_plus,
+ unquote,
+ unquote_plus,
+ urlencode,
+ splitquery)
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/request.py b/venv/lib/python3.10/site-packages/future/moves/urllib/request.py
new file mode 100644
index 0000000..972aa4a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/request.py
@@ -0,0 +1,94 @@
+from __future__ import absolute_import
+
+from future.standard_library import suspend_hooks
+from future.utils import PY3
+
+if PY3:
+ from urllib.request import *
+ # This aren't in __all__:
+ from urllib.request import (getproxies,
+ pathname2url,
+ proxy_bypass,
+ quote,
+ request_host,
+ thishost,
+ unquote,
+ url2pathname,
+ urlcleanup,
+ urljoin,
+ urlopen,
+ urlparse,
+ urlretrieve,
+ urlsplit,
+ urlunparse)
+
+ from urllib.parse import (splitattr,
+ splithost,
+ splitpasswd,
+ splitport,
+ splitquery,
+ splittag,
+ splittype,
+ splituser,
+ splitvalue,
+ to_bytes,
+ unwrap)
+else:
+ __future_module__ = True
+ with suspend_hooks():
+ from urllib import *
+ from urllib2 import *
+ from urlparse import *
+
+ # Rename:
+ from urllib import toBytes # missing from __all__ on Py2.6
+ to_bytes = toBytes
+
+ # from urllib import (pathname2url,
+ # url2pathname,
+ # getproxies,
+ # urlretrieve,
+ # urlcleanup,
+ # URLopener,
+ # FancyURLopener,
+ # proxy_bypass)
+
+ # from urllib2 import (
+ # AbstractBasicAuthHandler,
+ # AbstractDigestAuthHandler,
+ # BaseHandler,
+ # CacheFTPHandler,
+ # FileHandler,
+ # FTPHandler,
+ # HTTPBasicAuthHandler,
+ # HTTPCookieProcessor,
+ # HTTPDefaultErrorHandler,
+ # HTTPDigestAuthHandler,
+ # HTTPErrorProcessor,
+ # HTTPHandler,
+ # HTTPPasswordMgr,
+ # HTTPPasswordMgrWithDefaultRealm,
+ # HTTPRedirectHandler,
+ # HTTPSHandler,
+ # URLError,
+ # build_opener,
+ # install_opener,
+ # OpenerDirector,
+ # ProxyBasicAuthHandler,
+ # ProxyDigestAuthHandler,
+ # ProxyHandler,
+ # Request,
+ # UnknownHandler,
+ # urlopen,
+ # )
+
+ # from urlparse import (
+ # urldefrag
+ # urljoin,
+ # urlparse,
+ # urlunparse,
+ # urlsplit,
+ # urlunsplit,
+ # parse_qs,
+ # parse_q"
+ # )
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/response.py b/venv/lib/python3.10/site-packages/future/moves/urllib/response.py
new file mode 100644
index 0000000..a287ae2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/response.py
@@ -0,0 +1,12 @@
+from future import standard_library
+from future.utils import PY3
+
+if PY3:
+ from urllib.response import *
+else:
+ __future_module__ = True
+ with standard_library.suspend_hooks():
+ from urllib import (addbase,
+ addclosehook,
+ addinfo,
+ addinfourl)
diff --git a/venv/lib/python3.10/site-packages/future/moves/urllib/robotparser.py b/venv/lib/python3.10/site-packages/future/moves/urllib/robotparser.py
new file mode 100644
index 0000000..0dc8f57
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/urllib/robotparser.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from urllib.robotparser import *
+else:
+ __future_module__ = True
+ from robotparser import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/winreg.py b/venv/lib/python3.10/site-packages/future/moves/winreg.py
new file mode 100644
index 0000000..c8b1475
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/winreg.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from winreg import *
+else:
+ __future_module__ = True
+ from _winreg import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__init__.py b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..3f22d90
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-310.pyc
new file mode 100644
index 0000000..d2d6fdd
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-310.pyc
new file mode 100644
index 0000000..eb9fbb0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/client.py b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/client.py
new file mode 100644
index 0000000..4708cf8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/client.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from xmlrpc.client import *
+else:
+ from xmlrpclib import *
diff --git a/venv/lib/python3.10/site-packages/future/moves/xmlrpc/server.py b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/server.py
new file mode 100644
index 0000000..1a8af34
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/moves/xmlrpc/server.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+if PY3:
+ from xmlrpc.server import *
+else:
+ from xmlrpclib import *
diff --git a/venv/lib/python3.10/site-packages/future/standard_library/__init__.py b/venv/lib/python3.10/site-packages/future/standard_library/__init__.py
new file mode 100644
index 0000000..d467aaf
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/standard_library/__init__.py
@@ -0,0 +1,821 @@
+"""
+Python 3 reorganized the standard library (PEP 3108). This module exposes
+several standard library modules to Python 2 under their new Python 3
+names.
+
+It is designed to be used as follows::
+
+ from future import standard_library
+ standard_library.install_aliases()
+
+And then these normal Py3 imports work on both Py3 and Py2::
+
+ import builtins
+ import copyreg
+ import queue
+ import reprlib
+ import socketserver
+ import winreg # on Windows only
+ import test.support
+ import html, html.parser, html.entities
+ import http, http.client, http.server
+ import http.cookies, http.cookiejar
+ import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser
+ import xmlrpc.client, xmlrpc.server
+
+ import _thread
+ import _dummy_thread
+ import _markupbase
+
+ from itertools import filterfalse, zip_longest
+ from sys import intern
+ from collections import UserDict, UserList, UserString
+ from collections import OrderedDict, Counter, ChainMap # even on Py2.6
+ from subprocess import getoutput, getstatusoutput
+ from subprocess import check_output # even on Py2.6
+ from multiprocessing import SimpleQueue
+
+(The renamed modules and functions are still available under their old
+names on Python 2.)
+
+This is a cleaner alternative to this idiom (see
+http://docs.pythonsprints.com/python3_porting/py-porting.html)::
+
+ try:
+ import queue
+ except ImportError:
+ import Queue as queue
+
+
+Limitations
+-----------
+We don't currently support these modules, but would like to::
+
+ import dbm
+ import dbm.dumb
+ import dbm.gnu
+ import collections.abc # on Py33
+ import pickle # should (optionally) bring in cPickle on Python 2
+
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import sys
+import logging
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+ import importlib as imp
+else:
+ import imp
+import contextlib
+import copy
+import os
+
+# Make a dedicated logger; leave the root logger to be configured
+# by the application.
+flog = logging.getLogger('future_stdlib')
+_formatter = logging.Formatter(logging.BASIC_FORMAT)
+_handler = logging.StreamHandler()
+_handler.setFormatter(_formatter)
+flog.addHandler(_handler)
+flog.setLevel(logging.WARN)
+
+from future.utils import PY2, PY3
+
+# The modules that are defined under the same names on Py3 but with
+# different contents in a significant way (e.g. submodules) are:
+# pickle (fast one)
+# dbm
+# urllib
+# test
+# email
+
+REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'dbm']) # add email and dbm when we support it
+
+# The following module names are not present in Python 2.x, so they cause no
+# potential clashes between the old and new names:
+# http
+# html
+# tkinter
+# xmlrpc
+# Keys: Py2 / real module names
+# Values: Py3 / simulated module names
+RENAMES = {
+ # 'cStringIO': 'io', # there's a new io module in Python 2.6
+ # that provides StringIO and BytesIO
+ # 'StringIO': 'io', # ditto
+ # 'cPickle': 'pickle',
+ '__builtin__': 'builtins',
+ 'copy_reg': 'copyreg',
+ 'Queue': 'queue',
+ 'future.moves.socketserver': 'socketserver',
+ 'ConfigParser': 'configparser',
+ 'repr': 'reprlib',
+ 'multiprocessing.queues': 'multiprocessing',
+ # 'FileDialog': 'tkinter.filedialog',
+ # 'tkFileDialog': 'tkinter.filedialog',
+ # 'SimpleDialog': 'tkinter.simpledialog',
+ # 'tkSimpleDialog': 'tkinter.simpledialog',
+ # 'tkColorChooser': 'tkinter.colorchooser',
+ # 'tkCommonDialog': 'tkinter.commondialog',
+ # 'Dialog': 'tkinter.dialog',
+ # 'Tkdnd': 'tkinter.dnd',
+ # 'tkFont': 'tkinter.font',
+ # 'tkMessageBox': 'tkinter.messagebox',
+ # 'ScrolledText': 'tkinter.scrolledtext',
+ # 'Tkconstants': 'tkinter.constants',
+ # 'Tix': 'tkinter.tix',
+ # 'ttk': 'tkinter.ttk',
+ # 'Tkinter': 'tkinter',
+ '_winreg': 'winreg',
+ 'thread': '_thread',
+ 'dummy_thread': '_dummy_thread' if sys.version_info < (3, 9) else '_thread',
+ # 'anydbm': 'dbm', # causes infinite import loop
+ # 'whichdb': 'dbm', # causes infinite import loop
+ # anydbm and whichdb are handled by fix_imports2
+ # 'dbhash': 'dbm.bsd',
+ # 'dumbdbm': 'dbm.dumb',
+ # 'dbm': 'dbm.ndbm',
+ # 'gdbm': 'dbm.gnu',
+ 'future.moves.xmlrpc': 'xmlrpc',
+ # 'future.backports.email': 'email', # for use by urllib
+ # 'DocXMLRPCServer': 'xmlrpc.server',
+ # 'SimpleXMLRPCServer': 'xmlrpc.server',
+ # 'httplib': 'http.client',
+ # 'htmlentitydefs' : 'html.entities',
+ # 'HTMLParser' : 'html.parser',
+ # 'Cookie': 'http.cookies',
+ # 'cookielib': 'http.cookiejar',
+ # 'BaseHTTPServer': 'http.server',
+ # 'SimpleHTTPServer': 'http.server',
+ # 'CGIHTTPServer': 'http.server',
+ # 'future.backports.test': 'test', # primarily for renaming test_support to support
+ # 'commands': 'subprocess',
+ # 'urlparse' : 'urllib.parse',
+ # 'robotparser' : 'urllib.robotparser',
+ # 'abc': 'collections.abc', # for Py33
+ # 'future.utils.six.moves.html': 'html',
+ # 'future.utils.six.moves.http': 'http',
+ 'future.moves.html': 'html',
+ 'future.moves.http': 'http',
+ # 'future.backports.urllib': 'urllib',
+ # 'future.utils.six.moves.urllib': 'urllib',
+ 'future.moves._markupbase': '_markupbase',
+ }
+
+
+# It is complicated and apparently brittle to mess around with the
+# ``sys.modules`` cache in order to support "import urllib" meaning two
+# different things (Py2.7 urllib and backported Py3.3-like urllib) in different
+# contexts. So we require explicit imports for these modules.
+assert len(set(RENAMES.values()) & set(REPLACED_MODULES)) == 0
+
+
+# Harmless renames that we can insert.
+# These modules need names from elsewhere being added to them:
+# subprocess: should provide getoutput and other fns from commands
+# module but these fns are missing: getstatus, mk2arg,
+# mkarg
+# re: needs an ASCII constant that works compatibly with Py3
+
+# etc: see lib2to3/fixes/fix_imports.py
+
+# (New module name, new object name, old module name, old object name)
+MOVES = [('collections', 'UserList', 'UserList', 'UserList'),
+ ('collections', 'UserDict', 'UserDict', 'UserDict'),
+ ('collections', 'UserString','UserString', 'UserString'),
+ ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'),
+ ('itertools', 'filterfalse','itertools', 'ifilterfalse'),
+ ('itertools', 'zip_longest','itertools', 'izip_longest'),
+ ('sys', 'intern','__builtin__', 'intern'),
+ ('multiprocessing', 'SimpleQueue', 'multiprocessing.queues', 'SimpleQueue'),
+ # The re module has no ASCII flag in Py2, but this is the default.
+ # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one
+ # (and it exists on Py2.6+).
+ ('re', 'ASCII','stat', 'ST_MODE'),
+ ('base64', 'encodebytes','base64', 'encodestring'),
+ ('base64', 'decodebytes','base64', 'decodestring'),
+ ('subprocess', 'getoutput', 'commands', 'getoutput'),
+ ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'),
+ ('subprocess', 'check_output', 'future.backports.misc', 'check_output'),
+ ('math', 'ceil', 'future.backports.misc', 'ceil'),
+ ('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'),
+ ('collections', 'Counter', 'future.backports.misc', 'Counter'),
+ ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'),
+ ('itertools', 'count', 'future.backports.misc', 'count'),
+ ('reprlib', 'recursive_repr', 'future.backports.misc', 'recursive_repr'),
+ ('functools', 'cmp_to_key', 'future.backports.misc', 'cmp_to_key'),
+
+# This is no use, since "import urllib.request" etc. still fails:
+# ('urllib', 'error', 'future.moves.urllib', 'error'),
+# ('urllib', 'parse', 'future.moves.urllib', 'parse'),
+# ('urllib', 'request', 'future.moves.urllib', 'request'),
+# ('urllib', 'response', 'future.moves.urllib', 'response'),
+# ('urllib', 'robotparser', 'future.moves.urllib', 'robotparser'),
+ ]
+
+
+# A minimal example of an import hook:
+# class WarnOnImport(object):
+# def __init__(self, *args):
+# self.module_names = args
+#
+# def find_module(self, fullname, path=None):
+# if fullname in self.module_names:
+# self.path = path
+# return self
+# return None
+#
+# def load_module(self, name):
+# if name in sys.modules:
+# return sys.modules[name]
+# module_info = imp.find_module(name, self.path)
+# module = imp.load_module(name, *module_info)
+# sys.modules[name] = module
+# flog.warning("Imported deprecated module %s", name)
+# return module
+
+
+class RenameImport(object):
+ """
+ A class for import hooks mapping Py3 module names etc. to the Py2 equivalents.
+ """
+ # Different RenameImport classes are created when importing this module from
+ # different source files. This causes isinstance(hook, RenameImport) checks
+ # to produce inconsistent results. We add this RENAMER attribute here so
+ # remove_hooks() and install_hooks() can find instances of these classes
+ # easily:
+ RENAMER = True
+
+ def __init__(self, old_to_new):
+ '''
+ Pass in a dictionary-like object mapping from old names to new
+ names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'}
+ '''
+ self.old_to_new = old_to_new
+ both = set(old_to_new.keys()) & set(old_to_new.values())
+ assert (len(both) == 0 and
+ len(set(old_to_new.values())) == len(old_to_new.values())), \
+ 'Ambiguity in renaming (handler not implemented)'
+ self.new_to_old = dict((new, old) for (old, new) in old_to_new.items())
+
+ def find_module(self, fullname, path=None):
+ # Handles hierarchical importing: package.module.module2
+ new_base_names = set([s.split('.')[0] for s in self.new_to_old])
+ # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names:
+ if fullname in new_base_names:
+ return self
+ return None
+
+ def load_module(self, name):
+ path = None
+ if name in sys.modules:
+ return sys.modules[name]
+ elif name in self.new_to_old:
+ # New name. Look up the corresponding old (Py2) name:
+ oldname = self.new_to_old[name]
+ module = self._find_and_load_module(oldname)
+ # module.__future_module__ = True
+ else:
+ module = self._find_and_load_module(name)
+ # In any case, make it available under the requested (Py3) name
+ sys.modules[name] = module
+ return module
+
+ def _find_and_load_module(self, name, path=None):
+ """
+ Finds and loads it. But if there's a . in the name, handles it
+ properly.
+ """
+ bits = name.split('.')
+ while len(bits) > 1:
+ # Treat the first bit as a package
+ packagename = bits.pop(0)
+ package = self._find_and_load_module(packagename, path)
+ try:
+ path = package.__path__
+ except AttributeError:
+ # This could be e.g. moves.
+ flog.debug('Package {0} has no __path__.'.format(package))
+ if name in sys.modules:
+ return sys.modules[name]
+ flog.debug('What to do here?')
+
+ name = bits[0]
+ module_info = imp.find_module(name, path)
+ return imp.load_module(name, *module_info)
+
+
+class hooks(object):
+ """
+ Acts as a context manager. Saves the state of sys.modules and restores it
+ after the 'with' block.
+
+ Use like this:
+
+ >>> from future import standard_library
+ >>> with standard_library.hooks():
+ ... import http.client
+ >>> import requests
+
+ For this to work, http.client will be scrubbed from sys.modules after the
+ 'with' block. That way the modules imported in the 'with' block will
+ continue to be accessible in the current namespace but not from any
+ imported modules (like requests).
+ """
+ def __enter__(self):
+ # flog.debug('Entering hooks context manager')
+ self.old_sys_modules = copy.copy(sys.modules)
+ self.hooks_were_installed = detect_hooks()
+ # self.scrubbed = scrub_py2_sys_modules()
+ install_hooks()
+ return self
+
+ def __exit__(self, *args):
+ # flog.debug('Exiting hooks context manager')
+ # restore_sys_modules(self.scrubbed)
+ if not self.hooks_were_installed:
+ remove_hooks()
+ # scrub_future_sys_modules()
+
+# Sanity check for is_py2_stdlib_module(): We aren't replacing any
+# builtin modules names:
+if PY2:
+ assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0
+
+
+def is_py2_stdlib_module(m):
+ """
+ Tries to infer whether the module m is from the Python 2 standard library.
+ This may not be reliable on all systems.
+ """
+ if PY3:
+ return False
+ if not 'stdlib_path' in is_py2_stdlib_module.__dict__:
+ stdlib_files = [contextlib.__file__, os.__file__, copy.__file__]
+ stdlib_paths = [os.path.split(f)[0] for f in stdlib_files]
+ if not len(set(stdlib_paths)) == 1:
+ # This seems to happen on travis-ci.org. Very strange. We'll try to
+ # ignore it.
+ flog.warn('Multiple locations found for the Python standard '
+ 'library: %s' % stdlib_paths)
+ # Choose the first one arbitrarily
+ is_py2_stdlib_module.stdlib_path = stdlib_paths[0]
+
+ if m.__name__ in sys.builtin_module_names:
+ return True
+
+ if hasattr(m, '__file__'):
+ modpath = os.path.split(m.__file__)
+ if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and
+ 'site-packages' not in modpath[0]):
+ return True
+
+ return False
+
+
+def scrub_py2_sys_modules():
+ """
+ Removes any Python 2 standard library modules from ``sys.modules`` that
+ would interfere with Py3-style imports using import hooks. Examples are
+ modules with the same names (like urllib or email).
+
+ (Note that currently import hooks are disabled for modules like these
+ with ambiguous names anyway ...)
+ """
+ if PY3:
+ return {}
+ scrubbed = {}
+ for modulename in REPLACED_MODULES & set(RENAMES.keys()):
+ if not modulename in sys.modules:
+ continue
+
+ module = sys.modules[modulename]
+
+ if is_py2_stdlib_module(module):
+ flog.debug('Deleting (Py2) {} from sys.modules'.format(modulename))
+ scrubbed[modulename] = sys.modules[modulename]
+ del sys.modules[modulename]
+ return scrubbed
+
+
+def scrub_future_sys_modules():
+ """
+ Deprecated.
+ """
+ return {}
+
+class suspend_hooks(object):
+ """
+ Acts as a context manager. Use like this:
+
+ >>> from future import standard_library
+ >>> standard_library.install_hooks()
+ >>> import http.client
+ >>> # ...
+ >>> with standard_library.suspend_hooks():
+ >>> import requests # incompatible with ``future``'s standard library hooks
+
+ If the hooks were disabled before the context, they are not installed when
+ the context is left.
+ """
+ def __enter__(self):
+ self.hooks_were_installed = detect_hooks()
+ remove_hooks()
+ # self.scrubbed = scrub_future_sys_modules()
+ return self
+
+ def __exit__(self, *args):
+ if self.hooks_were_installed:
+ install_hooks()
+ # restore_sys_modules(self.scrubbed)
+
+
+def restore_sys_modules(scrubbed):
+ """
+ Add any previously scrubbed modules back to the sys.modules cache,
+ but only if it's safe to do so.
+ """
+ clash = set(sys.modules) & set(scrubbed)
+ if len(clash) != 0:
+ # If several, choose one arbitrarily to raise an exception about
+ first = list(clash)[0]
+ raise ImportError('future module {} clashes with Py2 module'
+ .format(first))
+ sys.modules.update(scrubbed)
+
+
+def install_aliases():
+ """
+ Monkey-patches the standard library in Py2.6/7 to provide
+ aliases for better Py3 compatibility.
+ """
+ if PY3:
+ return
+ # if hasattr(install_aliases, 'run_already'):
+ # return
+ for (newmodname, newobjname, oldmodname, oldobjname) in MOVES:
+ __import__(newmodname)
+ # We look up the module in sys.modules because __import__ just returns the
+ # top-level package:
+ newmod = sys.modules[newmodname]
+ # newmod.__future_module__ = True
+
+ __import__(oldmodname)
+ oldmod = sys.modules[oldmodname]
+
+ obj = getattr(oldmod, oldobjname)
+ setattr(newmod, newobjname, obj)
+
+ # Hack for urllib so it appears to have the same structure on Py2 as on Py3
+ import urllib
+ from future.backports.urllib import request
+ from future.backports.urllib import response
+ from future.backports.urllib import parse
+ from future.backports.urllib import error
+ from future.backports.urllib import robotparser
+ urllib.request = request
+ urllib.response = response
+ urllib.parse = parse
+ urllib.error = error
+ urllib.robotparser = robotparser
+ sys.modules['urllib.request'] = request
+ sys.modules['urllib.response'] = response
+ sys.modules['urllib.parse'] = parse
+ sys.modules['urllib.error'] = error
+ sys.modules['urllib.robotparser'] = robotparser
+
+ # Patch the test module so it appears to have the same structure on Py2 as on Py3
+ try:
+ import test
+ except ImportError:
+ pass
+ try:
+ from future.moves.test import support
+ except ImportError:
+ pass
+ else:
+ test.support = support
+ sys.modules['test.support'] = support
+
+ # Patch the dbm module so it appears to have the same structure on Py2 as on Py3
+ try:
+ import dbm
+ except ImportError:
+ pass
+ else:
+ from future.moves.dbm import dumb
+ dbm.dumb = dumb
+ sys.modules['dbm.dumb'] = dumb
+ try:
+ from future.moves.dbm import gnu
+ except ImportError:
+ pass
+ else:
+ dbm.gnu = gnu
+ sys.modules['dbm.gnu'] = gnu
+ try:
+ from future.moves.dbm import ndbm
+ except ImportError:
+ pass
+ else:
+ dbm.ndbm = ndbm
+ sys.modules['dbm.ndbm'] = ndbm
+
+ # install_aliases.run_already = True
+
+
+def install_hooks():
+ """
+ This function installs the future.standard_library import hook into
+ sys.meta_path.
+ """
+ if PY3:
+ return
+
+ install_aliases()
+
+ flog.debug('sys.meta_path was: {0}'.format(sys.meta_path))
+ flog.debug('Installing hooks ...')
+
+ # Add it unless it's there already
+ newhook = RenameImport(RENAMES)
+ if not detect_hooks():
+ sys.meta_path.append(newhook)
+ flog.debug('sys.meta_path is now: {0}'.format(sys.meta_path))
+
+
+def enable_hooks():
+ """
+ Deprecated. Use install_hooks() instead. This will be removed by
+ ``future`` v1.0.
+ """
+ install_hooks()
+
+
+def remove_hooks(scrub_sys_modules=False):
+ """
+ This function removes the import hook from sys.meta_path.
+ """
+ if PY3:
+ return
+ flog.debug('Uninstalling hooks ...')
+ # Loop backwards, so deleting items keeps the ordering:
+ for i, hook in list(enumerate(sys.meta_path))[::-1]:
+ if hasattr(hook, 'RENAMER'):
+ del sys.meta_path[i]
+
+ # Explicit is better than implicit. In the future the interface should
+ # probably change so that scrubbing the import hooks requires a separate
+ # function call. Left as is for now for backward compatibility with
+ # v0.11.x.
+ if scrub_sys_modules:
+ scrub_future_sys_modules()
+
+
+def disable_hooks():
+ """
+ Deprecated. Use remove_hooks() instead. This will be removed by
+ ``future`` v1.0.
+ """
+ remove_hooks()
+
+
+def detect_hooks():
+ """
+ Returns True if the import hooks are installed, False if not.
+ """
+ flog.debug('Detecting hooks ...')
+ present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path])
+ if present:
+ flog.debug('Detected.')
+ else:
+ flog.debug('Not detected.')
+ return present
+
+
+# As of v0.12, this no longer happens implicitly:
+# if not PY3:
+# install_hooks()
+
+
+if not hasattr(sys, 'py2_modules'):
+ sys.py2_modules = {}
+
+def cache_py2_modules():
+ """
+ Currently this function is unneeded, as we are not attempting to provide import hooks
+ for modules with ambiguous names: email, urllib, pickle.
+ """
+ if len(sys.py2_modules) != 0:
+ return
+ assert not detect_hooks()
+ import urllib
+ sys.py2_modules['urllib'] = urllib
+
+ import email
+ sys.py2_modules['email'] = email
+
+ import pickle
+ sys.py2_modules['pickle'] = pickle
+
+ # Not all Python installations have test module. (Anaconda doesn't, for example.)
+ # try:
+ # import test
+ # except ImportError:
+ # sys.py2_modules['test'] = None
+ # sys.py2_modules['test'] = test
+
+ # import dbm
+ # sys.py2_modules['dbm'] = dbm
+
+
+def import_(module_name, backport=False):
+ """
+ Pass a (potentially dotted) module name of a Python 3 standard library
+ module. This function imports the module compatibly on Py2 and Py3 and
+ returns the top-level module.
+
+ Example use:
+ >>> http = import_('http.client')
+ >>> http = import_('http.server')
+ >>> urllib = import_('urllib.request')
+
+ Then:
+ >>> conn = http.client.HTTPConnection(...)
+ >>> response = urllib.request.urlopen('http://mywebsite.com')
+ >>> # etc.
+
+ Use as follows:
+ >>> package_name = import_(module_name)
+
+ On Py3, equivalent to this:
+
+ >>> import module_name
+
+ On Py2, equivalent to this if backport=False:
+
+ >>> from future.moves import module_name
+
+ or to this if backport=True:
+
+ >>> from future.backports import module_name
+
+ except that it also handles dotted module names such as ``http.client``
+ The effect then is like this:
+
+ >>> from future.backports import module
+ >>> from future.backports.module import submodule
+ >>> module.submodule = submodule
+
+ Note that this would be a SyntaxError in Python:
+
+ >>> from future.backports import http.client
+
+ """
+ # Python 2.6 doesn't have importlib in the stdlib, so it requires
+ # the backported ``importlib`` package from PyPI as a dependency to use
+ # this function:
+ import importlib
+
+ if PY3:
+ return __import__(module_name)
+ else:
+ # client.blah = blah
+ # Then http.client = client
+ # etc.
+ if backport:
+ prefix = 'future.backports'
+ else:
+ prefix = 'future.moves'
+ parts = prefix.split('.') + module_name.split('.')
+
+ modules = []
+ for i, part in enumerate(parts):
+ sofar = '.'.join(parts[:i+1])
+ modules.append(importlib.import_module(sofar))
+ for i, part in reversed(list(enumerate(parts))):
+ if i == 0:
+ break
+ setattr(modules[i-1], part, modules[i])
+
+ # Return the next-most top-level module after future.backports / future.moves:
+ return modules[2]
+
+
+def from_import(module_name, *symbol_names, **kwargs):
+ """
+ Example use:
+ >>> HTTPConnection = from_import('http.client', 'HTTPConnection')
+ >>> HTTPServer = from_import('http.server', 'HTTPServer')
+ >>> urlopen, urlparse = from_import('urllib.request', 'urlopen', 'urlparse')
+
+ Equivalent to this on Py3:
+
+ >>> from module_name import symbol_names[0], symbol_names[1], ...
+
+ and this on Py2:
+
+ >>> from future.moves.module_name import symbol_names[0], ...
+
+ or:
+
+ >>> from future.backports.module_name import symbol_names[0], ...
+
+ except that it also handles dotted module names such as ``http.client``.
+ """
+
+ if PY3:
+ return __import__(module_name)
+ else:
+ if 'backport' in kwargs and bool(kwargs['backport']):
+ prefix = 'future.backports'
+ else:
+ prefix = 'future.moves'
+ parts = prefix.split('.') + module_name.split('.')
+ module = importlib.import_module(prefix + '.' + module_name)
+ output = [getattr(module, name) for name in symbol_names]
+ if len(output) == 1:
+ return output[0]
+ else:
+ return output
+
+
+class exclude_local_folder_imports(object):
+ """
+ A context-manager that prevents standard library modules like configparser
+ from being imported from the local python-future source folder on Py3.
+
+ (This was need prior to v0.16.0 because the presence of a configparser
+ folder would otherwise have prevented setuptools from running on Py3. Maybe
+ it's not needed any more?)
+ """
+ def __init__(self, *args):
+ assert len(args) > 0
+ self.module_names = args
+ # Disallow dotted module names like http.client:
+ if any(['.' in m for m in self.module_names]):
+ raise NotImplementedError('Dotted module names are not supported')
+
+ def __enter__(self):
+ self.old_sys_path = copy.copy(sys.path)
+ self.old_sys_modules = copy.copy(sys.modules)
+ if sys.version_info[0] < 3:
+ return
+ # The presence of all these indicates we've found our source folder,
+ # because `builtins` won't have been installed in site-packages by setup.py:
+ FUTURE_SOURCE_SUBFOLDERS = ['future', 'past', 'libfuturize', 'libpasteurize', 'builtins']
+
+ # Look for the future source folder:
+ for folder in self.old_sys_path:
+ if all([os.path.exists(os.path.join(folder, subfolder))
+ for subfolder in FUTURE_SOURCE_SUBFOLDERS]):
+ # Found it. Remove it.
+ sys.path.remove(folder)
+
+ # Ensure we import the system module:
+ for m in self.module_names:
+ # Delete the module and any submodules from sys.modules:
+ # for key in list(sys.modules):
+ # if key == m or key.startswith(m + '.'):
+ # try:
+ # del sys.modules[key]
+ # except KeyError:
+ # pass
+ try:
+ module = __import__(m, level=0)
+ except ImportError:
+ # There's a problem importing the system module. E.g. the
+ # winreg module is not available except on Windows.
+ pass
+
+ def __exit__(self, *args):
+ # Restore sys.path and sys.modules:
+ sys.path = self.old_sys_path
+ for m in set(self.old_sys_modules.keys()) - set(sys.modules.keys()):
+ sys.modules[m] = self.old_sys_modules[m]
+
+TOP_LEVEL_MODULES = ['builtins',
+ 'copyreg',
+ 'html',
+ 'http',
+ 'queue',
+ 'reprlib',
+ 'socketserver',
+ 'test',
+ 'tkinter',
+ 'winreg',
+ 'xmlrpc',
+ '_dummy_thread',
+ '_markupbase',
+ '_thread',
+ ]
+
+def import_top_level_modules():
+ with exclude_local_folder_imports(*TOP_LEVEL_MODULES):
+ for m in TOP_LEVEL_MODULES:
+ try:
+ __import__(m)
+ except ImportError: # e.g. winreg
+ pass
diff --git a/venv/lib/python3.10/site-packages/future/standard_library/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/standard_library/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..f186161
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/standard_library/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/tests/__init__.py b/venv/lib/python3.10/site-packages/future/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/future/tests/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/tests/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..9df5e5b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/tests/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/tests/__pycache__/base.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/tests/__pycache__/base.cpython-310.pyc
new file mode 100644
index 0000000..e155cb5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/tests/__pycache__/base.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/tests/base.py b/venv/lib/python3.10/site-packages/future/tests/base.py
new file mode 100644
index 0000000..4ef437b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/tests/base.py
@@ -0,0 +1,539 @@
+from __future__ import print_function, absolute_import
+import os
+import tempfile
+import unittest
+import sys
+import re
+import warnings
+import io
+from textwrap import dedent
+
+from future.utils import bind_method, PY26, PY3, PY2, PY27
+from future.moves.subprocess import check_output, STDOUT, CalledProcessError
+
+if PY26:
+ import unittest2 as unittest
+
+
+def reformat_code(code):
+ """
+ Removes any leading \n and dedents.
+ """
+ if code.startswith('\n'):
+ code = code[1:]
+ return dedent(code)
+
+
+def order_future_lines(code):
+ """
+ Returns the code block with any ``__future__`` import lines sorted, and
+ then any ``future`` import lines sorted, then any ``builtins`` import lines
+ sorted.
+
+ This only sorts the lines within the expected blocks.
+
+ See test_order_future_lines() for an example.
+ """
+
+ # We need .splitlines(keepends=True), which doesn't exist on Py2,
+ # so we use this instead:
+ lines = code.split('\n')
+
+ uufuture_line_numbers = [i for i, line in enumerate(lines)
+ if line.startswith('from __future__ import ')]
+
+ future_line_numbers = [i for i, line in enumerate(lines)
+ if line.startswith('from future')
+ or line.startswith('from past')]
+
+ builtins_line_numbers = [i for i, line in enumerate(lines)
+ if line.startswith('from builtins')]
+
+ assert code.lstrip() == code, ('internal usage error: '
+ 'dedent the code before calling order_future_lines()')
+
+ def mymax(numbers):
+ return max(numbers) if len(numbers) > 0 else 0
+
+ def mymin(numbers):
+ return min(numbers) if len(numbers) > 0 else float('inf')
+
+ assert mymax(uufuture_line_numbers) <= mymin(future_line_numbers), \
+ 'the __future__ and future imports are out of order'
+
+ # assert mymax(future_line_numbers) <= mymin(builtins_line_numbers), \
+ # 'the future and builtins imports are out of order'
+
+ uul = sorted([lines[i] for i in uufuture_line_numbers])
+ sorted_uufuture_lines = dict(zip(uufuture_line_numbers, uul))
+
+ fl = sorted([lines[i] for i in future_line_numbers])
+ sorted_future_lines = dict(zip(future_line_numbers, fl))
+
+ bl = sorted([lines[i] for i in builtins_line_numbers])
+ sorted_builtins_lines = dict(zip(builtins_line_numbers, bl))
+
+ # Replace the old unsorted "from __future__ import ..." lines with the
+ # new sorted ones:
+ new_lines = []
+ for i in range(len(lines)):
+ if i in uufuture_line_numbers:
+ new_lines.append(sorted_uufuture_lines[i])
+ elif i in future_line_numbers:
+ new_lines.append(sorted_future_lines[i])
+ elif i in builtins_line_numbers:
+ new_lines.append(sorted_builtins_lines[i])
+ else:
+ new_lines.append(lines[i])
+ return '\n'.join(new_lines)
+
+
+class VerboseCalledProcessError(CalledProcessError):
+ """
+ Like CalledProcessError, but it displays more information (message and
+ script output) for diagnosing test failures etc.
+ """
+ def __init__(self, msg, returncode, cmd, output=None):
+ self.msg = msg
+ self.returncode = returncode
+ self.cmd = cmd
+ self.output = output
+
+ def __str__(self):
+ return ("Command '%s' failed with exit status %d\nMessage: %s\nOutput: %s"
+ % (self.cmd, self.returncode, self.msg, self.output))
+
+class FuturizeError(VerboseCalledProcessError):
+ pass
+
+class PasteurizeError(VerboseCalledProcessError):
+ pass
+
+
+class CodeHandler(unittest.TestCase):
+ """
+ Handy mixin for test classes for writing / reading / futurizing /
+ running .py files in the test suite.
+ """
+ def setUp(self):
+ """
+ The outputs from the various futurize stages should have the
+ following headers:
+ """
+ # After stage1:
+ # TODO: use this form after implementing a fixer to consolidate
+ # __future__ imports into a single line:
+ # self.headers1 = """
+ # from __future__ import absolute_import, division, print_function
+ # """
+ self.headers1 = reformat_code("""
+ from __future__ import absolute_import
+ from __future__ import division
+ from __future__ import print_function
+ """)
+
+ # After stage2 --all-imports:
+ # TODO: use this form after implementing a fixer to consolidate
+ # __future__ imports into a single line:
+ # self.headers2 = """
+ # from __future__ import (absolute_import, division,
+ # print_function, unicode_literals)
+ # from future import standard_library
+ # from future.builtins import *
+ # """
+ self.headers2 = reformat_code("""
+ from __future__ import absolute_import
+ from __future__ import division
+ from __future__ import print_function
+ from __future__ import unicode_literals
+ from future import standard_library
+ standard_library.install_aliases()
+ from builtins import *
+ """)
+ self.interpreters = [sys.executable]
+ self.tempdir = tempfile.mkdtemp() + os.path.sep
+ pypath = os.getenv('PYTHONPATH')
+ if pypath:
+ self.env = {'PYTHONPATH': os.getcwd() + os.pathsep + pypath}
+ else:
+ self.env = {'PYTHONPATH': os.getcwd()}
+
+ def convert(self, code, stages=(1, 2), all_imports=False, from3=False,
+ reformat=True, run=True, conservative=False):
+ """
+ Converts the code block using ``futurize`` and returns the
+ resulting code.
+
+ Passing stages=[1] or stages=[2] passes the flag ``--stage1`` or
+ ``stage2`` to ``futurize``. Passing both stages runs ``futurize``
+ with both stages by default.
+
+ If from3 is False, runs ``futurize``, converting from Python 2 to
+ both 2 and 3. If from3 is True, runs ``pasteurize`` to convert
+ from Python 3 to both 2 and 3.
+
+ Optionally reformats the code block first using the reformat() function.
+
+ If run is True, runs the resulting code under all Python
+ interpreters in self.interpreters.
+ """
+ if reformat:
+ code = reformat_code(code)
+ self._write_test_script(code)
+ self._futurize_test_script(stages=stages, all_imports=all_imports,
+ from3=from3, conservative=conservative)
+ output = self._read_test_script()
+ if run:
+ for interpreter in self.interpreters:
+ _ = self._run_test_script(interpreter=interpreter)
+ return output
+
+ def compare(self, output, expected, ignore_imports=True):
+ """
+ Compares whether the code blocks are equal. If not, raises an
+ exception so the test fails. Ignores any trailing whitespace like
+ blank lines.
+
+ If ignore_imports is True, passes the code blocks into the
+ strip_future_imports method.
+
+ If one code block is a unicode string and the other a
+ byte-string, it assumes the byte-string is encoded as utf-8.
+ """
+ if ignore_imports:
+ output = self.strip_future_imports(output)
+ expected = self.strip_future_imports(expected)
+ if isinstance(output, bytes) and not isinstance(expected, bytes):
+ output = output.decode('utf-8')
+ if isinstance(expected, bytes) and not isinstance(output, bytes):
+ expected = expected.decode('utf-8')
+ self.assertEqual(order_future_lines(output.rstrip()),
+ expected.rstrip())
+
+ def strip_future_imports(self, code):
+ """
+ Strips any of these import lines:
+
+ from __future__ import
+ from future
+ from future.
+ from builtins
+
+ or any line containing:
+ install_hooks()
+ or:
+ install_aliases()
+
+ Limitation: doesn't handle imports split across multiple lines like
+ this:
+
+ from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+ """
+ output = []
+ # We need .splitlines(keepends=True), which doesn't exist on Py2,
+ # so we use this instead:
+ for line in code.split('\n'):
+ if not (line.startswith('from __future__ import ')
+ or line.startswith('from future ')
+ or line.startswith('from builtins ')
+ or 'install_hooks()' in line
+ or 'install_aliases()' in line
+ # but don't match "from future_builtins" :)
+ or line.startswith('from future.')):
+ output.append(line)
+ return '\n'.join(output)
+
+ def convert_check(self, before, expected, stages=(1, 2), all_imports=False,
+ ignore_imports=True, from3=False, run=True,
+ conservative=False):
+ """
+ Convenience method that calls convert() and compare().
+
+ Reformats the code blocks automatically using the reformat_code()
+ function.
+
+ If all_imports is passed, we add the appropriate import headers
+ for the stage(s) selected to the ``expected`` code-block, so they
+ needn't appear repeatedly in the test code.
+
+ If ignore_imports is True, ignores the presence of any lines
+ beginning:
+
+ from __future__ import ...
+ from future import ...
+
+ for the purpose of the comparison.
+ """
+ output = self.convert(before, stages=stages, all_imports=all_imports,
+ from3=from3, run=run, conservative=conservative)
+ if all_imports:
+ headers = self.headers2 if 2 in stages else self.headers1
+ else:
+ headers = ''
+
+ reformatted = reformat_code(expected)
+ if headers in reformatted:
+ headers = ''
+
+ self.compare(output, headers + reformatted,
+ ignore_imports=ignore_imports)
+
+ def unchanged(self, code, **kwargs):
+ """
+ Convenience method to ensure the code is unchanged by the
+ futurize process.
+ """
+ self.convert_check(code, code, **kwargs)
+
+ def _write_test_script(self, code, filename='mytestscript.py'):
+ """
+ Dedents the given code (a multiline string) and writes it out to
+ a file in a temporary folder like /tmp/tmpUDCn7x/mytestscript.py.
+ """
+ if isinstance(code, bytes):
+ code = code.decode('utf-8')
+ # Be explicit about encoding the temp file as UTF-8 (issue #63):
+ with io.open(self.tempdir + filename, 'wt', encoding='utf-8') as f:
+ f.write(dedent(code))
+
+ def _read_test_script(self, filename='mytestscript.py'):
+ with io.open(self.tempdir + filename, 'rt', encoding='utf-8') as f:
+ newsource = f.read()
+ return newsource
+
+ def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2),
+ all_imports=False, from3=False,
+ conservative=False):
+ params = []
+ stages = list(stages)
+ if all_imports:
+ params.append('--all-imports')
+ if from3:
+ script = 'pasteurize.py'
+ else:
+ script = 'futurize.py'
+ if stages == [1]:
+ params.append('--stage1')
+ elif stages == [2]:
+ params.append('--stage2')
+ else:
+ assert stages == [1, 2]
+ if conservative:
+ params.append('--conservative')
+ # No extra params needed
+
+ # Absolute file path:
+ fn = self.tempdir + filename
+ call_args = [sys.executable, script] + params + ['-w', fn]
+ try:
+ output = check_output(call_args, stderr=STDOUT, env=self.env)
+ except CalledProcessError as e:
+ with open(fn) as f:
+ msg = (
+ 'Error running the command %s\n'
+ '%s\n'
+ 'Contents of file %s:\n'
+ '\n'
+ '%s') % (
+ ' '.join(call_args),
+ 'env=%s' % self.env,
+ fn,
+ '----\n%s\n----' % f.read(),
+ )
+ ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError)
+
+ if not hasattr(e, 'output'):
+ # The attribute CalledProcessError.output doesn't exist on Py2.6
+ e.output = None
+ raise ErrorClass(msg, e.returncode, e.cmd, output=e.output)
+ return output
+
+ def _run_test_script(self, filename='mytestscript.py',
+ interpreter=sys.executable):
+ # Absolute file path:
+ fn = self.tempdir + filename
+ try:
+ output = check_output([interpreter, fn],
+ env=self.env, stderr=STDOUT)
+ except CalledProcessError as e:
+ with open(fn) as f:
+ msg = (
+ 'Error running the command %s\n'
+ '%s\n'
+ 'Contents of file %s:\n'
+ '\n'
+ '%s') % (
+ ' '.join([interpreter, fn]),
+ 'env=%s' % self.env,
+ fn,
+ '----\n%s\n----' % f.read(),
+ )
+ if not hasattr(e, 'output'):
+ # The attribute CalledProcessError.output doesn't exist on Py2.6
+ e.output = None
+ raise VerboseCalledProcessError(msg, e.returncode, e.cmd, output=e.output)
+ return output
+
+
+# Decorator to skip some tests on Python 2.6 ...
+skip26 = unittest.skipIf(PY26, "this test is known to fail on Py2.6")
+
+
+def expectedFailurePY3(func):
+ if not PY3:
+ return func
+ return unittest.expectedFailure(func)
+
+def expectedFailurePY26(func):
+ if not PY26:
+ return func
+ return unittest.expectedFailure(func)
+
+
+def expectedFailurePY27(func):
+ if not PY27:
+ return func
+ return unittest.expectedFailure(func)
+
+
+def expectedFailurePY2(func):
+ if not PY2:
+ return func
+ return unittest.expectedFailure(func)
+
+
+# Renamed in Py3.3:
+if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
+ unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
+
+# From Py3.3:
+def assertRegex(self, text, expected_regex, msg=None):
+ """Fail the test unless the text matches the regular expression."""
+ if isinstance(expected_regex, (str, unicode)):
+ assert expected_regex, "expected_regex must not be empty."
+ expected_regex = re.compile(expected_regex)
+ if not expected_regex.search(text):
+ msg = msg or "Regex didn't match"
+ msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text)
+ raise self.failureException(msg)
+
+if not hasattr(unittest.TestCase, 'assertRegex'):
+ bind_method(unittest.TestCase, 'assertRegex', assertRegex)
+
+class _AssertRaisesBaseContext(object):
+
+ def __init__(self, expected, test_case, callable_obj=None,
+ expected_regex=None):
+ self.expected = expected
+ self.test_case = test_case
+ if callable_obj is not None:
+ try:
+ self.obj_name = callable_obj.__name__
+ except AttributeError:
+ self.obj_name = str(callable_obj)
+ else:
+ self.obj_name = None
+ if isinstance(expected_regex, (bytes, str)):
+ expected_regex = re.compile(expected_regex)
+ self.expected_regex = expected_regex
+ self.msg = None
+
+ def _raiseFailure(self, standardMsg):
+ msg = self.test_case._formatMessage(self.msg, standardMsg)
+ raise self.test_case.failureException(msg)
+
+ def handle(self, name, callable_obj, args, kwargs):
+ """
+ If callable_obj is None, assertRaises/Warns is being used as a
+ context manager, so check for a 'msg' kwarg and return self.
+ If callable_obj is not None, call it passing args and kwargs.
+ """
+ if callable_obj is None:
+ self.msg = kwargs.pop('msg', None)
+ return self
+ with self:
+ callable_obj(*args, **kwargs)
+
+class _AssertWarnsContext(_AssertRaisesBaseContext):
+ """A context manager used to implement TestCase.assertWarns* methods."""
+
+ def __enter__(self):
+ # The __warningregistry__'s need to be in a pristine state for tests
+ # to work properly.
+ for v in sys.modules.values():
+ if getattr(v, '__warningregistry__', None):
+ v.__warningregistry__ = {}
+ self.warnings_manager = warnings.catch_warnings(record=True)
+ self.warnings = self.warnings_manager.__enter__()
+ warnings.simplefilter("always", self.expected)
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.warnings_manager.__exit__(exc_type, exc_value, tb)
+ if exc_type is not None:
+ # let unexpected exceptions pass through
+ return
+ try:
+ exc_name = self.expected.__name__
+ except AttributeError:
+ exc_name = str(self.expected)
+ first_matching = None
+ for m in self.warnings:
+ w = m.message
+ if not isinstance(w, self.expected):
+ continue
+ if first_matching is None:
+ first_matching = w
+ if (self.expected_regex is not None and
+ not self.expected_regex.search(str(w))):
+ continue
+ # store warning for later retrieval
+ self.warning = w
+ self.filename = m.filename
+ self.lineno = m.lineno
+ return
+ # Now we simply try to choose a helpful failure message
+ if first_matching is not None:
+ self._raiseFailure('"{}" does not match "{}"'.format(
+ self.expected_regex.pattern, str(first_matching)))
+ if self.obj_name:
+ self._raiseFailure("{} not triggered by {}".format(exc_name,
+ self.obj_name))
+ else:
+ self._raiseFailure("{} not triggered".format(exc_name))
+
+
+def assertWarns(self, expected_warning, callable_obj=None, *args, **kwargs):
+ """Fail unless a warning of class warnClass is triggered
+ by callable_obj when invoked with arguments args and keyword
+ arguments kwargs. If a different type of warning is
+ triggered, it will not be handled: depending on the other
+ warning filtering rules in effect, it might be silenced, printed
+ out, or raised as an exception.
+
+ If called with callable_obj omitted or None, will return a
+ context object used like this::
+
+ with self.assertWarns(SomeWarning):
+ do_something()
+
+ An optional keyword argument 'msg' can be provided when assertWarns
+ is used as a context object.
+
+ The context manager keeps a reference to the first matching
+ warning as the 'warning' attribute; similarly, the 'filename'
+ and 'lineno' attributes give you information about the line
+ of Python code from which the warning was triggered.
+ This allows you to inspect the warning after the assertion::
+
+ with self.assertWarns(SomeWarning) as cm:
+ do_something()
+ the_warning = cm.warning
+ self.assertEqual(the_warning.some_attribute, 147)
+ """
+ context = _AssertWarnsContext(expected_warning, self, callable_obj)
+ return context.handle('assertWarns', callable_obj, args, kwargs)
+
+if not hasattr(unittest.TestCase, 'assertWarns'):
+ bind_method(unittest.TestCase, 'assertWarns', assertWarns)
diff --git a/venv/lib/python3.10/site-packages/future/types/__init__.py b/venv/lib/python3.10/site-packages/future/types/__init__.py
new file mode 100644
index 0000000..0625077
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/__init__.py
@@ -0,0 +1,257 @@
+"""
+This module contains backports the data types that were significantly changed
+in the transition from Python 2 to Python 3.
+
+- an implementation of Python 3's bytes object (pure Python subclass of
+ Python 2's builtin 8-bit str type)
+- an implementation of Python 3's str object (pure Python subclass of
+ Python 2's builtin unicode type)
+- a backport of the range iterator from Py3 with slicing support
+
+It is used as follows::
+
+ from __future__ import division, absolute_import, print_function
+ from builtins import bytes, dict, int, range, str
+
+to bring in the new semantics for these functions from Python 3. And
+then, for example::
+
+ b = bytes(b'ABCD')
+ assert list(b) == [65, 66, 67, 68]
+ assert repr(b) == "b'ABCD'"
+ assert [65, 66] in b
+
+ # These raise TypeErrors:
+ # b + u'EFGH'
+ # b.split(u'B')
+ # bytes(b',').join([u'Fred', u'Bill'])
+
+
+ s = str(u'ABCD')
+
+ # These raise TypeErrors:
+ # s.join([b'Fred', b'Bill'])
+ # s.startswith(b'A')
+ # b'B' in s
+ # s.find(b'A')
+ # s.replace(u'A', b'a')
+
+ # This raises an AttributeError:
+ # s.decode('utf-8')
+
+ assert repr(s) == 'ABCD' # consistent repr with Py3 (no u prefix)
+
+
+ for i in range(10**11)[:10]:
+ pass
+
+and::
+
+ class VerboseList(list):
+ def append(self, item):
+ print('Adding an item')
+ super().append(item) # new simpler super() function
+
+For more information:
+---------------------
+
+- future.types.newbytes
+- future.types.newdict
+- future.types.newint
+- future.types.newobject
+- future.types.newrange
+- future.types.newstr
+
+
+Notes
+=====
+
+range()
+-------
+``range`` is a custom class that backports the slicing behaviour from
+Python 3 (based on the ``xrange`` module by Dan Crosta). See the
+``newrange`` module docstring for more details.
+
+
+super()
+-------
+``super()`` is based on Ryan Kelly's ``magicsuper`` module. See the
+``newsuper`` module docstring for more details.
+
+
+round()
+-------
+Python 3 modifies the behaviour of ``round()`` to use "Banker's Rounding".
+See http://stackoverflow.com/a/10825998. See the ``newround`` module
+docstring for more details.
+
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import functools
+from numbers import Integral
+
+from future import utils
+
+
+# Some utility functions to enforce strict type-separation of unicode str and
+# bytes:
+def disallow_types(argnums, disallowed_types):
+ """
+ A decorator that raises a TypeError if any of the given numbered
+ arguments is of the corresponding given type (e.g. bytes or unicode
+ string).
+
+ For example:
+
+ @disallow_types([0, 1], [unicode, bytes])
+ def f(a, b):
+ pass
+
+ raises a TypeError when f is called if a unicode object is passed as
+ `a` or a bytes object is passed as `b`.
+
+ This also skips over keyword arguments, so
+
+ @disallow_types([0, 1], [unicode, bytes])
+ def g(a, b=None):
+ pass
+
+ doesn't raise an exception if g is called with only one argument a,
+ e.g.:
+
+ g(b'Byte string')
+
+ Example use:
+
+ >>> class newbytes(object):
+ ... @disallow_types([1], [unicode])
+ ... def __add__(self, other):
+ ... pass
+
+ >>> newbytes('1234') + u'1234' #doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ TypeError: can't concat 'bytes' to (unicode) str
+ """
+
+ def decorator(function):
+
+ @functools.wraps(function)
+ def wrapper(*args, **kwargs):
+ # These imports are just for this decorator, and are defined here
+ # to prevent circular imports:
+ from .newbytes import newbytes
+ from .newint import newint
+ from .newstr import newstr
+
+ errmsg = "argument can't be {0}"
+ for (argnum, mytype) in zip(argnums, disallowed_types):
+ # Handle the case where the type is passed as a string like 'newbytes'.
+ if isinstance(mytype, str) or isinstance(mytype, bytes):
+ mytype = locals()[mytype]
+
+ # Only restrict kw args only if they are passed:
+ if len(args) <= argnum:
+ break
+
+ # Here we use type() rather than isinstance() because
+ # __instancecheck__ is being overridden. E.g.
+ # isinstance(b'abc', newbytes) is True on Py2.
+ if type(args[argnum]) == mytype:
+ raise TypeError(errmsg.format(mytype))
+
+ return function(*args, **kwargs)
+ return wrapper
+ return decorator
+
+
+def no(mytype, argnums=(1,)):
+ """
+ A shortcut for the disallow_types decorator that disallows only one type
+ (in any position in argnums).
+
+ Example use:
+
+ >>> class newstr(object):
+ ... @no('bytes')
+ ... def __add__(self, other):
+ ... pass
+
+ >>> newstr(u'1234') + b'1234' #doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ TypeError: argument can't be bytes
+
+ The object can also be passed directly, but passing the string helps
+ to prevent circular import problems.
+ """
+ if isinstance(argnums, Integral):
+ argnums = (argnums,)
+ disallowed_types = [mytype] * len(argnums)
+ return disallow_types(argnums, disallowed_types)
+
+
+def issubset(list1, list2):
+ """
+ Examples:
+
+ >>> issubset([], [65, 66, 67])
+ True
+ >>> issubset([65], [65, 66, 67])
+ True
+ >>> issubset([65, 66], [65, 66, 67])
+ True
+ >>> issubset([65, 67], [65, 66, 67])
+ False
+ """
+ n = len(list1)
+ for startpos in range(len(list2) - n + 1):
+ if list2[startpos:startpos+n] == list1:
+ return True
+ return False
+
+
+if utils.PY3:
+ import builtins
+ bytes = builtins.bytes
+ dict = builtins.dict
+ int = builtins.int
+ list = builtins.list
+ object = builtins.object
+ range = builtins.range
+ str = builtins.str
+
+ # The identity mapping
+ newtypes = {bytes: bytes,
+ dict: dict,
+ int: int,
+ list: list,
+ object: object,
+ range: range,
+ str: str}
+
+ __all__ = ['newtypes']
+
+else:
+
+ from .newbytes import newbytes
+ from .newdict import newdict
+ from .newint import newint
+ from .newlist import newlist
+ from .newrange import newrange
+ from .newobject import newobject
+ from .newstr import newstr
+
+ newtypes = {bytes: newbytes,
+ dict: newdict,
+ int: newint,
+ long: newint,
+ list: newlist,
+ object: newobject,
+ range: newrange,
+ str: newbytes,
+ unicode: newstr}
+
+ __all__ = ['newbytes', 'newdict', 'newint', 'newlist', 'newrange', 'newstr', 'newtypes']
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..326faa9
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newbytes.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newbytes.cpython-310.pyc
new file mode 100644
index 0000000..ac88cf0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newbytes.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newdict.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newdict.cpython-310.pyc
new file mode 100644
index 0000000..7abf1dd
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newdict.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newint.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newint.cpython-310.pyc
new file mode 100644
index 0000000..7e0f47a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newint.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newlist.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newlist.cpython-310.pyc
new file mode 100644
index 0000000..3b93535
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newlist.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newmemoryview.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newmemoryview.cpython-310.pyc
new file mode 100644
index 0000000..32baa06
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newmemoryview.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newobject.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newobject.cpython-310.pyc
new file mode 100644
index 0000000..2b75c75
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newobject.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newopen.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newopen.cpython-310.pyc
new file mode 100644
index 0000000..80c67c7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newopen.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newrange.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newrange.cpython-310.pyc
new file mode 100644
index 0000000..aa19a30
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newrange.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/__pycache__/newstr.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/types/__pycache__/newstr.cpython-310.pyc
new file mode 100644
index 0000000..0ea1c59
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/types/__pycache__/newstr.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/types/newbytes.py b/venv/lib/python3.10/site-packages/future/types/newbytes.py
new file mode 100644
index 0000000..c9d584a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newbytes.py
@@ -0,0 +1,460 @@
+"""
+Pure-Python implementation of a Python 3-like bytes object for Python 2.
+
+Why do this? Without it, the Python 2 bytes object is a very, very
+different beast to the Python 3 bytes object.
+"""
+
+from numbers import Integral
+import string
+import copy
+
+from future.utils import istext, isbytes, PY2, PY3, with_metaclass
+from future.types import no, issubset
+from future.types.newobject import newobject
+
+if PY2:
+ from collections import Iterable
+else:
+ from collections.abc import Iterable
+
+
+_builtin_bytes = bytes
+
+if PY3:
+ # We'll probably never use newstr on Py3 anyway...
+ unicode = str
+
+
+class BaseNewBytes(type):
+ def __instancecheck__(cls, instance):
+ if cls == newbytes:
+ return isinstance(instance, _builtin_bytes)
+ else:
+ return issubclass(instance.__class__, cls)
+
+
+def _newchr(x):
+ if isinstance(x, str): # this happens on pypy
+ return x.encode('ascii')
+ else:
+ return chr(x)
+
+
+class newbytes(with_metaclass(BaseNewBytes, _builtin_bytes)):
+ """
+ A backport of the Python 3 bytes object to Py2
+ """
+ def __new__(cls, *args, **kwargs):
+ """
+ From the Py3 bytes docstring:
+
+ bytes(iterable_of_ints) -> bytes
+ bytes(string, encoding[, errors]) -> bytes
+ bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
+ bytes(int) -> bytes object of size given by the parameter initialized with null bytes
+ bytes() -> empty bytes object
+
+ Construct an immutable array of bytes from:
+ - an iterable yielding integers in range(256)
+ - a text string encoded using the specified encoding
+ - any object implementing the buffer API.
+ - an integer
+ """
+
+ encoding = None
+ errors = None
+
+ if len(args) == 0:
+ return super(newbytes, cls).__new__(cls)
+ elif len(args) >= 2:
+ args = list(args)
+ if len(args) == 3:
+ errors = args.pop()
+ encoding=args.pop()
+ # Was: elif isinstance(args[0], newbytes):
+ # We use type() instead of the above because we're redefining
+ # this to be True for all unicode string subclasses. Warning:
+ # This may render newstr un-subclassable.
+ if type(args[0]) == newbytes:
+ # Special-case: for consistency with Py3.3, we return the same object
+ # (with the same id) if a newbytes object is passed into the
+ # newbytes constructor.
+ return args[0]
+ elif isinstance(args[0], _builtin_bytes):
+ value = args[0]
+ elif isinstance(args[0], unicode):
+ try:
+ if 'encoding' in kwargs:
+ assert encoding is None
+ encoding = kwargs['encoding']
+ if 'errors' in kwargs:
+ assert errors is None
+ errors = kwargs['errors']
+ except AssertionError:
+ raise TypeError('Argument given by name and position')
+ if encoding is None:
+ raise TypeError('unicode string argument without an encoding')
+ ###
+ # Was: value = args[0].encode(**kwargs)
+ # Python 2.6 string encode() method doesn't take kwargs:
+ # Use this instead:
+ newargs = [encoding]
+ if errors is not None:
+ newargs.append(errors)
+ value = args[0].encode(*newargs)
+ ###
+ elif hasattr(args[0], '__bytes__'):
+ value = args[0].__bytes__()
+ elif isinstance(args[0], Iterable):
+ if len(args[0]) == 0:
+ # This could be an empty list or tuple. Return b'' as on Py3.
+ value = b''
+ else:
+ # Was: elif len(args[0])>0 and isinstance(args[0][0], Integral):
+ # # It's a list of integers
+ # But then we can't index into e.g. frozensets. Try to proceed
+ # anyway.
+ try:
+ value = bytearray([_newchr(x) for x in args[0]])
+ except:
+ raise ValueError('bytes must be in range(0, 256)')
+ elif isinstance(args[0], Integral):
+ if args[0] < 0:
+ raise ValueError('negative count')
+ value = b'\x00' * args[0]
+ else:
+ value = args[0]
+ if type(value) == newbytes:
+ # Above we use type(...) rather than isinstance(...) because the
+ # newbytes metaclass overrides __instancecheck__.
+ # oldbytes(value) gives the wrong thing on Py2: the same
+ # result as str(value) on Py3, e.g. "b'abc'". (Issue #193).
+ # So we handle this case separately:
+ return copy.copy(value)
+ else:
+ return super(newbytes, cls).__new__(cls, value)
+
+ def __repr__(self):
+ return 'b' + super(newbytes, self).__repr__()
+
+ def __str__(self):
+ return 'b' + "'{0}'".format(super(newbytes, self).__str__())
+
+ def __getitem__(self, y):
+ value = super(newbytes, self).__getitem__(y)
+ if isinstance(y, Integral):
+ return ord(value)
+ else:
+ return newbytes(value)
+
+ def __getslice__(self, *args):
+ return self.__getitem__(slice(*args))
+
+ def __contains__(self, key):
+ if isinstance(key, int):
+ newbyteskey = newbytes([key])
+ # Don't use isinstance() here because we only want to catch
+ # newbytes, not Python 2 str:
+ elif type(key) == newbytes:
+ newbyteskey = key
+ else:
+ newbyteskey = newbytes(key)
+ return issubset(list(newbyteskey), list(self))
+
+ @no(unicode)
+ def __add__(self, other):
+ return newbytes(super(newbytes, self).__add__(other))
+
+ @no(unicode)
+ def __radd__(self, left):
+ return newbytes(left) + self
+
+ @no(unicode)
+ def __mul__(self, other):
+ return newbytes(super(newbytes, self).__mul__(other))
+
+ @no(unicode)
+ def __rmul__(self, other):
+ return newbytes(super(newbytes, self).__rmul__(other))
+
+ def __mod__(self, vals):
+ if isinstance(vals, newbytes):
+ vals = _builtin_bytes.__str__(vals)
+
+ elif isinstance(vals, tuple):
+ newvals = []
+ for v in vals:
+ if isinstance(v, newbytes):
+ v = _builtin_bytes.__str__(v)
+ newvals.append(v)
+ vals = tuple(newvals)
+
+ elif (hasattr(vals.__class__, '__getitem__') and
+ hasattr(vals.__class__, 'iteritems')):
+ for k, v in vals.iteritems():
+ if isinstance(v, newbytes):
+ vals[k] = _builtin_bytes.__str__(v)
+
+ return _builtin_bytes.__mod__(self, vals)
+
+ def __imod__(self, other):
+ return self.__mod__(other)
+
+ def join(self, iterable_of_bytes):
+ errmsg = 'sequence item {0}: expected bytes, {1} found'
+ if isbytes(iterable_of_bytes) or istext(iterable_of_bytes):
+ raise TypeError(errmsg.format(0, type(iterable_of_bytes)))
+ for i, item in enumerate(iterable_of_bytes):
+ if istext(item):
+ raise TypeError(errmsg.format(i, type(item)))
+ return newbytes(super(newbytes, self).join(iterable_of_bytes))
+
+ @classmethod
+ def fromhex(cls, string):
+ # Only on Py2:
+ return cls(string.replace(' ', '').decode('hex'))
+
+ @no(unicode)
+ def find(self, sub, *args):
+ return super(newbytes, self).find(sub, *args)
+
+ @no(unicode)
+ def rfind(self, sub, *args):
+ return super(newbytes, self).rfind(sub, *args)
+
+ @no(unicode, (1, 2))
+ def replace(self, old, new, *args):
+ return newbytes(super(newbytes, self).replace(old, new, *args))
+
+ def encode(self, *args):
+ raise AttributeError("encode method has been disabled in newbytes")
+
+ def decode(self, encoding='utf-8', errors='strict'):
+ """
+ Returns a newstr (i.e. unicode subclass)
+
+ Decode B using the codec registered for encoding. Default encoding
+ is 'utf-8'. errors may be given to set a different error
+ handling scheme. Default is 'strict' meaning that encoding errors raise
+ a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
+ as well as any other name registered with codecs.register_error that is
+ able to handle UnicodeDecodeErrors.
+ """
+ # Py2 str.encode() takes encoding and errors as optional parameter,
+ # not keyword arguments as in Python 3 str.
+
+ from future.types.newstr import newstr
+
+ if errors == 'surrogateescape':
+ from future.utils.surrogateescape import register_surrogateescape
+ register_surrogateescape()
+
+ return newstr(super(newbytes, self).decode(encoding, errors))
+
+ # This is currently broken:
+ # # We implement surrogateescape error handling here in addition rather
+ # # than relying on the custom error handler from
+ # # future.utils.surrogateescape to be registered globally, even though
+ # # that is fine in the case of decoding. (But not encoding: see the
+ # # comments in newstr.encode()``.)
+ #
+ # if errors == 'surrogateescape':
+ # # Decode char by char
+ # mybytes = []
+ # for code in self:
+ # # Code is an int
+ # if 0x80 <= code <= 0xFF:
+ # b = 0xDC00 + code
+ # elif code <= 0x7F:
+ # b = _unichr(c).decode(encoding=encoding)
+ # else:
+ # # # It may be a bad byte
+ # # FIXME: What to do in this case? See the Py3 docs / tests.
+ # # # Try swallowing it.
+ # # continue
+ # # print("RAISE!")
+ # raise NotASurrogateError
+ # mybytes.append(b)
+ # return newbytes(mybytes)
+ # return newbytes(super(newstr, self).decode(encoding, errors))
+
+ @no(unicode)
+ def startswith(self, prefix, *args):
+ return super(newbytes, self).startswith(prefix, *args)
+
+ @no(unicode)
+ def endswith(self, prefix, *args):
+ return super(newbytes, self).endswith(prefix, *args)
+
+ @no(unicode)
+ def split(self, sep=None, maxsplit=-1):
+ # Py2 str.split() takes maxsplit as an optional parameter, not as a
+ # keyword argument as in Python 3 bytes.
+ parts = super(newbytes, self).split(sep, maxsplit)
+ return [newbytes(part) for part in parts]
+
+ def splitlines(self, keepends=False):
+ """
+ B.splitlines([keepends]) -> list of lines
+
+ Return a list of the lines in B, breaking at line boundaries.
+ Line breaks are not included in the resulting list unless keepends
+ is given and true.
+ """
+ # Py2 str.splitlines() takes keepends as an optional parameter,
+ # not as a keyword argument as in Python 3 bytes.
+ parts = super(newbytes, self).splitlines(keepends)
+ return [newbytes(part) for part in parts]
+
+ @no(unicode)
+ def rsplit(self, sep=None, maxsplit=-1):
+ # Py2 str.rsplit() takes maxsplit as an optional parameter, not as a
+ # keyword argument as in Python 3 bytes.
+ parts = super(newbytes, self).rsplit(sep, maxsplit)
+ return [newbytes(part) for part in parts]
+
+ @no(unicode)
+ def partition(self, sep):
+ parts = super(newbytes, self).partition(sep)
+ return tuple(newbytes(part) for part in parts)
+
+ @no(unicode)
+ def rpartition(self, sep):
+ parts = super(newbytes, self).rpartition(sep)
+ return tuple(newbytes(part) for part in parts)
+
+ @no(unicode, (1,))
+ def rindex(self, sub, *args):
+ '''
+ S.rindex(sub [,start [,end]]) -> int
+
+ Like S.rfind() but raise ValueError when the substring is not found.
+ '''
+ pos = self.rfind(sub, *args)
+ if pos == -1:
+ raise ValueError('substring not found')
+
+ @no(unicode)
+ def index(self, sub, *args):
+ '''
+ Returns index of sub in bytes.
+ Raises ValueError if byte is not in bytes and TypeError if can't
+ be converted bytes or its length is not 1.
+ '''
+ if isinstance(sub, int):
+ if len(args) == 0:
+ start, end = 0, len(self)
+ elif len(args) == 1:
+ start = args[0]
+ elif len(args) == 2:
+ start, end = args
+ else:
+ raise TypeError('takes at most 3 arguments')
+ return list(self)[start:end].index(sub)
+ if not isinstance(sub, bytes):
+ try:
+ sub = self.__class__(sub)
+ except (TypeError, ValueError):
+ raise TypeError("can't convert sub to bytes")
+ try:
+ return super(newbytes, self).index(sub, *args)
+ except ValueError:
+ raise ValueError('substring not found')
+
+ def __eq__(self, other):
+ if isinstance(other, (_builtin_bytes, bytearray)):
+ return super(newbytes, self).__eq__(other)
+ else:
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, _builtin_bytes):
+ return super(newbytes, self).__ne__(other)
+ else:
+ return True
+
+ unorderable_err = 'unorderable types: bytes() and {0}'
+
+ def __lt__(self, other):
+ if isinstance(other, _builtin_bytes):
+ return super(newbytes, self).__lt__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __le__(self, other):
+ if isinstance(other, _builtin_bytes):
+ return super(newbytes, self).__le__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __gt__(self, other):
+ if isinstance(other, _builtin_bytes):
+ return super(newbytes, self).__gt__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __ge__(self, other):
+ if isinstance(other, _builtin_bytes):
+ return super(newbytes, self).__ge__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __native__(self):
+ # We can't just feed a newbytes object into str(), because
+ # newbytes.__str__() returns e.g. "b'blah'", consistent with Py3 bytes.
+ return super(newbytes, self).__str__()
+
+ def __getattribute__(self, name):
+ """
+ A trick to cause the ``hasattr`` builtin-fn to return False for
+ the 'encode' method on Py2.
+ """
+ if name in ['encode', u'encode']:
+ raise AttributeError("encode method has been disabled in newbytes")
+ return super(newbytes, self).__getattribute__(name)
+
+ @no(unicode)
+ def rstrip(self, bytes_to_strip=None):
+ """
+ Strip trailing bytes contained in the argument.
+ If the argument is omitted, strip trailing ASCII whitespace.
+ """
+ return newbytes(super(newbytes, self).rstrip(bytes_to_strip))
+
+ @no(unicode)
+ def strip(self, bytes_to_strip=None):
+ """
+ Strip leading and trailing bytes contained in the argument.
+ If the argument is omitted, strip trailing ASCII whitespace.
+ """
+ return newbytes(super(newbytes, self).strip(bytes_to_strip))
+
+ def lower(self):
+ """
+ b.lower() -> copy of b
+
+ Return a copy of b with all ASCII characters converted to lowercase.
+ """
+ return newbytes(super(newbytes, self).lower())
+
+ @no(unicode)
+ def upper(self):
+ """
+ b.upper() -> copy of b
+
+ Return a copy of b with all ASCII characters converted to uppercase.
+ """
+ return newbytes(super(newbytes, self).upper())
+
+ @classmethod
+ @no(unicode)
+ def maketrans(cls, frm, to):
+ """
+ B.maketrans(frm, to) -> translation table
+
+ Return a translation table (a bytes object of length 256) suitable
+ for use in the bytes or bytearray translate method where each byte
+ in frm is mapped to the byte at the same position in to.
+ The bytes objects frm and to must be of the same length.
+ """
+ return newbytes(string.maketrans(frm, to))
+
+
+__all__ = ['newbytes']
diff --git a/venv/lib/python3.10/site-packages/future/types/newdict.py b/venv/lib/python3.10/site-packages/future/types/newdict.py
new file mode 100644
index 0000000..d90316c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newdict.py
@@ -0,0 +1,76 @@
+"""
+A dict subclass for Python 2 that behaves like Python 3's dict
+
+Example use:
+
+>>> from builtins import dict
+>>> d1 = dict() # instead of {} for an empty dict
+>>> d2 = dict(key1='value1', key2='value2')
+
+The keys, values and items methods now return iterators on Python 2.x
+(with set-like behaviour on Python 2.7).
+
+>>> for d in (d1, d2):
+... assert not isinstance(d.keys(), list)
+... assert not isinstance(d.values(), list)
+... assert not isinstance(d.items(), list)
+"""
+
+import sys
+
+from future.utils import with_metaclass
+from future.types.newobject import newobject
+
+
+_builtin_dict = dict
+ver = sys.version_info
+
+
+class BaseNewDict(type):
+ def __instancecheck__(cls, instance):
+ if cls == newdict:
+ return isinstance(instance, _builtin_dict)
+ else:
+ return issubclass(instance.__class__, cls)
+
+
+class newdict(with_metaclass(BaseNewDict, _builtin_dict)):
+ """
+ A backport of the Python 3 dict object to Py2
+ """
+
+ if ver >= (3,):
+ # Inherit items, keys and values from `dict` in 3.x
+ pass
+ elif ver >= (2, 7):
+ items = dict.viewitems
+ keys = dict.viewkeys
+ values = dict.viewvalues
+ else:
+ items = dict.iteritems
+ keys = dict.iterkeys
+ values = dict.itervalues
+
+ def __new__(cls, *args, **kwargs):
+ """
+ dict() -> new empty dictionary
+ dict(mapping) -> new dictionary initialized from a mapping object's
+ (key, value) pairs
+ dict(iterable) -> new dictionary initialized as if via:
+ d = {}
+ for k, v in iterable:
+ d[k] = v
+ dict(**kwargs) -> new dictionary initialized with the name=value pairs
+ in the keyword argument list. For example: dict(one=1, two=2)
+ """
+
+ return super(newdict, cls).__new__(cls, *args)
+
+ def __native__(self):
+ """
+ Hook for the future.utils.native() function
+ """
+ return dict(self)
+
+
+__all__ = ['newdict']
diff --git a/venv/lib/python3.10/site-packages/future/types/newint.py b/venv/lib/python3.10/site-packages/future/types/newint.py
new file mode 100644
index 0000000..ebc5715
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newint.py
@@ -0,0 +1,386 @@
+"""
+Backport of Python 3's int, based on Py2's long.
+
+They are very similar. The most notable difference is:
+
+- representation: trailing L in Python 2 removed in Python 3
+"""
+from __future__ import division
+
+import struct
+
+from future.types.newbytes import newbytes
+from future.types.newobject import newobject
+from future.utils import PY3, isint, istext, isbytes, with_metaclass, native
+
+
+if PY3:
+ long = int
+ from collections.abc import Iterable
+else:
+ from collections import Iterable
+
+
+class BaseNewInt(type):
+ def __instancecheck__(cls, instance):
+ if cls == newint:
+ # Special case for Py2 short or long int
+ return isinstance(instance, (int, long))
+ else:
+ return issubclass(instance.__class__, cls)
+
+
+class newint(with_metaclass(BaseNewInt, long)):
+ """
+ A backport of the Python 3 int object to Py2
+ """
+ def __new__(cls, x=0, base=10):
+ """
+ From the Py3 int docstring:
+
+ | int(x=0) -> integer
+ | int(x, base=10) -> integer
+ |
+ | Convert a number or string to an integer, or return 0 if no
+ | arguments are given. If x is a number, return x.__int__(). For
+ | floating point numbers, this truncates towards zero.
+ |
+ | If x is not a number or if base is given, then x must be a string,
+ | bytes, or bytearray instance representing an integer literal in the
+ | given base. The literal can be preceded by '+' or '-' and be
+ | surrounded by whitespace. The base defaults to 10. Valid bases are
+ | 0 and 2-36. Base 0 means to interpret the base from the string as an
+ | integer literal.
+ | >>> int('0b100', base=0)
+ | 4
+
+ """
+ try:
+ val = x.__int__()
+ except AttributeError:
+ val = x
+ else:
+ if not isint(val):
+ raise TypeError('__int__ returned non-int ({0})'.format(
+ type(val)))
+
+ if base != 10:
+ # Explicit base
+ if not (istext(val) or isbytes(val) or isinstance(val, bytearray)):
+ raise TypeError(
+ "int() can't convert non-string with explicit base")
+ try:
+ return super(newint, cls).__new__(cls, val, base)
+ except TypeError:
+ return super(newint, cls).__new__(cls, newbytes(val), base)
+ # After here, base is 10
+ try:
+ return super(newint, cls).__new__(cls, val)
+ except TypeError:
+ # Py2 long doesn't handle bytearray input with an explicit base, so
+ # handle this here.
+ # Py3: int(bytearray(b'10'), 2) == 2
+ # Py2: int(bytearray(b'10'), 2) == 2 raises TypeError
+ # Py2: long(bytearray(b'10'), 2) == 2 raises TypeError
+ try:
+ return super(newint, cls).__new__(cls, newbytes(val))
+ except:
+ raise TypeError("newint argument must be a string or a number,"
+ "not '{0}'".format(type(val)))
+
+ def __repr__(self):
+ """
+ Without the L suffix
+ """
+ value = super(newint, self).__repr__()
+ assert value[-1] == 'L'
+ return value[:-1]
+
+ def __add__(self, other):
+ value = super(newint, self).__add__(other)
+ if value is NotImplemented:
+ return long(self) + other
+ return newint(value)
+
+ def __radd__(self, other):
+ value = super(newint, self).__radd__(other)
+ if value is NotImplemented:
+ return other + long(self)
+ return newint(value)
+
+ def __sub__(self, other):
+ value = super(newint, self).__sub__(other)
+ if value is NotImplemented:
+ return long(self) - other
+ return newint(value)
+
+ def __rsub__(self, other):
+ value = super(newint, self).__rsub__(other)
+ if value is NotImplemented:
+ return other - long(self)
+ return newint(value)
+
+ def __mul__(self, other):
+ value = super(newint, self).__mul__(other)
+ if isint(value):
+ return newint(value)
+ elif value is NotImplemented:
+ return long(self) * other
+ return value
+
+ def __rmul__(self, other):
+ value = super(newint, self).__rmul__(other)
+ if isint(value):
+ return newint(value)
+ elif value is NotImplemented:
+ return other * long(self)
+ return value
+
+ def __div__(self, other):
+ # We override this rather than e.g. relying on object.__div__ or
+ # long.__div__ because we want to wrap the value in a newint()
+ # call if other is another int
+ value = long(self) / other
+ if isinstance(other, (int, long)):
+ return newint(value)
+ else:
+ return value
+
+ def __rdiv__(self, other):
+ value = other / long(self)
+ if isinstance(other, (int, long)):
+ return newint(value)
+ else:
+ return value
+
+ def __idiv__(self, other):
+ # long has no __idiv__ method. Use __itruediv__ and cast back to
+ # newint:
+ value = self.__itruediv__(other)
+ if isinstance(other, (int, long)):
+ return newint(value)
+ else:
+ return value
+
+ def __truediv__(self, other):
+ value = super(newint, self).__truediv__(other)
+ if value is NotImplemented:
+ value = long(self) / other
+ return value
+
+ def __rtruediv__(self, other):
+ return super(newint, self).__rtruediv__(other)
+
+ def __itruediv__(self, other):
+ # long has no __itruediv__ method
+ mylong = long(self)
+ mylong /= other
+ return mylong
+
+ def __floordiv__(self, other):
+ return newint(super(newint, self).__floordiv__(other))
+
+ def __rfloordiv__(self, other):
+ return newint(super(newint, self).__rfloordiv__(other))
+
+ def __ifloordiv__(self, other):
+ # long has no __ifloordiv__ method
+ mylong = long(self)
+ mylong //= other
+ return newint(mylong)
+
+ def __mod__(self, other):
+ value = super(newint, self).__mod__(other)
+ if value is NotImplemented:
+ return long(self) % other
+ return newint(value)
+
+ def __rmod__(self, other):
+ value = super(newint, self).__rmod__(other)
+ if value is NotImplemented:
+ return other % long(self)
+ return newint(value)
+
+ def __divmod__(self, other):
+ value = super(newint, self).__divmod__(other)
+ if value is NotImplemented:
+ mylong = long(self)
+ return (mylong // other, mylong % other)
+ return (newint(value[0]), newint(value[1]))
+
+ def __rdivmod__(self, other):
+ value = super(newint, self).__rdivmod__(other)
+ if value is NotImplemented:
+ mylong = long(self)
+ return (other // mylong, other % mylong)
+ return (newint(value[0]), newint(value[1]))
+
+ def __pow__(self, other):
+ value = super(newint, self).__pow__(other)
+ if value is NotImplemented:
+ return long(self) ** other
+ return newint(value)
+
+ def __rpow__(self, other):
+ value = super(newint, self).__rpow__(other)
+ if isint(value):
+ return newint(value)
+ elif value is NotImplemented:
+ return other ** long(self)
+ return value
+
+ def __lshift__(self, other):
+ if not isint(other):
+ raise TypeError(
+ "unsupported operand type(s) for <<: '%s' and '%s'" %
+ (type(self).__name__, type(other).__name__))
+ return newint(super(newint, self).__lshift__(other))
+
+ def __rshift__(self, other):
+ if not isint(other):
+ raise TypeError(
+ "unsupported operand type(s) for >>: '%s' and '%s'" %
+ (type(self).__name__, type(other).__name__))
+ return newint(super(newint, self).__rshift__(other))
+
+ def __and__(self, other):
+ if not isint(other):
+ raise TypeError(
+ "unsupported operand type(s) for &: '%s' and '%s'" %
+ (type(self).__name__, type(other).__name__))
+ return newint(super(newint, self).__and__(other))
+
+ def __or__(self, other):
+ if not isint(other):
+ raise TypeError(
+ "unsupported operand type(s) for |: '%s' and '%s'" %
+ (type(self).__name__, type(other).__name__))
+ return newint(super(newint, self).__or__(other))
+
+ def __xor__(self, other):
+ if not isint(other):
+ raise TypeError(
+ "unsupported operand type(s) for ^: '%s' and '%s'" %
+ (type(self).__name__, type(other).__name__))
+ return newint(super(newint, self).__xor__(other))
+
+ def __neg__(self):
+ return newint(super(newint, self).__neg__())
+
+ def __pos__(self):
+ return newint(super(newint, self).__pos__())
+
+ def __abs__(self):
+ return newint(super(newint, self).__abs__())
+
+ def __invert__(self):
+ return newint(super(newint, self).__invert__())
+
+ def __int__(self):
+ return self
+
+ def __nonzero__(self):
+ return self.__bool__()
+
+ def __bool__(self):
+ """
+ So subclasses can override this, Py3-style
+ """
+ if PY3:
+ return super(newint, self).__bool__()
+
+ return super(newint, self).__nonzero__()
+
+ def __native__(self):
+ return long(self)
+
+ def to_bytes(self, length, byteorder='big', signed=False):
+ """
+ Return an array of bytes representing an integer.
+
+ The integer is represented using length bytes. An OverflowError is
+ raised if the integer is not representable with the given number of
+ bytes.
+
+ The byteorder argument determines the byte order used to represent the
+ integer. If byteorder is 'big', the most significant byte is at the
+ beginning of the byte array. If byteorder is 'little', the most
+ significant byte is at the end of the byte array. To request the native
+ byte order of the host system, use `sys.byteorder' as the byte order value.
+
+ The signed keyword-only argument determines whether two's complement is
+ used to represent the integer. If signed is False and a negative integer
+ is given, an OverflowError is raised.
+ """
+ if length < 0:
+ raise ValueError("length argument must be non-negative")
+ if length == 0 and self == 0:
+ return newbytes()
+ if signed and self < 0:
+ bits = length * 8
+ num = (2**bits) + self
+ if num <= 0:
+ raise OverflowError("int too small to convert")
+ else:
+ if self < 0:
+ raise OverflowError("can't convert negative int to unsigned")
+ num = self
+ if byteorder not in ('little', 'big'):
+ raise ValueError("byteorder must be either 'little' or 'big'")
+ h = b'%x' % num
+ s = newbytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex'))
+ if signed:
+ high_set = s[0] & 0x80
+ if self > 0 and high_set:
+ raise OverflowError("int too big to convert")
+ if self < 0 and not high_set:
+ raise OverflowError("int too small to convert")
+ if len(s) > length:
+ raise OverflowError("int too big to convert")
+ return s if byteorder == 'big' else s[::-1]
+
+ @classmethod
+ def from_bytes(cls, mybytes, byteorder='big', signed=False):
+ """
+ Return the integer represented by the given array of bytes.
+
+ The mybytes argument must either support the buffer protocol or be an
+ iterable object producing bytes. Bytes and bytearray are examples of
+ built-in objects that support the buffer protocol.
+
+ The byteorder argument determines the byte order used to represent the
+ integer. If byteorder is 'big', the most significant byte is at the
+ beginning of the byte array. If byteorder is 'little', the most
+ significant byte is at the end of the byte array. To request the native
+ byte order of the host system, use `sys.byteorder' as the byte order value.
+
+ The signed keyword-only argument indicates whether two's complement is
+ used to represent the integer.
+ """
+ if byteorder not in ('little', 'big'):
+ raise ValueError("byteorder must be either 'little' or 'big'")
+ if isinstance(mybytes, unicode):
+ raise TypeError("cannot convert unicode objects to bytes")
+ # mybytes can also be passed as a sequence of integers on Py3.
+ # Test for this:
+ elif isinstance(mybytes, Iterable):
+ mybytes = newbytes(mybytes)
+ b = mybytes if byteorder == 'big' else mybytes[::-1]
+ if len(b) == 0:
+ b = b'\x00'
+ # The encode() method has been disabled by newbytes, but Py2's
+ # str has it:
+ num = int(native(b).encode('hex'), 16)
+ if signed and (b[0] & 0x80):
+ num = num - (2 ** (len(b)*8))
+ return cls(num)
+
+
+# def _twos_comp(val, bits):
+# """compute the 2's compliment of int value val"""
+# if( (val&(1<<(bits-1))) != 0 ):
+# val = val - (1<>> from builtins import list
+>>> l1 = list() # instead of {} for an empty list
+>>> l1.append('hello')
+>>> l2 = l1.copy()
+
+"""
+
+import sys
+import copy
+
+from future.utils import with_metaclass
+from future.types.newobject import newobject
+
+
+_builtin_list = list
+ver = sys.version_info[:2]
+
+
+class BaseNewList(type):
+ def __instancecheck__(cls, instance):
+ if cls == newlist:
+ return isinstance(instance, _builtin_list)
+ else:
+ return issubclass(instance.__class__, cls)
+
+
+class newlist(with_metaclass(BaseNewList, _builtin_list)):
+ """
+ A backport of the Python 3 list object to Py2
+ """
+ def copy(self):
+ """
+ L.copy() -> list -- a shallow copy of L
+ """
+ return copy.copy(self)
+
+ def clear(self):
+ """L.clear() -> None -- remove all items from L"""
+ for i in range(len(self)):
+ self.pop()
+
+ def __new__(cls, *args, **kwargs):
+ """
+ list() -> new empty list
+ list(iterable) -> new list initialized from iterable's items
+ """
+
+ if len(args) == 0:
+ return super(newlist, cls).__new__(cls)
+ elif type(args[0]) == newlist:
+ value = args[0]
+ else:
+ value = args[0]
+ return super(newlist, cls).__new__(cls, value)
+
+ def __add__(self, value):
+ return newlist(super(newlist, self).__add__(value))
+
+ def __radd__(self, left):
+ " left + self "
+ try:
+ return newlist(left) + self
+ except:
+ return NotImplemented
+
+ def __getitem__(self, y):
+ """
+ x.__getitem__(y) <==> x[y]
+
+ Warning: a bug in Python 2.x prevents indexing via a slice from
+ returning a newlist object.
+ """
+ if isinstance(y, slice):
+ return newlist(super(newlist, self).__getitem__(y))
+ else:
+ return super(newlist, self).__getitem__(y)
+
+ def __native__(self):
+ """
+ Hook for the future.utils.native() function
+ """
+ return list(self)
+
+ def __nonzero__(self):
+ return len(self) > 0
+
+
+__all__ = ['newlist']
diff --git a/venv/lib/python3.10/site-packages/future/types/newmemoryview.py b/venv/lib/python3.10/site-packages/future/types/newmemoryview.py
new file mode 100644
index 0000000..09f804d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newmemoryview.py
@@ -0,0 +1,29 @@
+"""
+A pretty lame implementation of a memoryview object for Python 2.6.
+"""
+from numbers import Integral
+import string
+
+from future.utils import istext, isbytes, PY2, with_metaclass
+from future.types import no, issubset
+
+if PY2:
+ from collections import Iterable
+else:
+ from collections.abc import Iterable
+
+# class BaseNewBytes(type):
+# def __instancecheck__(cls, instance):
+# return isinstance(instance, _builtin_bytes)
+
+
+class newmemoryview(object): # with_metaclass(BaseNewBytes, _builtin_bytes)):
+ """
+ A pretty lame backport of the Python 2.7 and Python 3.x
+ memoryviewview object to Py2.6.
+ """
+ def __init__(self, obj):
+ return obj
+
+
+__all__ = ['newmemoryview']
diff --git a/venv/lib/python3.10/site-packages/future/types/newobject.py b/venv/lib/python3.10/site-packages/future/types/newobject.py
new file mode 100644
index 0000000..31b84fc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newobject.py
@@ -0,0 +1,117 @@
+"""
+An object subclass for Python 2 that gives new-style classes written in the
+style of Python 3 (with ``__next__`` and unicode-returning ``__str__`` methods)
+the appropriate Python 2-style ``next`` and ``__unicode__`` methods for compatible.
+
+Example use::
+
+ from builtins import object
+
+ my_unicode_str = u'Unicode string: \u5b54\u5b50'
+
+ class A(object):
+ def __str__(self):
+ return my_unicode_str
+
+ a = A()
+ print(str(a))
+
+ # On Python 2, these relations hold:
+ assert unicode(a) == my_unicode_string
+ assert str(a) == my_unicode_string.encode('utf-8')
+
+
+Another example::
+
+ from builtins import object
+
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __next__(self): # note the Py3 interface
+ return next(self._iter).upper()
+ def __iter__(self):
+ return self
+
+ assert list(Upper('hello')) == list('HELLO')
+
+"""
+
+
+class newobject(object):
+ """
+ A magical object class that provides Python 2 compatibility methods::
+ next
+ __unicode__
+ __nonzero__
+
+ Subclasses of this class can merely define the Python 3 methods (__next__,
+ __str__, and __bool__).
+ """
+ def next(self):
+ if hasattr(self, '__next__'):
+ return type(self).__next__(self)
+ raise TypeError('newobject is not an iterator')
+
+ def __unicode__(self):
+ # All subclasses of the builtin object should have __str__ defined.
+ # Note that old-style classes do not have __str__ defined.
+ if hasattr(self, '__str__'):
+ s = type(self).__str__(self)
+ else:
+ s = str(self)
+ if isinstance(s, unicode):
+ return s
+ else:
+ return s.decode('utf-8')
+
+ def __nonzero__(self):
+ if hasattr(self, '__bool__'):
+ return type(self).__bool__(self)
+ if hasattr(self, '__len__'):
+ return type(self).__len__(self)
+ # object has no __nonzero__ method
+ return True
+
+ # Are these ever needed?
+ # def __div__(self):
+ # return self.__truediv__()
+
+ # def __idiv__(self, other):
+ # return self.__itruediv__(other)
+
+ def __long__(self):
+ if not hasattr(self, '__int__'):
+ return NotImplemented
+ return self.__int__() # not type(self).__int__(self)
+
+ # def __new__(cls, *args, **kwargs):
+ # """
+ # dict() -> new empty dictionary
+ # dict(mapping) -> new dictionary initialized from a mapping object's
+ # (key, value) pairs
+ # dict(iterable) -> new dictionary initialized as if via:
+ # d = {}
+ # for k, v in iterable:
+ # d[k] = v
+ # dict(**kwargs) -> new dictionary initialized with the name=value pairs
+ # in the keyword argument list. For example: dict(one=1, two=2)
+ # """
+
+ # if len(args) == 0:
+ # return super(newdict, cls).__new__(cls)
+ # elif type(args[0]) == newdict:
+ # return args[0]
+ # else:
+ # value = args[0]
+ # return super(newdict, cls).__new__(cls, value)
+
+ def __native__(self):
+ """
+ Hook for the future.utils.native() function
+ """
+ return object(self)
+
+ __slots__ = []
+
+__all__ = ['newobject']
diff --git a/venv/lib/python3.10/site-packages/future/types/newopen.py b/venv/lib/python3.10/site-packages/future/types/newopen.py
new file mode 100644
index 0000000..b75d45a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newopen.py
@@ -0,0 +1,32 @@
+"""
+A substitute for the Python 3 open() function.
+
+Note that io.open() is more complete but maybe slower. Even so, the
+completeness may be a better default. TODO: compare these
+"""
+
+_builtin_open = open
+
+class newopen(object):
+ """Wrapper providing key part of Python 3 open() interface.
+
+ From IPython's py3compat.py module. License: BSD.
+ """
+ def __init__(self, fname, mode="r", encoding="utf-8"):
+ self.f = _builtin_open(fname, mode)
+ self.enc = encoding
+
+ def write(self, s):
+ return self.f.write(s.encode(self.enc))
+
+ def read(self, size=-1):
+ return self.f.read(size).decode(self.enc)
+
+ def close(self):
+ return self.f.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, etype, value, traceback):
+ self.f.close()
diff --git a/venv/lib/python3.10/site-packages/future/types/newrange.py b/venv/lib/python3.10/site-packages/future/types/newrange.py
new file mode 100644
index 0000000..dc5eb80
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newrange.py
@@ -0,0 +1,170 @@
+"""
+Nearly identical to xrange.py, by Dan Crosta, from
+
+ https://github.com/dcrosta/xrange.git
+
+This is included here in the ``future`` package rather than pointed to as
+a dependency because there is no package for ``xrange`` on PyPI. It is
+also tweaked to appear like a regular Python 3 ``range`` object rather
+than a Python 2 xrange.
+
+From Dan Crosta's README:
+
+ "A pure-Python implementation of Python 2.7's xrange built-in, with
+ some features backported from the Python 3.x range built-in (which
+ replaced xrange) in that version."
+
+ Read more at
+ https://late.am/post/2012/06/18/what-the-heck-is-an-xrange
+"""
+from __future__ import absolute_import
+
+from future.utils import PY2
+
+if PY2:
+ from collections import Sequence, Iterator
+else:
+ from collections.abc import Sequence, Iterator
+from itertools import islice
+
+from future.backports.misc import count # with step parameter on Py2.6
+# For backward compatibility with python-future versions < 0.14.4:
+_count = count
+
+
+class newrange(Sequence):
+ """
+ Pure-Python backport of Python 3's range object. See `the CPython
+ documentation for details:
+ `_
+ """
+
+ def __init__(self, *args):
+ if len(args) == 1:
+ start, stop, step = 0, args[0], 1
+ elif len(args) == 2:
+ start, stop, step = args[0], args[1], 1
+ elif len(args) == 3:
+ start, stop, step = args
+ else:
+ raise TypeError('range() requires 1-3 int arguments')
+
+ try:
+ start, stop, step = int(start), int(stop), int(step)
+ except ValueError:
+ raise TypeError('an integer is required')
+
+ if step == 0:
+ raise ValueError('range() arg 3 must not be zero')
+ elif step < 0:
+ stop = min(stop, start)
+ else:
+ stop = max(stop, start)
+
+ self._start = start
+ self._stop = stop
+ self._step = step
+ self._len = (stop - start) // step + bool((stop - start) % step)
+
+ @property
+ def start(self):
+ return self._start
+
+ @property
+ def stop(self):
+ return self._stop
+
+ @property
+ def step(self):
+ return self._step
+
+ def __repr__(self):
+ if self._step == 1:
+ return 'range(%d, %d)' % (self._start, self._stop)
+ return 'range(%d, %d, %d)' % (self._start, self._stop, self._step)
+
+ def __eq__(self, other):
+ return (isinstance(other, newrange) and
+ (self._len == 0 == other._len or
+ (self._start, self._step, self._len) ==
+ (other._start, other._step, other._len)))
+
+ def __len__(self):
+ return self._len
+
+ def index(self, value):
+ """Return the 0-based position of integer `value` in
+ the sequence this range represents."""
+ try:
+ diff = value - self._start
+ except TypeError:
+ raise ValueError('%r is not in range' % value)
+ quotient, remainder = divmod(diff, self._step)
+ if remainder == 0 and 0 <= quotient < self._len:
+ return abs(quotient)
+ raise ValueError('%r is not in range' % value)
+
+ def count(self, value):
+ """Return the number of occurrences of integer `value`
+ in the sequence this range represents."""
+ # a value can occur exactly zero or one times
+ return int(value in self)
+
+ def __contains__(self, value):
+ """Return ``True`` if the integer `value` occurs in
+ the sequence this range represents."""
+ try:
+ self.index(value)
+ return True
+ except ValueError:
+ return False
+
+ def __reversed__(self):
+ return iter(self[::-1])
+
+ def __getitem__(self, index):
+ """Return the element at position ``index`` in the sequence
+ this range represents, or raise :class:`IndexError` if the
+ position is out of range."""
+ if isinstance(index, slice):
+ return self.__getitem_slice(index)
+ if index < 0:
+ # negative indexes access from the end
+ index = self._len + index
+ if index < 0 or index >= self._len:
+ raise IndexError('range object index out of range')
+ return self._start + index * self._step
+
+ def __getitem_slice(self, slce):
+ """Return a range which represents the requested slce
+ of the sequence represented by this range.
+ """
+ scaled_indices = (self._step * n for n in slce.indices(self._len))
+ start_offset, stop_offset, new_step = scaled_indices
+ return newrange(self._start + start_offset,
+ self._start + stop_offset,
+ new_step)
+
+ def __iter__(self):
+ """Return an iterator which enumerates the elements of the
+ sequence this range represents."""
+ return range_iterator(self)
+
+
+class range_iterator(Iterator):
+ """An iterator for a :class:`range`.
+ """
+ def __init__(self, range_):
+ self._stepper = islice(count(range_.start, range_.step), len(range_))
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return next(self._stepper)
+
+ def next(self):
+ return next(self._stepper)
+
+
+__all__ = ['newrange']
diff --git a/venv/lib/python3.10/site-packages/future/types/newstr.py b/venv/lib/python3.10/site-packages/future/types/newstr.py
new file mode 100644
index 0000000..8ca191f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/types/newstr.py
@@ -0,0 +1,426 @@
+"""
+This module redefines ``str`` on Python 2.x to be a subclass of the Py2
+``unicode`` type that behaves like the Python 3.x ``str``.
+
+The main differences between ``newstr`` and Python 2.x's ``unicode`` type are
+the stricter type-checking and absence of a `u''` prefix in the representation.
+
+It is designed to be used together with the ``unicode_literals`` import
+as follows:
+
+ >>> from __future__ import unicode_literals
+ >>> from builtins import str, isinstance
+
+On Python 3.x and normally on Python 2.x, these expressions hold
+
+ >>> str('blah') is 'blah'
+ True
+ >>> isinstance('blah', str)
+ True
+
+However, on Python 2.x, with this import:
+
+ >>> from __future__ import unicode_literals
+
+the same expressions are False:
+
+ >>> str('blah') is 'blah'
+ False
+ >>> isinstance('blah', str)
+ False
+
+This module is designed to be imported together with ``unicode_literals`` on
+Python 2 to bring the meaning of ``str`` back into alignment with unprefixed
+string literals (i.e. ``unicode`` subclasses).
+
+Note that ``str()`` (and ``print()``) would then normally call the
+``__unicode__`` method on objects in Python 2. To define string
+representations of your objects portably across Py3 and Py2, use the
+:func:`python_2_unicode_compatible` decorator in :mod:`future.utils`.
+
+"""
+
+from numbers import Number
+
+from future.utils import PY3, istext, with_metaclass, isnewbytes
+from future.types import no, issubset
+from future.types.newobject import newobject
+
+
+if PY3:
+ # We'll probably never use newstr on Py3 anyway...
+ unicode = str
+ from collections.abc import Iterable
+else:
+ from collections import Iterable
+
+
+class BaseNewStr(type):
+ def __instancecheck__(cls, instance):
+ if cls == newstr:
+ return isinstance(instance, unicode)
+ else:
+ return issubclass(instance.__class__, cls)
+
+
+class newstr(with_metaclass(BaseNewStr, unicode)):
+ """
+ A backport of the Python 3 str object to Py2
+ """
+ no_convert_msg = "Can't convert '{0}' object to str implicitly"
+
+ def __new__(cls, *args, **kwargs):
+ """
+ From the Py3 str docstring:
+
+ str(object='') -> str
+ str(bytes_or_buffer[, encoding[, errors]]) -> str
+
+ Create a new string object from the given object. If encoding or
+ errors is specified, then the object must expose a data buffer
+ that will be decoded using the given encoding and error handler.
+ Otherwise, returns the result of object.__str__() (if defined)
+ or repr(object).
+ encoding defaults to sys.getdefaultencoding().
+ errors defaults to 'strict'.
+
+ """
+ if len(args) == 0:
+ return super(newstr, cls).__new__(cls)
+ # Special case: If someone requests str(str(u'abc')), return the same
+ # object (same id) for consistency with Py3.3. This is not true for
+ # other objects like list or dict.
+ elif type(args[0]) == newstr and cls == newstr:
+ return args[0]
+ elif isinstance(args[0], unicode):
+ value = args[0]
+ elif isinstance(args[0], bytes): # i.e. Py2 bytes or newbytes
+ if 'encoding' in kwargs or len(args) > 1:
+ value = args[0].decode(*args[1:], **kwargs)
+ else:
+ value = args[0].__str__()
+ else:
+ value = args[0]
+ return super(newstr, cls).__new__(cls, value)
+
+ def __repr__(self):
+ """
+ Without the u prefix
+ """
+
+ value = super(newstr, self).__repr__()
+ # assert value[0] == u'u'
+ return value[1:]
+
+ def __getitem__(self, y):
+ """
+ Warning: Python <= 2.7.6 has a bug that causes this method never to be called
+ when y is a slice object. Therefore the type of newstr()[:2] is wrong
+ (unicode instead of newstr).
+ """
+ return newstr(super(newstr, self).__getitem__(y))
+
+ def __contains__(self, key):
+ errmsg = "'in ' requires string as left operand, not {0}"
+ # Don't use isinstance() here because we only want to catch
+ # newstr, not Python 2 unicode:
+ if type(key) == newstr:
+ newkey = key
+ elif isinstance(key, unicode) or isinstance(key, bytes) and not isnewbytes(key):
+ newkey = newstr(key)
+ else:
+ raise TypeError(errmsg.format(type(key)))
+ return issubset(list(newkey), list(self))
+
+ @no('newbytes')
+ def __add__(self, other):
+ return newstr(super(newstr, self).__add__(other))
+
+ @no('newbytes')
+ def __radd__(self, left):
+ " left + self "
+ try:
+ return newstr(left) + self
+ except:
+ return NotImplemented
+
+ def __mul__(self, other):
+ return newstr(super(newstr, self).__mul__(other))
+
+ def __rmul__(self, other):
+ return newstr(super(newstr, self).__rmul__(other))
+
+ def join(self, iterable):
+ errmsg = 'sequence item {0}: expected unicode string, found bytes'
+ for i, item in enumerate(iterable):
+ # Here we use type() rather than isinstance() because
+ # __instancecheck__ is being overridden. E.g.
+ # isinstance(b'abc', newbytes) is True on Py2.
+ if isnewbytes(item):
+ raise TypeError(errmsg.format(i))
+ # Support use as a staticmethod: str.join('-', ['a', 'b'])
+ if type(self) == newstr:
+ return newstr(super(newstr, self).join(iterable))
+ else:
+ return newstr(super(newstr, newstr(self)).join(iterable))
+
+ @no('newbytes')
+ def find(self, sub, *args):
+ return super(newstr, self).find(sub, *args)
+
+ @no('newbytes')
+ def rfind(self, sub, *args):
+ return super(newstr, self).rfind(sub, *args)
+
+ @no('newbytes', (1, 2))
+ def replace(self, old, new, *args):
+ return newstr(super(newstr, self).replace(old, new, *args))
+
+ def decode(self, *args):
+ raise AttributeError("decode method has been disabled in newstr")
+
+ def encode(self, encoding='utf-8', errors='strict'):
+ """
+ Returns bytes
+
+ Encode S using the codec registered for encoding. Default encoding
+ is 'utf-8'. errors may be given to set a different error
+ handling scheme. Default is 'strict' meaning that encoding errors raise
+ a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
+ 'xmlcharrefreplace' as well as any other name registered with
+ codecs.register_error that can handle UnicodeEncodeErrors.
+ """
+ from future.types.newbytes import newbytes
+ # Py2 unicode.encode() takes encoding and errors as optional parameter,
+ # not keyword arguments as in Python 3 str.
+
+ # For the surrogateescape error handling mechanism, the
+ # codecs.register_error() function seems to be inadequate for an
+ # implementation of it when encoding. (Decoding seems fine, however.)
+ # For example, in the case of
+ # u'\udcc3'.encode('ascii', 'surrogateescape_handler')
+ # after registering the ``surrogateescape_handler`` function in
+ # future.utils.surrogateescape, both Python 2.x and 3.x raise an
+ # exception anyway after the function is called because the unicode
+ # string it has to return isn't encodable strictly as ASCII.
+
+ if errors == 'surrogateescape':
+ if encoding == 'utf-16':
+ # Known to fail here. See test_encoding_works_normally()
+ raise NotImplementedError('FIXME: surrogateescape handling is '
+ 'not yet implemented properly')
+ # Encode char by char, building up list of byte-strings
+ mybytes = []
+ for c in self:
+ code = ord(c)
+ if 0xD800 <= code <= 0xDCFF:
+ mybytes.append(newbytes([code - 0xDC00]))
+ else:
+ mybytes.append(c.encode(encoding=encoding))
+ return newbytes(b'').join(mybytes)
+ return newbytes(super(newstr, self).encode(encoding, errors))
+
+ @no('newbytes', 1)
+ def startswith(self, prefix, *args):
+ if isinstance(prefix, Iterable):
+ for thing in prefix:
+ if isnewbytes(thing):
+ raise TypeError(self.no_convert_msg.format(type(thing)))
+ return super(newstr, self).startswith(prefix, *args)
+
+ @no('newbytes', 1)
+ def endswith(self, prefix, *args):
+ # Note we need the decorator above as well as the isnewbytes()
+ # check because prefix can be either a bytes object or e.g. a
+ # tuple of possible prefixes. (If it's a bytes object, each item
+ # in it is an int.)
+ if isinstance(prefix, Iterable):
+ for thing in prefix:
+ if isnewbytes(thing):
+ raise TypeError(self.no_convert_msg.format(type(thing)))
+ return super(newstr, self).endswith(prefix, *args)
+
+ @no('newbytes', 1)
+ def split(self, sep=None, maxsplit=-1):
+ # Py2 unicode.split() takes maxsplit as an optional parameter,
+ # not as a keyword argument as in Python 3 str.
+ parts = super(newstr, self).split(sep, maxsplit)
+ return [newstr(part) for part in parts]
+
+ @no('newbytes', 1)
+ def rsplit(self, sep=None, maxsplit=-1):
+ # Py2 unicode.rsplit() takes maxsplit as an optional parameter,
+ # not as a keyword argument as in Python 3 str.
+ parts = super(newstr, self).rsplit(sep, maxsplit)
+ return [newstr(part) for part in parts]
+
+ @no('newbytes', 1)
+ def partition(self, sep):
+ parts = super(newstr, self).partition(sep)
+ return tuple(newstr(part) for part in parts)
+
+ @no('newbytes', 1)
+ def rpartition(self, sep):
+ parts = super(newstr, self).rpartition(sep)
+ return tuple(newstr(part) for part in parts)
+
+ @no('newbytes', 1)
+ def index(self, sub, *args):
+ """
+ Like newstr.find() but raise ValueError when the substring is not
+ found.
+ """
+ pos = self.find(sub, *args)
+ if pos == -1:
+ raise ValueError('substring not found')
+ return pos
+
+ def splitlines(self, keepends=False):
+ """
+ S.splitlines(keepends=False) -> list of strings
+
+ Return a list of the lines in S, breaking at line boundaries.
+ Line breaks are not included in the resulting list unless keepends
+ is given and true.
+ """
+ # Py2 unicode.splitlines() takes keepends as an optional parameter,
+ # not as a keyword argument as in Python 3 str.
+ parts = super(newstr, self).splitlines(keepends)
+ return [newstr(part) for part in parts]
+
+ def __eq__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__eq__(other)
+ else:
+ return NotImplemented
+
+ def __hash__(self):
+ if (isinstance(self, unicode) or
+ isinstance(self, bytes) and not isnewbytes(self)):
+ return super(newstr, self).__hash__()
+ else:
+ raise NotImplementedError()
+
+ def __ne__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__ne__(other)
+ else:
+ return True
+
+ unorderable_err = 'unorderable types: str() and {0}'
+
+ def __lt__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__lt__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __le__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__le__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __gt__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__gt__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __ge__(self, other):
+ if (isinstance(other, unicode) or
+ isinstance(other, bytes) and not isnewbytes(other)):
+ return super(newstr, self).__ge__(other)
+ raise TypeError(self.unorderable_err.format(type(other)))
+
+ def __getattribute__(self, name):
+ """
+ A trick to cause the ``hasattr`` builtin-fn to return False for
+ the 'decode' method on Py2.
+ """
+ if name in ['decode', u'decode']:
+ raise AttributeError("decode method has been disabled in newstr")
+ return super(newstr, self).__getattribute__(name)
+
+ def __native__(self):
+ """
+ A hook for the future.utils.native() function.
+ """
+ return unicode(self)
+
+ @staticmethod
+ def maketrans(x, y=None, z=None):
+ """
+ Return a translation table usable for str.translate().
+
+ If there is only one argument, it must be a dictionary mapping Unicode
+ ordinals (integers) or characters to Unicode ordinals, strings or None.
+ Character keys will be then converted to ordinals.
+ If there are two arguments, they must be strings of equal length, and
+ in the resulting dictionary, each character in x will be mapped to the
+ character at the same position in y. If there is a third argument, it
+ must be a string, whose characters will be mapped to None in the result.
+ """
+
+ if y is None:
+ assert z is None
+ if not isinstance(x, dict):
+ raise TypeError('if you give only one argument to maketrans it must be a dict')
+ result = {}
+ for (key, value) in x.items():
+ if len(key) > 1:
+ raise ValueError('keys in translate table must be strings or integers')
+ result[ord(key)] = value
+ else:
+ if not isinstance(x, unicode) and isinstance(y, unicode):
+ raise TypeError('x and y must be unicode strings')
+ if not len(x) == len(y):
+ raise ValueError('the first two maketrans arguments must have equal length')
+ result = {}
+ for (xi, yi) in zip(x, y):
+ if len(xi) > 1:
+ raise ValueError('keys in translate table must be strings or integers')
+ result[ord(xi)] = ord(yi)
+
+ if z is not None:
+ for char in z:
+ result[ord(char)] = None
+ return result
+
+ def translate(self, table):
+ """
+ S.translate(table) -> str
+
+ Return a copy of the string S, where all characters have been mapped
+ through the given translation table, which must be a mapping of
+ Unicode ordinals to Unicode ordinals, strings, or None.
+ Unmapped characters are left untouched. Characters mapped to None
+ are deleted.
+ """
+ l = []
+ for c in self:
+ if ord(c) in table:
+ val = table[ord(c)]
+ if val is None:
+ continue
+ elif isinstance(val, unicode):
+ l.append(val)
+ else:
+ l.append(chr(val))
+ else:
+ l.append(c)
+ return ''.join(l)
+
+ def isprintable(self):
+ raise NotImplementedError('fixme')
+
+ def isidentifier(self):
+ raise NotImplementedError('fixme')
+
+ def format_map(self):
+ raise NotImplementedError('fixme')
+
+
+__all__ = ['newstr']
diff --git a/venv/lib/python3.10/site-packages/future/utils/__init__.py b/venv/lib/python3.10/site-packages/future/utils/__init__.py
new file mode 100644
index 0000000..ec1b102
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/utils/__init__.py
@@ -0,0 +1,770 @@
+"""
+A selection of cross-compatible functions for Python 2 and 3.
+
+This module exports useful functions for 2/3 compatible code:
+
+ * bind_method: binds functions to classes
+ * ``native_str_to_bytes`` and ``bytes_to_native_str``
+ * ``native_str``: always equal to the native platform string object (because
+ this may be shadowed by imports from future.builtins)
+ * lists: lrange(), lmap(), lzip(), lfilter()
+ * iterable method compatibility:
+ - iteritems, iterkeys, itervalues
+ - viewitems, viewkeys, viewvalues
+
+ These use the original method if available, otherwise they use items,
+ keys, values.
+
+ * types:
+
+ * text_type: unicode in Python 2, str in Python 3
+ * string_types: basestring in Python 2, str in Python 3
+ * binary_type: str in Python 2, bytes in Python 3
+ * integer_types: (int, long) in Python 2, int in Python 3
+ * class_types: (type, types.ClassType) in Python 2, type in Python 3
+
+ * bchr(c):
+ Take an integer and make a 1-character byte string
+ * bord(c)
+ Take the result of indexing on a byte string and make an integer
+ * tobytes(s)
+ Take a text string, a byte string, or a sequence of characters taken
+ from a byte string, and make a byte string.
+
+ * raise_from()
+ * raise_with_traceback()
+
+This module also defines these decorators:
+
+ * ``python_2_unicode_compatible``
+ * ``with_metaclass``
+ * ``implements_iterator``
+
+Some of the functions in this module come from the following sources:
+
+ * Jinja2 (BSD licensed: see
+ https://github.com/mitsuhiko/jinja2/blob/master/LICENSE)
+ * Pandas compatibility module pandas.compat
+ * six.py by Benjamin Peterson
+ * Django
+"""
+
+import types
+import sys
+import numbers
+import functools
+import copy
+import inspect
+
+
+PY3 = sys.version_info[0] >= 3
+PY34_PLUS = sys.version_info[0:2] >= (3, 4)
+PY35_PLUS = sys.version_info[0:2] >= (3, 5)
+PY36_PLUS = sys.version_info[0:2] >= (3, 6)
+PY37_PLUS = sys.version_info[0:2] >= (3, 7)
+PY38_PLUS = sys.version_info[0:2] >= (3, 8)
+PY39_PLUS = sys.version_info[0:2] >= (3, 9)
+PY2 = sys.version_info[0] == 2
+PY26 = sys.version_info[0:2] == (2, 6)
+PY27 = sys.version_info[0:2] == (2, 7)
+PYPY = hasattr(sys, 'pypy_translation_info')
+
+
+def python_2_unicode_compatible(cls):
+ """
+ A decorator that defines __unicode__ and __str__ methods under Python
+ 2. Under Python 3, this decorator is a no-op.
+
+ To support Python 2 and 3 with a single code base, define a __str__
+ method returning unicode text and apply this decorator to the class, like
+ this::
+
+ >>> from future.utils import python_2_unicode_compatible
+
+ >>> @python_2_unicode_compatible
+ ... class MyClass(object):
+ ... def __str__(self):
+ ... return u'Unicode string: \u5b54\u5b50'
+
+ >>> a = MyClass()
+
+ Then, after this import:
+
+ >>> from future.builtins import str
+
+ the following is ``True`` on both Python 3 and 2::
+
+ >>> str(a) == a.encode('utf-8').decode('utf-8')
+ True
+
+ and, on a Unicode-enabled terminal with the right fonts, these both print the
+ Chinese characters for Confucius::
+
+ >>> print(a)
+ >>> print(str(a))
+
+ The implementation comes from django.utils.encoding.
+ """
+ if not PY3:
+ cls.__unicode__ = cls.__str__
+ cls.__str__ = lambda self: self.__unicode__().encode('utf-8')
+ return cls
+
+
+def with_metaclass(meta, *bases):
+ """
+ Function from jinja2/_compat.py. License: BSD.
+
+ Use it like this::
+
+ class BaseForm(object):
+ pass
+
+ class FormType(type):
+ pass
+
+ class Form(with_metaclass(FormType, BaseForm)):
+ pass
+
+ This requires a bit of explanation: the basic idea is to make a
+ dummy metaclass for one level of class instantiation that replaces
+ itself with the actual metaclass. Because of internal type checks
+ we also need to make sure that we downgrade the custom metaclass
+ for one level to something closer to type (that's why __call__ and
+ __init__ comes back from type etc.).
+
+ This has the advantage over six.with_metaclass of not introducing
+ dummy classes into the final MRO.
+ """
+ class metaclass(meta):
+ __call__ = type.__call__
+ __init__ = type.__init__
+ def __new__(cls, name, this_bases, d):
+ if this_bases is None:
+ return type.__new__(cls, name, (), d)
+ return meta(name, bases, d)
+ return metaclass('temporary_class', None, {})
+
+
+# Definitions from pandas.compat and six.py follow:
+if PY3:
+ def bchr(s):
+ return bytes([s])
+ def bstr(s):
+ if isinstance(s, str):
+ return bytes(s, 'latin-1')
+ else:
+ return bytes(s)
+ def bord(s):
+ return s
+
+ string_types = str,
+ integer_types = int,
+ class_types = type,
+ text_type = str
+ binary_type = bytes
+
+else:
+ # Python 2
+ def bchr(s):
+ return chr(s)
+ def bstr(s):
+ return str(s)
+ def bord(s):
+ return ord(s)
+
+ string_types = basestring,
+ integer_types = (int, long)
+ class_types = (type, types.ClassType)
+ text_type = unicode
+ binary_type = str
+
+###
+
+if PY3:
+ def tobytes(s):
+ if isinstance(s, bytes):
+ return s
+ else:
+ if isinstance(s, str):
+ return s.encode('latin-1')
+ else:
+ return bytes(s)
+else:
+ # Python 2
+ def tobytes(s):
+ if isinstance(s, unicode):
+ return s.encode('latin-1')
+ else:
+ return ''.join(s)
+
+tobytes.__doc__ = """
+ Encodes to latin-1 (where the first 256 chars are the same as
+ ASCII.)
+ """
+
+if PY3:
+ def native_str_to_bytes(s, encoding='utf-8'):
+ return s.encode(encoding)
+
+ def bytes_to_native_str(b, encoding='utf-8'):
+ return b.decode(encoding)
+
+ def text_to_native_str(t, encoding=None):
+ return t
+else:
+ # Python 2
+ def native_str_to_bytes(s, encoding=None):
+ from future.types import newbytes # to avoid a circular import
+ return newbytes(s)
+
+ def bytes_to_native_str(b, encoding=None):
+ return native(b)
+
+ def text_to_native_str(t, encoding='ascii'):
+ """
+ Use this to create a Py2 native string when "from __future__ import
+ unicode_literals" is in effect.
+ """
+ return unicode(t).encode(encoding)
+
+native_str_to_bytes.__doc__ = """
+ On Py3, returns an encoded string.
+ On Py2, returns a newbytes type, ignoring the ``encoding`` argument.
+ """
+
+if PY3:
+ # list-producing versions of the major Python iterating functions
+ def lrange(*args, **kwargs):
+ return list(range(*args, **kwargs))
+
+ def lzip(*args, **kwargs):
+ return list(zip(*args, **kwargs))
+
+ def lmap(*args, **kwargs):
+ return list(map(*args, **kwargs))
+
+ def lfilter(*args, **kwargs):
+ return list(filter(*args, **kwargs))
+else:
+ import __builtin__
+ # Python 2-builtin ranges produce lists
+ lrange = __builtin__.range
+ lzip = __builtin__.zip
+ lmap = __builtin__.map
+ lfilter = __builtin__.filter
+
+
+def isidentifier(s, dotted=False):
+ '''
+ A function equivalent to the str.isidentifier method on Py3
+ '''
+ if dotted:
+ return all(isidentifier(a) for a in s.split('.'))
+ if PY3:
+ return s.isidentifier()
+ else:
+ import re
+ _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
+ return bool(_name_re.match(s))
+
+
+def viewitems(obj, **kwargs):
+ """
+ Function for iterating over dictionary items with the same set-like
+ behaviour on Py2.7 as on Py3.
+
+ Passes kwargs to method."""
+ func = getattr(obj, "viewitems", None)
+ if not func:
+ func = obj.items
+ return func(**kwargs)
+
+
+def viewkeys(obj, **kwargs):
+ """
+ Function for iterating over dictionary keys with the same set-like
+ behaviour on Py2.7 as on Py3.
+
+ Passes kwargs to method."""
+ func = getattr(obj, "viewkeys", None)
+ if not func:
+ func = obj.keys
+ return func(**kwargs)
+
+
+def viewvalues(obj, **kwargs):
+ """
+ Function for iterating over dictionary values with the same set-like
+ behaviour on Py2.7 as on Py3.
+
+ Passes kwargs to method."""
+ func = getattr(obj, "viewvalues", None)
+ if not func:
+ func = obj.values
+ return func(**kwargs)
+
+
+def iteritems(obj, **kwargs):
+ """Use this only if compatibility with Python versions before 2.7 is
+ required. Otherwise, prefer viewitems().
+ """
+ func = getattr(obj, "iteritems", None)
+ if not func:
+ func = obj.items
+ return func(**kwargs)
+
+
+def iterkeys(obj, **kwargs):
+ """Use this only if compatibility with Python versions before 2.7 is
+ required. Otherwise, prefer viewkeys().
+ """
+ func = getattr(obj, "iterkeys", None)
+ if not func:
+ func = obj.keys
+ return func(**kwargs)
+
+
+def itervalues(obj, **kwargs):
+ """Use this only if compatibility with Python versions before 2.7 is
+ required. Otherwise, prefer viewvalues().
+ """
+ func = getattr(obj, "itervalues", None)
+ if not func:
+ func = obj.values
+ return func(**kwargs)
+
+
+def bind_method(cls, name, func):
+ """Bind a method to class, python 2 and python 3 compatible.
+
+ Parameters
+ ----------
+
+ cls : type
+ class to receive bound method
+ name : basestring
+ name of method on class instance
+ func : function
+ function to be bound as method
+
+ Returns
+ -------
+ None
+ """
+ # only python 2 has an issue with bound/unbound methods
+ if not PY3:
+ setattr(cls, name, types.MethodType(func, None, cls))
+ else:
+ setattr(cls, name, func)
+
+
+def getexception():
+ return sys.exc_info()[1]
+
+
+def _get_caller_globals_and_locals():
+ """
+ Returns the globals and locals of the calling frame.
+
+ Is there an alternative to frame hacking here?
+ """
+ caller_frame = inspect.stack()[2]
+ myglobals = caller_frame[0].f_globals
+ mylocals = caller_frame[0].f_locals
+ return myglobals, mylocals
+
+
+def _repr_strip(mystring):
+ """
+ Returns the string without any initial or final quotes.
+ """
+ r = repr(mystring)
+ if r.startswith("'") and r.endswith("'"):
+ return r[1:-1]
+ else:
+ return r
+
+
+if PY3:
+ def raise_from(exc, cause):
+ """
+ Equivalent to:
+
+ raise EXCEPTION from CAUSE
+
+ on Python 3. (See PEP 3134).
+ """
+ myglobals, mylocals = _get_caller_globals_and_locals()
+
+ # We pass the exception and cause along with other globals
+ # when we exec():
+ myglobals = myglobals.copy()
+ myglobals['__python_future_raise_from_exc'] = exc
+ myglobals['__python_future_raise_from_cause'] = cause
+ execstr = "raise __python_future_raise_from_exc from __python_future_raise_from_cause"
+ exec(execstr, myglobals, mylocals)
+
+ def raise_(tp, value=None, tb=None):
+ """
+ A function that matches the Python 2.x ``raise`` statement. This
+ allows re-raising exceptions with the cls value and traceback on
+ Python 2 and 3.
+ """
+ if isinstance(tp, BaseException):
+ # If the first object is an instance, the type of the exception
+ # is the class of the instance, the instance itself is the value,
+ # and the second object must be None.
+ if value is not None:
+ raise TypeError("instance exception may not have a separate value")
+ exc = tp
+ elif isinstance(tp, type) and not issubclass(tp, BaseException):
+ # If the first object is a class, it becomes the type of the
+ # exception.
+ raise TypeError("class must derive from BaseException, not %s" % tp.__name__)
+ else:
+ # The second object is used to determine the exception value: If it
+ # is an instance of the class, the instance becomes the exception
+ # value. If the second object is a tuple, it is used as the argument
+ # list for the class constructor; if it is None, an empty argument
+ # list is used, and any other object is treated as a single argument
+ # to the constructor. The instance so created by calling the
+ # constructor is used as the exception value.
+ if isinstance(value, tp):
+ exc = value
+ elif isinstance(value, tuple):
+ exc = tp(*value)
+ elif value is None:
+ exc = tp()
+ else:
+ exc = tp(value)
+
+ if exc.__traceback__ is not tb:
+ raise exc.with_traceback(tb)
+ raise exc
+
+ def raise_with_traceback(exc, traceback=Ellipsis):
+ if traceback == Ellipsis:
+ _, _, traceback = sys.exc_info()
+ raise exc.with_traceback(traceback)
+
+else:
+ def raise_from(exc, cause):
+ """
+ Equivalent to:
+
+ raise EXCEPTION from CAUSE
+
+ on Python 3. (See PEP 3134).
+ """
+ # Is either arg an exception class (e.g. IndexError) rather than
+ # instance (e.g. IndexError('my message here')? If so, pass the
+ # name of the class undisturbed through to "raise ... from ...".
+ if isinstance(exc, type) and issubclass(exc, Exception):
+ e = exc()
+ # exc = exc.__name__
+ # execstr = "e = " + _repr_strip(exc) + "()"
+ # myglobals, mylocals = _get_caller_globals_and_locals()
+ # exec(execstr, myglobals, mylocals)
+ else:
+ e = exc
+ e.__suppress_context__ = False
+ if isinstance(cause, type) and issubclass(cause, Exception):
+ e.__cause__ = cause()
+ e.__cause__.__traceback__ = sys.exc_info()[2]
+ e.__suppress_context__ = True
+ elif cause is None:
+ e.__cause__ = None
+ e.__suppress_context__ = True
+ elif isinstance(cause, BaseException):
+ e.__cause__ = cause
+ object.__setattr__(e.__cause__, '__traceback__', sys.exc_info()[2])
+ e.__suppress_context__ = True
+ else:
+ raise TypeError("exception causes must derive from BaseException")
+ e.__context__ = sys.exc_info()[1]
+ raise e
+
+ exec('''
+def raise_(tp, value=None, tb=None):
+ raise tp, value, tb
+
+def raise_with_traceback(exc, traceback=Ellipsis):
+ if traceback == Ellipsis:
+ _, _, traceback = sys.exc_info()
+ raise exc, None, traceback
+'''.strip())
+
+
+raise_with_traceback.__doc__ = (
+"""Raise exception with existing traceback.
+If traceback is not passed, uses sys.exc_info() to get traceback."""
+)
+
+
+# Deprecated alias for backward compatibility with ``future`` versions < 0.11:
+reraise = raise_
+
+
+def implements_iterator(cls):
+ '''
+ From jinja2/_compat.py. License: BSD.
+
+ Use as a decorator like this::
+
+ @implements_iterator
+ class UppercasingIterator(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __iter__(self):
+ return self
+ def __next__(self):
+ return next(self._iter).upper()
+
+ '''
+ if PY3:
+ return cls
+ else:
+ cls.next = cls.__next__
+ del cls.__next__
+ return cls
+
+if PY3:
+ get_next = lambda x: x.__next__
+else:
+ get_next = lambda x: x.next
+
+
+def encode_filename(filename):
+ if PY3:
+ return filename
+ else:
+ if isinstance(filename, unicode):
+ return filename.encode('utf-8')
+ return filename
+
+
+def is_new_style(cls):
+ """
+ Python 2.7 has both new-style and old-style classes. Old-style classes can
+ be pesky in some circumstances, such as when using inheritance. Use this
+ function to test for whether a class is new-style. (Python 3 only has
+ new-style classes.)
+ """
+ return hasattr(cls, '__class__') and ('__dict__' in dir(cls)
+ or hasattr(cls, '__slots__'))
+
+# The native platform string and bytes types. Useful because ``str`` and
+# ``bytes`` are redefined on Py2 by ``from future.builtins import *``.
+native_str = str
+native_bytes = bytes
+
+
+def istext(obj):
+ """
+ Deprecated. Use::
+ >>> isinstance(obj, str)
+ after this import:
+ >>> from future.builtins import str
+ """
+ return isinstance(obj, type(u''))
+
+
+def isbytes(obj):
+ """
+ Deprecated. Use::
+ >>> isinstance(obj, bytes)
+ after this import:
+ >>> from future.builtins import bytes
+ """
+ return isinstance(obj, type(b''))
+
+
+def isnewbytes(obj):
+ """
+ Equivalent to the result of ``type(obj) == type(newbytes)``
+ in other words, it is REALLY a newbytes instance, not a Py2 native str
+ object?
+
+ Note that this does not cover subclasses of newbytes, and it is not
+ equivalent to ininstance(obj, newbytes)
+ """
+ return type(obj).__name__ == 'newbytes'
+
+
+def isint(obj):
+ """
+ Deprecated. Tests whether an object is a Py3 ``int`` or either a Py2 ``int`` or
+ ``long``.
+
+ Instead of using this function, you can use:
+
+ >>> from future.builtins import int
+ >>> isinstance(obj, int)
+
+ The following idiom is equivalent:
+
+ >>> from numbers import Integral
+ >>> isinstance(obj, Integral)
+ """
+
+ return isinstance(obj, numbers.Integral)
+
+
+def native(obj):
+ """
+ On Py3, this is a no-op: native(obj) -> obj
+
+ On Py2, returns the corresponding native Py2 types that are
+ superclasses for backported objects from Py3:
+
+ >>> from builtins import str, bytes, int
+
+ >>> native(str(u'ABC'))
+ u'ABC'
+ >>> type(native(str(u'ABC')))
+ unicode
+
+ >>> native(bytes(b'ABC'))
+ b'ABC'
+ >>> type(native(bytes(b'ABC')))
+ bytes
+
+ >>> native(int(10**20))
+ 100000000000000000000L
+ >>> type(native(int(10**20)))
+ long
+
+ Existing native types on Py2 will be returned unchanged:
+
+ >>> type(native(u'ABC'))
+ unicode
+ """
+ if hasattr(obj, '__native__'):
+ return obj.__native__()
+ else:
+ return obj
+
+
+# Implementation of exec_ is from ``six``:
+if PY3:
+ import builtins
+ exec_ = getattr(builtins, "exec")
+else:
+ def exec_(code, globs=None, locs=None):
+ """Execute code in a namespace."""
+ if globs is None:
+ frame = sys._getframe(1)
+ globs = frame.f_globals
+ if locs is None:
+ locs = frame.f_locals
+ del frame
+ elif locs is None:
+ locs = globs
+ exec("""exec code in globs, locs""")
+
+
+# Defined here for backward compatibility:
+def old_div(a, b):
+ """
+ DEPRECATED: import ``old_div`` from ``past.utils`` instead.
+
+ Equivalent to ``a / b`` on Python 2 without ``from __future__ import
+ division``.
+
+ TODO: generalize this to other objects (like arrays etc.)
+ """
+ if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
+ return a // b
+ else:
+ return a / b
+
+
+def as_native_str(encoding='utf-8'):
+ '''
+ A decorator to turn a function or method call that returns text, i.e.
+ unicode, into one that returns a native platform str.
+
+ Use it as a decorator like this::
+
+ from __future__ import unicode_literals
+
+ class MyClass(object):
+ @as_native_str(encoding='ascii')
+ def __repr__(self):
+ return next(self._iter).upper()
+ '''
+ if PY3:
+ return lambda f: f
+ else:
+ def encoder(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ return f(*args, **kwargs).encode(encoding=encoding)
+ return wrapper
+ return encoder
+
+# listvalues and listitems definitions from Nick Coghlan's (withdrawn)
+# PEP 496:
+try:
+ dict.iteritems
+except AttributeError:
+ # Python 3
+ def listvalues(d):
+ return list(d.values())
+ def listitems(d):
+ return list(d.items())
+else:
+ # Python 2
+ def listvalues(d):
+ return d.values()
+ def listitems(d):
+ return d.items()
+
+if PY3:
+ def ensure_new_type(obj):
+ return obj
+else:
+ def ensure_new_type(obj):
+ from future.types.newbytes import newbytes
+ from future.types.newstr import newstr
+ from future.types.newint import newint
+ from future.types.newdict import newdict
+
+ native_type = type(native(obj))
+
+ # Upcast only if the type is already a native (non-future) type
+ if issubclass(native_type, type(obj)):
+ # Upcast
+ if native_type == str: # i.e. Py2 8-bit str
+ return newbytes(obj)
+ elif native_type == unicode:
+ return newstr(obj)
+ elif native_type == int:
+ return newint(obj)
+ elif native_type == long:
+ return newint(obj)
+ elif native_type == dict:
+ return newdict(obj)
+ else:
+ return obj
+ else:
+ # Already a new type
+ assert type(obj) in [newbytes, newstr]
+ return obj
+
+
+__all__ = ['PY2', 'PY26', 'PY3', 'PYPY',
+ 'as_native_str', 'binary_type', 'bind_method', 'bord', 'bstr',
+ 'bytes_to_native_str', 'class_types', 'encode_filename',
+ 'ensure_new_type', 'exec_', 'get_next', 'getexception',
+ 'implements_iterator', 'integer_types', 'is_new_style', 'isbytes',
+ 'isidentifier', 'isint', 'isnewbytes', 'istext', 'iteritems',
+ 'iterkeys', 'itervalues', 'lfilter', 'listitems', 'listvalues',
+ 'lmap', 'lrange', 'lzip', 'native', 'native_bytes', 'native_str',
+ 'native_str_to_bytes', 'old_div',
+ 'python_2_unicode_compatible', 'raise_',
+ 'raise_with_traceback', 'reraise', 'string_types',
+ 'text_to_native_str', 'text_type', 'tobytes', 'viewitems',
+ 'viewkeys', 'viewvalues', 'with_metaclass'
+ ]
diff --git a/venv/lib/python3.10/site-packages/future/utils/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/utils/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..2065d65
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/utils/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/utils/__pycache__/surrogateescape.cpython-310.pyc b/venv/lib/python3.10/site-packages/future/utils/__pycache__/surrogateescape.cpython-310.pyc
new file mode 100644
index 0000000..96ac328
Binary files /dev/null and b/venv/lib/python3.10/site-packages/future/utils/__pycache__/surrogateescape.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/future/utils/surrogateescape.py b/venv/lib/python3.10/site-packages/future/utils/surrogateescape.py
new file mode 100644
index 0000000..0dcc9fa
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/future/utils/surrogateescape.py
@@ -0,0 +1,198 @@
+"""
+This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error
+handler of Python 3.
+
+Source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc
+"""
+
+# This code is released under the Python license and the BSD 2-clause license
+
+import codecs
+import sys
+
+from future import utils
+
+
+FS_ERRORS = 'surrogateescape'
+
+# # -- Python 2/3 compatibility -------------------------------------
+# FS_ERRORS = 'my_surrogateescape'
+
+def u(text):
+ if utils.PY3:
+ return text
+ else:
+ return text.decode('unicode_escape')
+
+def b(data):
+ if utils.PY3:
+ return data.encode('latin1')
+ else:
+ return data
+
+if utils.PY3:
+ _unichr = chr
+ bytes_chr = lambda code: bytes((code,))
+else:
+ _unichr = unichr
+ bytes_chr = chr
+
+def surrogateescape_handler(exc):
+ """
+ Pure Python implementation of the PEP 383: the "surrogateescape" error
+ handler of Python 3. Undecodable bytes will be replaced by a Unicode
+ character U+DCxx on decoding, and these are translated into the
+ original bytes on encoding.
+ """
+ mystring = exc.object[exc.start:exc.end]
+
+ try:
+ if isinstance(exc, UnicodeDecodeError):
+ # mystring is a byte-string in this case
+ decoded = replace_surrogate_decode(mystring)
+ elif isinstance(exc, UnicodeEncodeError):
+ # In the case of u'\udcc3'.encode('ascii',
+ # 'this_surrogateescape_handler'), both Python 2.x and 3.x raise an
+ # exception anyway after this function is called, even though I think
+ # it's doing what it should. It seems that the strict encoder is called
+ # to encode the unicode string that this function returns ...
+ decoded = replace_surrogate_encode(mystring)
+ else:
+ raise exc
+ except NotASurrogateError:
+ raise exc
+ return (decoded, exc.end)
+
+
+class NotASurrogateError(Exception):
+ pass
+
+
+def replace_surrogate_encode(mystring):
+ """
+ Returns a (unicode) string, not the more logical bytes, because the codecs
+ register_error functionality expects this.
+ """
+ decoded = []
+ for ch in mystring:
+ # if utils.PY3:
+ # code = ch
+ # else:
+ code = ord(ch)
+
+ # The following magic comes from Py3.3's Python/codecs.c file:
+ if not 0xD800 <= code <= 0xDCFF:
+ # Not a surrogate. Fail with the original exception.
+ raise NotASurrogateError
+ # mybytes = [0xe0 | (code >> 12),
+ # 0x80 | ((code >> 6) & 0x3f),
+ # 0x80 | (code & 0x3f)]
+ # Is this a good idea?
+ if 0xDC00 <= code <= 0xDC7F:
+ decoded.append(_unichr(code - 0xDC00))
+ elif code <= 0xDCFF:
+ decoded.append(_unichr(code - 0xDC00))
+ else:
+ raise NotASurrogateError
+ return str().join(decoded)
+
+
+def replace_surrogate_decode(mybytes):
+ """
+ Returns a (unicode) string
+ """
+ decoded = []
+ for ch in mybytes:
+ # We may be parsing newbytes (in which case ch is an int) or a native
+ # str on Py2
+ if isinstance(ch, int):
+ code = ch
+ else:
+ code = ord(ch)
+ if 0x80 <= code <= 0xFF:
+ decoded.append(_unichr(0xDC00 + code))
+ elif code <= 0x7F:
+ decoded.append(_unichr(code))
+ else:
+ # # It may be a bad byte
+ # # Try swallowing it.
+ # continue
+ # print("RAISE!")
+ raise NotASurrogateError
+ return str().join(decoded)
+
+
+def encodefilename(fn):
+ if FS_ENCODING == 'ascii':
+ # ASCII encoder of Python 2 expects that the error handler returns a
+ # Unicode string encodable to ASCII, whereas our surrogateescape error
+ # handler has to return bytes in 0x80-0xFF range.
+ encoded = []
+ for index, ch in enumerate(fn):
+ code = ord(ch)
+ if code < 128:
+ ch = bytes_chr(code)
+ elif 0xDC80 <= code <= 0xDCFF:
+ ch = bytes_chr(code - 0xDC00)
+ else:
+ raise UnicodeEncodeError(FS_ENCODING,
+ fn, index, index+1,
+ 'ordinal not in range(128)')
+ encoded.append(ch)
+ return bytes().join(encoded)
+ elif FS_ENCODING == 'utf-8':
+ # UTF-8 encoder of Python 2 encodes surrogates, so U+DC80-U+DCFF
+ # doesn't go through our error handler
+ encoded = []
+ for index, ch in enumerate(fn):
+ code = ord(ch)
+ if 0xD800 <= code <= 0xDFFF:
+ if 0xDC80 <= code <= 0xDCFF:
+ ch = bytes_chr(code - 0xDC00)
+ encoded.append(ch)
+ else:
+ raise UnicodeEncodeError(
+ FS_ENCODING,
+ fn, index, index+1, 'surrogates not allowed')
+ else:
+ ch_utf8 = ch.encode('utf-8')
+ encoded.append(ch_utf8)
+ return bytes().join(encoded)
+ else:
+ return fn.encode(FS_ENCODING, FS_ERRORS)
+
+def decodefilename(fn):
+ return fn.decode(FS_ENCODING, FS_ERRORS)
+
+FS_ENCODING = 'ascii'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]')
+# FS_ENCODING = 'cp932'; fn = b('[abc\x81\x00]'); encoded = u('[abc\udc81\x00]')
+# FS_ENCODING = 'UTF-8'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]')
+
+
+# normalize the filesystem encoding name.
+# For example, we expect "utf-8", not "UTF8".
+FS_ENCODING = codecs.lookup(FS_ENCODING).name
+
+
+def register_surrogateescape():
+ """
+ Registers the surrogateescape error handler on Python 2 (only)
+ """
+ if utils.PY3:
+ return
+ try:
+ codecs.lookup_error(FS_ERRORS)
+ except LookupError:
+ codecs.register_error(FS_ERRORS, surrogateescape_handler)
+
+
+if __name__ == '__main__':
+ pass
+ # # Tests:
+ # register_surrogateescape()
+
+ # b = decodefilename(fn)
+ # assert b == encoded, "%r != %r" % (b, encoded)
+ # c = encodefilename(b)
+ # assert c == fn, '%r != %r' % (c, fn)
+ # # print("ok")
diff --git a/venv/lib/python3.10/site-packages/idna-3.11.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.10/site-packages/idna-3.11.dist-info/METADATA b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/METADATA
new file mode 100644
index 0000000..7a4a4b7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/METADATA
@@ -0,0 +1,209 @@
+Metadata-Version: 2.4
+Name: idna
+Version: 3.11
+Summary: Internationalized Domain Names in Applications (IDNA)
+Author-email: Kim Davies
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-Expression: BSD-3-Clause
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: Name Service (DNS)
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Utilities
+License-File: LICENSE.md
+Requires-Dist: ruff >= 0.6.2 ; extra == "all"
+Requires-Dist: mypy >= 1.11.2 ; extra == "all"
+Requires-Dist: pytest >= 8.3.2 ; extra == "all"
+Requires-Dist: flake8 >= 7.1.1 ; extra == "all"
+Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst
+Project-URL: Issue tracker, https://github.com/kjd/idna/issues
+Project-URL: Source, https://github.com/kjd/idna
+Provides-Extra: all
+
+Internationalized Domain Names in Applications (IDNA)
+=====================================================
+
+Support for `Internationalized Domain Names in
+Applications (IDNA) `_
+and `Unicode IDNA Compatibility Processing
+`_.
+
+The latest versions of these standards supplied here provide
+more comprehensive language coverage and reduce the potential of
+allowing domains with known security vulnerabilities. This library
+is a suitable replacement for the “encodings.idna”
+module that comes with the Python standard library, but which
+only supports an older superseded IDNA specification from 2003.
+
+Basic functions are simply executed:
+
+.. code-block:: pycon
+
+ >>> import idna
+ >>> idna.encode('ドメイン.テスト')
+ b'xn--eckwd4c7c.xn--zckzah'
+ >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah'))
+ ドメイン.テスト
+
+
+Installation
+------------
+
+This package is available for installation from PyPI via the
+typical mechanisms, such as:
+
+.. code-block:: bash
+
+ $ python3 -m pip install idna
+
+
+Usage
+-----
+
+For typical usage, the ``encode`` and ``decode`` functions will take a
+domain name argument and perform a conversion to ASCII compatible encoding
+(known as A-labels), or to Unicode strings (known as U-labels)
+respectively.
+
+.. code-block:: pycon
+
+ >>> import idna
+ >>> idna.encode('ドメイン.テスト')
+ b'xn--eckwd4c7c.xn--zckzah'
+ >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah'))
+ ドメイン.テスト
+
+Conversions can be applied at a per-label basis using the ``ulabel`` or
+``alabel`` functions if necessary:
+
+.. code-block:: pycon
+
+ >>> idna.alabel('测试')
+ b'xn--0zwm56d'
+
+
+Compatibility Mapping (UTS #46)
++++++++++++++++++++++++++++++++
+
+This library provides support for `Unicode IDNA Compatibility
+Processing `_ which normalizes input from
+different potential ways a user may input a domain prior to performing the IDNA
+conversion operations. This functionality, known as a
+`mapping `_, is considered by the
+specification to be a local user-interface issue distinct from IDNA
+conversion functionality.
+
+For example, “Königsgäßchen” is not a permissible label as *LATIN
+CAPITAL LETTER K* is not allowed (nor are capital letters in general).
+UTS 46 will convert this into lower case prior to applying the IDNA
+conversion.
+
+.. code-block:: pycon
+
+ >>> import idna
+ >>> idna.encode('Königsgäßchen')
+ ...
+ idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed
+ >>> idna.encode('Königsgäßchen', uts46=True)
+ b'xn--knigsgchen-b4a3dun'
+ >>> print(idna.decode('xn--knigsgchen-b4a3dun'))
+ königsgäßchen
+
+
+Exceptions
+----------
+
+All errors raised during the conversion following the specification
+should raise an exception derived from the ``idna.IDNAError`` base
+class.
+
+More specific exceptions that may be generated as ``idna.IDNABidiError``
+when the error reflects an illegal combination of left-to-right and
+right-to-left characters in a label; ``idna.InvalidCodepoint`` when
+a specific codepoint is an illegal character in an IDN label (i.e.
+INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is
+illegal based on its position in the string (i.e. it is CONTEXTO or CONTEXTJ
+but the contextual requirements are not satisfied.)
+
+Building and Diagnostics
+------------------------
+
+The IDNA and UTS 46 functionality relies upon pre-calculated lookup
+tables for performance. These tables are derived from computing against
+eligibility criteria in the respective standards using the command-line
+script ``tools/idna-data``.
+
+This tool will fetch relevant codepoint data from the Unicode repository
+and perform the required calculations to identify eligibility. There are
+three main modes:
+
+* ``idna-data make-libdata``. Generates ``idnadata.py`` and
+ ``uts46data.py``, the pre-calculated lookup tables used for IDNA and
+ UTS 46 conversions. Implementers who wish to track this library against
+ a different Unicode version may use this tool to manually generate a
+ different version of the ``idnadata.py`` and ``uts46data.py`` files.
+
+* ``idna-data make-table``. Generate a table of the IDNA disposition
+ (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix
+ B.1 of RFC 5892 and the pre-computed tables published by `IANA
+ `_.
+
+* ``idna-data U+0061``. Prints debugging output on the various
+ properties associated with an individual Unicode codepoint (in this
+ case, U+0061), that are used to assess the IDNA and UTS 46 status of a
+ codepoint. This is helpful in debugging or analysis.
+
+The tool accepts a number of arguments, described using ``idna-data
+-h``. Most notably, the ``--version`` argument allows the specification
+of the version of Unicode to be used in computing the table data. For
+example, ``idna-data --version 9.0.0 make-libdata`` will generate
+library data against Unicode 9.0.0.
+
+
+Additional Notes
+----------------
+
+* **Packages**. The latest tagged release version is published in the
+ `Python Package Index `_.
+
+* **Version support**. This library supports Python 3.8 and higher.
+ As this library serves as a low-level toolkit for a variety of
+ applications, many of which strive for broad compatibility with older
+ Python versions, there is no rush to remove older interpreter support.
+ Support for older versions are likely to be removed from new releases
+ as automated tests can no longer easily be run, i.e. once the Python
+ version is officially end-of-life.
+
+* **Testing**. The library has a test suite based on each rule of the
+ IDNA specification, as well as tests that are provided as part of the
+ Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing
+ `_.
+
+* **Emoji**. It is an occasional request to support emoji domains in
+ this library. Encoding of symbols like emoji is expressly prohibited by
+ the technical standard IDNA 2008 and emoji domains are broadly phased
+ out across the domain industry due to associated security risks. For
+ now, applications that need to support these non-compliant labels
+ may wish to consider trying the encode/decode operation in this library
+ first, and then falling back to using `encodings.idna`. See `the Github
+ project `_ for more discussion.
+
+* **Transitional processing**. Unicode 16.0.0 removed transitional
+ processing so the `transitional` argument for the encode() method
+ no longer has any effect and will be removed at a later date.
+
diff --git a/venv/lib/python3.10/site-packages/idna-3.11.dist-info/RECORD b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/RECORD
new file mode 100644
index 0000000..8995942
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/RECORD
@@ -0,0 +1,22 @@
+idna-3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+idna-3.11.dist-info/METADATA,sha256=fCwSww9SuiN8TIHllFSASUQCW55hAs8dzKnr9RaEEbA,8378
+idna-3.11.dist-info/RECORD,,
+idna-3.11.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+idna-3.11.dist-info/licenses/LICENSE.md,sha256=t6M2q_OwThgOwGXN0W5wXQeeHMehT5EKpukYfza5zYc,1541
+idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868
+idna/__pycache__/__init__.cpython-310.pyc,,
+idna/__pycache__/codec.cpython-310.pyc,,
+idna/__pycache__/compat.cpython-310.pyc,,
+idna/__pycache__/core.cpython-310.pyc,,
+idna/__pycache__/idnadata.cpython-310.pyc,,
+idna/__pycache__/intranges.cpython-310.pyc,,
+idna/__pycache__/package_data.cpython-310.pyc,,
+idna/__pycache__/uts46data.cpython-310.pyc,,
+idna/codec.py,sha256=M2SGWN7cs_6B32QmKTyTN6xQGZeYQgQ2wiX3_DR6loE,3438
+idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316
+idna/core.py,sha256=P26_XVycuMTZ1R2mNK1ZREVzM5mvTzdabBXfyZVU1Lc,13246
+idna/idnadata.py,sha256=SG8jhaGE53iiD6B49pt2pwTv_UvClciWE-N54oR2p4U,79623
+idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898
+idna/package_data.py,sha256=_CUavOxobnbyNG2FLyHoN8QHP3QM9W1tKuw7eq9QwBk,21
+idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+idna/uts46data.py,sha256=H9J35VkD0F9L9mKOqjeNGd2A-Va6FlPoz6Jz4K7h-ps,243725
diff --git a/venv/lib/python3.10/site-packages/idna-3.11.dist-info/WHEEL b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/WHEEL
new file mode 100644
index 0000000..d8b9936
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: flit 3.12.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/venv/lib/python3.10/site-packages/idna-3.11.dist-info/licenses/LICENSE.md b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/licenses/LICENSE.md
new file mode 100644
index 0000000..256ba90
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna-3.11.dist-info/licenses/LICENSE.md
@@ -0,0 +1,31 @@
+BSD 3-Clause License
+
+Copyright (c) 2013-2025, Kim Davies and contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.10/site-packages/idna/__init__.py b/venv/lib/python3.10/site-packages/idna/__init__.py
new file mode 100644
index 0000000..cfdc030
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/__init__.py
@@ -0,0 +1,45 @@
+from .core import (
+ IDNABidiError,
+ IDNAError,
+ InvalidCodepoint,
+ InvalidCodepointContext,
+ alabel,
+ check_bidi,
+ check_hyphen_ok,
+ check_initial_combiner,
+ check_label,
+ check_nfc,
+ decode,
+ encode,
+ ulabel,
+ uts46_remap,
+ valid_contextj,
+ valid_contexto,
+ valid_label_length,
+ valid_string_length,
+)
+from .intranges import intranges_contain
+from .package_data import __version__
+
+__all__ = [
+ "__version__",
+ "IDNABidiError",
+ "IDNAError",
+ "InvalidCodepoint",
+ "InvalidCodepointContext",
+ "alabel",
+ "check_bidi",
+ "check_hyphen_ok",
+ "check_initial_combiner",
+ "check_label",
+ "check_nfc",
+ "decode",
+ "encode",
+ "intranges_contain",
+ "ulabel",
+ "uts46_remap",
+ "valid_contextj",
+ "valid_contexto",
+ "valid_label_length",
+ "valid_string_length",
+]
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..9c505b7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc
new file mode 100644
index 0000000..4cd0935
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc
new file mode 100644
index 0000000..255cdb8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc
new file mode 100644
index 0000000..9418220
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc
new file mode 100644
index 0000000..1a435be
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc
new file mode 100644
index 0000000..db5f558
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc
new file mode 100644
index 0000000..542d452
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc b/venv/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc
new file mode 100644
index 0000000..50f9af2
Binary files /dev/null and b/venv/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/idna/codec.py b/venv/lib/python3.10/site-packages/idna/codec.py
new file mode 100644
index 0000000..cbc2e4f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/codec.py
@@ -0,0 +1,122 @@
+import codecs
+import re
+from typing import Any, Optional, Tuple
+
+from .core import IDNAError, alabel, decode, encode, ulabel
+
+_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
+
+
+class Codec(codecs.Codec):
+ def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]:
+ if errors != "strict":
+ raise IDNAError('Unsupported error handling "{}"'.format(errors))
+
+ if not data:
+ return b"", 0
+
+ return encode(data), len(data)
+
+ def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]:
+ if errors != "strict":
+ raise IDNAError('Unsupported error handling "{}"'.format(errors))
+
+ if not data:
+ return "", 0
+
+ return decode(data), len(data)
+
+
+class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
+ def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]:
+ if errors != "strict":
+ raise IDNAError('Unsupported error handling "{}"'.format(errors))
+
+ if not data:
+ return b"", 0
+
+ labels = _unicode_dots_re.split(data)
+ trailing_dot = b""
+ if labels:
+ if not labels[-1]:
+ trailing_dot = b"."
+ del labels[-1]
+ elif not final:
+ # Keep potentially unfinished label until the next call
+ del labels[-1]
+ if labels:
+ trailing_dot = b"."
+
+ result = []
+ size = 0
+ for label in labels:
+ result.append(alabel(label))
+ if size:
+ size += 1
+ size += len(label)
+
+ # Join with U+002E
+ result_bytes = b".".join(result) + trailing_dot
+ size += len(trailing_dot)
+ return result_bytes, size
+
+
+class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
+ def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]:
+ if errors != "strict":
+ raise IDNAError('Unsupported error handling "{}"'.format(errors))
+
+ if not data:
+ return ("", 0)
+
+ if not isinstance(data, str):
+ data = str(data, "ascii")
+
+ labels = _unicode_dots_re.split(data)
+ trailing_dot = ""
+ if labels:
+ if not labels[-1]:
+ trailing_dot = "."
+ del labels[-1]
+ elif not final:
+ # Keep potentially unfinished label until the next call
+ del labels[-1]
+ if labels:
+ trailing_dot = "."
+
+ result = []
+ size = 0
+ for label in labels:
+ result.append(ulabel(label))
+ if size:
+ size += 1
+ size += len(label)
+
+ result_str = ".".join(result) + trailing_dot
+ size += len(trailing_dot)
+ return (result_str, size)
+
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ pass
+
+
+class StreamReader(Codec, codecs.StreamReader):
+ pass
+
+
+def search_function(name: str) -> Optional[codecs.CodecInfo]:
+ if name != "idna2008":
+ return None
+ return codecs.CodecInfo(
+ name=name,
+ encode=Codec().encode,
+ decode=Codec().decode, # type: ignore
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
+
+
+codecs.register(search_function)
diff --git a/venv/lib/python3.10/site-packages/idna/compat.py b/venv/lib/python3.10/site-packages/idna/compat.py
new file mode 100644
index 0000000..1df9f2a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/compat.py
@@ -0,0 +1,15 @@
+from typing import Any, Union
+
+from .core import decode, encode
+
+
+def ToASCII(label: str) -> bytes:
+ return encode(label)
+
+
+def ToUnicode(label: Union[bytes, bytearray]) -> str:
+ return decode(label)
+
+
+def nameprep(s: Any) -> None:
+ raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol")
diff --git a/venv/lib/python3.10/site-packages/idna/core.py b/venv/lib/python3.10/site-packages/idna/core.py
new file mode 100644
index 0000000..8177bf7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/core.py
@@ -0,0 +1,437 @@
+import bisect
+import re
+import unicodedata
+from typing import Optional, Union
+
+from . import idnadata
+from .intranges import intranges_contain
+
+_virama_combining_class = 9
+_alabel_prefix = b"xn--"
+_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
+
+
+class IDNAError(UnicodeError):
+ """Base exception for all IDNA-encoding related problems"""
+
+ pass
+
+
+class IDNABidiError(IDNAError):
+ """Exception when bidirectional requirements are not satisfied"""
+
+ pass
+
+
+class InvalidCodepoint(IDNAError):
+ """Exception when a disallowed or unallocated codepoint is used"""
+
+ pass
+
+
+class InvalidCodepointContext(IDNAError):
+ """Exception when the codepoint is not valid in the context it is used"""
+
+ pass
+
+
+def _combining_class(cp: int) -> int:
+ v = unicodedata.combining(chr(cp))
+ if v == 0:
+ if not unicodedata.name(chr(cp)):
+ raise ValueError("Unknown character in unicodedata")
+ return v
+
+
+def _is_script(cp: str, script: str) -> bool:
+ return intranges_contain(ord(cp), idnadata.scripts[script])
+
+
+def _punycode(s: str) -> bytes:
+ return s.encode("punycode")
+
+
+def _unot(s: int) -> str:
+ return "U+{:04X}".format(s)
+
+
+def valid_label_length(label: Union[bytes, str]) -> bool:
+ if len(label) > 63:
+ return False
+ return True
+
+
+def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool:
+ if len(label) > (254 if trailing_dot else 253):
+ return False
+ return True
+
+
+def check_bidi(label: str, check_ltr: bool = False) -> bool:
+ # Bidi rules should only be applied if string contains RTL characters
+ bidi_label = False
+ for idx, cp in enumerate(label, 1):
+ direction = unicodedata.bidirectional(cp)
+ if direction == "":
+ # String likely comes from a newer version of Unicode
+ raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx))
+ if direction in ["R", "AL", "AN"]:
+ bidi_label = True
+ if not bidi_label and not check_ltr:
+ return True
+
+ # Bidi rule 1
+ direction = unicodedata.bidirectional(label[0])
+ if direction in ["R", "AL"]:
+ rtl = True
+ elif direction == "L":
+ rtl = False
+ else:
+ raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label)))
+
+ valid_ending = False
+ number_type: Optional[str] = None
+ for idx, cp in enumerate(label, 1):
+ direction = unicodedata.bidirectional(cp)
+
+ if rtl:
+ # Bidi rule 2
+ if direction not in [
+ "R",
+ "AL",
+ "AN",
+ "EN",
+ "ES",
+ "CS",
+ "ET",
+ "ON",
+ "BN",
+ "NSM",
+ ]:
+ raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx))
+ # Bidi rule 3
+ if direction in ["R", "AL", "EN", "AN"]:
+ valid_ending = True
+ elif direction != "NSM":
+ valid_ending = False
+ # Bidi rule 4
+ if direction in ["AN", "EN"]:
+ if not number_type:
+ number_type = direction
+ else:
+ if number_type != direction:
+ raise IDNABidiError("Can not mix numeral types in a right-to-left label")
+ else:
+ # Bidi rule 5
+ if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]:
+ raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx))
+ # Bidi rule 6
+ if direction in ["L", "EN"]:
+ valid_ending = True
+ elif direction != "NSM":
+ valid_ending = False
+
+ if not valid_ending:
+ raise IDNABidiError("Label ends with illegal codepoint directionality")
+
+ return True
+
+
+def check_initial_combiner(label: str) -> bool:
+ if unicodedata.category(label[0])[0] == "M":
+ raise IDNAError("Label begins with an illegal combining character")
+ return True
+
+
+def check_hyphen_ok(label: str) -> bool:
+ if label[2:4] == "--":
+ raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
+ if label[0] == "-" or label[-1] == "-":
+ raise IDNAError("Label must not start or end with a hyphen")
+ return True
+
+
+def check_nfc(label: str) -> None:
+ if unicodedata.normalize("NFC", label) != label:
+ raise IDNAError("Label must be in Normalization Form C")
+
+
+def valid_contextj(label: str, pos: int) -> bool:
+ cp_value = ord(label[pos])
+
+ if cp_value == 0x200C:
+ if pos > 0:
+ if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
+ return True
+
+ ok = False
+ for i in range(pos - 1, -1, -1):
+ joining_type = idnadata.joining_types.get(ord(label[i]))
+ if joining_type == ord("T"):
+ continue
+ elif joining_type in [ord("L"), ord("D")]:
+ ok = True
+ break
+ else:
+ break
+
+ if not ok:
+ return False
+
+ ok = False
+ for i in range(pos + 1, len(label)):
+ joining_type = idnadata.joining_types.get(ord(label[i]))
+ if joining_type == ord("T"):
+ continue
+ elif joining_type in [ord("R"), ord("D")]:
+ ok = True
+ break
+ else:
+ break
+ return ok
+
+ if cp_value == 0x200D:
+ if pos > 0:
+ if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
+ return True
+ return False
+
+ else:
+ return False
+
+
+def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
+ cp_value = ord(label[pos])
+
+ if cp_value == 0x00B7:
+ if 0 < pos < len(label) - 1:
+ if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C:
+ return True
+ return False
+
+ elif cp_value == 0x0375:
+ if pos < len(label) - 1 and len(label) > 1:
+ return _is_script(label[pos + 1], "Greek")
+ return False
+
+ elif cp_value == 0x05F3 or cp_value == 0x05F4:
+ if pos > 0:
+ return _is_script(label[pos - 1], "Hebrew")
+ return False
+
+ elif cp_value == 0x30FB:
+ for cp in label:
+ if cp == "\u30fb":
+ continue
+ if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"):
+ return True
+ return False
+
+ elif 0x660 <= cp_value <= 0x669:
+ for cp in label:
+ if 0x6F0 <= ord(cp) <= 0x06F9:
+ return False
+ return True
+
+ elif 0x6F0 <= cp_value <= 0x6F9:
+ for cp in label:
+ if 0x660 <= ord(cp) <= 0x0669:
+ return False
+ return True
+
+ return False
+
+
+def check_label(label: Union[str, bytes, bytearray]) -> None:
+ if isinstance(label, (bytes, bytearray)):
+ label = label.decode("utf-8")
+ if len(label) == 0:
+ raise IDNAError("Empty Label")
+
+ check_nfc(label)
+ check_hyphen_ok(label)
+ check_initial_combiner(label)
+
+ for pos, cp in enumerate(label):
+ cp_value = ord(cp)
+ if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
+ continue
+ elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
+ try:
+ if not valid_contextj(label, pos):
+ raise InvalidCodepointContext(
+ "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
+ )
+ except ValueError:
+ raise IDNAError(
+ "Unknown codepoint adjacent to joiner {} at position {} in {}".format(
+ _unot(cp_value), pos + 1, repr(label)
+ )
+ )
+ elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
+ if not valid_contexto(label, pos):
+ raise InvalidCodepointContext(
+ "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
+ )
+ else:
+ raise InvalidCodepoint(
+ "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label))
+ )
+
+ check_bidi(label)
+
+
+def alabel(label: str) -> bytes:
+ try:
+ label_bytes = label.encode("ascii")
+ ulabel(label_bytes)
+ if not valid_label_length(label_bytes):
+ raise IDNAError("Label too long")
+ return label_bytes
+ except UnicodeEncodeError:
+ pass
+
+ check_label(label)
+ label_bytes = _alabel_prefix + _punycode(label)
+
+ if not valid_label_length(label_bytes):
+ raise IDNAError("Label too long")
+
+ return label_bytes
+
+
+def ulabel(label: Union[str, bytes, bytearray]) -> str:
+ if not isinstance(label, (bytes, bytearray)):
+ try:
+ label_bytes = label.encode("ascii")
+ except UnicodeEncodeError:
+ check_label(label)
+ return label
+ else:
+ label_bytes = bytes(label)
+
+ label_bytes = label_bytes.lower()
+ if label_bytes.startswith(_alabel_prefix):
+ label_bytes = label_bytes[len(_alabel_prefix) :]
+ if not label_bytes:
+ raise IDNAError("Malformed A-label, no Punycode eligible content found")
+ if label_bytes.decode("ascii")[-1] == "-":
+ raise IDNAError("A-label must not end with a hyphen")
+ else:
+ check_label(label_bytes)
+ return label_bytes.decode("ascii")
+
+ try:
+ label = label_bytes.decode("punycode")
+ except UnicodeError:
+ raise IDNAError("Invalid A-label")
+ check_label(label)
+ return label
+
+
+def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:
+ """Re-map the characters in the string according to UTS46 processing."""
+ from .uts46data import uts46data
+
+ output = ""
+
+ for pos, char in enumerate(domain):
+ code_point = ord(char)
+ try:
+ uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1]
+ status = uts46row[1]
+ replacement: Optional[str] = None
+ if len(uts46row) == 3:
+ replacement = uts46row[2]
+ if (
+ status == "V"
+ or (status == "D" and not transitional)
+ or (status == "3" and not std3_rules and replacement is None)
+ ):
+ output += char
+ elif replacement is not None and (
+ status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
+ ):
+ output += replacement
+ elif status != "I":
+ raise IndexError()
+ except IndexError:
+ raise InvalidCodepoint(
+ "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain))
+ )
+
+ return unicodedata.normalize("NFC", output)
+
+
+def encode(
+ s: Union[str, bytes, bytearray],
+ strict: bool = False,
+ uts46: bool = False,
+ std3_rules: bool = False,
+ transitional: bool = False,
+) -> bytes:
+ if not isinstance(s, str):
+ try:
+ s = str(s, "ascii")
+ except UnicodeDecodeError:
+ raise IDNAError("should pass a unicode string to the function rather than a byte string.")
+ if uts46:
+ s = uts46_remap(s, std3_rules, transitional)
+ trailing_dot = False
+ result = []
+ if strict:
+ labels = s.split(".")
+ else:
+ labels = _unicode_dots_re.split(s)
+ if not labels or labels == [""]:
+ raise IDNAError("Empty domain")
+ if labels[-1] == "":
+ del labels[-1]
+ trailing_dot = True
+ for label in labels:
+ s = alabel(label)
+ if s:
+ result.append(s)
+ else:
+ raise IDNAError("Empty label")
+ if trailing_dot:
+ result.append(b"")
+ s = b".".join(result)
+ if not valid_string_length(s, trailing_dot):
+ raise IDNAError("Domain too long")
+ return s
+
+
+def decode(
+ s: Union[str, bytes, bytearray],
+ strict: bool = False,
+ uts46: bool = False,
+ std3_rules: bool = False,
+) -> str:
+ try:
+ if not isinstance(s, str):
+ s = str(s, "ascii")
+ except UnicodeDecodeError:
+ raise IDNAError("Invalid ASCII in A-label")
+ if uts46:
+ s = uts46_remap(s, std3_rules, False)
+ trailing_dot = False
+ result = []
+ if not strict:
+ labels = _unicode_dots_re.split(s)
+ else:
+ labels = s.split(".")
+ if not labels or labels == [""]:
+ raise IDNAError("Empty domain")
+ if not labels[-1]:
+ del labels[-1]
+ trailing_dot = True
+ for label in labels:
+ s = ulabel(label)
+ if s:
+ result.append(s)
+ else:
+ raise IDNAError("Empty label")
+ if trailing_dot:
+ result.append("")
+ return ".".join(result)
diff --git a/venv/lib/python3.10/site-packages/idna/idnadata.py b/venv/lib/python3.10/site-packages/idna/idnadata.py
new file mode 100644
index 0000000..ded47ca
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/idnadata.py
@@ -0,0 +1,4309 @@
+# This file is automatically generated by tools/idna-data
+
+__version__ = "16.0.0"
+
+scripts = {
+ "Greek": (
+ 0x37000000374,
+ 0x37500000378,
+ 0x37A0000037E,
+ 0x37F00000380,
+ 0x38400000385,
+ 0x38600000387,
+ 0x3880000038B,
+ 0x38C0000038D,
+ 0x38E000003A2,
+ 0x3A3000003E2,
+ 0x3F000000400,
+ 0x1D2600001D2B,
+ 0x1D5D00001D62,
+ 0x1D6600001D6B,
+ 0x1DBF00001DC0,
+ 0x1F0000001F16,
+ 0x1F1800001F1E,
+ 0x1F2000001F46,
+ 0x1F4800001F4E,
+ 0x1F5000001F58,
+ 0x1F5900001F5A,
+ 0x1F5B00001F5C,
+ 0x1F5D00001F5E,
+ 0x1F5F00001F7E,
+ 0x1F8000001FB5,
+ 0x1FB600001FC5,
+ 0x1FC600001FD4,
+ 0x1FD600001FDC,
+ 0x1FDD00001FF0,
+ 0x1FF200001FF5,
+ 0x1FF600001FFF,
+ 0x212600002127,
+ 0xAB650000AB66,
+ 0x101400001018F,
+ 0x101A0000101A1,
+ 0x1D2000001D246,
+ ),
+ "Han": (
+ 0x2E8000002E9A,
+ 0x2E9B00002EF4,
+ 0x2F0000002FD6,
+ 0x300500003006,
+ 0x300700003008,
+ 0x30210000302A,
+ 0x30380000303C,
+ 0x340000004DC0,
+ 0x4E000000A000,
+ 0xF9000000FA6E,
+ 0xFA700000FADA,
+ 0x16FE200016FE4,
+ 0x16FF000016FF2,
+ 0x200000002A6E0,
+ 0x2A7000002B73A,
+ 0x2B7400002B81E,
+ 0x2B8200002CEA2,
+ 0x2CEB00002EBE1,
+ 0x2EBF00002EE5E,
+ 0x2F8000002FA1E,
+ 0x300000003134B,
+ 0x31350000323B0,
+ ),
+ "Hebrew": (
+ 0x591000005C8,
+ 0x5D0000005EB,
+ 0x5EF000005F5,
+ 0xFB1D0000FB37,
+ 0xFB380000FB3D,
+ 0xFB3E0000FB3F,
+ 0xFB400000FB42,
+ 0xFB430000FB45,
+ 0xFB460000FB50,
+ ),
+ "Hiragana": (
+ 0x304100003097,
+ 0x309D000030A0,
+ 0x1B0010001B120,
+ 0x1B1320001B133,
+ 0x1B1500001B153,
+ 0x1F2000001F201,
+ ),
+ "Katakana": (
+ 0x30A1000030FB,
+ 0x30FD00003100,
+ 0x31F000003200,
+ 0x32D0000032FF,
+ 0x330000003358,
+ 0xFF660000FF70,
+ 0xFF710000FF9E,
+ 0x1AFF00001AFF4,
+ 0x1AFF50001AFFC,
+ 0x1AFFD0001AFFF,
+ 0x1B0000001B001,
+ 0x1B1200001B123,
+ 0x1B1550001B156,
+ 0x1B1640001B168,
+ ),
+}
+joining_types = {
+ 0xAD: 84,
+ 0x300: 84,
+ 0x301: 84,
+ 0x302: 84,
+ 0x303: 84,
+ 0x304: 84,
+ 0x305: 84,
+ 0x306: 84,
+ 0x307: 84,
+ 0x308: 84,
+ 0x309: 84,
+ 0x30A: 84,
+ 0x30B: 84,
+ 0x30C: 84,
+ 0x30D: 84,
+ 0x30E: 84,
+ 0x30F: 84,
+ 0x310: 84,
+ 0x311: 84,
+ 0x312: 84,
+ 0x313: 84,
+ 0x314: 84,
+ 0x315: 84,
+ 0x316: 84,
+ 0x317: 84,
+ 0x318: 84,
+ 0x319: 84,
+ 0x31A: 84,
+ 0x31B: 84,
+ 0x31C: 84,
+ 0x31D: 84,
+ 0x31E: 84,
+ 0x31F: 84,
+ 0x320: 84,
+ 0x321: 84,
+ 0x322: 84,
+ 0x323: 84,
+ 0x324: 84,
+ 0x325: 84,
+ 0x326: 84,
+ 0x327: 84,
+ 0x328: 84,
+ 0x329: 84,
+ 0x32A: 84,
+ 0x32B: 84,
+ 0x32C: 84,
+ 0x32D: 84,
+ 0x32E: 84,
+ 0x32F: 84,
+ 0x330: 84,
+ 0x331: 84,
+ 0x332: 84,
+ 0x333: 84,
+ 0x334: 84,
+ 0x335: 84,
+ 0x336: 84,
+ 0x337: 84,
+ 0x338: 84,
+ 0x339: 84,
+ 0x33A: 84,
+ 0x33B: 84,
+ 0x33C: 84,
+ 0x33D: 84,
+ 0x33E: 84,
+ 0x33F: 84,
+ 0x340: 84,
+ 0x341: 84,
+ 0x342: 84,
+ 0x343: 84,
+ 0x344: 84,
+ 0x345: 84,
+ 0x346: 84,
+ 0x347: 84,
+ 0x348: 84,
+ 0x349: 84,
+ 0x34A: 84,
+ 0x34B: 84,
+ 0x34C: 84,
+ 0x34D: 84,
+ 0x34E: 84,
+ 0x34F: 84,
+ 0x350: 84,
+ 0x351: 84,
+ 0x352: 84,
+ 0x353: 84,
+ 0x354: 84,
+ 0x355: 84,
+ 0x356: 84,
+ 0x357: 84,
+ 0x358: 84,
+ 0x359: 84,
+ 0x35A: 84,
+ 0x35B: 84,
+ 0x35C: 84,
+ 0x35D: 84,
+ 0x35E: 84,
+ 0x35F: 84,
+ 0x360: 84,
+ 0x361: 84,
+ 0x362: 84,
+ 0x363: 84,
+ 0x364: 84,
+ 0x365: 84,
+ 0x366: 84,
+ 0x367: 84,
+ 0x368: 84,
+ 0x369: 84,
+ 0x36A: 84,
+ 0x36B: 84,
+ 0x36C: 84,
+ 0x36D: 84,
+ 0x36E: 84,
+ 0x36F: 84,
+ 0x483: 84,
+ 0x484: 84,
+ 0x485: 84,
+ 0x486: 84,
+ 0x487: 84,
+ 0x488: 84,
+ 0x489: 84,
+ 0x591: 84,
+ 0x592: 84,
+ 0x593: 84,
+ 0x594: 84,
+ 0x595: 84,
+ 0x596: 84,
+ 0x597: 84,
+ 0x598: 84,
+ 0x599: 84,
+ 0x59A: 84,
+ 0x59B: 84,
+ 0x59C: 84,
+ 0x59D: 84,
+ 0x59E: 84,
+ 0x59F: 84,
+ 0x5A0: 84,
+ 0x5A1: 84,
+ 0x5A2: 84,
+ 0x5A3: 84,
+ 0x5A4: 84,
+ 0x5A5: 84,
+ 0x5A6: 84,
+ 0x5A7: 84,
+ 0x5A8: 84,
+ 0x5A9: 84,
+ 0x5AA: 84,
+ 0x5AB: 84,
+ 0x5AC: 84,
+ 0x5AD: 84,
+ 0x5AE: 84,
+ 0x5AF: 84,
+ 0x5B0: 84,
+ 0x5B1: 84,
+ 0x5B2: 84,
+ 0x5B3: 84,
+ 0x5B4: 84,
+ 0x5B5: 84,
+ 0x5B6: 84,
+ 0x5B7: 84,
+ 0x5B8: 84,
+ 0x5B9: 84,
+ 0x5BA: 84,
+ 0x5BB: 84,
+ 0x5BC: 84,
+ 0x5BD: 84,
+ 0x5BF: 84,
+ 0x5C1: 84,
+ 0x5C2: 84,
+ 0x5C4: 84,
+ 0x5C5: 84,
+ 0x5C7: 84,
+ 0x610: 84,
+ 0x611: 84,
+ 0x612: 84,
+ 0x613: 84,
+ 0x614: 84,
+ 0x615: 84,
+ 0x616: 84,
+ 0x617: 84,
+ 0x618: 84,
+ 0x619: 84,
+ 0x61A: 84,
+ 0x61C: 84,
+ 0x620: 68,
+ 0x622: 82,
+ 0x623: 82,
+ 0x624: 82,
+ 0x625: 82,
+ 0x626: 68,
+ 0x627: 82,
+ 0x628: 68,
+ 0x629: 82,
+ 0x62A: 68,
+ 0x62B: 68,
+ 0x62C: 68,
+ 0x62D: 68,
+ 0x62E: 68,
+ 0x62F: 82,
+ 0x630: 82,
+ 0x631: 82,
+ 0x632: 82,
+ 0x633: 68,
+ 0x634: 68,
+ 0x635: 68,
+ 0x636: 68,
+ 0x637: 68,
+ 0x638: 68,
+ 0x639: 68,
+ 0x63A: 68,
+ 0x63B: 68,
+ 0x63C: 68,
+ 0x63D: 68,
+ 0x63E: 68,
+ 0x63F: 68,
+ 0x640: 67,
+ 0x641: 68,
+ 0x642: 68,
+ 0x643: 68,
+ 0x644: 68,
+ 0x645: 68,
+ 0x646: 68,
+ 0x647: 68,
+ 0x648: 82,
+ 0x649: 68,
+ 0x64A: 68,
+ 0x64B: 84,
+ 0x64C: 84,
+ 0x64D: 84,
+ 0x64E: 84,
+ 0x64F: 84,
+ 0x650: 84,
+ 0x651: 84,
+ 0x652: 84,
+ 0x653: 84,
+ 0x654: 84,
+ 0x655: 84,
+ 0x656: 84,
+ 0x657: 84,
+ 0x658: 84,
+ 0x659: 84,
+ 0x65A: 84,
+ 0x65B: 84,
+ 0x65C: 84,
+ 0x65D: 84,
+ 0x65E: 84,
+ 0x65F: 84,
+ 0x66E: 68,
+ 0x66F: 68,
+ 0x670: 84,
+ 0x671: 82,
+ 0x672: 82,
+ 0x673: 82,
+ 0x675: 82,
+ 0x676: 82,
+ 0x677: 82,
+ 0x678: 68,
+ 0x679: 68,
+ 0x67A: 68,
+ 0x67B: 68,
+ 0x67C: 68,
+ 0x67D: 68,
+ 0x67E: 68,
+ 0x67F: 68,
+ 0x680: 68,
+ 0x681: 68,
+ 0x682: 68,
+ 0x683: 68,
+ 0x684: 68,
+ 0x685: 68,
+ 0x686: 68,
+ 0x687: 68,
+ 0x688: 82,
+ 0x689: 82,
+ 0x68A: 82,
+ 0x68B: 82,
+ 0x68C: 82,
+ 0x68D: 82,
+ 0x68E: 82,
+ 0x68F: 82,
+ 0x690: 82,
+ 0x691: 82,
+ 0x692: 82,
+ 0x693: 82,
+ 0x694: 82,
+ 0x695: 82,
+ 0x696: 82,
+ 0x697: 82,
+ 0x698: 82,
+ 0x699: 82,
+ 0x69A: 68,
+ 0x69B: 68,
+ 0x69C: 68,
+ 0x69D: 68,
+ 0x69E: 68,
+ 0x69F: 68,
+ 0x6A0: 68,
+ 0x6A1: 68,
+ 0x6A2: 68,
+ 0x6A3: 68,
+ 0x6A4: 68,
+ 0x6A5: 68,
+ 0x6A6: 68,
+ 0x6A7: 68,
+ 0x6A8: 68,
+ 0x6A9: 68,
+ 0x6AA: 68,
+ 0x6AB: 68,
+ 0x6AC: 68,
+ 0x6AD: 68,
+ 0x6AE: 68,
+ 0x6AF: 68,
+ 0x6B0: 68,
+ 0x6B1: 68,
+ 0x6B2: 68,
+ 0x6B3: 68,
+ 0x6B4: 68,
+ 0x6B5: 68,
+ 0x6B6: 68,
+ 0x6B7: 68,
+ 0x6B8: 68,
+ 0x6B9: 68,
+ 0x6BA: 68,
+ 0x6BB: 68,
+ 0x6BC: 68,
+ 0x6BD: 68,
+ 0x6BE: 68,
+ 0x6BF: 68,
+ 0x6C0: 82,
+ 0x6C1: 68,
+ 0x6C2: 68,
+ 0x6C3: 82,
+ 0x6C4: 82,
+ 0x6C5: 82,
+ 0x6C6: 82,
+ 0x6C7: 82,
+ 0x6C8: 82,
+ 0x6C9: 82,
+ 0x6CA: 82,
+ 0x6CB: 82,
+ 0x6CC: 68,
+ 0x6CD: 82,
+ 0x6CE: 68,
+ 0x6CF: 82,
+ 0x6D0: 68,
+ 0x6D1: 68,
+ 0x6D2: 82,
+ 0x6D3: 82,
+ 0x6D5: 82,
+ 0x6D6: 84,
+ 0x6D7: 84,
+ 0x6D8: 84,
+ 0x6D9: 84,
+ 0x6DA: 84,
+ 0x6DB: 84,
+ 0x6DC: 84,
+ 0x6DF: 84,
+ 0x6E0: 84,
+ 0x6E1: 84,
+ 0x6E2: 84,
+ 0x6E3: 84,
+ 0x6E4: 84,
+ 0x6E7: 84,
+ 0x6E8: 84,
+ 0x6EA: 84,
+ 0x6EB: 84,
+ 0x6EC: 84,
+ 0x6ED: 84,
+ 0x6EE: 82,
+ 0x6EF: 82,
+ 0x6FA: 68,
+ 0x6FB: 68,
+ 0x6FC: 68,
+ 0x6FF: 68,
+ 0x70F: 84,
+ 0x710: 82,
+ 0x711: 84,
+ 0x712: 68,
+ 0x713: 68,
+ 0x714: 68,
+ 0x715: 82,
+ 0x716: 82,
+ 0x717: 82,
+ 0x718: 82,
+ 0x719: 82,
+ 0x71A: 68,
+ 0x71B: 68,
+ 0x71C: 68,
+ 0x71D: 68,
+ 0x71E: 82,
+ 0x71F: 68,
+ 0x720: 68,
+ 0x721: 68,
+ 0x722: 68,
+ 0x723: 68,
+ 0x724: 68,
+ 0x725: 68,
+ 0x726: 68,
+ 0x727: 68,
+ 0x728: 82,
+ 0x729: 68,
+ 0x72A: 82,
+ 0x72B: 68,
+ 0x72C: 82,
+ 0x72D: 68,
+ 0x72E: 68,
+ 0x72F: 82,
+ 0x730: 84,
+ 0x731: 84,
+ 0x732: 84,
+ 0x733: 84,
+ 0x734: 84,
+ 0x735: 84,
+ 0x736: 84,
+ 0x737: 84,
+ 0x738: 84,
+ 0x739: 84,
+ 0x73A: 84,
+ 0x73B: 84,
+ 0x73C: 84,
+ 0x73D: 84,
+ 0x73E: 84,
+ 0x73F: 84,
+ 0x740: 84,
+ 0x741: 84,
+ 0x742: 84,
+ 0x743: 84,
+ 0x744: 84,
+ 0x745: 84,
+ 0x746: 84,
+ 0x747: 84,
+ 0x748: 84,
+ 0x749: 84,
+ 0x74A: 84,
+ 0x74D: 82,
+ 0x74E: 68,
+ 0x74F: 68,
+ 0x750: 68,
+ 0x751: 68,
+ 0x752: 68,
+ 0x753: 68,
+ 0x754: 68,
+ 0x755: 68,
+ 0x756: 68,
+ 0x757: 68,
+ 0x758: 68,
+ 0x759: 82,
+ 0x75A: 82,
+ 0x75B: 82,
+ 0x75C: 68,
+ 0x75D: 68,
+ 0x75E: 68,
+ 0x75F: 68,
+ 0x760: 68,
+ 0x761: 68,
+ 0x762: 68,
+ 0x763: 68,
+ 0x764: 68,
+ 0x765: 68,
+ 0x766: 68,
+ 0x767: 68,
+ 0x768: 68,
+ 0x769: 68,
+ 0x76A: 68,
+ 0x76B: 82,
+ 0x76C: 82,
+ 0x76D: 68,
+ 0x76E: 68,
+ 0x76F: 68,
+ 0x770: 68,
+ 0x771: 82,
+ 0x772: 68,
+ 0x773: 82,
+ 0x774: 82,
+ 0x775: 68,
+ 0x776: 68,
+ 0x777: 68,
+ 0x778: 82,
+ 0x779: 82,
+ 0x77A: 68,
+ 0x77B: 68,
+ 0x77C: 68,
+ 0x77D: 68,
+ 0x77E: 68,
+ 0x77F: 68,
+ 0x7A6: 84,
+ 0x7A7: 84,
+ 0x7A8: 84,
+ 0x7A9: 84,
+ 0x7AA: 84,
+ 0x7AB: 84,
+ 0x7AC: 84,
+ 0x7AD: 84,
+ 0x7AE: 84,
+ 0x7AF: 84,
+ 0x7B0: 84,
+ 0x7CA: 68,
+ 0x7CB: 68,
+ 0x7CC: 68,
+ 0x7CD: 68,
+ 0x7CE: 68,
+ 0x7CF: 68,
+ 0x7D0: 68,
+ 0x7D1: 68,
+ 0x7D2: 68,
+ 0x7D3: 68,
+ 0x7D4: 68,
+ 0x7D5: 68,
+ 0x7D6: 68,
+ 0x7D7: 68,
+ 0x7D8: 68,
+ 0x7D9: 68,
+ 0x7DA: 68,
+ 0x7DB: 68,
+ 0x7DC: 68,
+ 0x7DD: 68,
+ 0x7DE: 68,
+ 0x7DF: 68,
+ 0x7E0: 68,
+ 0x7E1: 68,
+ 0x7E2: 68,
+ 0x7E3: 68,
+ 0x7E4: 68,
+ 0x7E5: 68,
+ 0x7E6: 68,
+ 0x7E7: 68,
+ 0x7E8: 68,
+ 0x7E9: 68,
+ 0x7EA: 68,
+ 0x7EB: 84,
+ 0x7EC: 84,
+ 0x7ED: 84,
+ 0x7EE: 84,
+ 0x7EF: 84,
+ 0x7F0: 84,
+ 0x7F1: 84,
+ 0x7F2: 84,
+ 0x7F3: 84,
+ 0x7FA: 67,
+ 0x7FD: 84,
+ 0x816: 84,
+ 0x817: 84,
+ 0x818: 84,
+ 0x819: 84,
+ 0x81B: 84,
+ 0x81C: 84,
+ 0x81D: 84,
+ 0x81E: 84,
+ 0x81F: 84,
+ 0x820: 84,
+ 0x821: 84,
+ 0x822: 84,
+ 0x823: 84,
+ 0x825: 84,
+ 0x826: 84,
+ 0x827: 84,
+ 0x829: 84,
+ 0x82A: 84,
+ 0x82B: 84,
+ 0x82C: 84,
+ 0x82D: 84,
+ 0x840: 82,
+ 0x841: 68,
+ 0x842: 68,
+ 0x843: 68,
+ 0x844: 68,
+ 0x845: 68,
+ 0x846: 82,
+ 0x847: 82,
+ 0x848: 68,
+ 0x849: 82,
+ 0x84A: 68,
+ 0x84B: 68,
+ 0x84C: 68,
+ 0x84D: 68,
+ 0x84E: 68,
+ 0x84F: 68,
+ 0x850: 68,
+ 0x851: 68,
+ 0x852: 68,
+ 0x853: 68,
+ 0x854: 82,
+ 0x855: 68,
+ 0x856: 82,
+ 0x857: 82,
+ 0x858: 82,
+ 0x859: 84,
+ 0x85A: 84,
+ 0x85B: 84,
+ 0x860: 68,
+ 0x862: 68,
+ 0x863: 68,
+ 0x864: 68,
+ 0x865: 68,
+ 0x867: 82,
+ 0x868: 68,
+ 0x869: 82,
+ 0x86A: 82,
+ 0x870: 82,
+ 0x871: 82,
+ 0x872: 82,
+ 0x873: 82,
+ 0x874: 82,
+ 0x875: 82,
+ 0x876: 82,
+ 0x877: 82,
+ 0x878: 82,
+ 0x879: 82,
+ 0x87A: 82,
+ 0x87B: 82,
+ 0x87C: 82,
+ 0x87D: 82,
+ 0x87E: 82,
+ 0x87F: 82,
+ 0x880: 82,
+ 0x881: 82,
+ 0x882: 82,
+ 0x883: 67,
+ 0x884: 67,
+ 0x885: 67,
+ 0x886: 68,
+ 0x889: 68,
+ 0x88A: 68,
+ 0x88B: 68,
+ 0x88C: 68,
+ 0x88D: 68,
+ 0x88E: 82,
+ 0x897: 84,
+ 0x898: 84,
+ 0x899: 84,
+ 0x89A: 84,
+ 0x89B: 84,
+ 0x89C: 84,
+ 0x89D: 84,
+ 0x89E: 84,
+ 0x89F: 84,
+ 0x8A0: 68,
+ 0x8A1: 68,
+ 0x8A2: 68,
+ 0x8A3: 68,
+ 0x8A4: 68,
+ 0x8A5: 68,
+ 0x8A6: 68,
+ 0x8A7: 68,
+ 0x8A8: 68,
+ 0x8A9: 68,
+ 0x8AA: 82,
+ 0x8AB: 82,
+ 0x8AC: 82,
+ 0x8AE: 82,
+ 0x8AF: 68,
+ 0x8B0: 68,
+ 0x8B1: 82,
+ 0x8B2: 82,
+ 0x8B3: 68,
+ 0x8B4: 68,
+ 0x8B5: 68,
+ 0x8B6: 68,
+ 0x8B7: 68,
+ 0x8B8: 68,
+ 0x8B9: 82,
+ 0x8BA: 68,
+ 0x8BB: 68,
+ 0x8BC: 68,
+ 0x8BD: 68,
+ 0x8BE: 68,
+ 0x8BF: 68,
+ 0x8C0: 68,
+ 0x8C1: 68,
+ 0x8C2: 68,
+ 0x8C3: 68,
+ 0x8C4: 68,
+ 0x8C5: 68,
+ 0x8C6: 68,
+ 0x8C7: 68,
+ 0x8C8: 68,
+ 0x8CA: 84,
+ 0x8CB: 84,
+ 0x8CC: 84,
+ 0x8CD: 84,
+ 0x8CE: 84,
+ 0x8CF: 84,
+ 0x8D0: 84,
+ 0x8D1: 84,
+ 0x8D2: 84,
+ 0x8D3: 84,
+ 0x8D4: 84,
+ 0x8D5: 84,
+ 0x8D6: 84,
+ 0x8D7: 84,
+ 0x8D8: 84,
+ 0x8D9: 84,
+ 0x8DA: 84,
+ 0x8DB: 84,
+ 0x8DC: 84,
+ 0x8DD: 84,
+ 0x8DE: 84,
+ 0x8DF: 84,
+ 0x8E0: 84,
+ 0x8E1: 84,
+ 0x8E3: 84,
+ 0x8E4: 84,
+ 0x8E5: 84,
+ 0x8E6: 84,
+ 0x8E7: 84,
+ 0x8E8: 84,
+ 0x8E9: 84,
+ 0x8EA: 84,
+ 0x8EB: 84,
+ 0x8EC: 84,
+ 0x8ED: 84,
+ 0x8EE: 84,
+ 0x8EF: 84,
+ 0x8F0: 84,
+ 0x8F1: 84,
+ 0x8F2: 84,
+ 0x8F3: 84,
+ 0x8F4: 84,
+ 0x8F5: 84,
+ 0x8F6: 84,
+ 0x8F7: 84,
+ 0x8F8: 84,
+ 0x8F9: 84,
+ 0x8FA: 84,
+ 0x8FB: 84,
+ 0x8FC: 84,
+ 0x8FD: 84,
+ 0x8FE: 84,
+ 0x8FF: 84,
+ 0x900: 84,
+ 0x901: 84,
+ 0x902: 84,
+ 0x93A: 84,
+ 0x93C: 84,
+ 0x941: 84,
+ 0x942: 84,
+ 0x943: 84,
+ 0x944: 84,
+ 0x945: 84,
+ 0x946: 84,
+ 0x947: 84,
+ 0x948: 84,
+ 0x94D: 84,
+ 0x951: 84,
+ 0x952: 84,
+ 0x953: 84,
+ 0x954: 84,
+ 0x955: 84,
+ 0x956: 84,
+ 0x957: 84,
+ 0x962: 84,
+ 0x963: 84,
+ 0x981: 84,
+ 0x9BC: 84,
+ 0x9C1: 84,
+ 0x9C2: 84,
+ 0x9C3: 84,
+ 0x9C4: 84,
+ 0x9CD: 84,
+ 0x9E2: 84,
+ 0x9E3: 84,
+ 0x9FE: 84,
+ 0xA01: 84,
+ 0xA02: 84,
+ 0xA3C: 84,
+ 0xA41: 84,
+ 0xA42: 84,
+ 0xA47: 84,
+ 0xA48: 84,
+ 0xA4B: 84,
+ 0xA4C: 84,
+ 0xA4D: 84,
+ 0xA51: 84,
+ 0xA70: 84,
+ 0xA71: 84,
+ 0xA75: 84,
+ 0xA81: 84,
+ 0xA82: 84,
+ 0xABC: 84,
+ 0xAC1: 84,
+ 0xAC2: 84,
+ 0xAC3: 84,
+ 0xAC4: 84,
+ 0xAC5: 84,
+ 0xAC7: 84,
+ 0xAC8: 84,
+ 0xACD: 84,
+ 0xAE2: 84,
+ 0xAE3: 84,
+ 0xAFA: 84,
+ 0xAFB: 84,
+ 0xAFC: 84,
+ 0xAFD: 84,
+ 0xAFE: 84,
+ 0xAFF: 84,
+ 0xB01: 84,
+ 0xB3C: 84,
+ 0xB3F: 84,
+ 0xB41: 84,
+ 0xB42: 84,
+ 0xB43: 84,
+ 0xB44: 84,
+ 0xB4D: 84,
+ 0xB55: 84,
+ 0xB56: 84,
+ 0xB62: 84,
+ 0xB63: 84,
+ 0xB82: 84,
+ 0xBC0: 84,
+ 0xBCD: 84,
+ 0xC00: 84,
+ 0xC04: 84,
+ 0xC3C: 84,
+ 0xC3E: 84,
+ 0xC3F: 84,
+ 0xC40: 84,
+ 0xC46: 84,
+ 0xC47: 84,
+ 0xC48: 84,
+ 0xC4A: 84,
+ 0xC4B: 84,
+ 0xC4C: 84,
+ 0xC4D: 84,
+ 0xC55: 84,
+ 0xC56: 84,
+ 0xC62: 84,
+ 0xC63: 84,
+ 0xC81: 84,
+ 0xCBC: 84,
+ 0xCBF: 84,
+ 0xCC6: 84,
+ 0xCCC: 84,
+ 0xCCD: 84,
+ 0xCE2: 84,
+ 0xCE3: 84,
+ 0xD00: 84,
+ 0xD01: 84,
+ 0xD3B: 84,
+ 0xD3C: 84,
+ 0xD41: 84,
+ 0xD42: 84,
+ 0xD43: 84,
+ 0xD44: 84,
+ 0xD4D: 84,
+ 0xD62: 84,
+ 0xD63: 84,
+ 0xD81: 84,
+ 0xDCA: 84,
+ 0xDD2: 84,
+ 0xDD3: 84,
+ 0xDD4: 84,
+ 0xDD6: 84,
+ 0xE31: 84,
+ 0xE34: 84,
+ 0xE35: 84,
+ 0xE36: 84,
+ 0xE37: 84,
+ 0xE38: 84,
+ 0xE39: 84,
+ 0xE3A: 84,
+ 0xE47: 84,
+ 0xE48: 84,
+ 0xE49: 84,
+ 0xE4A: 84,
+ 0xE4B: 84,
+ 0xE4C: 84,
+ 0xE4D: 84,
+ 0xE4E: 84,
+ 0xEB1: 84,
+ 0xEB4: 84,
+ 0xEB5: 84,
+ 0xEB6: 84,
+ 0xEB7: 84,
+ 0xEB8: 84,
+ 0xEB9: 84,
+ 0xEBA: 84,
+ 0xEBB: 84,
+ 0xEBC: 84,
+ 0xEC8: 84,
+ 0xEC9: 84,
+ 0xECA: 84,
+ 0xECB: 84,
+ 0xECC: 84,
+ 0xECD: 84,
+ 0xECE: 84,
+ 0xF18: 84,
+ 0xF19: 84,
+ 0xF35: 84,
+ 0xF37: 84,
+ 0xF39: 84,
+ 0xF71: 84,
+ 0xF72: 84,
+ 0xF73: 84,
+ 0xF74: 84,
+ 0xF75: 84,
+ 0xF76: 84,
+ 0xF77: 84,
+ 0xF78: 84,
+ 0xF79: 84,
+ 0xF7A: 84,
+ 0xF7B: 84,
+ 0xF7C: 84,
+ 0xF7D: 84,
+ 0xF7E: 84,
+ 0xF80: 84,
+ 0xF81: 84,
+ 0xF82: 84,
+ 0xF83: 84,
+ 0xF84: 84,
+ 0xF86: 84,
+ 0xF87: 84,
+ 0xF8D: 84,
+ 0xF8E: 84,
+ 0xF8F: 84,
+ 0xF90: 84,
+ 0xF91: 84,
+ 0xF92: 84,
+ 0xF93: 84,
+ 0xF94: 84,
+ 0xF95: 84,
+ 0xF96: 84,
+ 0xF97: 84,
+ 0xF99: 84,
+ 0xF9A: 84,
+ 0xF9B: 84,
+ 0xF9C: 84,
+ 0xF9D: 84,
+ 0xF9E: 84,
+ 0xF9F: 84,
+ 0xFA0: 84,
+ 0xFA1: 84,
+ 0xFA2: 84,
+ 0xFA3: 84,
+ 0xFA4: 84,
+ 0xFA5: 84,
+ 0xFA6: 84,
+ 0xFA7: 84,
+ 0xFA8: 84,
+ 0xFA9: 84,
+ 0xFAA: 84,
+ 0xFAB: 84,
+ 0xFAC: 84,
+ 0xFAD: 84,
+ 0xFAE: 84,
+ 0xFAF: 84,
+ 0xFB0: 84,
+ 0xFB1: 84,
+ 0xFB2: 84,
+ 0xFB3: 84,
+ 0xFB4: 84,
+ 0xFB5: 84,
+ 0xFB6: 84,
+ 0xFB7: 84,
+ 0xFB8: 84,
+ 0xFB9: 84,
+ 0xFBA: 84,
+ 0xFBB: 84,
+ 0xFBC: 84,
+ 0xFC6: 84,
+ 0x102D: 84,
+ 0x102E: 84,
+ 0x102F: 84,
+ 0x1030: 84,
+ 0x1032: 84,
+ 0x1033: 84,
+ 0x1034: 84,
+ 0x1035: 84,
+ 0x1036: 84,
+ 0x1037: 84,
+ 0x1039: 84,
+ 0x103A: 84,
+ 0x103D: 84,
+ 0x103E: 84,
+ 0x1058: 84,
+ 0x1059: 84,
+ 0x105E: 84,
+ 0x105F: 84,
+ 0x1060: 84,
+ 0x1071: 84,
+ 0x1072: 84,
+ 0x1073: 84,
+ 0x1074: 84,
+ 0x1082: 84,
+ 0x1085: 84,
+ 0x1086: 84,
+ 0x108D: 84,
+ 0x109D: 84,
+ 0x135D: 84,
+ 0x135E: 84,
+ 0x135F: 84,
+ 0x1712: 84,
+ 0x1713: 84,
+ 0x1714: 84,
+ 0x1732: 84,
+ 0x1733: 84,
+ 0x1752: 84,
+ 0x1753: 84,
+ 0x1772: 84,
+ 0x1773: 84,
+ 0x17B4: 84,
+ 0x17B5: 84,
+ 0x17B7: 84,
+ 0x17B8: 84,
+ 0x17B9: 84,
+ 0x17BA: 84,
+ 0x17BB: 84,
+ 0x17BC: 84,
+ 0x17BD: 84,
+ 0x17C6: 84,
+ 0x17C9: 84,
+ 0x17CA: 84,
+ 0x17CB: 84,
+ 0x17CC: 84,
+ 0x17CD: 84,
+ 0x17CE: 84,
+ 0x17CF: 84,
+ 0x17D0: 84,
+ 0x17D1: 84,
+ 0x17D2: 84,
+ 0x17D3: 84,
+ 0x17DD: 84,
+ 0x1807: 68,
+ 0x180A: 67,
+ 0x180B: 84,
+ 0x180C: 84,
+ 0x180D: 84,
+ 0x180F: 84,
+ 0x1820: 68,
+ 0x1821: 68,
+ 0x1822: 68,
+ 0x1823: 68,
+ 0x1824: 68,
+ 0x1825: 68,
+ 0x1826: 68,
+ 0x1827: 68,
+ 0x1828: 68,
+ 0x1829: 68,
+ 0x182A: 68,
+ 0x182B: 68,
+ 0x182C: 68,
+ 0x182D: 68,
+ 0x182E: 68,
+ 0x182F: 68,
+ 0x1830: 68,
+ 0x1831: 68,
+ 0x1832: 68,
+ 0x1833: 68,
+ 0x1834: 68,
+ 0x1835: 68,
+ 0x1836: 68,
+ 0x1837: 68,
+ 0x1838: 68,
+ 0x1839: 68,
+ 0x183A: 68,
+ 0x183B: 68,
+ 0x183C: 68,
+ 0x183D: 68,
+ 0x183E: 68,
+ 0x183F: 68,
+ 0x1840: 68,
+ 0x1841: 68,
+ 0x1842: 68,
+ 0x1843: 68,
+ 0x1844: 68,
+ 0x1845: 68,
+ 0x1846: 68,
+ 0x1847: 68,
+ 0x1848: 68,
+ 0x1849: 68,
+ 0x184A: 68,
+ 0x184B: 68,
+ 0x184C: 68,
+ 0x184D: 68,
+ 0x184E: 68,
+ 0x184F: 68,
+ 0x1850: 68,
+ 0x1851: 68,
+ 0x1852: 68,
+ 0x1853: 68,
+ 0x1854: 68,
+ 0x1855: 68,
+ 0x1856: 68,
+ 0x1857: 68,
+ 0x1858: 68,
+ 0x1859: 68,
+ 0x185A: 68,
+ 0x185B: 68,
+ 0x185C: 68,
+ 0x185D: 68,
+ 0x185E: 68,
+ 0x185F: 68,
+ 0x1860: 68,
+ 0x1861: 68,
+ 0x1862: 68,
+ 0x1863: 68,
+ 0x1864: 68,
+ 0x1865: 68,
+ 0x1866: 68,
+ 0x1867: 68,
+ 0x1868: 68,
+ 0x1869: 68,
+ 0x186A: 68,
+ 0x186B: 68,
+ 0x186C: 68,
+ 0x186D: 68,
+ 0x186E: 68,
+ 0x186F: 68,
+ 0x1870: 68,
+ 0x1871: 68,
+ 0x1872: 68,
+ 0x1873: 68,
+ 0x1874: 68,
+ 0x1875: 68,
+ 0x1876: 68,
+ 0x1877: 68,
+ 0x1878: 68,
+ 0x1885: 84,
+ 0x1886: 84,
+ 0x1887: 68,
+ 0x1888: 68,
+ 0x1889: 68,
+ 0x188A: 68,
+ 0x188B: 68,
+ 0x188C: 68,
+ 0x188D: 68,
+ 0x188E: 68,
+ 0x188F: 68,
+ 0x1890: 68,
+ 0x1891: 68,
+ 0x1892: 68,
+ 0x1893: 68,
+ 0x1894: 68,
+ 0x1895: 68,
+ 0x1896: 68,
+ 0x1897: 68,
+ 0x1898: 68,
+ 0x1899: 68,
+ 0x189A: 68,
+ 0x189B: 68,
+ 0x189C: 68,
+ 0x189D: 68,
+ 0x189E: 68,
+ 0x189F: 68,
+ 0x18A0: 68,
+ 0x18A1: 68,
+ 0x18A2: 68,
+ 0x18A3: 68,
+ 0x18A4: 68,
+ 0x18A5: 68,
+ 0x18A6: 68,
+ 0x18A7: 68,
+ 0x18A8: 68,
+ 0x18A9: 84,
+ 0x18AA: 68,
+ 0x1920: 84,
+ 0x1921: 84,
+ 0x1922: 84,
+ 0x1927: 84,
+ 0x1928: 84,
+ 0x1932: 84,
+ 0x1939: 84,
+ 0x193A: 84,
+ 0x193B: 84,
+ 0x1A17: 84,
+ 0x1A18: 84,
+ 0x1A1B: 84,
+ 0x1A56: 84,
+ 0x1A58: 84,
+ 0x1A59: 84,
+ 0x1A5A: 84,
+ 0x1A5B: 84,
+ 0x1A5C: 84,
+ 0x1A5D: 84,
+ 0x1A5E: 84,
+ 0x1A60: 84,
+ 0x1A62: 84,
+ 0x1A65: 84,
+ 0x1A66: 84,
+ 0x1A67: 84,
+ 0x1A68: 84,
+ 0x1A69: 84,
+ 0x1A6A: 84,
+ 0x1A6B: 84,
+ 0x1A6C: 84,
+ 0x1A73: 84,
+ 0x1A74: 84,
+ 0x1A75: 84,
+ 0x1A76: 84,
+ 0x1A77: 84,
+ 0x1A78: 84,
+ 0x1A79: 84,
+ 0x1A7A: 84,
+ 0x1A7B: 84,
+ 0x1A7C: 84,
+ 0x1A7F: 84,
+ 0x1AB0: 84,
+ 0x1AB1: 84,
+ 0x1AB2: 84,
+ 0x1AB3: 84,
+ 0x1AB4: 84,
+ 0x1AB5: 84,
+ 0x1AB6: 84,
+ 0x1AB7: 84,
+ 0x1AB8: 84,
+ 0x1AB9: 84,
+ 0x1ABA: 84,
+ 0x1ABB: 84,
+ 0x1ABC: 84,
+ 0x1ABD: 84,
+ 0x1ABE: 84,
+ 0x1ABF: 84,
+ 0x1AC0: 84,
+ 0x1AC1: 84,
+ 0x1AC2: 84,
+ 0x1AC3: 84,
+ 0x1AC4: 84,
+ 0x1AC5: 84,
+ 0x1AC6: 84,
+ 0x1AC7: 84,
+ 0x1AC8: 84,
+ 0x1AC9: 84,
+ 0x1ACA: 84,
+ 0x1ACB: 84,
+ 0x1ACC: 84,
+ 0x1ACD: 84,
+ 0x1ACE: 84,
+ 0x1B00: 84,
+ 0x1B01: 84,
+ 0x1B02: 84,
+ 0x1B03: 84,
+ 0x1B34: 84,
+ 0x1B36: 84,
+ 0x1B37: 84,
+ 0x1B38: 84,
+ 0x1B39: 84,
+ 0x1B3A: 84,
+ 0x1B3C: 84,
+ 0x1B42: 84,
+ 0x1B6B: 84,
+ 0x1B6C: 84,
+ 0x1B6D: 84,
+ 0x1B6E: 84,
+ 0x1B6F: 84,
+ 0x1B70: 84,
+ 0x1B71: 84,
+ 0x1B72: 84,
+ 0x1B73: 84,
+ 0x1B80: 84,
+ 0x1B81: 84,
+ 0x1BA2: 84,
+ 0x1BA3: 84,
+ 0x1BA4: 84,
+ 0x1BA5: 84,
+ 0x1BA8: 84,
+ 0x1BA9: 84,
+ 0x1BAB: 84,
+ 0x1BAC: 84,
+ 0x1BAD: 84,
+ 0x1BE6: 84,
+ 0x1BE8: 84,
+ 0x1BE9: 84,
+ 0x1BED: 84,
+ 0x1BEF: 84,
+ 0x1BF0: 84,
+ 0x1BF1: 84,
+ 0x1C2C: 84,
+ 0x1C2D: 84,
+ 0x1C2E: 84,
+ 0x1C2F: 84,
+ 0x1C30: 84,
+ 0x1C31: 84,
+ 0x1C32: 84,
+ 0x1C33: 84,
+ 0x1C36: 84,
+ 0x1C37: 84,
+ 0x1CD0: 84,
+ 0x1CD1: 84,
+ 0x1CD2: 84,
+ 0x1CD4: 84,
+ 0x1CD5: 84,
+ 0x1CD6: 84,
+ 0x1CD7: 84,
+ 0x1CD8: 84,
+ 0x1CD9: 84,
+ 0x1CDA: 84,
+ 0x1CDB: 84,
+ 0x1CDC: 84,
+ 0x1CDD: 84,
+ 0x1CDE: 84,
+ 0x1CDF: 84,
+ 0x1CE0: 84,
+ 0x1CE2: 84,
+ 0x1CE3: 84,
+ 0x1CE4: 84,
+ 0x1CE5: 84,
+ 0x1CE6: 84,
+ 0x1CE7: 84,
+ 0x1CE8: 84,
+ 0x1CED: 84,
+ 0x1CF4: 84,
+ 0x1CF8: 84,
+ 0x1CF9: 84,
+ 0x1DC0: 84,
+ 0x1DC1: 84,
+ 0x1DC2: 84,
+ 0x1DC3: 84,
+ 0x1DC4: 84,
+ 0x1DC5: 84,
+ 0x1DC6: 84,
+ 0x1DC7: 84,
+ 0x1DC8: 84,
+ 0x1DC9: 84,
+ 0x1DCA: 84,
+ 0x1DCB: 84,
+ 0x1DCC: 84,
+ 0x1DCD: 84,
+ 0x1DCE: 84,
+ 0x1DCF: 84,
+ 0x1DD0: 84,
+ 0x1DD1: 84,
+ 0x1DD2: 84,
+ 0x1DD3: 84,
+ 0x1DD4: 84,
+ 0x1DD5: 84,
+ 0x1DD6: 84,
+ 0x1DD7: 84,
+ 0x1DD8: 84,
+ 0x1DD9: 84,
+ 0x1DDA: 84,
+ 0x1DDB: 84,
+ 0x1DDC: 84,
+ 0x1DDD: 84,
+ 0x1DDE: 84,
+ 0x1DDF: 84,
+ 0x1DE0: 84,
+ 0x1DE1: 84,
+ 0x1DE2: 84,
+ 0x1DE3: 84,
+ 0x1DE4: 84,
+ 0x1DE5: 84,
+ 0x1DE6: 84,
+ 0x1DE7: 84,
+ 0x1DE8: 84,
+ 0x1DE9: 84,
+ 0x1DEA: 84,
+ 0x1DEB: 84,
+ 0x1DEC: 84,
+ 0x1DED: 84,
+ 0x1DEE: 84,
+ 0x1DEF: 84,
+ 0x1DF0: 84,
+ 0x1DF1: 84,
+ 0x1DF2: 84,
+ 0x1DF3: 84,
+ 0x1DF4: 84,
+ 0x1DF5: 84,
+ 0x1DF6: 84,
+ 0x1DF7: 84,
+ 0x1DF8: 84,
+ 0x1DF9: 84,
+ 0x1DFA: 84,
+ 0x1DFB: 84,
+ 0x1DFC: 84,
+ 0x1DFD: 84,
+ 0x1DFE: 84,
+ 0x1DFF: 84,
+ 0x200B: 84,
+ 0x200D: 67,
+ 0x200E: 84,
+ 0x200F: 84,
+ 0x202A: 84,
+ 0x202B: 84,
+ 0x202C: 84,
+ 0x202D: 84,
+ 0x202E: 84,
+ 0x2060: 84,
+ 0x2061: 84,
+ 0x2062: 84,
+ 0x2063: 84,
+ 0x2064: 84,
+ 0x206A: 84,
+ 0x206B: 84,
+ 0x206C: 84,
+ 0x206D: 84,
+ 0x206E: 84,
+ 0x206F: 84,
+ 0x20D0: 84,
+ 0x20D1: 84,
+ 0x20D2: 84,
+ 0x20D3: 84,
+ 0x20D4: 84,
+ 0x20D5: 84,
+ 0x20D6: 84,
+ 0x20D7: 84,
+ 0x20D8: 84,
+ 0x20D9: 84,
+ 0x20DA: 84,
+ 0x20DB: 84,
+ 0x20DC: 84,
+ 0x20DD: 84,
+ 0x20DE: 84,
+ 0x20DF: 84,
+ 0x20E0: 84,
+ 0x20E1: 84,
+ 0x20E2: 84,
+ 0x20E3: 84,
+ 0x20E4: 84,
+ 0x20E5: 84,
+ 0x20E6: 84,
+ 0x20E7: 84,
+ 0x20E8: 84,
+ 0x20E9: 84,
+ 0x20EA: 84,
+ 0x20EB: 84,
+ 0x20EC: 84,
+ 0x20ED: 84,
+ 0x20EE: 84,
+ 0x20EF: 84,
+ 0x20F0: 84,
+ 0x2CEF: 84,
+ 0x2CF0: 84,
+ 0x2CF1: 84,
+ 0x2D7F: 84,
+ 0x2DE0: 84,
+ 0x2DE1: 84,
+ 0x2DE2: 84,
+ 0x2DE3: 84,
+ 0x2DE4: 84,
+ 0x2DE5: 84,
+ 0x2DE6: 84,
+ 0x2DE7: 84,
+ 0x2DE8: 84,
+ 0x2DE9: 84,
+ 0x2DEA: 84,
+ 0x2DEB: 84,
+ 0x2DEC: 84,
+ 0x2DED: 84,
+ 0x2DEE: 84,
+ 0x2DEF: 84,
+ 0x2DF0: 84,
+ 0x2DF1: 84,
+ 0x2DF2: 84,
+ 0x2DF3: 84,
+ 0x2DF4: 84,
+ 0x2DF5: 84,
+ 0x2DF6: 84,
+ 0x2DF7: 84,
+ 0x2DF8: 84,
+ 0x2DF9: 84,
+ 0x2DFA: 84,
+ 0x2DFB: 84,
+ 0x2DFC: 84,
+ 0x2DFD: 84,
+ 0x2DFE: 84,
+ 0x2DFF: 84,
+ 0x302A: 84,
+ 0x302B: 84,
+ 0x302C: 84,
+ 0x302D: 84,
+ 0x3099: 84,
+ 0x309A: 84,
+ 0xA66F: 84,
+ 0xA670: 84,
+ 0xA671: 84,
+ 0xA672: 84,
+ 0xA674: 84,
+ 0xA675: 84,
+ 0xA676: 84,
+ 0xA677: 84,
+ 0xA678: 84,
+ 0xA679: 84,
+ 0xA67A: 84,
+ 0xA67B: 84,
+ 0xA67C: 84,
+ 0xA67D: 84,
+ 0xA69E: 84,
+ 0xA69F: 84,
+ 0xA6F0: 84,
+ 0xA6F1: 84,
+ 0xA802: 84,
+ 0xA806: 84,
+ 0xA80B: 84,
+ 0xA825: 84,
+ 0xA826: 84,
+ 0xA82C: 84,
+ 0xA840: 68,
+ 0xA841: 68,
+ 0xA842: 68,
+ 0xA843: 68,
+ 0xA844: 68,
+ 0xA845: 68,
+ 0xA846: 68,
+ 0xA847: 68,
+ 0xA848: 68,
+ 0xA849: 68,
+ 0xA84A: 68,
+ 0xA84B: 68,
+ 0xA84C: 68,
+ 0xA84D: 68,
+ 0xA84E: 68,
+ 0xA84F: 68,
+ 0xA850: 68,
+ 0xA851: 68,
+ 0xA852: 68,
+ 0xA853: 68,
+ 0xA854: 68,
+ 0xA855: 68,
+ 0xA856: 68,
+ 0xA857: 68,
+ 0xA858: 68,
+ 0xA859: 68,
+ 0xA85A: 68,
+ 0xA85B: 68,
+ 0xA85C: 68,
+ 0xA85D: 68,
+ 0xA85E: 68,
+ 0xA85F: 68,
+ 0xA860: 68,
+ 0xA861: 68,
+ 0xA862: 68,
+ 0xA863: 68,
+ 0xA864: 68,
+ 0xA865: 68,
+ 0xA866: 68,
+ 0xA867: 68,
+ 0xA868: 68,
+ 0xA869: 68,
+ 0xA86A: 68,
+ 0xA86B: 68,
+ 0xA86C: 68,
+ 0xA86D: 68,
+ 0xA86E: 68,
+ 0xA86F: 68,
+ 0xA870: 68,
+ 0xA871: 68,
+ 0xA872: 76,
+ 0xA8C4: 84,
+ 0xA8C5: 84,
+ 0xA8E0: 84,
+ 0xA8E1: 84,
+ 0xA8E2: 84,
+ 0xA8E3: 84,
+ 0xA8E4: 84,
+ 0xA8E5: 84,
+ 0xA8E6: 84,
+ 0xA8E7: 84,
+ 0xA8E8: 84,
+ 0xA8E9: 84,
+ 0xA8EA: 84,
+ 0xA8EB: 84,
+ 0xA8EC: 84,
+ 0xA8ED: 84,
+ 0xA8EE: 84,
+ 0xA8EF: 84,
+ 0xA8F0: 84,
+ 0xA8F1: 84,
+ 0xA8FF: 84,
+ 0xA926: 84,
+ 0xA927: 84,
+ 0xA928: 84,
+ 0xA929: 84,
+ 0xA92A: 84,
+ 0xA92B: 84,
+ 0xA92C: 84,
+ 0xA92D: 84,
+ 0xA947: 84,
+ 0xA948: 84,
+ 0xA949: 84,
+ 0xA94A: 84,
+ 0xA94B: 84,
+ 0xA94C: 84,
+ 0xA94D: 84,
+ 0xA94E: 84,
+ 0xA94F: 84,
+ 0xA950: 84,
+ 0xA951: 84,
+ 0xA980: 84,
+ 0xA981: 84,
+ 0xA982: 84,
+ 0xA9B3: 84,
+ 0xA9B6: 84,
+ 0xA9B7: 84,
+ 0xA9B8: 84,
+ 0xA9B9: 84,
+ 0xA9BC: 84,
+ 0xA9BD: 84,
+ 0xA9E5: 84,
+ 0xAA29: 84,
+ 0xAA2A: 84,
+ 0xAA2B: 84,
+ 0xAA2C: 84,
+ 0xAA2D: 84,
+ 0xAA2E: 84,
+ 0xAA31: 84,
+ 0xAA32: 84,
+ 0xAA35: 84,
+ 0xAA36: 84,
+ 0xAA43: 84,
+ 0xAA4C: 84,
+ 0xAA7C: 84,
+ 0xAAB0: 84,
+ 0xAAB2: 84,
+ 0xAAB3: 84,
+ 0xAAB4: 84,
+ 0xAAB7: 84,
+ 0xAAB8: 84,
+ 0xAABE: 84,
+ 0xAABF: 84,
+ 0xAAC1: 84,
+ 0xAAEC: 84,
+ 0xAAED: 84,
+ 0xAAF6: 84,
+ 0xABE5: 84,
+ 0xABE8: 84,
+ 0xABED: 84,
+ 0xFB1E: 84,
+ 0xFE00: 84,
+ 0xFE01: 84,
+ 0xFE02: 84,
+ 0xFE03: 84,
+ 0xFE04: 84,
+ 0xFE05: 84,
+ 0xFE06: 84,
+ 0xFE07: 84,
+ 0xFE08: 84,
+ 0xFE09: 84,
+ 0xFE0A: 84,
+ 0xFE0B: 84,
+ 0xFE0C: 84,
+ 0xFE0D: 84,
+ 0xFE0E: 84,
+ 0xFE0F: 84,
+ 0xFE20: 84,
+ 0xFE21: 84,
+ 0xFE22: 84,
+ 0xFE23: 84,
+ 0xFE24: 84,
+ 0xFE25: 84,
+ 0xFE26: 84,
+ 0xFE27: 84,
+ 0xFE28: 84,
+ 0xFE29: 84,
+ 0xFE2A: 84,
+ 0xFE2B: 84,
+ 0xFE2C: 84,
+ 0xFE2D: 84,
+ 0xFE2E: 84,
+ 0xFE2F: 84,
+ 0xFEFF: 84,
+ 0xFFF9: 84,
+ 0xFFFA: 84,
+ 0xFFFB: 84,
+ 0x101FD: 84,
+ 0x102E0: 84,
+ 0x10376: 84,
+ 0x10377: 84,
+ 0x10378: 84,
+ 0x10379: 84,
+ 0x1037A: 84,
+ 0x10A01: 84,
+ 0x10A02: 84,
+ 0x10A03: 84,
+ 0x10A05: 84,
+ 0x10A06: 84,
+ 0x10A0C: 84,
+ 0x10A0D: 84,
+ 0x10A0E: 84,
+ 0x10A0F: 84,
+ 0x10A38: 84,
+ 0x10A39: 84,
+ 0x10A3A: 84,
+ 0x10A3F: 84,
+ 0x10AC0: 68,
+ 0x10AC1: 68,
+ 0x10AC2: 68,
+ 0x10AC3: 68,
+ 0x10AC4: 68,
+ 0x10AC5: 82,
+ 0x10AC7: 82,
+ 0x10AC9: 82,
+ 0x10ACA: 82,
+ 0x10ACD: 76,
+ 0x10ACE: 82,
+ 0x10ACF: 82,
+ 0x10AD0: 82,
+ 0x10AD1: 82,
+ 0x10AD2: 82,
+ 0x10AD3: 68,
+ 0x10AD4: 68,
+ 0x10AD5: 68,
+ 0x10AD6: 68,
+ 0x10AD7: 76,
+ 0x10AD8: 68,
+ 0x10AD9: 68,
+ 0x10ADA: 68,
+ 0x10ADB: 68,
+ 0x10ADC: 68,
+ 0x10ADD: 82,
+ 0x10ADE: 68,
+ 0x10ADF: 68,
+ 0x10AE0: 68,
+ 0x10AE1: 82,
+ 0x10AE4: 82,
+ 0x10AE5: 84,
+ 0x10AE6: 84,
+ 0x10AEB: 68,
+ 0x10AEC: 68,
+ 0x10AED: 68,
+ 0x10AEE: 68,
+ 0x10AEF: 82,
+ 0x10B80: 68,
+ 0x10B81: 82,
+ 0x10B82: 68,
+ 0x10B83: 82,
+ 0x10B84: 82,
+ 0x10B85: 82,
+ 0x10B86: 68,
+ 0x10B87: 68,
+ 0x10B88: 68,
+ 0x10B89: 82,
+ 0x10B8A: 68,
+ 0x10B8B: 68,
+ 0x10B8C: 82,
+ 0x10B8D: 68,
+ 0x10B8E: 82,
+ 0x10B8F: 82,
+ 0x10B90: 68,
+ 0x10B91: 82,
+ 0x10BA9: 82,
+ 0x10BAA: 82,
+ 0x10BAB: 82,
+ 0x10BAC: 82,
+ 0x10BAD: 68,
+ 0x10BAE: 68,
+ 0x10D00: 76,
+ 0x10D01: 68,
+ 0x10D02: 68,
+ 0x10D03: 68,
+ 0x10D04: 68,
+ 0x10D05: 68,
+ 0x10D06: 68,
+ 0x10D07: 68,
+ 0x10D08: 68,
+ 0x10D09: 68,
+ 0x10D0A: 68,
+ 0x10D0B: 68,
+ 0x10D0C: 68,
+ 0x10D0D: 68,
+ 0x10D0E: 68,
+ 0x10D0F: 68,
+ 0x10D10: 68,
+ 0x10D11: 68,
+ 0x10D12: 68,
+ 0x10D13: 68,
+ 0x10D14: 68,
+ 0x10D15: 68,
+ 0x10D16: 68,
+ 0x10D17: 68,
+ 0x10D18: 68,
+ 0x10D19: 68,
+ 0x10D1A: 68,
+ 0x10D1B: 68,
+ 0x10D1C: 68,
+ 0x10D1D: 68,
+ 0x10D1E: 68,
+ 0x10D1F: 68,
+ 0x10D20: 68,
+ 0x10D21: 68,
+ 0x10D22: 82,
+ 0x10D23: 68,
+ 0x10D24: 84,
+ 0x10D25: 84,
+ 0x10D26: 84,
+ 0x10D27: 84,
+ 0x10D69: 84,
+ 0x10D6A: 84,
+ 0x10D6B: 84,
+ 0x10D6C: 84,
+ 0x10D6D: 84,
+ 0x10EAB: 84,
+ 0x10EAC: 84,
+ 0x10EC2: 82,
+ 0x10EC3: 68,
+ 0x10EC4: 68,
+ 0x10EFC: 84,
+ 0x10EFD: 84,
+ 0x10EFE: 84,
+ 0x10EFF: 84,
+ 0x10F30: 68,
+ 0x10F31: 68,
+ 0x10F32: 68,
+ 0x10F33: 82,
+ 0x10F34: 68,
+ 0x10F35: 68,
+ 0x10F36: 68,
+ 0x10F37: 68,
+ 0x10F38: 68,
+ 0x10F39: 68,
+ 0x10F3A: 68,
+ 0x10F3B: 68,
+ 0x10F3C: 68,
+ 0x10F3D: 68,
+ 0x10F3E: 68,
+ 0x10F3F: 68,
+ 0x10F40: 68,
+ 0x10F41: 68,
+ 0x10F42: 68,
+ 0x10F43: 68,
+ 0x10F44: 68,
+ 0x10F46: 84,
+ 0x10F47: 84,
+ 0x10F48: 84,
+ 0x10F49: 84,
+ 0x10F4A: 84,
+ 0x10F4B: 84,
+ 0x10F4C: 84,
+ 0x10F4D: 84,
+ 0x10F4E: 84,
+ 0x10F4F: 84,
+ 0x10F50: 84,
+ 0x10F51: 68,
+ 0x10F52: 68,
+ 0x10F53: 68,
+ 0x10F54: 82,
+ 0x10F70: 68,
+ 0x10F71: 68,
+ 0x10F72: 68,
+ 0x10F73: 68,
+ 0x10F74: 82,
+ 0x10F75: 82,
+ 0x10F76: 68,
+ 0x10F77: 68,
+ 0x10F78: 68,
+ 0x10F79: 68,
+ 0x10F7A: 68,
+ 0x10F7B: 68,
+ 0x10F7C: 68,
+ 0x10F7D: 68,
+ 0x10F7E: 68,
+ 0x10F7F: 68,
+ 0x10F80: 68,
+ 0x10F81: 68,
+ 0x10F82: 84,
+ 0x10F83: 84,
+ 0x10F84: 84,
+ 0x10F85: 84,
+ 0x10FB0: 68,
+ 0x10FB2: 68,
+ 0x10FB3: 68,
+ 0x10FB4: 82,
+ 0x10FB5: 82,
+ 0x10FB6: 82,
+ 0x10FB8: 68,
+ 0x10FB9: 82,
+ 0x10FBA: 82,
+ 0x10FBB: 68,
+ 0x10FBC: 68,
+ 0x10FBD: 82,
+ 0x10FBE: 68,
+ 0x10FBF: 68,
+ 0x10FC1: 68,
+ 0x10FC2: 82,
+ 0x10FC3: 82,
+ 0x10FC4: 68,
+ 0x10FC9: 82,
+ 0x10FCA: 68,
+ 0x10FCB: 76,
+ 0x11001: 84,
+ 0x11038: 84,
+ 0x11039: 84,
+ 0x1103A: 84,
+ 0x1103B: 84,
+ 0x1103C: 84,
+ 0x1103D: 84,
+ 0x1103E: 84,
+ 0x1103F: 84,
+ 0x11040: 84,
+ 0x11041: 84,
+ 0x11042: 84,
+ 0x11043: 84,
+ 0x11044: 84,
+ 0x11045: 84,
+ 0x11046: 84,
+ 0x11070: 84,
+ 0x11073: 84,
+ 0x11074: 84,
+ 0x1107F: 84,
+ 0x11080: 84,
+ 0x11081: 84,
+ 0x110B3: 84,
+ 0x110B4: 84,
+ 0x110B5: 84,
+ 0x110B6: 84,
+ 0x110B9: 84,
+ 0x110BA: 84,
+ 0x110C2: 84,
+ 0x11100: 84,
+ 0x11101: 84,
+ 0x11102: 84,
+ 0x11127: 84,
+ 0x11128: 84,
+ 0x11129: 84,
+ 0x1112A: 84,
+ 0x1112B: 84,
+ 0x1112D: 84,
+ 0x1112E: 84,
+ 0x1112F: 84,
+ 0x11130: 84,
+ 0x11131: 84,
+ 0x11132: 84,
+ 0x11133: 84,
+ 0x11134: 84,
+ 0x11173: 84,
+ 0x11180: 84,
+ 0x11181: 84,
+ 0x111B6: 84,
+ 0x111B7: 84,
+ 0x111B8: 84,
+ 0x111B9: 84,
+ 0x111BA: 84,
+ 0x111BB: 84,
+ 0x111BC: 84,
+ 0x111BD: 84,
+ 0x111BE: 84,
+ 0x111C9: 84,
+ 0x111CA: 84,
+ 0x111CB: 84,
+ 0x111CC: 84,
+ 0x111CF: 84,
+ 0x1122F: 84,
+ 0x11230: 84,
+ 0x11231: 84,
+ 0x11234: 84,
+ 0x11236: 84,
+ 0x11237: 84,
+ 0x1123E: 84,
+ 0x11241: 84,
+ 0x112DF: 84,
+ 0x112E3: 84,
+ 0x112E4: 84,
+ 0x112E5: 84,
+ 0x112E6: 84,
+ 0x112E7: 84,
+ 0x112E8: 84,
+ 0x112E9: 84,
+ 0x112EA: 84,
+ 0x11300: 84,
+ 0x11301: 84,
+ 0x1133B: 84,
+ 0x1133C: 84,
+ 0x11340: 84,
+ 0x11366: 84,
+ 0x11367: 84,
+ 0x11368: 84,
+ 0x11369: 84,
+ 0x1136A: 84,
+ 0x1136B: 84,
+ 0x1136C: 84,
+ 0x11370: 84,
+ 0x11371: 84,
+ 0x11372: 84,
+ 0x11373: 84,
+ 0x11374: 84,
+ 0x113BB: 84,
+ 0x113BC: 84,
+ 0x113BD: 84,
+ 0x113BE: 84,
+ 0x113BF: 84,
+ 0x113C0: 84,
+ 0x113CE: 84,
+ 0x113D0: 84,
+ 0x113D2: 84,
+ 0x113E1: 84,
+ 0x113E2: 84,
+ 0x11438: 84,
+ 0x11439: 84,
+ 0x1143A: 84,
+ 0x1143B: 84,
+ 0x1143C: 84,
+ 0x1143D: 84,
+ 0x1143E: 84,
+ 0x1143F: 84,
+ 0x11442: 84,
+ 0x11443: 84,
+ 0x11444: 84,
+ 0x11446: 84,
+ 0x1145E: 84,
+ 0x114B3: 84,
+ 0x114B4: 84,
+ 0x114B5: 84,
+ 0x114B6: 84,
+ 0x114B7: 84,
+ 0x114B8: 84,
+ 0x114BA: 84,
+ 0x114BF: 84,
+ 0x114C0: 84,
+ 0x114C2: 84,
+ 0x114C3: 84,
+ 0x115B2: 84,
+ 0x115B3: 84,
+ 0x115B4: 84,
+ 0x115B5: 84,
+ 0x115BC: 84,
+ 0x115BD: 84,
+ 0x115BF: 84,
+ 0x115C0: 84,
+ 0x115DC: 84,
+ 0x115DD: 84,
+ 0x11633: 84,
+ 0x11634: 84,
+ 0x11635: 84,
+ 0x11636: 84,
+ 0x11637: 84,
+ 0x11638: 84,
+ 0x11639: 84,
+ 0x1163A: 84,
+ 0x1163D: 84,
+ 0x1163F: 84,
+ 0x11640: 84,
+ 0x116AB: 84,
+ 0x116AD: 84,
+ 0x116B0: 84,
+ 0x116B1: 84,
+ 0x116B2: 84,
+ 0x116B3: 84,
+ 0x116B4: 84,
+ 0x116B5: 84,
+ 0x116B7: 84,
+ 0x1171D: 84,
+ 0x1171F: 84,
+ 0x11722: 84,
+ 0x11723: 84,
+ 0x11724: 84,
+ 0x11725: 84,
+ 0x11727: 84,
+ 0x11728: 84,
+ 0x11729: 84,
+ 0x1172A: 84,
+ 0x1172B: 84,
+ 0x1182F: 84,
+ 0x11830: 84,
+ 0x11831: 84,
+ 0x11832: 84,
+ 0x11833: 84,
+ 0x11834: 84,
+ 0x11835: 84,
+ 0x11836: 84,
+ 0x11837: 84,
+ 0x11839: 84,
+ 0x1183A: 84,
+ 0x1193B: 84,
+ 0x1193C: 84,
+ 0x1193E: 84,
+ 0x11943: 84,
+ 0x119D4: 84,
+ 0x119D5: 84,
+ 0x119D6: 84,
+ 0x119D7: 84,
+ 0x119DA: 84,
+ 0x119DB: 84,
+ 0x119E0: 84,
+ 0x11A01: 84,
+ 0x11A02: 84,
+ 0x11A03: 84,
+ 0x11A04: 84,
+ 0x11A05: 84,
+ 0x11A06: 84,
+ 0x11A07: 84,
+ 0x11A08: 84,
+ 0x11A09: 84,
+ 0x11A0A: 84,
+ 0x11A33: 84,
+ 0x11A34: 84,
+ 0x11A35: 84,
+ 0x11A36: 84,
+ 0x11A37: 84,
+ 0x11A38: 84,
+ 0x11A3B: 84,
+ 0x11A3C: 84,
+ 0x11A3D: 84,
+ 0x11A3E: 84,
+ 0x11A47: 84,
+ 0x11A51: 84,
+ 0x11A52: 84,
+ 0x11A53: 84,
+ 0x11A54: 84,
+ 0x11A55: 84,
+ 0x11A56: 84,
+ 0x11A59: 84,
+ 0x11A5A: 84,
+ 0x11A5B: 84,
+ 0x11A8A: 84,
+ 0x11A8B: 84,
+ 0x11A8C: 84,
+ 0x11A8D: 84,
+ 0x11A8E: 84,
+ 0x11A8F: 84,
+ 0x11A90: 84,
+ 0x11A91: 84,
+ 0x11A92: 84,
+ 0x11A93: 84,
+ 0x11A94: 84,
+ 0x11A95: 84,
+ 0x11A96: 84,
+ 0x11A98: 84,
+ 0x11A99: 84,
+ 0x11C30: 84,
+ 0x11C31: 84,
+ 0x11C32: 84,
+ 0x11C33: 84,
+ 0x11C34: 84,
+ 0x11C35: 84,
+ 0x11C36: 84,
+ 0x11C38: 84,
+ 0x11C39: 84,
+ 0x11C3A: 84,
+ 0x11C3B: 84,
+ 0x11C3C: 84,
+ 0x11C3D: 84,
+ 0x11C3F: 84,
+ 0x11C92: 84,
+ 0x11C93: 84,
+ 0x11C94: 84,
+ 0x11C95: 84,
+ 0x11C96: 84,
+ 0x11C97: 84,
+ 0x11C98: 84,
+ 0x11C99: 84,
+ 0x11C9A: 84,
+ 0x11C9B: 84,
+ 0x11C9C: 84,
+ 0x11C9D: 84,
+ 0x11C9E: 84,
+ 0x11C9F: 84,
+ 0x11CA0: 84,
+ 0x11CA1: 84,
+ 0x11CA2: 84,
+ 0x11CA3: 84,
+ 0x11CA4: 84,
+ 0x11CA5: 84,
+ 0x11CA6: 84,
+ 0x11CA7: 84,
+ 0x11CAA: 84,
+ 0x11CAB: 84,
+ 0x11CAC: 84,
+ 0x11CAD: 84,
+ 0x11CAE: 84,
+ 0x11CAF: 84,
+ 0x11CB0: 84,
+ 0x11CB2: 84,
+ 0x11CB3: 84,
+ 0x11CB5: 84,
+ 0x11CB6: 84,
+ 0x11D31: 84,
+ 0x11D32: 84,
+ 0x11D33: 84,
+ 0x11D34: 84,
+ 0x11D35: 84,
+ 0x11D36: 84,
+ 0x11D3A: 84,
+ 0x11D3C: 84,
+ 0x11D3D: 84,
+ 0x11D3F: 84,
+ 0x11D40: 84,
+ 0x11D41: 84,
+ 0x11D42: 84,
+ 0x11D43: 84,
+ 0x11D44: 84,
+ 0x11D45: 84,
+ 0x11D47: 84,
+ 0x11D90: 84,
+ 0x11D91: 84,
+ 0x11D95: 84,
+ 0x11D97: 84,
+ 0x11EF3: 84,
+ 0x11EF4: 84,
+ 0x11F00: 84,
+ 0x11F01: 84,
+ 0x11F36: 84,
+ 0x11F37: 84,
+ 0x11F38: 84,
+ 0x11F39: 84,
+ 0x11F3A: 84,
+ 0x11F40: 84,
+ 0x11F42: 84,
+ 0x11F5A: 84,
+ 0x13430: 84,
+ 0x13431: 84,
+ 0x13432: 84,
+ 0x13433: 84,
+ 0x13434: 84,
+ 0x13435: 84,
+ 0x13436: 84,
+ 0x13437: 84,
+ 0x13438: 84,
+ 0x13439: 84,
+ 0x1343A: 84,
+ 0x1343B: 84,
+ 0x1343C: 84,
+ 0x1343D: 84,
+ 0x1343E: 84,
+ 0x1343F: 84,
+ 0x13440: 84,
+ 0x13447: 84,
+ 0x13448: 84,
+ 0x13449: 84,
+ 0x1344A: 84,
+ 0x1344B: 84,
+ 0x1344C: 84,
+ 0x1344D: 84,
+ 0x1344E: 84,
+ 0x1344F: 84,
+ 0x13450: 84,
+ 0x13451: 84,
+ 0x13452: 84,
+ 0x13453: 84,
+ 0x13454: 84,
+ 0x13455: 84,
+ 0x1611E: 84,
+ 0x1611F: 84,
+ 0x16120: 84,
+ 0x16121: 84,
+ 0x16122: 84,
+ 0x16123: 84,
+ 0x16124: 84,
+ 0x16125: 84,
+ 0x16126: 84,
+ 0x16127: 84,
+ 0x16128: 84,
+ 0x16129: 84,
+ 0x1612D: 84,
+ 0x1612E: 84,
+ 0x1612F: 84,
+ 0x16AF0: 84,
+ 0x16AF1: 84,
+ 0x16AF2: 84,
+ 0x16AF3: 84,
+ 0x16AF4: 84,
+ 0x16B30: 84,
+ 0x16B31: 84,
+ 0x16B32: 84,
+ 0x16B33: 84,
+ 0x16B34: 84,
+ 0x16B35: 84,
+ 0x16B36: 84,
+ 0x16F4F: 84,
+ 0x16F8F: 84,
+ 0x16F90: 84,
+ 0x16F91: 84,
+ 0x16F92: 84,
+ 0x16FE4: 84,
+ 0x1BC9D: 84,
+ 0x1BC9E: 84,
+ 0x1BCA0: 84,
+ 0x1BCA1: 84,
+ 0x1BCA2: 84,
+ 0x1BCA3: 84,
+ 0x1CF00: 84,
+ 0x1CF01: 84,
+ 0x1CF02: 84,
+ 0x1CF03: 84,
+ 0x1CF04: 84,
+ 0x1CF05: 84,
+ 0x1CF06: 84,
+ 0x1CF07: 84,
+ 0x1CF08: 84,
+ 0x1CF09: 84,
+ 0x1CF0A: 84,
+ 0x1CF0B: 84,
+ 0x1CF0C: 84,
+ 0x1CF0D: 84,
+ 0x1CF0E: 84,
+ 0x1CF0F: 84,
+ 0x1CF10: 84,
+ 0x1CF11: 84,
+ 0x1CF12: 84,
+ 0x1CF13: 84,
+ 0x1CF14: 84,
+ 0x1CF15: 84,
+ 0x1CF16: 84,
+ 0x1CF17: 84,
+ 0x1CF18: 84,
+ 0x1CF19: 84,
+ 0x1CF1A: 84,
+ 0x1CF1B: 84,
+ 0x1CF1C: 84,
+ 0x1CF1D: 84,
+ 0x1CF1E: 84,
+ 0x1CF1F: 84,
+ 0x1CF20: 84,
+ 0x1CF21: 84,
+ 0x1CF22: 84,
+ 0x1CF23: 84,
+ 0x1CF24: 84,
+ 0x1CF25: 84,
+ 0x1CF26: 84,
+ 0x1CF27: 84,
+ 0x1CF28: 84,
+ 0x1CF29: 84,
+ 0x1CF2A: 84,
+ 0x1CF2B: 84,
+ 0x1CF2C: 84,
+ 0x1CF2D: 84,
+ 0x1CF30: 84,
+ 0x1CF31: 84,
+ 0x1CF32: 84,
+ 0x1CF33: 84,
+ 0x1CF34: 84,
+ 0x1CF35: 84,
+ 0x1CF36: 84,
+ 0x1CF37: 84,
+ 0x1CF38: 84,
+ 0x1CF39: 84,
+ 0x1CF3A: 84,
+ 0x1CF3B: 84,
+ 0x1CF3C: 84,
+ 0x1CF3D: 84,
+ 0x1CF3E: 84,
+ 0x1CF3F: 84,
+ 0x1CF40: 84,
+ 0x1CF41: 84,
+ 0x1CF42: 84,
+ 0x1CF43: 84,
+ 0x1CF44: 84,
+ 0x1CF45: 84,
+ 0x1CF46: 84,
+ 0x1D167: 84,
+ 0x1D168: 84,
+ 0x1D169: 84,
+ 0x1D173: 84,
+ 0x1D174: 84,
+ 0x1D175: 84,
+ 0x1D176: 84,
+ 0x1D177: 84,
+ 0x1D178: 84,
+ 0x1D179: 84,
+ 0x1D17A: 84,
+ 0x1D17B: 84,
+ 0x1D17C: 84,
+ 0x1D17D: 84,
+ 0x1D17E: 84,
+ 0x1D17F: 84,
+ 0x1D180: 84,
+ 0x1D181: 84,
+ 0x1D182: 84,
+ 0x1D185: 84,
+ 0x1D186: 84,
+ 0x1D187: 84,
+ 0x1D188: 84,
+ 0x1D189: 84,
+ 0x1D18A: 84,
+ 0x1D18B: 84,
+ 0x1D1AA: 84,
+ 0x1D1AB: 84,
+ 0x1D1AC: 84,
+ 0x1D1AD: 84,
+ 0x1D242: 84,
+ 0x1D243: 84,
+ 0x1D244: 84,
+ 0x1DA00: 84,
+ 0x1DA01: 84,
+ 0x1DA02: 84,
+ 0x1DA03: 84,
+ 0x1DA04: 84,
+ 0x1DA05: 84,
+ 0x1DA06: 84,
+ 0x1DA07: 84,
+ 0x1DA08: 84,
+ 0x1DA09: 84,
+ 0x1DA0A: 84,
+ 0x1DA0B: 84,
+ 0x1DA0C: 84,
+ 0x1DA0D: 84,
+ 0x1DA0E: 84,
+ 0x1DA0F: 84,
+ 0x1DA10: 84,
+ 0x1DA11: 84,
+ 0x1DA12: 84,
+ 0x1DA13: 84,
+ 0x1DA14: 84,
+ 0x1DA15: 84,
+ 0x1DA16: 84,
+ 0x1DA17: 84,
+ 0x1DA18: 84,
+ 0x1DA19: 84,
+ 0x1DA1A: 84,
+ 0x1DA1B: 84,
+ 0x1DA1C: 84,
+ 0x1DA1D: 84,
+ 0x1DA1E: 84,
+ 0x1DA1F: 84,
+ 0x1DA20: 84,
+ 0x1DA21: 84,
+ 0x1DA22: 84,
+ 0x1DA23: 84,
+ 0x1DA24: 84,
+ 0x1DA25: 84,
+ 0x1DA26: 84,
+ 0x1DA27: 84,
+ 0x1DA28: 84,
+ 0x1DA29: 84,
+ 0x1DA2A: 84,
+ 0x1DA2B: 84,
+ 0x1DA2C: 84,
+ 0x1DA2D: 84,
+ 0x1DA2E: 84,
+ 0x1DA2F: 84,
+ 0x1DA30: 84,
+ 0x1DA31: 84,
+ 0x1DA32: 84,
+ 0x1DA33: 84,
+ 0x1DA34: 84,
+ 0x1DA35: 84,
+ 0x1DA36: 84,
+ 0x1DA3B: 84,
+ 0x1DA3C: 84,
+ 0x1DA3D: 84,
+ 0x1DA3E: 84,
+ 0x1DA3F: 84,
+ 0x1DA40: 84,
+ 0x1DA41: 84,
+ 0x1DA42: 84,
+ 0x1DA43: 84,
+ 0x1DA44: 84,
+ 0x1DA45: 84,
+ 0x1DA46: 84,
+ 0x1DA47: 84,
+ 0x1DA48: 84,
+ 0x1DA49: 84,
+ 0x1DA4A: 84,
+ 0x1DA4B: 84,
+ 0x1DA4C: 84,
+ 0x1DA4D: 84,
+ 0x1DA4E: 84,
+ 0x1DA4F: 84,
+ 0x1DA50: 84,
+ 0x1DA51: 84,
+ 0x1DA52: 84,
+ 0x1DA53: 84,
+ 0x1DA54: 84,
+ 0x1DA55: 84,
+ 0x1DA56: 84,
+ 0x1DA57: 84,
+ 0x1DA58: 84,
+ 0x1DA59: 84,
+ 0x1DA5A: 84,
+ 0x1DA5B: 84,
+ 0x1DA5C: 84,
+ 0x1DA5D: 84,
+ 0x1DA5E: 84,
+ 0x1DA5F: 84,
+ 0x1DA60: 84,
+ 0x1DA61: 84,
+ 0x1DA62: 84,
+ 0x1DA63: 84,
+ 0x1DA64: 84,
+ 0x1DA65: 84,
+ 0x1DA66: 84,
+ 0x1DA67: 84,
+ 0x1DA68: 84,
+ 0x1DA69: 84,
+ 0x1DA6A: 84,
+ 0x1DA6B: 84,
+ 0x1DA6C: 84,
+ 0x1DA75: 84,
+ 0x1DA84: 84,
+ 0x1DA9B: 84,
+ 0x1DA9C: 84,
+ 0x1DA9D: 84,
+ 0x1DA9E: 84,
+ 0x1DA9F: 84,
+ 0x1DAA1: 84,
+ 0x1DAA2: 84,
+ 0x1DAA3: 84,
+ 0x1DAA4: 84,
+ 0x1DAA5: 84,
+ 0x1DAA6: 84,
+ 0x1DAA7: 84,
+ 0x1DAA8: 84,
+ 0x1DAA9: 84,
+ 0x1DAAA: 84,
+ 0x1DAAB: 84,
+ 0x1DAAC: 84,
+ 0x1DAAD: 84,
+ 0x1DAAE: 84,
+ 0x1DAAF: 84,
+ 0x1E000: 84,
+ 0x1E001: 84,
+ 0x1E002: 84,
+ 0x1E003: 84,
+ 0x1E004: 84,
+ 0x1E005: 84,
+ 0x1E006: 84,
+ 0x1E008: 84,
+ 0x1E009: 84,
+ 0x1E00A: 84,
+ 0x1E00B: 84,
+ 0x1E00C: 84,
+ 0x1E00D: 84,
+ 0x1E00E: 84,
+ 0x1E00F: 84,
+ 0x1E010: 84,
+ 0x1E011: 84,
+ 0x1E012: 84,
+ 0x1E013: 84,
+ 0x1E014: 84,
+ 0x1E015: 84,
+ 0x1E016: 84,
+ 0x1E017: 84,
+ 0x1E018: 84,
+ 0x1E01B: 84,
+ 0x1E01C: 84,
+ 0x1E01D: 84,
+ 0x1E01E: 84,
+ 0x1E01F: 84,
+ 0x1E020: 84,
+ 0x1E021: 84,
+ 0x1E023: 84,
+ 0x1E024: 84,
+ 0x1E026: 84,
+ 0x1E027: 84,
+ 0x1E028: 84,
+ 0x1E029: 84,
+ 0x1E02A: 84,
+ 0x1E08F: 84,
+ 0x1E130: 84,
+ 0x1E131: 84,
+ 0x1E132: 84,
+ 0x1E133: 84,
+ 0x1E134: 84,
+ 0x1E135: 84,
+ 0x1E136: 84,
+ 0x1E2AE: 84,
+ 0x1E2EC: 84,
+ 0x1E2ED: 84,
+ 0x1E2EE: 84,
+ 0x1E2EF: 84,
+ 0x1E4EC: 84,
+ 0x1E4ED: 84,
+ 0x1E4EE: 84,
+ 0x1E4EF: 84,
+ 0x1E5EE: 84,
+ 0x1E5EF: 84,
+ 0x1E8D0: 84,
+ 0x1E8D1: 84,
+ 0x1E8D2: 84,
+ 0x1E8D3: 84,
+ 0x1E8D4: 84,
+ 0x1E8D5: 84,
+ 0x1E8D6: 84,
+ 0x1E900: 68,
+ 0x1E901: 68,
+ 0x1E902: 68,
+ 0x1E903: 68,
+ 0x1E904: 68,
+ 0x1E905: 68,
+ 0x1E906: 68,
+ 0x1E907: 68,
+ 0x1E908: 68,
+ 0x1E909: 68,
+ 0x1E90A: 68,
+ 0x1E90B: 68,
+ 0x1E90C: 68,
+ 0x1E90D: 68,
+ 0x1E90E: 68,
+ 0x1E90F: 68,
+ 0x1E910: 68,
+ 0x1E911: 68,
+ 0x1E912: 68,
+ 0x1E913: 68,
+ 0x1E914: 68,
+ 0x1E915: 68,
+ 0x1E916: 68,
+ 0x1E917: 68,
+ 0x1E918: 68,
+ 0x1E919: 68,
+ 0x1E91A: 68,
+ 0x1E91B: 68,
+ 0x1E91C: 68,
+ 0x1E91D: 68,
+ 0x1E91E: 68,
+ 0x1E91F: 68,
+ 0x1E920: 68,
+ 0x1E921: 68,
+ 0x1E922: 68,
+ 0x1E923: 68,
+ 0x1E924: 68,
+ 0x1E925: 68,
+ 0x1E926: 68,
+ 0x1E927: 68,
+ 0x1E928: 68,
+ 0x1E929: 68,
+ 0x1E92A: 68,
+ 0x1E92B: 68,
+ 0x1E92C: 68,
+ 0x1E92D: 68,
+ 0x1E92E: 68,
+ 0x1E92F: 68,
+ 0x1E930: 68,
+ 0x1E931: 68,
+ 0x1E932: 68,
+ 0x1E933: 68,
+ 0x1E934: 68,
+ 0x1E935: 68,
+ 0x1E936: 68,
+ 0x1E937: 68,
+ 0x1E938: 68,
+ 0x1E939: 68,
+ 0x1E93A: 68,
+ 0x1E93B: 68,
+ 0x1E93C: 68,
+ 0x1E93D: 68,
+ 0x1E93E: 68,
+ 0x1E93F: 68,
+ 0x1E940: 68,
+ 0x1E941: 68,
+ 0x1E942: 68,
+ 0x1E943: 68,
+ 0x1E944: 84,
+ 0x1E945: 84,
+ 0x1E946: 84,
+ 0x1E947: 84,
+ 0x1E948: 84,
+ 0x1E949: 84,
+ 0x1E94A: 84,
+ 0x1E94B: 84,
+ 0xE0001: 84,
+ 0xE0020: 84,
+ 0xE0021: 84,
+ 0xE0022: 84,
+ 0xE0023: 84,
+ 0xE0024: 84,
+ 0xE0025: 84,
+ 0xE0026: 84,
+ 0xE0027: 84,
+ 0xE0028: 84,
+ 0xE0029: 84,
+ 0xE002A: 84,
+ 0xE002B: 84,
+ 0xE002C: 84,
+ 0xE002D: 84,
+ 0xE002E: 84,
+ 0xE002F: 84,
+ 0xE0030: 84,
+ 0xE0031: 84,
+ 0xE0032: 84,
+ 0xE0033: 84,
+ 0xE0034: 84,
+ 0xE0035: 84,
+ 0xE0036: 84,
+ 0xE0037: 84,
+ 0xE0038: 84,
+ 0xE0039: 84,
+ 0xE003A: 84,
+ 0xE003B: 84,
+ 0xE003C: 84,
+ 0xE003D: 84,
+ 0xE003E: 84,
+ 0xE003F: 84,
+ 0xE0040: 84,
+ 0xE0041: 84,
+ 0xE0042: 84,
+ 0xE0043: 84,
+ 0xE0044: 84,
+ 0xE0045: 84,
+ 0xE0046: 84,
+ 0xE0047: 84,
+ 0xE0048: 84,
+ 0xE0049: 84,
+ 0xE004A: 84,
+ 0xE004B: 84,
+ 0xE004C: 84,
+ 0xE004D: 84,
+ 0xE004E: 84,
+ 0xE004F: 84,
+ 0xE0050: 84,
+ 0xE0051: 84,
+ 0xE0052: 84,
+ 0xE0053: 84,
+ 0xE0054: 84,
+ 0xE0055: 84,
+ 0xE0056: 84,
+ 0xE0057: 84,
+ 0xE0058: 84,
+ 0xE0059: 84,
+ 0xE005A: 84,
+ 0xE005B: 84,
+ 0xE005C: 84,
+ 0xE005D: 84,
+ 0xE005E: 84,
+ 0xE005F: 84,
+ 0xE0060: 84,
+ 0xE0061: 84,
+ 0xE0062: 84,
+ 0xE0063: 84,
+ 0xE0064: 84,
+ 0xE0065: 84,
+ 0xE0066: 84,
+ 0xE0067: 84,
+ 0xE0068: 84,
+ 0xE0069: 84,
+ 0xE006A: 84,
+ 0xE006B: 84,
+ 0xE006C: 84,
+ 0xE006D: 84,
+ 0xE006E: 84,
+ 0xE006F: 84,
+ 0xE0070: 84,
+ 0xE0071: 84,
+ 0xE0072: 84,
+ 0xE0073: 84,
+ 0xE0074: 84,
+ 0xE0075: 84,
+ 0xE0076: 84,
+ 0xE0077: 84,
+ 0xE0078: 84,
+ 0xE0079: 84,
+ 0xE007A: 84,
+ 0xE007B: 84,
+ 0xE007C: 84,
+ 0xE007D: 84,
+ 0xE007E: 84,
+ 0xE007F: 84,
+ 0xE0100: 84,
+ 0xE0101: 84,
+ 0xE0102: 84,
+ 0xE0103: 84,
+ 0xE0104: 84,
+ 0xE0105: 84,
+ 0xE0106: 84,
+ 0xE0107: 84,
+ 0xE0108: 84,
+ 0xE0109: 84,
+ 0xE010A: 84,
+ 0xE010B: 84,
+ 0xE010C: 84,
+ 0xE010D: 84,
+ 0xE010E: 84,
+ 0xE010F: 84,
+ 0xE0110: 84,
+ 0xE0111: 84,
+ 0xE0112: 84,
+ 0xE0113: 84,
+ 0xE0114: 84,
+ 0xE0115: 84,
+ 0xE0116: 84,
+ 0xE0117: 84,
+ 0xE0118: 84,
+ 0xE0119: 84,
+ 0xE011A: 84,
+ 0xE011B: 84,
+ 0xE011C: 84,
+ 0xE011D: 84,
+ 0xE011E: 84,
+ 0xE011F: 84,
+ 0xE0120: 84,
+ 0xE0121: 84,
+ 0xE0122: 84,
+ 0xE0123: 84,
+ 0xE0124: 84,
+ 0xE0125: 84,
+ 0xE0126: 84,
+ 0xE0127: 84,
+ 0xE0128: 84,
+ 0xE0129: 84,
+ 0xE012A: 84,
+ 0xE012B: 84,
+ 0xE012C: 84,
+ 0xE012D: 84,
+ 0xE012E: 84,
+ 0xE012F: 84,
+ 0xE0130: 84,
+ 0xE0131: 84,
+ 0xE0132: 84,
+ 0xE0133: 84,
+ 0xE0134: 84,
+ 0xE0135: 84,
+ 0xE0136: 84,
+ 0xE0137: 84,
+ 0xE0138: 84,
+ 0xE0139: 84,
+ 0xE013A: 84,
+ 0xE013B: 84,
+ 0xE013C: 84,
+ 0xE013D: 84,
+ 0xE013E: 84,
+ 0xE013F: 84,
+ 0xE0140: 84,
+ 0xE0141: 84,
+ 0xE0142: 84,
+ 0xE0143: 84,
+ 0xE0144: 84,
+ 0xE0145: 84,
+ 0xE0146: 84,
+ 0xE0147: 84,
+ 0xE0148: 84,
+ 0xE0149: 84,
+ 0xE014A: 84,
+ 0xE014B: 84,
+ 0xE014C: 84,
+ 0xE014D: 84,
+ 0xE014E: 84,
+ 0xE014F: 84,
+ 0xE0150: 84,
+ 0xE0151: 84,
+ 0xE0152: 84,
+ 0xE0153: 84,
+ 0xE0154: 84,
+ 0xE0155: 84,
+ 0xE0156: 84,
+ 0xE0157: 84,
+ 0xE0158: 84,
+ 0xE0159: 84,
+ 0xE015A: 84,
+ 0xE015B: 84,
+ 0xE015C: 84,
+ 0xE015D: 84,
+ 0xE015E: 84,
+ 0xE015F: 84,
+ 0xE0160: 84,
+ 0xE0161: 84,
+ 0xE0162: 84,
+ 0xE0163: 84,
+ 0xE0164: 84,
+ 0xE0165: 84,
+ 0xE0166: 84,
+ 0xE0167: 84,
+ 0xE0168: 84,
+ 0xE0169: 84,
+ 0xE016A: 84,
+ 0xE016B: 84,
+ 0xE016C: 84,
+ 0xE016D: 84,
+ 0xE016E: 84,
+ 0xE016F: 84,
+ 0xE0170: 84,
+ 0xE0171: 84,
+ 0xE0172: 84,
+ 0xE0173: 84,
+ 0xE0174: 84,
+ 0xE0175: 84,
+ 0xE0176: 84,
+ 0xE0177: 84,
+ 0xE0178: 84,
+ 0xE0179: 84,
+ 0xE017A: 84,
+ 0xE017B: 84,
+ 0xE017C: 84,
+ 0xE017D: 84,
+ 0xE017E: 84,
+ 0xE017F: 84,
+ 0xE0180: 84,
+ 0xE0181: 84,
+ 0xE0182: 84,
+ 0xE0183: 84,
+ 0xE0184: 84,
+ 0xE0185: 84,
+ 0xE0186: 84,
+ 0xE0187: 84,
+ 0xE0188: 84,
+ 0xE0189: 84,
+ 0xE018A: 84,
+ 0xE018B: 84,
+ 0xE018C: 84,
+ 0xE018D: 84,
+ 0xE018E: 84,
+ 0xE018F: 84,
+ 0xE0190: 84,
+ 0xE0191: 84,
+ 0xE0192: 84,
+ 0xE0193: 84,
+ 0xE0194: 84,
+ 0xE0195: 84,
+ 0xE0196: 84,
+ 0xE0197: 84,
+ 0xE0198: 84,
+ 0xE0199: 84,
+ 0xE019A: 84,
+ 0xE019B: 84,
+ 0xE019C: 84,
+ 0xE019D: 84,
+ 0xE019E: 84,
+ 0xE019F: 84,
+ 0xE01A0: 84,
+ 0xE01A1: 84,
+ 0xE01A2: 84,
+ 0xE01A3: 84,
+ 0xE01A4: 84,
+ 0xE01A5: 84,
+ 0xE01A6: 84,
+ 0xE01A7: 84,
+ 0xE01A8: 84,
+ 0xE01A9: 84,
+ 0xE01AA: 84,
+ 0xE01AB: 84,
+ 0xE01AC: 84,
+ 0xE01AD: 84,
+ 0xE01AE: 84,
+ 0xE01AF: 84,
+ 0xE01B0: 84,
+ 0xE01B1: 84,
+ 0xE01B2: 84,
+ 0xE01B3: 84,
+ 0xE01B4: 84,
+ 0xE01B5: 84,
+ 0xE01B6: 84,
+ 0xE01B7: 84,
+ 0xE01B8: 84,
+ 0xE01B9: 84,
+ 0xE01BA: 84,
+ 0xE01BB: 84,
+ 0xE01BC: 84,
+ 0xE01BD: 84,
+ 0xE01BE: 84,
+ 0xE01BF: 84,
+ 0xE01C0: 84,
+ 0xE01C1: 84,
+ 0xE01C2: 84,
+ 0xE01C3: 84,
+ 0xE01C4: 84,
+ 0xE01C5: 84,
+ 0xE01C6: 84,
+ 0xE01C7: 84,
+ 0xE01C8: 84,
+ 0xE01C9: 84,
+ 0xE01CA: 84,
+ 0xE01CB: 84,
+ 0xE01CC: 84,
+ 0xE01CD: 84,
+ 0xE01CE: 84,
+ 0xE01CF: 84,
+ 0xE01D0: 84,
+ 0xE01D1: 84,
+ 0xE01D2: 84,
+ 0xE01D3: 84,
+ 0xE01D4: 84,
+ 0xE01D5: 84,
+ 0xE01D6: 84,
+ 0xE01D7: 84,
+ 0xE01D8: 84,
+ 0xE01D9: 84,
+ 0xE01DA: 84,
+ 0xE01DB: 84,
+ 0xE01DC: 84,
+ 0xE01DD: 84,
+ 0xE01DE: 84,
+ 0xE01DF: 84,
+ 0xE01E0: 84,
+ 0xE01E1: 84,
+ 0xE01E2: 84,
+ 0xE01E3: 84,
+ 0xE01E4: 84,
+ 0xE01E5: 84,
+ 0xE01E6: 84,
+ 0xE01E7: 84,
+ 0xE01E8: 84,
+ 0xE01E9: 84,
+ 0xE01EA: 84,
+ 0xE01EB: 84,
+ 0xE01EC: 84,
+ 0xE01ED: 84,
+ 0xE01EE: 84,
+ 0xE01EF: 84,
+}
+codepoint_classes = {
+ "PVALID": (
+ 0x2D0000002E,
+ 0x300000003A,
+ 0x610000007B,
+ 0xDF000000F7,
+ 0xF800000100,
+ 0x10100000102,
+ 0x10300000104,
+ 0x10500000106,
+ 0x10700000108,
+ 0x1090000010A,
+ 0x10B0000010C,
+ 0x10D0000010E,
+ 0x10F00000110,
+ 0x11100000112,
+ 0x11300000114,
+ 0x11500000116,
+ 0x11700000118,
+ 0x1190000011A,
+ 0x11B0000011C,
+ 0x11D0000011E,
+ 0x11F00000120,
+ 0x12100000122,
+ 0x12300000124,
+ 0x12500000126,
+ 0x12700000128,
+ 0x1290000012A,
+ 0x12B0000012C,
+ 0x12D0000012E,
+ 0x12F00000130,
+ 0x13100000132,
+ 0x13500000136,
+ 0x13700000139,
+ 0x13A0000013B,
+ 0x13C0000013D,
+ 0x13E0000013F,
+ 0x14200000143,
+ 0x14400000145,
+ 0x14600000147,
+ 0x14800000149,
+ 0x14B0000014C,
+ 0x14D0000014E,
+ 0x14F00000150,
+ 0x15100000152,
+ 0x15300000154,
+ 0x15500000156,
+ 0x15700000158,
+ 0x1590000015A,
+ 0x15B0000015C,
+ 0x15D0000015E,
+ 0x15F00000160,
+ 0x16100000162,
+ 0x16300000164,
+ 0x16500000166,
+ 0x16700000168,
+ 0x1690000016A,
+ 0x16B0000016C,
+ 0x16D0000016E,
+ 0x16F00000170,
+ 0x17100000172,
+ 0x17300000174,
+ 0x17500000176,
+ 0x17700000178,
+ 0x17A0000017B,
+ 0x17C0000017D,
+ 0x17E0000017F,
+ 0x18000000181,
+ 0x18300000184,
+ 0x18500000186,
+ 0x18800000189,
+ 0x18C0000018E,
+ 0x19200000193,
+ 0x19500000196,
+ 0x1990000019C,
+ 0x19E0000019F,
+ 0x1A1000001A2,
+ 0x1A3000001A4,
+ 0x1A5000001A6,
+ 0x1A8000001A9,
+ 0x1AA000001AC,
+ 0x1AD000001AE,
+ 0x1B0000001B1,
+ 0x1B4000001B5,
+ 0x1B6000001B7,
+ 0x1B9000001BC,
+ 0x1BD000001C4,
+ 0x1CE000001CF,
+ 0x1D0000001D1,
+ 0x1D2000001D3,
+ 0x1D4000001D5,
+ 0x1D6000001D7,
+ 0x1D8000001D9,
+ 0x1DA000001DB,
+ 0x1DC000001DE,
+ 0x1DF000001E0,
+ 0x1E1000001E2,
+ 0x1E3000001E4,
+ 0x1E5000001E6,
+ 0x1E7000001E8,
+ 0x1E9000001EA,
+ 0x1EB000001EC,
+ 0x1ED000001EE,
+ 0x1EF000001F1,
+ 0x1F5000001F6,
+ 0x1F9000001FA,
+ 0x1FB000001FC,
+ 0x1FD000001FE,
+ 0x1FF00000200,
+ 0x20100000202,
+ 0x20300000204,
+ 0x20500000206,
+ 0x20700000208,
+ 0x2090000020A,
+ 0x20B0000020C,
+ 0x20D0000020E,
+ 0x20F00000210,
+ 0x21100000212,
+ 0x21300000214,
+ 0x21500000216,
+ 0x21700000218,
+ 0x2190000021A,
+ 0x21B0000021C,
+ 0x21D0000021E,
+ 0x21F00000220,
+ 0x22100000222,
+ 0x22300000224,
+ 0x22500000226,
+ 0x22700000228,
+ 0x2290000022A,
+ 0x22B0000022C,
+ 0x22D0000022E,
+ 0x22F00000230,
+ 0x23100000232,
+ 0x2330000023A,
+ 0x23C0000023D,
+ 0x23F00000241,
+ 0x24200000243,
+ 0x24700000248,
+ 0x2490000024A,
+ 0x24B0000024C,
+ 0x24D0000024E,
+ 0x24F000002B0,
+ 0x2B9000002C2,
+ 0x2C6000002D2,
+ 0x2EC000002ED,
+ 0x2EE000002EF,
+ 0x30000000340,
+ 0x34200000343,
+ 0x3460000034F,
+ 0x35000000370,
+ 0x37100000372,
+ 0x37300000374,
+ 0x37700000378,
+ 0x37B0000037E,
+ 0x39000000391,
+ 0x3AC000003CF,
+ 0x3D7000003D8,
+ 0x3D9000003DA,
+ 0x3DB000003DC,
+ 0x3DD000003DE,
+ 0x3DF000003E0,
+ 0x3E1000003E2,
+ 0x3E3000003E4,
+ 0x3E5000003E6,
+ 0x3E7000003E8,
+ 0x3E9000003EA,
+ 0x3EB000003EC,
+ 0x3ED000003EE,
+ 0x3EF000003F0,
+ 0x3F3000003F4,
+ 0x3F8000003F9,
+ 0x3FB000003FD,
+ 0x43000000460,
+ 0x46100000462,
+ 0x46300000464,
+ 0x46500000466,
+ 0x46700000468,
+ 0x4690000046A,
+ 0x46B0000046C,
+ 0x46D0000046E,
+ 0x46F00000470,
+ 0x47100000472,
+ 0x47300000474,
+ 0x47500000476,
+ 0x47700000478,
+ 0x4790000047A,
+ 0x47B0000047C,
+ 0x47D0000047E,
+ 0x47F00000480,
+ 0x48100000482,
+ 0x48300000488,
+ 0x48B0000048C,
+ 0x48D0000048E,
+ 0x48F00000490,
+ 0x49100000492,
+ 0x49300000494,
+ 0x49500000496,
+ 0x49700000498,
+ 0x4990000049A,
+ 0x49B0000049C,
+ 0x49D0000049E,
+ 0x49F000004A0,
+ 0x4A1000004A2,
+ 0x4A3000004A4,
+ 0x4A5000004A6,
+ 0x4A7000004A8,
+ 0x4A9000004AA,
+ 0x4AB000004AC,
+ 0x4AD000004AE,
+ 0x4AF000004B0,
+ 0x4B1000004B2,
+ 0x4B3000004B4,
+ 0x4B5000004B6,
+ 0x4B7000004B8,
+ 0x4B9000004BA,
+ 0x4BB000004BC,
+ 0x4BD000004BE,
+ 0x4BF000004C0,
+ 0x4C2000004C3,
+ 0x4C4000004C5,
+ 0x4C6000004C7,
+ 0x4C8000004C9,
+ 0x4CA000004CB,
+ 0x4CC000004CD,
+ 0x4CE000004D0,
+ 0x4D1000004D2,
+ 0x4D3000004D4,
+ 0x4D5000004D6,
+ 0x4D7000004D8,
+ 0x4D9000004DA,
+ 0x4DB000004DC,
+ 0x4DD000004DE,
+ 0x4DF000004E0,
+ 0x4E1000004E2,
+ 0x4E3000004E4,
+ 0x4E5000004E6,
+ 0x4E7000004E8,
+ 0x4E9000004EA,
+ 0x4EB000004EC,
+ 0x4ED000004EE,
+ 0x4EF000004F0,
+ 0x4F1000004F2,
+ 0x4F3000004F4,
+ 0x4F5000004F6,
+ 0x4F7000004F8,
+ 0x4F9000004FA,
+ 0x4FB000004FC,
+ 0x4FD000004FE,
+ 0x4FF00000500,
+ 0x50100000502,
+ 0x50300000504,
+ 0x50500000506,
+ 0x50700000508,
+ 0x5090000050A,
+ 0x50B0000050C,
+ 0x50D0000050E,
+ 0x50F00000510,
+ 0x51100000512,
+ 0x51300000514,
+ 0x51500000516,
+ 0x51700000518,
+ 0x5190000051A,
+ 0x51B0000051C,
+ 0x51D0000051E,
+ 0x51F00000520,
+ 0x52100000522,
+ 0x52300000524,
+ 0x52500000526,
+ 0x52700000528,
+ 0x5290000052A,
+ 0x52B0000052C,
+ 0x52D0000052E,
+ 0x52F00000530,
+ 0x5590000055A,
+ 0x56000000587,
+ 0x58800000589,
+ 0x591000005BE,
+ 0x5BF000005C0,
+ 0x5C1000005C3,
+ 0x5C4000005C6,
+ 0x5C7000005C8,
+ 0x5D0000005EB,
+ 0x5EF000005F3,
+ 0x6100000061B,
+ 0x62000000640,
+ 0x64100000660,
+ 0x66E00000675,
+ 0x679000006D4,
+ 0x6D5000006DD,
+ 0x6DF000006E9,
+ 0x6EA000006F0,
+ 0x6FA00000700,
+ 0x7100000074B,
+ 0x74D000007B2,
+ 0x7C0000007F6,
+ 0x7FD000007FE,
+ 0x8000000082E,
+ 0x8400000085C,
+ 0x8600000086B,
+ 0x87000000888,
+ 0x8890000088F,
+ 0x897000008E2,
+ 0x8E300000958,
+ 0x96000000964,
+ 0x96600000970,
+ 0x97100000984,
+ 0x9850000098D,
+ 0x98F00000991,
+ 0x993000009A9,
+ 0x9AA000009B1,
+ 0x9B2000009B3,
+ 0x9B6000009BA,
+ 0x9BC000009C5,
+ 0x9C7000009C9,
+ 0x9CB000009CF,
+ 0x9D7000009D8,
+ 0x9E0000009E4,
+ 0x9E6000009F2,
+ 0x9FC000009FD,
+ 0x9FE000009FF,
+ 0xA0100000A04,
+ 0xA0500000A0B,
+ 0xA0F00000A11,
+ 0xA1300000A29,
+ 0xA2A00000A31,
+ 0xA3200000A33,
+ 0xA3500000A36,
+ 0xA3800000A3A,
+ 0xA3C00000A3D,
+ 0xA3E00000A43,
+ 0xA4700000A49,
+ 0xA4B00000A4E,
+ 0xA5100000A52,
+ 0xA5C00000A5D,
+ 0xA6600000A76,
+ 0xA8100000A84,
+ 0xA8500000A8E,
+ 0xA8F00000A92,
+ 0xA9300000AA9,
+ 0xAAA00000AB1,
+ 0xAB200000AB4,
+ 0xAB500000ABA,
+ 0xABC00000AC6,
+ 0xAC700000ACA,
+ 0xACB00000ACE,
+ 0xAD000000AD1,
+ 0xAE000000AE4,
+ 0xAE600000AF0,
+ 0xAF900000B00,
+ 0xB0100000B04,
+ 0xB0500000B0D,
+ 0xB0F00000B11,
+ 0xB1300000B29,
+ 0xB2A00000B31,
+ 0xB3200000B34,
+ 0xB3500000B3A,
+ 0xB3C00000B45,
+ 0xB4700000B49,
+ 0xB4B00000B4E,
+ 0xB5500000B58,
+ 0xB5F00000B64,
+ 0xB6600000B70,
+ 0xB7100000B72,
+ 0xB8200000B84,
+ 0xB8500000B8B,
+ 0xB8E00000B91,
+ 0xB9200000B96,
+ 0xB9900000B9B,
+ 0xB9C00000B9D,
+ 0xB9E00000BA0,
+ 0xBA300000BA5,
+ 0xBA800000BAB,
+ 0xBAE00000BBA,
+ 0xBBE00000BC3,
+ 0xBC600000BC9,
+ 0xBCA00000BCE,
+ 0xBD000000BD1,
+ 0xBD700000BD8,
+ 0xBE600000BF0,
+ 0xC0000000C0D,
+ 0xC0E00000C11,
+ 0xC1200000C29,
+ 0xC2A00000C3A,
+ 0xC3C00000C45,
+ 0xC4600000C49,
+ 0xC4A00000C4E,
+ 0xC5500000C57,
+ 0xC5800000C5B,
+ 0xC5D00000C5E,
+ 0xC6000000C64,
+ 0xC6600000C70,
+ 0xC8000000C84,
+ 0xC8500000C8D,
+ 0xC8E00000C91,
+ 0xC9200000CA9,
+ 0xCAA00000CB4,
+ 0xCB500000CBA,
+ 0xCBC00000CC5,
+ 0xCC600000CC9,
+ 0xCCA00000CCE,
+ 0xCD500000CD7,
+ 0xCDD00000CDF,
+ 0xCE000000CE4,
+ 0xCE600000CF0,
+ 0xCF100000CF4,
+ 0xD0000000D0D,
+ 0xD0E00000D11,
+ 0xD1200000D45,
+ 0xD4600000D49,
+ 0xD4A00000D4F,
+ 0xD5400000D58,
+ 0xD5F00000D64,
+ 0xD6600000D70,
+ 0xD7A00000D80,
+ 0xD8100000D84,
+ 0xD8500000D97,
+ 0xD9A00000DB2,
+ 0xDB300000DBC,
+ 0xDBD00000DBE,
+ 0xDC000000DC7,
+ 0xDCA00000DCB,
+ 0xDCF00000DD5,
+ 0xDD600000DD7,
+ 0xDD800000DE0,
+ 0xDE600000DF0,
+ 0xDF200000DF4,
+ 0xE0100000E33,
+ 0xE3400000E3B,
+ 0xE4000000E4F,
+ 0xE5000000E5A,
+ 0xE8100000E83,
+ 0xE8400000E85,
+ 0xE8600000E8B,
+ 0xE8C00000EA4,
+ 0xEA500000EA6,
+ 0xEA700000EB3,
+ 0xEB400000EBE,
+ 0xEC000000EC5,
+ 0xEC600000EC7,
+ 0xEC800000ECF,
+ 0xED000000EDA,
+ 0xEDE00000EE0,
+ 0xF0000000F01,
+ 0xF0B00000F0C,
+ 0xF1800000F1A,
+ 0xF2000000F2A,
+ 0xF3500000F36,
+ 0xF3700000F38,
+ 0xF3900000F3A,
+ 0xF3E00000F43,
+ 0xF4400000F48,
+ 0xF4900000F4D,
+ 0xF4E00000F52,
+ 0xF5300000F57,
+ 0xF5800000F5C,
+ 0xF5D00000F69,
+ 0xF6A00000F6D,
+ 0xF7100000F73,
+ 0xF7400000F75,
+ 0xF7A00000F81,
+ 0xF8200000F85,
+ 0xF8600000F93,
+ 0xF9400000F98,
+ 0xF9900000F9D,
+ 0xF9E00000FA2,
+ 0xFA300000FA7,
+ 0xFA800000FAC,
+ 0xFAD00000FB9,
+ 0xFBA00000FBD,
+ 0xFC600000FC7,
+ 0x10000000104A,
+ 0x10500000109E,
+ 0x10D0000010FB,
+ 0x10FD00001100,
+ 0x120000001249,
+ 0x124A0000124E,
+ 0x125000001257,
+ 0x125800001259,
+ 0x125A0000125E,
+ 0x126000001289,
+ 0x128A0000128E,
+ 0x1290000012B1,
+ 0x12B2000012B6,
+ 0x12B8000012BF,
+ 0x12C0000012C1,
+ 0x12C2000012C6,
+ 0x12C8000012D7,
+ 0x12D800001311,
+ 0x131200001316,
+ 0x13180000135B,
+ 0x135D00001360,
+ 0x138000001390,
+ 0x13A0000013F6,
+ 0x14010000166D,
+ 0x166F00001680,
+ 0x16810000169B,
+ 0x16A0000016EB,
+ 0x16F1000016F9,
+ 0x170000001716,
+ 0x171F00001735,
+ 0x174000001754,
+ 0x17600000176D,
+ 0x176E00001771,
+ 0x177200001774,
+ 0x1780000017B4,
+ 0x17B6000017D4,
+ 0x17D7000017D8,
+ 0x17DC000017DE,
+ 0x17E0000017EA,
+ 0x18100000181A,
+ 0x182000001879,
+ 0x1880000018AB,
+ 0x18B0000018F6,
+ 0x19000000191F,
+ 0x19200000192C,
+ 0x19300000193C,
+ 0x19460000196E,
+ 0x197000001975,
+ 0x1980000019AC,
+ 0x19B0000019CA,
+ 0x19D0000019DA,
+ 0x1A0000001A1C,
+ 0x1A2000001A5F,
+ 0x1A6000001A7D,
+ 0x1A7F00001A8A,
+ 0x1A9000001A9A,
+ 0x1AA700001AA8,
+ 0x1AB000001ABE,
+ 0x1ABF00001ACF,
+ 0x1B0000001B4D,
+ 0x1B5000001B5A,
+ 0x1B6B00001B74,
+ 0x1B8000001BF4,
+ 0x1C0000001C38,
+ 0x1C4000001C4A,
+ 0x1C4D00001C7E,
+ 0x1C8A00001C8B,
+ 0x1CD000001CD3,
+ 0x1CD400001CFB,
+ 0x1D0000001D2C,
+ 0x1D2F00001D30,
+ 0x1D3B00001D3C,
+ 0x1D4E00001D4F,
+ 0x1D6B00001D78,
+ 0x1D7900001D9B,
+ 0x1DC000001E00,
+ 0x1E0100001E02,
+ 0x1E0300001E04,
+ 0x1E0500001E06,
+ 0x1E0700001E08,
+ 0x1E0900001E0A,
+ 0x1E0B00001E0C,
+ 0x1E0D00001E0E,
+ 0x1E0F00001E10,
+ 0x1E1100001E12,
+ 0x1E1300001E14,
+ 0x1E1500001E16,
+ 0x1E1700001E18,
+ 0x1E1900001E1A,
+ 0x1E1B00001E1C,
+ 0x1E1D00001E1E,
+ 0x1E1F00001E20,
+ 0x1E2100001E22,
+ 0x1E2300001E24,
+ 0x1E2500001E26,
+ 0x1E2700001E28,
+ 0x1E2900001E2A,
+ 0x1E2B00001E2C,
+ 0x1E2D00001E2E,
+ 0x1E2F00001E30,
+ 0x1E3100001E32,
+ 0x1E3300001E34,
+ 0x1E3500001E36,
+ 0x1E3700001E38,
+ 0x1E3900001E3A,
+ 0x1E3B00001E3C,
+ 0x1E3D00001E3E,
+ 0x1E3F00001E40,
+ 0x1E4100001E42,
+ 0x1E4300001E44,
+ 0x1E4500001E46,
+ 0x1E4700001E48,
+ 0x1E4900001E4A,
+ 0x1E4B00001E4C,
+ 0x1E4D00001E4E,
+ 0x1E4F00001E50,
+ 0x1E5100001E52,
+ 0x1E5300001E54,
+ 0x1E5500001E56,
+ 0x1E5700001E58,
+ 0x1E5900001E5A,
+ 0x1E5B00001E5C,
+ 0x1E5D00001E5E,
+ 0x1E5F00001E60,
+ 0x1E6100001E62,
+ 0x1E6300001E64,
+ 0x1E6500001E66,
+ 0x1E6700001E68,
+ 0x1E6900001E6A,
+ 0x1E6B00001E6C,
+ 0x1E6D00001E6E,
+ 0x1E6F00001E70,
+ 0x1E7100001E72,
+ 0x1E7300001E74,
+ 0x1E7500001E76,
+ 0x1E7700001E78,
+ 0x1E7900001E7A,
+ 0x1E7B00001E7C,
+ 0x1E7D00001E7E,
+ 0x1E7F00001E80,
+ 0x1E8100001E82,
+ 0x1E8300001E84,
+ 0x1E8500001E86,
+ 0x1E8700001E88,
+ 0x1E8900001E8A,
+ 0x1E8B00001E8C,
+ 0x1E8D00001E8E,
+ 0x1E8F00001E90,
+ 0x1E9100001E92,
+ 0x1E9300001E94,
+ 0x1E9500001E9A,
+ 0x1E9C00001E9E,
+ 0x1E9F00001EA0,
+ 0x1EA100001EA2,
+ 0x1EA300001EA4,
+ 0x1EA500001EA6,
+ 0x1EA700001EA8,
+ 0x1EA900001EAA,
+ 0x1EAB00001EAC,
+ 0x1EAD00001EAE,
+ 0x1EAF00001EB0,
+ 0x1EB100001EB2,
+ 0x1EB300001EB4,
+ 0x1EB500001EB6,
+ 0x1EB700001EB8,
+ 0x1EB900001EBA,
+ 0x1EBB00001EBC,
+ 0x1EBD00001EBE,
+ 0x1EBF00001EC0,
+ 0x1EC100001EC2,
+ 0x1EC300001EC4,
+ 0x1EC500001EC6,
+ 0x1EC700001EC8,
+ 0x1EC900001ECA,
+ 0x1ECB00001ECC,
+ 0x1ECD00001ECE,
+ 0x1ECF00001ED0,
+ 0x1ED100001ED2,
+ 0x1ED300001ED4,
+ 0x1ED500001ED6,
+ 0x1ED700001ED8,
+ 0x1ED900001EDA,
+ 0x1EDB00001EDC,
+ 0x1EDD00001EDE,
+ 0x1EDF00001EE0,
+ 0x1EE100001EE2,
+ 0x1EE300001EE4,
+ 0x1EE500001EE6,
+ 0x1EE700001EE8,
+ 0x1EE900001EEA,
+ 0x1EEB00001EEC,
+ 0x1EED00001EEE,
+ 0x1EEF00001EF0,
+ 0x1EF100001EF2,
+ 0x1EF300001EF4,
+ 0x1EF500001EF6,
+ 0x1EF700001EF8,
+ 0x1EF900001EFA,
+ 0x1EFB00001EFC,
+ 0x1EFD00001EFE,
+ 0x1EFF00001F08,
+ 0x1F1000001F16,
+ 0x1F2000001F28,
+ 0x1F3000001F38,
+ 0x1F4000001F46,
+ 0x1F5000001F58,
+ 0x1F6000001F68,
+ 0x1F7000001F71,
+ 0x1F7200001F73,
+ 0x1F7400001F75,
+ 0x1F7600001F77,
+ 0x1F7800001F79,
+ 0x1F7A00001F7B,
+ 0x1F7C00001F7D,
+ 0x1FB000001FB2,
+ 0x1FB600001FB7,
+ 0x1FC600001FC7,
+ 0x1FD000001FD3,
+ 0x1FD600001FD8,
+ 0x1FE000001FE3,
+ 0x1FE400001FE8,
+ 0x1FF600001FF7,
+ 0x214E0000214F,
+ 0x218400002185,
+ 0x2C3000002C60,
+ 0x2C6100002C62,
+ 0x2C6500002C67,
+ 0x2C6800002C69,
+ 0x2C6A00002C6B,
+ 0x2C6C00002C6D,
+ 0x2C7100002C72,
+ 0x2C7300002C75,
+ 0x2C7600002C7C,
+ 0x2C8100002C82,
+ 0x2C8300002C84,
+ 0x2C8500002C86,
+ 0x2C8700002C88,
+ 0x2C8900002C8A,
+ 0x2C8B00002C8C,
+ 0x2C8D00002C8E,
+ 0x2C8F00002C90,
+ 0x2C9100002C92,
+ 0x2C9300002C94,
+ 0x2C9500002C96,
+ 0x2C9700002C98,
+ 0x2C9900002C9A,
+ 0x2C9B00002C9C,
+ 0x2C9D00002C9E,
+ 0x2C9F00002CA0,
+ 0x2CA100002CA2,
+ 0x2CA300002CA4,
+ 0x2CA500002CA6,
+ 0x2CA700002CA8,
+ 0x2CA900002CAA,
+ 0x2CAB00002CAC,
+ 0x2CAD00002CAE,
+ 0x2CAF00002CB0,
+ 0x2CB100002CB2,
+ 0x2CB300002CB4,
+ 0x2CB500002CB6,
+ 0x2CB700002CB8,
+ 0x2CB900002CBA,
+ 0x2CBB00002CBC,
+ 0x2CBD00002CBE,
+ 0x2CBF00002CC0,
+ 0x2CC100002CC2,
+ 0x2CC300002CC4,
+ 0x2CC500002CC6,
+ 0x2CC700002CC8,
+ 0x2CC900002CCA,
+ 0x2CCB00002CCC,
+ 0x2CCD00002CCE,
+ 0x2CCF00002CD0,
+ 0x2CD100002CD2,
+ 0x2CD300002CD4,
+ 0x2CD500002CD6,
+ 0x2CD700002CD8,
+ 0x2CD900002CDA,
+ 0x2CDB00002CDC,
+ 0x2CDD00002CDE,
+ 0x2CDF00002CE0,
+ 0x2CE100002CE2,
+ 0x2CE300002CE5,
+ 0x2CEC00002CED,
+ 0x2CEE00002CF2,
+ 0x2CF300002CF4,
+ 0x2D0000002D26,
+ 0x2D2700002D28,
+ 0x2D2D00002D2E,
+ 0x2D3000002D68,
+ 0x2D7F00002D97,
+ 0x2DA000002DA7,
+ 0x2DA800002DAF,
+ 0x2DB000002DB7,
+ 0x2DB800002DBF,
+ 0x2DC000002DC7,
+ 0x2DC800002DCF,
+ 0x2DD000002DD7,
+ 0x2DD800002DDF,
+ 0x2DE000002E00,
+ 0x2E2F00002E30,
+ 0x300500003008,
+ 0x302A0000302E,
+ 0x303C0000303D,
+ 0x304100003097,
+ 0x30990000309B,
+ 0x309D0000309F,
+ 0x30A1000030FB,
+ 0x30FC000030FF,
+ 0x310500003130,
+ 0x31A0000031C0,
+ 0x31F000003200,
+ 0x340000004DC0,
+ 0x4E000000A48D,
+ 0xA4D00000A4FE,
+ 0xA5000000A60D,
+ 0xA6100000A62C,
+ 0xA6410000A642,
+ 0xA6430000A644,
+ 0xA6450000A646,
+ 0xA6470000A648,
+ 0xA6490000A64A,
+ 0xA64B0000A64C,
+ 0xA64D0000A64E,
+ 0xA64F0000A650,
+ 0xA6510000A652,
+ 0xA6530000A654,
+ 0xA6550000A656,
+ 0xA6570000A658,
+ 0xA6590000A65A,
+ 0xA65B0000A65C,
+ 0xA65D0000A65E,
+ 0xA65F0000A660,
+ 0xA6610000A662,
+ 0xA6630000A664,
+ 0xA6650000A666,
+ 0xA6670000A668,
+ 0xA6690000A66A,
+ 0xA66B0000A66C,
+ 0xA66D0000A670,
+ 0xA6740000A67E,
+ 0xA67F0000A680,
+ 0xA6810000A682,
+ 0xA6830000A684,
+ 0xA6850000A686,
+ 0xA6870000A688,
+ 0xA6890000A68A,
+ 0xA68B0000A68C,
+ 0xA68D0000A68E,
+ 0xA68F0000A690,
+ 0xA6910000A692,
+ 0xA6930000A694,
+ 0xA6950000A696,
+ 0xA6970000A698,
+ 0xA6990000A69A,
+ 0xA69B0000A69C,
+ 0xA69E0000A6E6,
+ 0xA6F00000A6F2,
+ 0xA7170000A720,
+ 0xA7230000A724,
+ 0xA7250000A726,
+ 0xA7270000A728,
+ 0xA7290000A72A,
+ 0xA72B0000A72C,
+ 0xA72D0000A72E,
+ 0xA72F0000A732,
+ 0xA7330000A734,
+ 0xA7350000A736,
+ 0xA7370000A738,
+ 0xA7390000A73A,
+ 0xA73B0000A73C,
+ 0xA73D0000A73E,
+ 0xA73F0000A740,
+ 0xA7410000A742,
+ 0xA7430000A744,
+ 0xA7450000A746,
+ 0xA7470000A748,
+ 0xA7490000A74A,
+ 0xA74B0000A74C,
+ 0xA74D0000A74E,
+ 0xA74F0000A750,
+ 0xA7510000A752,
+ 0xA7530000A754,
+ 0xA7550000A756,
+ 0xA7570000A758,
+ 0xA7590000A75A,
+ 0xA75B0000A75C,
+ 0xA75D0000A75E,
+ 0xA75F0000A760,
+ 0xA7610000A762,
+ 0xA7630000A764,
+ 0xA7650000A766,
+ 0xA7670000A768,
+ 0xA7690000A76A,
+ 0xA76B0000A76C,
+ 0xA76D0000A76E,
+ 0xA76F0000A770,
+ 0xA7710000A779,
+ 0xA77A0000A77B,
+ 0xA77C0000A77D,
+ 0xA77F0000A780,
+ 0xA7810000A782,
+ 0xA7830000A784,
+ 0xA7850000A786,
+ 0xA7870000A789,
+ 0xA78C0000A78D,
+ 0xA78E0000A790,
+ 0xA7910000A792,
+ 0xA7930000A796,
+ 0xA7970000A798,
+ 0xA7990000A79A,
+ 0xA79B0000A79C,
+ 0xA79D0000A79E,
+ 0xA79F0000A7A0,
+ 0xA7A10000A7A2,
+ 0xA7A30000A7A4,
+ 0xA7A50000A7A6,
+ 0xA7A70000A7A8,
+ 0xA7A90000A7AA,
+ 0xA7AF0000A7B0,
+ 0xA7B50000A7B6,
+ 0xA7B70000A7B8,
+ 0xA7B90000A7BA,
+ 0xA7BB0000A7BC,
+ 0xA7BD0000A7BE,
+ 0xA7BF0000A7C0,
+ 0xA7C10000A7C2,
+ 0xA7C30000A7C4,
+ 0xA7C80000A7C9,
+ 0xA7CA0000A7CB,
+ 0xA7CD0000A7CE,
+ 0xA7D10000A7D2,
+ 0xA7D30000A7D4,
+ 0xA7D50000A7D6,
+ 0xA7D70000A7D8,
+ 0xA7D90000A7DA,
+ 0xA7DB0000A7DC,
+ 0xA7F60000A7F8,
+ 0xA7FA0000A828,
+ 0xA82C0000A82D,
+ 0xA8400000A874,
+ 0xA8800000A8C6,
+ 0xA8D00000A8DA,
+ 0xA8E00000A8F8,
+ 0xA8FB0000A8FC,
+ 0xA8FD0000A92E,
+ 0xA9300000A954,
+ 0xA9800000A9C1,
+ 0xA9CF0000A9DA,
+ 0xA9E00000A9FF,
+ 0xAA000000AA37,
+ 0xAA400000AA4E,
+ 0xAA500000AA5A,
+ 0xAA600000AA77,
+ 0xAA7A0000AAC3,
+ 0xAADB0000AADE,
+ 0xAAE00000AAF0,
+ 0xAAF20000AAF7,
+ 0xAB010000AB07,
+ 0xAB090000AB0F,
+ 0xAB110000AB17,
+ 0xAB200000AB27,
+ 0xAB280000AB2F,
+ 0xAB300000AB5B,
+ 0xAB600000AB69,
+ 0xABC00000ABEB,
+ 0xABEC0000ABEE,
+ 0xABF00000ABFA,
+ 0xAC000000D7A4,
+ 0xFA0E0000FA10,
+ 0xFA110000FA12,
+ 0xFA130000FA15,
+ 0xFA1F0000FA20,
+ 0xFA210000FA22,
+ 0xFA230000FA25,
+ 0xFA270000FA2A,
+ 0xFB1E0000FB1F,
+ 0xFE200000FE30,
+ 0xFE730000FE74,
+ 0x100000001000C,
+ 0x1000D00010027,
+ 0x100280001003B,
+ 0x1003C0001003E,
+ 0x1003F0001004E,
+ 0x100500001005E,
+ 0x10080000100FB,
+ 0x101FD000101FE,
+ 0x102800001029D,
+ 0x102A0000102D1,
+ 0x102E0000102E1,
+ 0x1030000010320,
+ 0x1032D00010341,
+ 0x103420001034A,
+ 0x103500001037B,
+ 0x103800001039E,
+ 0x103A0000103C4,
+ 0x103C8000103D0,
+ 0x104280001049E,
+ 0x104A0000104AA,
+ 0x104D8000104FC,
+ 0x1050000010528,
+ 0x1053000010564,
+ 0x10597000105A2,
+ 0x105A3000105B2,
+ 0x105B3000105BA,
+ 0x105BB000105BD,
+ 0x105C0000105F4,
+ 0x1060000010737,
+ 0x1074000010756,
+ 0x1076000010768,
+ 0x1078000010781,
+ 0x1080000010806,
+ 0x1080800010809,
+ 0x1080A00010836,
+ 0x1083700010839,
+ 0x1083C0001083D,
+ 0x1083F00010856,
+ 0x1086000010877,
+ 0x108800001089F,
+ 0x108E0000108F3,
+ 0x108F4000108F6,
+ 0x1090000010916,
+ 0x109200001093A,
+ 0x10980000109B8,
+ 0x109BE000109C0,
+ 0x10A0000010A04,
+ 0x10A0500010A07,
+ 0x10A0C00010A14,
+ 0x10A1500010A18,
+ 0x10A1900010A36,
+ 0x10A3800010A3B,
+ 0x10A3F00010A40,
+ 0x10A6000010A7D,
+ 0x10A8000010A9D,
+ 0x10AC000010AC8,
+ 0x10AC900010AE7,
+ 0x10B0000010B36,
+ 0x10B4000010B56,
+ 0x10B6000010B73,
+ 0x10B8000010B92,
+ 0x10C0000010C49,
+ 0x10CC000010CF3,
+ 0x10D0000010D28,
+ 0x10D3000010D3A,
+ 0x10D4000010D50,
+ 0x10D6900010D6E,
+ 0x10D6F00010D86,
+ 0x10E8000010EAA,
+ 0x10EAB00010EAD,
+ 0x10EB000010EB2,
+ 0x10EC200010EC5,
+ 0x10EFC00010F1D,
+ 0x10F2700010F28,
+ 0x10F3000010F51,
+ 0x10F7000010F86,
+ 0x10FB000010FC5,
+ 0x10FE000010FF7,
+ 0x1100000011047,
+ 0x1106600011076,
+ 0x1107F000110BB,
+ 0x110C2000110C3,
+ 0x110D0000110E9,
+ 0x110F0000110FA,
+ 0x1110000011135,
+ 0x1113600011140,
+ 0x1114400011148,
+ 0x1115000011174,
+ 0x1117600011177,
+ 0x11180000111C5,
+ 0x111C9000111CD,
+ 0x111CE000111DB,
+ 0x111DC000111DD,
+ 0x1120000011212,
+ 0x1121300011238,
+ 0x1123E00011242,
+ 0x1128000011287,
+ 0x1128800011289,
+ 0x1128A0001128E,
+ 0x1128F0001129E,
+ 0x1129F000112A9,
+ 0x112B0000112EB,
+ 0x112F0000112FA,
+ 0x1130000011304,
+ 0x113050001130D,
+ 0x1130F00011311,
+ 0x1131300011329,
+ 0x1132A00011331,
+ 0x1133200011334,
+ 0x113350001133A,
+ 0x1133B00011345,
+ 0x1134700011349,
+ 0x1134B0001134E,
+ 0x1135000011351,
+ 0x1135700011358,
+ 0x1135D00011364,
+ 0x113660001136D,
+ 0x1137000011375,
+ 0x113800001138A,
+ 0x1138B0001138C,
+ 0x1138E0001138F,
+ 0x11390000113B6,
+ 0x113B7000113C1,
+ 0x113C2000113C3,
+ 0x113C5000113C6,
+ 0x113C7000113CB,
+ 0x113CC000113D4,
+ 0x113E1000113E3,
+ 0x114000001144B,
+ 0x114500001145A,
+ 0x1145E00011462,
+ 0x11480000114C6,
+ 0x114C7000114C8,
+ 0x114D0000114DA,
+ 0x11580000115B6,
+ 0x115B8000115C1,
+ 0x115D8000115DE,
+ 0x1160000011641,
+ 0x1164400011645,
+ 0x116500001165A,
+ 0x11680000116B9,
+ 0x116C0000116CA,
+ 0x116D0000116E4,
+ 0x117000001171B,
+ 0x1171D0001172C,
+ 0x117300001173A,
+ 0x1174000011747,
+ 0x118000001183B,
+ 0x118C0000118EA,
+ 0x118FF00011907,
+ 0x119090001190A,
+ 0x1190C00011914,
+ 0x1191500011917,
+ 0x1191800011936,
+ 0x1193700011939,
+ 0x1193B00011944,
+ 0x119500001195A,
+ 0x119A0000119A8,
+ 0x119AA000119D8,
+ 0x119DA000119E2,
+ 0x119E3000119E5,
+ 0x11A0000011A3F,
+ 0x11A4700011A48,
+ 0x11A5000011A9A,
+ 0x11A9D00011A9E,
+ 0x11AB000011AF9,
+ 0x11BC000011BE1,
+ 0x11BF000011BFA,
+ 0x11C0000011C09,
+ 0x11C0A00011C37,
+ 0x11C3800011C41,
+ 0x11C5000011C5A,
+ 0x11C7200011C90,
+ 0x11C9200011CA8,
+ 0x11CA900011CB7,
+ 0x11D0000011D07,
+ 0x11D0800011D0A,
+ 0x11D0B00011D37,
+ 0x11D3A00011D3B,
+ 0x11D3C00011D3E,
+ 0x11D3F00011D48,
+ 0x11D5000011D5A,
+ 0x11D6000011D66,
+ 0x11D6700011D69,
+ 0x11D6A00011D8F,
+ 0x11D9000011D92,
+ 0x11D9300011D99,
+ 0x11DA000011DAA,
+ 0x11EE000011EF7,
+ 0x11F0000011F11,
+ 0x11F1200011F3B,
+ 0x11F3E00011F43,
+ 0x11F5000011F5B,
+ 0x11FB000011FB1,
+ 0x120000001239A,
+ 0x1248000012544,
+ 0x12F9000012FF1,
+ 0x1300000013430,
+ 0x1344000013456,
+ 0x13460000143FB,
+ 0x1440000014647,
+ 0x161000001613A,
+ 0x1680000016A39,
+ 0x16A4000016A5F,
+ 0x16A6000016A6A,
+ 0x16A7000016ABF,
+ 0x16AC000016ACA,
+ 0x16AD000016AEE,
+ 0x16AF000016AF5,
+ 0x16B0000016B37,
+ 0x16B4000016B44,
+ 0x16B5000016B5A,
+ 0x16B6300016B78,
+ 0x16B7D00016B90,
+ 0x16D4000016D6D,
+ 0x16D7000016D7A,
+ 0x16E6000016E80,
+ 0x16F0000016F4B,
+ 0x16F4F00016F88,
+ 0x16F8F00016FA0,
+ 0x16FE000016FE2,
+ 0x16FE300016FE5,
+ 0x16FF000016FF2,
+ 0x17000000187F8,
+ 0x1880000018CD6,
+ 0x18CFF00018D09,
+ 0x1AFF00001AFF4,
+ 0x1AFF50001AFFC,
+ 0x1AFFD0001AFFF,
+ 0x1B0000001B123,
+ 0x1B1320001B133,
+ 0x1B1500001B153,
+ 0x1B1550001B156,
+ 0x1B1640001B168,
+ 0x1B1700001B2FC,
+ 0x1BC000001BC6B,
+ 0x1BC700001BC7D,
+ 0x1BC800001BC89,
+ 0x1BC900001BC9A,
+ 0x1BC9D0001BC9F,
+ 0x1CCF00001CCFA,
+ 0x1CF000001CF2E,
+ 0x1CF300001CF47,
+ 0x1DA000001DA37,
+ 0x1DA3B0001DA6D,
+ 0x1DA750001DA76,
+ 0x1DA840001DA85,
+ 0x1DA9B0001DAA0,
+ 0x1DAA10001DAB0,
+ 0x1DF000001DF1F,
+ 0x1DF250001DF2B,
+ 0x1E0000001E007,
+ 0x1E0080001E019,
+ 0x1E01B0001E022,
+ 0x1E0230001E025,
+ 0x1E0260001E02B,
+ 0x1E08F0001E090,
+ 0x1E1000001E12D,
+ 0x1E1300001E13E,
+ 0x1E1400001E14A,
+ 0x1E14E0001E14F,
+ 0x1E2900001E2AF,
+ 0x1E2C00001E2FA,
+ 0x1E4D00001E4FA,
+ 0x1E5D00001E5FB,
+ 0x1E7E00001E7E7,
+ 0x1E7E80001E7EC,
+ 0x1E7ED0001E7EF,
+ 0x1E7F00001E7FF,
+ 0x1E8000001E8C5,
+ 0x1E8D00001E8D7,
+ 0x1E9220001E94C,
+ 0x1E9500001E95A,
+ 0x200000002A6E0,
+ 0x2A7000002B73A,
+ 0x2B7400002B81E,
+ 0x2B8200002CEA2,
+ 0x2CEB00002EBE1,
+ 0x2EBF00002EE5E,
+ 0x300000003134B,
+ 0x31350000323B0,
+ ),
+ "CONTEXTJ": (0x200C0000200E,),
+ "CONTEXTO": (
+ 0xB7000000B8,
+ 0x37500000376,
+ 0x5F3000005F5,
+ 0x6600000066A,
+ 0x6F0000006FA,
+ 0x30FB000030FC,
+ ),
+}
diff --git a/venv/lib/python3.10/site-packages/idna/intranges.py b/venv/lib/python3.10/site-packages/idna/intranges.py
new file mode 100644
index 0000000..7bfaa8d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/intranges.py
@@ -0,0 +1,57 @@
+"""
+Given a list of integers, made up of (hopefully) a small number of long runs
+of consecutive integers, compute a representation of the form
+((start1, end1), (start2, end2) ...). Then answer the question "was x present
+in the original list?" in time O(log(# runs)).
+"""
+
+import bisect
+from typing import List, Tuple
+
+
+def intranges_from_list(list_: List[int]) -> Tuple[int, ...]:
+ """Represent a list of integers as a sequence of ranges:
+ ((start_0, end_0), (start_1, end_1), ...), such that the original
+ integers are exactly those x such that start_i <= x < end_i for some i.
+
+ Ranges are encoded as single integers (start << 32 | end), not as tuples.
+ """
+
+ sorted_list = sorted(list_)
+ ranges = []
+ last_write = -1
+ for i in range(len(sorted_list)):
+ if i + 1 < len(sorted_list):
+ if sorted_list[i] == sorted_list[i + 1] - 1:
+ continue
+ current_range = sorted_list[last_write + 1 : i + 1]
+ ranges.append(_encode_range(current_range[0], current_range[-1] + 1))
+ last_write = i
+
+ return tuple(ranges)
+
+
+def _encode_range(start: int, end: int) -> int:
+ return (start << 32) | end
+
+
+def _decode_range(r: int) -> Tuple[int, int]:
+ return (r >> 32), (r & ((1 << 32) - 1))
+
+
+def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool:
+ """Determine if `int_` falls into one of the ranges in `ranges`."""
+ tuple_ = _encode_range(int_, 0)
+ pos = bisect.bisect_left(ranges, tuple_)
+ # we could be immediately ahead of a tuple (start, end)
+ # with start < int_ <= end
+ if pos > 0:
+ left, right = _decode_range(ranges[pos - 1])
+ if left <= int_ < right:
+ return True
+ # or we could be immediately behind a tuple (int_, end)
+ if pos < len(ranges):
+ left, _ = _decode_range(ranges[pos])
+ if left == int_:
+ return True
+ return False
diff --git a/venv/lib/python3.10/site-packages/idna/package_data.py b/venv/lib/python3.10/site-packages/idna/package_data.py
new file mode 100644
index 0000000..7272c8d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/package_data.py
@@ -0,0 +1 @@
+__version__ = "3.11"
diff --git a/venv/lib/python3.10/site-packages/idna/py.typed b/venv/lib/python3.10/site-packages/idna/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/idna/uts46data.py b/venv/lib/python3.10/site-packages/idna/uts46data.py
new file mode 100644
index 0000000..4610b71
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/idna/uts46data.py
@@ -0,0 +1,8841 @@
+# This file is automatically generated by tools/idna-data
+# vim: set fileencoding=utf-8 :
+
+from typing import List, Tuple, Union
+
+"""IDNA Mapping Table from UTS46."""
+
+
+__version__ = "16.0.0"
+
+
+def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x0, "V"),
+ (0x1, "V"),
+ (0x2, "V"),
+ (0x3, "V"),
+ (0x4, "V"),
+ (0x5, "V"),
+ (0x6, "V"),
+ (0x7, "V"),
+ (0x8, "V"),
+ (0x9, "V"),
+ (0xA, "V"),
+ (0xB, "V"),
+ (0xC, "V"),
+ (0xD, "V"),
+ (0xE, "V"),
+ (0xF, "V"),
+ (0x10, "V"),
+ (0x11, "V"),
+ (0x12, "V"),
+ (0x13, "V"),
+ (0x14, "V"),
+ (0x15, "V"),
+ (0x16, "V"),
+ (0x17, "V"),
+ (0x18, "V"),
+ (0x19, "V"),
+ (0x1A, "V"),
+ (0x1B, "V"),
+ (0x1C, "V"),
+ (0x1D, "V"),
+ (0x1E, "V"),
+ (0x1F, "V"),
+ (0x20, "V"),
+ (0x21, "V"),
+ (0x22, "V"),
+ (0x23, "V"),
+ (0x24, "V"),
+ (0x25, "V"),
+ (0x26, "V"),
+ (0x27, "V"),
+ (0x28, "V"),
+ (0x29, "V"),
+ (0x2A, "V"),
+ (0x2B, "V"),
+ (0x2C, "V"),
+ (0x2D, "V"),
+ (0x2E, "V"),
+ (0x2F, "V"),
+ (0x30, "V"),
+ (0x31, "V"),
+ (0x32, "V"),
+ (0x33, "V"),
+ (0x34, "V"),
+ (0x35, "V"),
+ (0x36, "V"),
+ (0x37, "V"),
+ (0x38, "V"),
+ (0x39, "V"),
+ (0x3A, "V"),
+ (0x3B, "V"),
+ (0x3C, "V"),
+ (0x3D, "V"),
+ (0x3E, "V"),
+ (0x3F, "V"),
+ (0x40, "V"),
+ (0x41, "M", "a"),
+ (0x42, "M", "b"),
+ (0x43, "M", "c"),
+ (0x44, "M", "d"),
+ (0x45, "M", "e"),
+ (0x46, "M", "f"),
+ (0x47, "M", "g"),
+ (0x48, "M", "h"),
+ (0x49, "M", "i"),
+ (0x4A, "M", "j"),
+ (0x4B, "M", "k"),
+ (0x4C, "M", "l"),
+ (0x4D, "M", "m"),
+ (0x4E, "M", "n"),
+ (0x4F, "M", "o"),
+ (0x50, "M", "p"),
+ (0x51, "M", "q"),
+ (0x52, "M", "r"),
+ (0x53, "M", "s"),
+ (0x54, "M", "t"),
+ (0x55, "M", "u"),
+ (0x56, "M", "v"),
+ (0x57, "M", "w"),
+ (0x58, "M", "x"),
+ (0x59, "M", "y"),
+ (0x5A, "M", "z"),
+ (0x5B, "V"),
+ (0x5C, "V"),
+ (0x5D, "V"),
+ (0x5E, "V"),
+ (0x5F, "V"),
+ (0x60, "V"),
+ (0x61, "V"),
+ (0x62, "V"),
+ (0x63, "V"),
+ ]
+
+
+def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x64, "V"),
+ (0x65, "V"),
+ (0x66, "V"),
+ (0x67, "V"),
+ (0x68, "V"),
+ (0x69, "V"),
+ (0x6A, "V"),
+ (0x6B, "V"),
+ (0x6C, "V"),
+ (0x6D, "V"),
+ (0x6E, "V"),
+ (0x6F, "V"),
+ (0x70, "V"),
+ (0x71, "V"),
+ (0x72, "V"),
+ (0x73, "V"),
+ (0x74, "V"),
+ (0x75, "V"),
+ (0x76, "V"),
+ (0x77, "V"),
+ (0x78, "V"),
+ (0x79, "V"),
+ (0x7A, "V"),
+ (0x7B, "V"),
+ (0x7C, "V"),
+ (0x7D, "V"),
+ (0x7E, "V"),
+ (0x7F, "V"),
+ (0x80, "X"),
+ (0x81, "X"),
+ (0x82, "X"),
+ (0x83, "X"),
+ (0x84, "X"),
+ (0x85, "X"),
+ (0x86, "X"),
+ (0x87, "X"),
+ (0x88, "X"),
+ (0x89, "X"),
+ (0x8A, "X"),
+ (0x8B, "X"),
+ (0x8C, "X"),
+ (0x8D, "X"),
+ (0x8E, "X"),
+ (0x8F, "X"),
+ (0x90, "X"),
+ (0x91, "X"),
+ (0x92, "X"),
+ (0x93, "X"),
+ (0x94, "X"),
+ (0x95, "X"),
+ (0x96, "X"),
+ (0x97, "X"),
+ (0x98, "X"),
+ (0x99, "X"),
+ (0x9A, "X"),
+ (0x9B, "X"),
+ (0x9C, "X"),
+ (0x9D, "X"),
+ (0x9E, "X"),
+ (0x9F, "X"),
+ (0xA0, "M", " "),
+ (0xA1, "V"),
+ (0xA2, "V"),
+ (0xA3, "V"),
+ (0xA4, "V"),
+ (0xA5, "V"),
+ (0xA6, "V"),
+ (0xA7, "V"),
+ (0xA8, "M", " ̈"),
+ (0xA9, "V"),
+ (0xAA, "M", "a"),
+ (0xAB, "V"),
+ (0xAC, "V"),
+ (0xAD, "I"),
+ (0xAE, "V"),
+ (0xAF, "M", " ̄"),
+ (0xB0, "V"),
+ (0xB1, "V"),
+ (0xB2, "M", "2"),
+ (0xB3, "M", "3"),
+ (0xB4, "M", " ́"),
+ (0xB5, "M", "μ"),
+ (0xB6, "V"),
+ (0xB7, "V"),
+ (0xB8, "M", " ̧"),
+ (0xB9, "M", "1"),
+ (0xBA, "M", "o"),
+ (0xBB, "V"),
+ (0xBC, "M", "1⁄4"),
+ (0xBD, "M", "1⁄2"),
+ (0xBE, "M", "3⁄4"),
+ (0xBF, "V"),
+ (0xC0, "M", "à"),
+ (0xC1, "M", "á"),
+ (0xC2, "M", "â"),
+ (0xC3, "M", "ã"),
+ (0xC4, "M", "ä"),
+ (0xC5, "M", "å"),
+ (0xC6, "M", "æ"),
+ (0xC7, "M", "ç"),
+ ]
+
+
+def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xC8, "M", "è"),
+ (0xC9, "M", "é"),
+ (0xCA, "M", "ê"),
+ (0xCB, "M", "ë"),
+ (0xCC, "M", "ì"),
+ (0xCD, "M", "í"),
+ (0xCE, "M", "î"),
+ (0xCF, "M", "ï"),
+ (0xD0, "M", "ð"),
+ (0xD1, "M", "ñ"),
+ (0xD2, "M", "ò"),
+ (0xD3, "M", "ó"),
+ (0xD4, "M", "ô"),
+ (0xD5, "M", "õ"),
+ (0xD6, "M", "ö"),
+ (0xD7, "V"),
+ (0xD8, "M", "ø"),
+ (0xD9, "M", "ù"),
+ (0xDA, "M", "ú"),
+ (0xDB, "M", "û"),
+ (0xDC, "M", "ü"),
+ (0xDD, "M", "ý"),
+ (0xDE, "M", "þ"),
+ (0xDF, "D", "ss"),
+ (0xE0, "V"),
+ (0xE1, "V"),
+ (0xE2, "V"),
+ (0xE3, "V"),
+ (0xE4, "V"),
+ (0xE5, "V"),
+ (0xE6, "V"),
+ (0xE7, "V"),
+ (0xE8, "V"),
+ (0xE9, "V"),
+ (0xEA, "V"),
+ (0xEB, "V"),
+ (0xEC, "V"),
+ (0xED, "V"),
+ (0xEE, "V"),
+ (0xEF, "V"),
+ (0xF0, "V"),
+ (0xF1, "V"),
+ (0xF2, "V"),
+ (0xF3, "V"),
+ (0xF4, "V"),
+ (0xF5, "V"),
+ (0xF6, "V"),
+ (0xF7, "V"),
+ (0xF8, "V"),
+ (0xF9, "V"),
+ (0xFA, "V"),
+ (0xFB, "V"),
+ (0xFC, "V"),
+ (0xFD, "V"),
+ (0xFE, "V"),
+ (0xFF, "V"),
+ (0x100, "M", "ā"),
+ (0x101, "V"),
+ (0x102, "M", "ă"),
+ (0x103, "V"),
+ (0x104, "M", "ą"),
+ (0x105, "V"),
+ (0x106, "M", "ć"),
+ (0x107, "V"),
+ (0x108, "M", "ĉ"),
+ (0x109, "V"),
+ (0x10A, "M", "ċ"),
+ (0x10B, "V"),
+ (0x10C, "M", "č"),
+ (0x10D, "V"),
+ (0x10E, "M", "ď"),
+ (0x10F, "V"),
+ (0x110, "M", "đ"),
+ (0x111, "V"),
+ (0x112, "M", "ē"),
+ (0x113, "V"),
+ (0x114, "M", "ĕ"),
+ (0x115, "V"),
+ (0x116, "M", "ė"),
+ (0x117, "V"),
+ (0x118, "M", "ę"),
+ (0x119, "V"),
+ (0x11A, "M", "ě"),
+ (0x11B, "V"),
+ (0x11C, "M", "ĝ"),
+ (0x11D, "V"),
+ (0x11E, "M", "ğ"),
+ (0x11F, "V"),
+ (0x120, "M", "ġ"),
+ (0x121, "V"),
+ (0x122, "M", "ģ"),
+ (0x123, "V"),
+ (0x124, "M", "ĥ"),
+ (0x125, "V"),
+ (0x126, "M", "ħ"),
+ (0x127, "V"),
+ (0x128, "M", "ĩ"),
+ (0x129, "V"),
+ (0x12A, "M", "ī"),
+ (0x12B, "V"),
+ ]
+
+
+def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x12C, "M", "ĭ"),
+ (0x12D, "V"),
+ (0x12E, "M", "į"),
+ (0x12F, "V"),
+ (0x130, "M", "i̇"),
+ (0x131, "V"),
+ (0x132, "M", "ij"),
+ (0x134, "M", "ĵ"),
+ (0x135, "V"),
+ (0x136, "M", "ķ"),
+ (0x137, "V"),
+ (0x139, "M", "ĺ"),
+ (0x13A, "V"),
+ (0x13B, "M", "ļ"),
+ (0x13C, "V"),
+ (0x13D, "M", "ľ"),
+ (0x13E, "V"),
+ (0x13F, "M", "l·"),
+ (0x141, "M", "ł"),
+ (0x142, "V"),
+ (0x143, "M", "ń"),
+ (0x144, "V"),
+ (0x145, "M", "ņ"),
+ (0x146, "V"),
+ (0x147, "M", "ň"),
+ (0x148, "V"),
+ (0x149, "M", "ʼn"),
+ (0x14A, "M", "ŋ"),
+ (0x14B, "V"),
+ (0x14C, "M", "ō"),
+ (0x14D, "V"),
+ (0x14E, "M", "ŏ"),
+ (0x14F, "V"),
+ (0x150, "M", "ő"),
+ (0x151, "V"),
+ (0x152, "M", "œ"),
+ (0x153, "V"),
+ (0x154, "M", "ŕ"),
+ (0x155, "V"),
+ (0x156, "M", "ŗ"),
+ (0x157, "V"),
+ (0x158, "M", "ř"),
+ (0x159, "V"),
+ (0x15A, "M", "ś"),
+ (0x15B, "V"),
+ (0x15C, "M", "ŝ"),
+ (0x15D, "V"),
+ (0x15E, "M", "ş"),
+ (0x15F, "V"),
+ (0x160, "M", "š"),
+ (0x161, "V"),
+ (0x162, "M", "ţ"),
+ (0x163, "V"),
+ (0x164, "M", "ť"),
+ (0x165, "V"),
+ (0x166, "M", "ŧ"),
+ (0x167, "V"),
+ (0x168, "M", "ũ"),
+ (0x169, "V"),
+ (0x16A, "M", "ū"),
+ (0x16B, "V"),
+ (0x16C, "M", "ŭ"),
+ (0x16D, "V"),
+ (0x16E, "M", "ů"),
+ (0x16F, "V"),
+ (0x170, "M", "ű"),
+ (0x171, "V"),
+ (0x172, "M", "ų"),
+ (0x173, "V"),
+ (0x174, "M", "ŵ"),
+ (0x175, "V"),
+ (0x176, "M", "ŷ"),
+ (0x177, "V"),
+ (0x178, "M", "ÿ"),
+ (0x179, "M", "ź"),
+ (0x17A, "V"),
+ (0x17B, "M", "ż"),
+ (0x17C, "V"),
+ (0x17D, "M", "ž"),
+ (0x17E, "V"),
+ (0x17F, "M", "s"),
+ (0x180, "V"),
+ (0x181, "M", "ɓ"),
+ (0x182, "M", "ƃ"),
+ (0x183, "V"),
+ (0x184, "M", "ƅ"),
+ (0x185, "V"),
+ (0x186, "M", "ɔ"),
+ (0x187, "M", "ƈ"),
+ (0x188, "V"),
+ (0x189, "M", "ɖ"),
+ (0x18A, "M", "ɗ"),
+ (0x18B, "M", "ƌ"),
+ (0x18C, "V"),
+ (0x18E, "M", "ǝ"),
+ (0x18F, "M", "ə"),
+ (0x190, "M", "ɛ"),
+ (0x191, "M", "ƒ"),
+ (0x192, "V"),
+ (0x193, "M", "ɠ"),
+ ]
+
+
+def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x194, "M", "ɣ"),
+ (0x195, "V"),
+ (0x196, "M", "ɩ"),
+ (0x197, "M", "ɨ"),
+ (0x198, "M", "ƙ"),
+ (0x199, "V"),
+ (0x19C, "M", "ɯ"),
+ (0x19D, "M", "ɲ"),
+ (0x19E, "V"),
+ (0x19F, "M", "ɵ"),
+ (0x1A0, "M", "ơ"),
+ (0x1A1, "V"),
+ (0x1A2, "M", "ƣ"),
+ (0x1A3, "V"),
+ (0x1A4, "M", "ƥ"),
+ (0x1A5, "V"),
+ (0x1A6, "M", "ʀ"),
+ (0x1A7, "M", "ƨ"),
+ (0x1A8, "V"),
+ (0x1A9, "M", "ʃ"),
+ (0x1AA, "V"),
+ (0x1AC, "M", "ƭ"),
+ (0x1AD, "V"),
+ (0x1AE, "M", "ʈ"),
+ (0x1AF, "M", "ư"),
+ (0x1B0, "V"),
+ (0x1B1, "M", "ʊ"),
+ (0x1B2, "M", "ʋ"),
+ (0x1B3, "M", "ƴ"),
+ (0x1B4, "V"),
+ (0x1B5, "M", "ƶ"),
+ (0x1B6, "V"),
+ (0x1B7, "M", "ʒ"),
+ (0x1B8, "M", "ƹ"),
+ (0x1B9, "V"),
+ (0x1BC, "M", "ƽ"),
+ (0x1BD, "V"),
+ (0x1C4, "M", "dž"),
+ (0x1C7, "M", "lj"),
+ (0x1CA, "M", "nj"),
+ (0x1CD, "M", "ǎ"),
+ (0x1CE, "V"),
+ (0x1CF, "M", "ǐ"),
+ (0x1D0, "V"),
+ (0x1D1, "M", "ǒ"),
+ (0x1D2, "V"),
+ (0x1D3, "M", "ǔ"),
+ (0x1D4, "V"),
+ (0x1D5, "M", "ǖ"),
+ (0x1D6, "V"),
+ (0x1D7, "M", "ǘ"),
+ (0x1D8, "V"),
+ (0x1D9, "M", "ǚ"),
+ (0x1DA, "V"),
+ (0x1DB, "M", "ǜ"),
+ (0x1DC, "V"),
+ (0x1DE, "M", "ǟ"),
+ (0x1DF, "V"),
+ (0x1E0, "M", "ǡ"),
+ (0x1E1, "V"),
+ (0x1E2, "M", "ǣ"),
+ (0x1E3, "V"),
+ (0x1E4, "M", "ǥ"),
+ (0x1E5, "V"),
+ (0x1E6, "M", "ǧ"),
+ (0x1E7, "V"),
+ (0x1E8, "M", "ǩ"),
+ (0x1E9, "V"),
+ (0x1EA, "M", "ǫ"),
+ (0x1EB, "V"),
+ (0x1EC, "M", "ǭ"),
+ (0x1ED, "V"),
+ (0x1EE, "M", "ǯ"),
+ (0x1EF, "V"),
+ (0x1F1, "M", "dz"),
+ (0x1F4, "M", "ǵ"),
+ (0x1F5, "V"),
+ (0x1F6, "M", "ƕ"),
+ (0x1F7, "M", "ƿ"),
+ (0x1F8, "M", "ǹ"),
+ (0x1F9, "V"),
+ (0x1FA, "M", "ǻ"),
+ (0x1FB, "V"),
+ (0x1FC, "M", "ǽ"),
+ (0x1FD, "V"),
+ (0x1FE, "M", "ǿ"),
+ (0x1FF, "V"),
+ (0x200, "M", "ȁ"),
+ (0x201, "V"),
+ (0x202, "M", "ȃ"),
+ (0x203, "V"),
+ (0x204, "M", "ȅ"),
+ (0x205, "V"),
+ (0x206, "M", "ȇ"),
+ (0x207, "V"),
+ (0x208, "M", "ȉ"),
+ (0x209, "V"),
+ (0x20A, "M", "ȋ"),
+ (0x20B, "V"),
+ (0x20C, "M", "ȍ"),
+ ]
+
+
+def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x20D, "V"),
+ (0x20E, "M", "ȏ"),
+ (0x20F, "V"),
+ (0x210, "M", "ȑ"),
+ (0x211, "V"),
+ (0x212, "M", "ȓ"),
+ (0x213, "V"),
+ (0x214, "M", "ȕ"),
+ (0x215, "V"),
+ (0x216, "M", "ȗ"),
+ (0x217, "V"),
+ (0x218, "M", "ș"),
+ (0x219, "V"),
+ (0x21A, "M", "ț"),
+ (0x21B, "V"),
+ (0x21C, "M", "ȝ"),
+ (0x21D, "V"),
+ (0x21E, "M", "ȟ"),
+ (0x21F, "V"),
+ (0x220, "M", "ƞ"),
+ (0x221, "V"),
+ (0x222, "M", "ȣ"),
+ (0x223, "V"),
+ (0x224, "M", "ȥ"),
+ (0x225, "V"),
+ (0x226, "M", "ȧ"),
+ (0x227, "V"),
+ (0x228, "M", "ȩ"),
+ (0x229, "V"),
+ (0x22A, "M", "ȫ"),
+ (0x22B, "V"),
+ (0x22C, "M", "ȭ"),
+ (0x22D, "V"),
+ (0x22E, "M", "ȯ"),
+ (0x22F, "V"),
+ (0x230, "M", "ȱ"),
+ (0x231, "V"),
+ (0x232, "M", "ȳ"),
+ (0x233, "V"),
+ (0x23A, "M", "ⱥ"),
+ (0x23B, "M", "ȼ"),
+ (0x23C, "V"),
+ (0x23D, "M", "ƚ"),
+ (0x23E, "M", "ⱦ"),
+ (0x23F, "V"),
+ (0x241, "M", "ɂ"),
+ (0x242, "V"),
+ (0x243, "M", "ƀ"),
+ (0x244, "M", "ʉ"),
+ (0x245, "M", "ʌ"),
+ (0x246, "M", "ɇ"),
+ (0x247, "V"),
+ (0x248, "M", "ɉ"),
+ (0x249, "V"),
+ (0x24A, "M", "ɋ"),
+ (0x24B, "V"),
+ (0x24C, "M", "ɍ"),
+ (0x24D, "V"),
+ (0x24E, "M", "ɏ"),
+ (0x24F, "V"),
+ (0x2B0, "M", "h"),
+ (0x2B1, "M", "ɦ"),
+ (0x2B2, "M", "j"),
+ (0x2B3, "M", "r"),
+ (0x2B4, "M", "ɹ"),
+ (0x2B5, "M", "ɻ"),
+ (0x2B6, "M", "ʁ"),
+ (0x2B7, "M", "w"),
+ (0x2B8, "M", "y"),
+ (0x2B9, "V"),
+ (0x2D8, "M", " ̆"),
+ (0x2D9, "M", " ̇"),
+ (0x2DA, "M", " ̊"),
+ (0x2DB, "M", " ̨"),
+ (0x2DC, "M", " ̃"),
+ (0x2DD, "M", " ̋"),
+ (0x2DE, "V"),
+ (0x2E0, "M", "ɣ"),
+ (0x2E1, "M", "l"),
+ (0x2E2, "M", "s"),
+ (0x2E3, "M", "x"),
+ (0x2E4, "M", "ʕ"),
+ (0x2E5, "V"),
+ (0x340, "M", "̀"),
+ (0x341, "M", "́"),
+ (0x342, "V"),
+ (0x343, "M", "̓"),
+ (0x344, "M", "̈́"),
+ (0x345, "M", "ι"),
+ (0x346, "V"),
+ (0x34F, "I"),
+ (0x350, "V"),
+ (0x370, "M", "ͱ"),
+ (0x371, "V"),
+ (0x372, "M", "ͳ"),
+ (0x373, "V"),
+ (0x374, "M", "ʹ"),
+ (0x375, "V"),
+ (0x376, "M", "ͷ"),
+ (0x377, "V"),
+ ]
+
+
+def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x378, "X"),
+ (0x37A, "M", " ι"),
+ (0x37B, "V"),
+ (0x37E, "M", ";"),
+ (0x37F, "M", "ϳ"),
+ (0x380, "X"),
+ (0x384, "M", " ́"),
+ (0x385, "M", " ̈́"),
+ (0x386, "M", "ά"),
+ (0x387, "M", "·"),
+ (0x388, "M", "έ"),
+ (0x389, "M", "ή"),
+ (0x38A, "M", "ί"),
+ (0x38B, "X"),
+ (0x38C, "M", "ό"),
+ (0x38D, "X"),
+ (0x38E, "M", "ύ"),
+ (0x38F, "M", "ώ"),
+ (0x390, "V"),
+ (0x391, "M", "α"),
+ (0x392, "M", "β"),
+ (0x393, "M", "γ"),
+ (0x394, "M", "δ"),
+ (0x395, "M", "ε"),
+ (0x396, "M", "ζ"),
+ (0x397, "M", "η"),
+ (0x398, "M", "θ"),
+ (0x399, "M", "ι"),
+ (0x39A, "M", "κ"),
+ (0x39B, "M", "λ"),
+ (0x39C, "M", "μ"),
+ (0x39D, "M", "ν"),
+ (0x39E, "M", "ξ"),
+ (0x39F, "M", "ο"),
+ (0x3A0, "M", "π"),
+ (0x3A1, "M", "ρ"),
+ (0x3A2, "X"),
+ (0x3A3, "M", "σ"),
+ (0x3A4, "M", "τ"),
+ (0x3A5, "M", "υ"),
+ (0x3A6, "M", "φ"),
+ (0x3A7, "M", "χ"),
+ (0x3A8, "M", "ψ"),
+ (0x3A9, "M", "ω"),
+ (0x3AA, "M", "ϊ"),
+ (0x3AB, "M", "ϋ"),
+ (0x3AC, "V"),
+ (0x3C2, "D", "σ"),
+ (0x3C3, "V"),
+ (0x3CF, "M", "ϗ"),
+ (0x3D0, "M", "β"),
+ (0x3D1, "M", "θ"),
+ (0x3D2, "M", "υ"),
+ (0x3D3, "M", "ύ"),
+ (0x3D4, "M", "ϋ"),
+ (0x3D5, "M", "φ"),
+ (0x3D6, "M", "π"),
+ (0x3D7, "V"),
+ (0x3D8, "M", "ϙ"),
+ (0x3D9, "V"),
+ (0x3DA, "M", "ϛ"),
+ (0x3DB, "V"),
+ (0x3DC, "M", "ϝ"),
+ (0x3DD, "V"),
+ (0x3DE, "M", "ϟ"),
+ (0x3DF, "V"),
+ (0x3E0, "M", "ϡ"),
+ (0x3E1, "V"),
+ (0x3E2, "M", "ϣ"),
+ (0x3E3, "V"),
+ (0x3E4, "M", "ϥ"),
+ (0x3E5, "V"),
+ (0x3E6, "M", "ϧ"),
+ (0x3E7, "V"),
+ (0x3E8, "M", "ϩ"),
+ (0x3E9, "V"),
+ (0x3EA, "M", "ϫ"),
+ (0x3EB, "V"),
+ (0x3EC, "M", "ϭ"),
+ (0x3ED, "V"),
+ (0x3EE, "M", "ϯ"),
+ (0x3EF, "V"),
+ (0x3F0, "M", "κ"),
+ (0x3F1, "M", "ρ"),
+ (0x3F2, "M", "σ"),
+ (0x3F3, "V"),
+ (0x3F4, "M", "θ"),
+ (0x3F5, "M", "ε"),
+ (0x3F6, "V"),
+ (0x3F7, "M", "ϸ"),
+ (0x3F8, "V"),
+ (0x3F9, "M", "σ"),
+ (0x3FA, "M", "ϻ"),
+ (0x3FB, "V"),
+ (0x3FD, "M", "ͻ"),
+ (0x3FE, "M", "ͼ"),
+ (0x3FF, "M", "ͽ"),
+ (0x400, "M", "ѐ"),
+ (0x401, "M", "ё"),
+ (0x402, "M", "ђ"),
+ ]
+
+
+def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x403, "M", "ѓ"),
+ (0x404, "M", "є"),
+ (0x405, "M", "ѕ"),
+ (0x406, "M", "і"),
+ (0x407, "M", "ї"),
+ (0x408, "M", "ј"),
+ (0x409, "M", "љ"),
+ (0x40A, "M", "њ"),
+ (0x40B, "M", "ћ"),
+ (0x40C, "M", "ќ"),
+ (0x40D, "M", "ѝ"),
+ (0x40E, "M", "ў"),
+ (0x40F, "M", "џ"),
+ (0x410, "M", "а"),
+ (0x411, "M", "б"),
+ (0x412, "M", "в"),
+ (0x413, "M", "г"),
+ (0x414, "M", "д"),
+ (0x415, "M", "е"),
+ (0x416, "M", "ж"),
+ (0x417, "M", "з"),
+ (0x418, "M", "и"),
+ (0x419, "M", "й"),
+ (0x41A, "M", "к"),
+ (0x41B, "M", "л"),
+ (0x41C, "M", "м"),
+ (0x41D, "M", "н"),
+ (0x41E, "M", "о"),
+ (0x41F, "M", "п"),
+ (0x420, "M", "р"),
+ (0x421, "M", "с"),
+ (0x422, "M", "т"),
+ (0x423, "M", "у"),
+ (0x424, "M", "ф"),
+ (0x425, "M", "х"),
+ (0x426, "M", "ц"),
+ (0x427, "M", "ч"),
+ (0x428, "M", "ш"),
+ (0x429, "M", "щ"),
+ (0x42A, "M", "ъ"),
+ (0x42B, "M", "ы"),
+ (0x42C, "M", "ь"),
+ (0x42D, "M", "э"),
+ (0x42E, "M", "ю"),
+ (0x42F, "M", "я"),
+ (0x430, "V"),
+ (0x460, "M", "ѡ"),
+ (0x461, "V"),
+ (0x462, "M", "ѣ"),
+ (0x463, "V"),
+ (0x464, "M", "ѥ"),
+ (0x465, "V"),
+ (0x466, "M", "ѧ"),
+ (0x467, "V"),
+ (0x468, "M", "ѩ"),
+ (0x469, "V"),
+ (0x46A, "M", "ѫ"),
+ (0x46B, "V"),
+ (0x46C, "M", "ѭ"),
+ (0x46D, "V"),
+ (0x46E, "M", "ѯ"),
+ (0x46F, "V"),
+ (0x470, "M", "ѱ"),
+ (0x471, "V"),
+ (0x472, "M", "ѳ"),
+ (0x473, "V"),
+ (0x474, "M", "ѵ"),
+ (0x475, "V"),
+ (0x476, "M", "ѷ"),
+ (0x477, "V"),
+ (0x478, "M", "ѹ"),
+ (0x479, "V"),
+ (0x47A, "M", "ѻ"),
+ (0x47B, "V"),
+ (0x47C, "M", "ѽ"),
+ (0x47D, "V"),
+ (0x47E, "M", "ѿ"),
+ (0x47F, "V"),
+ (0x480, "M", "ҁ"),
+ (0x481, "V"),
+ (0x48A, "M", "ҋ"),
+ (0x48B, "V"),
+ (0x48C, "M", "ҍ"),
+ (0x48D, "V"),
+ (0x48E, "M", "ҏ"),
+ (0x48F, "V"),
+ (0x490, "M", "ґ"),
+ (0x491, "V"),
+ (0x492, "M", "ғ"),
+ (0x493, "V"),
+ (0x494, "M", "ҕ"),
+ (0x495, "V"),
+ (0x496, "M", "җ"),
+ (0x497, "V"),
+ (0x498, "M", "ҙ"),
+ (0x499, "V"),
+ (0x49A, "M", "қ"),
+ (0x49B, "V"),
+ (0x49C, "M", "ҝ"),
+ (0x49D, "V"),
+ ]
+
+
+def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x49E, "M", "ҟ"),
+ (0x49F, "V"),
+ (0x4A0, "M", "ҡ"),
+ (0x4A1, "V"),
+ (0x4A2, "M", "ң"),
+ (0x4A3, "V"),
+ (0x4A4, "M", "ҥ"),
+ (0x4A5, "V"),
+ (0x4A6, "M", "ҧ"),
+ (0x4A7, "V"),
+ (0x4A8, "M", "ҩ"),
+ (0x4A9, "V"),
+ (0x4AA, "M", "ҫ"),
+ (0x4AB, "V"),
+ (0x4AC, "M", "ҭ"),
+ (0x4AD, "V"),
+ (0x4AE, "M", "ү"),
+ (0x4AF, "V"),
+ (0x4B0, "M", "ұ"),
+ (0x4B1, "V"),
+ (0x4B2, "M", "ҳ"),
+ (0x4B3, "V"),
+ (0x4B4, "M", "ҵ"),
+ (0x4B5, "V"),
+ (0x4B6, "M", "ҷ"),
+ (0x4B7, "V"),
+ (0x4B8, "M", "ҹ"),
+ (0x4B9, "V"),
+ (0x4BA, "M", "һ"),
+ (0x4BB, "V"),
+ (0x4BC, "M", "ҽ"),
+ (0x4BD, "V"),
+ (0x4BE, "M", "ҿ"),
+ (0x4BF, "V"),
+ (0x4C0, "M", "ӏ"),
+ (0x4C1, "M", "ӂ"),
+ (0x4C2, "V"),
+ (0x4C3, "M", "ӄ"),
+ (0x4C4, "V"),
+ (0x4C5, "M", "ӆ"),
+ (0x4C6, "V"),
+ (0x4C7, "M", "ӈ"),
+ (0x4C8, "V"),
+ (0x4C9, "M", "ӊ"),
+ (0x4CA, "V"),
+ (0x4CB, "M", "ӌ"),
+ (0x4CC, "V"),
+ (0x4CD, "M", "ӎ"),
+ (0x4CE, "V"),
+ (0x4D0, "M", "ӑ"),
+ (0x4D1, "V"),
+ (0x4D2, "M", "ӓ"),
+ (0x4D3, "V"),
+ (0x4D4, "M", "ӕ"),
+ (0x4D5, "V"),
+ (0x4D6, "M", "ӗ"),
+ (0x4D7, "V"),
+ (0x4D8, "M", "ә"),
+ (0x4D9, "V"),
+ (0x4DA, "M", "ӛ"),
+ (0x4DB, "V"),
+ (0x4DC, "M", "ӝ"),
+ (0x4DD, "V"),
+ (0x4DE, "M", "ӟ"),
+ (0x4DF, "V"),
+ (0x4E0, "M", "ӡ"),
+ (0x4E1, "V"),
+ (0x4E2, "M", "ӣ"),
+ (0x4E3, "V"),
+ (0x4E4, "M", "ӥ"),
+ (0x4E5, "V"),
+ (0x4E6, "M", "ӧ"),
+ (0x4E7, "V"),
+ (0x4E8, "M", "ө"),
+ (0x4E9, "V"),
+ (0x4EA, "M", "ӫ"),
+ (0x4EB, "V"),
+ (0x4EC, "M", "ӭ"),
+ (0x4ED, "V"),
+ (0x4EE, "M", "ӯ"),
+ (0x4EF, "V"),
+ (0x4F0, "M", "ӱ"),
+ (0x4F1, "V"),
+ (0x4F2, "M", "ӳ"),
+ (0x4F3, "V"),
+ (0x4F4, "M", "ӵ"),
+ (0x4F5, "V"),
+ (0x4F6, "M", "ӷ"),
+ (0x4F7, "V"),
+ (0x4F8, "M", "ӹ"),
+ (0x4F9, "V"),
+ (0x4FA, "M", "ӻ"),
+ (0x4FB, "V"),
+ (0x4FC, "M", "ӽ"),
+ (0x4FD, "V"),
+ (0x4FE, "M", "ӿ"),
+ (0x4FF, "V"),
+ (0x500, "M", "ԁ"),
+ (0x501, "V"),
+ (0x502, "M", "ԃ"),
+ ]
+
+
+def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x503, "V"),
+ (0x504, "M", "ԅ"),
+ (0x505, "V"),
+ (0x506, "M", "ԇ"),
+ (0x507, "V"),
+ (0x508, "M", "ԉ"),
+ (0x509, "V"),
+ (0x50A, "M", "ԋ"),
+ (0x50B, "V"),
+ (0x50C, "M", "ԍ"),
+ (0x50D, "V"),
+ (0x50E, "M", "ԏ"),
+ (0x50F, "V"),
+ (0x510, "M", "ԑ"),
+ (0x511, "V"),
+ (0x512, "M", "ԓ"),
+ (0x513, "V"),
+ (0x514, "M", "ԕ"),
+ (0x515, "V"),
+ (0x516, "M", "ԗ"),
+ (0x517, "V"),
+ (0x518, "M", "ԙ"),
+ (0x519, "V"),
+ (0x51A, "M", "ԛ"),
+ (0x51B, "V"),
+ (0x51C, "M", "ԝ"),
+ (0x51D, "V"),
+ (0x51E, "M", "ԟ"),
+ (0x51F, "V"),
+ (0x520, "M", "ԡ"),
+ (0x521, "V"),
+ (0x522, "M", "ԣ"),
+ (0x523, "V"),
+ (0x524, "M", "ԥ"),
+ (0x525, "V"),
+ (0x526, "M", "ԧ"),
+ (0x527, "V"),
+ (0x528, "M", "ԩ"),
+ (0x529, "V"),
+ (0x52A, "M", "ԫ"),
+ (0x52B, "V"),
+ (0x52C, "M", "ԭ"),
+ (0x52D, "V"),
+ (0x52E, "M", "ԯ"),
+ (0x52F, "V"),
+ (0x530, "X"),
+ (0x531, "M", "ա"),
+ (0x532, "M", "բ"),
+ (0x533, "M", "գ"),
+ (0x534, "M", "դ"),
+ (0x535, "M", "ե"),
+ (0x536, "M", "զ"),
+ (0x537, "M", "է"),
+ (0x538, "M", "ը"),
+ (0x539, "M", "թ"),
+ (0x53A, "M", "ժ"),
+ (0x53B, "M", "ի"),
+ (0x53C, "M", "լ"),
+ (0x53D, "M", "խ"),
+ (0x53E, "M", "ծ"),
+ (0x53F, "M", "կ"),
+ (0x540, "M", "հ"),
+ (0x541, "M", "ձ"),
+ (0x542, "M", "ղ"),
+ (0x543, "M", "ճ"),
+ (0x544, "M", "մ"),
+ (0x545, "M", "յ"),
+ (0x546, "M", "ն"),
+ (0x547, "M", "շ"),
+ (0x548, "M", "ո"),
+ (0x549, "M", "չ"),
+ (0x54A, "M", "պ"),
+ (0x54B, "M", "ջ"),
+ (0x54C, "M", "ռ"),
+ (0x54D, "M", "ս"),
+ (0x54E, "M", "վ"),
+ (0x54F, "M", "տ"),
+ (0x550, "M", "ր"),
+ (0x551, "M", "ց"),
+ (0x552, "M", "ւ"),
+ (0x553, "M", "փ"),
+ (0x554, "M", "ք"),
+ (0x555, "M", "օ"),
+ (0x556, "M", "ֆ"),
+ (0x557, "X"),
+ (0x559, "V"),
+ (0x587, "M", "եւ"),
+ (0x588, "V"),
+ (0x58B, "X"),
+ (0x58D, "V"),
+ (0x590, "X"),
+ (0x591, "V"),
+ (0x5C8, "X"),
+ (0x5D0, "V"),
+ (0x5EB, "X"),
+ (0x5EF, "V"),
+ (0x5F5, "X"),
+ (0x606, "V"),
+ (0x61C, "X"),
+ (0x61D, "V"),
+ ]
+
+
+def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x675, "M", "اٴ"),
+ (0x676, "M", "وٴ"),
+ (0x677, "M", "ۇٴ"),
+ (0x678, "M", "يٴ"),
+ (0x679, "V"),
+ (0x6DD, "X"),
+ (0x6DE, "V"),
+ (0x70E, "X"),
+ (0x710, "V"),
+ (0x74B, "X"),
+ (0x74D, "V"),
+ (0x7B2, "X"),
+ (0x7C0, "V"),
+ (0x7FB, "X"),
+ (0x7FD, "V"),
+ (0x82E, "X"),
+ (0x830, "V"),
+ (0x83F, "X"),
+ (0x840, "V"),
+ (0x85C, "X"),
+ (0x85E, "V"),
+ (0x85F, "X"),
+ (0x860, "V"),
+ (0x86B, "X"),
+ (0x870, "V"),
+ (0x88F, "X"),
+ (0x897, "V"),
+ (0x8E2, "X"),
+ (0x8E3, "V"),
+ (0x958, "M", "क़"),
+ (0x959, "M", "ख़"),
+ (0x95A, "M", "ग़"),
+ (0x95B, "M", "ज़"),
+ (0x95C, "M", "ड़"),
+ (0x95D, "M", "ढ़"),
+ (0x95E, "M", "फ़"),
+ (0x95F, "M", "य़"),
+ (0x960, "V"),
+ (0x984, "X"),
+ (0x985, "V"),
+ (0x98D, "X"),
+ (0x98F, "V"),
+ (0x991, "X"),
+ (0x993, "V"),
+ (0x9A9, "X"),
+ (0x9AA, "V"),
+ (0x9B1, "X"),
+ (0x9B2, "V"),
+ (0x9B3, "X"),
+ (0x9B6, "V"),
+ (0x9BA, "X"),
+ (0x9BC, "V"),
+ (0x9C5, "X"),
+ (0x9C7, "V"),
+ (0x9C9, "X"),
+ (0x9CB, "V"),
+ (0x9CF, "X"),
+ (0x9D7, "V"),
+ (0x9D8, "X"),
+ (0x9DC, "M", "ড়"),
+ (0x9DD, "M", "ঢ়"),
+ (0x9DE, "X"),
+ (0x9DF, "M", "য়"),
+ (0x9E0, "V"),
+ (0x9E4, "X"),
+ (0x9E6, "V"),
+ (0x9FF, "X"),
+ (0xA01, "V"),
+ (0xA04, "X"),
+ (0xA05, "V"),
+ (0xA0B, "X"),
+ (0xA0F, "V"),
+ (0xA11, "X"),
+ (0xA13, "V"),
+ (0xA29, "X"),
+ (0xA2A, "V"),
+ (0xA31, "X"),
+ (0xA32, "V"),
+ (0xA33, "M", "ਲ਼"),
+ (0xA34, "X"),
+ (0xA35, "V"),
+ (0xA36, "M", "ਸ਼"),
+ (0xA37, "X"),
+ (0xA38, "V"),
+ (0xA3A, "X"),
+ (0xA3C, "V"),
+ (0xA3D, "X"),
+ (0xA3E, "V"),
+ (0xA43, "X"),
+ (0xA47, "V"),
+ (0xA49, "X"),
+ (0xA4B, "V"),
+ (0xA4E, "X"),
+ (0xA51, "V"),
+ (0xA52, "X"),
+ (0xA59, "M", "ਖ਼"),
+ (0xA5A, "M", "ਗ਼"),
+ (0xA5B, "M", "ਜ਼"),
+ (0xA5C, "V"),
+ (0xA5D, "X"),
+ ]
+
+
+def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xA5E, "M", "ਫ਼"),
+ (0xA5F, "X"),
+ (0xA66, "V"),
+ (0xA77, "X"),
+ (0xA81, "V"),
+ (0xA84, "X"),
+ (0xA85, "V"),
+ (0xA8E, "X"),
+ (0xA8F, "V"),
+ (0xA92, "X"),
+ (0xA93, "V"),
+ (0xAA9, "X"),
+ (0xAAA, "V"),
+ (0xAB1, "X"),
+ (0xAB2, "V"),
+ (0xAB4, "X"),
+ (0xAB5, "V"),
+ (0xABA, "X"),
+ (0xABC, "V"),
+ (0xAC6, "X"),
+ (0xAC7, "V"),
+ (0xACA, "X"),
+ (0xACB, "V"),
+ (0xACE, "X"),
+ (0xAD0, "V"),
+ (0xAD1, "X"),
+ (0xAE0, "V"),
+ (0xAE4, "X"),
+ (0xAE6, "V"),
+ (0xAF2, "X"),
+ (0xAF9, "V"),
+ (0xB00, "X"),
+ (0xB01, "V"),
+ (0xB04, "X"),
+ (0xB05, "V"),
+ (0xB0D, "X"),
+ (0xB0F, "V"),
+ (0xB11, "X"),
+ (0xB13, "V"),
+ (0xB29, "X"),
+ (0xB2A, "V"),
+ (0xB31, "X"),
+ (0xB32, "V"),
+ (0xB34, "X"),
+ (0xB35, "V"),
+ (0xB3A, "X"),
+ (0xB3C, "V"),
+ (0xB45, "X"),
+ (0xB47, "V"),
+ (0xB49, "X"),
+ (0xB4B, "V"),
+ (0xB4E, "X"),
+ (0xB55, "V"),
+ (0xB58, "X"),
+ (0xB5C, "M", "ଡ଼"),
+ (0xB5D, "M", "ଢ଼"),
+ (0xB5E, "X"),
+ (0xB5F, "V"),
+ (0xB64, "X"),
+ (0xB66, "V"),
+ (0xB78, "X"),
+ (0xB82, "V"),
+ (0xB84, "X"),
+ (0xB85, "V"),
+ (0xB8B, "X"),
+ (0xB8E, "V"),
+ (0xB91, "X"),
+ (0xB92, "V"),
+ (0xB96, "X"),
+ (0xB99, "V"),
+ (0xB9B, "X"),
+ (0xB9C, "V"),
+ (0xB9D, "X"),
+ (0xB9E, "V"),
+ (0xBA0, "X"),
+ (0xBA3, "V"),
+ (0xBA5, "X"),
+ (0xBA8, "V"),
+ (0xBAB, "X"),
+ (0xBAE, "V"),
+ (0xBBA, "X"),
+ (0xBBE, "V"),
+ (0xBC3, "X"),
+ (0xBC6, "V"),
+ (0xBC9, "X"),
+ (0xBCA, "V"),
+ (0xBCE, "X"),
+ (0xBD0, "V"),
+ (0xBD1, "X"),
+ (0xBD7, "V"),
+ (0xBD8, "X"),
+ (0xBE6, "V"),
+ (0xBFB, "X"),
+ (0xC00, "V"),
+ (0xC0D, "X"),
+ (0xC0E, "V"),
+ (0xC11, "X"),
+ (0xC12, "V"),
+ (0xC29, "X"),
+ (0xC2A, "V"),
+ ]
+
+
+def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xC3A, "X"),
+ (0xC3C, "V"),
+ (0xC45, "X"),
+ (0xC46, "V"),
+ (0xC49, "X"),
+ (0xC4A, "V"),
+ (0xC4E, "X"),
+ (0xC55, "V"),
+ (0xC57, "X"),
+ (0xC58, "V"),
+ (0xC5B, "X"),
+ (0xC5D, "V"),
+ (0xC5E, "X"),
+ (0xC60, "V"),
+ (0xC64, "X"),
+ (0xC66, "V"),
+ (0xC70, "X"),
+ (0xC77, "V"),
+ (0xC8D, "X"),
+ (0xC8E, "V"),
+ (0xC91, "X"),
+ (0xC92, "V"),
+ (0xCA9, "X"),
+ (0xCAA, "V"),
+ (0xCB4, "X"),
+ (0xCB5, "V"),
+ (0xCBA, "X"),
+ (0xCBC, "V"),
+ (0xCC5, "X"),
+ (0xCC6, "V"),
+ (0xCC9, "X"),
+ (0xCCA, "V"),
+ (0xCCE, "X"),
+ (0xCD5, "V"),
+ (0xCD7, "X"),
+ (0xCDD, "V"),
+ (0xCDF, "X"),
+ (0xCE0, "V"),
+ (0xCE4, "X"),
+ (0xCE6, "V"),
+ (0xCF0, "X"),
+ (0xCF1, "V"),
+ (0xCF4, "X"),
+ (0xD00, "V"),
+ (0xD0D, "X"),
+ (0xD0E, "V"),
+ (0xD11, "X"),
+ (0xD12, "V"),
+ (0xD45, "X"),
+ (0xD46, "V"),
+ (0xD49, "X"),
+ (0xD4A, "V"),
+ (0xD50, "X"),
+ (0xD54, "V"),
+ (0xD64, "X"),
+ (0xD66, "V"),
+ (0xD80, "X"),
+ (0xD81, "V"),
+ (0xD84, "X"),
+ (0xD85, "V"),
+ (0xD97, "X"),
+ (0xD9A, "V"),
+ (0xDB2, "X"),
+ (0xDB3, "V"),
+ (0xDBC, "X"),
+ (0xDBD, "V"),
+ (0xDBE, "X"),
+ (0xDC0, "V"),
+ (0xDC7, "X"),
+ (0xDCA, "V"),
+ (0xDCB, "X"),
+ (0xDCF, "V"),
+ (0xDD5, "X"),
+ (0xDD6, "V"),
+ (0xDD7, "X"),
+ (0xDD8, "V"),
+ (0xDE0, "X"),
+ (0xDE6, "V"),
+ (0xDF0, "X"),
+ (0xDF2, "V"),
+ (0xDF5, "X"),
+ (0xE01, "V"),
+ (0xE33, "M", "ํา"),
+ (0xE34, "V"),
+ (0xE3B, "X"),
+ (0xE3F, "V"),
+ (0xE5C, "X"),
+ (0xE81, "V"),
+ (0xE83, "X"),
+ (0xE84, "V"),
+ (0xE85, "X"),
+ (0xE86, "V"),
+ (0xE8B, "X"),
+ (0xE8C, "V"),
+ (0xEA4, "X"),
+ (0xEA5, "V"),
+ (0xEA6, "X"),
+ (0xEA7, "V"),
+ (0xEB3, "M", "ໍາ"),
+ (0xEB4, "V"),
+ ]
+
+
+def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xEBE, "X"),
+ (0xEC0, "V"),
+ (0xEC5, "X"),
+ (0xEC6, "V"),
+ (0xEC7, "X"),
+ (0xEC8, "V"),
+ (0xECF, "X"),
+ (0xED0, "V"),
+ (0xEDA, "X"),
+ (0xEDC, "M", "ຫນ"),
+ (0xEDD, "M", "ຫມ"),
+ (0xEDE, "V"),
+ (0xEE0, "X"),
+ (0xF00, "V"),
+ (0xF0C, "M", "་"),
+ (0xF0D, "V"),
+ (0xF43, "M", "གྷ"),
+ (0xF44, "V"),
+ (0xF48, "X"),
+ (0xF49, "V"),
+ (0xF4D, "M", "ཌྷ"),
+ (0xF4E, "V"),
+ (0xF52, "M", "དྷ"),
+ (0xF53, "V"),
+ (0xF57, "M", "བྷ"),
+ (0xF58, "V"),
+ (0xF5C, "M", "ཛྷ"),
+ (0xF5D, "V"),
+ (0xF69, "M", "ཀྵ"),
+ (0xF6A, "V"),
+ (0xF6D, "X"),
+ (0xF71, "V"),
+ (0xF73, "M", "ཱི"),
+ (0xF74, "V"),
+ (0xF75, "M", "ཱུ"),
+ (0xF76, "M", "ྲྀ"),
+ (0xF77, "M", "ྲཱྀ"),
+ (0xF78, "M", "ླྀ"),
+ (0xF79, "M", "ླཱྀ"),
+ (0xF7A, "V"),
+ (0xF81, "M", "ཱྀ"),
+ (0xF82, "V"),
+ (0xF93, "M", "ྒྷ"),
+ (0xF94, "V"),
+ (0xF98, "X"),
+ (0xF99, "V"),
+ (0xF9D, "M", "ྜྷ"),
+ (0xF9E, "V"),
+ (0xFA2, "M", "ྡྷ"),
+ (0xFA3, "V"),
+ (0xFA7, "M", "ྦྷ"),
+ (0xFA8, "V"),
+ (0xFAC, "M", "ྫྷ"),
+ (0xFAD, "V"),
+ (0xFB9, "M", "ྐྵ"),
+ (0xFBA, "V"),
+ (0xFBD, "X"),
+ (0xFBE, "V"),
+ (0xFCD, "X"),
+ (0xFCE, "V"),
+ (0xFDB, "X"),
+ (0x1000, "V"),
+ (0x10A0, "M", "ⴀ"),
+ (0x10A1, "M", "ⴁ"),
+ (0x10A2, "M", "ⴂ"),
+ (0x10A3, "M", "ⴃ"),
+ (0x10A4, "M", "ⴄ"),
+ (0x10A5, "M", "ⴅ"),
+ (0x10A6, "M", "ⴆ"),
+ (0x10A7, "M", "ⴇ"),
+ (0x10A8, "M", "ⴈ"),
+ (0x10A9, "M", "ⴉ"),
+ (0x10AA, "M", "ⴊ"),
+ (0x10AB, "M", "ⴋ"),
+ (0x10AC, "M", "ⴌ"),
+ (0x10AD, "M", "ⴍ"),
+ (0x10AE, "M", "ⴎ"),
+ (0x10AF, "M", "ⴏ"),
+ (0x10B0, "M", "ⴐ"),
+ (0x10B1, "M", "ⴑ"),
+ (0x10B2, "M", "ⴒ"),
+ (0x10B3, "M", "ⴓ"),
+ (0x10B4, "M", "ⴔ"),
+ (0x10B5, "M", "ⴕ"),
+ (0x10B6, "M", "ⴖ"),
+ (0x10B7, "M", "ⴗ"),
+ (0x10B8, "M", "ⴘ"),
+ (0x10B9, "M", "ⴙ"),
+ (0x10BA, "M", "ⴚ"),
+ (0x10BB, "M", "ⴛ"),
+ (0x10BC, "M", "ⴜ"),
+ (0x10BD, "M", "ⴝ"),
+ (0x10BE, "M", "ⴞ"),
+ (0x10BF, "M", "ⴟ"),
+ (0x10C0, "M", "ⴠ"),
+ (0x10C1, "M", "ⴡ"),
+ (0x10C2, "M", "ⴢ"),
+ (0x10C3, "M", "ⴣ"),
+ (0x10C4, "M", "ⴤ"),
+ (0x10C5, "M", "ⴥ"),
+ ]
+
+
+def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x10C6, "X"),
+ (0x10C7, "M", "ⴧ"),
+ (0x10C8, "X"),
+ (0x10CD, "M", "ⴭ"),
+ (0x10CE, "X"),
+ (0x10D0, "V"),
+ (0x10FC, "M", "ნ"),
+ (0x10FD, "V"),
+ (0x115F, "I"),
+ (0x1161, "V"),
+ (0x1249, "X"),
+ (0x124A, "V"),
+ (0x124E, "X"),
+ (0x1250, "V"),
+ (0x1257, "X"),
+ (0x1258, "V"),
+ (0x1259, "X"),
+ (0x125A, "V"),
+ (0x125E, "X"),
+ (0x1260, "V"),
+ (0x1289, "X"),
+ (0x128A, "V"),
+ (0x128E, "X"),
+ (0x1290, "V"),
+ (0x12B1, "X"),
+ (0x12B2, "V"),
+ (0x12B6, "X"),
+ (0x12B8, "V"),
+ (0x12BF, "X"),
+ (0x12C0, "V"),
+ (0x12C1, "X"),
+ (0x12C2, "V"),
+ (0x12C6, "X"),
+ (0x12C8, "V"),
+ (0x12D7, "X"),
+ (0x12D8, "V"),
+ (0x1311, "X"),
+ (0x1312, "V"),
+ (0x1316, "X"),
+ (0x1318, "V"),
+ (0x135B, "X"),
+ (0x135D, "V"),
+ (0x137D, "X"),
+ (0x1380, "V"),
+ (0x139A, "X"),
+ (0x13A0, "V"),
+ (0x13F6, "X"),
+ (0x13F8, "M", "Ᏸ"),
+ (0x13F9, "M", "Ᏹ"),
+ (0x13FA, "M", "Ᏺ"),
+ (0x13FB, "M", "Ᏻ"),
+ (0x13FC, "M", "Ᏼ"),
+ (0x13FD, "M", "Ᏽ"),
+ (0x13FE, "X"),
+ (0x1400, "V"),
+ (0x1680, "X"),
+ (0x1681, "V"),
+ (0x169D, "X"),
+ (0x16A0, "V"),
+ (0x16F9, "X"),
+ (0x1700, "V"),
+ (0x1716, "X"),
+ (0x171F, "V"),
+ (0x1737, "X"),
+ (0x1740, "V"),
+ (0x1754, "X"),
+ (0x1760, "V"),
+ (0x176D, "X"),
+ (0x176E, "V"),
+ (0x1771, "X"),
+ (0x1772, "V"),
+ (0x1774, "X"),
+ (0x1780, "V"),
+ (0x17B4, "I"),
+ (0x17B6, "V"),
+ (0x17DE, "X"),
+ (0x17E0, "V"),
+ (0x17EA, "X"),
+ (0x17F0, "V"),
+ (0x17FA, "X"),
+ (0x1800, "V"),
+ (0x180B, "I"),
+ (0x1810, "V"),
+ (0x181A, "X"),
+ (0x1820, "V"),
+ (0x1879, "X"),
+ (0x1880, "V"),
+ (0x18AB, "X"),
+ (0x18B0, "V"),
+ (0x18F6, "X"),
+ (0x1900, "V"),
+ (0x191F, "X"),
+ (0x1920, "V"),
+ (0x192C, "X"),
+ (0x1930, "V"),
+ (0x193C, "X"),
+ (0x1940, "V"),
+ (0x1941, "X"),
+ (0x1944, "V"),
+ (0x196E, "X"),
+ ]
+
+
+def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1970, "V"),
+ (0x1975, "X"),
+ (0x1980, "V"),
+ (0x19AC, "X"),
+ (0x19B0, "V"),
+ (0x19CA, "X"),
+ (0x19D0, "V"),
+ (0x19DB, "X"),
+ (0x19DE, "V"),
+ (0x1A1C, "X"),
+ (0x1A1E, "V"),
+ (0x1A5F, "X"),
+ (0x1A60, "V"),
+ (0x1A7D, "X"),
+ (0x1A7F, "V"),
+ (0x1A8A, "X"),
+ (0x1A90, "V"),
+ (0x1A9A, "X"),
+ (0x1AA0, "V"),
+ (0x1AAE, "X"),
+ (0x1AB0, "V"),
+ (0x1ACF, "X"),
+ (0x1B00, "V"),
+ (0x1B4D, "X"),
+ (0x1B4E, "V"),
+ (0x1BF4, "X"),
+ (0x1BFC, "V"),
+ (0x1C38, "X"),
+ (0x1C3B, "V"),
+ (0x1C4A, "X"),
+ (0x1C4D, "V"),
+ (0x1C80, "M", "в"),
+ (0x1C81, "M", "д"),
+ (0x1C82, "M", "о"),
+ (0x1C83, "M", "с"),
+ (0x1C84, "M", "т"),
+ (0x1C86, "M", "ъ"),
+ (0x1C87, "M", "ѣ"),
+ (0x1C88, "M", "ꙋ"),
+ (0x1C89, "M", ""),
+ (0x1C8A, "V"),
+ (0x1C8B, "X"),
+ (0x1C90, "M", "ა"),
+ (0x1C91, "M", "ბ"),
+ (0x1C92, "M", "გ"),
+ (0x1C93, "M", "დ"),
+ (0x1C94, "M", "ე"),
+ (0x1C95, "M", "ვ"),
+ (0x1C96, "M", "ზ"),
+ (0x1C97, "M", "თ"),
+ (0x1C98, "M", "ი"),
+ (0x1C99, "M", "კ"),
+ (0x1C9A, "M", "ლ"),
+ (0x1C9B, "M", "მ"),
+ (0x1C9C, "M", "ნ"),
+ (0x1C9D, "M", "ო"),
+ (0x1C9E, "M", "პ"),
+ (0x1C9F, "M", "ჟ"),
+ (0x1CA0, "M", "რ"),
+ (0x1CA1, "M", "ს"),
+ (0x1CA2, "M", "ტ"),
+ (0x1CA3, "M", "უ"),
+ (0x1CA4, "M", "ფ"),
+ (0x1CA5, "M", "ქ"),
+ (0x1CA6, "M", "ღ"),
+ (0x1CA7, "M", "ყ"),
+ (0x1CA8, "M", "შ"),
+ (0x1CA9, "M", "ჩ"),
+ (0x1CAA, "M", "ც"),
+ (0x1CAB, "M", "ძ"),
+ (0x1CAC, "M", "წ"),
+ (0x1CAD, "M", "ჭ"),
+ (0x1CAE, "M", "ხ"),
+ (0x1CAF, "M", "ჯ"),
+ (0x1CB0, "M", "ჰ"),
+ (0x1CB1, "M", "ჱ"),
+ (0x1CB2, "M", "ჲ"),
+ (0x1CB3, "M", "ჳ"),
+ (0x1CB4, "M", "ჴ"),
+ (0x1CB5, "M", "ჵ"),
+ (0x1CB6, "M", "ჶ"),
+ (0x1CB7, "M", "ჷ"),
+ (0x1CB8, "M", "ჸ"),
+ (0x1CB9, "M", "ჹ"),
+ (0x1CBA, "M", "ჺ"),
+ (0x1CBB, "X"),
+ (0x1CBD, "M", "ჽ"),
+ (0x1CBE, "M", "ჾ"),
+ (0x1CBF, "M", "ჿ"),
+ (0x1CC0, "V"),
+ (0x1CC8, "X"),
+ (0x1CD0, "V"),
+ (0x1CFB, "X"),
+ (0x1D00, "V"),
+ (0x1D2C, "M", "a"),
+ (0x1D2D, "M", "æ"),
+ (0x1D2E, "M", "b"),
+ (0x1D2F, "V"),
+ (0x1D30, "M", "d"),
+ (0x1D31, "M", "e"),
+ ]
+
+
+def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D32, "M", "ǝ"),
+ (0x1D33, "M", "g"),
+ (0x1D34, "M", "h"),
+ (0x1D35, "M", "i"),
+ (0x1D36, "M", "j"),
+ (0x1D37, "M", "k"),
+ (0x1D38, "M", "l"),
+ (0x1D39, "M", "m"),
+ (0x1D3A, "M", "n"),
+ (0x1D3B, "V"),
+ (0x1D3C, "M", "o"),
+ (0x1D3D, "M", "ȣ"),
+ (0x1D3E, "M", "p"),
+ (0x1D3F, "M", "r"),
+ (0x1D40, "M", "t"),
+ (0x1D41, "M", "u"),
+ (0x1D42, "M", "w"),
+ (0x1D43, "M", "a"),
+ (0x1D44, "M", "ɐ"),
+ (0x1D45, "M", "ɑ"),
+ (0x1D46, "M", "ᴂ"),
+ (0x1D47, "M", "b"),
+ (0x1D48, "M", "d"),
+ (0x1D49, "M", "e"),
+ (0x1D4A, "M", "ə"),
+ (0x1D4B, "M", "ɛ"),
+ (0x1D4C, "M", "ɜ"),
+ (0x1D4D, "M", "g"),
+ (0x1D4E, "V"),
+ (0x1D4F, "M", "k"),
+ (0x1D50, "M", "m"),
+ (0x1D51, "M", "ŋ"),
+ (0x1D52, "M", "o"),
+ (0x1D53, "M", "ɔ"),
+ (0x1D54, "M", "ᴖ"),
+ (0x1D55, "M", "ᴗ"),
+ (0x1D56, "M", "p"),
+ (0x1D57, "M", "t"),
+ (0x1D58, "M", "u"),
+ (0x1D59, "M", "ᴝ"),
+ (0x1D5A, "M", "ɯ"),
+ (0x1D5B, "M", "v"),
+ (0x1D5C, "M", "ᴥ"),
+ (0x1D5D, "M", "β"),
+ (0x1D5E, "M", "γ"),
+ (0x1D5F, "M", "δ"),
+ (0x1D60, "M", "φ"),
+ (0x1D61, "M", "χ"),
+ (0x1D62, "M", "i"),
+ (0x1D63, "M", "r"),
+ (0x1D64, "M", "u"),
+ (0x1D65, "M", "v"),
+ (0x1D66, "M", "β"),
+ (0x1D67, "M", "γ"),
+ (0x1D68, "M", "ρ"),
+ (0x1D69, "M", "φ"),
+ (0x1D6A, "M", "χ"),
+ (0x1D6B, "V"),
+ (0x1D78, "M", "н"),
+ (0x1D79, "V"),
+ (0x1D9B, "M", "ɒ"),
+ (0x1D9C, "M", "c"),
+ (0x1D9D, "M", "ɕ"),
+ (0x1D9E, "M", "ð"),
+ (0x1D9F, "M", "ɜ"),
+ (0x1DA0, "M", "f"),
+ (0x1DA1, "M", "ɟ"),
+ (0x1DA2, "M", "ɡ"),
+ (0x1DA3, "M", "ɥ"),
+ (0x1DA4, "M", "ɨ"),
+ (0x1DA5, "M", "ɩ"),
+ (0x1DA6, "M", "ɪ"),
+ (0x1DA7, "M", "ᵻ"),
+ (0x1DA8, "M", "ʝ"),
+ (0x1DA9, "M", "ɭ"),
+ (0x1DAA, "M", "ᶅ"),
+ (0x1DAB, "M", "ʟ"),
+ (0x1DAC, "M", "ɱ"),
+ (0x1DAD, "M", "ɰ"),
+ (0x1DAE, "M", "ɲ"),
+ (0x1DAF, "M", "ɳ"),
+ (0x1DB0, "M", "ɴ"),
+ (0x1DB1, "M", "ɵ"),
+ (0x1DB2, "M", "ɸ"),
+ (0x1DB3, "M", "ʂ"),
+ (0x1DB4, "M", "ʃ"),
+ (0x1DB5, "M", "ƫ"),
+ (0x1DB6, "M", "ʉ"),
+ (0x1DB7, "M", "ʊ"),
+ (0x1DB8, "M", "ᴜ"),
+ (0x1DB9, "M", "ʋ"),
+ (0x1DBA, "M", "ʌ"),
+ (0x1DBB, "M", "z"),
+ (0x1DBC, "M", "ʐ"),
+ (0x1DBD, "M", "ʑ"),
+ (0x1DBE, "M", "ʒ"),
+ (0x1DBF, "M", "θ"),
+ (0x1DC0, "V"),
+ (0x1E00, "M", "ḁ"),
+ (0x1E01, "V"),
+ ]
+
+
+def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1E02, "M", "ḃ"),
+ (0x1E03, "V"),
+ (0x1E04, "M", "ḅ"),
+ (0x1E05, "V"),
+ (0x1E06, "M", "ḇ"),
+ (0x1E07, "V"),
+ (0x1E08, "M", "ḉ"),
+ (0x1E09, "V"),
+ (0x1E0A, "M", "ḋ"),
+ (0x1E0B, "V"),
+ (0x1E0C, "M", "ḍ"),
+ (0x1E0D, "V"),
+ (0x1E0E, "M", "ḏ"),
+ (0x1E0F, "V"),
+ (0x1E10, "M", "ḑ"),
+ (0x1E11, "V"),
+ (0x1E12, "M", "ḓ"),
+ (0x1E13, "V"),
+ (0x1E14, "M", "ḕ"),
+ (0x1E15, "V"),
+ (0x1E16, "M", "ḗ"),
+ (0x1E17, "V"),
+ (0x1E18, "M", "ḙ"),
+ (0x1E19, "V"),
+ (0x1E1A, "M", "ḛ"),
+ (0x1E1B, "V"),
+ (0x1E1C, "M", "ḝ"),
+ (0x1E1D, "V"),
+ (0x1E1E, "M", "ḟ"),
+ (0x1E1F, "V"),
+ (0x1E20, "M", "ḡ"),
+ (0x1E21, "V"),
+ (0x1E22, "M", "ḣ"),
+ (0x1E23, "V"),
+ (0x1E24, "M", "ḥ"),
+ (0x1E25, "V"),
+ (0x1E26, "M", "ḧ"),
+ (0x1E27, "V"),
+ (0x1E28, "M", "ḩ"),
+ (0x1E29, "V"),
+ (0x1E2A, "M", "ḫ"),
+ (0x1E2B, "V"),
+ (0x1E2C, "M", "ḭ"),
+ (0x1E2D, "V"),
+ (0x1E2E, "M", "ḯ"),
+ (0x1E2F, "V"),
+ (0x1E30, "M", "ḱ"),
+ (0x1E31, "V"),
+ (0x1E32, "M", "ḳ"),
+ (0x1E33, "V"),
+ (0x1E34, "M", "ḵ"),
+ (0x1E35, "V"),
+ (0x1E36, "M", "ḷ"),
+ (0x1E37, "V"),
+ (0x1E38, "M", "ḹ"),
+ (0x1E39, "V"),
+ (0x1E3A, "M", "ḻ"),
+ (0x1E3B, "V"),
+ (0x1E3C, "M", "ḽ"),
+ (0x1E3D, "V"),
+ (0x1E3E, "M", "ḿ"),
+ (0x1E3F, "V"),
+ (0x1E40, "M", "ṁ"),
+ (0x1E41, "V"),
+ (0x1E42, "M", "ṃ"),
+ (0x1E43, "V"),
+ (0x1E44, "M", "ṅ"),
+ (0x1E45, "V"),
+ (0x1E46, "M", "ṇ"),
+ (0x1E47, "V"),
+ (0x1E48, "M", "ṉ"),
+ (0x1E49, "V"),
+ (0x1E4A, "M", "ṋ"),
+ (0x1E4B, "V"),
+ (0x1E4C, "M", "ṍ"),
+ (0x1E4D, "V"),
+ (0x1E4E, "M", "ṏ"),
+ (0x1E4F, "V"),
+ (0x1E50, "M", "ṑ"),
+ (0x1E51, "V"),
+ (0x1E52, "M", "ṓ"),
+ (0x1E53, "V"),
+ (0x1E54, "M", "ṕ"),
+ (0x1E55, "V"),
+ (0x1E56, "M", "ṗ"),
+ (0x1E57, "V"),
+ (0x1E58, "M", "ṙ"),
+ (0x1E59, "V"),
+ (0x1E5A, "M", "ṛ"),
+ (0x1E5B, "V"),
+ (0x1E5C, "M", "ṝ"),
+ (0x1E5D, "V"),
+ (0x1E5E, "M", "ṟ"),
+ (0x1E5F, "V"),
+ (0x1E60, "M", "ṡ"),
+ (0x1E61, "V"),
+ (0x1E62, "M", "ṣ"),
+ (0x1E63, "V"),
+ (0x1E64, "M", "ṥ"),
+ (0x1E65, "V"),
+ ]
+
+
+def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1E66, "M", "ṧ"),
+ (0x1E67, "V"),
+ (0x1E68, "M", "ṩ"),
+ (0x1E69, "V"),
+ (0x1E6A, "M", "ṫ"),
+ (0x1E6B, "V"),
+ (0x1E6C, "M", "ṭ"),
+ (0x1E6D, "V"),
+ (0x1E6E, "M", "ṯ"),
+ (0x1E6F, "V"),
+ (0x1E70, "M", "ṱ"),
+ (0x1E71, "V"),
+ (0x1E72, "M", "ṳ"),
+ (0x1E73, "V"),
+ (0x1E74, "M", "ṵ"),
+ (0x1E75, "V"),
+ (0x1E76, "M", "ṷ"),
+ (0x1E77, "V"),
+ (0x1E78, "M", "ṹ"),
+ (0x1E79, "V"),
+ (0x1E7A, "M", "ṻ"),
+ (0x1E7B, "V"),
+ (0x1E7C, "M", "ṽ"),
+ (0x1E7D, "V"),
+ (0x1E7E, "M", "ṿ"),
+ (0x1E7F, "V"),
+ (0x1E80, "M", "ẁ"),
+ (0x1E81, "V"),
+ (0x1E82, "M", "ẃ"),
+ (0x1E83, "V"),
+ (0x1E84, "M", "ẅ"),
+ (0x1E85, "V"),
+ (0x1E86, "M", "ẇ"),
+ (0x1E87, "V"),
+ (0x1E88, "M", "ẉ"),
+ (0x1E89, "V"),
+ (0x1E8A, "M", "ẋ"),
+ (0x1E8B, "V"),
+ (0x1E8C, "M", "ẍ"),
+ (0x1E8D, "V"),
+ (0x1E8E, "M", "ẏ"),
+ (0x1E8F, "V"),
+ (0x1E90, "M", "ẑ"),
+ (0x1E91, "V"),
+ (0x1E92, "M", "ẓ"),
+ (0x1E93, "V"),
+ (0x1E94, "M", "ẕ"),
+ (0x1E95, "V"),
+ (0x1E9A, "M", "aʾ"),
+ (0x1E9B, "M", "ṡ"),
+ (0x1E9C, "V"),
+ (0x1E9E, "M", "ß"),
+ (0x1E9F, "V"),
+ (0x1EA0, "M", "ạ"),
+ (0x1EA1, "V"),
+ (0x1EA2, "M", "ả"),
+ (0x1EA3, "V"),
+ (0x1EA4, "M", "ấ"),
+ (0x1EA5, "V"),
+ (0x1EA6, "M", "ầ"),
+ (0x1EA7, "V"),
+ (0x1EA8, "M", "ẩ"),
+ (0x1EA9, "V"),
+ (0x1EAA, "M", "ẫ"),
+ (0x1EAB, "V"),
+ (0x1EAC, "M", "ậ"),
+ (0x1EAD, "V"),
+ (0x1EAE, "M", "ắ"),
+ (0x1EAF, "V"),
+ (0x1EB0, "M", "ằ"),
+ (0x1EB1, "V"),
+ (0x1EB2, "M", "ẳ"),
+ (0x1EB3, "V"),
+ (0x1EB4, "M", "ẵ"),
+ (0x1EB5, "V"),
+ (0x1EB6, "M", "ặ"),
+ (0x1EB7, "V"),
+ (0x1EB8, "M", "ẹ"),
+ (0x1EB9, "V"),
+ (0x1EBA, "M", "ẻ"),
+ (0x1EBB, "V"),
+ (0x1EBC, "M", "ẽ"),
+ (0x1EBD, "V"),
+ (0x1EBE, "M", "ế"),
+ (0x1EBF, "V"),
+ (0x1EC0, "M", "ề"),
+ (0x1EC1, "V"),
+ (0x1EC2, "M", "ể"),
+ (0x1EC3, "V"),
+ (0x1EC4, "M", "ễ"),
+ (0x1EC5, "V"),
+ (0x1EC6, "M", "ệ"),
+ (0x1EC7, "V"),
+ (0x1EC8, "M", "ỉ"),
+ (0x1EC9, "V"),
+ (0x1ECA, "M", "ị"),
+ (0x1ECB, "V"),
+ (0x1ECC, "M", "ọ"),
+ (0x1ECD, "V"),
+ (0x1ECE, "M", "ỏ"),
+ ]
+
+
+def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1ECF, "V"),
+ (0x1ED0, "M", "ố"),
+ (0x1ED1, "V"),
+ (0x1ED2, "M", "ồ"),
+ (0x1ED3, "V"),
+ (0x1ED4, "M", "ổ"),
+ (0x1ED5, "V"),
+ (0x1ED6, "M", "ỗ"),
+ (0x1ED7, "V"),
+ (0x1ED8, "M", "ộ"),
+ (0x1ED9, "V"),
+ (0x1EDA, "M", "ớ"),
+ (0x1EDB, "V"),
+ (0x1EDC, "M", "ờ"),
+ (0x1EDD, "V"),
+ (0x1EDE, "M", "ở"),
+ (0x1EDF, "V"),
+ (0x1EE0, "M", "ỡ"),
+ (0x1EE1, "V"),
+ (0x1EE2, "M", "ợ"),
+ (0x1EE3, "V"),
+ (0x1EE4, "M", "ụ"),
+ (0x1EE5, "V"),
+ (0x1EE6, "M", "ủ"),
+ (0x1EE7, "V"),
+ (0x1EE8, "M", "ứ"),
+ (0x1EE9, "V"),
+ (0x1EEA, "M", "ừ"),
+ (0x1EEB, "V"),
+ (0x1EEC, "M", "ử"),
+ (0x1EED, "V"),
+ (0x1EEE, "M", "ữ"),
+ (0x1EEF, "V"),
+ (0x1EF0, "M", "ự"),
+ (0x1EF1, "V"),
+ (0x1EF2, "M", "ỳ"),
+ (0x1EF3, "V"),
+ (0x1EF4, "M", "ỵ"),
+ (0x1EF5, "V"),
+ (0x1EF6, "M", "ỷ"),
+ (0x1EF7, "V"),
+ (0x1EF8, "M", "ỹ"),
+ (0x1EF9, "V"),
+ (0x1EFA, "M", "ỻ"),
+ (0x1EFB, "V"),
+ (0x1EFC, "M", "ỽ"),
+ (0x1EFD, "V"),
+ (0x1EFE, "M", "ỿ"),
+ (0x1EFF, "V"),
+ (0x1F08, "M", "ἀ"),
+ (0x1F09, "M", "ἁ"),
+ (0x1F0A, "M", "ἂ"),
+ (0x1F0B, "M", "ἃ"),
+ (0x1F0C, "M", "ἄ"),
+ (0x1F0D, "M", "ἅ"),
+ (0x1F0E, "M", "ἆ"),
+ (0x1F0F, "M", "ἇ"),
+ (0x1F10, "V"),
+ (0x1F16, "X"),
+ (0x1F18, "M", "ἐ"),
+ (0x1F19, "M", "ἑ"),
+ (0x1F1A, "M", "ἒ"),
+ (0x1F1B, "M", "ἓ"),
+ (0x1F1C, "M", "ἔ"),
+ (0x1F1D, "M", "ἕ"),
+ (0x1F1E, "X"),
+ (0x1F20, "V"),
+ (0x1F28, "M", "ἠ"),
+ (0x1F29, "M", "ἡ"),
+ (0x1F2A, "M", "ἢ"),
+ (0x1F2B, "M", "ἣ"),
+ (0x1F2C, "M", "ἤ"),
+ (0x1F2D, "M", "ἥ"),
+ (0x1F2E, "M", "ἦ"),
+ (0x1F2F, "M", "ἧ"),
+ (0x1F30, "V"),
+ (0x1F38, "M", "ἰ"),
+ (0x1F39, "M", "ἱ"),
+ (0x1F3A, "M", "ἲ"),
+ (0x1F3B, "M", "ἳ"),
+ (0x1F3C, "M", "ἴ"),
+ (0x1F3D, "M", "ἵ"),
+ (0x1F3E, "M", "ἶ"),
+ (0x1F3F, "M", "ἷ"),
+ (0x1F40, "V"),
+ (0x1F46, "X"),
+ (0x1F48, "M", "ὀ"),
+ (0x1F49, "M", "ὁ"),
+ (0x1F4A, "M", "ὂ"),
+ (0x1F4B, "M", "ὃ"),
+ (0x1F4C, "M", "ὄ"),
+ (0x1F4D, "M", "ὅ"),
+ (0x1F4E, "X"),
+ (0x1F50, "V"),
+ (0x1F58, "X"),
+ (0x1F59, "M", "ὑ"),
+ (0x1F5A, "X"),
+ (0x1F5B, "M", "ὓ"),
+ (0x1F5C, "X"),
+ (0x1F5D, "M", "ὕ"),
+ ]
+
+
+def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1F5E, "X"),
+ (0x1F5F, "M", "ὗ"),
+ (0x1F60, "V"),
+ (0x1F68, "M", "ὠ"),
+ (0x1F69, "M", "ὡ"),
+ (0x1F6A, "M", "ὢ"),
+ (0x1F6B, "M", "ὣ"),
+ (0x1F6C, "M", "ὤ"),
+ (0x1F6D, "M", "ὥ"),
+ (0x1F6E, "M", "ὦ"),
+ (0x1F6F, "M", "ὧ"),
+ (0x1F70, "V"),
+ (0x1F71, "M", "ά"),
+ (0x1F72, "V"),
+ (0x1F73, "M", "έ"),
+ (0x1F74, "V"),
+ (0x1F75, "M", "ή"),
+ (0x1F76, "V"),
+ (0x1F77, "M", "ί"),
+ (0x1F78, "V"),
+ (0x1F79, "M", "ό"),
+ (0x1F7A, "V"),
+ (0x1F7B, "M", "ύ"),
+ (0x1F7C, "V"),
+ (0x1F7D, "M", "ώ"),
+ (0x1F7E, "X"),
+ (0x1F80, "M", "ἀι"),
+ (0x1F81, "M", "ἁι"),
+ (0x1F82, "M", "ἂι"),
+ (0x1F83, "M", "ἃι"),
+ (0x1F84, "M", "ἄι"),
+ (0x1F85, "M", "ἅι"),
+ (0x1F86, "M", "ἆι"),
+ (0x1F87, "M", "ἇι"),
+ (0x1F88, "M", "ἀι"),
+ (0x1F89, "M", "ἁι"),
+ (0x1F8A, "M", "ἂι"),
+ (0x1F8B, "M", "ἃι"),
+ (0x1F8C, "M", "ἄι"),
+ (0x1F8D, "M", "ἅι"),
+ (0x1F8E, "M", "ἆι"),
+ (0x1F8F, "M", "ἇι"),
+ (0x1F90, "M", "ἠι"),
+ (0x1F91, "M", "ἡι"),
+ (0x1F92, "M", "ἢι"),
+ (0x1F93, "M", "ἣι"),
+ (0x1F94, "M", "ἤι"),
+ (0x1F95, "M", "ἥι"),
+ (0x1F96, "M", "ἦι"),
+ (0x1F97, "M", "ἧι"),
+ (0x1F98, "M", "ἠι"),
+ (0x1F99, "M", "ἡι"),
+ (0x1F9A, "M", "ἢι"),
+ (0x1F9B, "M", "ἣι"),
+ (0x1F9C, "M", "ἤι"),
+ (0x1F9D, "M", "ἥι"),
+ (0x1F9E, "M", "ἦι"),
+ (0x1F9F, "M", "ἧι"),
+ (0x1FA0, "M", "ὠι"),
+ (0x1FA1, "M", "ὡι"),
+ (0x1FA2, "M", "ὢι"),
+ (0x1FA3, "M", "ὣι"),
+ (0x1FA4, "M", "ὤι"),
+ (0x1FA5, "M", "ὥι"),
+ (0x1FA6, "M", "ὦι"),
+ (0x1FA7, "M", "ὧι"),
+ (0x1FA8, "M", "ὠι"),
+ (0x1FA9, "M", "ὡι"),
+ (0x1FAA, "M", "ὢι"),
+ (0x1FAB, "M", "ὣι"),
+ (0x1FAC, "M", "ὤι"),
+ (0x1FAD, "M", "ὥι"),
+ (0x1FAE, "M", "ὦι"),
+ (0x1FAF, "M", "ὧι"),
+ (0x1FB0, "V"),
+ (0x1FB2, "M", "ὰι"),
+ (0x1FB3, "M", "αι"),
+ (0x1FB4, "M", "άι"),
+ (0x1FB5, "X"),
+ (0x1FB6, "V"),
+ (0x1FB7, "M", "ᾶι"),
+ (0x1FB8, "M", "ᾰ"),
+ (0x1FB9, "M", "ᾱ"),
+ (0x1FBA, "M", "ὰ"),
+ (0x1FBB, "M", "ά"),
+ (0x1FBC, "M", "αι"),
+ (0x1FBD, "M", " ̓"),
+ (0x1FBE, "M", "ι"),
+ (0x1FBF, "M", " ̓"),
+ (0x1FC0, "M", " ͂"),
+ (0x1FC1, "M", " ̈͂"),
+ (0x1FC2, "M", "ὴι"),
+ (0x1FC3, "M", "ηι"),
+ (0x1FC4, "M", "ήι"),
+ (0x1FC5, "X"),
+ (0x1FC6, "V"),
+ (0x1FC7, "M", "ῆι"),
+ (0x1FC8, "M", "ὲ"),
+ (0x1FC9, "M", "έ"),
+ (0x1FCA, "M", "ὴ"),
+ ]
+
+
+def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1FCB, "M", "ή"),
+ (0x1FCC, "M", "ηι"),
+ (0x1FCD, "M", " ̓̀"),
+ (0x1FCE, "M", " ̓́"),
+ (0x1FCF, "M", " ̓͂"),
+ (0x1FD0, "V"),
+ (0x1FD3, "M", "ΐ"),
+ (0x1FD4, "X"),
+ (0x1FD6, "V"),
+ (0x1FD8, "M", "ῐ"),
+ (0x1FD9, "M", "ῑ"),
+ (0x1FDA, "M", "ὶ"),
+ (0x1FDB, "M", "ί"),
+ (0x1FDC, "X"),
+ (0x1FDD, "M", " ̔̀"),
+ (0x1FDE, "M", " ̔́"),
+ (0x1FDF, "M", " ̔͂"),
+ (0x1FE0, "V"),
+ (0x1FE3, "M", "ΰ"),
+ (0x1FE4, "V"),
+ (0x1FE8, "M", "ῠ"),
+ (0x1FE9, "M", "ῡ"),
+ (0x1FEA, "M", "ὺ"),
+ (0x1FEB, "M", "ύ"),
+ (0x1FEC, "M", "ῥ"),
+ (0x1FED, "M", " ̈̀"),
+ (0x1FEE, "M", " ̈́"),
+ (0x1FEF, "M", "`"),
+ (0x1FF0, "X"),
+ (0x1FF2, "M", "ὼι"),
+ (0x1FF3, "M", "ωι"),
+ (0x1FF4, "M", "ώι"),
+ (0x1FF5, "X"),
+ (0x1FF6, "V"),
+ (0x1FF7, "M", "ῶι"),
+ (0x1FF8, "M", "ὸ"),
+ (0x1FF9, "M", "ό"),
+ (0x1FFA, "M", "ὼ"),
+ (0x1FFB, "M", "ώ"),
+ (0x1FFC, "M", "ωι"),
+ (0x1FFD, "M", " ́"),
+ (0x1FFE, "M", " ̔"),
+ (0x1FFF, "X"),
+ (0x2000, "M", " "),
+ (0x200B, "I"),
+ (0x200C, "D", ""),
+ (0x200E, "X"),
+ (0x2010, "V"),
+ (0x2011, "M", "‐"),
+ (0x2012, "V"),
+ (0x2017, "M", " ̳"),
+ (0x2018, "V"),
+ (0x2024, "X"),
+ (0x2027, "V"),
+ (0x2028, "X"),
+ (0x202F, "M", " "),
+ (0x2030, "V"),
+ (0x2033, "M", "′′"),
+ (0x2034, "M", "′′′"),
+ (0x2035, "V"),
+ (0x2036, "M", "‵‵"),
+ (0x2037, "M", "‵‵‵"),
+ (0x2038, "V"),
+ (0x203C, "M", "!!"),
+ (0x203D, "V"),
+ (0x203E, "M", " ̅"),
+ (0x203F, "V"),
+ (0x2047, "M", "??"),
+ (0x2048, "M", "?!"),
+ (0x2049, "M", "!?"),
+ (0x204A, "V"),
+ (0x2057, "M", "′′′′"),
+ (0x2058, "V"),
+ (0x205F, "M", " "),
+ (0x2060, "I"),
+ (0x2065, "X"),
+ (0x206A, "I"),
+ (0x2070, "M", "0"),
+ (0x2071, "M", "i"),
+ (0x2072, "X"),
+ (0x2074, "M", "4"),
+ (0x2075, "M", "5"),
+ (0x2076, "M", "6"),
+ (0x2077, "M", "7"),
+ (0x2078, "M", "8"),
+ (0x2079, "M", "9"),
+ (0x207A, "M", "+"),
+ (0x207B, "M", "−"),
+ (0x207C, "M", "="),
+ (0x207D, "M", "("),
+ (0x207E, "M", ")"),
+ (0x207F, "M", "n"),
+ (0x2080, "M", "0"),
+ (0x2081, "M", "1"),
+ (0x2082, "M", "2"),
+ (0x2083, "M", "3"),
+ (0x2084, "M", "4"),
+ (0x2085, "M", "5"),
+ (0x2086, "M", "6"),
+ (0x2087, "M", "7"),
+ ]
+
+
+def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2088, "M", "8"),
+ (0x2089, "M", "9"),
+ (0x208A, "M", "+"),
+ (0x208B, "M", "−"),
+ (0x208C, "M", "="),
+ (0x208D, "M", "("),
+ (0x208E, "M", ")"),
+ (0x208F, "X"),
+ (0x2090, "M", "a"),
+ (0x2091, "M", "e"),
+ (0x2092, "M", "o"),
+ (0x2093, "M", "x"),
+ (0x2094, "M", "ə"),
+ (0x2095, "M", "h"),
+ (0x2096, "M", "k"),
+ (0x2097, "M", "l"),
+ (0x2098, "M", "m"),
+ (0x2099, "M", "n"),
+ (0x209A, "M", "p"),
+ (0x209B, "M", "s"),
+ (0x209C, "M", "t"),
+ (0x209D, "X"),
+ (0x20A0, "V"),
+ (0x20A8, "M", "rs"),
+ (0x20A9, "V"),
+ (0x20C1, "X"),
+ (0x20D0, "V"),
+ (0x20F1, "X"),
+ (0x2100, "M", "a/c"),
+ (0x2101, "M", "a/s"),
+ (0x2102, "M", "c"),
+ (0x2103, "M", "°c"),
+ (0x2104, "V"),
+ (0x2105, "M", "c/o"),
+ (0x2106, "M", "c/u"),
+ (0x2107, "M", "ɛ"),
+ (0x2108, "V"),
+ (0x2109, "M", "°f"),
+ (0x210A, "M", "g"),
+ (0x210B, "M", "h"),
+ (0x210F, "M", "ħ"),
+ (0x2110, "M", "i"),
+ (0x2112, "M", "l"),
+ (0x2114, "V"),
+ (0x2115, "M", "n"),
+ (0x2116, "M", "no"),
+ (0x2117, "V"),
+ (0x2119, "M", "p"),
+ (0x211A, "M", "q"),
+ (0x211B, "M", "r"),
+ (0x211E, "V"),
+ (0x2120, "M", "sm"),
+ (0x2121, "M", "tel"),
+ (0x2122, "M", "tm"),
+ (0x2123, "V"),
+ (0x2124, "M", "z"),
+ (0x2125, "V"),
+ (0x2126, "M", "ω"),
+ (0x2127, "V"),
+ (0x2128, "M", "z"),
+ (0x2129, "V"),
+ (0x212A, "M", "k"),
+ (0x212B, "M", "å"),
+ (0x212C, "M", "b"),
+ (0x212D, "M", "c"),
+ (0x212E, "V"),
+ (0x212F, "M", "e"),
+ (0x2131, "M", "f"),
+ (0x2132, "M", "ⅎ"),
+ (0x2133, "M", "m"),
+ (0x2134, "M", "o"),
+ (0x2135, "M", "א"),
+ (0x2136, "M", "ב"),
+ (0x2137, "M", "ג"),
+ (0x2138, "M", "ד"),
+ (0x2139, "M", "i"),
+ (0x213A, "V"),
+ (0x213B, "M", "fax"),
+ (0x213C, "M", "π"),
+ (0x213D, "M", "γ"),
+ (0x213F, "M", "π"),
+ (0x2140, "M", "∑"),
+ (0x2141, "V"),
+ (0x2145, "M", "d"),
+ (0x2147, "M", "e"),
+ (0x2148, "M", "i"),
+ (0x2149, "M", "j"),
+ (0x214A, "V"),
+ (0x2150, "M", "1⁄7"),
+ (0x2151, "M", "1⁄9"),
+ (0x2152, "M", "1⁄10"),
+ (0x2153, "M", "1⁄3"),
+ (0x2154, "M", "2⁄3"),
+ (0x2155, "M", "1⁄5"),
+ (0x2156, "M", "2⁄5"),
+ (0x2157, "M", "3⁄5"),
+ (0x2158, "M", "4⁄5"),
+ (0x2159, "M", "1⁄6"),
+ (0x215A, "M", "5⁄6"),
+ (0x215B, "M", "1⁄8"),
+ ]
+
+
+def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x215C, "M", "3⁄8"),
+ (0x215D, "M", "5⁄8"),
+ (0x215E, "M", "7⁄8"),
+ (0x215F, "M", "1⁄"),
+ (0x2160, "M", "i"),
+ (0x2161, "M", "ii"),
+ (0x2162, "M", "iii"),
+ (0x2163, "M", "iv"),
+ (0x2164, "M", "v"),
+ (0x2165, "M", "vi"),
+ (0x2166, "M", "vii"),
+ (0x2167, "M", "viii"),
+ (0x2168, "M", "ix"),
+ (0x2169, "M", "x"),
+ (0x216A, "M", "xi"),
+ (0x216B, "M", "xii"),
+ (0x216C, "M", "l"),
+ (0x216D, "M", "c"),
+ (0x216E, "M", "d"),
+ (0x216F, "M", "m"),
+ (0x2170, "M", "i"),
+ (0x2171, "M", "ii"),
+ (0x2172, "M", "iii"),
+ (0x2173, "M", "iv"),
+ (0x2174, "M", "v"),
+ (0x2175, "M", "vi"),
+ (0x2176, "M", "vii"),
+ (0x2177, "M", "viii"),
+ (0x2178, "M", "ix"),
+ (0x2179, "M", "x"),
+ (0x217A, "M", "xi"),
+ (0x217B, "M", "xii"),
+ (0x217C, "M", "l"),
+ (0x217D, "M", "c"),
+ (0x217E, "M", "d"),
+ (0x217F, "M", "m"),
+ (0x2180, "V"),
+ (0x2183, "M", "ↄ"),
+ (0x2184, "V"),
+ (0x2189, "M", "0⁄3"),
+ (0x218A, "V"),
+ (0x218C, "X"),
+ (0x2190, "V"),
+ (0x222C, "M", "∫∫"),
+ (0x222D, "M", "∫∫∫"),
+ (0x222E, "V"),
+ (0x222F, "M", "∮∮"),
+ (0x2230, "M", "∮∮∮"),
+ (0x2231, "V"),
+ (0x2329, "M", "〈"),
+ (0x232A, "M", "〉"),
+ (0x232B, "V"),
+ (0x242A, "X"),
+ (0x2440, "V"),
+ (0x244B, "X"),
+ (0x2460, "M", "1"),
+ (0x2461, "M", "2"),
+ (0x2462, "M", "3"),
+ (0x2463, "M", "4"),
+ (0x2464, "M", "5"),
+ (0x2465, "M", "6"),
+ (0x2466, "M", "7"),
+ (0x2467, "M", "8"),
+ (0x2468, "M", "9"),
+ (0x2469, "M", "10"),
+ (0x246A, "M", "11"),
+ (0x246B, "M", "12"),
+ (0x246C, "M", "13"),
+ (0x246D, "M", "14"),
+ (0x246E, "M", "15"),
+ (0x246F, "M", "16"),
+ (0x2470, "M", "17"),
+ (0x2471, "M", "18"),
+ (0x2472, "M", "19"),
+ (0x2473, "M", "20"),
+ (0x2474, "M", "(1)"),
+ (0x2475, "M", "(2)"),
+ (0x2476, "M", "(3)"),
+ (0x2477, "M", "(4)"),
+ (0x2478, "M", "(5)"),
+ (0x2479, "M", "(6)"),
+ (0x247A, "M", "(7)"),
+ (0x247B, "M", "(8)"),
+ (0x247C, "M", "(9)"),
+ (0x247D, "M", "(10)"),
+ (0x247E, "M", "(11)"),
+ (0x247F, "M", "(12)"),
+ (0x2480, "M", "(13)"),
+ (0x2481, "M", "(14)"),
+ (0x2482, "M", "(15)"),
+ (0x2483, "M", "(16)"),
+ (0x2484, "M", "(17)"),
+ (0x2485, "M", "(18)"),
+ (0x2486, "M", "(19)"),
+ (0x2487, "M", "(20)"),
+ (0x2488, "X"),
+ (0x249C, "M", "(a)"),
+ (0x249D, "M", "(b)"),
+ (0x249E, "M", "(c)"),
+ (0x249F, "M", "(d)"),
+ ]
+
+
+def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x24A0, "M", "(e)"),
+ (0x24A1, "M", "(f)"),
+ (0x24A2, "M", "(g)"),
+ (0x24A3, "M", "(h)"),
+ (0x24A4, "M", "(i)"),
+ (0x24A5, "M", "(j)"),
+ (0x24A6, "M", "(k)"),
+ (0x24A7, "M", "(l)"),
+ (0x24A8, "M", "(m)"),
+ (0x24A9, "M", "(n)"),
+ (0x24AA, "M", "(o)"),
+ (0x24AB, "M", "(p)"),
+ (0x24AC, "M", "(q)"),
+ (0x24AD, "M", "(r)"),
+ (0x24AE, "M", "(s)"),
+ (0x24AF, "M", "(t)"),
+ (0x24B0, "M", "(u)"),
+ (0x24B1, "M", "(v)"),
+ (0x24B2, "M", "(w)"),
+ (0x24B3, "M", "(x)"),
+ (0x24B4, "M", "(y)"),
+ (0x24B5, "M", "(z)"),
+ (0x24B6, "M", "a"),
+ (0x24B7, "M", "b"),
+ (0x24B8, "M", "c"),
+ (0x24B9, "M", "d"),
+ (0x24BA, "M", "e"),
+ (0x24BB, "M", "f"),
+ (0x24BC, "M", "g"),
+ (0x24BD, "M", "h"),
+ (0x24BE, "M", "i"),
+ (0x24BF, "M", "j"),
+ (0x24C0, "M", "k"),
+ (0x24C1, "M", "l"),
+ (0x24C2, "M", "m"),
+ (0x24C3, "M", "n"),
+ (0x24C4, "M", "o"),
+ (0x24C5, "M", "p"),
+ (0x24C6, "M", "q"),
+ (0x24C7, "M", "r"),
+ (0x24C8, "M", "s"),
+ (0x24C9, "M", "t"),
+ (0x24CA, "M", "u"),
+ (0x24CB, "M", "v"),
+ (0x24CC, "M", "w"),
+ (0x24CD, "M", "x"),
+ (0x24CE, "M", "y"),
+ (0x24CF, "M", "z"),
+ (0x24D0, "M", "a"),
+ (0x24D1, "M", "b"),
+ (0x24D2, "M", "c"),
+ (0x24D3, "M", "d"),
+ (0x24D4, "M", "e"),
+ (0x24D5, "M", "f"),
+ (0x24D6, "M", "g"),
+ (0x24D7, "M", "h"),
+ (0x24D8, "M", "i"),
+ (0x24D9, "M", "j"),
+ (0x24DA, "M", "k"),
+ (0x24DB, "M", "l"),
+ (0x24DC, "M", "m"),
+ (0x24DD, "M", "n"),
+ (0x24DE, "M", "o"),
+ (0x24DF, "M", "p"),
+ (0x24E0, "M", "q"),
+ (0x24E1, "M", "r"),
+ (0x24E2, "M", "s"),
+ (0x24E3, "M", "t"),
+ (0x24E4, "M", "u"),
+ (0x24E5, "M", "v"),
+ (0x24E6, "M", "w"),
+ (0x24E7, "M", "x"),
+ (0x24E8, "M", "y"),
+ (0x24E9, "M", "z"),
+ (0x24EA, "M", "0"),
+ (0x24EB, "V"),
+ (0x2A0C, "M", "∫∫∫∫"),
+ (0x2A0D, "V"),
+ (0x2A74, "M", "::="),
+ (0x2A75, "M", "=="),
+ (0x2A76, "M", "==="),
+ (0x2A77, "V"),
+ (0x2ADC, "M", "⫝̸"),
+ (0x2ADD, "V"),
+ (0x2B74, "X"),
+ (0x2B76, "V"),
+ (0x2B96, "X"),
+ (0x2B97, "V"),
+ (0x2C00, "M", "ⰰ"),
+ (0x2C01, "M", "ⰱ"),
+ (0x2C02, "M", "ⰲ"),
+ (0x2C03, "M", "ⰳ"),
+ (0x2C04, "M", "ⰴ"),
+ (0x2C05, "M", "ⰵ"),
+ (0x2C06, "M", "ⰶ"),
+ (0x2C07, "M", "ⰷ"),
+ (0x2C08, "M", "ⰸ"),
+ (0x2C09, "M", "ⰹ"),
+ (0x2C0A, "M", "ⰺ"),
+ (0x2C0B, "M", "ⰻ"),
+ ]
+
+
+def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2C0C, "M", "ⰼ"),
+ (0x2C0D, "M", "ⰽ"),
+ (0x2C0E, "M", "ⰾ"),
+ (0x2C0F, "M", "ⰿ"),
+ (0x2C10, "M", "ⱀ"),
+ (0x2C11, "M", "ⱁ"),
+ (0x2C12, "M", "ⱂ"),
+ (0x2C13, "M", "ⱃ"),
+ (0x2C14, "M", "ⱄ"),
+ (0x2C15, "M", "ⱅ"),
+ (0x2C16, "M", "ⱆ"),
+ (0x2C17, "M", "ⱇ"),
+ (0x2C18, "M", "ⱈ"),
+ (0x2C19, "M", "ⱉ"),
+ (0x2C1A, "M", "ⱊ"),
+ (0x2C1B, "M", "ⱋ"),
+ (0x2C1C, "M", "ⱌ"),
+ (0x2C1D, "M", "ⱍ"),
+ (0x2C1E, "M", "ⱎ"),
+ (0x2C1F, "M", "ⱏ"),
+ (0x2C20, "M", "ⱐ"),
+ (0x2C21, "M", "ⱑ"),
+ (0x2C22, "M", "ⱒ"),
+ (0x2C23, "M", "ⱓ"),
+ (0x2C24, "M", "ⱔ"),
+ (0x2C25, "M", "ⱕ"),
+ (0x2C26, "M", "ⱖ"),
+ (0x2C27, "M", "ⱗ"),
+ (0x2C28, "M", "ⱘ"),
+ (0x2C29, "M", "ⱙ"),
+ (0x2C2A, "M", "ⱚ"),
+ (0x2C2B, "M", "ⱛ"),
+ (0x2C2C, "M", "ⱜ"),
+ (0x2C2D, "M", "ⱝ"),
+ (0x2C2E, "M", "ⱞ"),
+ (0x2C2F, "M", "ⱟ"),
+ (0x2C30, "V"),
+ (0x2C60, "M", "ⱡ"),
+ (0x2C61, "V"),
+ (0x2C62, "M", "ɫ"),
+ (0x2C63, "M", "ᵽ"),
+ (0x2C64, "M", "ɽ"),
+ (0x2C65, "V"),
+ (0x2C67, "M", "ⱨ"),
+ (0x2C68, "V"),
+ (0x2C69, "M", "ⱪ"),
+ (0x2C6A, "V"),
+ (0x2C6B, "M", "ⱬ"),
+ (0x2C6C, "V"),
+ (0x2C6D, "M", "ɑ"),
+ (0x2C6E, "M", "ɱ"),
+ (0x2C6F, "M", "ɐ"),
+ (0x2C70, "M", "ɒ"),
+ (0x2C71, "V"),
+ (0x2C72, "M", "ⱳ"),
+ (0x2C73, "V"),
+ (0x2C75, "M", "ⱶ"),
+ (0x2C76, "V"),
+ (0x2C7C, "M", "j"),
+ (0x2C7D, "M", "v"),
+ (0x2C7E, "M", "ȿ"),
+ (0x2C7F, "M", "ɀ"),
+ (0x2C80, "M", "ⲁ"),
+ (0x2C81, "V"),
+ (0x2C82, "M", "ⲃ"),
+ (0x2C83, "V"),
+ (0x2C84, "M", "ⲅ"),
+ (0x2C85, "V"),
+ (0x2C86, "M", "ⲇ"),
+ (0x2C87, "V"),
+ (0x2C88, "M", "ⲉ"),
+ (0x2C89, "V"),
+ (0x2C8A, "M", "ⲋ"),
+ (0x2C8B, "V"),
+ (0x2C8C, "M", "ⲍ"),
+ (0x2C8D, "V"),
+ (0x2C8E, "M", "ⲏ"),
+ (0x2C8F, "V"),
+ (0x2C90, "M", "ⲑ"),
+ (0x2C91, "V"),
+ (0x2C92, "M", "ⲓ"),
+ (0x2C93, "V"),
+ (0x2C94, "M", "ⲕ"),
+ (0x2C95, "V"),
+ (0x2C96, "M", "ⲗ"),
+ (0x2C97, "V"),
+ (0x2C98, "M", "ⲙ"),
+ (0x2C99, "V"),
+ (0x2C9A, "M", "ⲛ"),
+ (0x2C9B, "V"),
+ (0x2C9C, "M", "ⲝ"),
+ (0x2C9D, "V"),
+ (0x2C9E, "M", "ⲟ"),
+ (0x2C9F, "V"),
+ (0x2CA0, "M", "ⲡ"),
+ (0x2CA1, "V"),
+ (0x2CA2, "M", "ⲣ"),
+ (0x2CA3, "V"),
+ (0x2CA4, "M", "ⲥ"),
+ (0x2CA5, "V"),
+ ]
+
+
+def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2CA6, "M", "ⲧ"),
+ (0x2CA7, "V"),
+ (0x2CA8, "M", "ⲩ"),
+ (0x2CA9, "V"),
+ (0x2CAA, "M", "ⲫ"),
+ (0x2CAB, "V"),
+ (0x2CAC, "M", "ⲭ"),
+ (0x2CAD, "V"),
+ (0x2CAE, "M", "ⲯ"),
+ (0x2CAF, "V"),
+ (0x2CB0, "M", "ⲱ"),
+ (0x2CB1, "V"),
+ (0x2CB2, "M", "ⲳ"),
+ (0x2CB3, "V"),
+ (0x2CB4, "M", "ⲵ"),
+ (0x2CB5, "V"),
+ (0x2CB6, "M", "ⲷ"),
+ (0x2CB7, "V"),
+ (0x2CB8, "M", "ⲹ"),
+ (0x2CB9, "V"),
+ (0x2CBA, "M", "ⲻ"),
+ (0x2CBB, "V"),
+ (0x2CBC, "M", "ⲽ"),
+ (0x2CBD, "V"),
+ (0x2CBE, "M", "ⲿ"),
+ (0x2CBF, "V"),
+ (0x2CC0, "M", "ⳁ"),
+ (0x2CC1, "V"),
+ (0x2CC2, "M", "ⳃ"),
+ (0x2CC3, "V"),
+ (0x2CC4, "M", "ⳅ"),
+ (0x2CC5, "V"),
+ (0x2CC6, "M", "ⳇ"),
+ (0x2CC7, "V"),
+ (0x2CC8, "M", "ⳉ"),
+ (0x2CC9, "V"),
+ (0x2CCA, "M", "ⳋ"),
+ (0x2CCB, "V"),
+ (0x2CCC, "M", "ⳍ"),
+ (0x2CCD, "V"),
+ (0x2CCE, "M", "ⳏ"),
+ (0x2CCF, "V"),
+ (0x2CD0, "M", "ⳑ"),
+ (0x2CD1, "V"),
+ (0x2CD2, "M", "ⳓ"),
+ (0x2CD3, "V"),
+ (0x2CD4, "M", "ⳕ"),
+ (0x2CD5, "V"),
+ (0x2CD6, "M", "ⳗ"),
+ (0x2CD7, "V"),
+ (0x2CD8, "M", "ⳙ"),
+ (0x2CD9, "V"),
+ (0x2CDA, "M", "ⳛ"),
+ (0x2CDB, "V"),
+ (0x2CDC, "M", "ⳝ"),
+ (0x2CDD, "V"),
+ (0x2CDE, "M", "ⳟ"),
+ (0x2CDF, "V"),
+ (0x2CE0, "M", "ⳡ"),
+ (0x2CE1, "V"),
+ (0x2CE2, "M", "ⳣ"),
+ (0x2CE3, "V"),
+ (0x2CEB, "M", "ⳬ"),
+ (0x2CEC, "V"),
+ (0x2CED, "M", "ⳮ"),
+ (0x2CEE, "V"),
+ (0x2CF2, "M", "ⳳ"),
+ (0x2CF3, "V"),
+ (0x2CF4, "X"),
+ (0x2CF9, "V"),
+ (0x2D26, "X"),
+ (0x2D27, "V"),
+ (0x2D28, "X"),
+ (0x2D2D, "V"),
+ (0x2D2E, "X"),
+ (0x2D30, "V"),
+ (0x2D68, "X"),
+ (0x2D6F, "M", "ⵡ"),
+ (0x2D70, "V"),
+ (0x2D71, "X"),
+ (0x2D7F, "V"),
+ (0x2D97, "X"),
+ (0x2DA0, "V"),
+ (0x2DA7, "X"),
+ (0x2DA8, "V"),
+ (0x2DAF, "X"),
+ (0x2DB0, "V"),
+ (0x2DB7, "X"),
+ (0x2DB8, "V"),
+ (0x2DBF, "X"),
+ (0x2DC0, "V"),
+ (0x2DC7, "X"),
+ (0x2DC8, "V"),
+ (0x2DCF, "X"),
+ (0x2DD0, "V"),
+ (0x2DD7, "X"),
+ (0x2DD8, "V"),
+ (0x2DDF, "X"),
+ (0x2DE0, "V"),
+ (0x2E5E, "X"),
+ ]
+
+
+def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2E80, "V"),
+ (0x2E9A, "X"),
+ (0x2E9B, "V"),
+ (0x2E9F, "M", "母"),
+ (0x2EA0, "V"),
+ (0x2EF3, "M", "龟"),
+ (0x2EF4, "X"),
+ (0x2F00, "M", "一"),
+ (0x2F01, "M", "丨"),
+ (0x2F02, "M", "丶"),
+ (0x2F03, "M", "丿"),
+ (0x2F04, "M", "乙"),
+ (0x2F05, "M", "亅"),
+ (0x2F06, "M", "二"),
+ (0x2F07, "M", "亠"),
+ (0x2F08, "M", "人"),
+ (0x2F09, "M", "儿"),
+ (0x2F0A, "M", "入"),
+ (0x2F0B, "M", "八"),
+ (0x2F0C, "M", "冂"),
+ (0x2F0D, "M", "冖"),
+ (0x2F0E, "M", "冫"),
+ (0x2F0F, "M", "几"),
+ (0x2F10, "M", "凵"),
+ (0x2F11, "M", "刀"),
+ (0x2F12, "M", "力"),
+ (0x2F13, "M", "勹"),
+ (0x2F14, "M", "匕"),
+ (0x2F15, "M", "匚"),
+ (0x2F16, "M", "匸"),
+ (0x2F17, "M", "十"),
+ (0x2F18, "M", "卜"),
+ (0x2F19, "M", "卩"),
+ (0x2F1A, "M", "厂"),
+ (0x2F1B, "M", "厶"),
+ (0x2F1C, "M", "又"),
+ (0x2F1D, "M", "口"),
+ (0x2F1E, "M", "囗"),
+ (0x2F1F, "M", "土"),
+ (0x2F20, "M", "士"),
+ (0x2F21, "M", "夂"),
+ (0x2F22, "M", "夊"),
+ (0x2F23, "M", "夕"),
+ (0x2F24, "M", "大"),
+ (0x2F25, "M", "女"),
+ (0x2F26, "M", "子"),
+ (0x2F27, "M", "宀"),
+ (0x2F28, "M", "寸"),
+ (0x2F29, "M", "小"),
+ (0x2F2A, "M", "尢"),
+ (0x2F2B, "M", "尸"),
+ (0x2F2C, "M", "屮"),
+ (0x2F2D, "M", "山"),
+ (0x2F2E, "M", "巛"),
+ (0x2F2F, "M", "工"),
+ (0x2F30, "M", "己"),
+ (0x2F31, "M", "巾"),
+ (0x2F32, "M", "干"),
+ (0x2F33, "M", "幺"),
+ (0x2F34, "M", "广"),
+ (0x2F35, "M", "廴"),
+ (0x2F36, "M", "廾"),
+ (0x2F37, "M", "弋"),
+ (0x2F38, "M", "弓"),
+ (0x2F39, "M", "彐"),
+ (0x2F3A, "M", "彡"),
+ (0x2F3B, "M", "彳"),
+ (0x2F3C, "M", "心"),
+ (0x2F3D, "M", "戈"),
+ (0x2F3E, "M", "戶"),
+ (0x2F3F, "M", "手"),
+ (0x2F40, "M", "支"),
+ (0x2F41, "M", "攴"),
+ (0x2F42, "M", "文"),
+ (0x2F43, "M", "斗"),
+ (0x2F44, "M", "斤"),
+ (0x2F45, "M", "方"),
+ (0x2F46, "M", "无"),
+ (0x2F47, "M", "日"),
+ (0x2F48, "M", "曰"),
+ (0x2F49, "M", "月"),
+ (0x2F4A, "M", "木"),
+ (0x2F4B, "M", "欠"),
+ (0x2F4C, "M", "止"),
+ (0x2F4D, "M", "歹"),
+ (0x2F4E, "M", "殳"),
+ (0x2F4F, "M", "毋"),
+ (0x2F50, "M", "比"),
+ (0x2F51, "M", "毛"),
+ (0x2F52, "M", "氏"),
+ (0x2F53, "M", "气"),
+ (0x2F54, "M", "水"),
+ (0x2F55, "M", "火"),
+ (0x2F56, "M", "爪"),
+ (0x2F57, "M", "父"),
+ (0x2F58, "M", "爻"),
+ (0x2F59, "M", "爿"),
+ (0x2F5A, "M", "片"),
+ (0x2F5B, "M", "牙"),
+ (0x2F5C, "M", "牛"),
+ ]
+
+
+def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F5D, "M", "犬"),
+ (0x2F5E, "M", "玄"),
+ (0x2F5F, "M", "玉"),
+ (0x2F60, "M", "瓜"),
+ (0x2F61, "M", "瓦"),
+ (0x2F62, "M", "甘"),
+ (0x2F63, "M", "生"),
+ (0x2F64, "M", "用"),
+ (0x2F65, "M", "田"),
+ (0x2F66, "M", "疋"),
+ (0x2F67, "M", "疒"),
+ (0x2F68, "M", "癶"),
+ (0x2F69, "M", "白"),
+ (0x2F6A, "M", "皮"),
+ (0x2F6B, "M", "皿"),
+ (0x2F6C, "M", "目"),
+ (0x2F6D, "M", "矛"),
+ (0x2F6E, "M", "矢"),
+ (0x2F6F, "M", "石"),
+ (0x2F70, "M", "示"),
+ (0x2F71, "M", "禸"),
+ (0x2F72, "M", "禾"),
+ (0x2F73, "M", "穴"),
+ (0x2F74, "M", "立"),
+ (0x2F75, "M", "竹"),
+ (0x2F76, "M", "米"),
+ (0x2F77, "M", "糸"),
+ (0x2F78, "M", "缶"),
+ (0x2F79, "M", "网"),
+ (0x2F7A, "M", "羊"),
+ (0x2F7B, "M", "羽"),
+ (0x2F7C, "M", "老"),
+ (0x2F7D, "M", "而"),
+ (0x2F7E, "M", "耒"),
+ (0x2F7F, "M", "耳"),
+ (0x2F80, "M", "聿"),
+ (0x2F81, "M", "肉"),
+ (0x2F82, "M", "臣"),
+ (0x2F83, "M", "自"),
+ (0x2F84, "M", "至"),
+ (0x2F85, "M", "臼"),
+ (0x2F86, "M", "舌"),
+ (0x2F87, "M", "舛"),
+ (0x2F88, "M", "舟"),
+ (0x2F89, "M", "艮"),
+ (0x2F8A, "M", "色"),
+ (0x2F8B, "M", "艸"),
+ (0x2F8C, "M", "虍"),
+ (0x2F8D, "M", "虫"),
+ (0x2F8E, "M", "血"),
+ (0x2F8F, "M", "行"),
+ (0x2F90, "M", "衣"),
+ (0x2F91, "M", "襾"),
+ (0x2F92, "M", "見"),
+ (0x2F93, "M", "角"),
+ (0x2F94, "M", "言"),
+ (0x2F95, "M", "谷"),
+ (0x2F96, "M", "豆"),
+ (0x2F97, "M", "豕"),
+ (0x2F98, "M", "豸"),
+ (0x2F99, "M", "貝"),
+ (0x2F9A, "M", "赤"),
+ (0x2F9B, "M", "走"),
+ (0x2F9C, "M", "足"),
+ (0x2F9D, "M", "身"),
+ (0x2F9E, "M", "車"),
+ (0x2F9F, "M", "辛"),
+ (0x2FA0, "M", "辰"),
+ (0x2FA1, "M", "辵"),
+ (0x2FA2, "M", "邑"),
+ (0x2FA3, "M", "酉"),
+ (0x2FA4, "M", "釆"),
+ (0x2FA5, "M", "里"),
+ (0x2FA6, "M", "金"),
+ (0x2FA7, "M", "長"),
+ (0x2FA8, "M", "門"),
+ (0x2FA9, "M", "阜"),
+ (0x2FAA, "M", "隶"),
+ (0x2FAB, "M", "隹"),
+ (0x2FAC, "M", "雨"),
+ (0x2FAD, "M", "靑"),
+ (0x2FAE, "M", "非"),
+ (0x2FAF, "M", "面"),
+ (0x2FB0, "M", "革"),
+ (0x2FB1, "M", "韋"),
+ (0x2FB2, "M", "韭"),
+ (0x2FB3, "M", "音"),
+ (0x2FB4, "M", "頁"),
+ (0x2FB5, "M", "風"),
+ (0x2FB6, "M", "飛"),
+ (0x2FB7, "M", "食"),
+ (0x2FB8, "M", "首"),
+ (0x2FB9, "M", "香"),
+ (0x2FBA, "M", "馬"),
+ (0x2FBB, "M", "骨"),
+ (0x2FBC, "M", "高"),
+ (0x2FBD, "M", "髟"),
+ (0x2FBE, "M", "鬥"),
+ (0x2FBF, "M", "鬯"),
+ (0x2FC0, "M", "鬲"),
+ ]
+
+
+def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2FC1, "M", "鬼"),
+ (0x2FC2, "M", "魚"),
+ (0x2FC3, "M", "鳥"),
+ (0x2FC4, "M", "鹵"),
+ (0x2FC5, "M", "鹿"),
+ (0x2FC6, "M", "麥"),
+ (0x2FC7, "M", "麻"),
+ (0x2FC8, "M", "黃"),
+ (0x2FC9, "M", "黍"),
+ (0x2FCA, "M", "黑"),
+ (0x2FCB, "M", "黹"),
+ (0x2FCC, "M", "黽"),
+ (0x2FCD, "M", "鼎"),
+ (0x2FCE, "M", "鼓"),
+ (0x2FCF, "M", "鼠"),
+ (0x2FD0, "M", "鼻"),
+ (0x2FD1, "M", "齊"),
+ (0x2FD2, "M", "齒"),
+ (0x2FD3, "M", "龍"),
+ (0x2FD4, "M", "龜"),
+ (0x2FD5, "M", "龠"),
+ (0x2FD6, "X"),
+ (0x3000, "M", " "),
+ (0x3001, "V"),
+ (0x3002, "M", "."),
+ (0x3003, "V"),
+ (0x3036, "M", "〒"),
+ (0x3037, "V"),
+ (0x3038, "M", "十"),
+ (0x3039, "M", "卄"),
+ (0x303A, "M", "卅"),
+ (0x303B, "V"),
+ (0x3040, "X"),
+ (0x3041, "V"),
+ (0x3097, "X"),
+ (0x3099, "V"),
+ (0x309B, "M", " ゙"),
+ (0x309C, "M", " ゚"),
+ (0x309D, "V"),
+ (0x309F, "M", "より"),
+ (0x30A0, "V"),
+ (0x30FF, "M", "コト"),
+ (0x3100, "X"),
+ (0x3105, "V"),
+ (0x3130, "X"),
+ (0x3131, "M", "ᄀ"),
+ (0x3132, "M", "ᄁ"),
+ (0x3133, "M", "ᆪ"),
+ (0x3134, "M", "ᄂ"),
+ (0x3135, "M", "ᆬ"),
+ (0x3136, "M", "ᆭ"),
+ (0x3137, "M", "ᄃ"),
+ (0x3138, "M", "ᄄ"),
+ (0x3139, "M", "ᄅ"),
+ (0x313A, "M", "ᆰ"),
+ (0x313B, "M", "ᆱ"),
+ (0x313C, "M", "ᆲ"),
+ (0x313D, "M", "ᆳ"),
+ (0x313E, "M", "ᆴ"),
+ (0x313F, "M", "ᆵ"),
+ (0x3140, "M", "ᄚ"),
+ (0x3141, "M", "ᄆ"),
+ (0x3142, "M", "ᄇ"),
+ (0x3143, "M", "ᄈ"),
+ (0x3144, "M", "ᄡ"),
+ (0x3145, "M", "ᄉ"),
+ (0x3146, "M", "ᄊ"),
+ (0x3147, "M", "ᄋ"),
+ (0x3148, "M", "ᄌ"),
+ (0x3149, "M", "ᄍ"),
+ (0x314A, "M", "ᄎ"),
+ (0x314B, "M", "ᄏ"),
+ (0x314C, "M", "ᄐ"),
+ (0x314D, "M", "ᄑ"),
+ (0x314E, "M", "ᄒ"),
+ (0x314F, "M", "ᅡ"),
+ (0x3150, "M", "ᅢ"),
+ (0x3151, "M", "ᅣ"),
+ (0x3152, "M", "ᅤ"),
+ (0x3153, "M", "ᅥ"),
+ (0x3154, "M", "ᅦ"),
+ (0x3155, "M", "ᅧ"),
+ (0x3156, "M", "ᅨ"),
+ (0x3157, "M", "ᅩ"),
+ (0x3158, "M", "ᅪ"),
+ (0x3159, "M", "ᅫ"),
+ (0x315A, "M", "ᅬ"),
+ (0x315B, "M", "ᅭ"),
+ (0x315C, "M", "ᅮ"),
+ (0x315D, "M", "ᅯ"),
+ (0x315E, "M", "ᅰ"),
+ (0x315F, "M", "ᅱ"),
+ (0x3160, "M", "ᅲ"),
+ (0x3161, "M", "ᅳ"),
+ (0x3162, "M", "ᅴ"),
+ (0x3163, "M", "ᅵ"),
+ (0x3164, "I"),
+ (0x3165, "M", "ᄔ"),
+ (0x3166, "M", "ᄕ"),
+ (0x3167, "M", "ᇇ"),
+ ]
+
+
+def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x3168, "M", "ᇈ"),
+ (0x3169, "M", "ᇌ"),
+ (0x316A, "M", "ᇎ"),
+ (0x316B, "M", "ᇓ"),
+ (0x316C, "M", "ᇗ"),
+ (0x316D, "M", "ᇙ"),
+ (0x316E, "M", "ᄜ"),
+ (0x316F, "M", "ᇝ"),
+ (0x3170, "M", "ᇟ"),
+ (0x3171, "M", "ᄝ"),
+ (0x3172, "M", "ᄞ"),
+ (0x3173, "M", "ᄠ"),
+ (0x3174, "M", "ᄢ"),
+ (0x3175, "M", "ᄣ"),
+ (0x3176, "M", "ᄧ"),
+ (0x3177, "M", "ᄩ"),
+ (0x3178, "M", "ᄫ"),
+ (0x3179, "M", "ᄬ"),
+ (0x317A, "M", "ᄭ"),
+ (0x317B, "M", "ᄮ"),
+ (0x317C, "M", "ᄯ"),
+ (0x317D, "M", "ᄲ"),
+ (0x317E, "M", "ᄶ"),
+ (0x317F, "M", "ᅀ"),
+ (0x3180, "M", "ᅇ"),
+ (0x3181, "M", "ᅌ"),
+ (0x3182, "M", "ᇱ"),
+ (0x3183, "M", "ᇲ"),
+ (0x3184, "M", "ᅗ"),
+ (0x3185, "M", "ᅘ"),
+ (0x3186, "M", "ᅙ"),
+ (0x3187, "M", "ᆄ"),
+ (0x3188, "M", "ᆅ"),
+ (0x3189, "M", "ᆈ"),
+ (0x318A, "M", "ᆑ"),
+ (0x318B, "M", "ᆒ"),
+ (0x318C, "M", "ᆔ"),
+ (0x318D, "M", "ᆞ"),
+ (0x318E, "M", "ᆡ"),
+ (0x318F, "X"),
+ (0x3190, "V"),
+ (0x3192, "M", "一"),
+ (0x3193, "M", "二"),
+ (0x3194, "M", "三"),
+ (0x3195, "M", "四"),
+ (0x3196, "M", "上"),
+ (0x3197, "M", "中"),
+ (0x3198, "M", "下"),
+ (0x3199, "M", "甲"),
+ (0x319A, "M", "乙"),
+ (0x319B, "M", "丙"),
+ (0x319C, "M", "丁"),
+ (0x319D, "M", "天"),
+ (0x319E, "M", "地"),
+ (0x319F, "M", "人"),
+ (0x31A0, "V"),
+ (0x31E6, "X"),
+ (0x31F0, "V"),
+ (0x3200, "M", "(ᄀ)"),
+ (0x3201, "M", "(ᄂ)"),
+ (0x3202, "M", "(ᄃ)"),
+ (0x3203, "M", "(ᄅ)"),
+ (0x3204, "M", "(ᄆ)"),
+ (0x3205, "M", "(ᄇ)"),
+ (0x3206, "M", "(ᄉ)"),
+ (0x3207, "M", "(ᄋ)"),
+ (0x3208, "M", "(ᄌ)"),
+ (0x3209, "M", "(ᄎ)"),
+ (0x320A, "M", "(ᄏ)"),
+ (0x320B, "M", "(ᄐ)"),
+ (0x320C, "M", "(ᄑ)"),
+ (0x320D, "M", "(ᄒ)"),
+ (0x320E, "M", "(가)"),
+ (0x320F, "M", "(나)"),
+ (0x3210, "M", "(다)"),
+ (0x3211, "M", "(라)"),
+ (0x3212, "M", "(마)"),
+ (0x3213, "M", "(바)"),
+ (0x3214, "M", "(사)"),
+ (0x3215, "M", "(아)"),
+ (0x3216, "M", "(자)"),
+ (0x3217, "M", "(차)"),
+ (0x3218, "M", "(카)"),
+ (0x3219, "M", "(타)"),
+ (0x321A, "M", "(파)"),
+ (0x321B, "M", "(하)"),
+ (0x321C, "M", "(주)"),
+ (0x321D, "M", "(오전)"),
+ (0x321E, "M", "(오후)"),
+ (0x321F, "X"),
+ (0x3220, "M", "(一)"),
+ (0x3221, "M", "(二)"),
+ (0x3222, "M", "(三)"),
+ (0x3223, "M", "(四)"),
+ (0x3224, "M", "(五)"),
+ (0x3225, "M", "(六)"),
+ (0x3226, "M", "(七)"),
+ (0x3227, "M", "(八)"),
+ (0x3228, "M", "(九)"),
+ (0x3229, "M", "(十)"),
+ ]
+
+
+def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x322A, "M", "(月)"),
+ (0x322B, "M", "(火)"),
+ (0x322C, "M", "(水)"),
+ (0x322D, "M", "(木)"),
+ (0x322E, "M", "(金)"),
+ (0x322F, "M", "(土)"),
+ (0x3230, "M", "(日)"),
+ (0x3231, "M", "(株)"),
+ (0x3232, "M", "(有)"),
+ (0x3233, "M", "(社)"),
+ (0x3234, "M", "(名)"),
+ (0x3235, "M", "(特)"),
+ (0x3236, "M", "(財)"),
+ (0x3237, "M", "(祝)"),
+ (0x3238, "M", "(労)"),
+ (0x3239, "M", "(代)"),
+ (0x323A, "M", "(呼)"),
+ (0x323B, "M", "(学)"),
+ (0x323C, "M", "(監)"),
+ (0x323D, "M", "(企)"),
+ (0x323E, "M", "(資)"),
+ (0x323F, "M", "(協)"),
+ (0x3240, "M", "(祭)"),
+ (0x3241, "M", "(休)"),
+ (0x3242, "M", "(自)"),
+ (0x3243, "M", "(至)"),
+ (0x3244, "M", "問"),
+ (0x3245, "M", "幼"),
+ (0x3246, "M", "文"),
+ (0x3247, "M", "箏"),
+ (0x3248, "V"),
+ (0x3250, "M", "pte"),
+ (0x3251, "M", "21"),
+ (0x3252, "M", "22"),
+ (0x3253, "M", "23"),
+ (0x3254, "M", "24"),
+ (0x3255, "M", "25"),
+ (0x3256, "M", "26"),
+ (0x3257, "M", "27"),
+ (0x3258, "M", "28"),
+ (0x3259, "M", "29"),
+ (0x325A, "M", "30"),
+ (0x325B, "M", "31"),
+ (0x325C, "M", "32"),
+ (0x325D, "M", "33"),
+ (0x325E, "M", "34"),
+ (0x325F, "M", "35"),
+ (0x3260, "M", "ᄀ"),
+ (0x3261, "M", "ᄂ"),
+ (0x3262, "M", "ᄃ"),
+ (0x3263, "M", "ᄅ"),
+ (0x3264, "M", "ᄆ"),
+ (0x3265, "M", "ᄇ"),
+ (0x3266, "M", "ᄉ"),
+ (0x3267, "M", "ᄋ"),
+ (0x3268, "M", "ᄌ"),
+ (0x3269, "M", "ᄎ"),
+ (0x326A, "M", "ᄏ"),
+ (0x326B, "M", "ᄐ"),
+ (0x326C, "M", "ᄑ"),
+ (0x326D, "M", "ᄒ"),
+ (0x326E, "M", "가"),
+ (0x326F, "M", "나"),
+ (0x3270, "M", "다"),
+ (0x3271, "M", "라"),
+ (0x3272, "M", "마"),
+ (0x3273, "M", "바"),
+ (0x3274, "M", "사"),
+ (0x3275, "M", "아"),
+ (0x3276, "M", "자"),
+ (0x3277, "M", "차"),
+ (0x3278, "M", "카"),
+ (0x3279, "M", "타"),
+ (0x327A, "M", "파"),
+ (0x327B, "M", "하"),
+ (0x327C, "M", "참고"),
+ (0x327D, "M", "주의"),
+ (0x327E, "M", "우"),
+ (0x327F, "V"),
+ (0x3280, "M", "一"),
+ (0x3281, "M", "二"),
+ (0x3282, "M", "三"),
+ (0x3283, "M", "四"),
+ (0x3284, "M", "五"),
+ (0x3285, "M", "六"),
+ (0x3286, "M", "七"),
+ (0x3287, "M", "八"),
+ (0x3288, "M", "九"),
+ (0x3289, "M", "十"),
+ (0x328A, "M", "月"),
+ (0x328B, "M", "火"),
+ (0x328C, "M", "水"),
+ (0x328D, "M", "木"),
+ (0x328E, "M", "金"),
+ (0x328F, "M", "土"),
+ (0x3290, "M", "日"),
+ (0x3291, "M", "株"),
+ (0x3292, "M", "有"),
+ (0x3293, "M", "社"),
+ (0x3294, "M", "名"),
+ ]
+
+
+def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x3295, "M", "特"),
+ (0x3296, "M", "財"),
+ (0x3297, "M", "祝"),
+ (0x3298, "M", "労"),
+ (0x3299, "M", "秘"),
+ (0x329A, "M", "男"),
+ (0x329B, "M", "女"),
+ (0x329C, "M", "適"),
+ (0x329D, "M", "優"),
+ (0x329E, "M", "印"),
+ (0x329F, "M", "注"),
+ (0x32A0, "M", "項"),
+ (0x32A1, "M", "休"),
+ (0x32A2, "M", "写"),
+ (0x32A3, "M", "正"),
+ (0x32A4, "M", "上"),
+ (0x32A5, "M", "中"),
+ (0x32A6, "M", "下"),
+ (0x32A7, "M", "左"),
+ (0x32A8, "M", "右"),
+ (0x32A9, "M", "医"),
+ (0x32AA, "M", "宗"),
+ (0x32AB, "M", "学"),
+ (0x32AC, "M", "監"),
+ (0x32AD, "M", "企"),
+ (0x32AE, "M", "資"),
+ (0x32AF, "M", "協"),
+ (0x32B0, "M", "夜"),
+ (0x32B1, "M", "36"),
+ (0x32B2, "M", "37"),
+ (0x32B3, "M", "38"),
+ (0x32B4, "M", "39"),
+ (0x32B5, "M", "40"),
+ (0x32B6, "M", "41"),
+ (0x32B7, "M", "42"),
+ (0x32B8, "M", "43"),
+ (0x32B9, "M", "44"),
+ (0x32BA, "M", "45"),
+ (0x32BB, "M", "46"),
+ (0x32BC, "M", "47"),
+ (0x32BD, "M", "48"),
+ (0x32BE, "M", "49"),
+ (0x32BF, "M", "50"),
+ (0x32C0, "M", "1月"),
+ (0x32C1, "M", "2月"),
+ (0x32C2, "M", "3月"),
+ (0x32C3, "M", "4月"),
+ (0x32C4, "M", "5月"),
+ (0x32C5, "M", "6月"),
+ (0x32C6, "M", "7月"),
+ (0x32C7, "M", "8月"),
+ (0x32C8, "M", "9月"),
+ (0x32C9, "M", "10月"),
+ (0x32CA, "M", "11月"),
+ (0x32CB, "M", "12月"),
+ (0x32CC, "M", "hg"),
+ (0x32CD, "M", "erg"),
+ (0x32CE, "M", "ev"),
+ (0x32CF, "M", "ltd"),
+ (0x32D0, "M", "ア"),
+ (0x32D1, "M", "イ"),
+ (0x32D2, "M", "ウ"),
+ (0x32D3, "M", "エ"),
+ (0x32D4, "M", "オ"),
+ (0x32D5, "M", "カ"),
+ (0x32D6, "M", "キ"),
+ (0x32D7, "M", "ク"),
+ (0x32D8, "M", "ケ"),
+ (0x32D9, "M", "コ"),
+ (0x32DA, "M", "サ"),
+ (0x32DB, "M", "シ"),
+ (0x32DC, "M", "ス"),
+ (0x32DD, "M", "セ"),
+ (0x32DE, "M", "ソ"),
+ (0x32DF, "M", "タ"),
+ (0x32E0, "M", "チ"),
+ (0x32E1, "M", "ツ"),
+ (0x32E2, "M", "テ"),
+ (0x32E3, "M", "ト"),
+ (0x32E4, "M", "ナ"),
+ (0x32E5, "M", "ニ"),
+ (0x32E6, "M", "ヌ"),
+ (0x32E7, "M", "ネ"),
+ (0x32E8, "M", "ノ"),
+ (0x32E9, "M", "ハ"),
+ (0x32EA, "M", "ヒ"),
+ (0x32EB, "M", "フ"),
+ (0x32EC, "M", "ヘ"),
+ (0x32ED, "M", "ホ"),
+ (0x32EE, "M", "マ"),
+ (0x32EF, "M", "ミ"),
+ (0x32F0, "M", "ム"),
+ (0x32F1, "M", "メ"),
+ (0x32F2, "M", "モ"),
+ (0x32F3, "M", "ヤ"),
+ (0x32F4, "M", "ユ"),
+ (0x32F5, "M", "ヨ"),
+ (0x32F6, "M", "ラ"),
+ (0x32F7, "M", "リ"),
+ (0x32F8, "M", "ル"),
+ ]
+
+
+def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x32F9, "M", "レ"),
+ (0x32FA, "M", "ロ"),
+ (0x32FB, "M", "ワ"),
+ (0x32FC, "M", "ヰ"),
+ (0x32FD, "M", "ヱ"),
+ (0x32FE, "M", "ヲ"),
+ (0x32FF, "M", "令和"),
+ (0x3300, "M", "アパート"),
+ (0x3301, "M", "アルファ"),
+ (0x3302, "M", "アンペア"),
+ (0x3303, "M", "アール"),
+ (0x3304, "M", "イニング"),
+ (0x3305, "M", "インチ"),
+ (0x3306, "M", "ウォン"),
+ (0x3307, "M", "エスクード"),
+ (0x3308, "M", "エーカー"),
+ (0x3309, "M", "オンス"),
+ (0x330A, "M", "オーム"),
+ (0x330B, "M", "カイリ"),
+ (0x330C, "M", "カラット"),
+ (0x330D, "M", "カロリー"),
+ (0x330E, "M", "ガロン"),
+ (0x330F, "M", "ガンマ"),
+ (0x3310, "M", "ギガ"),
+ (0x3311, "M", "ギニー"),
+ (0x3312, "M", "キュリー"),
+ (0x3313, "M", "ギルダー"),
+ (0x3314, "M", "キロ"),
+ (0x3315, "M", "キログラム"),
+ (0x3316, "M", "キロメートル"),
+ (0x3317, "M", "キロワット"),
+ (0x3318, "M", "グラム"),
+ (0x3319, "M", "グラムトン"),
+ (0x331A, "M", "クルゼイロ"),
+ (0x331B, "M", "クローネ"),
+ (0x331C, "M", "ケース"),
+ (0x331D, "M", "コルナ"),
+ (0x331E, "M", "コーポ"),
+ (0x331F, "M", "サイクル"),
+ (0x3320, "M", "サンチーム"),
+ (0x3321, "M", "シリング"),
+ (0x3322, "M", "センチ"),
+ (0x3323, "M", "セント"),
+ (0x3324, "M", "ダース"),
+ (0x3325, "M", "デシ"),
+ (0x3326, "M", "ドル"),
+ (0x3327, "M", "トン"),
+ (0x3328, "M", "ナノ"),
+ (0x3329, "M", "ノット"),
+ (0x332A, "M", "ハイツ"),
+ (0x332B, "M", "パーセント"),
+ (0x332C, "M", "パーツ"),
+ (0x332D, "M", "バーレル"),
+ (0x332E, "M", "ピアストル"),
+ (0x332F, "M", "ピクル"),
+ (0x3330, "M", "ピコ"),
+ (0x3331, "M", "ビル"),
+ (0x3332, "M", "ファラッド"),
+ (0x3333, "M", "フィート"),
+ (0x3334, "M", "ブッシェル"),
+ (0x3335, "M", "フラン"),
+ (0x3336, "M", "ヘクタール"),
+ (0x3337, "M", "ペソ"),
+ (0x3338, "M", "ペニヒ"),
+ (0x3339, "M", "ヘルツ"),
+ (0x333A, "M", "ペンス"),
+ (0x333B, "M", "ページ"),
+ (0x333C, "M", "ベータ"),
+ (0x333D, "M", "ポイント"),
+ (0x333E, "M", "ボルト"),
+ (0x333F, "M", "ホン"),
+ (0x3340, "M", "ポンド"),
+ (0x3341, "M", "ホール"),
+ (0x3342, "M", "ホーン"),
+ (0x3343, "M", "マイクロ"),
+ (0x3344, "M", "マイル"),
+ (0x3345, "M", "マッハ"),
+ (0x3346, "M", "マルク"),
+ (0x3347, "M", "マンション"),
+ (0x3348, "M", "ミクロン"),
+ (0x3349, "M", "ミリ"),
+ (0x334A, "M", "ミリバール"),
+ (0x334B, "M", "メガ"),
+ (0x334C, "M", "メガトン"),
+ (0x334D, "M", "メートル"),
+ (0x334E, "M", "ヤード"),
+ (0x334F, "M", "ヤール"),
+ (0x3350, "M", "ユアン"),
+ (0x3351, "M", "リットル"),
+ (0x3352, "M", "リラ"),
+ (0x3353, "M", "ルピー"),
+ (0x3354, "M", "ルーブル"),
+ (0x3355, "M", "レム"),
+ (0x3356, "M", "レントゲン"),
+ (0x3357, "M", "ワット"),
+ (0x3358, "M", "0点"),
+ (0x3359, "M", "1点"),
+ (0x335A, "M", "2点"),
+ (0x335B, "M", "3点"),
+ (0x335C, "M", "4点"),
+ ]
+
+
+def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x335D, "M", "5点"),
+ (0x335E, "M", "6点"),
+ (0x335F, "M", "7点"),
+ (0x3360, "M", "8点"),
+ (0x3361, "M", "9点"),
+ (0x3362, "M", "10点"),
+ (0x3363, "M", "11点"),
+ (0x3364, "M", "12点"),
+ (0x3365, "M", "13点"),
+ (0x3366, "M", "14点"),
+ (0x3367, "M", "15点"),
+ (0x3368, "M", "16点"),
+ (0x3369, "M", "17点"),
+ (0x336A, "M", "18点"),
+ (0x336B, "M", "19点"),
+ (0x336C, "M", "20点"),
+ (0x336D, "M", "21点"),
+ (0x336E, "M", "22点"),
+ (0x336F, "M", "23点"),
+ (0x3370, "M", "24点"),
+ (0x3371, "M", "hpa"),
+ (0x3372, "M", "da"),
+ (0x3373, "M", "au"),
+ (0x3374, "M", "bar"),
+ (0x3375, "M", "ov"),
+ (0x3376, "M", "pc"),
+ (0x3377, "M", "dm"),
+ (0x3378, "M", "dm2"),
+ (0x3379, "M", "dm3"),
+ (0x337A, "M", "iu"),
+ (0x337B, "M", "平成"),
+ (0x337C, "M", "昭和"),
+ (0x337D, "M", "大正"),
+ (0x337E, "M", "明治"),
+ (0x337F, "M", "株式会社"),
+ (0x3380, "M", "pa"),
+ (0x3381, "M", "na"),
+ (0x3382, "M", "μa"),
+ (0x3383, "M", "ma"),
+ (0x3384, "M", "ka"),
+ (0x3385, "M", "kb"),
+ (0x3386, "M", "mb"),
+ (0x3387, "M", "gb"),
+ (0x3388, "M", "cal"),
+ (0x3389, "M", "kcal"),
+ (0x338A, "M", "pf"),
+ (0x338B, "M", "nf"),
+ (0x338C, "M", "μf"),
+ (0x338D, "M", "μg"),
+ (0x338E, "M", "mg"),
+ (0x338F, "M", "kg"),
+ (0x3390, "M", "hz"),
+ (0x3391, "M", "khz"),
+ (0x3392, "M", "mhz"),
+ (0x3393, "M", "ghz"),
+ (0x3394, "M", "thz"),
+ (0x3395, "M", "μl"),
+ (0x3396, "M", "ml"),
+ (0x3397, "M", "dl"),
+ (0x3398, "M", "kl"),
+ (0x3399, "M", "fm"),
+ (0x339A, "M", "nm"),
+ (0x339B, "M", "μm"),
+ (0x339C, "M", "mm"),
+ (0x339D, "M", "cm"),
+ (0x339E, "M", "km"),
+ (0x339F, "M", "mm2"),
+ (0x33A0, "M", "cm2"),
+ (0x33A1, "M", "m2"),
+ (0x33A2, "M", "km2"),
+ (0x33A3, "M", "mm3"),
+ (0x33A4, "M", "cm3"),
+ (0x33A5, "M", "m3"),
+ (0x33A6, "M", "km3"),
+ (0x33A7, "M", "m∕s"),
+ (0x33A8, "M", "m∕s2"),
+ (0x33A9, "M", "pa"),
+ (0x33AA, "M", "kpa"),
+ (0x33AB, "M", "mpa"),
+ (0x33AC, "M", "gpa"),
+ (0x33AD, "M", "rad"),
+ (0x33AE, "M", "rad∕s"),
+ (0x33AF, "M", "rad∕s2"),
+ (0x33B0, "M", "ps"),
+ (0x33B1, "M", "ns"),
+ (0x33B2, "M", "μs"),
+ (0x33B3, "M", "ms"),
+ (0x33B4, "M", "pv"),
+ (0x33B5, "M", "nv"),
+ (0x33B6, "M", "μv"),
+ (0x33B7, "M", "mv"),
+ (0x33B8, "M", "kv"),
+ (0x33B9, "M", "mv"),
+ (0x33BA, "M", "pw"),
+ (0x33BB, "M", "nw"),
+ (0x33BC, "M", "μw"),
+ (0x33BD, "M", "mw"),
+ (0x33BE, "M", "kw"),
+ (0x33BF, "M", "mw"),
+ (0x33C0, "M", "kω"),
+ ]
+
+
+def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x33C1, "M", "mω"),
+ (0x33C2, "X"),
+ (0x33C3, "M", "bq"),
+ (0x33C4, "M", "cc"),
+ (0x33C5, "M", "cd"),
+ (0x33C6, "M", "c∕kg"),
+ (0x33C7, "X"),
+ (0x33C8, "M", "db"),
+ (0x33C9, "M", "gy"),
+ (0x33CA, "M", "ha"),
+ (0x33CB, "M", "hp"),
+ (0x33CC, "M", "in"),
+ (0x33CD, "M", "kk"),
+ (0x33CE, "M", "km"),
+ (0x33CF, "M", "kt"),
+ (0x33D0, "M", "lm"),
+ (0x33D1, "M", "ln"),
+ (0x33D2, "M", "log"),
+ (0x33D3, "M", "lx"),
+ (0x33D4, "M", "mb"),
+ (0x33D5, "M", "mil"),
+ (0x33D6, "M", "mol"),
+ (0x33D7, "M", "ph"),
+ (0x33D8, "X"),
+ (0x33D9, "M", "ppm"),
+ (0x33DA, "M", "pr"),
+ (0x33DB, "M", "sr"),
+ (0x33DC, "M", "sv"),
+ (0x33DD, "M", "wb"),
+ (0x33DE, "M", "v∕m"),
+ (0x33DF, "M", "a∕m"),
+ (0x33E0, "M", "1日"),
+ (0x33E1, "M", "2日"),
+ (0x33E2, "M", "3日"),
+ (0x33E3, "M", "4日"),
+ (0x33E4, "M", "5日"),
+ (0x33E5, "M", "6日"),
+ (0x33E6, "M", "7日"),
+ (0x33E7, "M", "8日"),
+ (0x33E8, "M", "9日"),
+ (0x33E9, "M", "10日"),
+ (0x33EA, "M", "11日"),
+ (0x33EB, "M", "12日"),
+ (0x33EC, "M", "13日"),
+ (0x33ED, "M", "14日"),
+ (0x33EE, "M", "15日"),
+ (0x33EF, "M", "16日"),
+ (0x33F0, "M", "17日"),
+ (0x33F1, "M", "18日"),
+ (0x33F2, "M", "19日"),
+ (0x33F3, "M", "20日"),
+ (0x33F4, "M", "21日"),
+ (0x33F5, "M", "22日"),
+ (0x33F6, "M", "23日"),
+ (0x33F7, "M", "24日"),
+ (0x33F8, "M", "25日"),
+ (0x33F9, "M", "26日"),
+ (0x33FA, "M", "27日"),
+ (0x33FB, "M", "28日"),
+ (0x33FC, "M", "29日"),
+ (0x33FD, "M", "30日"),
+ (0x33FE, "M", "31日"),
+ (0x33FF, "M", "gal"),
+ (0x3400, "V"),
+ (0xA48D, "X"),
+ (0xA490, "V"),
+ (0xA4C7, "X"),
+ (0xA4D0, "V"),
+ (0xA62C, "X"),
+ (0xA640, "M", "ꙁ"),
+ (0xA641, "V"),
+ (0xA642, "M", "ꙃ"),
+ (0xA643, "V"),
+ (0xA644, "M", "ꙅ"),
+ (0xA645, "V"),
+ (0xA646, "M", "ꙇ"),
+ (0xA647, "V"),
+ (0xA648, "M", "ꙉ"),
+ (0xA649, "V"),
+ (0xA64A, "M", "ꙋ"),
+ (0xA64B, "V"),
+ (0xA64C, "M", "ꙍ"),
+ (0xA64D, "V"),
+ (0xA64E, "M", "ꙏ"),
+ (0xA64F, "V"),
+ (0xA650, "M", "ꙑ"),
+ (0xA651, "V"),
+ (0xA652, "M", "ꙓ"),
+ (0xA653, "V"),
+ (0xA654, "M", "ꙕ"),
+ (0xA655, "V"),
+ (0xA656, "M", "ꙗ"),
+ (0xA657, "V"),
+ (0xA658, "M", "ꙙ"),
+ (0xA659, "V"),
+ (0xA65A, "M", "ꙛ"),
+ (0xA65B, "V"),
+ (0xA65C, "M", "ꙝ"),
+ (0xA65D, "V"),
+ (0xA65E, "M", "ꙟ"),
+ ]
+
+
+def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xA65F, "V"),
+ (0xA660, "M", "ꙡ"),
+ (0xA661, "V"),
+ (0xA662, "M", "ꙣ"),
+ (0xA663, "V"),
+ (0xA664, "M", "ꙥ"),
+ (0xA665, "V"),
+ (0xA666, "M", "ꙧ"),
+ (0xA667, "V"),
+ (0xA668, "M", "ꙩ"),
+ (0xA669, "V"),
+ (0xA66A, "M", "ꙫ"),
+ (0xA66B, "V"),
+ (0xA66C, "M", "ꙭ"),
+ (0xA66D, "V"),
+ (0xA680, "M", "ꚁ"),
+ (0xA681, "V"),
+ (0xA682, "M", "ꚃ"),
+ (0xA683, "V"),
+ (0xA684, "M", "ꚅ"),
+ (0xA685, "V"),
+ (0xA686, "M", "ꚇ"),
+ (0xA687, "V"),
+ (0xA688, "M", "ꚉ"),
+ (0xA689, "V"),
+ (0xA68A, "M", "ꚋ"),
+ (0xA68B, "V"),
+ (0xA68C, "M", "ꚍ"),
+ (0xA68D, "V"),
+ (0xA68E, "M", "ꚏ"),
+ (0xA68F, "V"),
+ (0xA690, "M", "ꚑ"),
+ (0xA691, "V"),
+ (0xA692, "M", "ꚓ"),
+ (0xA693, "V"),
+ (0xA694, "M", "ꚕ"),
+ (0xA695, "V"),
+ (0xA696, "M", "ꚗ"),
+ (0xA697, "V"),
+ (0xA698, "M", "ꚙ"),
+ (0xA699, "V"),
+ (0xA69A, "M", "ꚛ"),
+ (0xA69B, "V"),
+ (0xA69C, "M", "ъ"),
+ (0xA69D, "M", "ь"),
+ (0xA69E, "V"),
+ (0xA6F8, "X"),
+ (0xA700, "V"),
+ (0xA722, "M", "ꜣ"),
+ (0xA723, "V"),
+ (0xA724, "M", "ꜥ"),
+ (0xA725, "V"),
+ (0xA726, "M", "ꜧ"),
+ (0xA727, "V"),
+ (0xA728, "M", "ꜩ"),
+ (0xA729, "V"),
+ (0xA72A, "M", "ꜫ"),
+ (0xA72B, "V"),
+ (0xA72C, "M", "ꜭ"),
+ (0xA72D, "V"),
+ (0xA72E, "M", "ꜯ"),
+ (0xA72F, "V"),
+ (0xA732, "M", "ꜳ"),
+ (0xA733, "V"),
+ (0xA734, "M", "ꜵ"),
+ (0xA735, "V"),
+ (0xA736, "M", "ꜷ"),
+ (0xA737, "V"),
+ (0xA738, "M", "ꜹ"),
+ (0xA739, "V"),
+ (0xA73A, "M", "ꜻ"),
+ (0xA73B, "V"),
+ (0xA73C, "M", "ꜽ"),
+ (0xA73D, "V"),
+ (0xA73E, "M", "ꜿ"),
+ (0xA73F, "V"),
+ (0xA740, "M", "ꝁ"),
+ (0xA741, "V"),
+ (0xA742, "M", "ꝃ"),
+ (0xA743, "V"),
+ (0xA744, "M", "ꝅ"),
+ (0xA745, "V"),
+ (0xA746, "M", "ꝇ"),
+ (0xA747, "V"),
+ (0xA748, "M", "ꝉ"),
+ (0xA749, "V"),
+ (0xA74A, "M", "ꝋ"),
+ (0xA74B, "V"),
+ (0xA74C, "M", "ꝍ"),
+ (0xA74D, "V"),
+ (0xA74E, "M", "ꝏ"),
+ (0xA74F, "V"),
+ (0xA750, "M", "ꝑ"),
+ (0xA751, "V"),
+ (0xA752, "M", "ꝓ"),
+ (0xA753, "V"),
+ (0xA754, "M", "ꝕ"),
+ (0xA755, "V"),
+ (0xA756, "M", "ꝗ"),
+ (0xA757, "V"),
+ ]
+
+
+def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xA758, "M", "ꝙ"),
+ (0xA759, "V"),
+ (0xA75A, "M", "ꝛ"),
+ (0xA75B, "V"),
+ (0xA75C, "M", "ꝝ"),
+ (0xA75D, "V"),
+ (0xA75E, "M", "ꝟ"),
+ (0xA75F, "V"),
+ (0xA760, "M", "ꝡ"),
+ (0xA761, "V"),
+ (0xA762, "M", "ꝣ"),
+ (0xA763, "V"),
+ (0xA764, "M", "ꝥ"),
+ (0xA765, "V"),
+ (0xA766, "M", "ꝧ"),
+ (0xA767, "V"),
+ (0xA768, "M", "ꝩ"),
+ (0xA769, "V"),
+ (0xA76A, "M", "ꝫ"),
+ (0xA76B, "V"),
+ (0xA76C, "M", "ꝭ"),
+ (0xA76D, "V"),
+ (0xA76E, "M", "ꝯ"),
+ (0xA76F, "V"),
+ (0xA770, "M", "ꝯ"),
+ (0xA771, "V"),
+ (0xA779, "M", "ꝺ"),
+ (0xA77A, "V"),
+ (0xA77B, "M", "ꝼ"),
+ (0xA77C, "V"),
+ (0xA77D, "M", "ᵹ"),
+ (0xA77E, "M", "ꝿ"),
+ (0xA77F, "V"),
+ (0xA780, "M", "ꞁ"),
+ (0xA781, "V"),
+ (0xA782, "M", "ꞃ"),
+ (0xA783, "V"),
+ (0xA784, "M", "ꞅ"),
+ (0xA785, "V"),
+ (0xA786, "M", "ꞇ"),
+ (0xA787, "V"),
+ (0xA78B, "M", "ꞌ"),
+ (0xA78C, "V"),
+ (0xA78D, "M", "ɥ"),
+ (0xA78E, "V"),
+ (0xA790, "M", "ꞑ"),
+ (0xA791, "V"),
+ (0xA792, "M", "ꞓ"),
+ (0xA793, "V"),
+ (0xA796, "M", "ꞗ"),
+ (0xA797, "V"),
+ (0xA798, "M", "ꞙ"),
+ (0xA799, "V"),
+ (0xA79A, "M", "ꞛ"),
+ (0xA79B, "V"),
+ (0xA79C, "M", "ꞝ"),
+ (0xA79D, "V"),
+ (0xA79E, "M", "ꞟ"),
+ (0xA79F, "V"),
+ (0xA7A0, "M", "ꞡ"),
+ (0xA7A1, "V"),
+ (0xA7A2, "M", "ꞣ"),
+ (0xA7A3, "V"),
+ (0xA7A4, "M", "ꞥ"),
+ (0xA7A5, "V"),
+ (0xA7A6, "M", "ꞧ"),
+ (0xA7A7, "V"),
+ (0xA7A8, "M", "ꞩ"),
+ (0xA7A9, "V"),
+ (0xA7AA, "M", "ɦ"),
+ (0xA7AB, "M", "ɜ"),
+ (0xA7AC, "M", "ɡ"),
+ (0xA7AD, "M", "ɬ"),
+ (0xA7AE, "M", "ɪ"),
+ (0xA7AF, "V"),
+ (0xA7B0, "M", "ʞ"),
+ (0xA7B1, "M", "ʇ"),
+ (0xA7B2, "M", "ʝ"),
+ (0xA7B3, "M", "ꭓ"),
+ (0xA7B4, "M", "ꞵ"),
+ (0xA7B5, "V"),
+ (0xA7B6, "M", "ꞷ"),
+ (0xA7B7, "V"),
+ (0xA7B8, "M", "ꞹ"),
+ (0xA7B9, "V"),
+ (0xA7BA, "M", "ꞻ"),
+ (0xA7BB, "V"),
+ (0xA7BC, "M", "ꞽ"),
+ (0xA7BD, "V"),
+ (0xA7BE, "M", "ꞿ"),
+ (0xA7BF, "V"),
+ (0xA7C0, "M", "ꟁ"),
+ (0xA7C1, "V"),
+ (0xA7C2, "M", "ꟃ"),
+ (0xA7C3, "V"),
+ (0xA7C4, "M", "ꞔ"),
+ (0xA7C5, "M", "ʂ"),
+ (0xA7C6, "M", "ᶎ"),
+ (0xA7C7, "M", "ꟈ"),
+ (0xA7C8, "V"),
+ ]
+
+
+def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xA7C9, "M", "ꟊ"),
+ (0xA7CA, "V"),
+ (0xA7CB, "M", "ɤ"),
+ (0xA7CC, "M", ""),
+ (0xA7CD, "V"),
+ (0xA7CE, "X"),
+ (0xA7D0, "M", "ꟑ"),
+ (0xA7D1, "V"),
+ (0xA7D2, "X"),
+ (0xA7D3, "V"),
+ (0xA7D4, "X"),
+ (0xA7D5, "V"),
+ (0xA7D6, "M", "ꟗ"),
+ (0xA7D7, "V"),
+ (0xA7D8, "M", "ꟙ"),
+ (0xA7D9, "V"),
+ (0xA7DA, "M", ""),
+ (0xA7DB, "V"),
+ (0xA7DC, "M", "ƛ"),
+ (0xA7DD, "X"),
+ (0xA7F2, "M", "c"),
+ (0xA7F3, "M", "f"),
+ (0xA7F4, "M", "q"),
+ (0xA7F5, "M", "ꟶ"),
+ (0xA7F6, "V"),
+ (0xA7F8, "M", "ħ"),
+ (0xA7F9, "M", "œ"),
+ (0xA7FA, "V"),
+ (0xA82D, "X"),
+ (0xA830, "V"),
+ (0xA83A, "X"),
+ (0xA840, "V"),
+ (0xA878, "X"),
+ (0xA880, "V"),
+ (0xA8C6, "X"),
+ (0xA8CE, "V"),
+ (0xA8DA, "X"),
+ (0xA8E0, "V"),
+ (0xA954, "X"),
+ (0xA95F, "V"),
+ (0xA97D, "X"),
+ (0xA980, "V"),
+ (0xA9CE, "X"),
+ (0xA9CF, "V"),
+ (0xA9DA, "X"),
+ (0xA9DE, "V"),
+ (0xA9FF, "X"),
+ (0xAA00, "V"),
+ (0xAA37, "X"),
+ (0xAA40, "V"),
+ (0xAA4E, "X"),
+ (0xAA50, "V"),
+ (0xAA5A, "X"),
+ (0xAA5C, "V"),
+ (0xAAC3, "X"),
+ (0xAADB, "V"),
+ (0xAAF7, "X"),
+ (0xAB01, "V"),
+ (0xAB07, "X"),
+ (0xAB09, "V"),
+ (0xAB0F, "X"),
+ (0xAB11, "V"),
+ (0xAB17, "X"),
+ (0xAB20, "V"),
+ (0xAB27, "X"),
+ (0xAB28, "V"),
+ (0xAB2F, "X"),
+ (0xAB30, "V"),
+ (0xAB5C, "M", "ꜧ"),
+ (0xAB5D, "M", "ꬷ"),
+ (0xAB5E, "M", "ɫ"),
+ (0xAB5F, "M", "ꭒ"),
+ (0xAB60, "V"),
+ (0xAB69, "M", "ʍ"),
+ (0xAB6A, "V"),
+ (0xAB6C, "X"),
+ (0xAB70, "M", "Ꭰ"),
+ (0xAB71, "M", "Ꭱ"),
+ (0xAB72, "M", "Ꭲ"),
+ (0xAB73, "M", "Ꭳ"),
+ (0xAB74, "M", "Ꭴ"),
+ (0xAB75, "M", "Ꭵ"),
+ (0xAB76, "M", "Ꭶ"),
+ (0xAB77, "M", "Ꭷ"),
+ (0xAB78, "M", "Ꭸ"),
+ (0xAB79, "M", "Ꭹ"),
+ (0xAB7A, "M", "Ꭺ"),
+ (0xAB7B, "M", "Ꭻ"),
+ (0xAB7C, "M", "Ꭼ"),
+ (0xAB7D, "M", "Ꭽ"),
+ (0xAB7E, "M", "Ꭾ"),
+ (0xAB7F, "M", "Ꭿ"),
+ (0xAB80, "M", "Ꮀ"),
+ (0xAB81, "M", "Ꮁ"),
+ (0xAB82, "M", "Ꮂ"),
+ (0xAB83, "M", "Ꮃ"),
+ (0xAB84, "M", "Ꮄ"),
+ (0xAB85, "M", "Ꮅ"),
+ (0xAB86, "M", "Ꮆ"),
+ (0xAB87, "M", "Ꮇ"),
+ ]
+
+
+def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xAB88, "M", "Ꮈ"),
+ (0xAB89, "M", "Ꮉ"),
+ (0xAB8A, "M", "Ꮊ"),
+ (0xAB8B, "M", "Ꮋ"),
+ (0xAB8C, "M", "Ꮌ"),
+ (0xAB8D, "M", "Ꮍ"),
+ (0xAB8E, "M", "Ꮎ"),
+ (0xAB8F, "M", "Ꮏ"),
+ (0xAB90, "M", "Ꮐ"),
+ (0xAB91, "M", "Ꮑ"),
+ (0xAB92, "M", "Ꮒ"),
+ (0xAB93, "M", "Ꮓ"),
+ (0xAB94, "M", "Ꮔ"),
+ (0xAB95, "M", "Ꮕ"),
+ (0xAB96, "M", "Ꮖ"),
+ (0xAB97, "M", "Ꮗ"),
+ (0xAB98, "M", "Ꮘ"),
+ (0xAB99, "M", "Ꮙ"),
+ (0xAB9A, "M", "Ꮚ"),
+ (0xAB9B, "M", "Ꮛ"),
+ (0xAB9C, "M", "Ꮜ"),
+ (0xAB9D, "M", "Ꮝ"),
+ (0xAB9E, "M", "Ꮞ"),
+ (0xAB9F, "M", "Ꮟ"),
+ (0xABA0, "M", "Ꮠ"),
+ (0xABA1, "M", "Ꮡ"),
+ (0xABA2, "M", "Ꮢ"),
+ (0xABA3, "M", "Ꮣ"),
+ (0xABA4, "M", "Ꮤ"),
+ (0xABA5, "M", "Ꮥ"),
+ (0xABA6, "M", "Ꮦ"),
+ (0xABA7, "M", "Ꮧ"),
+ (0xABA8, "M", "Ꮨ"),
+ (0xABA9, "M", "Ꮩ"),
+ (0xABAA, "M", "Ꮪ"),
+ (0xABAB, "M", "Ꮫ"),
+ (0xABAC, "M", "Ꮬ"),
+ (0xABAD, "M", "Ꮭ"),
+ (0xABAE, "M", "Ꮮ"),
+ (0xABAF, "M", "Ꮯ"),
+ (0xABB0, "M", "Ꮰ"),
+ (0xABB1, "M", "Ꮱ"),
+ (0xABB2, "M", "Ꮲ"),
+ (0xABB3, "M", "Ꮳ"),
+ (0xABB4, "M", "Ꮴ"),
+ (0xABB5, "M", "Ꮵ"),
+ (0xABB6, "M", "Ꮶ"),
+ (0xABB7, "M", "Ꮷ"),
+ (0xABB8, "M", "Ꮸ"),
+ (0xABB9, "M", "Ꮹ"),
+ (0xABBA, "M", "Ꮺ"),
+ (0xABBB, "M", "Ꮻ"),
+ (0xABBC, "M", "Ꮼ"),
+ (0xABBD, "M", "Ꮽ"),
+ (0xABBE, "M", "Ꮾ"),
+ (0xABBF, "M", "Ꮿ"),
+ (0xABC0, "V"),
+ (0xABEE, "X"),
+ (0xABF0, "V"),
+ (0xABFA, "X"),
+ (0xAC00, "V"),
+ (0xD7A4, "X"),
+ (0xD7B0, "V"),
+ (0xD7C7, "X"),
+ (0xD7CB, "V"),
+ (0xD7FC, "X"),
+ (0xF900, "M", "豈"),
+ (0xF901, "M", "更"),
+ (0xF902, "M", "車"),
+ (0xF903, "M", "賈"),
+ (0xF904, "M", "滑"),
+ (0xF905, "M", "串"),
+ (0xF906, "M", "句"),
+ (0xF907, "M", "龜"),
+ (0xF909, "M", "契"),
+ (0xF90A, "M", "金"),
+ (0xF90B, "M", "喇"),
+ (0xF90C, "M", "奈"),
+ (0xF90D, "M", "懶"),
+ (0xF90E, "M", "癩"),
+ (0xF90F, "M", "羅"),
+ (0xF910, "M", "蘿"),
+ (0xF911, "M", "螺"),
+ (0xF912, "M", "裸"),
+ (0xF913, "M", "邏"),
+ (0xF914, "M", "樂"),
+ (0xF915, "M", "洛"),
+ (0xF916, "M", "烙"),
+ (0xF917, "M", "珞"),
+ (0xF918, "M", "落"),
+ (0xF919, "M", "酪"),
+ (0xF91A, "M", "駱"),
+ (0xF91B, "M", "亂"),
+ (0xF91C, "M", "卵"),
+ (0xF91D, "M", "欄"),
+ (0xF91E, "M", "爛"),
+ (0xF91F, "M", "蘭"),
+ (0xF920, "M", "鸞"),
+ (0xF921, "M", "嵐"),
+ (0xF922, "M", "濫"),
+ ]
+
+
+def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xF923, "M", "藍"),
+ (0xF924, "M", "襤"),
+ (0xF925, "M", "拉"),
+ (0xF926, "M", "臘"),
+ (0xF927, "M", "蠟"),
+ (0xF928, "M", "廊"),
+ (0xF929, "M", "朗"),
+ (0xF92A, "M", "浪"),
+ (0xF92B, "M", "狼"),
+ (0xF92C, "M", "郎"),
+ (0xF92D, "M", "來"),
+ (0xF92E, "M", "冷"),
+ (0xF92F, "M", "勞"),
+ (0xF930, "M", "擄"),
+ (0xF931, "M", "櫓"),
+ (0xF932, "M", "爐"),
+ (0xF933, "M", "盧"),
+ (0xF934, "M", "老"),
+ (0xF935, "M", "蘆"),
+ (0xF936, "M", "虜"),
+ (0xF937, "M", "路"),
+ (0xF938, "M", "露"),
+ (0xF939, "M", "魯"),
+ (0xF93A, "M", "鷺"),
+ (0xF93B, "M", "碌"),
+ (0xF93C, "M", "祿"),
+ (0xF93D, "M", "綠"),
+ (0xF93E, "M", "菉"),
+ (0xF93F, "M", "錄"),
+ (0xF940, "M", "鹿"),
+ (0xF941, "M", "論"),
+ (0xF942, "M", "壟"),
+ (0xF943, "M", "弄"),
+ (0xF944, "M", "籠"),
+ (0xF945, "M", "聾"),
+ (0xF946, "M", "牢"),
+ (0xF947, "M", "磊"),
+ (0xF948, "M", "賂"),
+ (0xF949, "M", "雷"),
+ (0xF94A, "M", "壘"),
+ (0xF94B, "M", "屢"),
+ (0xF94C, "M", "樓"),
+ (0xF94D, "M", "淚"),
+ (0xF94E, "M", "漏"),
+ (0xF94F, "M", "累"),
+ (0xF950, "M", "縷"),
+ (0xF951, "M", "陋"),
+ (0xF952, "M", "勒"),
+ (0xF953, "M", "肋"),
+ (0xF954, "M", "凜"),
+ (0xF955, "M", "凌"),
+ (0xF956, "M", "稜"),
+ (0xF957, "M", "綾"),
+ (0xF958, "M", "菱"),
+ (0xF959, "M", "陵"),
+ (0xF95A, "M", "讀"),
+ (0xF95B, "M", "拏"),
+ (0xF95C, "M", "樂"),
+ (0xF95D, "M", "諾"),
+ (0xF95E, "M", "丹"),
+ (0xF95F, "M", "寧"),
+ (0xF960, "M", "怒"),
+ (0xF961, "M", "率"),
+ (0xF962, "M", "異"),
+ (0xF963, "M", "北"),
+ (0xF964, "M", "磻"),
+ (0xF965, "M", "便"),
+ (0xF966, "M", "復"),
+ (0xF967, "M", "不"),
+ (0xF968, "M", "泌"),
+ (0xF969, "M", "數"),
+ (0xF96A, "M", "索"),
+ (0xF96B, "M", "參"),
+ (0xF96C, "M", "塞"),
+ (0xF96D, "M", "省"),
+ (0xF96E, "M", "葉"),
+ (0xF96F, "M", "說"),
+ (0xF970, "M", "殺"),
+ (0xF971, "M", "辰"),
+ (0xF972, "M", "沈"),
+ (0xF973, "M", "拾"),
+ (0xF974, "M", "若"),
+ (0xF975, "M", "掠"),
+ (0xF976, "M", "略"),
+ (0xF977, "M", "亮"),
+ (0xF978, "M", "兩"),
+ (0xF979, "M", "凉"),
+ (0xF97A, "M", "梁"),
+ (0xF97B, "M", "糧"),
+ (0xF97C, "M", "良"),
+ (0xF97D, "M", "諒"),
+ (0xF97E, "M", "量"),
+ (0xF97F, "M", "勵"),
+ (0xF980, "M", "呂"),
+ (0xF981, "M", "女"),
+ (0xF982, "M", "廬"),
+ (0xF983, "M", "旅"),
+ (0xF984, "M", "濾"),
+ (0xF985, "M", "礪"),
+ (0xF986, "M", "閭"),
+ ]
+
+
+def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xF987, "M", "驪"),
+ (0xF988, "M", "麗"),
+ (0xF989, "M", "黎"),
+ (0xF98A, "M", "力"),
+ (0xF98B, "M", "曆"),
+ (0xF98C, "M", "歷"),
+ (0xF98D, "M", "轢"),
+ (0xF98E, "M", "年"),
+ (0xF98F, "M", "憐"),
+ (0xF990, "M", "戀"),
+ (0xF991, "M", "撚"),
+ (0xF992, "M", "漣"),
+ (0xF993, "M", "煉"),
+ (0xF994, "M", "璉"),
+ (0xF995, "M", "秊"),
+ (0xF996, "M", "練"),
+ (0xF997, "M", "聯"),
+ (0xF998, "M", "輦"),
+ (0xF999, "M", "蓮"),
+ (0xF99A, "M", "連"),
+ (0xF99B, "M", "鍊"),
+ (0xF99C, "M", "列"),
+ (0xF99D, "M", "劣"),
+ (0xF99E, "M", "咽"),
+ (0xF99F, "M", "烈"),
+ (0xF9A0, "M", "裂"),
+ (0xF9A1, "M", "說"),
+ (0xF9A2, "M", "廉"),
+ (0xF9A3, "M", "念"),
+ (0xF9A4, "M", "捻"),
+ (0xF9A5, "M", "殮"),
+ (0xF9A6, "M", "簾"),
+ (0xF9A7, "M", "獵"),
+ (0xF9A8, "M", "令"),
+ (0xF9A9, "M", "囹"),
+ (0xF9AA, "M", "寧"),
+ (0xF9AB, "M", "嶺"),
+ (0xF9AC, "M", "怜"),
+ (0xF9AD, "M", "玲"),
+ (0xF9AE, "M", "瑩"),
+ (0xF9AF, "M", "羚"),
+ (0xF9B0, "M", "聆"),
+ (0xF9B1, "M", "鈴"),
+ (0xF9B2, "M", "零"),
+ (0xF9B3, "M", "靈"),
+ (0xF9B4, "M", "領"),
+ (0xF9B5, "M", "例"),
+ (0xF9B6, "M", "禮"),
+ (0xF9B7, "M", "醴"),
+ (0xF9B8, "M", "隸"),
+ (0xF9B9, "M", "惡"),
+ (0xF9BA, "M", "了"),
+ (0xF9BB, "M", "僚"),
+ (0xF9BC, "M", "寮"),
+ (0xF9BD, "M", "尿"),
+ (0xF9BE, "M", "料"),
+ (0xF9BF, "M", "樂"),
+ (0xF9C0, "M", "燎"),
+ (0xF9C1, "M", "療"),
+ (0xF9C2, "M", "蓼"),
+ (0xF9C3, "M", "遼"),
+ (0xF9C4, "M", "龍"),
+ (0xF9C5, "M", "暈"),
+ (0xF9C6, "M", "阮"),
+ (0xF9C7, "M", "劉"),
+ (0xF9C8, "M", "杻"),
+ (0xF9C9, "M", "柳"),
+ (0xF9CA, "M", "流"),
+ (0xF9CB, "M", "溜"),
+ (0xF9CC, "M", "琉"),
+ (0xF9CD, "M", "留"),
+ (0xF9CE, "M", "硫"),
+ (0xF9CF, "M", "紐"),
+ (0xF9D0, "M", "類"),
+ (0xF9D1, "M", "六"),
+ (0xF9D2, "M", "戮"),
+ (0xF9D3, "M", "陸"),
+ (0xF9D4, "M", "倫"),
+ (0xF9D5, "M", "崙"),
+ (0xF9D6, "M", "淪"),
+ (0xF9D7, "M", "輪"),
+ (0xF9D8, "M", "律"),
+ (0xF9D9, "M", "慄"),
+ (0xF9DA, "M", "栗"),
+ (0xF9DB, "M", "率"),
+ (0xF9DC, "M", "隆"),
+ (0xF9DD, "M", "利"),
+ (0xF9DE, "M", "吏"),
+ (0xF9DF, "M", "履"),
+ (0xF9E0, "M", "易"),
+ (0xF9E1, "M", "李"),
+ (0xF9E2, "M", "梨"),
+ (0xF9E3, "M", "泥"),
+ (0xF9E4, "M", "理"),
+ (0xF9E5, "M", "痢"),
+ (0xF9E6, "M", "罹"),
+ (0xF9E7, "M", "裏"),
+ (0xF9E8, "M", "裡"),
+ (0xF9E9, "M", "里"),
+ (0xF9EA, "M", "離"),
+ ]
+
+
+def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xF9EB, "M", "匿"),
+ (0xF9EC, "M", "溺"),
+ (0xF9ED, "M", "吝"),
+ (0xF9EE, "M", "燐"),
+ (0xF9EF, "M", "璘"),
+ (0xF9F0, "M", "藺"),
+ (0xF9F1, "M", "隣"),
+ (0xF9F2, "M", "鱗"),
+ (0xF9F3, "M", "麟"),
+ (0xF9F4, "M", "林"),
+ (0xF9F5, "M", "淋"),
+ (0xF9F6, "M", "臨"),
+ (0xF9F7, "M", "立"),
+ (0xF9F8, "M", "笠"),
+ (0xF9F9, "M", "粒"),
+ (0xF9FA, "M", "狀"),
+ (0xF9FB, "M", "炙"),
+ (0xF9FC, "M", "識"),
+ (0xF9FD, "M", "什"),
+ (0xF9FE, "M", "茶"),
+ (0xF9FF, "M", "刺"),
+ (0xFA00, "M", "切"),
+ (0xFA01, "M", "度"),
+ (0xFA02, "M", "拓"),
+ (0xFA03, "M", "糖"),
+ (0xFA04, "M", "宅"),
+ (0xFA05, "M", "洞"),
+ (0xFA06, "M", "暴"),
+ (0xFA07, "M", "輻"),
+ (0xFA08, "M", "行"),
+ (0xFA09, "M", "降"),
+ (0xFA0A, "M", "見"),
+ (0xFA0B, "M", "廓"),
+ (0xFA0C, "M", "兀"),
+ (0xFA0D, "M", "嗀"),
+ (0xFA0E, "V"),
+ (0xFA10, "M", "塚"),
+ (0xFA11, "V"),
+ (0xFA12, "M", "晴"),
+ (0xFA13, "V"),
+ (0xFA15, "M", "凞"),
+ (0xFA16, "M", "猪"),
+ (0xFA17, "M", "益"),
+ (0xFA18, "M", "礼"),
+ (0xFA19, "M", "神"),
+ (0xFA1A, "M", "祥"),
+ (0xFA1B, "M", "福"),
+ (0xFA1C, "M", "靖"),
+ (0xFA1D, "M", "精"),
+ (0xFA1E, "M", "羽"),
+ (0xFA1F, "V"),
+ (0xFA20, "M", "蘒"),
+ (0xFA21, "V"),
+ (0xFA22, "M", "諸"),
+ (0xFA23, "V"),
+ (0xFA25, "M", "逸"),
+ (0xFA26, "M", "都"),
+ (0xFA27, "V"),
+ (0xFA2A, "M", "飯"),
+ (0xFA2B, "M", "飼"),
+ (0xFA2C, "M", "館"),
+ (0xFA2D, "M", "鶴"),
+ (0xFA2E, "M", "郞"),
+ (0xFA2F, "M", "隷"),
+ (0xFA30, "M", "侮"),
+ (0xFA31, "M", "僧"),
+ (0xFA32, "M", "免"),
+ (0xFA33, "M", "勉"),
+ (0xFA34, "M", "勤"),
+ (0xFA35, "M", "卑"),
+ (0xFA36, "M", "喝"),
+ (0xFA37, "M", "嘆"),
+ (0xFA38, "M", "器"),
+ (0xFA39, "M", "塀"),
+ (0xFA3A, "M", "墨"),
+ (0xFA3B, "M", "層"),
+ (0xFA3C, "M", "屮"),
+ (0xFA3D, "M", "悔"),
+ (0xFA3E, "M", "慨"),
+ (0xFA3F, "M", "憎"),
+ (0xFA40, "M", "懲"),
+ (0xFA41, "M", "敏"),
+ (0xFA42, "M", "既"),
+ (0xFA43, "M", "暑"),
+ (0xFA44, "M", "梅"),
+ (0xFA45, "M", "海"),
+ (0xFA46, "M", "渚"),
+ (0xFA47, "M", "漢"),
+ (0xFA48, "M", "煮"),
+ (0xFA49, "M", "爫"),
+ (0xFA4A, "M", "琢"),
+ (0xFA4B, "M", "碑"),
+ (0xFA4C, "M", "社"),
+ (0xFA4D, "M", "祉"),
+ (0xFA4E, "M", "祈"),
+ (0xFA4F, "M", "祐"),
+ (0xFA50, "M", "祖"),
+ (0xFA51, "M", "祝"),
+ (0xFA52, "M", "禍"),
+ (0xFA53, "M", "禎"),
+ ]
+
+
+def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFA54, "M", "穀"),
+ (0xFA55, "M", "突"),
+ (0xFA56, "M", "節"),
+ (0xFA57, "M", "練"),
+ (0xFA58, "M", "縉"),
+ (0xFA59, "M", "繁"),
+ (0xFA5A, "M", "署"),
+ (0xFA5B, "M", "者"),
+ (0xFA5C, "M", "臭"),
+ (0xFA5D, "M", "艹"),
+ (0xFA5F, "M", "著"),
+ (0xFA60, "M", "褐"),
+ (0xFA61, "M", "視"),
+ (0xFA62, "M", "謁"),
+ (0xFA63, "M", "謹"),
+ (0xFA64, "M", "賓"),
+ (0xFA65, "M", "贈"),
+ (0xFA66, "M", "辶"),
+ (0xFA67, "M", "逸"),
+ (0xFA68, "M", "難"),
+ (0xFA69, "M", "響"),
+ (0xFA6A, "M", "頻"),
+ (0xFA6B, "M", "恵"),
+ (0xFA6C, "M", "𤋮"),
+ (0xFA6D, "M", "舘"),
+ (0xFA6E, "X"),
+ (0xFA70, "M", "並"),
+ (0xFA71, "M", "况"),
+ (0xFA72, "M", "全"),
+ (0xFA73, "M", "侀"),
+ (0xFA74, "M", "充"),
+ (0xFA75, "M", "冀"),
+ (0xFA76, "M", "勇"),
+ (0xFA77, "M", "勺"),
+ (0xFA78, "M", "喝"),
+ (0xFA79, "M", "啕"),
+ (0xFA7A, "M", "喙"),
+ (0xFA7B, "M", "嗢"),
+ (0xFA7C, "M", "塚"),
+ (0xFA7D, "M", "墳"),
+ (0xFA7E, "M", "奄"),
+ (0xFA7F, "M", "奔"),
+ (0xFA80, "M", "婢"),
+ (0xFA81, "M", "嬨"),
+ (0xFA82, "M", "廒"),
+ (0xFA83, "M", "廙"),
+ (0xFA84, "M", "彩"),
+ (0xFA85, "M", "徭"),
+ (0xFA86, "M", "惘"),
+ (0xFA87, "M", "慎"),
+ (0xFA88, "M", "愈"),
+ (0xFA89, "M", "憎"),
+ (0xFA8A, "M", "慠"),
+ (0xFA8B, "M", "懲"),
+ (0xFA8C, "M", "戴"),
+ (0xFA8D, "M", "揄"),
+ (0xFA8E, "M", "搜"),
+ (0xFA8F, "M", "摒"),
+ (0xFA90, "M", "敖"),
+ (0xFA91, "M", "晴"),
+ (0xFA92, "M", "朗"),
+ (0xFA93, "M", "望"),
+ (0xFA94, "M", "杖"),
+ (0xFA95, "M", "歹"),
+ (0xFA96, "M", "殺"),
+ (0xFA97, "M", "流"),
+ (0xFA98, "M", "滛"),
+ (0xFA99, "M", "滋"),
+ (0xFA9A, "M", "漢"),
+ (0xFA9B, "M", "瀞"),
+ (0xFA9C, "M", "煮"),
+ (0xFA9D, "M", "瞧"),
+ (0xFA9E, "M", "爵"),
+ (0xFA9F, "M", "犯"),
+ (0xFAA0, "M", "猪"),
+ (0xFAA1, "M", "瑱"),
+ (0xFAA2, "M", "甆"),
+ (0xFAA3, "M", "画"),
+ (0xFAA4, "M", "瘝"),
+ (0xFAA5, "M", "瘟"),
+ (0xFAA6, "M", "益"),
+ (0xFAA7, "M", "盛"),
+ (0xFAA8, "M", "直"),
+ (0xFAA9, "M", "睊"),
+ (0xFAAA, "M", "着"),
+ (0xFAAB, "M", "磌"),
+ (0xFAAC, "M", "窱"),
+ (0xFAAD, "M", "節"),
+ (0xFAAE, "M", "类"),
+ (0xFAAF, "M", "絛"),
+ (0xFAB0, "M", "練"),
+ (0xFAB1, "M", "缾"),
+ (0xFAB2, "M", "者"),
+ (0xFAB3, "M", "荒"),
+ (0xFAB4, "M", "華"),
+ (0xFAB5, "M", "蝹"),
+ (0xFAB6, "M", "襁"),
+ (0xFAB7, "M", "覆"),
+ (0xFAB8, "M", "視"),
+ (0xFAB9, "M", "調"),
+ ]
+
+
+def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFABA, "M", "諸"),
+ (0xFABB, "M", "請"),
+ (0xFABC, "M", "謁"),
+ (0xFABD, "M", "諾"),
+ (0xFABE, "M", "諭"),
+ (0xFABF, "M", "謹"),
+ (0xFAC0, "M", "變"),
+ (0xFAC1, "M", "贈"),
+ (0xFAC2, "M", "輸"),
+ (0xFAC3, "M", "遲"),
+ (0xFAC4, "M", "醙"),
+ (0xFAC5, "M", "鉶"),
+ (0xFAC6, "M", "陼"),
+ (0xFAC7, "M", "難"),
+ (0xFAC8, "M", "靖"),
+ (0xFAC9, "M", "韛"),
+ (0xFACA, "M", "響"),
+ (0xFACB, "M", "頋"),
+ (0xFACC, "M", "頻"),
+ (0xFACD, "M", "鬒"),
+ (0xFACE, "M", "龜"),
+ (0xFACF, "M", "𢡊"),
+ (0xFAD0, "M", "𢡄"),
+ (0xFAD1, "M", "𣏕"),
+ (0xFAD2, "M", "㮝"),
+ (0xFAD3, "M", "䀘"),
+ (0xFAD4, "M", "䀹"),
+ (0xFAD5, "M", "𥉉"),
+ (0xFAD6, "M", "𥳐"),
+ (0xFAD7, "M", "𧻓"),
+ (0xFAD8, "M", "齃"),
+ (0xFAD9, "M", "龎"),
+ (0xFADA, "X"),
+ (0xFB00, "M", "ff"),
+ (0xFB01, "M", "fi"),
+ (0xFB02, "M", "fl"),
+ (0xFB03, "M", "ffi"),
+ (0xFB04, "M", "ffl"),
+ (0xFB05, "M", "st"),
+ (0xFB07, "X"),
+ (0xFB13, "M", "մն"),
+ (0xFB14, "M", "մե"),
+ (0xFB15, "M", "մի"),
+ (0xFB16, "M", "վն"),
+ (0xFB17, "M", "մխ"),
+ (0xFB18, "X"),
+ (0xFB1D, "M", "יִ"),
+ (0xFB1E, "V"),
+ (0xFB1F, "M", "ײַ"),
+ (0xFB20, "M", "ע"),
+ (0xFB21, "M", "א"),
+ (0xFB22, "M", "ד"),
+ (0xFB23, "M", "ה"),
+ (0xFB24, "M", "כ"),
+ (0xFB25, "M", "ל"),
+ (0xFB26, "M", "ם"),
+ (0xFB27, "M", "ר"),
+ (0xFB28, "M", "ת"),
+ (0xFB29, "M", "+"),
+ (0xFB2A, "M", "שׁ"),
+ (0xFB2B, "M", "שׂ"),
+ (0xFB2C, "M", "שּׁ"),
+ (0xFB2D, "M", "שּׂ"),
+ (0xFB2E, "M", "אַ"),
+ (0xFB2F, "M", "אָ"),
+ (0xFB30, "M", "אּ"),
+ (0xFB31, "M", "בּ"),
+ (0xFB32, "M", "גּ"),
+ (0xFB33, "M", "דּ"),
+ (0xFB34, "M", "הּ"),
+ (0xFB35, "M", "וּ"),
+ (0xFB36, "M", "זּ"),
+ (0xFB37, "X"),
+ (0xFB38, "M", "טּ"),
+ (0xFB39, "M", "יּ"),
+ (0xFB3A, "M", "ךּ"),
+ (0xFB3B, "M", "כּ"),
+ (0xFB3C, "M", "לּ"),
+ (0xFB3D, "X"),
+ (0xFB3E, "M", "מּ"),
+ (0xFB3F, "X"),
+ (0xFB40, "M", "נּ"),
+ (0xFB41, "M", "סּ"),
+ (0xFB42, "X"),
+ (0xFB43, "M", "ףּ"),
+ (0xFB44, "M", "פּ"),
+ (0xFB45, "X"),
+ (0xFB46, "M", "צּ"),
+ (0xFB47, "M", "קּ"),
+ (0xFB48, "M", "רּ"),
+ (0xFB49, "M", "שּ"),
+ (0xFB4A, "M", "תּ"),
+ (0xFB4B, "M", "וֹ"),
+ (0xFB4C, "M", "בֿ"),
+ (0xFB4D, "M", "כֿ"),
+ (0xFB4E, "M", "פֿ"),
+ (0xFB4F, "M", "אל"),
+ (0xFB50, "M", "ٱ"),
+ (0xFB52, "M", "ٻ"),
+ (0xFB56, "M", "پ"),
+ ]
+
+
+def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFB5A, "M", "ڀ"),
+ (0xFB5E, "M", "ٺ"),
+ (0xFB62, "M", "ٿ"),
+ (0xFB66, "M", "ٹ"),
+ (0xFB6A, "M", "ڤ"),
+ (0xFB6E, "M", "ڦ"),
+ (0xFB72, "M", "ڄ"),
+ (0xFB76, "M", "ڃ"),
+ (0xFB7A, "M", "چ"),
+ (0xFB7E, "M", "ڇ"),
+ (0xFB82, "M", "ڍ"),
+ (0xFB84, "M", "ڌ"),
+ (0xFB86, "M", "ڎ"),
+ (0xFB88, "M", "ڈ"),
+ (0xFB8A, "M", "ژ"),
+ (0xFB8C, "M", "ڑ"),
+ (0xFB8E, "M", "ک"),
+ (0xFB92, "M", "گ"),
+ (0xFB96, "M", "ڳ"),
+ (0xFB9A, "M", "ڱ"),
+ (0xFB9E, "M", "ں"),
+ (0xFBA0, "M", "ڻ"),
+ (0xFBA4, "M", "ۀ"),
+ (0xFBA6, "M", "ہ"),
+ (0xFBAA, "M", "ھ"),
+ (0xFBAE, "M", "ے"),
+ (0xFBB0, "M", "ۓ"),
+ (0xFBB2, "V"),
+ (0xFBC3, "X"),
+ (0xFBD3, "M", "ڭ"),
+ (0xFBD7, "M", "ۇ"),
+ (0xFBD9, "M", "ۆ"),
+ (0xFBDB, "M", "ۈ"),
+ (0xFBDD, "M", "ۇٴ"),
+ (0xFBDE, "M", "ۋ"),
+ (0xFBE0, "M", "ۅ"),
+ (0xFBE2, "M", "ۉ"),
+ (0xFBE4, "M", "ې"),
+ (0xFBE8, "M", "ى"),
+ (0xFBEA, "M", "ئا"),
+ (0xFBEC, "M", "ئە"),
+ (0xFBEE, "M", "ئو"),
+ (0xFBF0, "M", "ئۇ"),
+ (0xFBF2, "M", "ئۆ"),
+ (0xFBF4, "M", "ئۈ"),
+ (0xFBF6, "M", "ئې"),
+ (0xFBF9, "M", "ئى"),
+ (0xFBFC, "M", "ی"),
+ (0xFC00, "M", "ئج"),
+ (0xFC01, "M", "ئح"),
+ (0xFC02, "M", "ئم"),
+ (0xFC03, "M", "ئى"),
+ (0xFC04, "M", "ئي"),
+ (0xFC05, "M", "بج"),
+ (0xFC06, "M", "بح"),
+ (0xFC07, "M", "بخ"),
+ (0xFC08, "M", "بم"),
+ (0xFC09, "M", "بى"),
+ (0xFC0A, "M", "بي"),
+ (0xFC0B, "M", "تج"),
+ (0xFC0C, "M", "تح"),
+ (0xFC0D, "M", "تخ"),
+ (0xFC0E, "M", "تم"),
+ (0xFC0F, "M", "تى"),
+ (0xFC10, "M", "تي"),
+ (0xFC11, "M", "ثج"),
+ (0xFC12, "M", "ثم"),
+ (0xFC13, "M", "ثى"),
+ (0xFC14, "M", "ثي"),
+ (0xFC15, "M", "جح"),
+ (0xFC16, "M", "جم"),
+ (0xFC17, "M", "حج"),
+ (0xFC18, "M", "حم"),
+ (0xFC19, "M", "خج"),
+ (0xFC1A, "M", "خح"),
+ (0xFC1B, "M", "خم"),
+ (0xFC1C, "M", "سج"),
+ (0xFC1D, "M", "سح"),
+ (0xFC1E, "M", "سخ"),
+ (0xFC1F, "M", "سم"),
+ (0xFC20, "M", "صح"),
+ (0xFC21, "M", "صم"),
+ (0xFC22, "M", "ضج"),
+ (0xFC23, "M", "ضح"),
+ (0xFC24, "M", "ضخ"),
+ (0xFC25, "M", "ضم"),
+ (0xFC26, "M", "طح"),
+ (0xFC27, "M", "طم"),
+ (0xFC28, "M", "ظم"),
+ (0xFC29, "M", "عج"),
+ (0xFC2A, "M", "عم"),
+ (0xFC2B, "M", "غج"),
+ (0xFC2C, "M", "غم"),
+ (0xFC2D, "M", "فج"),
+ (0xFC2E, "M", "فح"),
+ (0xFC2F, "M", "فخ"),
+ (0xFC30, "M", "فم"),
+ (0xFC31, "M", "فى"),
+ (0xFC32, "M", "في"),
+ (0xFC33, "M", "قح"),
+ ]
+
+
+def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFC34, "M", "قم"),
+ (0xFC35, "M", "قى"),
+ (0xFC36, "M", "قي"),
+ (0xFC37, "M", "كا"),
+ (0xFC38, "M", "كج"),
+ (0xFC39, "M", "كح"),
+ (0xFC3A, "M", "كخ"),
+ (0xFC3B, "M", "كل"),
+ (0xFC3C, "M", "كم"),
+ (0xFC3D, "M", "كى"),
+ (0xFC3E, "M", "كي"),
+ (0xFC3F, "M", "لج"),
+ (0xFC40, "M", "لح"),
+ (0xFC41, "M", "لخ"),
+ (0xFC42, "M", "لم"),
+ (0xFC43, "M", "لى"),
+ (0xFC44, "M", "لي"),
+ (0xFC45, "M", "مج"),
+ (0xFC46, "M", "مح"),
+ (0xFC47, "M", "مخ"),
+ (0xFC48, "M", "مم"),
+ (0xFC49, "M", "مى"),
+ (0xFC4A, "M", "مي"),
+ (0xFC4B, "M", "نج"),
+ (0xFC4C, "M", "نح"),
+ (0xFC4D, "M", "نخ"),
+ (0xFC4E, "M", "نم"),
+ (0xFC4F, "M", "نى"),
+ (0xFC50, "M", "ني"),
+ (0xFC51, "M", "هج"),
+ (0xFC52, "M", "هم"),
+ (0xFC53, "M", "هى"),
+ (0xFC54, "M", "هي"),
+ (0xFC55, "M", "يج"),
+ (0xFC56, "M", "يح"),
+ (0xFC57, "M", "يخ"),
+ (0xFC58, "M", "يم"),
+ (0xFC59, "M", "يى"),
+ (0xFC5A, "M", "يي"),
+ (0xFC5B, "M", "ذٰ"),
+ (0xFC5C, "M", "رٰ"),
+ (0xFC5D, "M", "ىٰ"),
+ (0xFC5E, "M", " ٌّ"),
+ (0xFC5F, "M", " ٍّ"),
+ (0xFC60, "M", " َّ"),
+ (0xFC61, "M", " ُّ"),
+ (0xFC62, "M", " ِّ"),
+ (0xFC63, "M", " ّٰ"),
+ (0xFC64, "M", "ئر"),
+ (0xFC65, "M", "ئز"),
+ (0xFC66, "M", "ئم"),
+ (0xFC67, "M", "ئن"),
+ (0xFC68, "M", "ئى"),
+ (0xFC69, "M", "ئي"),
+ (0xFC6A, "M", "بر"),
+ (0xFC6B, "M", "بز"),
+ (0xFC6C, "M", "بم"),
+ (0xFC6D, "M", "بن"),
+ (0xFC6E, "M", "بى"),
+ (0xFC6F, "M", "بي"),
+ (0xFC70, "M", "تر"),
+ (0xFC71, "M", "تز"),
+ (0xFC72, "M", "تم"),
+ (0xFC73, "M", "تن"),
+ (0xFC74, "M", "تى"),
+ (0xFC75, "M", "تي"),
+ (0xFC76, "M", "ثر"),
+ (0xFC77, "M", "ثز"),
+ (0xFC78, "M", "ثم"),
+ (0xFC79, "M", "ثن"),
+ (0xFC7A, "M", "ثى"),
+ (0xFC7B, "M", "ثي"),
+ (0xFC7C, "M", "فى"),
+ (0xFC7D, "M", "في"),
+ (0xFC7E, "M", "قى"),
+ (0xFC7F, "M", "قي"),
+ (0xFC80, "M", "كا"),
+ (0xFC81, "M", "كل"),
+ (0xFC82, "M", "كم"),
+ (0xFC83, "M", "كى"),
+ (0xFC84, "M", "كي"),
+ (0xFC85, "M", "لم"),
+ (0xFC86, "M", "لى"),
+ (0xFC87, "M", "لي"),
+ (0xFC88, "M", "ما"),
+ (0xFC89, "M", "مم"),
+ (0xFC8A, "M", "نر"),
+ (0xFC8B, "M", "نز"),
+ (0xFC8C, "M", "نم"),
+ (0xFC8D, "M", "نن"),
+ (0xFC8E, "M", "نى"),
+ (0xFC8F, "M", "ني"),
+ (0xFC90, "M", "ىٰ"),
+ (0xFC91, "M", "ير"),
+ (0xFC92, "M", "يز"),
+ (0xFC93, "M", "يم"),
+ (0xFC94, "M", "ين"),
+ (0xFC95, "M", "يى"),
+ (0xFC96, "M", "يي"),
+ (0xFC97, "M", "ئج"),
+ ]
+
+
+def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFC98, "M", "ئح"),
+ (0xFC99, "M", "ئخ"),
+ (0xFC9A, "M", "ئم"),
+ (0xFC9B, "M", "ئه"),
+ (0xFC9C, "M", "بج"),
+ (0xFC9D, "M", "بح"),
+ (0xFC9E, "M", "بخ"),
+ (0xFC9F, "M", "بم"),
+ (0xFCA0, "M", "به"),
+ (0xFCA1, "M", "تج"),
+ (0xFCA2, "M", "تح"),
+ (0xFCA3, "M", "تخ"),
+ (0xFCA4, "M", "تم"),
+ (0xFCA5, "M", "ته"),
+ (0xFCA6, "M", "ثم"),
+ (0xFCA7, "M", "جح"),
+ (0xFCA8, "M", "جم"),
+ (0xFCA9, "M", "حج"),
+ (0xFCAA, "M", "حم"),
+ (0xFCAB, "M", "خج"),
+ (0xFCAC, "M", "خم"),
+ (0xFCAD, "M", "سج"),
+ (0xFCAE, "M", "سح"),
+ (0xFCAF, "M", "سخ"),
+ (0xFCB0, "M", "سم"),
+ (0xFCB1, "M", "صح"),
+ (0xFCB2, "M", "صخ"),
+ (0xFCB3, "M", "صم"),
+ (0xFCB4, "M", "ضج"),
+ (0xFCB5, "M", "ضح"),
+ (0xFCB6, "M", "ضخ"),
+ (0xFCB7, "M", "ضم"),
+ (0xFCB8, "M", "طح"),
+ (0xFCB9, "M", "ظم"),
+ (0xFCBA, "M", "عج"),
+ (0xFCBB, "M", "عم"),
+ (0xFCBC, "M", "غج"),
+ (0xFCBD, "M", "غم"),
+ (0xFCBE, "M", "فج"),
+ (0xFCBF, "M", "فح"),
+ (0xFCC0, "M", "فخ"),
+ (0xFCC1, "M", "فم"),
+ (0xFCC2, "M", "قح"),
+ (0xFCC3, "M", "قم"),
+ (0xFCC4, "M", "كج"),
+ (0xFCC5, "M", "كح"),
+ (0xFCC6, "M", "كخ"),
+ (0xFCC7, "M", "كل"),
+ (0xFCC8, "M", "كم"),
+ (0xFCC9, "M", "لج"),
+ (0xFCCA, "M", "لح"),
+ (0xFCCB, "M", "لخ"),
+ (0xFCCC, "M", "لم"),
+ (0xFCCD, "M", "له"),
+ (0xFCCE, "M", "مج"),
+ (0xFCCF, "M", "مح"),
+ (0xFCD0, "M", "مخ"),
+ (0xFCD1, "M", "مم"),
+ (0xFCD2, "M", "نج"),
+ (0xFCD3, "M", "نح"),
+ (0xFCD4, "M", "نخ"),
+ (0xFCD5, "M", "نم"),
+ (0xFCD6, "M", "نه"),
+ (0xFCD7, "M", "هج"),
+ (0xFCD8, "M", "هم"),
+ (0xFCD9, "M", "هٰ"),
+ (0xFCDA, "M", "يج"),
+ (0xFCDB, "M", "يح"),
+ (0xFCDC, "M", "يخ"),
+ (0xFCDD, "M", "يم"),
+ (0xFCDE, "M", "يه"),
+ (0xFCDF, "M", "ئم"),
+ (0xFCE0, "M", "ئه"),
+ (0xFCE1, "M", "بم"),
+ (0xFCE2, "M", "به"),
+ (0xFCE3, "M", "تم"),
+ (0xFCE4, "M", "ته"),
+ (0xFCE5, "M", "ثم"),
+ (0xFCE6, "M", "ثه"),
+ (0xFCE7, "M", "سم"),
+ (0xFCE8, "M", "سه"),
+ (0xFCE9, "M", "شم"),
+ (0xFCEA, "M", "شه"),
+ (0xFCEB, "M", "كل"),
+ (0xFCEC, "M", "كم"),
+ (0xFCED, "M", "لم"),
+ (0xFCEE, "M", "نم"),
+ (0xFCEF, "M", "نه"),
+ (0xFCF0, "M", "يم"),
+ (0xFCF1, "M", "يه"),
+ (0xFCF2, "M", "ـَّ"),
+ (0xFCF3, "M", "ـُّ"),
+ (0xFCF4, "M", "ـِّ"),
+ (0xFCF5, "M", "طى"),
+ (0xFCF6, "M", "طي"),
+ (0xFCF7, "M", "عى"),
+ (0xFCF8, "M", "عي"),
+ (0xFCF9, "M", "غى"),
+ (0xFCFA, "M", "غي"),
+ (0xFCFB, "M", "سى"),
+ ]
+
+
+def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFCFC, "M", "سي"),
+ (0xFCFD, "M", "شى"),
+ (0xFCFE, "M", "شي"),
+ (0xFCFF, "M", "حى"),
+ (0xFD00, "M", "حي"),
+ (0xFD01, "M", "جى"),
+ (0xFD02, "M", "جي"),
+ (0xFD03, "M", "خى"),
+ (0xFD04, "M", "خي"),
+ (0xFD05, "M", "صى"),
+ (0xFD06, "M", "صي"),
+ (0xFD07, "M", "ضى"),
+ (0xFD08, "M", "ضي"),
+ (0xFD09, "M", "شج"),
+ (0xFD0A, "M", "شح"),
+ (0xFD0B, "M", "شخ"),
+ (0xFD0C, "M", "شم"),
+ (0xFD0D, "M", "شر"),
+ (0xFD0E, "M", "سر"),
+ (0xFD0F, "M", "صر"),
+ (0xFD10, "M", "ضر"),
+ (0xFD11, "M", "طى"),
+ (0xFD12, "M", "طي"),
+ (0xFD13, "M", "عى"),
+ (0xFD14, "M", "عي"),
+ (0xFD15, "M", "غى"),
+ (0xFD16, "M", "غي"),
+ (0xFD17, "M", "سى"),
+ (0xFD18, "M", "سي"),
+ (0xFD19, "M", "شى"),
+ (0xFD1A, "M", "شي"),
+ (0xFD1B, "M", "حى"),
+ (0xFD1C, "M", "حي"),
+ (0xFD1D, "M", "جى"),
+ (0xFD1E, "M", "جي"),
+ (0xFD1F, "M", "خى"),
+ (0xFD20, "M", "خي"),
+ (0xFD21, "M", "صى"),
+ (0xFD22, "M", "صي"),
+ (0xFD23, "M", "ضى"),
+ (0xFD24, "M", "ضي"),
+ (0xFD25, "M", "شج"),
+ (0xFD26, "M", "شح"),
+ (0xFD27, "M", "شخ"),
+ (0xFD28, "M", "شم"),
+ (0xFD29, "M", "شر"),
+ (0xFD2A, "M", "سر"),
+ (0xFD2B, "M", "صر"),
+ (0xFD2C, "M", "ضر"),
+ (0xFD2D, "M", "شج"),
+ (0xFD2E, "M", "شح"),
+ (0xFD2F, "M", "شخ"),
+ (0xFD30, "M", "شم"),
+ (0xFD31, "M", "سه"),
+ (0xFD32, "M", "شه"),
+ (0xFD33, "M", "طم"),
+ (0xFD34, "M", "سج"),
+ (0xFD35, "M", "سح"),
+ (0xFD36, "M", "سخ"),
+ (0xFD37, "M", "شج"),
+ (0xFD38, "M", "شح"),
+ (0xFD39, "M", "شخ"),
+ (0xFD3A, "M", "طم"),
+ (0xFD3B, "M", "ظم"),
+ (0xFD3C, "M", "اً"),
+ (0xFD3E, "V"),
+ (0xFD50, "M", "تجم"),
+ (0xFD51, "M", "تحج"),
+ (0xFD53, "M", "تحم"),
+ (0xFD54, "M", "تخم"),
+ (0xFD55, "M", "تمج"),
+ (0xFD56, "M", "تمح"),
+ (0xFD57, "M", "تمخ"),
+ (0xFD58, "M", "جمح"),
+ (0xFD5A, "M", "حمي"),
+ (0xFD5B, "M", "حمى"),
+ (0xFD5C, "M", "سحج"),
+ (0xFD5D, "M", "سجح"),
+ (0xFD5E, "M", "سجى"),
+ (0xFD5F, "M", "سمح"),
+ (0xFD61, "M", "سمج"),
+ (0xFD62, "M", "سمم"),
+ (0xFD64, "M", "صحح"),
+ (0xFD66, "M", "صمم"),
+ (0xFD67, "M", "شحم"),
+ (0xFD69, "M", "شجي"),
+ (0xFD6A, "M", "شمخ"),
+ (0xFD6C, "M", "شمم"),
+ (0xFD6E, "M", "ضحى"),
+ (0xFD6F, "M", "ضخم"),
+ (0xFD71, "M", "طمح"),
+ (0xFD73, "M", "طمم"),
+ (0xFD74, "M", "طمي"),
+ (0xFD75, "M", "عجم"),
+ (0xFD76, "M", "عمم"),
+ (0xFD78, "M", "عمى"),
+ (0xFD79, "M", "غمم"),
+ (0xFD7A, "M", "غمي"),
+ (0xFD7B, "M", "غمى"),
+ (0xFD7C, "M", "فخم"),
+ ]
+
+
+def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFD7E, "M", "قمح"),
+ (0xFD7F, "M", "قمم"),
+ (0xFD80, "M", "لحم"),
+ (0xFD81, "M", "لحي"),
+ (0xFD82, "M", "لحى"),
+ (0xFD83, "M", "لجج"),
+ (0xFD85, "M", "لخم"),
+ (0xFD87, "M", "لمح"),
+ (0xFD89, "M", "محج"),
+ (0xFD8A, "M", "محم"),
+ (0xFD8B, "M", "محي"),
+ (0xFD8C, "M", "مجح"),
+ (0xFD8D, "M", "مجم"),
+ (0xFD8E, "M", "مخج"),
+ (0xFD8F, "M", "مخم"),
+ (0xFD90, "X"),
+ (0xFD92, "M", "مجخ"),
+ (0xFD93, "M", "همج"),
+ (0xFD94, "M", "همم"),
+ (0xFD95, "M", "نحم"),
+ (0xFD96, "M", "نحى"),
+ (0xFD97, "M", "نجم"),
+ (0xFD99, "M", "نجى"),
+ (0xFD9A, "M", "نمي"),
+ (0xFD9B, "M", "نمى"),
+ (0xFD9C, "M", "يمم"),
+ (0xFD9E, "M", "بخي"),
+ (0xFD9F, "M", "تجي"),
+ (0xFDA0, "M", "تجى"),
+ (0xFDA1, "M", "تخي"),
+ (0xFDA2, "M", "تخى"),
+ (0xFDA3, "M", "تمي"),
+ (0xFDA4, "M", "تمى"),
+ (0xFDA5, "M", "جمي"),
+ (0xFDA6, "M", "جحى"),
+ (0xFDA7, "M", "جمى"),
+ (0xFDA8, "M", "سخى"),
+ (0xFDA9, "M", "صحي"),
+ (0xFDAA, "M", "شحي"),
+ (0xFDAB, "M", "ضحي"),
+ (0xFDAC, "M", "لجي"),
+ (0xFDAD, "M", "لمي"),
+ (0xFDAE, "M", "يحي"),
+ (0xFDAF, "M", "يجي"),
+ (0xFDB0, "M", "يمي"),
+ (0xFDB1, "M", "ممي"),
+ (0xFDB2, "M", "قمي"),
+ (0xFDB3, "M", "نحي"),
+ (0xFDB4, "M", "قمح"),
+ (0xFDB5, "M", "لحم"),
+ (0xFDB6, "M", "عمي"),
+ (0xFDB7, "M", "كمي"),
+ (0xFDB8, "M", "نجح"),
+ (0xFDB9, "M", "مخي"),
+ (0xFDBA, "M", "لجم"),
+ (0xFDBB, "M", "كمم"),
+ (0xFDBC, "M", "لجم"),
+ (0xFDBD, "M", "نجح"),
+ (0xFDBE, "M", "جحي"),
+ (0xFDBF, "M", "حجي"),
+ (0xFDC0, "M", "مجي"),
+ (0xFDC1, "M", "فمي"),
+ (0xFDC2, "M", "بحي"),
+ (0xFDC3, "M", "كمم"),
+ (0xFDC4, "M", "عجم"),
+ (0xFDC5, "M", "صمم"),
+ (0xFDC6, "M", "سخي"),
+ (0xFDC7, "M", "نجي"),
+ (0xFDC8, "X"),
+ (0xFDCF, "V"),
+ (0xFDD0, "X"),
+ (0xFDF0, "M", "صلے"),
+ (0xFDF1, "M", "قلے"),
+ (0xFDF2, "M", "الله"),
+ (0xFDF3, "M", "اكبر"),
+ (0xFDF4, "M", "محمد"),
+ (0xFDF5, "M", "صلعم"),
+ (0xFDF6, "M", "رسول"),
+ (0xFDF7, "M", "عليه"),
+ (0xFDF8, "M", "وسلم"),
+ (0xFDF9, "M", "صلى"),
+ (0xFDFA, "M", "صلى الله عليه وسلم"),
+ (0xFDFB, "M", "جل جلاله"),
+ (0xFDFC, "M", "ریال"),
+ (0xFDFD, "V"),
+ (0xFE00, "I"),
+ (0xFE10, "M", ","),
+ (0xFE11, "M", "、"),
+ (0xFE12, "X"),
+ (0xFE13, "M", ":"),
+ (0xFE14, "M", ";"),
+ (0xFE15, "M", "!"),
+ (0xFE16, "M", "?"),
+ (0xFE17, "M", "〖"),
+ (0xFE18, "M", "〗"),
+ (0xFE19, "X"),
+ (0xFE20, "V"),
+ (0xFE30, "X"),
+ (0xFE31, "M", "—"),
+ (0xFE32, "M", "–"),
+ ]
+
+
+def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFE33, "M", "_"),
+ (0xFE35, "M", "("),
+ (0xFE36, "M", ")"),
+ (0xFE37, "M", "{"),
+ (0xFE38, "M", "}"),
+ (0xFE39, "M", "〔"),
+ (0xFE3A, "M", "〕"),
+ (0xFE3B, "M", "【"),
+ (0xFE3C, "M", "】"),
+ (0xFE3D, "M", "《"),
+ (0xFE3E, "M", "》"),
+ (0xFE3F, "M", "〈"),
+ (0xFE40, "M", "〉"),
+ (0xFE41, "M", "「"),
+ (0xFE42, "M", "」"),
+ (0xFE43, "M", "『"),
+ (0xFE44, "M", "』"),
+ (0xFE45, "V"),
+ (0xFE47, "M", "["),
+ (0xFE48, "M", "]"),
+ (0xFE49, "M", " ̅"),
+ (0xFE4D, "M", "_"),
+ (0xFE50, "M", ","),
+ (0xFE51, "M", "、"),
+ (0xFE52, "X"),
+ (0xFE54, "M", ";"),
+ (0xFE55, "M", ":"),
+ (0xFE56, "M", "?"),
+ (0xFE57, "M", "!"),
+ (0xFE58, "M", "—"),
+ (0xFE59, "M", "("),
+ (0xFE5A, "M", ")"),
+ (0xFE5B, "M", "{"),
+ (0xFE5C, "M", "}"),
+ (0xFE5D, "M", "〔"),
+ (0xFE5E, "M", "〕"),
+ (0xFE5F, "M", "#"),
+ (0xFE60, "M", "&"),
+ (0xFE61, "M", "*"),
+ (0xFE62, "M", "+"),
+ (0xFE63, "M", "-"),
+ (0xFE64, "M", "<"),
+ (0xFE65, "M", ">"),
+ (0xFE66, "M", "="),
+ (0xFE67, "X"),
+ (0xFE68, "M", "\\"),
+ (0xFE69, "M", "$"),
+ (0xFE6A, "M", "%"),
+ (0xFE6B, "M", "@"),
+ (0xFE6C, "X"),
+ (0xFE70, "M", " ً"),
+ (0xFE71, "M", "ـً"),
+ (0xFE72, "M", " ٌ"),
+ (0xFE73, "V"),
+ (0xFE74, "M", " ٍ"),
+ (0xFE75, "X"),
+ (0xFE76, "M", " َ"),
+ (0xFE77, "M", "ـَ"),
+ (0xFE78, "M", " ُ"),
+ (0xFE79, "M", "ـُ"),
+ (0xFE7A, "M", " ِ"),
+ (0xFE7B, "M", "ـِ"),
+ (0xFE7C, "M", " ّ"),
+ (0xFE7D, "M", "ـّ"),
+ (0xFE7E, "M", " ْ"),
+ (0xFE7F, "M", "ـْ"),
+ (0xFE80, "M", "ء"),
+ (0xFE81, "M", "آ"),
+ (0xFE83, "M", "أ"),
+ (0xFE85, "M", "ؤ"),
+ (0xFE87, "M", "إ"),
+ (0xFE89, "M", "ئ"),
+ (0xFE8D, "M", "ا"),
+ (0xFE8F, "M", "ب"),
+ (0xFE93, "M", "ة"),
+ (0xFE95, "M", "ت"),
+ (0xFE99, "M", "ث"),
+ (0xFE9D, "M", "ج"),
+ (0xFEA1, "M", "ح"),
+ (0xFEA5, "M", "خ"),
+ (0xFEA9, "M", "د"),
+ (0xFEAB, "M", "ذ"),
+ (0xFEAD, "M", "ر"),
+ (0xFEAF, "M", "ز"),
+ (0xFEB1, "M", "س"),
+ (0xFEB5, "M", "ش"),
+ (0xFEB9, "M", "ص"),
+ (0xFEBD, "M", "ض"),
+ (0xFEC1, "M", "ط"),
+ (0xFEC5, "M", "ظ"),
+ (0xFEC9, "M", "ع"),
+ (0xFECD, "M", "غ"),
+ (0xFED1, "M", "ف"),
+ (0xFED5, "M", "ق"),
+ (0xFED9, "M", "ك"),
+ (0xFEDD, "M", "ل"),
+ (0xFEE1, "M", "م"),
+ (0xFEE5, "M", "ن"),
+ (0xFEE9, "M", "ه"),
+ (0xFEED, "M", "و"),
+ ]
+
+
+def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFEEF, "M", "ى"),
+ (0xFEF1, "M", "ي"),
+ (0xFEF5, "M", "لآ"),
+ (0xFEF7, "M", "لأ"),
+ (0xFEF9, "M", "لإ"),
+ (0xFEFB, "M", "لا"),
+ (0xFEFD, "X"),
+ (0xFEFF, "I"),
+ (0xFF00, "X"),
+ (0xFF01, "M", "!"),
+ (0xFF02, "M", '"'),
+ (0xFF03, "M", "#"),
+ (0xFF04, "M", "$"),
+ (0xFF05, "M", "%"),
+ (0xFF06, "M", "&"),
+ (0xFF07, "M", "'"),
+ (0xFF08, "M", "("),
+ (0xFF09, "M", ")"),
+ (0xFF0A, "M", "*"),
+ (0xFF0B, "M", "+"),
+ (0xFF0C, "M", ","),
+ (0xFF0D, "M", "-"),
+ (0xFF0E, "M", "."),
+ (0xFF0F, "M", "/"),
+ (0xFF10, "M", "0"),
+ (0xFF11, "M", "1"),
+ (0xFF12, "M", "2"),
+ (0xFF13, "M", "3"),
+ (0xFF14, "M", "4"),
+ (0xFF15, "M", "5"),
+ (0xFF16, "M", "6"),
+ (0xFF17, "M", "7"),
+ (0xFF18, "M", "8"),
+ (0xFF19, "M", "9"),
+ (0xFF1A, "M", ":"),
+ (0xFF1B, "M", ";"),
+ (0xFF1C, "M", "<"),
+ (0xFF1D, "M", "="),
+ (0xFF1E, "M", ">"),
+ (0xFF1F, "M", "?"),
+ (0xFF20, "M", "@"),
+ (0xFF21, "M", "a"),
+ (0xFF22, "M", "b"),
+ (0xFF23, "M", "c"),
+ (0xFF24, "M", "d"),
+ (0xFF25, "M", "e"),
+ (0xFF26, "M", "f"),
+ (0xFF27, "M", "g"),
+ (0xFF28, "M", "h"),
+ (0xFF29, "M", "i"),
+ (0xFF2A, "M", "j"),
+ (0xFF2B, "M", "k"),
+ (0xFF2C, "M", "l"),
+ (0xFF2D, "M", "m"),
+ (0xFF2E, "M", "n"),
+ (0xFF2F, "M", "o"),
+ (0xFF30, "M", "p"),
+ (0xFF31, "M", "q"),
+ (0xFF32, "M", "r"),
+ (0xFF33, "M", "s"),
+ (0xFF34, "M", "t"),
+ (0xFF35, "M", "u"),
+ (0xFF36, "M", "v"),
+ (0xFF37, "M", "w"),
+ (0xFF38, "M", "x"),
+ (0xFF39, "M", "y"),
+ (0xFF3A, "M", "z"),
+ (0xFF3B, "M", "["),
+ (0xFF3C, "M", "\\"),
+ (0xFF3D, "M", "]"),
+ (0xFF3E, "M", "^"),
+ (0xFF3F, "M", "_"),
+ (0xFF40, "M", "`"),
+ (0xFF41, "M", "a"),
+ (0xFF42, "M", "b"),
+ (0xFF43, "M", "c"),
+ (0xFF44, "M", "d"),
+ (0xFF45, "M", "e"),
+ (0xFF46, "M", "f"),
+ (0xFF47, "M", "g"),
+ (0xFF48, "M", "h"),
+ (0xFF49, "M", "i"),
+ (0xFF4A, "M", "j"),
+ (0xFF4B, "M", "k"),
+ (0xFF4C, "M", "l"),
+ (0xFF4D, "M", "m"),
+ (0xFF4E, "M", "n"),
+ (0xFF4F, "M", "o"),
+ (0xFF50, "M", "p"),
+ (0xFF51, "M", "q"),
+ (0xFF52, "M", "r"),
+ (0xFF53, "M", "s"),
+ (0xFF54, "M", "t"),
+ (0xFF55, "M", "u"),
+ (0xFF56, "M", "v"),
+ (0xFF57, "M", "w"),
+ (0xFF58, "M", "x"),
+ (0xFF59, "M", "y"),
+ (0xFF5A, "M", "z"),
+ (0xFF5B, "M", "{"),
+ ]
+
+
+def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFF5C, "M", "|"),
+ (0xFF5D, "M", "}"),
+ (0xFF5E, "M", "~"),
+ (0xFF5F, "M", "⦅"),
+ (0xFF60, "M", "⦆"),
+ (0xFF61, "M", "."),
+ (0xFF62, "M", "「"),
+ (0xFF63, "M", "」"),
+ (0xFF64, "M", "、"),
+ (0xFF65, "M", "・"),
+ (0xFF66, "M", "ヲ"),
+ (0xFF67, "M", "ァ"),
+ (0xFF68, "M", "ィ"),
+ (0xFF69, "M", "ゥ"),
+ (0xFF6A, "M", "ェ"),
+ (0xFF6B, "M", "ォ"),
+ (0xFF6C, "M", "ャ"),
+ (0xFF6D, "M", "ュ"),
+ (0xFF6E, "M", "ョ"),
+ (0xFF6F, "M", "ッ"),
+ (0xFF70, "M", "ー"),
+ (0xFF71, "M", "ア"),
+ (0xFF72, "M", "イ"),
+ (0xFF73, "M", "ウ"),
+ (0xFF74, "M", "エ"),
+ (0xFF75, "M", "オ"),
+ (0xFF76, "M", "カ"),
+ (0xFF77, "M", "キ"),
+ (0xFF78, "M", "ク"),
+ (0xFF79, "M", "ケ"),
+ (0xFF7A, "M", "コ"),
+ (0xFF7B, "M", "サ"),
+ (0xFF7C, "M", "シ"),
+ (0xFF7D, "M", "ス"),
+ (0xFF7E, "M", "セ"),
+ (0xFF7F, "M", "ソ"),
+ (0xFF80, "M", "タ"),
+ (0xFF81, "M", "チ"),
+ (0xFF82, "M", "ツ"),
+ (0xFF83, "M", "テ"),
+ (0xFF84, "M", "ト"),
+ (0xFF85, "M", "ナ"),
+ (0xFF86, "M", "ニ"),
+ (0xFF87, "M", "ヌ"),
+ (0xFF88, "M", "ネ"),
+ (0xFF89, "M", "ノ"),
+ (0xFF8A, "M", "ハ"),
+ (0xFF8B, "M", "ヒ"),
+ (0xFF8C, "M", "フ"),
+ (0xFF8D, "M", "ヘ"),
+ (0xFF8E, "M", "ホ"),
+ (0xFF8F, "M", "マ"),
+ (0xFF90, "M", "ミ"),
+ (0xFF91, "M", "ム"),
+ (0xFF92, "M", "メ"),
+ (0xFF93, "M", "モ"),
+ (0xFF94, "M", "ヤ"),
+ (0xFF95, "M", "ユ"),
+ (0xFF96, "M", "ヨ"),
+ (0xFF97, "M", "ラ"),
+ (0xFF98, "M", "リ"),
+ (0xFF99, "M", "ル"),
+ (0xFF9A, "M", "レ"),
+ (0xFF9B, "M", "ロ"),
+ (0xFF9C, "M", "ワ"),
+ (0xFF9D, "M", "ン"),
+ (0xFF9E, "M", "゙"),
+ (0xFF9F, "M", "゚"),
+ (0xFFA0, "I"),
+ (0xFFA1, "M", "ᄀ"),
+ (0xFFA2, "M", "ᄁ"),
+ (0xFFA3, "M", "ᆪ"),
+ (0xFFA4, "M", "ᄂ"),
+ (0xFFA5, "M", "ᆬ"),
+ (0xFFA6, "M", "ᆭ"),
+ (0xFFA7, "M", "ᄃ"),
+ (0xFFA8, "M", "ᄄ"),
+ (0xFFA9, "M", "ᄅ"),
+ (0xFFAA, "M", "ᆰ"),
+ (0xFFAB, "M", "ᆱ"),
+ (0xFFAC, "M", "ᆲ"),
+ (0xFFAD, "M", "ᆳ"),
+ (0xFFAE, "M", "ᆴ"),
+ (0xFFAF, "M", "ᆵ"),
+ (0xFFB0, "M", "ᄚ"),
+ (0xFFB1, "M", "ᄆ"),
+ (0xFFB2, "M", "ᄇ"),
+ (0xFFB3, "M", "ᄈ"),
+ (0xFFB4, "M", "ᄡ"),
+ (0xFFB5, "M", "ᄉ"),
+ (0xFFB6, "M", "ᄊ"),
+ (0xFFB7, "M", "ᄋ"),
+ (0xFFB8, "M", "ᄌ"),
+ (0xFFB9, "M", "ᄍ"),
+ (0xFFBA, "M", "ᄎ"),
+ (0xFFBB, "M", "ᄏ"),
+ (0xFFBC, "M", "ᄐ"),
+ (0xFFBD, "M", "ᄑ"),
+ (0xFFBE, "M", "ᄒ"),
+ (0xFFBF, "X"),
+ ]
+
+
+def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0xFFC2, "M", "ᅡ"),
+ (0xFFC3, "M", "ᅢ"),
+ (0xFFC4, "M", "ᅣ"),
+ (0xFFC5, "M", "ᅤ"),
+ (0xFFC6, "M", "ᅥ"),
+ (0xFFC7, "M", "ᅦ"),
+ (0xFFC8, "X"),
+ (0xFFCA, "M", "ᅧ"),
+ (0xFFCB, "M", "ᅨ"),
+ (0xFFCC, "M", "ᅩ"),
+ (0xFFCD, "M", "ᅪ"),
+ (0xFFCE, "M", "ᅫ"),
+ (0xFFCF, "M", "ᅬ"),
+ (0xFFD0, "X"),
+ (0xFFD2, "M", "ᅭ"),
+ (0xFFD3, "M", "ᅮ"),
+ (0xFFD4, "M", "ᅯ"),
+ (0xFFD5, "M", "ᅰ"),
+ (0xFFD6, "M", "ᅱ"),
+ (0xFFD7, "M", "ᅲ"),
+ (0xFFD8, "X"),
+ (0xFFDA, "M", "ᅳ"),
+ (0xFFDB, "M", "ᅴ"),
+ (0xFFDC, "M", "ᅵ"),
+ (0xFFDD, "X"),
+ (0xFFE0, "M", "¢"),
+ (0xFFE1, "M", "£"),
+ (0xFFE2, "M", "¬"),
+ (0xFFE3, "M", " ̄"),
+ (0xFFE4, "M", "¦"),
+ (0xFFE5, "M", "¥"),
+ (0xFFE6, "M", "₩"),
+ (0xFFE7, "X"),
+ (0xFFE8, "M", "│"),
+ (0xFFE9, "M", "←"),
+ (0xFFEA, "M", "↑"),
+ (0xFFEB, "M", "→"),
+ (0xFFEC, "M", "↓"),
+ (0xFFED, "M", "■"),
+ (0xFFEE, "M", "○"),
+ (0xFFEF, "X"),
+ (0x10000, "V"),
+ (0x1000C, "X"),
+ (0x1000D, "V"),
+ (0x10027, "X"),
+ (0x10028, "V"),
+ (0x1003B, "X"),
+ (0x1003C, "V"),
+ (0x1003E, "X"),
+ (0x1003F, "V"),
+ (0x1004E, "X"),
+ (0x10050, "V"),
+ (0x1005E, "X"),
+ (0x10080, "V"),
+ (0x100FB, "X"),
+ (0x10100, "V"),
+ (0x10103, "X"),
+ (0x10107, "V"),
+ (0x10134, "X"),
+ (0x10137, "V"),
+ (0x1018F, "X"),
+ (0x10190, "V"),
+ (0x1019D, "X"),
+ (0x101A0, "V"),
+ (0x101A1, "X"),
+ (0x101D0, "V"),
+ (0x101FE, "X"),
+ (0x10280, "V"),
+ (0x1029D, "X"),
+ (0x102A0, "V"),
+ (0x102D1, "X"),
+ (0x102E0, "V"),
+ (0x102FC, "X"),
+ (0x10300, "V"),
+ (0x10324, "X"),
+ (0x1032D, "V"),
+ (0x1034B, "X"),
+ (0x10350, "V"),
+ (0x1037B, "X"),
+ (0x10380, "V"),
+ (0x1039E, "X"),
+ (0x1039F, "V"),
+ (0x103C4, "X"),
+ (0x103C8, "V"),
+ (0x103D6, "X"),
+ (0x10400, "M", "𐐨"),
+ (0x10401, "M", "𐐩"),
+ (0x10402, "M", "𐐪"),
+ (0x10403, "M", "𐐫"),
+ (0x10404, "M", "𐐬"),
+ (0x10405, "M", "𐐭"),
+ (0x10406, "M", "𐐮"),
+ (0x10407, "M", "𐐯"),
+ (0x10408, "M", "𐐰"),
+ (0x10409, "M", "𐐱"),
+ (0x1040A, "M", "𐐲"),
+ (0x1040B, "M", "𐐳"),
+ (0x1040C, "M", "𐐴"),
+ (0x1040D, "M", "𐐵"),
+ (0x1040E, "M", "𐐶"),
+ ]
+
+
+def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1040F, "M", "𐐷"),
+ (0x10410, "M", "𐐸"),
+ (0x10411, "M", "𐐹"),
+ (0x10412, "M", "𐐺"),
+ (0x10413, "M", "𐐻"),
+ (0x10414, "M", "𐐼"),
+ (0x10415, "M", "𐐽"),
+ (0x10416, "M", "𐐾"),
+ (0x10417, "M", "𐐿"),
+ (0x10418, "M", "𐑀"),
+ (0x10419, "M", "𐑁"),
+ (0x1041A, "M", "𐑂"),
+ (0x1041B, "M", "𐑃"),
+ (0x1041C, "M", "𐑄"),
+ (0x1041D, "M", "𐑅"),
+ (0x1041E, "M", "𐑆"),
+ (0x1041F, "M", "𐑇"),
+ (0x10420, "M", "𐑈"),
+ (0x10421, "M", "𐑉"),
+ (0x10422, "M", "𐑊"),
+ (0x10423, "M", "𐑋"),
+ (0x10424, "M", "𐑌"),
+ (0x10425, "M", "𐑍"),
+ (0x10426, "M", "𐑎"),
+ (0x10427, "M", "𐑏"),
+ (0x10428, "V"),
+ (0x1049E, "X"),
+ (0x104A0, "V"),
+ (0x104AA, "X"),
+ (0x104B0, "M", "𐓘"),
+ (0x104B1, "M", "𐓙"),
+ (0x104B2, "M", "𐓚"),
+ (0x104B3, "M", "𐓛"),
+ (0x104B4, "M", "𐓜"),
+ (0x104B5, "M", "𐓝"),
+ (0x104B6, "M", "𐓞"),
+ (0x104B7, "M", "𐓟"),
+ (0x104B8, "M", "𐓠"),
+ (0x104B9, "M", "𐓡"),
+ (0x104BA, "M", "𐓢"),
+ (0x104BB, "M", "𐓣"),
+ (0x104BC, "M", "𐓤"),
+ (0x104BD, "M", "𐓥"),
+ (0x104BE, "M", "𐓦"),
+ (0x104BF, "M", "𐓧"),
+ (0x104C0, "M", "𐓨"),
+ (0x104C1, "M", "𐓩"),
+ (0x104C2, "M", "𐓪"),
+ (0x104C3, "M", "𐓫"),
+ (0x104C4, "M", "𐓬"),
+ (0x104C5, "M", "𐓭"),
+ (0x104C6, "M", "𐓮"),
+ (0x104C7, "M", "𐓯"),
+ (0x104C8, "M", "𐓰"),
+ (0x104C9, "M", "𐓱"),
+ (0x104CA, "M", "𐓲"),
+ (0x104CB, "M", "𐓳"),
+ (0x104CC, "M", "𐓴"),
+ (0x104CD, "M", "𐓵"),
+ (0x104CE, "M", "𐓶"),
+ (0x104CF, "M", "𐓷"),
+ (0x104D0, "M", "𐓸"),
+ (0x104D1, "M", "𐓹"),
+ (0x104D2, "M", "𐓺"),
+ (0x104D3, "M", "𐓻"),
+ (0x104D4, "X"),
+ (0x104D8, "V"),
+ (0x104FC, "X"),
+ (0x10500, "V"),
+ (0x10528, "X"),
+ (0x10530, "V"),
+ (0x10564, "X"),
+ (0x1056F, "V"),
+ (0x10570, "M", "𐖗"),
+ (0x10571, "M", "𐖘"),
+ (0x10572, "M", "𐖙"),
+ (0x10573, "M", "𐖚"),
+ (0x10574, "M", "𐖛"),
+ (0x10575, "M", "𐖜"),
+ (0x10576, "M", "𐖝"),
+ (0x10577, "M", "𐖞"),
+ (0x10578, "M", "𐖟"),
+ (0x10579, "M", "𐖠"),
+ (0x1057A, "M", "𐖡"),
+ (0x1057B, "X"),
+ (0x1057C, "M", "𐖣"),
+ (0x1057D, "M", "𐖤"),
+ (0x1057E, "M", "𐖥"),
+ (0x1057F, "M", "𐖦"),
+ (0x10580, "M", "𐖧"),
+ (0x10581, "M", "𐖨"),
+ (0x10582, "M", "𐖩"),
+ (0x10583, "M", "𐖪"),
+ (0x10584, "M", "𐖫"),
+ (0x10585, "M", "𐖬"),
+ (0x10586, "M", "𐖭"),
+ (0x10587, "M", "𐖮"),
+ (0x10588, "M", "𐖯"),
+ (0x10589, "M", "𐖰"),
+ (0x1058A, "M", "𐖱"),
+ ]
+
+
+def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1058B, "X"),
+ (0x1058C, "M", "𐖳"),
+ (0x1058D, "M", "𐖴"),
+ (0x1058E, "M", "𐖵"),
+ (0x1058F, "M", "𐖶"),
+ (0x10590, "M", "𐖷"),
+ (0x10591, "M", "𐖸"),
+ (0x10592, "M", "𐖹"),
+ (0x10593, "X"),
+ (0x10594, "M", "𐖻"),
+ (0x10595, "M", "𐖼"),
+ (0x10596, "X"),
+ (0x10597, "V"),
+ (0x105A2, "X"),
+ (0x105A3, "V"),
+ (0x105B2, "X"),
+ (0x105B3, "V"),
+ (0x105BA, "X"),
+ (0x105BB, "V"),
+ (0x105BD, "X"),
+ (0x105C0, "V"),
+ (0x105F4, "X"),
+ (0x10600, "V"),
+ (0x10737, "X"),
+ (0x10740, "V"),
+ (0x10756, "X"),
+ (0x10760, "V"),
+ (0x10768, "X"),
+ (0x10780, "V"),
+ (0x10781, "M", "ː"),
+ (0x10782, "M", "ˑ"),
+ (0x10783, "M", "æ"),
+ (0x10784, "M", "ʙ"),
+ (0x10785, "M", "ɓ"),
+ (0x10786, "X"),
+ (0x10787, "M", "ʣ"),
+ (0x10788, "M", "ꭦ"),
+ (0x10789, "M", "ʥ"),
+ (0x1078A, "M", "ʤ"),
+ (0x1078B, "M", "ɖ"),
+ (0x1078C, "M", "ɗ"),
+ (0x1078D, "M", "ᶑ"),
+ (0x1078E, "M", "ɘ"),
+ (0x1078F, "M", "ɞ"),
+ (0x10790, "M", "ʩ"),
+ (0x10791, "M", "ɤ"),
+ (0x10792, "M", "ɢ"),
+ (0x10793, "M", "ɠ"),
+ (0x10794, "M", "ʛ"),
+ (0x10795, "M", "ħ"),
+ (0x10796, "M", "ʜ"),
+ (0x10797, "M", "ɧ"),
+ (0x10798, "M", "ʄ"),
+ (0x10799, "M", "ʪ"),
+ (0x1079A, "M", "ʫ"),
+ (0x1079B, "M", "ɬ"),
+ (0x1079C, "M", "𝼄"),
+ (0x1079D, "M", "ꞎ"),
+ (0x1079E, "M", "ɮ"),
+ (0x1079F, "M", "𝼅"),
+ (0x107A0, "M", "ʎ"),
+ (0x107A1, "M", "𝼆"),
+ (0x107A2, "M", "ø"),
+ (0x107A3, "M", "ɶ"),
+ (0x107A4, "M", "ɷ"),
+ (0x107A5, "M", "q"),
+ (0x107A6, "M", "ɺ"),
+ (0x107A7, "M", "𝼈"),
+ (0x107A8, "M", "ɽ"),
+ (0x107A9, "M", "ɾ"),
+ (0x107AA, "M", "ʀ"),
+ (0x107AB, "M", "ʨ"),
+ (0x107AC, "M", "ʦ"),
+ (0x107AD, "M", "ꭧ"),
+ (0x107AE, "M", "ʧ"),
+ (0x107AF, "M", "ʈ"),
+ (0x107B0, "M", "ⱱ"),
+ (0x107B1, "X"),
+ (0x107B2, "M", "ʏ"),
+ (0x107B3, "M", "ʡ"),
+ (0x107B4, "M", "ʢ"),
+ (0x107B5, "M", "ʘ"),
+ (0x107B6, "M", "ǀ"),
+ (0x107B7, "M", "ǁ"),
+ (0x107B8, "M", "ǂ"),
+ (0x107B9, "M", "𝼊"),
+ (0x107BA, "M", "𝼞"),
+ (0x107BB, "X"),
+ (0x10800, "V"),
+ (0x10806, "X"),
+ (0x10808, "V"),
+ (0x10809, "X"),
+ (0x1080A, "V"),
+ (0x10836, "X"),
+ (0x10837, "V"),
+ (0x10839, "X"),
+ (0x1083C, "V"),
+ (0x1083D, "X"),
+ (0x1083F, "V"),
+ (0x10856, "X"),
+ ]
+
+
+def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x10857, "V"),
+ (0x1089F, "X"),
+ (0x108A7, "V"),
+ (0x108B0, "X"),
+ (0x108E0, "V"),
+ (0x108F3, "X"),
+ (0x108F4, "V"),
+ (0x108F6, "X"),
+ (0x108FB, "V"),
+ (0x1091C, "X"),
+ (0x1091F, "V"),
+ (0x1093A, "X"),
+ (0x1093F, "V"),
+ (0x10940, "X"),
+ (0x10980, "V"),
+ (0x109B8, "X"),
+ (0x109BC, "V"),
+ (0x109D0, "X"),
+ (0x109D2, "V"),
+ (0x10A04, "X"),
+ (0x10A05, "V"),
+ (0x10A07, "X"),
+ (0x10A0C, "V"),
+ (0x10A14, "X"),
+ (0x10A15, "V"),
+ (0x10A18, "X"),
+ (0x10A19, "V"),
+ (0x10A36, "X"),
+ (0x10A38, "V"),
+ (0x10A3B, "X"),
+ (0x10A3F, "V"),
+ (0x10A49, "X"),
+ (0x10A50, "V"),
+ (0x10A59, "X"),
+ (0x10A60, "V"),
+ (0x10AA0, "X"),
+ (0x10AC0, "V"),
+ (0x10AE7, "X"),
+ (0x10AEB, "V"),
+ (0x10AF7, "X"),
+ (0x10B00, "V"),
+ (0x10B36, "X"),
+ (0x10B39, "V"),
+ (0x10B56, "X"),
+ (0x10B58, "V"),
+ (0x10B73, "X"),
+ (0x10B78, "V"),
+ (0x10B92, "X"),
+ (0x10B99, "V"),
+ (0x10B9D, "X"),
+ (0x10BA9, "V"),
+ (0x10BB0, "X"),
+ (0x10C00, "V"),
+ (0x10C49, "X"),
+ (0x10C80, "M", "𐳀"),
+ (0x10C81, "M", "𐳁"),
+ (0x10C82, "M", "𐳂"),
+ (0x10C83, "M", "𐳃"),
+ (0x10C84, "M", "𐳄"),
+ (0x10C85, "M", "𐳅"),
+ (0x10C86, "M", "𐳆"),
+ (0x10C87, "M", "𐳇"),
+ (0x10C88, "M", "𐳈"),
+ (0x10C89, "M", "𐳉"),
+ (0x10C8A, "M", "𐳊"),
+ (0x10C8B, "M", "𐳋"),
+ (0x10C8C, "M", "𐳌"),
+ (0x10C8D, "M", "𐳍"),
+ (0x10C8E, "M", "𐳎"),
+ (0x10C8F, "M", "𐳏"),
+ (0x10C90, "M", "𐳐"),
+ (0x10C91, "M", "𐳑"),
+ (0x10C92, "M", "𐳒"),
+ (0x10C93, "M", "𐳓"),
+ (0x10C94, "M", "𐳔"),
+ (0x10C95, "M", "𐳕"),
+ (0x10C96, "M", "𐳖"),
+ (0x10C97, "M", "𐳗"),
+ (0x10C98, "M", "𐳘"),
+ (0x10C99, "M", "𐳙"),
+ (0x10C9A, "M", "𐳚"),
+ (0x10C9B, "M", "𐳛"),
+ (0x10C9C, "M", "𐳜"),
+ (0x10C9D, "M", "𐳝"),
+ (0x10C9E, "M", "𐳞"),
+ (0x10C9F, "M", "𐳟"),
+ (0x10CA0, "M", "𐳠"),
+ (0x10CA1, "M", "𐳡"),
+ (0x10CA2, "M", "𐳢"),
+ (0x10CA3, "M", "𐳣"),
+ (0x10CA4, "M", "𐳤"),
+ (0x10CA5, "M", "𐳥"),
+ (0x10CA6, "M", "𐳦"),
+ (0x10CA7, "M", "𐳧"),
+ (0x10CA8, "M", "𐳨"),
+ (0x10CA9, "M", "𐳩"),
+ (0x10CAA, "M", "𐳪"),
+ (0x10CAB, "M", "𐳫"),
+ (0x10CAC, "M", "𐳬"),
+ (0x10CAD, "M", "𐳭"),
+ ]
+
+
+def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x10CAE, "M", "𐳮"),
+ (0x10CAF, "M", "𐳯"),
+ (0x10CB0, "M", "𐳰"),
+ (0x10CB1, "M", "𐳱"),
+ (0x10CB2, "M", "𐳲"),
+ (0x10CB3, "X"),
+ (0x10CC0, "V"),
+ (0x10CF3, "X"),
+ (0x10CFA, "V"),
+ (0x10D28, "X"),
+ (0x10D30, "V"),
+ (0x10D3A, "X"),
+ (0x10D40, "V"),
+ (0x10D50, "M", ""),
+ (0x10D51, "M", ""),
+ (0x10D52, "M", ""),
+ (0x10D53, "M", ""),
+ (0x10D54, "M", ""),
+ (0x10D55, "M", ""),
+ (0x10D56, "M", ""),
+ (0x10D57, "M", ""),
+ (0x10D58, "M", ""),
+ (0x10D59, "M", ""),
+ (0x10D5A, "M", ""),
+ (0x10D5B, "M", ""),
+ (0x10D5C, "M", ""),
+ (0x10D5D, "M", ""),
+ (0x10D5E, "M", ""),
+ (0x10D5F, "M", ""),
+ (0x10D60, "M", ""),
+ (0x10D61, "M", ""),
+ (0x10D62, "M", ""),
+ (0x10D63, "M", ""),
+ (0x10D64, "M", ""),
+ (0x10D65, "M", ""),
+ (0x10D66, "X"),
+ (0x10D69, "V"),
+ (0x10D86, "X"),
+ (0x10D8E, "V"),
+ (0x10D90, "X"),
+ (0x10E60, "V"),
+ (0x10E7F, "X"),
+ (0x10E80, "V"),
+ (0x10EAA, "X"),
+ (0x10EAB, "V"),
+ (0x10EAE, "X"),
+ (0x10EB0, "V"),
+ (0x10EB2, "X"),
+ (0x10EC2, "V"),
+ (0x10EC5, "X"),
+ (0x10EFC, "V"),
+ (0x10F28, "X"),
+ (0x10F30, "V"),
+ (0x10F5A, "X"),
+ (0x10F70, "V"),
+ (0x10F8A, "X"),
+ (0x10FB0, "V"),
+ (0x10FCC, "X"),
+ (0x10FE0, "V"),
+ (0x10FF7, "X"),
+ (0x11000, "V"),
+ (0x1104E, "X"),
+ (0x11052, "V"),
+ (0x11076, "X"),
+ (0x1107F, "V"),
+ (0x110BD, "X"),
+ (0x110BE, "V"),
+ (0x110C3, "X"),
+ (0x110D0, "V"),
+ (0x110E9, "X"),
+ (0x110F0, "V"),
+ (0x110FA, "X"),
+ (0x11100, "V"),
+ (0x11135, "X"),
+ (0x11136, "V"),
+ (0x11148, "X"),
+ (0x11150, "V"),
+ (0x11177, "X"),
+ (0x11180, "V"),
+ (0x111E0, "X"),
+ (0x111E1, "V"),
+ (0x111F5, "X"),
+ (0x11200, "V"),
+ (0x11212, "X"),
+ (0x11213, "V"),
+ (0x11242, "X"),
+ (0x11280, "V"),
+ (0x11287, "X"),
+ (0x11288, "V"),
+ (0x11289, "X"),
+ (0x1128A, "V"),
+ (0x1128E, "X"),
+ (0x1128F, "V"),
+ (0x1129E, "X"),
+ (0x1129F, "V"),
+ (0x112AA, "X"),
+ (0x112B0, "V"),
+ (0x112EB, "X"),
+ (0x112F0, "V"),
+ (0x112FA, "X"),
+ ]
+
+
+def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x11300, "V"),
+ (0x11304, "X"),
+ (0x11305, "V"),
+ (0x1130D, "X"),
+ (0x1130F, "V"),
+ (0x11311, "X"),
+ (0x11313, "V"),
+ (0x11329, "X"),
+ (0x1132A, "V"),
+ (0x11331, "X"),
+ (0x11332, "V"),
+ (0x11334, "X"),
+ (0x11335, "V"),
+ (0x1133A, "X"),
+ (0x1133B, "V"),
+ (0x11345, "X"),
+ (0x11347, "V"),
+ (0x11349, "X"),
+ (0x1134B, "V"),
+ (0x1134E, "X"),
+ (0x11350, "V"),
+ (0x11351, "X"),
+ (0x11357, "V"),
+ (0x11358, "X"),
+ (0x1135D, "V"),
+ (0x11364, "X"),
+ (0x11366, "V"),
+ (0x1136D, "X"),
+ (0x11370, "V"),
+ (0x11375, "X"),
+ (0x11380, "V"),
+ (0x1138A, "X"),
+ (0x1138B, "V"),
+ (0x1138C, "X"),
+ (0x1138E, "V"),
+ (0x1138F, "X"),
+ (0x11390, "V"),
+ (0x113B6, "X"),
+ (0x113B7, "V"),
+ (0x113C1, "X"),
+ (0x113C2, "V"),
+ (0x113C3, "X"),
+ (0x113C5, "V"),
+ (0x113C6, "X"),
+ (0x113C7, "V"),
+ (0x113CB, "X"),
+ (0x113CC, "V"),
+ (0x113D6, "X"),
+ (0x113D7, "V"),
+ (0x113D9, "X"),
+ (0x113E1, "V"),
+ (0x113E3, "X"),
+ (0x11400, "V"),
+ (0x1145C, "X"),
+ (0x1145D, "V"),
+ (0x11462, "X"),
+ (0x11480, "V"),
+ (0x114C8, "X"),
+ (0x114D0, "V"),
+ (0x114DA, "X"),
+ (0x11580, "V"),
+ (0x115B6, "X"),
+ (0x115B8, "V"),
+ (0x115DE, "X"),
+ (0x11600, "V"),
+ (0x11645, "X"),
+ (0x11650, "V"),
+ (0x1165A, "X"),
+ (0x11660, "V"),
+ (0x1166D, "X"),
+ (0x11680, "V"),
+ (0x116BA, "X"),
+ (0x116C0, "V"),
+ (0x116CA, "X"),
+ (0x116D0, "V"),
+ (0x116E4, "X"),
+ (0x11700, "V"),
+ (0x1171B, "X"),
+ (0x1171D, "V"),
+ (0x1172C, "X"),
+ (0x11730, "V"),
+ (0x11747, "X"),
+ (0x11800, "V"),
+ (0x1183C, "X"),
+ (0x118A0, "M", "𑣀"),
+ (0x118A1, "M", "𑣁"),
+ (0x118A2, "M", "𑣂"),
+ (0x118A3, "M", "𑣃"),
+ (0x118A4, "M", "𑣄"),
+ (0x118A5, "M", "𑣅"),
+ (0x118A6, "M", "𑣆"),
+ (0x118A7, "M", "𑣇"),
+ (0x118A8, "M", "𑣈"),
+ (0x118A9, "M", "𑣉"),
+ (0x118AA, "M", "𑣊"),
+ (0x118AB, "M", "𑣋"),
+ (0x118AC, "M", "𑣌"),
+ (0x118AD, "M", "𑣍"),
+ (0x118AE, "M", "𑣎"),
+ (0x118AF, "M", "𑣏"),
+ ]
+
+
+def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x118B0, "M", "𑣐"),
+ (0x118B1, "M", "𑣑"),
+ (0x118B2, "M", "𑣒"),
+ (0x118B3, "M", "𑣓"),
+ (0x118B4, "M", "𑣔"),
+ (0x118B5, "M", "𑣕"),
+ (0x118B6, "M", "𑣖"),
+ (0x118B7, "M", "𑣗"),
+ (0x118B8, "M", "𑣘"),
+ (0x118B9, "M", "𑣙"),
+ (0x118BA, "M", "𑣚"),
+ (0x118BB, "M", "𑣛"),
+ (0x118BC, "M", "𑣜"),
+ (0x118BD, "M", "𑣝"),
+ (0x118BE, "M", "𑣞"),
+ (0x118BF, "M", "𑣟"),
+ (0x118C0, "V"),
+ (0x118F3, "X"),
+ (0x118FF, "V"),
+ (0x11907, "X"),
+ (0x11909, "V"),
+ (0x1190A, "X"),
+ (0x1190C, "V"),
+ (0x11914, "X"),
+ (0x11915, "V"),
+ (0x11917, "X"),
+ (0x11918, "V"),
+ (0x11936, "X"),
+ (0x11937, "V"),
+ (0x11939, "X"),
+ (0x1193B, "V"),
+ (0x11947, "X"),
+ (0x11950, "V"),
+ (0x1195A, "X"),
+ (0x119A0, "V"),
+ (0x119A8, "X"),
+ (0x119AA, "V"),
+ (0x119D8, "X"),
+ (0x119DA, "V"),
+ (0x119E5, "X"),
+ (0x11A00, "V"),
+ (0x11A48, "X"),
+ (0x11A50, "V"),
+ (0x11AA3, "X"),
+ (0x11AB0, "V"),
+ (0x11AF9, "X"),
+ (0x11B00, "V"),
+ (0x11B0A, "X"),
+ (0x11BC0, "V"),
+ (0x11BE2, "X"),
+ (0x11BF0, "V"),
+ (0x11BFA, "X"),
+ (0x11C00, "V"),
+ (0x11C09, "X"),
+ (0x11C0A, "V"),
+ (0x11C37, "X"),
+ (0x11C38, "V"),
+ (0x11C46, "X"),
+ (0x11C50, "V"),
+ (0x11C6D, "X"),
+ (0x11C70, "V"),
+ (0x11C90, "X"),
+ (0x11C92, "V"),
+ (0x11CA8, "X"),
+ (0x11CA9, "V"),
+ (0x11CB7, "X"),
+ (0x11D00, "V"),
+ (0x11D07, "X"),
+ (0x11D08, "V"),
+ (0x11D0A, "X"),
+ (0x11D0B, "V"),
+ (0x11D37, "X"),
+ (0x11D3A, "V"),
+ (0x11D3B, "X"),
+ (0x11D3C, "V"),
+ (0x11D3E, "X"),
+ (0x11D3F, "V"),
+ (0x11D48, "X"),
+ (0x11D50, "V"),
+ (0x11D5A, "X"),
+ (0x11D60, "V"),
+ (0x11D66, "X"),
+ (0x11D67, "V"),
+ (0x11D69, "X"),
+ (0x11D6A, "V"),
+ (0x11D8F, "X"),
+ (0x11D90, "V"),
+ (0x11D92, "X"),
+ (0x11D93, "V"),
+ (0x11D99, "X"),
+ (0x11DA0, "V"),
+ (0x11DAA, "X"),
+ (0x11EE0, "V"),
+ (0x11EF9, "X"),
+ (0x11F00, "V"),
+ (0x11F11, "X"),
+ (0x11F12, "V"),
+ (0x11F3B, "X"),
+ (0x11F3E, "V"),
+ (0x11F5B, "X"),
+ ]
+
+
+def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x11FB0, "V"),
+ (0x11FB1, "X"),
+ (0x11FC0, "V"),
+ (0x11FF2, "X"),
+ (0x11FFF, "V"),
+ (0x1239A, "X"),
+ (0x12400, "V"),
+ (0x1246F, "X"),
+ (0x12470, "V"),
+ (0x12475, "X"),
+ (0x12480, "V"),
+ (0x12544, "X"),
+ (0x12F90, "V"),
+ (0x12FF3, "X"),
+ (0x13000, "V"),
+ (0x13430, "X"),
+ (0x13440, "V"),
+ (0x13456, "X"),
+ (0x13460, "V"),
+ (0x143FB, "X"),
+ (0x14400, "V"),
+ (0x14647, "X"),
+ (0x16100, "V"),
+ (0x1613A, "X"),
+ (0x16800, "V"),
+ (0x16A39, "X"),
+ (0x16A40, "V"),
+ (0x16A5F, "X"),
+ (0x16A60, "V"),
+ (0x16A6A, "X"),
+ (0x16A6E, "V"),
+ (0x16ABF, "X"),
+ (0x16AC0, "V"),
+ (0x16ACA, "X"),
+ (0x16AD0, "V"),
+ (0x16AEE, "X"),
+ (0x16AF0, "V"),
+ (0x16AF6, "X"),
+ (0x16B00, "V"),
+ (0x16B46, "X"),
+ (0x16B50, "V"),
+ (0x16B5A, "X"),
+ (0x16B5B, "V"),
+ (0x16B62, "X"),
+ (0x16B63, "V"),
+ (0x16B78, "X"),
+ (0x16B7D, "V"),
+ (0x16B90, "X"),
+ (0x16D40, "V"),
+ (0x16D7A, "X"),
+ (0x16E40, "M", "𖹠"),
+ (0x16E41, "M", "𖹡"),
+ (0x16E42, "M", "𖹢"),
+ (0x16E43, "M", "𖹣"),
+ (0x16E44, "M", "𖹤"),
+ (0x16E45, "M", "𖹥"),
+ (0x16E46, "M", "𖹦"),
+ (0x16E47, "M", "𖹧"),
+ (0x16E48, "M", "𖹨"),
+ (0x16E49, "M", "𖹩"),
+ (0x16E4A, "M", "𖹪"),
+ (0x16E4B, "M", "𖹫"),
+ (0x16E4C, "M", "𖹬"),
+ (0x16E4D, "M", "𖹭"),
+ (0x16E4E, "M", "𖹮"),
+ (0x16E4F, "M", "𖹯"),
+ (0x16E50, "M", "𖹰"),
+ (0x16E51, "M", "𖹱"),
+ (0x16E52, "M", "𖹲"),
+ (0x16E53, "M", "𖹳"),
+ (0x16E54, "M", "𖹴"),
+ (0x16E55, "M", "𖹵"),
+ (0x16E56, "M", "𖹶"),
+ (0x16E57, "M", "𖹷"),
+ (0x16E58, "M", "𖹸"),
+ (0x16E59, "M", "𖹹"),
+ (0x16E5A, "M", "𖹺"),
+ (0x16E5B, "M", "𖹻"),
+ (0x16E5C, "M", "𖹼"),
+ (0x16E5D, "M", "𖹽"),
+ (0x16E5E, "M", "𖹾"),
+ (0x16E5F, "M", "𖹿"),
+ (0x16E60, "V"),
+ (0x16E9B, "X"),
+ (0x16F00, "V"),
+ (0x16F4B, "X"),
+ (0x16F4F, "V"),
+ (0x16F88, "X"),
+ (0x16F8F, "V"),
+ (0x16FA0, "X"),
+ (0x16FE0, "V"),
+ (0x16FE5, "X"),
+ (0x16FF0, "V"),
+ (0x16FF2, "X"),
+ (0x17000, "V"),
+ (0x187F8, "X"),
+ (0x18800, "V"),
+ (0x18CD6, "X"),
+ (0x18CFF, "V"),
+ (0x18D09, "X"),
+ ]
+
+
+def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1AFF0, "V"),
+ (0x1AFF4, "X"),
+ (0x1AFF5, "V"),
+ (0x1AFFC, "X"),
+ (0x1AFFD, "V"),
+ (0x1AFFF, "X"),
+ (0x1B000, "V"),
+ (0x1B123, "X"),
+ (0x1B132, "V"),
+ (0x1B133, "X"),
+ (0x1B150, "V"),
+ (0x1B153, "X"),
+ (0x1B155, "V"),
+ (0x1B156, "X"),
+ (0x1B164, "V"),
+ (0x1B168, "X"),
+ (0x1B170, "V"),
+ (0x1B2FC, "X"),
+ (0x1BC00, "V"),
+ (0x1BC6B, "X"),
+ (0x1BC70, "V"),
+ (0x1BC7D, "X"),
+ (0x1BC80, "V"),
+ (0x1BC89, "X"),
+ (0x1BC90, "V"),
+ (0x1BC9A, "X"),
+ (0x1BC9C, "V"),
+ (0x1BCA0, "I"),
+ (0x1BCA4, "X"),
+ (0x1CC00, "V"),
+ (0x1CCD6, "M", "a"),
+ (0x1CCD7, "M", "b"),
+ (0x1CCD8, "M", "c"),
+ (0x1CCD9, "M", "d"),
+ (0x1CCDA, "M", "e"),
+ (0x1CCDB, "M", "f"),
+ (0x1CCDC, "M", "g"),
+ (0x1CCDD, "M", "h"),
+ (0x1CCDE, "M", "i"),
+ (0x1CCDF, "M", "j"),
+ (0x1CCE0, "M", "k"),
+ (0x1CCE1, "M", "l"),
+ (0x1CCE2, "M", "m"),
+ (0x1CCE3, "M", "n"),
+ (0x1CCE4, "M", "o"),
+ (0x1CCE5, "M", "p"),
+ (0x1CCE6, "M", "q"),
+ (0x1CCE7, "M", "r"),
+ (0x1CCE8, "M", "s"),
+ (0x1CCE9, "M", "t"),
+ (0x1CCEA, "M", "u"),
+ (0x1CCEB, "M", "v"),
+ (0x1CCEC, "M", "w"),
+ (0x1CCED, "M", "x"),
+ (0x1CCEE, "M", "y"),
+ (0x1CCEF, "M", "z"),
+ (0x1CCF0, "M", "0"),
+ (0x1CCF1, "M", "1"),
+ (0x1CCF2, "M", "2"),
+ (0x1CCF3, "M", "3"),
+ (0x1CCF4, "M", "4"),
+ (0x1CCF5, "M", "5"),
+ (0x1CCF6, "M", "6"),
+ (0x1CCF7, "M", "7"),
+ (0x1CCF8, "M", "8"),
+ (0x1CCF9, "M", "9"),
+ (0x1CCFA, "X"),
+ (0x1CD00, "V"),
+ (0x1CEB4, "X"),
+ (0x1CF00, "V"),
+ (0x1CF2E, "X"),
+ (0x1CF30, "V"),
+ (0x1CF47, "X"),
+ (0x1CF50, "V"),
+ (0x1CFC4, "X"),
+ (0x1D000, "V"),
+ (0x1D0F6, "X"),
+ (0x1D100, "V"),
+ (0x1D127, "X"),
+ (0x1D129, "V"),
+ (0x1D15E, "M", "𝅗𝅥"),
+ (0x1D15F, "M", "𝅘𝅥"),
+ (0x1D160, "M", "𝅘𝅥𝅮"),
+ (0x1D161, "M", "𝅘𝅥𝅯"),
+ (0x1D162, "M", "𝅘𝅥𝅰"),
+ (0x1D163, "M", "𝅘𝅥𝅱"),
+ (0x1D164, "M", "𝅘𝅥𝅲"),
+ (0x1D165, "V"),
+ (0x1D173, "I"),
+ (0x1D17B, "V"),
+ (0x1D1BB, "M", "𝆹𝅥"),
+ (0x1D1BC, "M", "𝆺𝅥"),
+ (0x1D1BD, "M", "𝆹𝅥𝅮"),
+ (0x1D1BE, "M", "𝆺𝅥𝅮"),
+ (0x1D1BF, "M", "𝆹𝅥𝅯"),
+ (0x1D1C0, "M", "𝆺𝅥𝅯"),
+ (0x1D1C1, "V"),
+ (0x1D1EB, "X"),
+ (0x1D200, "V"),
+ (0x1D246, "X"),
+ ]
+
+
+def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D2C0, "V"),
+ (0x1D2D4, "X"),
+ (0x1D2E0, "V"),
+ (0x1D2F4, "X"),
+ (0x1D300, "V"),
+ (0x1D357, "X"),
+ (0x1D360, "V"),
+ (0x1D379, "X"),
+ (0x1D400, "M", "a"),
+ (0x1D401, "M", "b"),
+ (0x1D402, "M", "c"),
+ (0x1D403, "M", "d"),
+ (0x1D404, "M", "e"),
+ (0x1D405, "M", "f"),
+ (0x1D406, "M", "g"),
+ (0x1D407, "M", "h"),
+ (0x1D408, "M", "i"),
+ (0x1D409, "M", "j"),
+ (0x1D40A, "M", "k"),
+ (0x1D40B, "M", "l"),
+ (0x1D40C, "M", "m"),
+ (0x1D40D, "M", "n"),
+ (0x1D40E, "M", "o"),
+ (0x1D40F, "M", "p"),
+ (0x1D410, "M", "q"),
+ (0x1D411, "M", "r"),
+ (0x1D412, "M", "s"),
+ (0x1D413, "M", "t"),
+ (0x1D414, "M", "u"),
+ (0x1D415, "M", "v"),
+ (0x1D416, "M", "w"),
+ (0x1D417, "M", "x"),
+ (0x1D418, "M", "y"),
+ (0x1D419, "M", "z"),
+ (0x1D41A, "M", "a"),
+ (0x1D41B, "M", "b"),
+ (0x1D41C, "M", "c"),
+ (0x1D41D, "M", "d"),
+ (0x1D41E, "M", "e"),
+ (0x1D41F, "M", "f"),
+ (0x1D420, "M", "g"),
+ (0x1D421, "M", "h"),
+ (0x1D422, "M", "i"),
+ (0x1D423, "M", "j"),
+ (0x1D424, "M", "k"),
+ (0x1D425, "M", "l"),
+ (0x1D426, "M", "m"),
+ (0x1D427, "M", "n"),
+ (0x1D428, "M", "o"),
+ (0x1D429, "M", "p"),
+ (0x1D42A, "M", "q"),
+ (0x1D42B, "M", "r"),
+ (0x1D42C, "M", "s"),
+ (0x1D42D, "M", "t"),
+ (0x1D42E, "M", "u"),
+ (0x1D42F, "M", "v"),
+ (0x1D430, "M", "w"),
+ (0x1D431, "M", "x"),
+ (0x1D432, "M", "y"),
+ (0x1D433, "M", "z"),
+ (0x1D434, "M", "a"),
+ (0x1D435, "M", "b"),
+ (0x1D436, "M", "c"),
+ (0x1D437, "M", "d"),
+ (0x1D438, "M", "e"),
+ (0x1D439, "M", "f"),
+ (0x1D43A, "M", "g"),
+ (0x1D43B, "M", "h"),
+ (0x1D43C, "M", "i"),
+ (0x1D43D, "M", "j"),
+ (0x1D43E, "M", "k"),
+ (0x1D43F, "M", "l"),
+ (0x1D440, "M", "m"),
+ (0x1D441, "M", "n"),
+ (0x1D442, "M", "o"),
+ (0x1D443, "M", "p"),
+ (0x1D444, "M", "q"),
+ (0x1D445, "M", "r"),
+ (0x1D446, "M", "s"),
+ (0x1D447, "M", "t"),
+ (0x1D448, "M", "u"),
+ (0x1D449, "M", "v"),
+ (0x1D44A, "M", "w"),
+ (0x1D44B, "M", "x"),
+ (0x1D44C, "M", "y"),
+ (0x1D44D, "M", "z"),
+ (0x1D44E, "M", "a"),
+ (0x1D44F, "M", "b"),
+ (0x1D450, "M", "c"),
+ (0x1D451, "M", "d"),
+ (0x1D452, "M", "e"),
+ (0x1D453, "M", "f"),
+ (0x1D454, "M", "g"),
+ (0x1D455, "X"),
+ (0x1D456, "M", "i"),
+ (0x1D457, "M", "j"),
+ (0x1D458, "M", "k"),
+ (0x1D459, "M", "l"),
+ (0x1D45A, "M", "m"),
+ (0x1D45B, "M", "n"),
+ ]
+
+
+def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D45C, "M", "o"),
+ (0x1D45D, "M", "p"),
+ (0x1D45E, "M", "q"),
+ (0x1D45F, "M", "r"),
+ (0x1D460, "M", "s"),
+ (0x1D461, "M", "t"),
+ (0x1D462, "M", "u"),
+ (0x1D463, "M", "v"),
+ (0x1D464, "M", "w"),
+ (0x1D465, "M", "x"),
+ (0x1D466, "M", "y"),
+ (0x1D467, "M", "z"),
+ (0x1D468, "M", "a"),
+ (0x1D469, "M", "b"),
+ (0x1D46A, "M", "c"),
+ (0x1D46B, "M", "d"),
+ (0x1D46C, "M", "e"),
+ (0x1D46D, "M", "f"),
+ (0x1D46E, "M", "g"),
+ (0x1D46F, "M", "h"),
+ (0x1D470, "M", "i"),
+ (0x1D471, "M", "j"),
+ (0x1D472, "M", "k"),
+ (0x1D473, "M", "l"),
+ (0x1D474, "M", "m"),
+ (0x1D475, "M", "n"),
+ (0x1D476, "M", "o"),
+ (0x1D477, "M", "p"),
+ (0x1D478, "M", "q"),
+ (0x1D479, "M", "r"),
+ (0x1D47A, "M", "s"),
+ (0x1D47B, "M", "t"),
+ (0x1D47C, "M", "u"),
+ (0x1D47D, "M", "v"),
+ (0x1D47E, "M", "w"),
+ (0x1D47F, "M", "x"),
+ (0x1D480, "M", "y"),
+ (0x1D481, "M", "z"),
+ (0x1D482, "M", "a"),
+ (0x1D483, "M", "b"),
+ (0x1D484, "M", "c"),
+ (0x1D485, "M", "d"),
+ (0x1D486, "M", "e"),
+ (0x1D487, "M", "f"),
+ (0x1D488, "M", "g"),
+ (0x1D489, "M", "h"),
+ (0x1D48A, "M", "i"),
+ (0x1D48B, "M", "j"),
+ (0x1D48C, "M", "k"),
+ (0x1D48D, "M", "l"),
+ (0x1D48E, "M", "m"),
+ (0x1D48F, "M", "n"),
+ (0x1D490, "M", "o"),
+ (0x1D491, "M", "p"),
+ (0x1D492, "M", "q"),
+ (0x1D493, "M", "r"),
+ (0x1D494, "M", "s"),
+ (0x1D495, "M", "t"),
+ (0x1D496, "M", "u"),
+ (0x1D497, "M", "v"),
+ (0x1D498, "M", "w"),
+ (0x1D499, "M", "x"),
+ (0x1D49A, "M", "y"),
+ (0x1D49B, "M", "z"),
+ (0x1D49C, "M", "a"),
+ (0x1D49D, "X"),
+ (0x1D49E, "M", "c"),
+ (0x1D49F, "M", "d"),
+ (0x1D4A0, "X"),
+ (0x1D4A2, "M", "g"),
+ (0x1D4A3, "X"),
+ (0x1D4A5, "M", "j"),
+ (0x1D4A6, "M", "k"),
+ (0x1D4A7, "X"),
+ (0x1D4A9, "M", "n"),
+ (0x1D4AA, "M", "o"),
+ (0x1D4AB, "M", "p"),
+ (0x1D4AC, "M", "q"),
+ (0x1D4AD, "X"),
+ (0x1D4AE, "M", "s"),
+ (0x1D4AF, "M", "t"),
+ (0x1D4B0, "M", "u"),
+ (0x1D4B1, "M", "v"),
+ (0x1D4B2, "M", "w"),
+ (0x1D4B3, "M", "x"),
+ (0x1D4B4, "M", "y"),
+ (0x1D4B5, "M", "z"),
+ (0x1D4B6, "M", "a"),
+ (0x1D4B7, "M", "b"),
+ (0x1D4B8, "M", "c"),
+ (0x1D4B9, "M", "d"),
+ (0x1D4BA, "X"),
+ (0x1D4BB, "M", "f"),
+ (0x1D4BC, "X"),
+ (0x1D4BD, "M", "h"),
+ (0x1D4BE, "M", "i"),
+ (0x1D4BF, "M", "j"),
+ (0x1D4C0, "M", "k"),
+ (0x1D4C1, "M", "l"),
+ (0x1D4C2, "M", "m"),
+ ]
+
+
+def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D4C3, "M", "n"),
+ (0x1D4C4, "X"),
+ (0x1D4C5, "M", "p"),
+ (0x1D4C6, "M", "q"),
+ (0x1D4C7, "M", "r"),
+ (0x1D4C8, "M", "s"),
+ (0x1D4C9, "M", "t"),
+ (0x1D4CA, "M", "u"),
+ (0x1D4CB, "M", "v"),
+ (0x1D4CC, "M", "w"),
+ (0x1D4CD, "M", "x"),
+ (0x1D4CE, "M", "y"),
+ (0x1D4CF, "M", "z"),
+ (0x1D4D0, "M", "a"),
+ (0x1D4D1, "M", "b"),
+ (0x1D4D2, "M", "c"),
+ (0x1D4D3, "M", "d"),
+ (0x1D4D4, "M", "e"),
+ (0x1D4D5, "M", "f"),
+ (0x1D4D6, "M", "g"),
+ (0x1D4D7, "M", "h"),
+ (0x1D4D8, "M", "i"),
+ (0x1D4D9, "M", "j"),
+ (0x1D4DA, "M", "k"),
+ (0x1D4DB, "M", "l"),
+ (0x1D4DC, "M", "m"),
+ (0x1D4DD, "M", "n"),
+ (0x1D4DE, "M", "o"),
+ (0x1D4DF, "M", "p"),
+ (0x1D4E0, "M", "q"),
+ (0x1D4E1, "M", "r"),
+ (0x1D4E2, "M", "s"),
+ (0x1D4E3, "M", "t"),
+ (0x1D4E4, "M", "u"),
+ (0x1D4E5, "M", "v"),
+ (0x1D4E6, "M", "w"),
+ (0x1D4E7, "M", "x"),
+ (0x1D4E8, "M", "y"),
+ (0x1D4E9, "M", "z"),
+ (0x1D4EA, "M", "a"),
+ (0x1D4EB, "M", "b"),
+ (0x1D4EC, "M", "c"),
+ (0x1D4ED, "M", "d"),
+ (0x1D4EE, "M", "e"),
+ (0x1D4EF, "M", "f"),
+ (0x1D4F0, "M", "g"),
+ (0x1D4F1, "M", "h"),
+ (0x1D4F2, "M", "i"),
+ (0x1D4F3, "M", "j"),
+ (0x1D4F4, "M", "k"),
+ (0x1D4F5, "M", "l"),
+ (0x1D4F6, "M", "m"),
+ (0x1D4F7, "M", "n"),
+ (0x1D4F8, "M", "o"),
+ (0x1D4F9, "M", "p"),
+ (0x1D4FA, "M", "q"),
+ (0x1D4FB, "M", "r"),
+ (0x1D4FC, "M", "s"),
+ (0x1D4FD, "M", "t"),
+ (0x1D4FE, "M", "u"),
+ (0x1D4FF, "M", "v"),
+ (0x1D500, "M", "w"),
+ (0x1D501, "M", "x"),
+ (0x1D502, "M", "y"),
+ (0x1D503, "M", "z"),
+ (0x1D504, "M", "a"),
+ (0x1D505, "M", "b"),
+ (0x1D506, "X"),
+ (0x1D507, "M", "d"),
+ (0x1D508, "M", "e"),
+ (0x1D509, "M", "f"),
+ (0x1D50A, "M", "g"),
+ (0x1D50B, "X"),
+ (0x1D50D, "M", "j"),
+ (0x1D50E, "M", "k"),
+ (0x1D50F, "M", "l"),
+ (0x1D510, "M", "m"),
+ (0x1D511, "M", "n"),
+ (0x1D512, "M", "o"),
+ (0x1D513, "M", "p"),
+ (0x1D514, "M", "q"),
+ (0x1D515, "X"),
+ (0x1D516, "M", "s"),
+ (0x1D517, "M", "t"),
+ (0x1D518, "M", "u"),
+ (0x1D519, "M", "v"),
+ (0x1D51A, "M", "w"),
+ (0x1D51B, "M", "x"),
+ (0x1D51C, "M", "y"),
+ (0x1D51D, "X"),
+ (0x1D51E, "M", "a"),
+ (0x1D51F, "M", "b"),
+ (0x1D520, "M", "c"),
+ (0x1D521, "M", "d"),
+ (0x1D522, "M", "e"),
+ (0x1D523, "M", "f"),
+ (0x1D524, "M", "g"),
+ (0x1D525, "M", "h"),
+ (0x1D526, "M", "i"),
+ (0x1D527, "M", "j"),
+ ]
+
+
+def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D528, "M", "k"),
+ (0x1D529, "M", "l"),
+ (0x1D52A, "M", "m"),
+ (0x1D52B, "M", "n"),
+ (0x1D52C, "M", "o"),
+ (0x1D52D, "M", "p"),
+ (0x1D52E, "M", "q"),
+ (0x1D52F, "M", "r"),
+ (0x1D530, "M", "s"),
+ (0x1D531, "M", "t"),
+ (0x1D532, "M", "u"),
+ (0x1D533, "M", "v"),
+ (0x1D534, "M", "w"),
+ (0x1D535, "M", "x"),
+ (0x1D536, "M", "y"),
+ (0x1D537, "M", "z"),
+ (0x1D538, "M", "a"),
+ (0x1D539, "M", "b"),
+ (0x1D53A, "X"),
+ (0x1D53B, "M", "d"),
+ (0x1D53C, "M", "e"),
+ (0x1D53D, "M", "f"),
+ (0x1D53E, "M", "g"),
+ (0x1D53F, "X"),
+ (0x1D540, "M", "i"),
+ (0x1D541, "M", "j"),
+ (0x1D542, "M", "k"),
+ (0x1D543, "M", "l"),
+ (0x1D544, "M", "m"),
+ (0x1D545, "X"),
+ (0x1D546, "M", "o"),
+ (0x1D547, "X"),
+ (0x1D54A, "M", "s"),
+ (0x1D54B, "M", "t"),
+ (0x1D54C, "M", "u"),
+ (0x1D54D, "M", "v"),
+ (0x1D54E, "M", "w"),
+ (0x1D54F, "M", "x"),
+ (0x1D550, "M", "y"),
+ (0x1D551, "X"),
+ (0x1D552, "M", "a"),
+ (0x1D553, "M", "b"),
+ (0x1D554, "M", "c"),
+ (0x1D555, "M", "d"),
+ (0x1D556, "M", "e"),
+ (0x1D557, "M", "f"),
+ (0x1D558, "M", "g"),
+ (0x1D559, "M", "h"),
+ (0x1D55A, "M", "i"),
+ (0x1D55B, "M", "j"),
+ (0x1D55C, "M", "k"),
+ (0x1D55D, "M", "l"),
+ (0x1D55E, "M", "m"),
+ (0x1D55F, "M", "n"),
+ (0x1D560, "M", "o"),
+ (0x1D561, "M", "p"),
+ (0x1D562, "M", "q"),
+ (0x1D563, "M", "r"),
+ (0x1D564, "M", "s"),
+ (0x1D565, "M", "t"),
+ (0x1D566, "M", "u"),
+ (0x1D567, "M", "v"),
+ (0x1D568, "M", "w"),
+ (0x1D569, "M", "x"),
+ (0x1D56A, "M", "y"),
+ (0x1D56B, "M", "z"),
+ (0x1D56C, "M", "a"),
+ (0x1D56D, "M", "b"),
+ (0x1D56E, "M", "c"),
+ (0x1D56F, "M", "d"),
+ (0x1D570, "M", "e"),
+ (0x1D571, "M", "f"),
+ (0x1D572, "M", "g"),
+ (0x1D573, "M", "h"),
+ (0x1D574, "M", "i"),
+ (0x1D575, "M", "j"),
+ (0x1D576, "M", "k"),
+ (0x1D577, "M", "l"),
+ (0x1D578, "M", "m"),
+ (0x1D579, "M", "n"),
+ (0x1D57A, "M", "o"),
+ (0x1D57B, "M", "p"),
+ (0x1D57C, "M", "q"),
+ (0x1D57D, "M", "r"),
+ (0x1D57E, "M", "s"),
+ (0x1D57F, "M", "t"),
+ (0x1D580, "M", "u"),
+ (0x1D581, "M", "v"),
+ (0x1D582, "M", "w"),
+ (0x1D583, "M", "x"),
+ (0x1D584, "M", "y"),
+ (0x1D585, "M", "z"),
+ (0x1D586, "M", "a"),
+ (0x1D587, "M", "b"),
+ (0x1D588, "M", "c"),
+ (0x1D589, "M", "d"),
+ (0x1D58A, "M", "e"),
+ (0x1D58B, "M", "f"),
+ (0x1D58C, "M", "g"),
+ (0x1D58D, "M", "h"),
+ ]
+
+
+def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D58E, "M", "i"),
+ (0x1D58F, "M", "j"),
+ (0x1D590, "M", "k"),
+ (0x1D591, "M", "l"),
+ (0x1D592, "M", "m"),
+ (0x1D593, "M", "n"),
+ (0x1D594, "M", "o"),
+ (0x1D595, "M", "p"),
+ (0x1D596, "M", "q"),
+ (0x1D597, "M", "r"),
+ (0x1D598, "M", "s"),
+ (0x1D599, "M", "t"),
+ (0x1D59A, "M", "u"),
+ (0x1D59B, "M", "v"),
+ (0x1D59C, "M", "w"),
+ (0x1D59D, "M", "x"),
+ (0x1D59E, "M", "y"),
+ (0x1D59F, "M", "z"),
+ (0x1D5A0, "M", "a"),
+ (0x1D5A1, "M", "b"),
+ (0x1D5A2, "M", "c"),
+ (0x1D5A3, "M", "d"),
+ (0x1D5A4, "M", "e"),
+ (0x1D5A5, "M", "f"),
+ (0x1D5A6, "M", "g"),
+ (0x1D5A7, "M", "h"),
+ (0x1D5A8, "M", "i"),
+ (0x1D5A9, "M", "j"),
+ (0x1D5AA, "M", "k"),
+ (0x1D5AB, "M", "l"),
+ (0x1D5AC, "M", "m"),
+ (0x1D5AD, "M", "n"),
+ (0x1D5AE, "M", "o"),
+ (0x1D5AF, "M", "p"),
+ (0x1D5B0, "M", "q"),
+ (0x1D5B1, "M", "r"),
+ (0x1D5B2, "M", "s"),
+ (0x1D5B3, "M", "t"),
+ (0x1D5B4, "M", "u"),
+ (0x1D5B5, "M", "v"),
+ (0x1D5B6, "M", "w"),
+ (0x1D5B7, "M", "x"),
+ (0x1D5B8, "M", "y"),
+ (0x1D5B9, "M", "z"),
+ (0x1D5BA, "M", "a"),
+ (0x1D5BB, "M", "b"),
+ (0x1D5BC, "M", "c"),
+ (0x1D5BD, "M", "d"),
+ (0x1D5BE, "M", "e"),
+ (0x1D5BF, "M", "f"),
+ (0x1D5C0, "M", "g"),
+ (0x1D5C1, "M", "h"),
+ (0x1D5C2, "M", "i"),
+ (0x1D5C3, "M", "j"),
+ (0x1D5C4, "M", "k"),
+ (0x1D5C5, "M", "l"),
+ (0x1D5C6, "M", "m"),
+ (0x1D5C7, "M", "n"),
+ (0x1D5C8, "M", "o"),
+ (0x1D5C9, "M", "p"),
+ (0x1D5CA, "M", "q"),
+ (0x1D5CB, "M", "r"),
+ (0x1D5CC, "M", "s"),
+ (0x1D5CD, "M", "t"),
+ (0x1D5CE, "M", "u"),
+ (0x1D5CF, "M", "v"),
+ (0x1D5D0, "M", "w"),
+ (0x1D5D1, "M", "x"),
+ (0x1D5D2, "M", "y"),
+ (0x1D5D3, "M", "z"),
+ (0x1D5D4, "M", "a"),
+ (0x1D5D5, "M", "b"),
+ (0x1D5D6, "M", "c"),
+ (0x1D5D7, "M", "d"),
+ (0x1D5D8, "M", "e"),
+ (0x1D5D9, "M", "f"),
+ (0x1D5DA, "M", "g"),
+ (0x1D5DB, "M", "h"),
+ (0x1D5DC, "M", "i"),
+ (0x1D5DD, "M", "j"),
+ (0x1D5DE, "M", "k"),
+ (0x1D5DF, "M", "l"),
+ (0x1D5E0, "M", "m"),
+ (0x1D5E1, "M", "n"),
+ (0x1D5E2, "M", "o"),
+ (0x1D5E3, "M", "p"),
+ (0x1D5E4, "M", "q"),
+ (0x1D5E5, "M", "r"),
+ (0x1D5E6, "M", "s"),
+ (0x1D5E7, "M", "t"),
+ (0x1D5E8, "M", "u"),
+ (0x1D5E9, "M", "v"),
+ (0x1D5EA, "M", "w"),
+ (0x1D5EB, "M", "x"),
+ (0x1D5EC, "M", "y"),
+ (0x1D5ED, "M", "z"),
+ (0x1D5EE, "M", "a"),
+ (0x1D5EF, "M", "b"),
+ (0x1D5F0, "M", "c"),
+ (0x1D5F1, "M", "d"),
+ ]
+
+
+def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D5F2, "M", "e"),
+ (0x1D5F3, "M", "f"),
+ (0x1D5F4, "M", "g"),
+ (0x1D5F5, "M", "h"),
+ (0x1D5F6, "M", "i"),
+ (0x1D5F7, "M", "j"),
+ (0x1D5F8, "M", "k"),
+ (0x1D5F9, "M", "l"),
+ (0x1D5FA, "M", "m"),
+ (0x1D5FB, "M", "n"),
+ (0x1D5FC, "M", "o"),
+ (0x1D5FD, "M", "p"),
+ (0x1D5FE, "M", "q"),
+ (0x1D5FF, "M", "r"),
+ (0x1D600, "M", "s"),
+ (0x1D601, "M", "t"),
+ (0x1D602, "M", "u"),
+ (0x1D603, "M", "v"),
+ (0x1D604, "M", "w"),
+ (0x1D605, "M", "x"),
+ (0x1D606, "M", "y"),
+ (0x1D607, "M", "z"),
+ (0x1D608, "M", "a"),
+ (0x1D609, "M", "b"),
+ (0x1D60A, "M", "c"),
+ (0x1D60B, "M", "d"),
+ (0x1D60C, "M", "e"),
+ (0x1D60D, "M", "f"),
+ (0x1D60E, "M", "g"),
+ (0x1D60F, "M", "h"),
+ (0x1D610, "M", "i"),
+ (0x1D611, "M", "j"),
+ (0x1D612, "M", "k"),
+ (0x1D613, "M", "l"),
+ (0x1D614, "M", "m"),
+ (0x1D615, "M", "n"),
+ (0x1D616, "M", "o"),
+ (0x1D617, "M", "p"),
+ (0x1D618, "M", "q"),
+ (0x1D619, "M", "r"),
+ (0x1D61A, "M", "s"),
+ (0x1D61B, "M", "t"),
+ (0x1D61C, "M", "u"),
+ (0x1D61D, "M", "v"),
+ (0x1D61E, "M", "w"),
+ (0x1D61F, "M", "x"),
+ (0x1D620, "M", "y"),
+ (0x1D621, "M", "z"),
+ (0x1D622, "M", "a"),
+ (0x1D623, "M", "b"),
+ (0x1D624, "M", "c"),
+ (0x1D625, "M", "d"),
+ (0x1D626, "M", "e"),
+ (0x1D627, "M", "f"),
+ (0x1D628, "M", "g"),
+ (0x1D629, "M", "h"),
+ (0x1D62A, "M", "i"),
+ (0x1D62B, "M", "j"),
+ (0x1D62C, "M", "k"),
+ (0x1D62D, "M", "l"),
+ (0x1D62E, "M", "m"),
+ (0x1D62F, "M", "n"),
+ (0x1D630, "M", "o"),
+ (0x1D631, "M", "p"),
+ (0x1D632, "M", "q"),
+ (0x1D633, "M", "r"),
+ (0x1D634, "M", "s"),
+ (0x1D635, "M", "t"),
+ (0x1D636, "M", "u"),
+ (0x1D637, "M", "v"),
+ (0x1D638, "M", "w"),
+ (0x1D639, "M", "x"),
+ (0x1D63A, "M", "y"),
+ (0x1D63B, "M", "z"),
+ (0x1D63C, "M", "a"),
+ (0x1D63D, "M", "b"),
+ (0x1D63E, "M", "c"),
+ (0x1D63F, "M", "d"),
+ (0x1D640, "M", "e"),
+ (0x1D641, "M", "f"),
+ (0x1D642, "M", "g"),
+ (0x1D643, "M", "h"),
+ (0x1D644, "M", "i"),
+ (0x1D645, "M", "j"),
+ (0x1D646, "M", "k"),
+ (0x1D647, "M", "l"),
+ (0x1D648, "M", "m"),
+ (0x1D649, "M", "n"),
+ (0x1D64A, "M", "o"),
+ (0x1D64B, "M", "p"),
+ (0x1D64C, "M", "q"),
+ (0x1D64D, "M", "r"),
+ (0x1D64E, "M", "s"),
+ (0x1D64F, "M", "t"),
+ (0x1D650, "M", "u"),
+ (0x1D651, "M", "v"),
+ (0x1D652, "M", "w"),
+ (0x1D653, "M", "x"),
+ (0x1D654, "M", "y"),
+ (0x1D655, "M", "z"),
+ ]
+
+
+def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D656, "M", "a"),
+ (0x1D657, "M", "b"),
+ (0x1D658, "M", "c"),
+ (0x1D659, "M", "d"),
+ (0x1D65A, "M", "e"),
+ (0x1D65B, "M", "f"),
+ (0x1D65C, "M", "g"),
+ (0x1D65D, "M", "h"),
+ (0x1D65E, "M", "i"),
+ (0x1D65F, "M", "j"),
+ (0x1D660, "M", "k"),
+ (0x1D661, "M", "l"),
+ (0x1D662, "M", "m"),
+ (0x1D663, "M", "n"),
+ (0x1D664, "M", "o"),
+ (0x1D665, "M", "p"),
+ (0x1D666, "M", "q"),
+ (0x1D667, "M", "r"),
+ (0x1D668, "M", "s"),
+ (0x1D669, "M", "t"),
+ (0x1D66A, "M", "u"),
+ (0x1D66B, "M", "v"),
+ (0x1D66C, "M", "w"),
+ (0x1D66D, "M", "x"),
+ (0x1D66E, "M", "y"),
+ (0x1D66F, "M", "z"),
+ (0x1D670, "M", "a"),
+ (0x1D671, "M", "b"),
+ (0x1D672, "M", "c"),
+ (0x1D673, "M", "d"),
+ (0x1D674, "M", "e"),
+ (0x1D675, "M", "f"),
+ (0x1D676, "M", "g"),
+ (0x1D677, "M", "h"),
+ (0x1D678, "M", "i"),
+ (0x1D679, "M", "j"),
+ (0x1D67A, "M", "k"),
+ (0x1D67B, "M", "l"),
+ (0x1D67C, "M", "m"),
+ (0x1D67D, "M", "n"),
+ (0x1D67E, "M", "o"),
+ (0x1D67F, "M", "p"),
+ (0x1D680, "M", "q"),
+ (0x1D681, "M", "r"),
+ (0x1D682, "M", "s"),
+ (0x1D683, "M", "t"),
+ (0x1D684, "M", "u"),
+ (0x1D685, "M", "v"),
+ (0x1D686, "M", "w"),
+ (0x1D687, "M", "x"),
+ (0x1D688, "M", "y"),
+ (0x1D689, "M", "z"),
+ (0x1D68A, "M", "a"),
+ (0x1D68B, "M", "b"),
+ (0x1D68C, "M", "c"),
+ (0x1D68D, "M", "d"),
+ (0x1D68E, "M", "e"),
+ (0x1D68F, "M", "f"),
+ (0x1D690, "M", "g"),
+ (0x1D691, "M", "h"),
+ (0x1D692, "M", "i"),
+ (0x1D693, "M", "j"),
+ (0x1D694, "M", "k"),
+ (0x1D695, "M", "l"),
+ (0x1D696, "M", "m"),
+ (0x1D697, "M", "n"),
+ (0x1D698, "M", "o"),
+ (0x1D699, "M", "p"),
+ (0x1D69A, "M", "q"),
+ (0x1D69B, "M", "r"),
+ (0x1D69C, "M", "s"),
+ (0x1D69D, "M", "t"),
+ (0x1D69E, "M", "u"),
+ (0x1D69F, "M", "v"),
+ (0x1D6A0, "M", "w"),
+ (0x1D6A1, "M", "x"),
+ (0x1D6A2, "M", "y"),
+ (0x1D6A3, "M", "z"),
+ (0x1D6A4, "M", "ı"),
+ (0x1D6A5, "M", "ȷ"),
+ (0x1D6A6, "X"),
+ (0x1D6A8, "M", "α"),
+ (0x1D6A9, "M", "β"),
+ (0x1D6AA, "M", "γ"),
+ (0x1D6AB, "M", "δ"),
+ (0x1D6AC, "M", "ε"),
+ (0x1D6AD, "M", "ζ"),
+ (0x1D6AE, "M", "η"),
+ (0x1D6AF, "M", "θ"),
+ (0x1D6B0, "M", "ι"),
+ (0x1D6B1, "M", "κ"),
+ (0x1D6B2, "M", "λ"),
+ (0x1D6B3, "M", "μ"),
+ (0x1D6B4, "M", "ν"),
+ (0x1D6B5, "M", "ξ"),
+ (0x1D6B6, "M", "ο"),
+ (0x1D6B7, "M", "π"),
+ (0x1D6B8, "M", "ρ"),
+ (0x1D6B9, "M", "θ"),
+ (0x1D6BA, "M", "σ"),
+ ]
+
+
+def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D6BB, "M", "τ"),
+ (0x1D6BC, "M", "υ"),
+ (0x1D6BD, "M", "φ"),
+ (0x1D6BE, "M", "χ"),
+ (0x1D6BF, "M", "ψ"),
+ (0x1D6C0, "M", "ω"),
+ (0x1D6C1, "M", "∇"),
+ (0x1D6C2, "M", "α"),
+ (0x1D6C3, "M", "β"),
+ (0x1D6C4, "M", "γ"),
+ (0x1D6C5, "M", "δ"),
+ (0x1D6C6, "M", "ε"),
+ (0x1D6C7, "M", "ζ"),
+ (0x1D6C8, "M", "η"),
+ (0x1D6C9, "M", "θ"),
+ (0x1D6CA, "M", "ι"),
+ (0x1D6CB, "M", "κ"),
+ (0x1D6CC, "M", "λ"),
+ (0x1D6CD, "M", "μ"),
+ (0x1D6CE, "M", "ν"),
+ (0x1D6CF, "M", "ξ"),
+ (0x1D6D0, "M", "ο"),
+ (0x1D6D1, "M", "π"),
+ (0x1D6D2, "M", "ρ"),
+ (0x1D6D3, "M", "σ"),
+ (0x1D6D5, "M", "τ"),
+ (0x1D6D6, "M", "υ"),
+ (0x1D6D7, "M", "φ"),
+ (0x1D6D8, "M", "χ"),
+ (0x1D6D9, "M", "ψ"),
+ (0x1D6DA, "M", "ω"),
+ (0x1D6DB, "M", "∂"),
+ (0x1D6DC, "M", "ε"),
+ (0x1D6DD, "M", "θ"),
+ (0x1D6DE, "M", "κ"),
+ (0x1D6DF, "M", "φ"),
+ (0x1D6E0, "M", "ρ"),
+ (0x1D6E1, "M", "π"),
+ (0x1D6E2, "M", "α"),
+ (0x1D6E3, "M", "β"),
+ (0x1D6E4, "M", "γ"),
+ (0x1D6E5, "M", "δ"),
+ (0x1D6E6, "M", "ε"),
+ (0x1D6E7, "M", "ζ"),
+ (0x1D6E8, "M", "η"),
+ (0x1D6E9, "M", "θ"),
+ (0x1D6EA, "M", "ι"),
+ (0x1D6EB, "M", "κ"),
+ (0x1D6EC, "M", "λ"),
+ (0x1D6ED, "M", "μ"),
+ (0x1D6EE, "M", "ν"),
+ (0x1D6EF, "M", "ξ"),
+ (0x1D6F0, "M", "ο"),
+ (0x1D6F1, "M", "π"),
+ (0x1D6F2, "M", "ρ"),
+ (0x1D6F3, "M", "θ"),
+ (0x1D6F4, "M", "σ"),
+ (0x1D6F5, "M", "τ"),
+ (0x1D6F6, "M", "υ"),
+ (0x1D6F7, "M", "φ"),
+ (0x1D6F8, "M", "χ"),
+ (0x1D6F9, "M", "ψ"),
+ (0x1D6FA, "M", "ω"),
+ (0x1D6FB, "M", "∇"),
+ (0x1D6FC, "M", "α"),
+ (0x1D6FD, "M", "β"),
+ (0x1D6FE, "M", "γ"),
+ (0x1D6FF, "M", "δ"),
+ (0x1D700, "M", "ε"),
+ (0x1D701, "M", "ζ"),
+ (0x1D702, "M", "η"),
+ (0x1D703, "M", "θ"),
+ (0x1D704, "M", "ι"),
+ (0x1D705, "M", "κ"),
+ (0x1D706, "M", "λ"),
+ (0x1D707, "M", "μ"),
+ (0x1D708, "M", "ν"),
+ (0x1D709, "M", "ξ"),
+ (0x1D70A, "M", "ο"),
+ (0x1D70B, "M", "π"),
+ (0x1D70C, "M", "ρ"),
+ (0x1D70D, "M", "σ"),
+ (0x1D70F, "M", "τ"),
+ (0x1D710, "M", "υ"),
+ (0x1D711, "M", "φ"),
+ (0x1D712, "M", "χ"),
+ (0x1D713, "M", "ψ"),
+ (0x1D714, "M", "ω"),
+ (0x1D715, "M", "∂"),
+ (0x1D716, "M", "ε"),
+ (0x1D717, "M", "θ"),
+ (0x1D718, "M", "κ"),
+ (0x1D719, "M", "φ"),
+ (0x1D71A, "M", "ρ"),
+ (0x1D71B, "M", "π"),
+ (0x1D71C, "M", "α"),
+ (0x1D71D, "M", "β"),
+ (0x1D71E, "M", "γ"),
+ (0x1D71F, "M", "δ"),
+ (0x1D720, "M", "ε"),
+ ]
+
+
+def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D721, "M", "ζ"),
+ (0x1D722, "M", "η"),
+ (0x1D723, "M", "θ"),
+ (0x1D724, "M", "ι"),
+ (0x1D725, "M", "κ"),
+ (0x1D726, "M", "λ"),
+ (0x1D727, "M", "μ"),
+ (0x1D728, "M", "ν"),
+ (0x1D729, "M", "ξ"),
+ (0x1D72A, "M", "ο"),
+ (0x1D72B, "M", "π"),
+ (0x1D72C, "M", "ρ"),
+ (0x1D72D, "M", "θ"),
+ (0x1D72E, "M", "σ"),
+ (0x1D72F, "M", "τ"),
+ (0x1D730, "M", "υ"),
+ (0x1D731, "M", "φ"),
+ (0x1D732, "M", "χ"),
+ (0x1D733, "M", "ψ"),
+ (0x1D734, "M", "ω"),
+ (0x1D735, "M", "∇"),
+ (0x1D736, "M", "α"),
+ (0x1D737, "M", "β"),
+ (0x1D738, "M", "γ"),
+ (0x1D739, "M", "δ"),
+ (0x1D73A, "M", "ε"),
+ (0x1D73B, "M", "ζ"),
+ (0x1D73C, "M", "η"),
+ (0x1D73D, "M", "θ"),
+ (0x1D73E, "M", "ι"),
+ (0x1D73F, "M", "κ"),
+ (0x1D740, "M", "λ"),
+ (0x1D741, "M", "μ"),
+ (0x1D742, "M", "ν"),
+ (0x1D743, "M", "ξ"),
+ (0x1D744, "M", "ο"),
+ (0x1D745, "M", "π"),
+ (0x1D746, "M", "ρ"),
+ (0x1D747, "M", "σ"),
+ (0x1D749, "M", "τ"),
+ (0x1D74A, "M", "υ"),
+ (0x1D74B, "M", "φ"),
+ (0x1D74C, "M", "χ"),
+ (0x1D74D, "M", "ψ"),
+ (0x1D74E, "M", "ω"),
+ (0x1D74F, "M", "∂"),
+ (0x1D750, "M", "ε"),
+ (0x1D751, "M", "θ"),
+ (0x1D752, "M", "κ"),
+ (0x1D753, "M", "φ"),
+ (0x1D754, "M", "ρ"),
+ (0x1D755, "M", "π"),
+ (0x1D756, "M", "α"),
+ (0x1D757, "M", "β"),
+ (0x1D758, "M", "γ"),
+ (0x1D759, "M", "δ"),
+ (0x1D75A, "M", "ε"),
+ (0x1D75B, "M", "ζ"),
+ (0x1D75C, "M", "η"),
+ (0x1D75D, "M", "θ"),
+ (0x1D75E, "M", "ι"),
+ (0x1D75F, "M", "κ"),
+ (0x1D760, "M", "λ"),
+ (0x1D761, "M", "μ"),
+ (0x1D762, "M", "ν"),
+ (0x1D763, "M", "ξ"),
+ (0x1D764, "M", "ο"),
+ (0x1D765, "M", "π"),
+ (0x1D766, "M", "ρ"),
+ (0x1D767, "M", "θ"),
+ (0x1D768, "M", "σ"),
+ (0x1D769, "M", "τ"),
+ (0x1D76A, "M", "υ"),
+ (0x1D76B, "M", "φ"),
+ (0x1D76C, "M", "χ"),
+ (0x1D76D, "M", "ψ"),
+ (0x1D76E, "M", "ω"),
+ (0x1D76F, "M", "∇"),
+ (0x1D770, "M", "α"),
+ (0x1D771, "M", "β"),
+ (0x1D772, "M", "γ"),
+ (0x1D773, "M", "δ"),
+ (0x1D774, "M", "ε"),
+ (0x1D775, "M", "ζ"),
+ (0x1D776, "M", "η"),
+ (0x1D777, "M", "θ"),
+ (0x1D778, "M", "ι"),
+ (0x1D779, "M", "κ"),
+ (0x1D77A, "M", "λ"),
+ (0x1D77B, "M", "μ"),
+ (0x1D77C, "M", "ν"),
+ (0x1D77D, "M", "ξ"),
+ (0x1D77E, "M", "ο"),
+ (0x1D77F, "M", "π"),
+ (0x1D780, "M", "ρ"),
+ (0x1D781, "M", "σ"),
+ (0x1D783, "M", "τ"),
+ (0x1D784, "M", "υ"),
+ (0x1D785, "M", "φ"),
+ (0x1D786, "M", "χ"),
+ ]
+
+
+def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D787, "M", "ψ"),
+ (0x1D788, "M", "ω"),
+ (0x1D789, "M", "∂"),
+ (0x1D78A, "M", "ε"),
+ (0x1D78B, "M", "θ"),
+ (0x1D78C, "M", "κ"),
+ (0x1D78D, "M", "φ"),
+ (0x1D78E, "M", "ρ"),
+ (0x1D78F, "M", "π"),
+ (0x1D790, "M", "α"),
+ (0x1D791, "M", "β"),
+ (0x1D792, "M", "γ"),
+ (0x1D793, "M", "δ"),
+ (0x1D794, "M", "ε"),
+ (0x1D795, "M", "ζ"),
+ (0x1D796, "M", "η"),
+ (0x1D797, "M", "θ"),
+ (0x1D798, "M", "ι"),
+ (0x1D799, "M", "κ"),
+ (0x1D79A, "M", "λ"),
+ (0x1D79B, "M", "μ"),
+ (0x1D79C, "M", "ν"),
+ (0x1D79D, "M", "ξ"),
+ (0x1D79E, "M", "ο"),
+ (0x1D79F, "M", "π"),
+ (0x1D7A0, "M", "ρ"),
+ (0x1D7A1, "M", "θ"),
+ (0x1D7A2, "M", "σ"),
+ (0x1D7A3, "M", "τ"),
+ (0x1D7A4, "M", "υ"),
+ (0x1D7A5, "M", "φ"),
+ (0x1D7A6, "M", "χ"),
+ (0x1D7A7, "M", "ψ"),
+ (0x1D7A8, "M", "ω"),
+ (0x1D7A9, "M", "∇"),
+ (0x1D7AA, "M", "α"),
+ (0x1D7AB, "M", "β"),
+ (0x1D7AC, "M", "γ"),
+ (0x1D7AD, "M", "δ"),
+ (0x1D7AE, "M", "ε"),
+ (0x1D7AF, "M", "ζ"),
+ (0x1D7B0, "M", "η"),
+ (0x1D7B1, "M", "θ"),
+ (0x1D7B2, "M", "ι"),
+ (0x1D7B3, "M", "κ"),
+ (0x1D7B4, "M", "λ"),
+ (0x1D7B5, "M", "μ"),
+ (0x1D7B6, "M", "ν"),
+ (0x1D7B7, "M", "ξ"),
+ (0x1D7B8, "M", "ο"),
+ (0x1D7B9, "M", "π"),
+ (0x1D7BA, "M", "ρ"),
+ (0x1D7BB, "M", "σ"),
+ (0x1D7BD, "M", "τ"),
+ (0x1D7BE, "M", "υ"),
+ (0x1D7BF, "M", "φ"),
+ (0x1D7C0, "M", "χ"),
+ (0x1D7C1, "M", "ψ"),
+ (0x1D7C2, "M", "ω"),
+ (0x1D7C3, "M", "∂"),
+ (0x1D7C4, "M", "ε"),
+ (0x1D7C5, "M", "θ"),
+ (0x1D7C6, "M", "κ"),
+ (0x1D7C7, "M", "φ"),
+ (0x1D7C8, "M", "ρ"),
+ (0x1D7C9, "M", "π"),
+ (0x1D7CA, "M", "ϝ"),
+ (0x1D7CC, "X"),
+ (0x1D7CE, "M", "0"),
+ (0x1D7CF, "M", "1"),
+ (0x1D7D0, "M", "2"),
+ (0x1D7D1, "M", "3"),
+ (0x1D7D2, "M", "4"),
+ (0x1D7D3, "M", "5"),
+ (0x1D7D4, "M", "6"),
+ (0x1D7D5, "M", "7"),
+ (0x1D7D6, "M", "8"),
+ (0x1D7D7, "M", "9"),
+ (0x1D7D8, "M", "0"),
+ (0x1D7D9, "M", "1"),
+ (0x1D7DA, "M", "2"),
+ (0x1D7DB, "M", "3"),
+ (0x1D7DC, "M", "4"),
+ (0x1D7DD, "M", "5"),
+ (0x1D7DE, "M", "6"),
+ (0x1D7DF, "M", "7"),
+ (0x1D7E0, "M", "8"),
+ (0x1D7E1, "M", "9"),
+ (0x1D7E2, "M", "0"),
+ (0x1D7E3, "M", "1"),
+ (0x1D7E4, "M", "2"),
+ (0x1D7E5, "M", "3"),
+ (0x1D7E6, "M", "4"),
+ (0x1D7E7, "M", "5"),
+ (0x1D7E8, "M", "6"),
+ (0x1D7E9, "M", "7"),
+ (0x1D7EA, "M", "8"),
+ (0x1D7EB, "M", "9"),
+ (0x1D7EC, "M", "0"),
+ (0x1D7ED, "M", "1"),
+ ]
+
+
+def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1D7EE, "M", "2"),
+ (0x1D7EF, "M", "3"),
+ (0x1D7F0, "M", "4"),
+ (0x1D7F1, "M", "5"),
+ (0x1D7F2, "M", "6"),
+ (0x1D7F3, "M", "7"),
+ (0x1D7F4, "M", "8"),
+ (0x1D7F5, "M", "9"),
+ (0x1D7F6, "M", "0"),
+ (0x1D7F7, "M", "1"),
+ (0x1D7F8, "M", "2"),
+ (0x1D7F9, "M", "3"),
+ (0x1D7FA, "M", "4"),
+ (0x1D7FB, "M", "5"),
+ (0x1D7FC, "M", "6"),
+ (0x1D7FD, "M", "7"),
+ (0x1D7FE, "M", "8"),
+ (0x1D7FF, "M", "9"),
+ (0x1D800, "V"),
+ (0x1DA8C, "X"),
+ (0x1DA9B, "V"),
+ (0x1DAA0, "X"),
+ (0x1DAA1, "V"),
+ (0x1DAB0, "X"),
+ (0x1DF00, "V"),
+ (0x1DF1F, "X"),
+ (0x1DF25, "V"),
+ (0x1DF2B, "X"),
+ (0x1E000, "V"),
+ (0x1E007, "X"),
+ (0x1E008, "V"),
+ (0x1E019, "X"),
+ (0x1E01B, "V"),
+ (0x1E022, "X"),
+ (0x1E023, "V"),
+ (0x1E025, "X"),
+ (0x1E026, "V"),
+ (0x1E02B, "X"),
+ (0x1E030, "M", "а"),
+ (0x1E031, "M", "б"),
+ (0x1E032, "M", "в"),
+ (0x1E033, "M", "г"),
+ (0x1E034, "M", "д"),
+ (0x1E035, "M", "е"),
+ (0x1E036, "M", "ж"),
+ (0x1E037, "M", "з"),
+ (0x1E038, "M", "и"),
+ (0x1E039, "M", "к"),
+ (0x1E03A, "M", "л"),
+ (0x1E03B, "M", "м"),
+ (0x1E03C, "M", "о"),
+ (0x1E03D, "M", "п"),
+ (0x1E03E, "M", "р"),
+ (0x1E03F, "M", "с"),
+ (0x1E040, "M", "т"),
+ (0x1E041, "M", "у"),
+ (0x1E042, "M", "ф"),
+ (0x1E043, "M", "х"),
+ (0x1E044, "M", "ц"),
+ (0x1E045, "M", "ч"),
+ (0x1E046, "M", "ш"),
+ (0x1E047, "M", "ы"),
+ (0x1E048, "M", "э"),
+ (0x1E049, "M", "ю"),
+ (0x1E04A, "M", "ꚉ"),
+ (0x1E04B, "M", "ә"),
+ (0x1E04C, "M", "і"),
+ (0x1E04D, "M", "ј"),
+ (0x1E04E, "M", "ө"),
+ (0x1E04F, "M", "ү"),
+ (0x1E050, "M", "ӏ"),
+ (0x1E051, "M", "а"),
+ (0x1E052, "M", "б"),
+ (0x1E053, "M", "в"),
+ (0x1E054, "M", "г"),
+ (0x1E055, "M", "д"),
+ (0x1E056, "M", "е"),
+ (0x1E057, "M", "ж"),
+ (0x1E058, "M", "з"),
+ (0x1E059, "M", "и"),
+ (0x1E05A, "M", "к"),
+ (0x1E05B, "M", "л"),
+ (0x1E05C, "M", "о"),
+ (0x1E05D, "M", "п"),
+ (0x1E05E, "M", "с"),
+ (0x1E05F, "M", "у"),
+ (0x1E060, "M", "ф"),
+ (0x1E061, "M", "х"),
+ (0x1E062, "M", "ц"),
+ (0x1E063, "M", "ч"),
+ (0x1E064, "M", "ш"),
+ (0x1E065, "M", "ъ"),
+ (0x1E066, "M", "ы"),
+ (0x1E067, "M", "ґ"),
+ (0x1E068, "M", "і"),
+ (0x1E069, "M", "ѕ"),
+ (0x1E06A, "M", "џ"),
+ (0x1E06B, "M", "ҫ"),
+ (0x1E06C, "M", "ꙑ"),
+ (0x1E06D, "M", "ұ"),
+ ]
+
+
+def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1E06E, "X"),
+ (0x1E08F, "V"),
+ (0x1E090, "X"),
+ (0x1E100, "V"),
+ (0x1E12D, "X"),
+ (0x1E130, "V"),
+ (0x1E13E, "X"),
+ (0x1E140, "V"),
+ (0x1E14A, "X"),
+ (0x1E14E, "V"),
+ (0x1E150, "X"),
+ (0x1E290, "V"),
+ (0x1E2AF, "X"),
+ (0x1E2C0, "V"),
+ (0x1E2FA, "X"),
+ (0x1E2FF, "V"),
+ (0x1E300, "X"),
+ (0x1E4D0, "V"),
+ (0x1E4FA, "X"),
+ (0x1E5D0, "V"),
+ (0x1E5FB, "X"),
+ (0x1E5FF, "V"),
+ (0x1E600, "X"),
+ (0x1E7E0, "V"),
+ (0x1E7E7, "X"),
+ (0x1E7E8, "V"),
+ (0x1E7EC, "X"),
+ (0x1E7ED, "V"),
+ (0x1E7EF, "X"),
+ (0x1E7F0, "V"),
+ (0x1E7FF, "X"),
+ (0x1E800, "V"),
+ (0x1E8C5, "X"),
+ (0x1E8C7, "V"),
+ (0x1E8D7, "X"),
+ (0x1E900, "M", "𞤢"),
+ (0x1E901, "M", "𞤣"),
+ (0x1E902, "M", "𞤤"),
+ (0x1E903, "M", "𞤥"),
+ (0x1E904, "M", "𞤦"),
+ (0x1E905, "M", "𞤧"),
+ (0x1E906, "M", "𞤨"),
+ (0x1E907, "M", "𞤩"),
+ (0x1E908, "M", "𞤪"),
+ (0x1E909, "M", "𞤫"),
+ (0x1E90A, "M", "𞤬"),
+ (0x1E90B, "M", "𞤭"),
+ (0x1E90C, "M", "𞤮"),
+ (0x1E90D, "M", "𞤯"),
+ (0x1E90E, "M", "𞤰"),
+ (0x1E90F, "M", "𞤱"),
+ (0x1E910, "M", "𞤲"),
+ (0x1E911, "M", "𞤳"),
+ (0x1E912, "M", "𞤴"),
+ (0x1E913, "M", "𞤵"),
+ (0x1E914, "M", "𞤶"),
+ (0x1E915, "M", "𞤷"),
+ (0x1E916, "M", "𞤸"),
+ (0x1E917, "M", "𞤹"),
+ (0x1E918, "M", "𞤺"),
+ (0x1E919, "M", "𞤻"),
+ (0x1E91A, "M", "𞤼"),
+ (0x1E91B, "M", "𞤽"),
+ (0x1E91C, "M", "𞤾"),
+ (0x1E91D, "M", "𞤿"),
+ (0x1E91E, "M", "𞥀"),
+ (0x1E91F, "M", "𞥁"),
+ (0x1E920, "M", "𞥂"),
+ (0x1E921, "M", "𞥃"),
+ (0x1E922, "V"),
+ (0x1E94C, "X"),
+ (0x1E950, "V"),
+ (0x1E95A, "X"),
+ (0x1E95E, "V"),
+ (0x1E960, "X"),
+ (0x1EC71, "V"),
+ (0x1ECB5, "X"),
+ (0x1ED01, "V"),
+ (0x1ED3E, "X"),
+ (0x1EE00, "M", "ا"),
+ (0x1EE01, "M", "ب"),
+ (0x1EE02, "M", "ج"),
+ (0x1EE03, "M", "د"),
+ (0x1EE04, "X"),
+ (0x1EE05, "M", "و"),
+ (0x1EE06, "M", "ز"),
+ (0x1EE07, "M", "ح"),
+ (0x1EE08, "M", "ط"),
+ (0x1EE09, "M", "ي"),
+ (0x1EE0A, "M", "ك"),
+ (0x1EE0B, "M", "ل"),
+ (0x1EE0C, "M", "م"),
+ (0x1EE0D, "M", "ن"),
+ (0x1EE0E, "M", "س"),
+ (0x1EE0F, "M", "ع"),
+ (0x1EE10, "M", "ف"),
+ (0x1EE11, "M", "ص"),
+ (0x1EE12, "M", "ق"),
+ (0x1EE13, "M", "ر"),
+ (0x1EE14, "M", "ش"),
+ ]
+
+
+def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1EE15, "M", "ت"),
+ (0x1EE16, "M", "ث"),
+ (0x1EE17, "M", "خ"),
+ (0x1EE18, "M", "ذ"),
+ (0x1EE19, "M", "ض"),
+ (0x1EE1A, "M", "ظ"),
+ (0x1EE1B, "M", "غ"),
+ (0x1EE1C, "M", "ٮ"),
+ (0x1EE1D, "M", "ں"),
+ (0x1EE1E, "M", "ڡ"),
+ (0x1EE1F, "M", "ٯ"),
+ (0x1EE20, "X"),
+ (0x1EE21, "M", "ب"),
+ (0x1EE22, "M", "ج"),
+ (0x1EE23, "X"),
+ (0x1EE24, "M", "ه"),
+ (0x1EE25, "X"),
+ (0x1EE27, "M", "ح"),
+ (0x1EE28, "X"),
+ (0x1EE29, "M", "ي"),
+ (0x1EE2A, "M", "ك"),
+ (0x1EE2B, "M", "ل"),
+ (0x1EE2C, "M", "م"),
+ (0x1EE2D, "M", "ن"),
+ (0x1EE2E, "M", "س"),
+ (0x1EE2F, "M", "ع"),
+ (0x1EE30, "M", "ف"),
+ (0x1EE31, "M", "ص"),
+ (0x1EE32, "M", "ق"),
+ (0x1EE33, "X"),
+ (0x1EE34, "M", "ش"),
+ (0x1EE35, "M", "ت"),
+ (0x1EE36, "M", "ث"),
+ (0x1EE37, "M", "خ"),
+ (0x1EE38, "X"),
+ (0x1EE39, "M", "ض"),
+ (0x1EE3A, "X"),
+ (0x1EE3B, "M", "غ"),
+ (0x1EE3C, "X"),
+ (0x1EE42, "M", "ج"),
+ (0x1EE43, "X"),
+ (0x1EE47, "M", "ح"),
+ (0x1EE48, "X"),
+ (0x1EE49, "M", "ي"),
+ (0x1EE4A, "X"),
+ (0x1EE4B, "M", "ل"),
+ (0x1EE4C, "X"),
+ (0x1EE4D, "M", "ن"),
+ (0x1EE4E, "M", "س"),
+ (0x1EE4F, "M", "ع"),
+ (0x1EE50, "X"),
+ (0x1EE51, "M", "ص"),
+ (0x1EE52, "M", "ق"),
+ (0x1EE53, "X"),
+ (0x1EE54, "M", "ش"),
+ (0x1EE55, "X"),
+ (0x1EE57, "M", "خ"),
+ (0x1EE58, "X"),
+ (0x1EE59, "M", "ض"),
+ (0x1EE5A, "X"),
+ (0x1EE5B, "M", "غ"),
+ (0x1EE5C, "X"),
+ (0x1EE5D, "M", "ں"),
+ (0x1EE5E, "X"),
+ (0x1EE5F, "M", "ٯ"),
+ (0x1EE60, "X"),
+ (0x1EE61, "M", "ب"),
+ (0x1EE62, "M", "ج"),
+ (0x1EE63, "X"),
+ (0x1EE64, "M", "ه"),
+ (0x1EE65, "X"),
+ (0x1EE67, "M", "ح"),
+ (0x1EE68, "M", "ط"),
+ (0x1EE69, "M", "ي"),
+ (0x1EE6A, "M", "ك"),
+ (0x1EE6B, "X"),
+ (0x1EE6C, "M", "م"),
+ (0x1EE6D, "M", "ن"),
+ (0x1EE6E, "M", "س"),
+ (0x1EE6F, "M", "ع"),
+ (0x1EE70, "M", "ف"),
+ (0x1EE71, "M", "ص"),
+ (0x1EE72, "M", "ق"),
+ (0x1EE73, "X"),
+ (0x1EE74, "M", "ش"),
+ (0x1EE75, "M", "ت"),
+ (0x1EE76, "M", "ث"),
+ (0x1EE77, "M", "خ"),
+ (0x1EE78, "X"),
+ (0x1EE79, "M", "ض"),
+ (0x1EE7A, "M", "ظ"),
+ (0x1EE7B, "M", "غ"),
+ (0x1EE7C, "M", "ٮ"),
+ (0x1EE7D, "X"),
+ (0x1EE7E, "M", "ڡ"),
+ (0x1EE7F, "X"),
+ (0x1EE80, "M", "ا"),
+ (0x1EE81, "M", "ب"),
+ (0x1EE82, "M", "ج"),
+ (0x1EE83, "M", "د"),
+ ]
+
+
+def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1EE84, "M", "ه"),
+ (0x1EE85, "M", "و"),
+ (0x1EE86, "M", "ز"),
+ (0x1EE87, "M", "ح"),
+ (0x1EE88, "M", "ط"),
+ (0x1EE89, "M", "ي"),
+ (0x1EE8A, "X"),
+ (0x1EE8B, "M", "ل"),
+ (0x1EE8C, "M", "م"),
+ (0x1EE8D, "M", "ن"),
+ (0x1EE8E, "M", "س"),
+ (0x1EE8F, "M", "ع"),
+ (0x1EE90, "M", "ف"),
+ (0x1EE91, "M", "ص"),
+ (0x1EE92, "M", "ق"),
+ (0x1EE93, "M", "ر"),
+ (0x1EE94, "M", "ش"),
+ (0x1EE95, "M", "ت"),
+ (0x1EE96, "M", "ث"),
+ (0x1EE97, "M", "خ"),
+ (0x1EE98, "M", "ذ"),
+ (0x1EE99, "M", "ض"),
+ (0x1EE9A, "M", "ظ"),
+ (0x1EE9B, "M", "غ"),
+ (0x1EE9C, "X"),
+ (0x1EEA1, "M", "ب"),
+ (0x1EEA2, "M", "ج"),
+ (0x1EEA3, "M", "د"),
+ (0x1EEA4, "X"),
+ (0x1EEA5, "M", "و"),
+ (0x1EEA6, "M", "ز"),
+ (0x1EEA7, "M", "ح"),
+ (0x1EEA8, "M", "ط"),
+ (0x1EEA9, "M", "ي"),
+ (0x1EEAA, "X"),
+ (0x1EEAB, "M", "ل"),
+ (0x1EEAC, "M", "م"),
+ (0x1EEAD, "M", "ن"),
+ (0x1EEAE, "M", "س"),
+ (0x1EEAF, "M", "ع"),
+ (0x1EEB0, "M", "ف"),
+ (0x1EEB1, "M", "ص"),
+ (0x1EEB2, "M", "ق"),
+ (0x1EEB3, "M", "ر"),
+ (0x1EEB4, "M", "ش"),
+ (0x1EEB5, "M", "ت"),
+ (0x1EEB6, "M", "ث"),
+ (0x1EEB7, "M", "خ"),
+ (0x1EEB8, "M", "ذ"),
+ (0x1EEB9, "M", "ض"),
+ (0x1EEBA, "M", "ظ"),
+ (0x1EEBB, "M", "غ"),
+ (0x1EEBC, "X"),
+ (0x1EEF0, "V"),
+ (0x1EEF2, "X"),
+ (0x1F000, "V"),
+ (0x1F02C, "X"),
+ (0x1F030, "V"),
+ (0x1F094, "X"),
+ (0x1F0A0, "V"),
+ (0x1F0AF, "X"),
+ (0x1F0B1, "V"),
+ (0x1F0C0, "X"),
+ (0x1F0C1, "V"),
+ (0x1F0D0, "X"),
+ (0x1F0D1, "V"),
+ (0x1F0F6, "X"),
+ (0x1F101, "M", "0,"),
+ (0x1F102, "M", "1,"),
+ (0x1F103, "M", "2,"),
+ (0x1F104, "M", "3,"),
+ (0x1F105, "M", "4,"),
+ (0x1F106, "M", "5,"),
+ (0x1F107, "M", "6,"),
+ (0x1F108, "M", "7,"),
+ (0x1F109, "M", "8,"),
+ (0x1F10A, "M", "9,"),
+ (0x1F10B, "V"),
+ (0x1F110, "M", "(a)"),
+ (0x1F111, "M", "(b)"),
+ (0x1F112, "M", "(c)"),
+ (0x1F113, "M", "(d)"),
+ (0x1F114, "M", "(e)"),
+ (0x1F115, "M", "(f)"),
+ (0x1F116, "M", "(g)"),
+ (0x1F117, "M", "(h)"),
+ (0x1F118, "M", "(i)"),
+ (0x1F119, "M", "(j)"),
+ (0x1F11A, "M", "(k)"),
+ (0x1F11B, "M", "(l)"),
+ (0x1F11C, "M", "(m)"),
+ (0x1F11D, "M", "(n)"),
+ (0x1F11E, "M", "(o)"),
+ (0x1F11F, "M", "(p)"),
+ (0x1F120, "M", "(q)"),
+ (0x1F121, "M", "(r)"),
+ (0x1F122, "M", "(s)"),
+ (0x1F123, "M", "(t)"),
+ (0x1F124, "M", "(u)"),
+ (0x1F125, "M", "(v)"),
+ ]
+
+
+def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1F126, "M", "(w)"),
+ (0x1F127, "M", "(x)"),
+ (0x1F128, "M", "(y)"),
+ (0x1F129, "M", "(z)"),
+ (0x1F12A, "M", "〔s〕"),
+ (0x1F12B, "M", "c"),
+ (0x1F12C, "M", "r"),
+ (0x1F12D, "M", "cd"),
+ (0x1F12E, "M", "wz"),
+ (0x1F12F, "V"),
+ (0x1F130, "M", "a"),
+ (0x1F131, "M", "b"),
+ (0x1F132, "M", "c"),
+ (0x1F133, "M", "d"),
+ (0x1F134, "M", "e"),
+ (0x1F135, "M", "f"),
+ (0x1F136, "M", "g"),
+ (0x1F137, "M", "h"),
+ (0x1F138, "M", "i"),
+ (0x1F139, "M", "j"),
+ (0x1F13A, "M", "k"),
+ (0x1F13B, "M", "l"),
+ (0x1F13C, "M", "m"),
+ (0x1F13D, "M", "n"),
+ (0x1F13E, "M", "o"),
+ (0x1F13F, "M", "p"),
+ (0x1F140, "M", "q"),
+ (0x1F141, "M", "r"),
+ (0x1F142, "M", "s"),
+ (0x1F143, "M", "t"),
+ (0x1F144, "M", "u"),
+ (0x1F145, "M", "v"),
+ (0x1F146, "M", "w"),
+ (0x1F147, "M", "x"),
+ (0x1F148, "M", "y"),
+ (0x1F149, "M", "z"),
+ (0x1F14A, "M", "hv"),
+ (0x1F14B, "M", "mv"),
+ (0x1F14C, "M", "sd"),
+ (0x1F14D, "M", "ss"),
+ (0x1F14E, "M", "ppv"),
+ (0x1F14F, "M", "wc"),
+ (0x1F150, "V"),
+ (0x1F16A, "M", "mc"),
+ (0x1F16B, "M", "md"),
+ (0x1F16C, "M", "mr"),
+ (0x1F16D, "V"),
+ (0x1F190, "M", "dj"),
+ (0x1F191, "V"),
+ (0x1F1AE, "X"),
+ (0x1F1E6, "V"),
+ (0x1F200, "M", "ほか"),
+ (0x1F201, "M", "ココ"),
+ (0x1F202, "M", "サ"),
+ (0x1F203, "X"),
+ (0x1F210, "M", "手"),
+ (0x1F211, "M", "字"),
+ (0x1F212, "M", "双"),
+ (0x1F213, "M", "デ"),
+ (0x1F214, "M", "二"),
+ (0x1F215, "M", "多"),
+ (0x1F216, "M", "解"),
+ (0x1F217, "M", "天"),
+ (0x1F218, "M", "交"),
+ (0x1F219, "M", "映"),
+ (0x1F21A, "M", "無"),
+ (0x1F21B, "M", "料"),
+ (0x1F21C, "M", "前"),
+ (0x1F21D, "M", "後"),
+ (0x1F21E, "M", "再"),
+ (0x1F21F, "M", "新"),
+ (0x1F220, "M", "初"),
+ (0x1F221, "M", "終"),
+ (0x1F222, "M", "生"),
+ (0x1F223, "M", "販"),
+ (0x1F224, "M", "声"),
+ (0x1F225, "M", "吹"),
+ (0x1F226, "M", "演"),
+ (0x1F227, "M", "投"),
+ (0x1F228, "M", "捕"),
+ (0x1F229, "M", "一"),
+ (0x1F22A, "M", "三"),
+ (0x1F22B, "M", "遊"),
+ (0x1F22C, "M", "左"),
+ (0x1F22D, "M", "中"),
+ (0x1F22E, "M", "右"),
+ (0x1F22F, "M", "指"),
+ (0x1F230, "M", "走"),
+ (0x1F231, "M", "打"),
+ (0x1F232, "M", "禁"),
+ (0x1F233, "M", "空"),
+ (0x1F234, "M", "合"),
+ (0x1F235, "M", "満"),
+ (0x1F236, "M", "有"),
+ (0x1F237, "M", "月"),
+ (0x1F238, "M", "申"),
+ (0x1F239, "M", "割"),
+ (0x1F23A, "M", "営"),
+ (0x1F23B, "M", "配"),
+ (0x1F23C, "X"),
+ ]
+
+
+def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1F240, "M", "〔本〕"),
+ (0x1F241, "M", "〔三〕"),
+ (0x1F242, "M", "〔二〕"),
+ (0x1F243, "M", "〔安〕"),
+ (0x1F244, "M", "〔点〕"),
+ (0x1F245, "M", "〔打〕"),
+ (0x1F246, "M", "〔盗〕"),
+ (0x1F247, "M", "〔勝〕"),
+ (0x1F248, "M", "〔敗〕"),
+ (0x1F249, "X"),
+ (0x1F250, "M", "得"),
+ (0x1F251, "M", "可"),
+ (0x1F252, "X"),
+ (0x1F260, "V"),
+ (0x1F266, "X"),
+ (0x1F300, "V"),
+ (0x1F6D8, "X"),
+ (0x1F6DC, "V"),
+ (0x1F6ED, "X"),
+ (0x1F6F0, "V"),
+ (0x1F6FD, "X"),
+ (0x1F700, "V"),
+ (0x1F777, "X"),
+ (0x1F77B, "V"),
+ (0x1F7DA, "X"),
+ (0x1F7E0, "V"),
+ (0x1F7EC, "X"),
+ (0x1F7F0, "V"),
+ (0x1F7F1, "X"),
+ (0x1F800, "V"),
+ (0x1F80C, "X"),
+ (0x1F810, "V"),
+ (0x1F848, "X"),
+ (0x1F850, "V"),
+ (0x1F85A, "X"),
+ (0x1F860, "V"),
+ (0x1F888, "X"),
+ (0x1F890, "V"),
+ (0x1F8AE, "X"),
+ (0x1F8B0, "V"),
+ (0x1F8BC, "X"),
+ (0x1F8C0, "V"),
+ (0x1F8C2, "X"),
+ (0x1F900, "V"),
+ (0x1FA54, "X"),
+ (0x1FA60, "V"),
+ (0x1FA6E, "X"),
+ (0x1FA70, "V"),
+ (0x1FA7D, "X"),
+ (0x1FA80, "V"),
+ (0x1FA8A, "X"),
+ (0x1FA8F, "V"),
+ (0x1FAC7, "X"),
+ (0x1FACE, "V"),
+ (0x1FADD, "X"),
+ (0x1FADF, "V"),
+ (0x1FAEA, "X"),
+ (0x1FAF0, "V"),
+ (0x1FAF9, "X"),
+ (0x1FB00, "V"),
+ (0x1FB93, "X"),
+ (0x1FB94, "V"),
+ (0x1FBF0, "M", "0"),
+ (0x1FBF1, "M", "1"),
+ (0x1FBF2, "M", "2"),
+ (0x1FBF3, "M", "3"),
+ (0x1FBF4, "M", "4"),
+ (0x1FBF5, "M", "5"),
+ (0x1FBF6, "M", "6"),
+ (0x1FBF7, "M", "7"),
+ (0x1FBF8, "M", "8"),
+ (0x1FBF9, "M", "9"),
+ (0x1FBFA, "X"),
+ (0x20000, "V"),
+ (0x2A6E0, "X"),
+ (0x2A700, "V"),
+ (0x2B73A, "X"),
+ (0x2B740, "V"),
+ (0x2B81E, "X"),
+ (0x2B820, "V"),
+ (0x2CEA2, "X"),
+ (0x2CEB0, "V"),
+ (0x2EBE1, "X"),
+ (0x2EBF0, "V"),
+ (0x2EE5E, "X"),
+ (0x2F800, "M", "丽"),
+ (0x2F801, "M", "丸"),
+ (0x2F802, "M", "乁"),
+ (0x2F803, "M", "𠄢"),
+ (0x2F804, "M", "你"),
+ (0x2F805, "M", "侮"),
+ (0x2F806, "M", "侻"),
+ (0x2F807, "M", "倂"),
+ (0x2F808, "M", "偺"),
+ (0x2F809, "M", "備"),
+ (0x2F80A, "M", "僧"),
+ (0x2F80B, "M", "像"),
+ (0x2F80C, "M", "㒞"),
+ (0x2F80D, "M", "𠘺"),
+ (0x2F80E, "M", "免"),
+ ]
+
+
+def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F80F, "M", "兔"),
+ (0x2F810, "M", "兤"),
+ (0x2F811, "M", "具"),
+ (0x2F812, "M", "𠔜"),
+ (0x2F813, "M", "㒹"),
+ (0x2F814, "M", "內"),
+ (0x2F815, "M", "再"),
+ (0x2F816, "M", "𠕋"),
+ (0x2F817, "M", "冗"),
+ (0x2F818, "M", "冤"),
+ (0x2F819, "M", "仌"),
+ (0x2F81A, "M", "冬"),
+ (0x2F81B, "M", "况"),
+ (0x2F81C, "M", "𩇟"),
+ (0x2F81D, "M", "凵"),
+ (0x2F81E, "M", "刃"),
+ (0x2F81F, "M", "㓟"),
+ (0x2F820, "M", "刻"),
+ (0x2F821, "M", "剆"),
+ (0x2F822, "M", "割"),
+ (0x2F823, "M", "剷"),
+ (0x2F824, "M", "㔕"),
+ (0x2F825, "M", "勇"),
+ (0x2F826, "M", "勉"),
+ (0x2F827, "M", "勤"),
+ (0x2F828, "M", "勺"),
+ (0x2F829, "M", "包"),
+ (0x2F82A, "M", "匆"),
+ (0x2F82B, "M", "北"),
+ (0x2F82C, "M", "卉"),
+ (0x2F82D, "M", "卑"),
+ (0x2F82E, "M", "博"),
+ (0x2F82F, "M", "即"),
+ (0x2F830, "M", "卽"),
+ (0x2F831, "M", "卿"),
+ (0x2F834, "M", "𠨬"),
+ (0x2F835, "M", "灰"),
+ (0x2F836, "M", "及"),
+ (0x2F837, "M", "叟"),
+ (0x2F838, "M", "𠭣"),
+ (0x2F839, "M", "叫"),
+ (0x2F83A, "M", "叱"),
+ (0x2F83B, "M", "吆"),
+ (0x2F83C, "M", "咞"),
+ (0x2F83D, "M", "吸"),
+ (0x2F83E, "M", "呈"),
+ (0x2F83F, "M", "周"),
+ (0x2F840, "M", "咢"),
+ (0x2F841, "M", "哶"),
+ (0x2F842, "M", "唐"),
+ (0x2F843, "M", "啓"),
+ (0x2F844, "M", "啣"),
+ (0x2F845, "M", "善"),
+ (0x2F847, "M", "喙"),
+ (0x2F848, "M", "喫"),
+ (0x2F849, "M", "喳"),
+ (0x2F84A, "M", "嗂"),
+ (0x2F84B, "M", "圖"),
+ (0x2F84C, "M", "嘆"),
+ (0x2F84D, "M", "圗"),
+ (0x2F84E, "M", "噑"),
+ (0x2F84F, "M", "噴"),
+ (0x2F850, "M", "切"),
+ (0x2F851, "M", "壮"),
+ (0x2F852, "M", "城"),
+ (0x2F853, "M", "埴"),
+ (0x2F854, "M", "堍"),
+ (0x2F855, "M", "型"),
+ (0x2F856, "M", "堲"),
+ (0x2F857, "M", "報"),
+ (0x2F858, "M", "墬"),
+ (0x2F859, "M", "𡓤"),
+ (0x2F85A, "M", "売"),
+ (0x2F85B, "M", "壷"),
+ (0x2F85C, "M", "夆"),
+ (0x2F85D, "M", "多"),
+ (0x2F85E, "M", "夢"),
+ (0x2F85F, "M", "奢"),
+ (0x2F860, "M", "𡚨"),
+ (0x2F861, "M", "𡛪"),
+ (0x2F862, "M", "姬"),
+ (0x2F863, "M", "娛"),
+ (0x2F864, "M", "娧"),
+ (0x2F865, "M", "姘"),
+ (0x2F866, "M", "婦"),
+ (0x2F867, "M", "㛮"),
+ (0x2F868, "M", "㛼"),
+ (0x2F869, "M", "嬈"),
+ (0x2F86A, "M", "嬾"),
+ (0x2F86C, "M", "𡧈"),
+ (0x2F86D, "M", "寃"),
+ (0x2F86E, "M", "寘"),
+ (0x2F86F, "M", "寧"),
+ (0x2F870, "M", "寳"),
+ (0x2F871, "M", "𡬘"),
+ (0x2F872, "M", "寿"),
+ (0x2F873, "M", "将"),
+ (0x2F874, "M", "当"),
+ (0x2F875, "M", "尢"),
+ (0x2F876, "M", "㞁"),
+ ]
+
+
+def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F877, "M", "屠"),
+ (0x2F878, "M", "屮"),
+ (0x2F879, "M", "峀"),
+ (0x2F87A, "M", "岍"),
+ (0x2F87B, "M", "𡷤"),
+ (0x2F87C, "M", "嵃"),
+ (0x2F87D, "M", "𡷦"),
+ (0x2F87E, "M", "嵮"),
+ (0x2F87F, "M", "嵫"),
+ (0x2F880, "M", "嵼"),
+ (0x2F881, "M", "巡"),
+ (0x2F882, "M", "巢"),
+ (0x2F883, "M", "㠯"),
+ (0x2F884, "M", "巽"),
+ (0x2F885, "M", "帨"),
+ (0x2F886, "M", "帽"),
+ (0x2F887, "M", "幩"),
+ (0x2F888, "M", "㡢"),
+ (0x2F889, "M", "𢆃"),
+ (0x2F88A, "M", "㡼"),
+ (0x2F88B, "M", "庰"),
+ (0x2F88C, "M", "庳"),
+ (0x2F88D, "M", "庶"),
+ (0x2F88E, "M", "廊"),
+ (0x2F88F, "M", "𪎒"),
+ (0x2F890, "M", "廾"),
+ (0x2F891, "M", "𢌱"),
+ (0x2F893, "M", "舁"),
+ (0x2F894, "M", "弢"),
+ (0x2F896, "M", "㣇"),
+ (0x2F897, "M", "𣊸"),
+ (0x2F898, "M", "𦇚"),
+ (0x2F899, "M", "形"),
+ (0x2F89A, "M", "彫"),
+ (0x2F89B, "M", "㣣"),
+ (0x2F89C, "M", "徚"),
+ (0x2F89D, "M", "忍"),
+ (0x2F89E, "M", "志"),
+ (0x2F89F, "M", "忹"),
+ (0x2F8A0, "M", "悁"),
+ (0x2F8A1, "M", "㤺"),
+ (0x2F8A2, "M", "㤜"),
+ (0x2F8A3, "M", "悔"),
+ (0x2F8A4, "M", "𢛔"),
+ (0x2F8A5, "M", "惇"),
+ (0x2F8A6, "M", "慈"),
+ (0x2F8A7, "M", "慌"),
+ (0x2F8A8, "M", "慎"),
+ (0x2F8A9, "M", "慌"),
+ (0x2F8AA, "M", "慺"),
+ (0x2F8AB, "M", "憎"),
+ (0x2F8AC, "M", "憲"),
+ (0x2F8AD, "M", "憤"),
+ (0x2F8AE, "M", "憯"),
+ (0x2F8AF, "M", "懞"),
+ (0x2F8B0, "M", "懲"),
+ (0x2F8B1, "M", "懶"),
+ (0x2F8B2, "M", "成"),
+ (0x2F8B3, "M", "戛"),
+ (0x2F8B4, "M", "扝"),
+ (0x2F8B5, "M", "抱"),
+ (0x2F8B6, "M", "拔"),
+ (0x2F8B7, "M", "捐"),
+ (0x2F8B8, "M", "𢬌"),
+ (0x2F8B9, "M", "挽"),
+ (0x2F8BA, "M", "拼"),
+ (0x2F8BB, "M", "捨"),
+ (0x2F8BC, "M", "掃"),
+ (0x2F8BD, "M", "揤"),
+ (0x2F8BE, "M", "𢯱"),
+ (0x2F8BF, "M", "搢"),
+ (0x2F8C0, "M", "揅"),
+ (0x2F8C1, "M", "掩"),
+ (0x2F8C2, "M", "㨮"),
+ (0x2F8C3, "M", "摩"),
+ (0x2F8C4, "M", "摾"),
+ (0x2F8C5, "M", "撝"),
+ (0x2F8C6, "M", "摷"),
+ (0x2F8C7, "M", "㩬"),
+ (0x2F8C8, "M", "敏"),
+ (0x2F8C9, "M", "敬"),
+ (0x2F8CA, "M", "𣀊"),
+ (0x2F8CB, "M", "旣"),
+ (0x2F8CC, "M", "書"),
+ (0x2F8CD, "M", "晉"),
+ (0x2F8CE, "M", "㬙"),
+ (0x2F8CF, "M", "暑"),
+ (0x2F8D0, "M", "㬈"),
+ (0x2F8D1, "M", "㫤"),
+ (0x2F8D2, "M", "冒"),
+ (0x2F8D3, "M", "冕"),
+ (0x2F8D4, "M", "最"),
+ (0x2F8D5, "M", "暜"),
+ (0x2F8D6, "M", "肭"),
+ (0x2F8D7, "M", "䏙"),
+ (0x2F8D8, "M", "朗"),
+ (0x2F8D9, "M", "望"),
+ (0x2F8DA, "M", "朡"),
+ (0x2F8DB, "M", "杞"),
+ (0x2F8DC, "M", "杓"),
+ ]
+
+
+def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F8DD, "M", "𣏃"),
+ (0x2F8DE, "M", "㭉"),
+ (0x2F8DF, "M", "柺"),
+ (0x2F8E0, "M", "枅"),
+ (0x2F8E1, "M", "桒"),
+ (0x2F8E2, "M", "梅"),
+ (0x2F8E3, "M", "𣑭"),
+ (0x2F8E4, "M", "梎"),
+ (0x2F8E5, "M", "栟"),
+ (0x2F8E6, "M", "椔"),
+ (0x2F8E7, "M", "㮝"),
+ (0x2F8E8, "M", "楂"),
+ (0x2F8E9, "M", "榣"),
+ (0x2F8EA, "M", "槪"),
+ (0x2F8EB, "M", "檨"),
+ (0x2F8EC, "M", "𣚣"),
+ (0x2F8ED, "M", "櫛"),
+ (0x2F8EE, "M", "㰘"),
+ (0x2F8EF, "M", "次"),
+ (0x2F8F0, "M", "𣢧"),
+ (0x2F8F1, "M", "歔"),
+ (0x2F8F2, "M", "㱎"),
+ (0x2F8F3, "M", "歲"),
+ (0x2F8F4, "M", "殟"),
+ (0x2F8F5, "M", "殺"),
+ (0x2F8F6, "M", "殻"),
+ (0x2F8F7, "M", "𣪍"),
+ (0x2F8F8, "M", "𡴋"),
+ (0x2F8F9, "M", "𣫺"),
+ (0x2F8FA, "M", "汎"),
+ (0x2F8FB, "M", "𣲼"),
+ (0x2F8FC, "M", "沿"),
+ (0x2F8FD, "M", "泍"),
+ (0x2F8FE, "M", "汧"),
+ (0x2F8FF, "M", "洖"),
+ (0x2F900, "M", "派"),
+ (0x2F901, "M", "海"),
+ (0x2F902, "M", "流"),
+ (0x2F903, "M", "浩"),
+ (0x2F904, "M", "浸"),
+ (0x2F905, "M", "涅"),
+ (0x2F906, "M", "𣴞"),
+ (0x2F907, "M", "洴"),
+ (0x2F908, "M", "港"),
+ (0x2F909, "M", "湮"),
+ (0x2F90A, "M", "㴳"),
+ (0x2F90B, "M", "滋"),
+ (0x2F90C, "M", "滇"),
+ (0x2F90D, "M", "𣻑"),
+ (0x2F90E, "M", "淹"),
+ (0x2F90F, "M", "潮"),
+ (0x2F910, "M", "𣽞"),
+ (0x2F911, "M", "𣾎"),
+ (0x2F912, "M", "濆"),
+ (0x2F913, "M", "瀹"),
+ (0x2F914, "M", "瀞"),
+ (0x2F915, "M", "瀛"),
+ (0x2F916, "M", "㶖"),
+ (0x2F917, "M", "灊"),
+ (0x2F918, "M", "災"),
+ (0x2F919, "M", "灷"),
+ (0x2F91A, "M", "炭"),
+ (0x2F91B, "M", "𠔥"),
+ (0x2F91C, "M", "煅"),
+ (0x2F91D, "M", "𤉣"),
+ (0x2F91E, "M", "熜"),
+ (0x2F91F, "M", "𤎫"),
+ (0x2F920, "M", "爨"),
+ (0x2F921, "M", "爵"),
+ (0x2F922, "M", "牐"),
+ (0x2F923, "M", "𤘈"),
+ (0x2F924, "M", "犀"),
+ (0x2F925, "M", "犕"),
+ (0x2F926, "M", "𤜵"),
+ (0x2F927, "M", "𤠔"),
+ (0x2F928, "M", "獺"),
+ (0x2F929, "M", "王"),
+ (0x2F92A, "M", "㺬"),
+ (0x2F92B, "M", "玥"),
+ (0x2F92C, "M", "㺸"),
+ (0x2F92E, "M", "瑇"),
+ (0x2F92F, "M", "瑜"),
+ (0x2F930, "M", "瑱"),
+ (0x2F931, "M", "璅"),
+ (0x2F932, "M", "瓊"),
+ (0x2F933, "M", "㼛"),
+ (0x2F934, "M", "甤"),
+ (0x2F935, "M", "𤰶"),
+ (0x2F936, "M", "甾"),
+ (0x2F937, "M", "𤲒"),
+ (0x2F938, "M", "異"),
+ (0x2F939, "M", "𢆟"),
+ (0x2F93A, "M", "瘐"),
+ (0x2F93B, "M", "𤾡"),
+ (0x2F93C, "M", "𤾸"),
+ (0x2F93D, "M", "𥁄"),
+ (0x2F93E, "M", "㿼"),
+ (0x2F93F, "M", "䀈"),
+ (0x2F940, "M", "直"),
+ (0x2F941, "M", "𥃳"),
+ ]
+
+
+def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F942, "M", "𥃲"),
+ (0x2F943, "M", "𥄙"),
+ (0x2F944, "M", "𥄳"),
+ (0x2F945, "M", "眞"),
+ (0x2F946, "M", "真"),
+ (0x2F948, "M", "睊"),
+ (0x2F949, "M", "䀹"),
+ (0x2F94A, "M", "瞋"),
+ (0x2F94B, "M", "䁆"),
+ (0x2F94C, "M", "䂖"),
+ (0x2F94D, "M", "𥐝"),
+ (0x2F94E, "M", "硎"),
+ (0x2F94F, "M", "碌"),
+ (0x2F950, "M", "磌"),
+ (0x2F951, "M", "䃣"),
+ (0x2F952, "M", "𥘦"),
+ (0x2F953, "M", "祖"),
+ (0x2F954, "M", "𥚚"),
+ (0x2F955, "M", "𥛅"),
+ (0x2F956, "M", "福"),
+ (0x2F957, "M", "秫"),
+ (0x2F958, "M", "䄯"),
+ (0x2F959, "M", "穀"),
+ (0x2F95A, "M", "穊"),
+ (0x2F95B, "M", "穏"),
+ (0x2F95C, "M", "𥥼"),
+ (0x2F95D, "M", "𥪧"),
+ (0x2F95F, "M", "竮"),
+ (0x2F960, "M", "䈂"),
+ (0x2F961, "M", "𥮫"),
+ (0x2F962, "M", "篆"),
+ (0x2F963, "M", "築"),
+ (0x2F964, "M", "䈧"),
+ (0x2F965, "M", "𥲀"),
+ (0x2F966, "M", "糒"),
+ (0x2F967, "M", "䊠"),
+ (0x2F968, "M", "糨"),
+ (0x2F969, "M", "糣"),
+ (0x2F96A, "M", "紀"),
+ (0x2F96B, "M", "𥾆"),
+ (0x2F96C, "M", "絣"),
+ (0x2F96D, "M", "䌁"),
+ (0x2F96E, "M", "緇"),
+ (0x2F96F, "M", "縂"),
+ (0x2F970, "M", "繅"),
+ (0x2F971, "M", "䌴"),
+ (0x2F972, "M", "𦈨"),
+ (0x2F973, "M", "𦉇"),
+ (0x2F974, "M", "䍙"),
+ (0x2F975, "M", "𦋙"),
+ (0x2F976, "M", "罺"),
+ (0x2F977, "M", "𦌾"),
+ (0x2F978, "M", "羕"),
+ (0x2F979, "M", "翺"),
+ (0x2F97A, "M", "者"),
+ (0x2F97B, "M", "𦓚"),
+ (0x2F97C, "M", "𦔣"),
+ (0x2F97D, "M", "聠"),
+ (0x2F97E, "M", "𦖨"),
+ (0x2F97F, "M", "聰"),
+ (0x2F980, "M", "𣍟"),
+ (0x2F981, "M", "䏕"),
+ (0x2F982, "M", "育"),
+ (0x2F983, "M", "脃"),
+ (0x2F984, "M", "䐋"),
+ (0x2F985, "M", "脾"),
+ (0x2F986, "M", "媵"),
+ (0x2F987, "M", "𦞧"),
+ (0x2F988, "M", "𦞵"),
+ (0x2F989, "M", "𣎓"),
+ (0x2F98A, "M", "𣎜"),
+ (0x2F98B, "M", "舁"),
+ (0x2F98C, "M", "舄"),
+ (0x2F98D, "M", "辞"),
+ (0x2F98E, "M", "䑫"),
+ (0x2F98F, "M", "芑"),
+ (0x2F990, "M", "芋"),
+ (0x2F991, "M", "芝"),
+ (0x2F992, "M", "劳"),
+ (0x2F993, "M", "花"),
+ (0x2F994, "M", "芳"),
+ (0x2F995, "M", "芽"),
+ (0x2F996, "M", "苦"),
+ (0x2F997, "M", "𦬼"),
+ (0x2F998, "M", "若"),
+ (0x2F999, "M", "茝"),
+ (0x2F99A, "M", "荣"),
+ (0x2F99B, "M", "莭"),
+ (0x2F99C, "M", "茣"),
+ (0x2F99D, "M", "莽"),
+ (0x2F99E, "M", "菧"),
+ (0x2F99F, "M", "著"),
+ (0x2F9A0, "M", "荓"),
+ (0x2F9A1, "M", "菊"),
+ (0x2F9A2, "M", "菌"),
+ (0x2F9A3, "M", "菜"),
+ (0x2F9A4, "M", "𦰶"),
+ (0x2F9A5, "M", "𦵫"),
+ (0x2F9A6, "M", "𦳕"),
+ (0x2F9A7, "M", "䔫"),
+ ]
+
+
+def _seg_82() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2F9A8, "M", "蓱"),
+ (0x2F9A9, "M", "蓳"),
+ (0x2F9AA, "M", "蔖"),
+ (0x2F9AB, "M", "𧏊"),
+ (0x2F9AC, "M", "蕤"),
+ (0x2F9AD, "M", "𦼬"),
+ (0x2F9AE, "M", "䕝"),
+ (0x2F9AF, "M", "䕡"),
+ (0x2F9B0, "M", "𦾱"),
+ (0x2F9B1, "M", "𧃒"),
+ (0x2F9B2, "M", "䕫"),
+ (0x2F9B3, "M", "虐"),
+ (0x2F9B4, "M", "虜"),
+ (0x2F9B5, "M", "虧"),
+ (0x2F9B6, "M", "虩"),
+ (0x2F9B7, "M", "蚩"),
+ (0x2F9B8, "M", "蚈"),
+ (0x2F9B9, "M", "蜎"),
+ (0x2F9BA, "M", "蛢"),
+ (0x2F9BB, "M", "蝹"),
+ (0x2F9BC, "M", "蜨"),
+ (0x2F9BD, "M", "蝫"),
+ (0x2F9BE, "M", "螆"),
+ (0x2F9BF, "M", "䗗"),
+ (0x2F9C0, "M", "蟡"),
+ (0x2F9C1, "M", "蠁"),
+ (0x2F9C2, "M", "䗹"),
+ (0x2F9C3, "M", "衠"),
+ (0x2F9C4, "M", "衣"),
+ (0x2F9C5, "M", "𧙧"),
+ (0x2F9C6, "M", "裗"),
+ (0x2F9C7, "M", "裞"),
+ (0x2F9C8, "M", "䘵"),
+ (0x2F9C9, "M", "裺"),
+ (0x2F9CA, "M", "㒻"),
+ (0x2F9CB, "M", "𧢮"),
+ (0x2F9CC, "M", "𧥦"),
+ (0x2F9CD, "M", "䚾"),
+ (0x2F9CE, "M", "䛇"),
+ (0x2F9CF, "M", "誠"),
+ (0x2F9D0, "M", "諭"),
+ (0x2F9D1, "M", "變"),
+ (0x2F9D2, "M", "豕"),
+ (0x2F9D3, "M", "𧲨"),
+ (0x2F9D4, "M", "貫"),
+ (0x2F9D5, "M", "賁"),
+ (0x2F9D6, "M", "贛"),
+ (0x2F9D7, "M", "起"),
+ (0x2F9D8, "M", "𧼯"),
+ (0x2F9D9, "M", "𠠄"),
+ (0x2F9DA, "M", "跋"),
+ (0x2F9DB, "M", "趼"),
+ (0x2F9DC, "M", "跰"),
+ (0x2F9DD, "M", "𠣞"),
+ (0x2F9DE, "M", "軔"),
+ (0x2F9DF, "M", "輸"),
+ (0x2F9E0, "M", "𨗒"),
+ (0x2F9E1, "M", "𨗭"),
+ (0x2F9E2, "M", "邔"),
+ (0x2F9E3, "M", "郱"),
+ (0x2F9E4, "M", "鄑"),
+ (0x2F9E5, "M", "𨜮"),
+ (0x2F9E6, "M", "鄛"),
+ (0x2F9E7, "M", "鈸"),
+ (0x2F9E8, "M", "鋗"),
+ (0x2F9E9, "M", "鋘"),
+ (0x2F9EA, "M", "鉼"),
+ (0x2F9EB, "M", "鏹"),
+ (0x2F9EC, "M", "鐕"),
+ (0x2F9ED, "M", "𨯺"),
+ (0x2F9EE, "M", "開"),
+ (0x2F9EF, "M", "䦕"),
+ (0x2F9F0, "M", "閷"),
+ (0x2F9F1, "M", "𨵷"),
+ (0x2F9F2, "M", "䧦"),
+ (0x2F9F3, "M", "雃"),
+ (0x2F9F4, "M", "嶲"),
+ (0x2F9F5, "M", "霣"),
+ (0x2F9F6, "M", "𩅅"),
+ (0x2F9F7, "M", "𩈚"),
+ (0x2F9F8, "M", "䩮"),
+ (0x2F9F9, "M", "䩶"),
+ (0x2F9FA, "M", "韠"),
+ (0x2F9FB, "M", "𩐊"),
+ (0x2F9FC, "M", "䪲"),
+ (0x2F9FD, "M", "𩒖"),
+ (0x2F9FE, "M", "頋"),
+ (0x2FA00, "M", "頩"),
+ (0x2FA01, "M", "𩖶"),
+ (0x2FA02, "M", "飢"),
+ (0x2FA03, "M", "䬳"),
+ (0x2FA04, "M", "餩"),
+ (0x2FA05, "M", "馧"),
+ (0x2FA06, "M", "駂"),
+ (0x2FA07, "M", "駾"),
+ (0x2FA08, "M", "䯎"),
+ (0x2FA09, "M", "𩬰"),
+ (0x2FA0A, "M", "鬒"),
+ (0x2FA0B, "M", "鱀"),
+ (0x2FA0C, "M", "鳽"),
+ ]
+
+
+def _seg_83() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x2FA0D, "M", "䳎"),
+ (0x2FA0E, "M", "䳭"),
+ (0x2FA0F, "M", "鵧"),
+ (0x2FA10, "M", "𪃎"),
+ (0x2FA11, "M", "䳸"),
+ (0x2FA12, "M", "𪄅"),
+ (0x2FA13, "M", "𪈎"),
+ (0x2FA14, "M", "𪊑"),
+ (0x2FA15, "M", "麻"),
+ (0x2FA16, "M", "䵖"),
+ (0x2FA17, "M", "黹"),
+ (0x2FA18, "M", "黾"),
+ (0x2FA19, "M", "鼅"),
+ (0x2FA1A, "M", "鼏"),
+ (0x2FA1B, "M", "鼖"),
+ (0x2FA1C, "M", "鼻"),
+ (0x2FA1D, "M", "𪘀"),
+ (0x2FA1E, "X"),
+ (0x30000, "V"),
+ (0x3134B, "X"),
+ (0x31350, "V"),
+ (0x323B0, "X"),
+ (0xE0100, "I"),
+ (0xE01F0, "X"),
+ ]
+
+
+uts46data = tuple(
+ _seg_0()
+ + _seg_1()
+ + _seg_2()
+ + _seg_3()
+ + _seg_4()
+ + _seg_5()
+ + _seg_6()
+ + _seg_7()
+ + _seg_8()
+ + _seg_9()
+ + _seg_10()
+ + _seg_11()
+ + _seg_12()
+ + _seg_13()
+ + _seg_14()
+ + _seg_15()
+ + _seg_16()
+ + _seg_17()
+ + _seg_18()
+ + _seg_19()
+ + _seg_20()
+ + _seg_21()
+ + _seg_22()
+ + _seg_23()
+ + _seg_24()
+ + _seg_25()
+ + _seg_26()
+ + _seg_27()
+ + _seg_28()
+ + _seg_29()
+ + _seg_30()
+ + _seg_31()
+ + _seg_32()
+ + _seg_33()
+ + _seg_34()
+ + _seg_35()
+ + _seg_36()
+ + _seg_37()
+ + _seg_38()
+ + _seg_39()
+ + _seg_40()
+ + _seg_41()
+ + _seg_42()
+ + _seg_43()
+ + _seg_44()
+ + _seg_45()
+ + _seg_46()
+ + _seg_47()
+ + _seg_48()
+ + _seg_49()
+ + _seg_50()
+ + _seg_51()
+ + _seg_52()
+ + _seg_53()
+ + _seg_54()
+ + _seg_55()
+ + _seg_56()
+ + _seg_57()
+ + _seg_58()
+ + _seg_59()
+ + _seg_60()
+ + _seg_61()
+ + _seg_62()
+ + _seg_63()
+ + _seg_64()
+ + _seg_65()
+ + _seg_66()
+ + _seg_67()
+ + _seg_68()
+ + _seg_69()
+ + _seg_70()
+ + _seg_71()
+ + _seg_72()
+ + _seg_73()
+ + _seg_74()
+ + _seg_75()
+ + _seg_76()
+ + _seg_77()
+ + _seg_78()
+ + _seg_79()
+ + _seg_80()
+ + _seg_81()
+ + _seg_82()
+ + _seg_83()
+) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...]
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/METADATA
new file mode 100644
index 0000000..11ba9ff
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/METADATA
@@ -0,0 +1,40 @@
+Metadata-Version: 2.1
+Name: ilorest
+Version: 7.0.0.0
+Summary: HPE iLORest Tool
+Home-page: https://github.com/HewlettPackard/python-redfish-utility
+Author: Hewlett Packard Enterprise
+Author-email: rajeevalochana.kallur@hpe.com
+Keywords: Hewlett Packard Enterprise
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Topic :: Communications
+Requires-Dist: urllib3 >=1.26.2
+Requires-Dist: pyaes >=1.6.1
+Requires-Dist: colorama >=0.4.4
+Requires-Dist: jsonpointer >=2.0
+Requires-Dist: six >=1.15.0
+Requires-Dist: ply
+Requires-Dist: requests
+Requires-Dist: decorator >=4.4.2
+Requires-Dist: jsonpatch >=1.28
+Requires-Dist: jsonpath-rw >=1.4.0
+Requires-Dist: jsondiff >=1.2.0
+Requires-Dist: tabulate >=0.8.7
+Requires-Dist: prompt-toolkit
+Requires-Dist: certifi >=2020.12.5
+Requires-Dist: wcwidth >=0.2.5
+Requires-Dist: pyudev
+Requires-Dist: future
+Requires-Dist: python-ilorest-library >=7.0.0.0
+Requires-Dist: setproctitle >=1.1.8 ; platform_system == "Linux"
+Requires-Dist: pywin32 ; platform_system == "Windows"
+Requires-Dist: enum ; python_version <= "2.7.19"
+Requires-Dist: futures ; python_version <= "2.7.19"
+
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/RECORD
new file mode 100644
index 0000000..bb49051
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/RECORD
@@ -0,0 +1,326 @@
+../../../bin/ilorest,sha256=PLP7vUiznKIPPNKauuYE1AkTyXlCYO0Ko25uS3mQ238,264
+../../../logging_config.json,sha256=H_vbfeKiyQPNLkCJ_p1iAcvfV_QlUWcq1qJqs2p-_EE,1171
+ilorest-7.0.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+ilorest-7.0.0.0.dist-info/METADATA,sha256=s851iAC0ePMC6Ns0aK9K6EB57roYOVmwGTzk5BWzPIM,1531
+ilorest-7.0.0.0.dist-info/RECORD,,
+ilorest-7.0.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest-7.0.0.0.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
+ilorest-7.0.0.0.dist-info/entry_points.txt,sha256=hZK831ZpPnfSjuVPord6I0D4r2NEzv0XO7AFKKd9UqU,56
+ilorest-7.0.0.0.dist-info/top_level.txt,sha256=PBm3JtzoqCberFEbu0RbzOYwiAVP05Exdx9afhHPAfg,14
+ilorest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/__pycache__/__init__.cpython-310.pyc,,
+ilorest/__pycache__/cliutils.cpython-310.pyc,,
+ilorest/__pycache__/debug_utils.cpython-310.pyc,,
+ilorest/__pycache__/logging_config_path.cpython-310.pyc,,
+ilorest/__pycache__/rdmc.cpython-310.pyc,,
+ilorest/__pycache__/rdmc_base_classes.cpython-310.pyc,,
+ilorest/__pycache__/rdmc_helper.cpython-310.pyc,,
+ilorest/__pycache__/security_masking.cpython-310.pyc,,
+ilorest/__pycache__/versioning.cpython-310.pyc,,
+ilorest/chiflibrary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/chiflibrary/__pycache__/__init__.cpython-310.pyc,,
+ilorest/chiflibrary/arm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/chiflibrary/arm/__pycache__/__init__.cpython-310.pyc,,
+ilorest/chiflibrary/arm/ilorest_chif.so,sha256=6kW4rWEmFWELZOkb30ezYXbBPMxZCjqPGo9CdbGRU28,9423008
+ilorest/chiflibrary/ilorest_chif.dll,sha256=1PLq8d7u7on53n8FcLXDFu_rLYI5-XQtkB3AiMQAdUI,3580048
+ilorest/chiflibrary/ilorest_chif.so,sha256=TnyuB9O791U3Pa485Hm4Zn_yEDvRxfMzyLLhktyCz0o,9918784
+ilorest/cliutils.py,sha256=Rgcu2Iqu-PK3bSDpQgkisDnFIMsPGjuyw7y7aARFhkg,6837
+ilorest/config/__init__.py,sha256=RWSNi8PsptVoQ4E3CvFKPU0RBQ7gSRjKu1QsDG9U97Q,30
+ilorest/config/__pycache__/__init__.cpython-310.pyc,,
+ilorest/config/__pycache__/config.cpython-310.pyc,,
+ilorest/config/__pycache__/rdmc_config.cpython-310.pyc,,
+ilorest/config/config.py,sha256=917hDTAOLONGV6iQtMG54kuZFhUs2fzIqNtiUDGwboA,4603
+ilorest/config/rdmc_config.py,sha256=BWGp0GJulWPLkl81DPOW6utF5M4zexCIF9wZ5E3VF1w,8356
+ilorest/debug_utils.py,sha256=9RRfnfqpcWUyFbuy5EEabmkWpkzLkZdgdEnLbirvOlw,15914
+ilorest/extensions/BIOS_COMMANDS/BiosDefaultsCommand.py,sha256=svj96UdwlANpHRDJVtBxwLbejRNSfpQg7Vr56FKM73Q,5944
+ilorest/extensions/BIOS_COMMANDS/BootOrderCommand.py,sha256=Of_PGsNUqE8y9UziGNBV4zEzJKwaQ27pHeE20wgOKiQ,30819
+ilorest/extensions/BIOS_COMMANDS/IscsiConfigCommand.py,sha256=UGQew-apGEFxVzoTo-X9SmfQ2TX2GOymzr5CRNy8ZqM,41079
+ilorest/extensions/BIOS_COMMANDS/SetPasswordCommand.py,sha256=rmVGOfuCo1w6is2ZmUEPySlW02eSFiJWYt4IuhYKJcg,8101
+ilorest/extensions/BIOS_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/BIOS_COMMANDS/__pycache__/BiosDefaultsCommand.cpython-310.pyc,,
+ilorest/extensions/BIOS_COMMANDS/__pycache__/BootOrderCommand.cpython-310.pyc,,
+ilorest/extensions/BIOS_COMMANDS/__pycache__/IscsiConfigCommand.cpython-310.pyc,,
+ilorest/extensions/BIOS_COMMANDS/__pycache__/SetPasswordCommand.cpython-310.pyc,,
+ilorest/extensions/BIOS_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/CommitCommand.py,sha256=J0TGidmyDr6CfAWQ45Py5MgkSdyupPU4sfA47ifvlEs,4710
+ilorest/extensions/COMMANDS/DetectiLOCommand.py,sha256=5-qSebt-yqXrQsdyiEZpQp8qt87ke67qJlNRmmh3hYc,7944
+ilorest/extensions/COMMANDS/GetCommand.py,sha256=_Krup0OgHyera46nT7VmS1ndSBvwYnL79d4Eh46Z6yU,21015
+ilorest/extensions/COMMANDS/InfoCommand.py,sha256=ooEooTW1r8gs3ZMIfsZaqdTxmQOlx-ib3JcoE0csdts,6614
+ilorest/extensions/COMMANDS/ListCommand.py,sha256=6d7PMUkQgJTfzf9Pir14KKN0_k0ZvAHeUm_VS1-czBo,6696
+ilorest/extensions/COMMANDS/LoadCommand.py,sha256=hPIA8uZfiDmhwbICM6TAmnqfkWnK25F-e2Fmb7AOugo,20867
+ilorest/extensions/COMMANDS/LoginCommand.py,sha256=5FfXEnCxEzFo7mon-zfoVYPfk8VYo_HggE-6HUDk40g,23852
+ilorest/extensions/COMMANDS/LogoutCommand.py,sha256=ugSDQ82L2iYkHg2HotER3Bib2y19rmuRiDdZIhjIHi8,2486
+ilorest/extensions/COMMANDS/PendingChangesCommand.py,sha256=3vEasrpdHLpMulMd2JPX1bH5A8gjas5m4aInySEMKdE,7116
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/ExitCommand.py,sha256=La8VTu8qqmHflAn5cHik5KTffXphTFOW0N2Hju79cVg,2635
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/HelpCommand.py,sha256=QHH9qF8KCPDdQu1Ed-FmXn06TL5Vvug2Nwqc-FPySyo,4037
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/ExitCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/HelpCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/ResultsCommand.py,sha256=BrVezaY0o0WtS490gLUZWKENa0UCAA1oK8l5D3zBxUk,5809
+ilorest/extensions/COMMANDS/SaveCommand.py,sha256=kYNkxqQ6bj1I0gg62S-ACCBlldbkLdXQdqaQJaUUUQI,16873
+ilorest/extensions/COMMANDS/SelectCommand.py,sha256=6E_x97WCMELf8Xp9vLWMELtSLtPDG927J_CUOD72wGE,5069
+ilorest/extensions/COMMANDS/SetCommand.py,sha256=7DlOvsdgIqr0LMfkClyZHxnv5GyCzJ-b49BqGU1-rAs,20664
+ilorest/extensions/COMMANDS/StatusCommand.py,sha256=o1R48UQDtyAMlbwR8tEwadk8FT9w_ToqIo9VWHsNc7g,8037
+ilorest/extensions/COMMANDS/TypesCommand.py,sha256=-uG2n1vUJLGJv30-LkjHxUaULY_eiRkWkj-pnL739Tg,3883
+ilorest/extensions/COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/COMMANDS/__pycache__/CommitCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/DetectiLOCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/GetCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/InfoCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/ListCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/LoadCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/LoginCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/LogoutCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/PendingChangesCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/ResultsCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/SaveCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/SelectCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/SetCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/StatusCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/TypesCommand.cpython-310.pyc,,
+ilorest/extensions/COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/AdvancedPmmConfigCommand.py,sha256=_MbN5SP1ehHzFWUjVy24UgO22hStFVht2w8eoKHGtNY,18808
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ApplyPmemConfigCommand.py,sha256=gmQyD-z60tPvFsZZazE9z2WmFFc3b18IHe-VwNZXqu8,14398
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ClearPendingConfigCommand.py,sha256=1ChJAb_VDyoL94x9Njw31REpqcj8bVuwa0IbZrDFDKU,4827
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/DisplaySecurityStateCommand.py,sha256=5FaeXgJueiWojgMKwcf5V1g5XDUVpa4PcIYAPCAtfss,3996
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemCommand.py,sha256=9n4_zCDmksNek0Ld7qqtGw55ddNuI2dUTxpMwS89SMw,17737
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemPendingConfigCommand.py,sha256=ncRm7Dc2-wHpxovPA4H2yXDWK8nTjjau2uh3YpyvOjM,6303
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowRecommendedConfigCommand.py,sha256=i3Sx9FKMiNhi2xUIqWeWv6T72pd_9SY0apa2Y3BdvIg,7216
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/AdvancedPmmConfigCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ApplyPmemConfigCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ClearPendingConfigCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/DisplaySecurityStateCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemPendingConfigCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowRecommendedConfigCommand.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/DisplayHelpers.py,sha256=HFeOk7PR1_upJw7DiQ7yt_9oIggkZWwax7RBW9HcYlA,7133
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/Mapper.py,sha256=B_Xb2VpBuRfW5AVqQd_B3szbpsNc8I1jAJlmfb34EjI,4326
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/MapperRenderers.py,sha256=-_SimQIsIrG2eUPJeulacu7x9_pwgliwQ_SxSr6A2SI,18668
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/PmemHelpers.py,sha256=aFjrhiKmGw6U8DGosep9E3yORx13GR5tFhsh4IYqOKw,4802
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/RestHelpers.py,sha256=_f935lAQG_wjLADx5pwUtMO6HYSVJH1gBq4K7r9sjmA,11361
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/DisplayHelpers.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/Mapper.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/MapperRenderers.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/PmemHelpers.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/RestHelpers.cpython-310.pyc,,
+ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/RawDeleteCommand.py,sha256=YyPKH1_XUyzlZFZg68OmSy8Set8xq2mMDnnyiD7tyv0,6927
+ilorest/extensions/RAW_COMMANDS/RawGetCommand.py,sha256=8OnhE4t1MVs6LnJXZZI5gAystihzJ-uyDA0DqzyKogc,10936
+ilorest/extensions/RAW_COMMANDS/RawHeadCommand.py,sha256=8-SioIss_02rZXr112ofFJL0bWZv-llC74kJkt2Hs9o,4728
+ilorest/extensions/RAW_COMMANDS/RawPatchCommand.py,sha256=xIxSpIzdUEn2lxzC4KKMi8PzyoKkXTIUhADtkPpCcVU,8965
+ilorest/extensions/RAW_COMMANDS/RawPostCommand.py,sha256=noWsPUSAG0o8sLkOt8_FI_qVSprOwN8uePU37JsWeqc,9664
+ilorest/extensions/RAW_COMMANDS/RawPutCommand.py,sha256=6XlDQpPhdoW9Z7oRWOirKzjs1HEIPknG7KxqcZG2RYI,9155
+ilorest/extensions/RAW_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawDeleteCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawGetCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawHeadCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawPatchCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawPostCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/RawPutCommand.cpython-310.pyc,,
+ilorest/extensions/RAW_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/ClearControllerConfigCommand.py,sha256=oQyRUERDeRbUWLmNq_jlo9aPUo2MKZzcv2k69cNpU0E,6710
+ilorest/extensions/SMART_ARRAY_COMMANDS/CreateVolumeCommand.py,sha256=sTBWpFeFJd--oktN9NMtr8QV7cCF0BtomzOh7RMaSnU,51544
+ilorest/extensions/SMART_ARRAY_COMMANDS/DeleteVolumeCommand.py,sha256=mbyn6Ln7VtnwkuJkzC6U_IWZR0jo_nlmXowgffRLn7I,18602
+ilorest/extensions/SMART_ARRAY_COMMANDS/DriveSanitizeCommand.py,sha256=O1HMG0C9TQrAIEIV10pJU9Fkbtc5ucBdpdZU2gV9BDs,25977
+ilorest/extensions/SMART_ARRAY_COMMANDS/FactoryResetControllerCommand.py,sha256=A5AaSt9jLwOzqLUcDi_5ymWRJYzP4K9OyeljQg0MO-U,9361
+ilorest/extensions/SMART_ARRAY_COMMANDS/StorageControllerCommand.py,sha256=p8qtXCQIPMd3Q7J7bJXls4eUWeyqyQj34zDz0dsNrEg,115762
+ilorest/extensions/SMART_ARRAY_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/ClearControllerConfigCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/CreateVolumeCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DeleteVolumeCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DriveSanitizeCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/FactoryResetControllerCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/StorageControllerCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/SMART_NIC_COMMANDS/SmartNicCommand.py,sha256=g6gWnnCqpgaKyeWUrEyuG07MYEz-y0M5dim3dZpJHhE,30343
+ilorest/extensions/SMART_NIC_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/SmartNicCommand.cpython-310.pyc,,
+ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/__init__.py,sha256=3rsbSdxIc6lIarsPy7XQnDg8DIL6aTvAVMaq-LolXRQ,899
+ilorest/extensions/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/AHSdiagCommand.py,sha256=Wt_BUevii30PhNCeJ8bpscbf4RGHOkQAJQasb-5G2oE,16463
+ilorest/extensions/_hidden_commands/AutomaticTestingCommand.py,sha256=G1Nz1_dpLK3DIgc7phkxq9NxMyKWbVVj-zmpOgKjOWM,122529
+ilorest/extensions/_hidden_commands/ExportAllConfigCommand.py,sha256=L3R3gZCGLmIMZHooPOvBq9W9-AGGWAB5jZNO5oS0vJk,3569
+ilorest/extensions/_hidden_commands/GetInventoryCommand.py,sha256=9Myn7vjfu03cxK0udwyflACogZWJ3BVzz2dvLn53LYs,12647
+ilorest/extensions/_hidden_commands/HpGooeyCommand.py,sha256=JITcGsZhNAM_9xvRDwc_RtcX0uMAi2c3ZiqP7vxTC8M,40801
+ilorest/extensions/_hidden_commands/ISToolCommand.py,sha256=AI9QUDXDG5wqYYBhAJ-IS39YJcITZtHNiSv-pKvP9Tw,6502
+ilorest/extensions/_hidden_commands/MonolithCommand.py,sha256=FzgTVvv7Bg0ma_0wrUyCn3md7zcIPCzU2SOmeY6k8W8,4023
+ilorest/extensions/_hidden_commands/SMBiosCommand.py,sha256=f9olt0JchtfUEglSrRSi3QoiLEBvNiUn3uAsCTzZlfg,6753
+ilorest/extensions/_hidden_commands/SecurityStatusCommand.py,sha256=dLH0PZ_9iN5QOxZSo99GOCFYjCutoktxUohJ56B9BEw,6798
+ilorest/extensions/_hidden_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/_hidden_commands/__pycache__/AHSdiagCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/AutomaticTestingCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/ExportAllConfigCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/GetInventoryCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/HpGooeyCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/ISToolCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/MonolithCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/SMBiosCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/SecurityStatusCommand.cpython-310.pyc,,
+ilorest/extensions/_hidden_commands/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/AppAccountCommand.py,sha256=txmffIVmyXwl5XfrHuLU_svv18dNJS7ULhYgMR1DEn0,34957
+ilorest/extensions/iLO_COMMANDS/CertificateCommand.py,sha256=p2r3sjkoIAosWmW1bejQCRi-93DLalZ3nau-jzVNXlI,57083
+ilorest/extensions/iLO_COMMANDS/ChannelInterfaceCommand.py,sha256=Kb9nbKxegOaQ6_UDaPwgr7LvY7ZfdzV5cdpwbDl9bBA,4358
+ilorest/extensions/iLO_COMMANDS/ClearRestApiStateCommand.py,sha256=wmKmlkvtqkA_5bf5V_geSFeguL7vAiWJaVYNY8P5JYE,4331
+ilorest/extensions/iLO_COMMANDS/ComputeOpsManagementCommand.py,sha256=hAl5FXeWRy4ScNMg4zCbXaIBUK6JAFSkzzWMgGZMh-I,147029
+ilorest/extensions/iLO_COMMANDS/DirectoryCommand.py,sha256=k44ai8_9aqXMcW3dstSoIsFogxxqpMNGJ4SWBQIv69s,38752
+ilorest/extensions/iLO_COMMANDS/DisableIloFunctionalityCommand.py,sha256=AEzYrIGl0b7mcHGTogxykJr4hmmAY_6E-mMKiTIHN5g,7201
+ilorest/extensions/iLO_COMMANDS/ESKMCommand.py,sha256=5be1-WdDpzSnZgXzeLDUBPElOV5NjeGKYxCEaP8RNFU,4119
+ilorest/extensions/iLO_COMMANDS/EthernetCommand.py,sha256=vxI4_VQvmpfSg5uSKGeefGnsNpcNoyjjIXn6L0zy3cM,60484
+ilorest/extensions/iLO_COMMANDS/FactoryDefaultsCommand.py,sha256=T0UXwnCITMwN_vkrSPPEwyQT_7V401ZnQaEBvCe2bbs,4364
+ilorest/extensions/iLO_COMMANDS/FirmwareIntegrityCheckCommand.py,sha256=YWz37yR4W8_tdNRndBc20HZmZZlvdZCioJvMVvfDxGk,6047
+ilorest/extensions/iLO_COMMANDS/FirmwareUpdateCommand.py,sha256=yVGpgz_pohTKeMGhErq9aznzg3kESSoRrUrNWADW3tY,12031
+ilorest/extensions/iLO_COMMANDS/IPProfilesCommand.py,sha256=amjTIzQ4kC5AB8T7GDcC1qxg_mFQ6XsSpQszToSNwh4,25536
+ilorest/extensions/iLO_COMMANDS/IloAccountsCommand.py,sha256=-n_Aesw55P99s2BcFZXwozPytT5pFQi-MZHmvY-BU4I,33192
+ilorest/extensions/iLO_COMMANDS/IloBackupRestoreCommand.py,sha256=XNj68Z6rDjASJjdkyg9CIQnMXBqmCqzIVI3XabRPu7c,11422
+ilorest/extensions/iLO_COMMANDS/IloFederationCommand.py,sha256=TV6kypUB7oGyF0ZqBjaCO7PY2ChzRtZQC8ePstOEfNQ,21571
+ilorest/extensions/iLO_COMMANDS/IloLicenseCommand.py,sha256=O5yBH8m68Eh6ZqC5u_aqtiGYnLHuj8I99DalnaXvf5E,11629
+ilorest/extensions/iLO_COMMANDS/IloResetCommand.py,sha256=eBaa6yj0CazGjgYs2eGinbTl6q5AMVqGxdfNz87k5mc,4368
+ilorest/extensions/iLO_COMMANDS/LogconfigCommand.py,sha256=ZGUi-lzmU6TY5rFlrGTOyNwEeyGxqB6FIdXpJILDZxA,38374
+ilorest/extensions/iLO_COMMANDS/OneButtonEraseCommand.py,sha256=4nx5oS9348tZJcmgFJQMYrFi8T-x8S1RitnanR-9TXQ,10970
+ilorest/extensions/iLO_COMMANDS/RebootCommand.py,sha256=yLFILZyMUjSsoKMHucyag7UU1DJiOmTUR6perlFeOws,10817
+ilorest/extensions/iLO_COMMANDS/SendTestCommand.py,sha256=LIwsGy2e7BfcXuDzPeNXuY_27T574JWtJB8-ETiAL6c,5255
+ilorest/extensions/iLO_COMMANDS/ServerCloneCommand.py,sha256=gVjdEjkGj-9vCzc49Pp6BmPeDnuXRTrgR7-y9P6uEHs,116013
+ilorest/extensions/iLO_COMMANDS/ServerConfigurationLockCommand.py,sha256=MGLk9vnUbPxnLjh9XYp6flR0Q40kkGGvpcaNCd4TDkw,11946
+ilorest/extensions/iLO_COMMANDS/ServerInfoCommand.py,sha256=6htgW9ct9pdV91ejMFVScWYllhHru8iihFiPWcb-xi4,55364
+ilorest/extensions/iLO_COMMANDS/ServerStateCommand.py,sha256=6N-RN5M8TjrKH-m4__ftlsMZ5siObtxXI2NautOr7gs,3492
+ilorest/extensions/iLO_COMMANDS/ServerlogsCommand.py,sha256=gEbmQLYiVJFwBo0wlrwvVaMvmKPvVeTabUfGkhASPRU,57706
+ilorest/extensions/iLO_COMMANDS/SetTwoFactorAuthenticationCommand.py,sha256=szF7k2wvXks_U3Skrbj0eozBclzpvDdmMXyxB--5k8Q,15395
+ilorest/extensions/iLO_COMMANDS/SigRecomputeCommand.py,sha256=JRwmOlRZppyfbzr3BkqrHFG181D5cb7K40N5IwdVBrE,3540
+ilorest/extensions/iLO_COMMANDS/SingleSignOnCommand.py,sha256=8G1QVrr1TUgpay8juPt5mbo18qK_tE7Tlu6BVrwf3h0,7232
+ilorest/extensions/iLO_COMMANDS/VirtualMediaCommand.py,sha256=EdB9t86MvdOIYPLHL_twdiRCVJkaWiCGEe6f8QkGysQ,16503
+ilorest/extensions/iLO_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/iLO_COMMANDS/__pycache__/AppAccountCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/CertificateCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ChannelInterfaceCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ClearRestApiStateCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ComputeOpsManagementCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/DirectoryCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/DisableIloFunctionalityCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ESKMCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/EthernetCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/FactoryDefaultsCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/FirmwareIntegrityCheckCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/FirmwareUpdateCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IPProfilesCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IloAccountsCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IloBackupRestoreCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IloFederationCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IloLicenseCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/IloResetCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/LogconfigCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/OneButtonEraseCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/RebootCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/SendTestCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ServerCloneCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ServerConfigurationLockCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ServerInfoCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ServerStateCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/ServerlogsCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/SetTwoFactorAuthenticationCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/SigRecomputeCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/SingleSignOnCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/VirtualMediaCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/__init__.py,sha256=NjC5YIsmVXxSvGcCxULFdg3yttoi3vACJ8CkSAbpIw8,430
+ilorest/extensions/iLO_COMMANDS/com_helpers/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/__pycache__/config_resolver.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/__pycache__/ilo_connector.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/__pycache__/network_configurator.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/__pycache__/progress_tracker.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/com_helpers/config_resolver.py,sha256=Q1KUwNP6Xkcq9V-Bn2NpcUZs1yrT6wjPLviXxPTtu2g,5945
+ilorest/extensions/iLO_COMMANDS/com_helpers/ilo_connector.py,sha256=XX5_WjwYXW1-f7ER39m2pp-HUzzazuVKDtI0gWTvWkM,8558
+ilorest/extensions/iLO_COMMANDS/com_helpers/network_configurator.py,sha256=yZkvVEmE3j4MMKaiuLV4-UikXSGwyKNszj1UloJvR4A,9572
+ilorest/extensions/iLO_COMMANDS/com_helpers/progress_tracker.py,sha256=J-y-8Tk3JAyoEg3nv4lZmW9u2TTXfIuvw-kcos7k2JI,3287
+ilorest/extensions/iLO_COMMANDS/data/InputTemplate.py,sha256=9QlHIW3VnzSw5rJ2yyInz-BOHMpFSPknKqS5lzkF818,1578
+ilorest/extensions/iLO_COMMANDS/data/__init__.py,sha256=ffkT9zzMfrTDZcngRQpum3EQB_zPs_ZUdGn3OkB2Xwo,36
+ilorest/extensions/iLO_COMMANDS/data/__pycache__/InputTemplate.cpython-310.pyc,,
+ilorest/extensions/iLO_COMMANDS/data/__pycache__/__init__.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/DeleteComponentCommand.py,sha256=4aWxZO-f34vdocbJEA8v4etDlpBpEHTlkofs4XYF4kE,6160
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/DownloadComponentCommand.py,sha256=cunaJk5Ek1q5Ai0hbb6Egv4oa8IlFIo1L44_ZWV1P4I,8165
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/FwpkgCommand.py,sha256=VNN6AMF80BDnXQqJ-8zrgvtMBtyPGnUizTYx_Ns4D-M,32483
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/InstallSetCommand.py,sha256=s3bFatYA0Y4UFbxujBsMNJ-zP-_DgpZRiu_-IQWE0EI,23843
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/ListComponentCommand.py,sha256=-nv0lClwTk9X3pVegRE1Azos_LCz4Vd9zkm6nqiH630,4576
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/MaintenanceWindowCommand.py,sha256=-mwcLxQJnB_1j9IkXex3NLVh05kTsFRyQzYJmjDeAN8,12486
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/MakeInstallSetCommand.py,sha256=AeGuqvFK5-GwYpFiqtmhWuivlqqxz9hnJuJ_S8mCRB0,13167
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/UpdateTaskQueueCommand.py,sha256=CFYps3nivVYHf0BjnRxw-Y9JGe2HaoZh7kMSRx8iqtU,16480
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/UploadComponentCommand.py,sha256=LftdjEr2zNmKWC66LmCXOU1Qv71fDwHt3-DEJ5B7Sgs,34071
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/DeleteComponentCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/DownloadComponentCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/FwpkgCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/InstallSetCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/ListComponentCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/MaintenanceWindowCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/MakeInstallSetCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/UpdateTaskQueueCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/UploadComponentCommand.cpython-310.pyc,,
+ilorest/extensions/iLO_REPOSITORY_COMMANDS/__pycache__/__init__.cpython-310.pyc,,
+ilorest/logging_config_path.py,sha256=BHbopU-MEoU9Kac6viEEX7sFp0h81P7BGZI334dIGbM,1050
+ilorest/rdmc.py,sha256=0wkcDpKVwpSmfpE1XPmGGIc-kIaAhTayG3WpZnP2ckU,52247
+ilorest/rdmc_base_classes.py,sha256=NoZj8fW7H8--2Jk8bk3sjomu9gf8w8IcXw8BAJUC2BQ,21499
+ilorest/rdmc_helper.py,sha256=sbS4pycbM0JATEUG3HAFUYzWvL6I9kRc4Q6HU9aE-L0,45253
+ilorest/security_masking.py,sha256=9D5BrLASpjTXZn3Ds0CdsRGLWRyshRE0mZbkVpcZCGg,23386
+ilorest/versioning.py,sha256=bUNIH5lUhefdirBDtDo-heXTuQumfHDFO0rKbJ2cTl4,865
+tests/pmemtests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/pmemtests/__pycache__/__init__.cpython-310.pyc,,
+tests/pmemtests/__pycache__/conftest.cpython-310.pyc,,
+tests/pmemtests/conftest.py,sha256=66bT2_cGlPmGiQ28N1jXe1ia7M_weILFEqNSCXTH8F8,3387
+tests/pmemtests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/pmemtests/functional/__pycache__/__init__.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/conftest.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/ilomock.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_all_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_applydcpmmconfig.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_cleardcpmmpendingconfig.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_config_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_device_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_display_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_logical_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_showdcpmm.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_showdcpmmpendingconfig.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/test_summary_flag.cpython-310.pyc,,
+tests/pmemtests/functional/__pycache__/utils.cpython-310.pyc,,
+tests/pmemtests/functional/conftest.py,sha256=k-x_1iHYdxzyXdXif5oqLYE_vE4c71LkCRNizLV9_YY,7351
+tests/pmemtests/functional/ilomock.py,sha256=RVaI4wgM3t4c5DysABowPoTPY-4aewftKsajMWUDL_w,3229
+tests/pmemtests/functional/test_all_flag.py,sha256=3aoP3SpPotIRxE6OqZme8T1EN7gDtOebiiVv5UjUVcU,1364
+tests/pmemtests/functional/test_applydcpmmconfig.py,sha256=qBde_vSFsCwecPRYtOzD2J7ykeH55W3535PeWti-K4A,6504
+tests/pmemtests/functional/test_cleardcpmmpendingconfig.py,sha256=6S03RdG8SSIMzvlj5e2CdL0u3kc_wNrPq-0sMBM5mKA,2131
+tests/pmemtests/functional/test_config_flag.py,sha256=4OM08T9VjnTFxTYOkY7ocAyCW-wm-EiJeiM3NwN1rEU,3968
+tests/pmemtests/functional/test_device_flag.py,sha256=ZJ2N2gncAj0QJCCo4eVTr7oofsxUKGvWMKxcKiOpj2A,4632
+tests/pmemtests/functional/test_display_flag.py,sha256=T9hos6NksyBFieQwd30YYZS5nVCy9ihOUUtxArqukpQ,2573
+tests/pmemtests/functional/test_logical_flag.py,sha256=v-xKCRGbo6el6UzUQd62PfYOEjhQ0oW75i_mwbsUBFI,1796
+tests/pmemtests/functional/test_showdcpmm.py,sha256=Wr4odVffd4g7al5IdhUJH8LgRwtQT9vWelTJ3Sn3n8s,5746
+tests/pmemtests/functional/test_showdcpmmpendingconfig.py,sha256=-gBXzgVpAzSChkXE_wJ9zkripe1e7KfMM-xaojXAN-U,2786
+tests/pmemtests/functional/test_summary_flag.py,sha256=zGvBk2MspJJ-mOhpNoLZKSrSflJBfUPHMxaksc6eHsQ,1796
+tests/pmemtests/functional/utils.py,sha256=mtL8gZprd2fJ0AspzwxAWhe68a6uRmoGKa5-X2-AuTA,3359
+tests/pmemtests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/pmemtests/integration/__pycache__/__init__.cpython-310.pyc,,
+tests/pmemtests/integration/__pycache__/conftest.cpython-310.pyc,,
+tests/pmemtests/integration/__pycache__/ilocli.cpython-310.pyc,,
+tests/pmemtests/integration/__pycache__/ilodebug.cpython-310.pyc,,
+tests/pmemtests/integration/conftest.py,sha256=64jwzUefkm4kXd-5MZwfgwQNOINk-HwNfPZtfPAYBSg,4960
+tests/pmemtests/integration/ilocli.py,sha256=Td5khTodClRvUiKXVzBQXxX8erahhQaW8Y6MQQBa0Ic,2635
+tests/pmemtests/integration/ilodebug.py,sha256=ZE-1RXBqAFPG0h4oHtgtkTiuGJmyXcJ5jtzLiJTWRZc,1874
+tests/pmemtests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/pmemtests/unit/__pycache__/__init__.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/conftest.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_applydcpmmconfig.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_clear_pending_config_command.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_display_helpers.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_mapper_renderers.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_pmem_helpers.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_rest_helpers.cpython-310.pyc,,
+tests/pmemtests/unit/__pycache__/test_show_pmem_commands.cpython-310.pyc,,
+tests/pmemtests/unit/conftest.py,sha256=uFoPRv4LN2t_tVrTXVcHY_qcOmL_FKYXwWnxyWiaM9I,409
+tests/pmemtests/unit/test_applydcpmmconfig.py,sha256=K6zZ4mWte_X6kA8YPxQ78FaaKtcceDhE8mxVL2I-Xfw,18985
+tests/pmemtests/unit/test_clear_pending_config_command.py,sha256=qStxUaXd9n4eE8Y9uQum3raVImqaIEZUXHwV_ix6W2Y,7366
+tests/pmemtests/unit/test_display_helpers.py,sha256=J40sv49RBfWSAH6pe4m_0ouwRYafcVEHvrAUpIvf-NQ,6745
+tests/pmemtests/unit/test_mapper_renderers.py,sha256=5LTC-LCfcvCQRm4HQU_yehZkCrbZrU1mr7lBFlLtKq8,10684
+tests/pmemtests/unit/test_pmem_helpers.py,sha256=dxqvoOEYOHQSgBPHl7XTSGvwitBK1HtdIM6GJTkoRmo,6963
+tests/pmemtests/unit/test_rest_helpers.py,sha256=xUbm5aUk3zwfsZy9KUIC9_2t1dEbu-nZq7Kn4akSxUE,19419
+tests/pmemtests/unit/test_show_pmem_commands.py,sha256=XJqZWSqzT9ss34ViSdOlr5zs-QDaRGw5vyCf3XPknt4,12666
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/REQUESTED b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/WHEEL
new file mode 100644
index 0000000..832be11
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.43.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/entry_points.txt
new file mode 100644
index 0000000..3770bdf
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[console_scripts]
+ilorest = ilorest.rdmc:ilorestcommand
diff --git a/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/top_level.txt
new file mode 100644
index 0000000..b872e13
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest-7.0.0.0.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+ilorest
+tests
diff --git a/venv/lib/python3.10/site-packages/ilorest/__init__.py b/venv/lib/python3.10/site-packages/ilorest/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..6f56cc4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/cliutils.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/cliutils.cpython-310.pyc
new file mode 100644
index 0000000..2e01ef1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/cliutils.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/debug_utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/debug_utils.cpython-310.pyc
new file mode 100644
index 0000000..fa98962
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/debug_utils.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/logging_config_path.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/logging_config_path.cpython-310.pyc
new file mode 100644
index 0000000..c92d76a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/logging_config_path.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc.cpython-310.pyc
new file mode 100644
index 0000000..845a088
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_base_classes.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_base_classes.cpython-310.pyc
new file mode 100644
index 0000000..357e5d1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_base_classes.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_helper.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_helper.cpython-310.pyc
new file mode 100644
index 0000000..9e500eb
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/rdmc_helper.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/security_masking.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/security_masking.cpython-310.pyc
new file mode 100644
index 0000000..64f9f5e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/security_masking.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/__pycache__/versioning.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/__pycache__/versioning.cpython-310.pyc
new file mode 100644
index 0000000..dc634cc
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/__pycache__/versioning.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/__init__.py b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..1fb4a23
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/__init__.py b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..e6efd55
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/ilorest_chif.so b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/ilorest_chif.so
new file mode 100644
index 0000000..06bce1a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/arm/ilorest_chif.so differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.dll b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.dll
new file mode 100644
index 0000000..9075f71
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.dll differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.so b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.so
new file mode 100644
index 0000000..562f0f6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/chiflibrary/ilorest_chif.so differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/cliutils.py b/venv/lib/python3.10/site-packages/ilorest/cliutils.py
new file mode 100644
index 0000000..0e738d3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/cliutils.py
@@ -0,0 +1,246 @@
+###
+# Copyright 2017 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Base implementation for cli interaction with Redfish interface"""
+
+# ---------Imports---------
+
+import getpass
+import os
+import re
+import subprocess
+import sys
+
+try:
+ from rdmc_helper import UI
+except ImportError:
+ from ilorest.rdmc_helper import UI
+
+if os.name == "nt":
+ import ctypes
+ from ctypes import windll, wintypes
+
+# ---------End of imports---------
+
+
+class CommandNotFoundException(Exception):
+ """Exception throw when Command is not found"""
+
+ pass
+
+
+class ResourceAllocationError(Exception):
+ """Exception throw when Command is not found"""
+
+ pass
+
+
+def get_user_config_dir():
+ """Platform specific directory for user configuration.
+
+ :returns: returns the user configuration directory.
+ :rtype: string
+ """
+ if os.name == "nt":
+ try:
+ csidl_appdata = 26
+
+ shgetfolderpath = windll.shell32.SHGetFolderPathW
+ shgetfolderpath.argtypes = [
+ wintypes.HWND,
+ ctypes.c_int,
+ wintypes.HANDLE,
+ wintypes.DWORD,
+ wintypes.LPCWSTR,
+ ]
+
+ path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
+ result = shgetfolderpath(0, csidl_appdata, 0, 0, path_buf)
+
+ if result == 0:
+ return path_buf.value
+ except ImportError:
+ pass
+
+ return os.environ["APPDATA"]
+
+ return os.path.expanduser("~")
+
+
+def is_exe(filename):
+ """Determine if filename is an executable.
+
+ :param filename: the filename to examine.
+ :type filename: string
+ :returns: True if filename is executable False otherwise.
+ :rtype: boolean
+ """
+ if sys.platform == "win32":
+ if os.path.exists(filename):
+ return True
+
+ # windows uses PATHEXT to list valid executable extensions
+ pathext = os.environ["PATHEXT"].split(os.pathsep)
+ (_, ext) = os.path.splitext(filename)
+
+ if ext in pathext:
+ return True
+ else:
+ if os.path.exists(filename) and os.access(filename, os.X_OK):
+ return True
+
+ return False
+
+
+def find_exe(filename, path=None):
+ """Search path for a executable (aka which)
+
+ :param filename: the filename to search for.
+ :type filename: string.
+ :param path: the path(s) to search (default: os.environ['PATH'])
+ :param path: string separated with os.pathsep
+ :returns: string with full path to the file or None if not found.
+ :rtype: string or None.
+ """
+ if path is None:
+ path = os.environ["PATH"]
+
+ pathlist = path.split(os.pathsep)
+
+ # handle fully qualified paths
+ if os.path.isfile(filename) and is_exe(filename):
+ return filename
+
+ for innerpath in pathlist:
+ foundpath = os.path.join(innerpath, filename)
+
+ if is_exe(foundpath):
+ return foundpath
+
+ return None
+
+
+def get_terminal_size():
+ """Returns the rows and columns of the terminal as a tuple.
+
+ :returns: the row and column count of the terminal
+ :rtype: tuple (cols, rows)
+ """
+ _tuple = (80, 25) # default
+
+ if os.name == "nt":
+ pass
+ else:
+ which_stty = find_exe("stty")
+
+ if which_stty:
+ args = [which_stty, "size"]
+ try:
+ procs = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError as excp:
+ raise ResourceAllocationError(str(excp))
+ (stdout_s, _) = procs.communicate()
+
+ _ = procs.wait()
+ # python3 change
+ if isinstance(stdout_s, bytes):
+ stdout_s = stdout_s.decode("utf-8")
+ if stdout_s and re.search(r"^\d+ \d+$", stdout_s):
+ rows, cols = stdout_s.split()
+ _tuple = (cols, rows)
+
+ return _tuple
+
+
+class CLI(object):
+ """Class for building command line interfaces."""
+
+ def __init__(self, verbosity=1, out=sys.stdout):
+ self._verbosity = verbosity
+ self._out = out
+ cols, rows = get_terminal_size()
+ self._cols = int(cols)
+ self._rows = int(rows)
+
+ def verbosity(self, verbosity):
+ self._verbosity = verbosity
+
+ def get_hrstr(self, character="-"):
+ """returns a string suitable for use as a horizontal rule.
+
+ :param character: the character to use as the rule. (default -)
+ :type character: str.
+ """
+ return "%s\n" % (character * self._cols)
+
+ def printer(self, data, flush=True):
+ """printing wrapper
+
+ :param data: data to be printed to the output stream
+ :type data: str.
+ :param flush: flush buffer - True, not flush output buffer - False
+ :type flush: boolean
+ """
+
+ UI(self._verbosity).printer(data, flush)
+
+ def horizontalrule(self, character="-"):
+ """writes a horizontal rule to the file handle.
+
+ :param character: the character to use as the rule. (default -)
+ :type character: str.
+ """
+
+ self.printer(self.get_hrstr(character=character))
+
+ def version(self, progname, version, extracontent):
+ """Prints a version string to fileh.
+
+ :param progname: the name of the program.
+ :type progname: str.
+ :param version: the version of the program.
+ :type version str.
+ :param fileh: the file handle to write to. Default is sys.stdout.
+ :type fileh: file object
+ :returns: None
+ """
+ tmp = "%s version %s\n%s" % (progname, version, extracontent)
+ self.printer(tmp)
+ self.horizontalrule()
+
+ def prompt_password(self, msg, default=None):
+ """Convenient password prompting function
+
+ :param msg: prompt text.
+ :type msg: str.
+ :param default: default value if user does not enter anything.
+ :type default: str.
+ :returns: string user entered, or default if nothing entered
+ """
+ message = "%s : " % msg
+
+ if default is not None:
+ message = "%s [%s] : " % (msg, default)
+
+ i = getpass.getpass(message)
+
+ if i is None or len(i) == 0:
+ i = default
+
+ i = str(i)
+
+ return i.strip()
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/__init__.py b/venv/lib/python3.10/site-packages/ilorest/config/__init__.py
new file mode 100644
index 0000000..4886160
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/config/__init__.py
@@ -0,0 +1 @@
+"""Config parsing for RDMC"""
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..91c8ebb
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/config.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/config.cpython-310.pyc
new file mode 100644
index 0000000..8f094c7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/config.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/rdmc_config.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/rdmc_config.cpython-310.pyc
new file mode 100644
index 0000000..de3815c
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/config/__pycache__/rdmc_config.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/config.py b/venv/lib/python3.10/site-packages/ilorest/config/config.py
new file mode 100644
index 0000000..7edbcb8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/config/config.py
@@ -0,0 +1,159 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Module for working with global configuration options."""
+
+# ---------Imports---------
+
+import logging
+import os
+import re
+
+import six
+from six.moves import configparser
+
+# ---------End of imports---------
+
+
+# ---------Debug logger---------
+
+LOGGER = logging.getLogger(__name__)
+
+# ---------End of debug logger---------
+
+
+class AutoConfigParser(object):
+ """Auto configuration parser. Properties starting with _ac__ are automatically
+ serialized to config file"""
+
+ _config_pattern = re.compile(r"_ac__(?P.*)")
+
+ def __init__(self, filename=None):
+ """Initialize AutoConfigParser
+
+ :param filename: file name to be used for config loading.
+ :type filename: str.
+
+ """
+ self._sectionname = "globals"
+ self._configfile = filename
+
+ @property
+ def configfile(self):
+ """The current configuration file location"""
+ return self._configfile
+
+ def _get_ac_keys(self):
+ """Retrieve parse option keys"""
+ result = []
+ for key in six.iterkeys(self.__dict__):
+ match = AutoConfigParser._config_pattern.search(key)
+ if match:
+ result.append(match.group("confkey"))
+ return result
+
+ def _get(self, key):
+ """Retrieve parse option key
+
+ :param key: key to retrieve.
+ :type key: str.
+
+ """
+ ackey = "_ac__%s" % key.replace("-", "_")
+ if ackey in self.__dict__:
+ return self.__dict__[ackey]
+ return None
+
+ def _set(self, key, value):
+ """Set parse option key
+
+ :param key: key to be set.
+ :type key: str.
+ :param value: value to be given to key.
+ :type value: str.
+
+ """
+ ackey = "_ac__%s" % key.replace("-", "_")
+ if ackey in self.__dict__:
+ self.__dict__[ackey] = value
+ return None
+
+ def load(self, filename=None):
+ """Load configuration settings from the file filename, if filename
+ is None then the value from configfile property is used
+
+ :param filename: file name to be used for config loading.
+ :type filename: str.
+
+ """
+ fname = self.configfile
+ if filename:
+ fname = filename
+
+ if not fname or not os.path.isfile(fname):
+ return
+
+ try:
+ config = configparser.RawConfigParser()
+ config.read(fname)
+ for key in self._get_ac_keys():
+ configval = None
+ try:
+ configval = config.get(self._sectionname, key)
+ except configparser.NoOptionError:
+ # also try with - instead of _
+ try:
+ configval = config.get(self._sectionname, key.replace("_", "-"))
+ except configparser.NoOptionError:
+ pass
+
+ if configval:
+ ackey = "_ac__%s" % key
+ self.__dict__[ackey] = configval
+ except configparser.NoOptionError:
+ pass
+ except configparser.NoSectionError:
+ pass
+
+ def save(self, filename=None):
+ """Save configuration settings from the file filename, if filename
+ is None then the value from configfile property is used
+
+ :param filename: file name to be used for config saving.
+ :type filename: str.
+
+ """
+ fname = self.configfile
+ if filename:
+ fname = filename
+
+ if fname:
+ return
+
+ config = configparser.RawConfigParser()
+ try:
+ config.add_section(self._sectionname)
+ except configparser.DuplicateSectionError:
+ pass # ignored
+
+ for key in self._get_ac_keys():
+ ackey = "_ac__%s" % key
+ config.set(self._sectionname, key, str(self.__dict__[ackey]))
+
+ fileh = open(self._configfile, "wb")
+ config.write(fileh)
+ fileh.close()
diff --git a/venv/lib/python3.10/site-packages/ilorest/config/rdmc_config.py b/venv/lib/python3.10/site-packages/ilorest/config/rdmc_config.py
new file mode 100644
index 0000000..9758c15
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/config/rdmc_config.py
@@ -0,0 +1,288 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Rdmc config"""
+
+import os
+import json
+
+try:
+ from config.config import AutoConfigParser
+ from logging_config_path import get_logging_config_path
+except ImportError:
+ from ilorest.config.config import AutoConfigParser
+ from ilorest.logging_config_path import get_logging_config_path
+
+
+class RdmcConfig(AutoConfigParser):
+ """Rdmc config class for loading and parsing the .conf file global configuration options.
+ Uses the AutoConfigParser."""
+
+ def _load_logdir_from_json(self):
+ """Load logdir from logging configuration JSON file.
+
+ :returns: logdir path from JSON config, or current directory as fallback
+ :rtype: str
+ """
+ config_path = get_logging_config_path()
+ try:
+ if os.path.isfile(config_path):
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+ handlers = config.get("handlers", {})
+ file_handler = handlers.get("file", {})
+ filename = file_handler.get("filename", "")
+ if "%(logdir)s" in filename:
+ logdir = file_handler.get("filename").split("%(logdir)s")[0].rstrip("/\\")
+ return logdir if logdir else os.getcwd()
+ else:
+ # If filename does not contain %(logdir)s, extract directory from filename
+ return os.path.dirname(filename) if os.path.dirname(filename) else os.getcwd()
+ except Exception:
+ pass
+
+ # Fallback to current working directory if any issues
+ return os.getcwd()
+
+ def __init__(self, filename=None):
+ """Initialize RdmcConfig
+
+ :param filename: file name to be used for Rdmcconfig loading.
+ :type filename: str
+
+ """
+ AutoConfigParser.__init__(self, filename=filename)
+ self._sectionname = "redfish"
+ self._configfile = filename
+ self._ac__logdir = self._load_logdir_from_json()
+ self._ac__cache = True
+ self._ac__url = ""
+ self._ac__username = ""
+ self._ac__password = ""
+ self._ac__sslcert = ""
+ self._ac__commit = ""
+ self._ac__format = ""
+ self._ac__cachedir = ""
+ self._ac__savefile = ""
+ self._ac__loadfile = ""
+ self._ac__user_cert = ""
+ self._ac__user_root_ca_key = ""
+ self._ac__user_root_ca_password = ""
+
+ @property
+ def configfile(self):
+ """The current configuration file"""
+ return self._configfile
+
+ @configfile.setter
+ def configfile(self, config_file):
+ """Set the current configuration file
+
+ :param config_file: file name to be used for Rmcconfig loading.
+ :type config_file: str
+ """
+ self._configfile = config_file
+
+ @property
+ def logdir(self):
+ """Get the current log directory"""
+ return self._get("logdir")
+
+ @logdir.setter
+ def logdir(self, value):
+ """Set the current log directory
+ :param value: current working directory for logging
+ :type value: str
+ """
+ return self._set("logdir", value)
+
+ @property
+ def cache(self):
+ """Get the config file cache status"""
+
+ if isinstance(self._get("cache"), bool):
+ return self._get("cache")
+
+ return self._get("cache").lower() in ("yes", "true", "t", "1")
+
+ @cache.setter
+ def cache(self, value):
+ """Get the config file cache status
+
+ :param value: status of config file cache
+ :type value: bool
+ """
+ return self._set("cache", value)
+
+ @property
+ def url(self):
+ """Get the config file URL"""
+ url = self._get("url")
+ url = url[:-1] if url.endswith("/") else url
+
+ return url
+
+ @url.setter
+ def url(self, value):
+ """Set the config file URL
+
+ :param value: URL path for the config file
+ :type value: str
+ """
+ return self._set("url", value)
+
+ @property
+ def username(self):
+ """Get the config file user name"""
+ return self._get("username")
+
+ @username.setter
+ def username(self, value):
+ """Set the config file user name
+
+ :param value: user name for config file
+ :type value: str
+ """
+ return self._set("username", value)
+
+ @property
+ def password(self):
+ """Get the config file password"""
+ return self._get("password")
+
+ @password.setter
+ def password(self, value):
+ """Set the config file password
+
+ :param value: password for config file
+ :type value: str
+ """
+ return self._set("password", value)
+
+ @property
+ def commit(self):
+ """Get the config file commit status"""
+ return self._get("commit")
+
+ @commit.setter
+ def commit(self, value):
+ """Set the config file commit status
+
+ :param value: commit status
+ :type value: str
+ """
+ return self._set("commit", value)
+
+ @property
+ def format(self):
+ """Get the config file default format"""
+ return self._get("format")
+
+ @format.setter
+ def format(self, value):
+ """Set the config file default format
+
+ :param value: set the config file format
+ :type value: str
+ """
+ return self._set("format", value)
+
+ @property
+ def cachedir(self):
+ """Get the config file cache directory"""
+ return self._get("cachedir")
+
+ @cachedir.setter
+ def cachedir(self, value):
+ """Set the config file cache directory
+
+ :param value: config file cache directory
+ :type value: str
+ """
+ return self._set("cachedir", value)
+
+ @property
+ def defaultsavefilename(self):
+ """Get the config file default save name"""
+ return self._get("savefile")
+
+ @defaultsavefilename.setter
+ def defaultsavefilename(self, value):
+ """Set the config file default save name
+
+ :param value: config file save name
+ :type value: str
+ """
+ return self._set("savefile", value)
+
+ @property
+ def defaultloadfilename(self):
+ """Get the config file default load name"""
+ return self._get("loadfile")
+
+ @defaultloadfilename.setter
+ def defaultloadfilename(self, value):
+ """Set the config file default load name
+
+ :param value: name of config file to load by default
+ :type value: str
+ """
+ return self._set("loadfile", value)
+
+ @property
+ def proxy(self):
+ """Get proxy value to be set for communication"""
+ return self._get("proxy")
+
+ @proxy.setter
+ def proxy(self, value):
+ """Set proxy value for communication"""
+ return self._set("proxy", value)
+
+ @property
+ def ssl_cert(self):
+ """Get proxy value to be set for communication"""
+ return self._get("sslcert")
+
+ @ssl_cert.setter
+ def ssl_cert(self, value):
+ """Set proxy value for communication"""
+ return self._set("sslcert", value)
+
+ @property
+ def user_cert(self):
+ return self._get("usercert")
+
+ @user_cert.setter
+ def user_cert(self, value):
+ return self._set("usercert", value)
+
+ @property
+ def user_root_ca_key(self):
+ return self._get("user_root_ca_key")
+
+ @user_root_ca_key.setter
+ def user_root_ca_key(self, value):
+ return self._set("user_root_ca_key", value)
+
+ @property
+ def user_root_ca_password(self):
+ return self._get("user_root_ca_password")
+
+ @user_root_ca_password.setter
+ def user_root_ca_password(self, value):
+ return self._set("user_root_ca_password", value)
diff --git a/venv/lib/python3.10/site-packages/ilorest/debug_utils.py b/venv/lib/python3.10/site-packages/ilorest/debug_utils.py
new file mode 100644
index 0000000..4c528ac
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/debug_utils.py
@@ -0,0 +1,475 @@
+###
+# Copyright 2016-2025 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Debug utilities for iLOrest"""
+
+import json
+import os
+import logging
+
+try:
+ from logging_config_path import get_logging_config_path
+except ModuleNotFoundError:
+ from ilorest.logging_config_path import get_logging_config_path
+
+
+def is_debug_enabled(config_path=None):
+ """
+ Check if debug logging is enabled by reading the logging configuration file.
+
+ Args:
+ config_path (str, optional): Path to the logging config file.
+ Defaults to the standard config location.
+
+ Returns:
+ bool: True if debug logging is enabled, False otherwise.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ # Check if config file exists
+ if not os.path.isfile(config_path):
+ # If config file doesn't exist, assume debug is disabled
+ return False
+
+ # Load the configuration file
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ # Check the root logger level first
+ loggers = config.get("loggers", {})
+ root_logger = loggers.get("", {})
+ root_level = root_logger.get("level", "INFO").upper()
+
+ # If root logger is set to DEBUG, debug is enabled
+ if root_level == "DEBUG":
+ return True
+
+ # Check if any specific loggers have DEBUG level
+ for logger_name, logger_config in loggers.items():
+ if logger_name == "": # Skip root logger, already checked
+ continue
+ level = logger_config.get("level", "INFO").upper()
+ if level == "DEBUG":
+ return True
+
+ # Check handlers for DEBUG level
+ handlers = config.get("handlers", {})
+ for handler_name, handler_config in handlers.items():
+ level = handler_config.get("level", "INFO").upper()
+ if level == "DEBUG":
+ return True
+
+ # If no DEBUG level found anywhere, debug is disabled
+ return False
+
+ except (FileNotFoundError, json.JSONDecodeError, KeyError, ValueError):
+ # If there's any error reading the config, assume debug is disabled
+ return False
+ except Exception:
+ # For any other unexpected error, assume debug is disabled
+ return False
+
+
+def get_current_log_level(logger_name="", config_path=None):
+ """
+ Get the current log level for a specific logger from the configuration file.
+
+ Args:
+ logger_name (str): Name of the logger. Empty string for root logger.
+ config_path (str, optional): Path to the logging config file.
+
+ Returns:
+ str: The log level (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
+ Returns 'INFO' as default if not found.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ # Check if config file exists
+ if not os.path.isfile(config_path):
+ return "INFO"
+
+ # Load the configuration file
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ # Get the specific logger configuration
+ loggers = config.get("loggers", {})
+ logger_config = loggers.get(logger_name, {})
+
+ # Return the level, defaulting to INFO
+ return logger_config.get("level", "INFO").upper()
+
+ except Exception:
+ # For any error, return INFO as default
+ return "INFO"
+
+
+def set_debug_level_in_config(enable_debug=True, config_path=None):
+ """
+ Update the logging configuration file to enable or disable debug logging.
+
+ Args:
+ enable_debug (bool): True to enable debug, False to disable.
+ config_path (str, optional): Path to the logging config file.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ # Check if config file exists
+ if not os.path.isfile(config_path):
+ return False
+
+ # Load the configuration file
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ # Set the desired level
+ new_level = "DEBUG" if enable_debug else "INFO"
+
+ # Update root logger level
+ if "loggers" not in config:
+ config["loggers"] = {}
+ if "" not in config["loggers"]:
+ config["loggers"][""] = {}
+
+ config["loggers"][""]["level"] = new_level
+
+ # Update all other loggers to the same level
+ for logger_name in config["loggers"]:
+ config["loggers"][logger_name]["level"] = new_level
+
+ # Update handlers to the same level
+ if "handlers" in config:
+ for handler_name in config["handlers"]:
+ config["handlers"][handler_name]["level"] = new_level
+
+ # Write the updated configuration back to file
+ with open(config_path, "w", encoding="utf-8") as f:
+ json.dump(config, f, indent=2)
+
+ return True
+
+ except PermissionError as e:
+ logging.warning("Administrator privileges are required to save the configuration")
+ return False
+ except Exception as e:
+ logging.debug(f"Unexpected error in set_debug_level_in_config: {e}")
+ return False
+
+
+def set_file_handlers_level(root_logger=None, level=logging.INFO):
+ """Set level for any FileHandler-like handlers on the provided logger.
+
+ Args:
+ root_logger (logging.Logger): Logger instance to operate on. If None, uses logging.getLogger().
+ level (int): Level to set for file handlers.
+ Returns:
+ int: Number of handlers updated.
+ """
+ updated = 0
+ try:
+ if root_logger is None:
+ root_logger = logging.getLogger()
+
+ for h in list(root_logger.handlers):
+ try:
+ if isinstance(h, logging.FileHandler) or getattr(h, "baseFilename", None):
+ h.setLevel(level)
+ updated += 1
+ except Exception:
+ pass
+ return updated
+ except Exception:
+ return 0
+
+
+def set_named_handler_level_if_exists(root_logger=None, name="logconfig_stdout", level=logging.INFO):
+ """Set level on a named handler only if it exists; do not create handlers.
+
+ Args:
+ root_logger (logging.Logger): Logger to operate on (defaults to root logger).
+ name (str): Handler name to look for.
+ level (int): Level to set.
+ Returns:
+ bool: True if handler level was updated, False otherwise.
+ """
+ try:
+ if root_logger is None:
+ root_logger = logging.getLogger()
+
+ for h in list(root_logger.handlers):
+ if getattr(h, "name", None) == name:
+ try:
+ h.setLevel(level)
+ return True
+ except Exception:
+ return False
+ return False
+ except Exception:
+ return False
+
+
+def update_config_handlers_levels(config_path=None, level_str="INFO", handler_names=None):
+ """Update handler levels in the JSON logging configuration file.
+
+ Args:
+ config_path (str): Path to the logging_config.json file.
+ level_str (str): Level string to set (e.g., 'DEBUG', 'INFO').
+ handler_names (iterable|None): If provided, only update these handler names; otherwise update all handlers.
+
+ Returns:
+ bool: True if update succeeded, False otherwise.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ if not os.path.isfile(config_path):
+ return False
+
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ if "handlers" not in config:
+ config["handlers"] = {}
+
+ for hname in list(config["handlers"].keys()):
+ if handler_names is None or hname in handler_names:
+ try:
+ config["handlers"][hname]["level"] = level_str
+ except Exception:
+ pass
+
+ with open(config_path, "w", encoding="utf-8") as f:
+ json.dump(config, f, indent=2)
+
+ return True
+ except PermissionError as e:
+ logging.warning("Administrator privileges are required to save the configuration")
+ return False
+ except Exception as e:
+ logging.debug(f"Unexpected error in update_config_handlers_levels: {e}")
+ return False
+
+
+def update_config_loggers_levels(config_path=None, level_str="INFO"):
+ """Update all logger levels in the JSON logging configuration file.
+
+ Args:
+ config_path (str): Path to the logging_config.json file.
+ level_str (str): Level string to set for loggers.
+
+ Returns:
+ bool: True if update succeeded, False otherwise.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ if not os.path.isfile(config_path):
+ return False
+
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ if "loggers" not in config:
+ config["loggers"] = {}
+
+ # Set root logger
+ if "" not in config["loggers"]:
+ config["loggers"][""] = {}
+ config["loggers"][""]["level"] = level_str
+
+ # Set all other loggers to the same level
+ for lname in list(config["loggers"].keys()):
+ try:
+ config["loggers"][lname]["level"] = level_str
+ except Exception:
+ pass
+
+ with open(config_path, "w", encoding="utf-8") as f:
+ json.dump(config, f, indent=2)
+
+ return True
+ except PermissionError as e:
+ logging.warning("Administrator privileges are required to save the configuration")
+ return False
+ except Exception as e:
+ logging.debug(f"Unexpected error in update_config_loggers_levels: {e}")
+ return False
+
+
+def set_runtime_logger_levels(root_logger=None, runtime_level=logging.INFO, set_module_logger=None):
+ """Set runtime logger and handler levels consistently.
+
+ Args:
+ root_logger (logging.Logger): Logger instance to operate on. Defaults to root logger.
+ runtime_level (int): Logging level (e.g., logging.DEBUG).
+ set_module_logger (logging.Logger|None): Optional module-level logger to set (e.g., rdmc helper LOGGER).
+
+ Returns:
+ bool: True if operations completed (best-effort), False on failure.
+ """
+ try:
+ if root_logger is None:
+ root_logger = logging.getLogger()
+
+ root_logger.setLevel(runtime_level)
+
+ # Update named stdout handler if present
+ set_named_handler_level_if_exists(root_logger, "logconfig_stdout", runtime_level)
+
+ # Update file handlers level
+ set_file_handlers_level(root_logger, runtime_level)
+
+ if set_module_logger is not None:
+ try:
+ set_module_logger.setLevel(runtime_level)
+ except Exception:
+ pass
+
+ return True
+ except Exception:
+ return False
+
+
+def update_config_file_rotation_settings(config_path=None, max_bytes=None, backup_count=None):
+ """Update file rotation settings for RotatingFileHandler in the JSON logging configuration file.
+
+ Args:
+ config_path (str): Path to the logging_config.json file.
+ max_bytes (int|None): Maximum size of log file in bytes before rotation.
+ backup_count (int|None): Number of backup files to keep.
+
+ Returns:
+ bool: True if update succeeded, False otherwise.
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ try:
+ if not os.path.isfile(config_path):
+ return False
+
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ if "handlers" not in config:
+ config["handlers"] = {}
+
+ # Update rotation settings for RotatingFileHandler instances
+ updated = False
+ for handler_name, handler_config in config["handlers"].items():
+ if not isinstance(handler_config, dict):
+ continue
+
+ handler_class = handler_config.get("class", "")
+ if "RotatingFileHandler" in handler_class:
+ if max_bytes is not None:
+ handler_config["maxBytes"] = max_bytes
+ updated = True
+
+ if backup_count is not None:
+ handler_config["backupCount"] = backup_count
+ updated = True
+
+ if updated:
+ with open(config_path, "w", encoding="utf-8") as f:
+ json.dump(config, f, indent=2)
+
+ return updated
+ except PermissionError as e:
+ logging.warning("Administrator privileges are required to save the configuration")
+ return False
+ except Exception as e:
+ logging.debug(f"Unexpected error in update_config_file_rotation_settings: {e}")
+ return False
+
+
+def get_config_file_rotation_settings(config_path=None):
+ """Get current file rotation settings from the JSON logging configuration file.
+
+ Args:
+ config_path (str): Path to the logging_config.json file.
+
+ Returns:
+ dict: Dictionary containing rotation settings for each RotatingFileHandler.
+ Format: {handler_name: {'maxBytes': value, 'backupCount': value}}
+ """
+ if config_path is None:
+ config_path = get_logging_config_path()
+
+ rotation_settings = {}
+
+ try:
+ if not os.path.isfile(config_path):
+ return rotation_settings
+
+ with open(config_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ handlers = config.get("handlers", {})
+ for handler_name, handler_config in handlers.items():
+ if not isinstance(handler_config, dict):
+ continue
+
+ handler_class = handler_config.get("class", "")
+ if "RotatingFileHandler" in handler_class:
+ rotation_settings[handler_name] = {
+ "maxBytes": handler_config.get("maxBytes"),
+ "backupCount": handler_config.get("backupCount"),
+ }
+
+ return rotation_settings
+ except Exception:
+ return rotation_settings
+
+
+def validate_rotation_parameters(max_bytes=None, backup_count=None):
+ """Validate file rotation parameters.
+
+ Args:
+ max_bytes (int|None): Maximum size of log file in bytes before rotation.
+ backup_count (int|None): Number of backup files to keep.
+
+ Returns:
+ tuple: (is_valid, error_message) where is_valid is bool and error_message is str or None.
+ """
+ try:
+ if max_bytes is not None:
+ if not isinstance(max_bytes, int):
+ return False, "log_file_size must be an integer"
+ if max_bytes <= 0:
+ return False, "log_file_size must be a positive integer"
+
+ if backup_count is not None:
+ if not isinstance(backup_count, int):
+ return False, "backup_count must be an integer"
+ if backup_count < 0:
+ return False, "backup_count must be a non-negative integer"
+
+ return True, None
+ except Exception:
+ return False, "Invalid rotation parameters"
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BiosDefaultsCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BiosDefaultsCommand.py
new file mode 100644
index 0000000..9901a5f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BiosDefaultsCommand.py
@@ -0,0 +1,156 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""BiosDefaultsCommand for rdmc"""
+
+try:
+ from rdmc_helper import Encryption, InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import Encryption, InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class BiosDefaultsCommand:
+ """Set BIOS settings back to default for the server that is currently
+ logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "biosdefaults",
+ "usage": None,
+ "description": "Run to set the currently"
+ " logged in server's Bios. type settings to defaults\n\texample: "
+ "biosdefaults\n\n\tRun to set the currently logged in server's "
+ "Bios. type settings to user defaults\n\texample: biosdefaults "
+ "--userdefaults\n\n\tRun to set the currently logged in server "
+ "to manufacturing defaults, including boot order and secure boot."
+ "\n\texample: biosdefaults --manufacturingdefaults",
+ "summary": "Set the currently logged in server to default BIOS settings.",
+ "aliases": [],
+ "auxcommands": ["SetCommand", "RebootCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main BIOS defaults worker function"""
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.defaultsvalidation(options)
+
+ if options.manufdefaults:
+ self.rdmc.ui.printer("Resetting BIOS attributes and settings to manufacturing defaults.\n")
+ elif options.userdefaults:
+ self.rdmc.ui.printer("Resetting BIOS attributes and settings to user defaults.\n")
+
+ put_path = self.rdmc.app.typepath.defs.biospath
+ body = None
+
+ if self.rdmc.app.typepath.defs.isgen10 and not options.manufdefaults:
+ bodydict = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.biospath, service=True, silent=True).dict
+
+ for item in bodydict["Actions"]:
+ if "ResetBios" in item:
+ action = item.split("#")[-1]
+ path = bodydict["Actions"][item]["target"]
+ break
+
+ body = {"Action": action}
+
+ if options.userdefaults:
+ body["ResetType"] = "default.user"
+ else:
+ body["ResetType"] = "default"
+
+ self.rdmc.app.post_handler(path, body)
+ else:
+ if options.userdefaults:
+ body = {"BaseConfig": "default.user"}
+ elif not options.manufdefaults:
+ body = {"BaseConfig": "default"}
+ if body:
+ self.rdmc.app.put_handler(
+ put_path + "/settings",
+ body=body,
+ optionalpassword=options.biospassword,
+ )
+
+ if not body and options.manufdefaults:
+ setstring = "RestoreManufacturingDefaults=Yes --selector=HpBios. --commit"
+ if options.reboot:
+ setstring += " --reboot=%s" % options.reboot
+
+ self.auxcommands["set"].run(setstring)
+
+ elif options.reboot:
+ self.auxcommands["reboot"].run(options.reboot)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def defaultsvalidation(self, options):
+ """BIOS defaults method validation function"""
+ self.cmdbase.login_select_validation(self, options)
+
+ if options.encode:
+ options.biospassword = Encryption.decode_credentials(options.biospassword)
+ if isinstance(options.biospassword, bytes):
+ options.biospassword = options.biospassword.decode("utf-8")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--userdefaults",
+ dest="userdefaults",
+ action="store_true",
+ help="Sets bios to user defaults instead of manufacturing defaults.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--manufacturingdefaults",
+ dest="manufdefaults",
+ action="store_true",
+ help="Reset all configuration settings to manufacturing defaults, " "including boot order and secure boot.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BootOrderCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BootOrderCommand.py
new file mode 100644
index 0000000..7be53c2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/BootOrderCommand.py
@@ -0,0 +1,746 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""BootOrder Command for rdmc"""
+
+import ast
+import copy
+import fnmatch
+from functools import reduce
+
+import six
+
+try:
+ from rdmc_helper import (
+ UI,
+ BootOrderMissingEntriesError,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ ReturnCodes,
+ )
+except:
+ from ilorest.rdmc_helper import (
+ UI,
+ BootOrderMissingEntriesError,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ ReturnCodes,
+ )
+
+
+class BootOrderCommand:
+ """Changes the boot order for the server that is currently logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "bootorder",
+ "usage": None,
+ "description": "Run without arguments for current boot order and one time boot options."
+ "\n\texample: bootorder\n\n\tTo set the persistent boot order pick items "
+ 'from\n\tthe "Current Persistent Boot Order" section.\n\t'
+ "example: bootorder [5,4,3,2,1] --commit\n\n\tSetting partial"
+ " boot order is also supported.\n\tMissing entries are "
+ "concatenated at the end.\n\texample: bootorder [5] --commit\n\n\t"
+ "You can also set the boot order using partial string matching.\n\t"
+ "example: bootorder NIC.*v4 HD* Generic.USB.1.1 --commit\n\n\tThis will set "
+ "All v4 NICs first, followed by all hard drives,\n\tfollowed by Generic.USB.1.1. "
+ "Everything not listed will be\n\tadded to the end of the boot order."
+ '\n\n\tTo set one time boot entry pick items from the\n\t"'
+ 'Continuous and one time boot uefi options" section.\n\t'
+ "example: bootorder --onetimeboot=Hdd\n\n\tTo set continuous"
+ ' boot entry pick items from the\n\t"Continuous and one time '
+ 'boot uefi options" section.\n\texample: bootorder --'
+ "continuousboot=Utilities --commit\n\n\tDisable either "
+ "continuous or one time boot options.\n\texample: bootorder "
+ "--disablebootflag --commit\n\n\t"
+ "Changing Secure Boot Keys:\n\tTo manage secure boot keys use"
+ " the --securebootkeys flag.\n\tTo delete all keys.\n\n\texample:"
+ " bootorder --securebootkeys=deletepk\n\tFor all possibilities"
+ " see the --securebootkeys flag \n\tin the options list.\n\n\t"
+ "NOTE: pick ONETIMEBOOT and "
+ 'CONTINUOUS items from "Continuous\n\tand one time boot '
+ 'options" section. Items in this list represent\n\ta '
+ '"clustered" view of the "Continuous and one time boot uefi'
+ '\n\toptions" section. Example: choosing Pxe will try to Pxe'
+ ' boot\n\tcapable devices in the order found in the "'
+ 'Continuous and one\n\ttime boot options".\n\n\t',
+ "summary": "Displays and sets the current boot order.",
+ "aliases": [],
+ "auxcommands": [
+ "GetCommand",
+ "SelectCommand",
+ "SetCommand",
+ "RebootCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main boot order worker function"""
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.bootordervalidation(options)
+
+ if options.secureboot:
+ self.secureboothelper(options.secureboot)
+
+ if options.reboot:
+ self.auxcommands["reboot"].run(options.reboot)
+
+ return ReturnCodes.SUCCESS
+
+ self.rdmc.app.select(selector="HpeServerBootSettings.", path_refresh=True)
+
+ props = self.rdmc.app.getprops(skipnonsetting=False)
+
+ for prop in props:
+ bootname = prop.get("Name")
+
+ self.rdmc.ui.printer("bootname is : {0}\n\n".format(bootname))
+
+ if self.rdmc.app.typepath.defs.isgen10:
+ if "current" in bootname.lower() or "pending" in bootname.lower():
+ try:
+ bootpath = prop.get("@odata.id")
+ except:
+ bootpath = prop.get("links")
+ else:
+ bootpath = "/rest/v1/systems/1/bios/boot"
+
+ self.rdmc.ui.printer("bootpath is : {0}\n\n".format(bootpath))
+
+ bootsources = self.rdmc.app.get_handler(bootpath, service=True, silent=True).dict["BootSources"]
+
+ bootoverride = None
+ self.auxcommands["select"].selectfunction("HpeBios.")
+ try:
+ bootmode = self.auxcommands["get"].getworkerfunction("BootMode", options, results=True, uselist=True)
+ except:
+ bootmode = dict()
+ bootmode["BootMode"] = "Uefi"
+
+ self.auxcommands["select"].selectfunction("ComputerSystem.")
+ onetimebootsettings = self.auxcommands["get"].getworkerfunction(
+ ["Boot/" + self.rdmc.app.typepath.defs.bootoverridetargettype],
+ options,
+ results=True,
+ uselist=True,
+ )
+
+ bootstatus = self.auxcommands["get"].getworkerfunction(
+ ["Boot/BootSourceOverrideEnabled"],
+ options,
+ results=True,
+ uselist=True,
+ )
+
+ targetstatus = self.auxcommands["get"].getworkerfunction(
+ ["Boot/BootSourceOverrideTarget"],
+ options,
+ results=True,
+ uselist=True,
+ )
+
+ uefitargetstatus = self.auxcommands["get"].getworkerfunction(
+ ["Boot/UefiTargetBootSourceOverride"],
+ options,
+ results=True,
+ uselist=True,
+ )
+
+ currentsettings = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.systempath, service=True, silent=True)
+
+ if bootmode and bootmode[0].get("BootMode", None) == "Uefi":
+ if self.rdmc.app.typepath.defs.isgen9:
+ uefionetimebootsettings = self.auxcommands["get"].getworkerfunction(
+ ["Boot/UefiTargetBootSourceOverrideSupported"],
+ options,
+ results=True,
+ uselist=True,
+ )
+
+ else:
+ # Gen 10
+ bootsettings = self.auxcommands["get"].getworkerfunction(
+ ["Boot"],
+ options,
+ results=True,
+ uselist=True,
+ )[0]["Boot"]
+ finaluefi = []
+ if "UefiTargetBootSourceOverride@Redfish.AllowableValues" in bootsettings:
+ uefionetimebootsettings = bootsettings["UefiTargetBootSourceOverride@Redfish.AllowableValues"]
+
+ for setting in uefionetimebootsettings:
+ for source in bootsources:
+ if "UEFIDevicePath" in source and source["UEFIDevicePath"].endswith(setting):
+ finaluefi.append(source["StructuredBootString"])
+ continue
+
+ uefionetimebootsettings = {"Boot": {"UefiTargetBootSourceOverrideSupported": finaluefi}}
+ else:
+ uefionetimebootsettings = None
+
+ if options.onetimeboot is None and options.continuousboot is None and not options.disablebootflag:
+ self.auxcommands["select"].selectfunction("HpeServerBootSettings.")
+ bootsettings = self.auxcommands["get"].getworkerfunction(
+ "PersistentBootConfigOrder", options, results=True, uselist=True
+ )
+
+ if not args:
+ if not options.json:
+ self.print_out_boot_order(
+ bootsettings,
+ onetimebootsettings,
+ uefionetimebootsettings,
+ bootmode,
+ bootsources,
+ bootstatus,
+ targetstatus,
+ )
+ elif options.json:
+ UI().print_out_json(bootsettings)
+ elif len(args) == 1 and args[0][0] == "[":
+ bootlist = args[0][1:-1].split(",")
+ currentlist = bootsettings[0]["PersistentBootConfigOrder"]
+
+ if not isinstance(currentlist, list):
+ templist = ast.literal_eval(currentlist[1:-1])
+ currentlist = [n.strip() for n in templist]
+
+ removallist = copy.deepcopy(currentlist)
+
+ if len(bootlist) > len(currentlist):
+ raise InvalidCommandLineError("Number of entries is greater than the current boot order length.")
+ else:
+ newlist = "["
+
+ for value, _ in enumerate(bootlist):
+ try:
+ newlist += currentlist[int(bootlist[value]) - 1]
+ except:
+ raise InvalidCommandLineError(
+ "Invalid entry "
+ "number passed to bootorder. Please \n"
+ " run bootorder without arguments"
+ " for possible boot \n order numbers. "
+ )
+
+ removallist.remove(currentlist[int(bootlist[value]) - 1])
+
+ if removallist:
+ newlist += ","
+
+ if not removallist:
+ newlist += "]"
+ else:
+ for value, _ in enumerate(removallist):
+ newlist += removallist[value]
+
+ if not value == len(removallist) - 1:
+ newlist += ","
+
+ newlist += "]"
+
+ if options.biospassword:
+ newlist += " --biospassword " + options.biospassword
+
+ if options.reboot:
+ newlist += " --commit --reboot " + options.reboot
+ elif options.commit:
+ newlist += " --commit"
+
+ self.auxcommands["set"].run("PersistentBootConfigOrder=" + newlist)
+ else:
+ currlist = bootsettings[0]["PersistentBootConfigOrder"]
+ if not isinstance(currlist, list):
+ templist = ast.literal_eval(currlist[1:-1])
+ currlist = [n.strip() for n in templist]
+ remlist = copy.deepcopy(currlist)
+ if len(args) > len(currlist):
+ raise InvalidCommandLineError("Number of entries is " "greater than the current boot order length.")
+ newlist = []
+ for arg in args:
+ argmatch = [val for val in remlist if fnmatch.fnmatch(val.lower(), arg.lower())]
+ if not argmatch and not options.ime:
+ raise InvalidCommandLineError(
+ "Invalid entry passed: "
+ "{0}. Please run bootorder to check for possible "
+ "values and reevaluate.\n".format(arg)
+ )
+ if argmatch:
+ newlist.extend(argmatch)
+ _ = [remlist.remove(val) for val in newlist if val in remlist]
+ newlist.extend(remlist)
+ strlist = "["
+ concatlist = reduce((lambda x, y: x + "," + y), newlist)
+ strlist = strlist + concatlist + "]"
+
+ if options.biospassword:
+ strlist += " --biospassword " + options.biospassword
+ if options.reboot:
+ strlist += " --commit --reboot " + options.reboot
+ elif options.commit:
+ strlist += " --commit"
+ self.auxcommands["set"].run("PersistentBootConfigOrder=" + strlist)
+ else:
+ if options.onetimeboot is not None:
+ entry = options.onetimeboot
+
+ if not bootstatus[0]["Boot"]["BootSourceOverrideEnabled"] == "Once":
+ bootoverride = " Boot/BootSourceOverrideEnabled=Once"
+ elif options.continuousboot is not None:
+ entry = options.continuousboot
+
+ if not bootstatus[0]["Boot"]["BootSourceOverrideEnabled"] == "Continuous":
+ bootoverride = " Boot/BootSourceOverrideEnabled=Continuous"
+ else:
+ entry = "JacksBootOption"
+ if not bootstatus[0]["Boot"]["BootSourceOverrideEnabled"] == "Disabled":
+ if currentsettings.dict["Boot"]["BootSourceOverrideEnabled"] == "Disabled":
+ bootoverride = "Boot/BootSourceOverrideTarget=None" " Boot/BootSourceOverrideEnabled=Disabled"
+ else:
+ bootoverride = "Boot/BootSourceOverrideTarget=None"
+
+ newlist = ""
+
+ if entry.lower() in (
+ item.lower()
+ for item in onetimebootsettings[0]["Boot"][self.rdmc.app.typepath.defs.bootoverridetargettype]
+ ):
+ if entry and isinstance(entry, six.string_types):
+ entry = entry.upper()
+
+ entry = self.searchcasestring(
+ entry,
+ onetimebootsettings[0]["Boot"][self.rdmc.app.typepath.defs.bootoverridetargettype],
+ )
+
+ if not entry == targetstatus[0]["Boot"]["BootSourceOverrideTarget"]:
+ newlist += " Boot/BootSourceOverrideTarget=" + entry
+
+ if bootoverride:
+ newlist += bootoverride
+
+ if options.biospassword and newlist:
+ newlist += " --biospassword " + options.biospassword
+
+ if options.reboot and newlist:
+ newlist += " --commit --reboot " + options.reboot
+ elif options.commit and newlist:
+ newlist += " --commit"
+
+ if newlist:
+ self.auxcommands["set"].run(newlist)
+ else:
+ raise InvalidOrNothingChangedSettingsError("Entry is the current boot setting.")
+ elif (
+ uefionetimebootsettings
+ and uefionetimebootsettings["Boot"]["UefiTargetBootSourceOverrideSupported"]
+ and entry in (item for item in uefionetimebootsettings["Boot"]["UefiTargetBootSourceOverrideSupported"])
+ ):
+ if entry and isinstance(entry, six.string_types):
+ entry = entry.upper()
+
+ entry = self.searchcasestring(
+ entry,
+ uefionetimebootsettings["Boot"]["UefiTargetBootSourceOverrideSupported"],
+ )
+ try:
+ # gen10
+ allowable_vals = next(
+ iter(
+ self.auxcommands["get"].getworkerfunction(
+ ["Boot/UefiTargetBootSourceOverride@Redfish.AllowableValues"],
+ options,
+ results=True,
+ uselist=True,
+ )
+ ),
+ {},
+ )["Boot"]["UefiTargetBootSourceOverride@Redfish.AllowableValues"]
+ for source in bootsources:
+ if source["StructuredBootString"].upper() == entry.upper():
+ for val in allowable_vals:
+ if "UEFIDevicePath" in source and source["UEFIDevicePath"].endswith(val):
+ entry = val
+ break
+ except KeyError:
+ pass
+
+ if not entry == uefitargetstatus["Boot"]["UefiTargetBootSourceOverride"]:
+ newlist += " Boot/UefiTargetBootSourceOverride=" + entry
+ elif not targetstatus["Boot"]["BootSourceOverrideTarget"] == "UefiTarget":
+ newlist += " Boot/BootSourceOverrideTarget=UefiTarget"
+
+ if bootoverride:
+ if self.rdmc.app.typepath.defs.isgen9 and newlist:
+ if not bootoverride.split("=")[-1] == bootstatus["Boot"]["BootSourceOverrideEnabled"]:
+ # Preemptively set UefiTargetBootSourceOverride so iLO 4 doesn't complain
+ self.rdmc.app.patch_handler(
+ self.rdmc.app.typepath.defs.systempath,
+ {"Boot": {"UefiTargetBootSourceOverride": entry}},
+ silent=True,
+ service=True,
+ )
+ self.rdmc.app.select(selector=self.rdmc.app.selector, path_refresh=True)
+ newlist = ""
+ newlist += bootoverride
+ else:
+ newlist += bootoverride
+
+ if options.reboot and newlist:
+ newlist += " --commit --reboot " + options.reboot
+ elif options.commit and newlist:
+ newlist += " --commit"
+
+ if newlist:
+ try:
+ self.auxcommands["set"].run(newlist)
+ except InvalidOrNothingChangedSettingsError:
+ if self.rdmc.app.typepath.defs.isgen9:
+ pass
+ else:
+ raise
+ else:
+ raise InvalidOrNothingChangedSettingsError("Entry is the " "current boot setting.\n")
+ elif options.disablebootflag:
+ if bootoverride:
+ newlist += bootoverride
+
+ if options.reboot:
+ newlist += " --commit --reboot " + options.reboot
+ elif options.commit and newlist:
+ newlist += " --commit"
+
+ if newlist:
+ self.auxcommands["set"].run(newlist)
+ else:
+ raise InvalidOrNothingChangedSettingsError("Entry is the " "current boot setting.\n")
+ else:
+ raise InvalidCommandLineError(
+ "Invalid entry passed for one"
+ " time boot. Please run boot \n order without"
+ " arguments to view available options.\n"
+ )
+
+ self.cmdbase.logout_routine(self, options)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def searchcasestring(self, entry, content):
+ """Helper function for retrieving correct case for value
+
+ :param entry: entry to correlate case
+ :type entry: string.
+ :param content: list of items
+ :type content: list.
+ """
+ for item in content:
+ if entry.upper() == item.upper():
+ return item
+
+ def secureboothelper(self, securebootoption):
+ """Helper function for secure boot function
+
+ :param securebootoption: option passed in for secure boot
+ :type securebootoption: string.
+ """
+ actionlist = ["defaultkeys", "deletekeys", "deletepk"]
+
+ if not securebootoption.lower() in actionlist:
+ raise InvalidCommandLineError("%s is not a valid option for " "the securebootkeys flag." % securebootoption)
+
+ if securebootoption == actionlist[0]:
+ action = "ResetAllKeysToDefault"
+ elif securebootoption == actionlist[1]:
+ action = "DeleteAllKeys"
+ elif securebootoption == actionlist[2]:
+ action = "DeletePK"
+
+ results = self.rdmc.app.select(selector=self.rdmc.app.typepath.defs.hpsecureboot)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ results = results.resp.dict
+
+ try:
+ for item in results["Actions"]:
+ if "ResetKeys" in item:
+ path = results["Actions"][item]["target"]
+ break
+
+ body = {"ResetKeysType": action}
+ self.rdmc.app.post_handler(path, body)
+ except:
+ if securebootoption == actionlist[0]:
+ self.auxcommands["select"].selectfunction(self.rdmc.app.typepath.defs.hpsecureboot)
+ self.auxcommands["set"].run("ResetToDefaultKeys=True --commit")
+ elif securebootoption == actionlist[1]:
+ self.auxcommands["select"].selectfunction(self.rdmc.app.typepath.defs.hpsecureboot)
+ self.auxcommands["set"].run("ResetAllKeys=True --commit")
+ else:
+ self.rdmc.ui.warn("DeletePK option is not available on Gen9.\n")
+
+ def print_out_boot_order(
+ self,
+ content,
+ onetimecontent,
+ uefionetimecontent,
+ bootmode,
+ bootsources,
+ bootstatus,
+ targetstatus,
+ ):
+ """Convert content to human readable and print out to std.out
+
+ :param content: current content
+ :type content: string.
+ :param onetimecontent: list of one time boot entries
+ :type onetimecontent: list.
+ :param uefionetimecontent: list of uefi one time boot entries
+ :type uefionetimecontent: list.
+ :param bootmode: current system boot mode
+ :type bootmode: string.
+ :param bootsources: current systems boot sources
+ :type bootsources: list.
+ """
+ if content is None:
+ raise BootOrderMissingEntriesError("No entries found in " "current boot order.\n\n")
+ else:
+ self.print_boot_helper(content[0], "\nCurrent Persistent Boot " "Order:", bootsources=bootsources)
+
+ bootstatusval = bootstatus[0]["Boot"]["BootSourceOverrideEnabled"]
+ boottoval = targetstatus[0]["Boot"]["BootSourceOverrideTarget"]
+ if bootstatusval == "Continuous":
+ self.rdmc.ui.printer("Current continuous boot: {0}\n\n".format(boottoval))
+ elif bootstatusval == "Once":
+ self.rdmc.ui.printer("Current one time boot: {0}\n\n".format(boottoval))
+
+ if onetimecontent is None:
+ raise BootOrderMissingEntriesError("No entries found for one time boot options.\n\n")
+ else:
+ self.print_boot_helper(onetimecontent[0]["Boot"], "Continuous and one time boot options:")
+
+ if bootmode and any([bootmode[0].get(boot, None) == "Uefi" for boot in bootmode[0]]):
+ if uefionetimecontent is None:
+ self.rdmc.ui.printer("Continuous and one time boot uefi options:\n")
+ self.rdmc.ui.printer(
+ "No entries found for one-time UEFI options or boot source mode " "is not set to UEFI."
+ )
+ else:
+ if isinstance(uefionetimecontent, dict):
+ self.print_boot_helper(
+ uefionetimecontent["Boot"],
+ "Continuous and one time boot uefi options:",
+ bootsources=bootsources,
+ )
+ else:
+ self.print_boot_helper(
+ uefionetimecontent[0]["Boot"],
+ "Continuous and one time boot uefi options:",
+ bootsources=bootsources,
+ )
+
+ def print_boot_helper(self, content, outstring, indent=0, bootsources=None):
+ """Print boot helper
+
+ :param content: current content
+ :type content: string.
+ :param outstring: output string
+ :type outstring: string.
+ :param indent: indent format
+ :type indent: string.
+ :param bootsources: current systems boot sources
+ :type bootsources: list.
+ """
+ for _, value in list(content.items()):
+ self.rdmc.ui.printer("\t" * indent + outstring)
+
+ if isinstance(value, list):
+ count = 1
+
+ for item in value:
+ self.rdmc.ui.printer("\n")
+
+ if not item:
+ item = str("null")
+
+ # if isinstance(item, six.string_types):
+ bootstring = False
+ try:
+ for source in bootsources:
+ if item == source["StructuredBootString"]:
+ self.rdmc.ui.printer(
+ "\t" * indent
+ + str(count)
+ + ". "
+ + str(item)
+ + " ("
+ + str(source["BootString"])
+ + ")"
+ )
+ bootstring = True
+ break
+
+ if not bootstring:
+ self.rdmc.ui.printer("\t" * indent + str(count) + ". " + str(item))
+ except:
+ self.rdmc.ui.printer("\t" * indent + str(count) + ". " + str(item))
+
+ count += 1
+ # else:
+ # self.print_boot_helper(item, indent+1)
+
+ self.rdmc.ui.printer("\n\n")
+
+ def bootordervalidation(self, options):
+ """Boot order method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ inputline = list()
+
+ if self.rdmc.config.commit.lower() == "true":
+ options.commit = True
+
+ self.cmdbase.login_select_validation(self, options)
+
+ if inputline:
+ self.lobobj.loginfunction(inputline)
+
+ if options.encode:
+ options.biospassword = Encryption.decode_credentials(options.biospassword)
+ if isinstance(options.biospassword, bytes):
+ options.biospassword = options.biospassword.decode("utf-8")
+
+ @staticmethod
+ def options_argument_group(parser):
+ """Additional argument
+
+ :param parser: The parser to add the removeprivs option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+
+ parser.add_argument_group(
+ "GLOBAL OPTION:",
+ "Option(s) are available " "for all arguments within the scope of this command.",
+ )
+
+ parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ self.options_argument_group(customparser)
+
+ customparser.add_argument(
+ "--onetimeboot",
+ dest="onetimeboot",
+ help="Use this flag to configure a one-time boot option."
+ " Using this flag will prioritize the provided boot source"
+ " only on the very next time the server is booted.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--continuousboot",
+ dest="continuousboot",
+ help="Use this flag to enable a continuous boot option. Using"
+ " this flag will cause the system to boot to the selected"
+ " device every time the system boots.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--disablebootflag",
+ dest="disablebootflag",
+ action="store_true",
+ help="Use this to disable either continuous or one-time boot modes.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--securebootkeys",
+ dest="secureboot",
+ help="Use this flag to perform actions on secure boot keys."
+ "Possible values include defaultkeys: resets all keys to default,"
+ "deletekeys: deletes all keys, deletepk: deletes all product keys.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--commit",
+ dest="commit",
+ action="store_true",
+ help="Use this flag when you are ready to commit all pending"
+ " changes. Note that some changes made in this way will be updated"
+ " instantly, while others will be reflected the next time the"
+ " server is started.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--ignorematcherror",
+ dest="ime",
+ action="store_true",
+ help="Use this flag when you want to run multiple matches and "
+ "not throw an error in case there are no matches found for given "
+ "expression.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/IscsiConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/IscsiConfigCommand.py
new file mode 100644
index 0000000..a6e0d31
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/IscsiConfigCommand.py
@@ -0,0 +1,978 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""IscsiConfig Command for rdmc"""
+
+import json
+import re
+import time
+
+import redfish.ris
+
+try:
+ from rdmc_helper import (
+ BootOrderMissingEntriesError,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NicMissingOrConfigurationError,
+ ReturnCodes,
+ )
+except:
+ from ilorest.rdmc_helper import (
+ BootOrderMissingEntriesError,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NicMissingOrConfigurationError,
+ ReturnCodes,
+ )
+
+
+class IscsiConfigCommand:
+ """Changes the iscsi configuration for the server that is currently logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "iscsiconfig",
+ "usage": None,
+ "description": "Run without"
+ " arguments for available NIC sources for iSCSI"
+ " configuration.\n\texample: iscsiconfig\n\n\tDisplay"
+ " the current iSCSI configuration:\n\texample: "
+ "iscsiconfig --list\n\n\tSaving current iSCSI "
+ "configuration to a file:\n\texample: iscsiconfig "
+ "--list -f output.txt\n\n\tLoading iSCSI "
+ "configurations from a file:\n\texample: iscsiconfig "
+ "--modify output.txt\n\n\tIn order to add a NIC "
+ 'source to an iSCSI boot attempt you must run \n\t"'
+ 'iscsiconfig" without any paramters. This will '
+ "display a list of NIC\n\tsources which are currently"
+ " present in the system.\n\tAdding an iSCSI boot "
+ "attempt:\n\texample: iscsiconfig --add 1\n\n\tIn "
+ "order to delete an iSCSI boot attempt you must run"
+ '\n\t"iscsiconfig --list" to view the currently '
+ "configured attempts.\n\tOnce you find the attempt "
+ "you want to delete simply pass the attempt\n\tnumber"
+ " to the iSCSI delete function.\n\tDeleting an iSCSI "
+ "boot attempt:\n\texample: iscsiconfig --delete 1",
+ "summary": "Displays and configures the current iscsi settings.",
+ "aliases": [],
+ "auxcommands": [
+ "GetCommand",
+ "SetCommand",
+ "SelectCommand",
+ "RebootCommand",
+ "LogoutCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main iscsi configuration worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.iscsiconfigurationvalidation(options)
+
+ if self.rdmc.app.typepath.defs.isgen10:
+ iscsipath = self.gencompatpaths(selector="HpeiSCSISoftwareInitiator.", rel=False)
+ if "/settings" not in iscsipath:
+ iscsisettingspath = iscsipath + "settings"
+ else:
+ iscsisettingspath = iscsipath
+ bootpath = self.gencompatpaths(selector="HpeServerBootSettings.")
+ bootpath = bootpath.replace("/settings", "")
+ else:
+ # TODO: update gencompats to handle the nesting of these links within the gen 9 version.
+ if self.rdmc.app.typepath.defs.biospath[-1] == "/":
+ iscsipath = self.rdmc.app.typepath.defs.biospath + "iScsi/"
+ iscsisettingspath = self.rdmc.app.typepath.defs.biospath + "iScsi/settings/"
+ bootpath = self.rdmc.app.typepath.defs.biospath + "Boot/"
+ else:
+ iscsipath = self.rdmc.app.typepath.defs.biospath + "/iScsi"
+ iscsisettingspath = self.rdmc.app.typepath.defs.biospath + "/iScsi/settings"
+ bootpath = self.rdmc.app.typepath.defs.biospath + "/Boot"
+
+ if options.list:
+ self.listoptionhelper(options, iscsipath, iscsisettingspath, bootpath)
+ elif options.modify:
+ self.modifyoptionhelper(options, iscsisettingspath)
+ elif options.add:
+ self.addoptionhelper(options, iscsipath, iscsisettingspath, bootpath)
+ elif options.delete:
+ self.deleteoptionhelper(options, iscsisettingspath)
+ elif not args:
+ self.defaultsoptionhelper(options, iscsipath, bootpath)
+ else:
+ if len(args) < 2:
+ self.iscsiconfigurationvalidation(options)
+ else:
+ raise InvalidCommandLineError(
+ "Invalid number of parameters. " "Iscsi configuration takes a maximum of 1 parameter."
+ )
+
+ if options.reboot:
+ self.auxcommands["reboot"].run(options.reboot)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def gencompatpaths(self, selector=None, rel=False):
+ """Helper function for finding gen compatible paths
+
+ :param selector: the type selection for the get operation
+ :type selector: str.
+ :param rel: flag to tell select function to reload selected instance
+ :type rel: boolean.
+ :returns: returns urls
+ """
+
+ self.rdmc.app.select(selector=selector, path_refresh=rel)
+ props = self.rdmc.app.getprops(skipnonsetting=False)
+ for prop in props:
+ name = prop.get("Name")
+ if "current" in name.lower() or "pending" in name.lower():
+ try:
+ path = prop.get("@odata.id")
+ except:
+ self.rdmc.ui.error("URI path could not be found.")
+ return path
+
+ def get_enabled_only(self):
+ enable_disable = dict()
+ final_enabled_list = list()
+
+ bios = "/redfish/v1/systems/1/bios/"
+ bios_dict = self.rdmc.app.get_handler(bios, silent=True, service=True).dict
+ if "Attributes" in bios_dict:
+ bios_dict_attributes = bios_dict["Attributes"].items()
+ else:
+ bios_dict_attributes = bios_dict.items()
+ for attr, val in bios_dict_attributes:
+ if "NetworkBoot" in str(val):
+ enable_disable[attr] = val
+ # Enabled NIC only listing
+ for k, v in enable_disable.items():
+ final_enabled_list.append(k)
+
+ return final_enabled_list
+
+ def addoptionhelper(self, options, iscsipath, iscsisettingspath, bootpath):
+ """Helper function to add option for iscsi
+
+ :param options: command line options
+ :type options: list.
+ :param iscsipath: current iscsi path
+ :type iscsipath: str.
+ :param iscsisettingspath: current iscsi settings path
+ :type iscsisettingspath: str.
+ :param bootpath: current boot path
+ :type bootpath: str.
+ """
+ devicealloc = list()
+ self.auxcommands["select"].selectfunction("HpeBiosMapping.")
+ pcisettingsmap = self.auxcommands["get"].getworkerfunction(
+ "BiosPciSettingsMappings", options, results=True, uselist=True
+ )
+
+ for item in pcisettingsmap[0]["BiosPciSettingsMappings"]:
+ if "Associations" in item:
+ if (
+ "EmbNicEnable" in item["Associations"]
+ or "EmbNicConfig" in item["Associations"]
+ or "OcpAEnable" in item["Associations"]
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+
+ if (
+ re.match("FlexLom[0-9]+Enable", item["Associations"][0])
+ or re.match("PciSlot[0-9]+Enable", item["Associations"][0])
+ or re.match("Slot[0-9]+NicBoot[0-9]+", item["Associations"][0])
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+
+ foundlocation = False
+ iscsibootsources = self.rawdatahandler(action="GET", silent=True, jsonflag=True, path=iscsisettingspath)
+ count = 0
+ attemptinstancenumber = self.bootattemptcounter(iscsibootsources[self.rdmc.app.typepath.defs.iscsisource])
+ if self.rdmc.app.typepath.defs.isgen10:
+ newpcilist = []
+ self.auxcommands["select"].selectfunction("HpeServerPciDeviceCollection")
+ pcideviceslist = next(
+ iter(self.auxcommands["get"].getworkerfunction("Members", options, results=True, uselist=False)),
+ None,
+ )
+ for device in pcideviceslist["Members"]:
+ newpcilist.append(self.rdmc.app.get_handler(device["@odata.id"], silent=True).dict)
+ pcideviceslist = newpcilist
+ else:
+ self.auxcommands["select"].selectfunction(["Collection."])
+ pcideviceslist = next(
+ iter(
+ self.auxcommands["get"].getworkerfunction(
+ "Items",
+ options,
+ results=True,
+ uselist=False,
+ filtervals=("MemberType", "HpServerPciDevice.*"),
+ )
+ ),
+ None,
+ )["Items"]
+
+ self.pcidevicehelper(devicealloc, iscsipath, bootpath, pcideviceslist)
+
+ devicealloc_final = self.make_final_list(devicealloc, pcideviceslist)
+
+ print_flag = False
+ iscsi_list = self.listoptionhelper(options, iscsipath, iscsisettingspath, bootpath, print_flag)
+ refined_list = list()
+ for s in iscsi_list:
+ for key, val in s.items():
+ if key != "Not Added":
+ for k, v in val.items():
+ refined_list.append(v["iSCSINicSource"])
+ enabled_only_bootsource = self.get_enabled_only()
+ refined_devicealloc = list()
+ for d in devicealloc:
+ if "PreBootNetwork" in d["Associations"][0]:
+ nic = d["Associations"][1]
+ else:
+ nic = d["Associations"][0]
+ if "Storage" in nic:
+ continue
+ if nic in enabled_only_bootsource:
+ refined_devicealloc.append(d)
+
+ for item in iscsibootsources[self.rdmc.app.typepath.defs.iscsisource]:
+ try:
+ if not item[self.rdmc.app.typepath.defs.iscsiattemptinstance]:
+ nicsourcedata = refined_devicealloc[int(options.add) - 1]["Associations"]
+ if "PreBootNetwork" in nicsourcedata[0]:
+ nic_getting_added = nicsourcedata[1]
+ else:
+ nic_getting_added = nicsourcedata[0]
+ if nic_getting_added in refined_list:
+ self.rdmc.ui.printer("Warning: This NIC is already added to existing attempt\n")
+ iscsibootsources[self.rdmc.app.typepath.defs.iscsisource][count]["iSCSINicSource"] = (
+ nicsourcedata[1] if isinstance(nicsourcedata[0], dict) else nicsourcedata[0]
+ )
+
+ iscsibootsources[self.rdmc.app.typepath.defs.iscsisource][count][
+ self.rdmc.app.typepath.defs.iscsiattemptinstance
+ ] = attemptinstancenumber
+ iscsibootsources[self.rdmc.app.typepath.defs.iscsisource][count][
+ self.rdmc.app.typepath.defs.iscsiattemptname
+ ] = str(attemptinstancenumber)
+ foundlocation = True
+ break
+ except Exception:
+ raise NicMissingOrConfigurationError("Invalid input value for configuring NIC.")
+ count += 1
+
+ if foundlocation:
+ self.rdmc.app.patch_handler(
+ iscsisettingspath,
+ iscsibootsources,
+ optionalpassword=options.biospassword,
+ )
+ else:
+ raise NicMissingOrConfigurationError("Failed to add NIC. All NICs" " have already been configured.")
+
+ def make_final_list(self, devicealloc, pcideviceslist):
+ final_devicealloc = []
+ for item in devicealloc:
+ if isinstance(item["Associations"][0], dict):
+ listval = 1
+ else:
+ listval = 0
+ if "Storage" not in item["Associations"][listval]:
+ for pcidevice in pcideviceslist:
+ # if item["CorrelatableID"] == pcidevice["UEFIDevicePath"]:
+ final_devicealloc.append(item)
+ return final_devicealloc
+
+ def bootattemptcounter(self, bootsources):
+ """Helper function to count the current boot entries for iscsi
+
+ :param bootsources: current iscsi boot sources
+ :type bootsources: list.
+ """
+ size = 0
+ count = list()
+
+ for item in bootsources:
+ size += 1
+
+ if item[self.rdmc.app.typepath.defs.iscsiattemptinstance]:
+ count.append(int(item[self.rdmc.app.typepath.defs.iscsiattemptinstance]))
+
+ if size == len(count):
+ raise NicMissingOrConfigurationError("Failed to add NIC. All " "NICs have already been configured.")
+
+ count.sort(key=None, reverse=False)
+
+ if len(count) > 0:
+ iterate = 0
+
+ for i in range(1, size + 1, 1):
+ if iterate < len(count) and i == count[iterate]:
+ iterate += 1
+ else:
+ return iterate + 1
+ else:
+ return int(1)
+
+ def deleteoptionhelper(self, options, iscsisettingspath):
+ """Helper function to delete option for iscsi
+
+ :param options: command line options
+ :type options: list.
+ :param iscsisettingspath: current iscsi settings path
+ :type iscsisettingspath: string.
+ """
+ patch = None
+
+ self.auxcommands["select"].selectfunction("HpBaseConfigs.")
+ contents = self.rdmc.app.getprops(selector="BaseConfigs")
+
+ for content in contents:
+ for key in content["BaseConfigs"][0]["default"]:
+ if key == self.rdmc.app.typepath.defs.iscsisource:
+ patch = content["BaseConfigs"][0]["default"][key]
+
+ if not patch:
+ raise NicMissingOrConfigurationError("Could not access Base Configurations.")
+
+ self.validateinput(options=options, deleteoption=True)
+
+ foundlocation = False
+ iscsibootsources = self.rawdatahandler(action="GET", silent=True, jsonflag=False, path=iscsisettingspath)
+ holdetag = iscsibootsources.getheader("etag")
+ iscsibootsources = json.loads(iscsibootsources.read)
+
+ try:
+ count = 0
+ for item in iscsibootsources[self.rdmc.app.typepath.defs.iscsisource]:
+ if item[self.rdmc.app.typepath.defs.iscsiattemptinstance] == int(options.delete):
+ iscsibootsources[self.rdmc.app.typepath.defs.iscsisource][count] = patch[count]
+ foundlocation = True
+
+ count += 1
+ except Exception:
+ raise NicMissingOrConfigurationError(
+ "The NIC targeted for delete" " does not exist. The request for " "delete could not be completed."
+ )
+
+ if foundlocation:
+ self.rdmc.app.put_handler(
+ iscsisettingspath,
+ iscsibootsources,
+ optionalpassword=options.biospassword,
+ headers={"if-Match": holdetag},
+ )
+ self.rdmc.app.get_handler(iscsisettingspath, silent=True)
+ else:
+ raise NicMissingOrConfigurationError("The given attempt instance does not exist.")
+
+ def listoptionhelper(self, options, iscsipath, iscsisettingspath, bootpath, print_flag=True):
+ """Helper function to list options for iscsi
+
+ :param options: command line options
+ :type options: list.
+ :param iscsipath: current iscsi path
+ :type iscsipath: str.
+ :param iscsisettingspath: current iscsi settings path
+ :type iscsisettingspath: str.
+ :param bootpath: current boot path
+ :type bootpath: str.
+ """
+ self.auxcommands["select"].selectfunction("HpeBiosMapping.")
+ pcisettingsmap = self.auxcommands["get"].getworkerfunction(
+ "BiosPciSettingsMappings", options, results=True, uselist=True
+ )
+
+ devicealloc = list()
+ for item in pcisettingsmap[0]["BiosPciSettingsMappings"]:
+ if "Associations" in item:
+ if (
+ "EmbNicEnable" in item["Associations"]
+ or "EmbNicConfig" in item["Associations"]
+ or "OcpAEnable" in item["Associations"]
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+
+ if (
+ re.match("FlexLom[0-9]+Enable", item["Associations"][0])
+ or re.match("PciSlot[0-9]+Enable", item["Associations"][0])
+ or re.match("Slot[0-9]+NicBoot[0-9]+", item["Associations"][0])
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+
+ if self.rdmc.app.typepath.defs.isgen10:
+ newpcilist = []
+
+ self.auxcommands["select"].selectfunction("HpeServerPciDeviceCollection")
+ pcideviceslist = next(
+ iter(self.auxcommands["get"].getworkerfunction("Members", options, results=True, uselist=False)),
+ None,
+ )
+
+ for device in pcideviceslist["Members"]:
+ newpcilist.append(self.rdmc.app.get_handler(device["@odata.id"], silent=True).dict)
+
+ pcideviceslist = newpcilist
+ else:
+ self.auxcommands["select"].selectfunction(["Collection."])
+ pcideviceslist = next(
+ iter(
+ self.auxcommands["get"].getworkerfunction(
+ "Items",
+ options,
+ results=True,
+ uselist=False,
+ filtervals=("MemberType", "HpServerPciDevice.*"),
+ )
+ ),
+ None,
+ )["Items"]
+ i = 0
+ iscsibootsources = ""
+ while i < 4:
+ self.auxcommands["select"].selectfunction("HpiSCSISoftwareInitiator.")
+ iscsibootsources = self.rawdatahandler(action="GET", silent=True, jsonflag=True, path=iscsisettingspath)
+ if not ("error" in iscsibootsources):
+ break
+ time.sleep(10)
+ i = i + 1
+ self.rdmc.ui.printer("Retrying ...\n\n")
+ structeredlist = list()
+
+ self.pcidevicehelper(devicealloc, iscsipath, bootpath, pcideviceslist)
+ if not ("error" in iscsibootsources):
+ total_attempts = []
+ for item in iscsibootsources[self.rdmc.app.typepath.defs.iscsisource]:
+ if item["iSCSINicSource"]:
+ iscsi_attempt = item[self.rdmc.app.typepath.defs.iscsiattemptinstance]
+ for device in devicealloc:
+ it = device["CorrelatableID"].split(",")
+ del it[-1]
+ it = ",".join(it)
+ listval = 0
+ if not isinstance(device["Associations"][0], list):
+ listval = 1 if isinstance(device["Associations"][0], dict) else 0
+
+ if item["iSCSINicSource"] == device["Associations"][listval]:
+ for pcidevice in pcideviceslist:
+ if "Storage" in pcidevice["DeviceType"]:
+ continue
+ pcid = pcidevice["UEFIDevicePath"].split(",")
+ del pcid[-1]
+ pcid = ",".join(pcid)
+ # using total_attempts to avoid duplicate entries
+ if (
+ device["CorrelatableID"] == pcidevice["UEFIDevicePath"]
+ or ("Embedded" not in pcidevice["DeviceType"] and (pcid == it))
+ ) and iscsi_attempt not in total_attempts:
+ total_attempts.append(iscsi_attempt)
+ inputstring = (
+ pcidevice["DeviceType"]
+ + " "
+ + str(pcidevice["DeviceInstance"])
+ + " Port "
+ + str(device["Subinstance"])
+ + " : "
+ + pcidevice.get("Name", "").strip()
+ )
+ structeredlist.append({inputstring: {str("Attempt " + str(iscsi_attempt)): item}})
+
+ else:
+ structeredlist.append({"Not Added": {}})
+
+ try:
+ if iscsibootsources is None:
+ raise BootOrderMissingEntriesError("No entries found for iscsi boot sources \n\n")
+ elif "error" in iscsibootsources:
+ raise BootOrderMissingEntriesError(
+ "/redfish/v1/systems/1/bios/oem/hpe/iscsi/settings URI seems to be not reachable even after"
+ " 4 retry attempts , kindly retry the command after some time \n\n"
+ )
+ elif not options.filename and print_flag:
+ self.print_iscsi_config_helper(structeredlist, "Current iSCSI Attempts: \n")
+ except Exception as excp:
+ raise excp
+
+ if structeredlist is None:
+ self.rdmc.ui.error("No entries found for iscsi boot sources.\n\n")
+ elif options.filename:
+ output = json.dumps(structeredlist, indent=2, cls=redfish.ris.JSONEncoder, sort_keys=True)
+ filehndl = open(options.filename[0], "w")
+ filehndl.write(output)
+ filehndl.close()
+
+ self.rdmc.ui.printer("Results written out to '%s'\n" % options.filename[0])
+ return structeredlist
+
+ def defaultsoptionhelper(self, options, iscsipath, bootpath):
+ """Helper function for default options for iscsi
+
+ :param options: command line options
+ :type options: list.
+ :param iscsipath: current iscsi path
+ :type iscsipath: str.
+ :param bootpath: current boot path
+ :type bootpath: str.
+ """
+ self.auxcommands["select"].selectfunction("HpBiosMapping.")
+ pcisettingsmap = self.auxcommands["get"].getworkerfunction(
+ "BiosPciSettingsMappings", options, results=True, uselist=True
+ )
+
+ devicealloc = list()
+ for item in pcisettingsmap[0]["BiosPciSettingsMappings"]:
+ if "Associations" in item:
+ # self.rdmc.ui.printer("Assoc 1 : %s\n" % (item["Associations"]))
+ # self.rdmc.ui.printer("Sub 1 : %s\n" % (item["Subinstances"]))
+ if (
+ "EmbNicEnable" in item["Associations"]
+ or "EmbNicConfig" in item["Associations"]
+ or "OcpAEnable" in item["Associations"]
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+
+ try:
+ if (
+ re.match("FlexLom[0-9]+Enable", item["Associations"][0])
+ or re.match("PciSlot[0-9]+Enable", item["Associations"][0])
+ or re.match("Slot[0-9]+NicBoot[0-9]+", item["Associations"][0])
+ ):
+ _ = [devicealloc.append(x) for x in item["Subinstances"]]
+ except IndexError:
+ pass
+ # self.rdmc.ui.printer("Device alloc : %s\n" % devicealloc)
+
+ if self.rdmc.app.typepath.defs.isgen10:
+ newpcilist = []
+ self.auxcommands["select"].selectfunction("HpeServerPciDeviceCollection")
+ pcideviceslist = self.auxcommands["get"].getworkerfunction("Members", options, results=True, uselist=False)
+ pcideviceslist = pcideviceslist[0]
+ for device in pcideviceslist["Members"]:
+ newpcilist.append(self.rdmc.app.get_handler(device["@odata.id"], silent=True).dict)
+ pcideviceslist = newpcilist
+ else:
+ self.auxcommands["select"].selectfunction(["Collection."])
+ pcideviceslist = self.auxcommands["get"].getworkerfunction(
+ "Items",
+ options,
+ results=True,
+ uselist=False,
+ filtervals=("MemberType", "HpServerPciDevice.*"),
+ )[0]["Items"]
+
+ self.auxcommands["select"].selectfunction(self.rdmc.app.typepath.defs.hpiscsisoftwareinitiatortype)
+ iscsiinitiatorname = self.auxcommands["get"].getworkerfunction(
+ "iSCSIInitiatorName", options, results=True, uselist=True
+ )
+
+ disabledlist = self.pcidevicehelper(devicealloc, iscsipath, bootpath, pcideviceslist)
+
+ self.print_out_iscsi_configuration(iscsiinitiatorname[0], devicealloc, pcideviceslist)
+
+ if disabledlist:
+ self.print_out_iscsi_configuration(iscsiinitiatorname[0], disabledlist, pcideviceslist, disabled=True)
+
+ def modifyoptionhelper(self, options, iscsisettingspath):
+ """Helper function to modify options for iscsi
+
+ :param options: command line options
+ :type options: list.
+ :param iscsisettingspath: current iscsi settings path
+ :type iscsisettingspath: str.
+ """
+ try:
+ inputfile = open(options.modify, "r")
+ contentsholder = json.loads(inputfile.read())
+ except Exception as excp:
+ raise InvalidCommandLineError("%s" % excp)
+
+ iscsibootsources = self.rawdatahandler(action="GET", silent=True, jsonflag=True, path=iscsisettingspath)
+
+ count = 0
+ resultsdict = list()
+
+ for item in contentsholder:
+ for entry in item.values():
+ enteredsection = False
+
+ for key, value in entry.items():
+ enteredsection = True
+ resultsdict.append(
+ self.modifyfunctionhelper(
+ key,
+ value,
+ iscsibootsources[self.rdmc.app.typepath.defs.iscsisource],
+ )
+ )
+
+ if not enteredsection:
+ resultsdict.append(iscsibootsources[self.rdmc.app.typepath.defs.iscsisource][count])
+
+ count += 1
+
+ contentsholder = {self.rdmc.app.typepath.defs.iscsisource: resultsdict}
+
+ self.rdmc.app.patch_handler(iscsisettingspath, contentsholder, optionalpassword=options.biospassword)
+ self.rdmc.app.get_handler(iscsisettingspath, silent=True)
+ self.rdmc.ui.printer("Please reboot the server for changes to take effect\n")
+
+ def modifyfunctionhelper(self, key, value, bootsources):
+ """Helper function to modify the entries for iscsi
+
+ :param key: key to be used for attempt
+ :type key: string.
+ :param value: value to apply to attempt
+ :type value: str.
+ :param bootsources: current boot sources
+ :type bootsources: list.
+ """
+ foundoption = False
+
+ for bootsource in bootsources:
+ if bootsource[self.rdmc.app.typepath.defs.iscsiattemptinstance] == int(key[-1:]):
+ foundoption = True
+ break
+ else:
+ return value
+ if foundoption:
+ return value
+
+ def pcidevicehelper(self, devicealloc, iscsipath, bootpath, pcideviceslist=None, options=None):
+ """Helper function to check for extra pci devices / identify disabled devices
+
+ :param devicealloc: list of devices allocated
+ :type devicealloc: list.
+ :param iscsipath: current iscsi path
+ :type iscsipath: str.
+ :param bootpath: current boot path
+ :type bootpath: str.
+ :param pcideviceslist: current pci device list
+ :type pcideviceslist: list.
+ :param options: command line options
+ :type options: list.
+ """
+ if not pcideviceslist:
+ if self.rdmc.app.typepath.defs.isgen10:
+ newpcilist = []
+ self.auxcommands["select"].selectfunction("HpeServerPciDeviceCollection")
+ pcideviceslist = next(
+ iter(self.auxcommands["get"].getworkerfunction("Members", options, results=True, uselist=False)),
+ None,
+ )
+
+ for device in pcideviceslist["Members"]:
+ newpcilist.append(self.rdmc.app.get_handler(device["@odata.id"], silent=True).dict)
+
+ pcideviceslist = newpcilist
+ else:
+ self.auxcommands["select"].selectfunction(["Collection."])
+ pcideviceslist = next(
+ iter(
+ self.auxcommands["get"].getworkerfunction(
+ "Items",
+ options,
+ results=True,
+ uselist=False,
+ filtervals=("MemberType", "HpServerPciDevice.*"),
+ )
+ ),
+ None,
+ )["Items"]
+ try:
+ self.rawdatahandler(action="GET", silent=True, jsonflag=True, path=iscsipath)["iSCSINicSources"]
+ except:
+ raise NicMissingOrConfigurationError("No iSCSI nic sources available.")
+
+ _ = [x["UEFIDevicePath"] for x in pcideviceslist]
+ removal = list()
+
+ bios = self.rawdatahandler(action="GET", silent=True, jsonflag=True, path=bootpath)
+
+ for item in devicealloc:
+ if isinstance(item["Associations"][0], dict):
+ if "PreBootNetwork" in list(item["Associations"][0].keys()):
+ if item["Associations"] and item["Associations"][0]["PreBootNetwork"] in list(bios.keys()):
+ if bios[item["Associations"][0]["PreBootNetwork"]] == "Disabled":
+ removal.append(item)
+ else:
+ if item["Associations"] and item["Associations"][0] in list(bios.keys()):
+ if bios[item["Associations"][0]] == "Disabled":
+ removal.append(item)
+
+ _ = [devicealloc.remove(x) for x in removal]
+
+ return removal
+
+ def print_out_iscsi_configuration(self, iscsiinitiatorname, devicealloc, pcideviceslist, disabled=False):
+ """Convert content to human readable and print out to std.out
+
+ :param iscsiinitiatorname: iscsi initiator name
+ :type iscsiinitiatorname: str.
+ :param devicealloc: list of devices allocated
+ :type devicealloc: list.
+ :param pcideviceslist: current pci device list
+ :type pcideviceslist: list.
+ :param disabled: command line options
+ :type disabled: boolean.
+ """
+ try:
+ if iscsiinitiatorname is None:
+ BootOrderMissingEntriesError("No entry found for the iscsi initiator name.\n\n")
+ elif disabled:
+ pass
+ else:
+ self.print_iscsi_config_helper(iscsiinitiatorname["iSCSIInitiatorName"], "\nIscsi Initiator Name: ")
+ except Exception as excp:
+ raise excp
+
+ try:
+ final_device_list = list()
+ enable_val = list()
+
+ bios = "/redfish/v1/systems/1/bios/"
+ bios_dict = self.rdmc.app.get_handler(bios, silent=True, service=True).dict
+ if "Attributes" in bios_dict:
+ bios_dict_items = bios_dict["Attributes"].items()
+ else:
+ bios_dict_items = bios_dict.items()
+ for attr, val in bios_dict_items:
+ if "NetworkBoot" in str(val):
+ enable_val.append(attr.lower().strip())
+
+ # before comparing with pcideviceslist filtering enabled nics only
+ devicealloc = [
+ val
+ for val in devicealloc
+ if (len(val["Associations"]) == 2 and val["Associations"][1].lower() in enable_val)
+ ]
+
+ if devicealloc and pcideviceslist:
+ count = 0
+ output_data = []
+ for pcidevice in pcideviceslist:
+ if "storage" in pcidevice["DeviceType"].lower():
+ continue
+
+ pcid = pcidevice["UEFIDevicePath"].split(",")
+ del pcid[-1]
+ pcid = ",".join(pcid)
+
+ for item in devicealloc:
+ it = item["CorrelatableID"].split(",")
+ del it[-1]
+ it = ",".join(it)
+
+ # Match the correlatable ID from the
+ # PCI device & allocated device
+ if pcid == it:
+ # Format the device information
+ device = (
+ pcidevice["DeviceType"]
+ + " "
+ + str(pcidevice["DeviceInstance"])
+ + " Port "
+ + str(item["Subinstance"])
+ )
+ # final_device_list using to
+ # avoid the duplicate entries
+ if device not in final_device_list:
+ final_device_list.append(device)
+ # Add the device name or
+ # structured name to the dictionary
+ if "Name" not in pcidevice:
+ name = pcidevice.get(
+ "StructuredName",
+ "",
+ ).strip()
+ else:
+ name = pcidevice.get("Name", "").strip()
+
+ count = count + 1
+ output_data.append(f"{[count]} {device} : {name}\n")
+
+ if output_data:
+ # display output data
+ status = "Disabled" if disabled else "Available"
+ self.rdmc.ui.printer(f"\n{status} iSCSI Boot Network Interfaces: \n")
+ [self.rdmc.ui.printer(i) for i in output_data]
+ print() # For a blank line, after the output
+ else:
+ self.rdmc.ui.printer("No iSCSI Boot Network Interfaces Available.\n\n")
+
+ else:
+ raise BootOrderMissingEntriesError("No entries found for" " iscsi configurations devices.\n")
+ except Exception as excp:
+ raise excp
+
+ def print_iscsi_config_helper(self, content, outstring, indent=0):
+ """Print iscsi configuration helper
+
+ :param content: current content to be output
+ :type content: string.
+ :param outstring: current output string
+ :type outstring: str.
+ :param indent: current iscsi settings path
+ :type indent: str.
+ """
+ self.rdmc.ui.printer("\t" * indent + outstring)
+
+ if content:
+ self.rdmc.ui.print_out_json(content)
+ else:
+ self.rdmc.ui.error("\t" * indent + "No entries currently configured.\n")
+
+ self.rdmc.ui.printer("\n\n")
+
+ def rawdatahandler(self, action="None", path=None, silent=True, jsonflag=False):
+ """Helper function to get and put the raw data
+
+ :param action: current rest action
+ :type action: list.
+ :param path: current path
+ :type path: str.
+ :param silent: flag to determine silent mode
+ :type silent: boolean.
+ :param jsonflag: flag to determine json output
+ :type jsonflag: boolean.
+ """
+ if action == "GET":
+ rawdata = self.rdmc.app.get_handler(get_path=path, silent=silent)
+
+ if jsonflag is True:
+ rawdata = json.loads(rawdata.read)
+
+ return rawdata
+
+ def validateinput(self, deviceallocsize=None, options=None, deleteoption=False):
+ """Helper function to validate that the input is correct
+
+ :param deviceallocsize: current device allocated size
+ :type deviceallocsize: str.
+ :param options: command line options
+ :type options: list.
+ :param deleteoption: flag to delete option
+ :type deleteoption: boolean.
+ """
+ if deviceallocsize:
+ try:
+ if int(options.add) - 1 >= deviceallocsize:
+ raise NicMissingOrConfigurationError("Please verify the " "given input value for configuring NIC.")
+ except Exception:
+ raise NicMissingOrConfigurationError("Please verify the " "given input value for configuring NIC.")
+ if deleteoption:
+ try:
+ if int(options.delete) == 0:
+ raise NicMissingOrConfigurationError(
+ "Invalid input value." "Please give valid attempt for instance values."
+ )
+ except Exception:
+ raise NicMissingOrConfigurationError(
+ "Invalid input value." "Please give valid attempt for instance values."
+ )
+
+ def iscsiconfigurationvalidation(self, options):
+ """iscsi configuration method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ if options.encode:
+ options.biospassword = Encryption.decode_credentials(options.biospassword)
+ if isinstance(options.biospassword, bytes):
+ options.biospassword = options.biospassword.decode("utf-8")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to use a different"
+ " filename than the default one. The default filename is"
+ " ilorest.json.",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "--add",
+ dest="add",
+ help="Use this iSCSI configuration option to add an iSCSI" " configuration option.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--delete",
+ dest="delete",
+ help="Use this iSCSI configuration option to delete an iSCSI" " configuration option.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--modify",
+ dest="modify",
+ help="Use this iSCSI configuration option to modify an iSCSI" " configuration option.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--list",
+ dest="list",
+ action="store_true",
+ help="Use this iSCSI configuration option to list the details" " of the different iSCSI configurations.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/SetPasswordCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/SetPasswordCommand.py
new file mode 100644
index 0000000..528bad6
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/SetPasswordCommand.py
@@ -0,0 +1,225 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""SetPassword Command for rdmc"""
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ UnableToDecodeError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ Encryption,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ UnableToDecodeError,
+ )
+
+
+class SetPasswordCommand:
+ """Set password class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "setpassword",
+ "usage": None,
+ "description": "Sets the admin password and power-on password\n"
+ "setpassword --newpassword --currentpassword [OPTIONS]\n\n\t"
+ "Setting the admin password with no previous password set."
+ "\n\texample: setpassword --newpassword testnew --currentpassword None\n\n\tSetting the admin "
+ "password back to nothing.\n\texample: setpassword --newpassword None --currentpassword testnew "
+ "\n\n\tSetting the power on password.\n\texample: setpassword"
+ " --newpassword testnew --currentpassword None --poweron\n\tNote: "
+ "if it is empty password, send None as above.",
+ "summary": "Sets the admin password and power-on password",
+ "aliases": [],
+ "auxcommands": [
+ "LoginCommand",
+ "SetCommand",
+ "SelectCommand",
+ "CommitCommand",
+ "RebootCommand",
+ "LogoutCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main set password worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.setpasswordvalidation(options)
+
+ # if not args:
+ # self.rdmc.ui.printer('Please input the current password.\n')
+ # tempoldpass = getpass.getpass()
+
+ # if tempoldpass and tempoldpass != '\r':
+ # tempoldpass = tempoldpass
+ # else:
+ # tempoldpass = '""'
+
+ # self.rdmc.ui.printer('Please input the new password.\n')
+ # tempnewpass = getpass.getpass()
+
+ # if tempnewpass and tempnewpass != '\r':
+ # tempnewpass = tempnewpass
+ # else:
+ # tempnewpass = '""'
+ # args.extend([tempnewpass, tempoldpass])
+
+ # if len(args) < 2:
+ # raise InvalidCommandLineError("Please pass both new password and old password.")
+
+ args = list()
+ args.append(options.newpassword)
+ args.append(options.currentpassword)
+ count = 0
+ for arg in args:
+ if arg:
+ if arg.lower() == "none" or arg.lower() == "null" or arg is None:
+ args[count] = ""
+ elif len(arg) > 2:
+ if ('"' in arg[0] and '"' in arg[-1]) or ("'" in arg[0] and "'" in arg[-1]):
+ args[count] = arg[1:-1]
+ elif len(arg) == 2:
+ if (arg[0] == '"' and arg[1] == '"') or (arg[0] == "'" and arg[1] == "'"):
+ args[count] = ""
+ count += 1
+
+ if options.encode:
+ _args = []
+ for arg in args:
+ try:
+ arg = Encryption.decode_credentials(arg)
+ if isinstance(arg, bytes):
+ arg = arg.decode("utf-8")
+ _args.append(arg)
+ except UnableToDecodeError:
+ _args.append(arg)
+ args = _args
+ if self.rdmc.app.typepath.defs.isgen10:
+ bodydict = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.biospath, service=True, silent=True).dict
+
+ for item in bodydict["Actions"]:
+ if "ChangePassword" in item:
+ path = bodydict["Actions"][item]["target"]
+ break
+
+ if options.poweron:
+ body = {
+ "PasswordName": "User",
+ "OldPassword": args[1],
+ "NewPassword": args[0],
+ }
+ else:
+ body = {
+ "PasswordName": "Administrator",
+ "OldPassword": args[1],
+ "NewPassword": args[0],
+ }
+
+ self.rdmc.app.post_handler(path, body)
+ else:
+ if options.poweron:
+ self.auxcommands["select"].run("HpBios.")
+ self.auxcommands["set"].run("PowerOnPassword=%s OldPowerOnPassword=%s" % (args[0], args[1]))
+ self.auxcommands["commit"].run("")
+ else:
+ self.auxcommands["select"].run("HpBios.")
+ self.auxcommands["set"].run("AdminPassword=%s OldAdminPassword=%s" % (args[0], args[1]))
+ self.auxcommands["commit"].run("")
+ self.rdmc.ui.printer(
+ "\nThe session will now be terminated.\n"
+ " login again with updated credentials in order to continue.\n"
+ )
+ self.auxcommands["logout"].run("")
+
+ if options:
+ if options.reboot:
+ self.auxcommands["reboot"].run(options.reboot)
+
+ self.cmdbase.logout_routine(self, options)
+ return ReturnCodes.SUCCESS
+
+ def setpasswordvalidation(self, options):
+ """Results method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--currentpassword",
+ dest="currentpassword",
+ help="Use this flag to provide current password.",
+ required=True,
+ )
+
+ customparser.add_argument(
+ "--newpassword",
+ dest="newpassword",
+ help="Use this flag to provide new password.",
+ required=True,
+ )
+
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after "
+ "completion of operations. 'REBOOT' is a replaceable parameter "
+ "that can have multiple values. For help with parameters and "
+ "descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--poweron",
+ dest="poweron",
+ action="store_true",
+ help="""Use this flag to set power on password instead""",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BiosDefaultsCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BiosDefaultsCommand.cpython-310.pyc
new file mode 100644
index 0000000..08b26da
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BiosDefaultsCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BootOrderCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BootOrderCommand.cpython-310.pyc
new file mode 100644
index 0000000..670ce4d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/BootOrderCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/IscsiConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/IscsiConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..e1fde02
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/IscsiConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/SetPasswordCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/SetPasswordCommand.cpython-310.pyc
new file mode 100644
index 0000000..9720483
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/SetPasswordCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..f6124ed
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/BIOS_COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/CommitCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/CommitCommand.py
new file mode 100644
index 0000000..9a9f821
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/CommitCommand.py
@@ -0,0 +1,140 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Commit Command for RDMC"""
+
+from redfish.ris.rmc_helper import NothingSelectedError
+
+try:
+ from rdmc_helper import (
+ FailureDuringCommitError,
+ InvalidCommandLineErrorOPTS,
+ NoChangesFoundOrMadeError,
+ NoCurrentSessionEstablished,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ FailureDuringCommitError,
+ InvalidCommandLineErrorOPTS,
+ NoChangesFoundOrMadeError,
+ NoCurrentSessionEstablished,
+ ReturnCodes,
+ )
+
+
+class CommitCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "commit",
+ "usage": None,
+ "description": "commit [OPTIONS]\n\n\tRun to apply all changes made during"
+ " the current session\n\texample: commit",
+ "summary": "Applies all the changes made during the current session.",
+ "aliases": [],
+ "auxcommands": ["LogoutCommand", "RebootCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def commitfunction(self, options=None):
+ """Main commit worker function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.commitvalidation()
+
+ self.rdmc.ui.printer("Committing changes...\n")
+
+ if options:
+ if options.biospassword:
+ self.rdmc.app.current_client.bios_password = options.biospassword
+ try:
+ failure = False
+ commit_opp = self.rdmc.app.commit()
+ for path in commit_opp:
+ if self.rdmc.opts.verbose:
+ self.rdmc.ui.printer("Changes are being made to path: %s\n" % path)
+ if next(commit_opp):
+ failure = True
+ except NothingSelectedError:
+ raise NoChangesFoundOrMadeError("No changes found or made during commit operation.")
+ else:
+ if failure:
+ raise FailureDuringCommitError(
+ "One or more types failed to commit. Run the "
+ "status command to see uncommitted data. "
+ "if you wish to discard failed changes refresh the "
+ "type using select with the --refresh flag."
+ )
+
+ if options.reboot:
+ self.auxcommands["reboot"].run(options.reboot)
+ self.auxcommands["logout"].run("")
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for commit main function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.commitfunction(options)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def commitvalidation(self):
+ """Commit method validation function"""
+
+ try:
+ _ = self.rdmc.app.current_client
+ except:
+ raise NoCurrentSessionEstablished("Please login and make setting" " changes before using commit command.")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/DetectiLOCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/DetectiLOCommand.py
new file mode 100644
index 0000000..8e4b238
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/DetectiLOCommand.py
@@ -0,0 +1,212 @@
+###
+# Copyright 2016-2024 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""DetectiLO Command for RDMC"""
+from redfish.hpilo.vnichpilo import AppAccount
+from redfish.rest.connections import ChifDriverMissingOrNotFound, VnicNotEnabledError
+import requests
+
+try:
+ from rdmc_helper import (
+ UI,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ GenBeforeLoginError,
+ VnicExistsError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ UI,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ GenBeforeLoginError,
+ VnicExistsError,
+ )
+
+
+class DetectiLOCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "detectilo",
+ "usage": None,
+ "description": "Provides the iLO generation of the currently logged-in server.\n\n"
+ "If --ignore_session is specified, it retrieves the iLO generation of the IP mentioned in --url\n"
+ "or attempts to determine the iLO generation of the current server.",
+ "summary": "Retrieves the iLO generation of the server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main get worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not options.ignore_session and not options.url:
+ client = self.detectilovalidation(options)
+ if client:
+ if not options.json:
+ self.rdmc.ui.printer("Using the current logged in session information.\n")
+ if not any(substring in client.base_url for substring in ("blobstore", "16.1.15.1")):
+ options.url = client.base_url.split("/")[-1]
+
+ if options.url:
+ if not options.json:
+ self.rdmc.ui.printer("Detecting iLO for remote device %s\n" % options.url)
+ retcode = self.detect_remote_ilotype(options)
+ else:
+ if not options.json:
+ self.rdmc.ui.printer("Detecting iLO for this server.\n")
+ retcode = self.detect_local_ilotype(options)
+ return retcode
+
+ def detectilovalidation(self, options):
+ """detectilo validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ return self.rdmc.login_select_validation(self, options)
+
+ def detect_remote_ilotype(self, options):
+ try:
+ path = "https://" + options.url + "/redfish/v1"
+ data = requests.get(path, verify=False)
+ json_data = data.json()
+ Oem_Hpe = json_data["Oem"]["Hpe"]
+ if "ManagerType" in Oem_Hpe["Manager"][0]:
+ ilo_ver = int(Oem_Hpe["Manager"][0]["ManagerType"].split(" ")[1])
+ else:
+ manager_data = Oem_Hpe["Moniker"]
+ ilo_ver = int(manager_data["PRODGEN"].split(" ")[1])
+ except Exception as excp:
+ raise GenBeforeLoginError(
+ "An error occurred while retrieving the iLO generation. "
+ "Please ensure that the virtual NIC is enabled for iLO7 based "
+ "servers, or that the CHIF driver is installed for iLO5 "
+ "and iLO6 based servers.\n"
+ )
+
+ if options.json:
+ content = {"iLOType": ilo_ver}
+ UI().print_out_json(content)
+ else:
+ self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
+
+ return ReturnCodes.SUCCESS
+
+ def detect_local_ilotype(self, options):
+ app_obj = AppAccount(log_dir=self.rdmc.log_dir)
+ security_state_dict = {
+ 0: "Error occurred while fetching the security state",
+ 1: "Factory",
+ 2: "Wipe",
+ 3: "Production",
+ 4: "HighSecurity",
+ 5: "FIPS",
+ 6: "SuiteB",
+ }
+ try:
+ ilo_ver, sec_state = self.rdmc.app.getilover_beforelogin(app_obj)
+ if options.json:
+ contents = {"iLOType": str(ilo_ver)}
+ if ilo_ver < 7:
+ contents["SecurityState"] = security_state_dict[sec_state]
+ UI().print_out_json(contents)
+ else:
+ self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
+ if ilo_ver < 7:
+ self.rdmc.ui.printer("Security State: %s\n" % security_state_dict[sec_state])
+ return ReturnCodes.SUCCESS
+ except ChifDriverMissingOrNotFound:
+ if options.json:
+ contents = {"iLOType": "5_6"}
+ UI().print_out_json(contents)
+ else:
+ self.rdmc.ui.printer("iLO Type: Unable to find iLO generation\n")
+ raise
+ except VnicNotEnabledError:
+ ilo_ver = 7
+ if options.json:
+ contents = {"iLOType": str(ilo_ver)}
+ UI().print_out_json(contents)
+ else:
+ self.rdmc.ui.printer("iLO Type: %s\n" % ilo_ver)
+ raise VnicExistsError(
+ "Unable to access iLO using virtual NIC. "
+ "Please ensure virtual NIC is enabled in iLO. "
+ "Ensure that virtual NIC in the host OS is "
+ "configured properly. Refer to documentation for more information.\n"
+ )
+ except Exception:
+ raise GenBeforeLoginError(
+ "An error occurred while retrieving the iLO generation. "
+ "Please ensure that the virtual NIC is enabled for iLO7 based "
+ "servers, or that the CHIF driver is installed for iLO5 and iLO6 "
+ "based servers.\n"
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "--ignore_session",
+ dest="ignore_session",
+ action="store_true",
+ help="Optionally include this flag if you wish to ignore any"
+ " session that has been logged in, in order to perform detectilo",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/GetCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/GetCommand.py
new file mode 100644
index 0000000..797ecbf
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/GetCommand.py
@@ -0,0 +1,512 @@
+###
+# Copyright 2016-2023 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Get Command for RDMC"""
+from collections import OrderedDict
+
+import six
+import logging
+
+import redfish.ris
+from redfish.ris.utils import iterateandclear
+
+try:
+ from rdmc_helper import (
+ UI,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ UI,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+try:
+ from rdmc_helper import HARDCODEDLIST
+except ImportError:
+ from ilorest.rdmc_helper import HARDCODEDLIST
+
+LOGGER = logging.getLogger(__name__)
+
+
+class GetCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "get",
+ "usage": None,
+ "description": "To retrieve all"
+ " the properties run without arguments. \n\t*Note*: "
+ "a type will need to be selected or this will return an "
+ "error.\n\texample: get\n\n\tTo retrieve multiple "
+ "properties use the following example\n\texample: "
+ "get Temperatures/ReadingCelsius Fans/Name --selector=Thermal."
+ "\n\n\tTo change output style format provide"
+ " the json flag\n\texample: get --json",
+ "summary": "Displays the current value(s) of a " "property(ies) within a selected type.",
+ "aliases": [],
+ "auxcommands": ["LogoutCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main get worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if getattr(options, "json"):
+ self.rdmc.json = True
+
+ selector = options.selector.lower() if options.selector else self.rdmc.app.selector.lower()
+
+ if "securityservice" in selector:
+ self.cmdbase.login_validation(self, options)
+ else:
+ self.getvalidation(options)
+
+ filtr = (None, None)
+ if options.filter:
+ try:
+ if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
+ options.filter = options.filter[1:-1]
+
+ (sel, val) = options.filter.split("=")
+ filtr = (sel.strip(), val.strip())
+
+ except (InvalidCommandLineError, SystemExit):
+ raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
+
+ self.getworkerfunction(
+ args,
+ options,
+ results=None,
+ uselist=True,
+ filtervals=filtr,
+ readonly=options.noreadonly,
+ )
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def getworkerfunction(
+ self,
+ args,
+ options,
+ readonly=False,
+ filtervals=(None, None),
+ results=None,
+ uselist=False,
+ ):
+ """main get worker function
+
+ :param args: command line arguments
+ :type args: list.
+ :param options: command line options
+ :type options: list.
+ :param line: command line input
+ :type line: string.
+ :param readonly: remove readonly properties
+ :type readonly: bool
+ :param filtervals: filter key value pair (Key,Val)
+ :type filtervals: tuple
+ :param results: current results collected
+ :type results: string.
+ :param uselist: use reserved properties list to filter results
+ :type uselist: boolean.
+ """
+ nocontent = set()
+ instances = None
+ arg = None
+
+ # For rest redfish compatibility of bios.
+ if hasattr(options, "selector") and options.selector:
+ self.rdmc.app.selector = options.selector
+ args = [args] if args and isinstance(args, six.string_types) else args
+ if self.rdmc.app.selector and "." not in self.rdmc.app.selector:
+ self.rdmc.app.selector = self.rdmc.app.selector + "."
+ args = (
+ [
+ (
+ "Attributes/" + arg
+ if self.rdmc.app.selector
+ and self.rdmc.app.selector.lower().startswith("bios.")
+ and not (
+ arg.lower() in ["id", "name", "attributeregistry", "attributes"] or "@odata" in arg.lower()
+ )
+ else arg
+ )
+ for arg in args
+ ]
+ if args
+ else args
+ )
+ if filtervals[0]:
+ instances = self.rdmc.app.select(selector=self.rdmc.app.selector, fltrvals=filtervals)
+
+ try:
+ if "securityservice" in self.rdmc.app.selector.lower():
+ url = "/redfish/v1/Managers/1/SecurityService/"
+ LOGGER.info("Fetching SecurityService data from: %s", url)
+ contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
+ security_contents = []
+ if not args and not options.json:
+ LOGGER.debug("Printing human-readable security service data.")
+ UI().print_out_human_readable(contents)
+ elif options and options.json and contents:
+ LOGGER.debug("Printing JSON security service data.")
+ UI().print_out_json(contents)
+ else:
+ attr = args[0]
+ contents_lower = {k.lower(): v for k, v in contents.items()}
+ security_contents.append({attr: contents_lower[attr.lower()]})
+ if security_contents:
+ UI().print_out_human_readable(security_contents)
+ elif (
+ "componentintegrity"
+ in self.rdmc.app.selector.lower()
+ # or "bios." in self.rdmc.app.selector.lower()
+ ):
+ url = ""
+ if "componentintegrity" in self.rdmc.app.selector.lower():
+ url = "/redfish/v1/ComponentIntegrity/?$expand=."
+ # elif "bios." in self.rdmc.app.selector.lower():
+ # url = "/redfish/v1/systems/1/bios/?$expand=."
+
+ LOGGER.info("Fetching %s data from: %s", self.rdmc.app.selector, url)
+ contents = self.get_content(url, args, uselist, options)
+ else:
+ LOGGER.debug("Handling general selector case.")
+ skipnonsettingflag = True
+ if "networkadapter" in self.rdmc.app.selector.lower():
+ skipnonsettingflag = False
+
+ if "selector" in options:
+ LOGGER.info("Fetching properties with selector: %s", options.selector)
+ contents = self.rdmc.app.getprops(
+ props=args,
+ remread=readonly,
+ selector=options.selector,
+ nocontent=nocontent,
+ insts=instances,
+ skipnonsetting=skipnonsettingflag,
+ )
+
+ if "networkadapter" in self.rdmc.app.selector.lower():
+ unique_adapters = {}
+ for entry in contents:
+ # Remove keys @odata.id & Id if it contains "settings"
+ adapter_id = entry.get("Id", "")
+ odata_id = entry.get("@odata.id", "")
+ if "settings" in odata_id.lower() or "settings" in adapter_id.lower():
+ continue
+ # Preventing deduplicates by Id
+ if adapter_id:
+ unique_adapters[adapter_id] = entry
+
+ if unique_adapters:
+ contents = list(unique_adapters.values())
+
+ if (
+ contents
+ and "logentrycollection" in self.rdmc.app.selector.lower()
+ and "@odata.id" in args
+ and not uselist
+ ):
+ # as per documentation
+ # collection of LogEntry resource instances
+ log_contents = self.rdmc.app.getprops(
+ props=["entries/@odata.id"],
+ remread=readonly,
+ selector="logservice",
+ nocontent=nocontent,
+ insts=instances,
+ skipnonsetting=skipnonsettingflag,
+ )
+ if log_contents:
+ log_contents = [
+ val["Entries"]
+ for val in log_contents
+ if "Entries" in val and "@odata.id" in val["Entries"]
+ ]
+
+ if log_contents:
+ contents.extend(log_contents)
+ # Removing duplicates
+ contents = list({val["@odata.id"]: val for val in contents}.values())
+
+ else:
+ LOGGER.info("Fetching properties without selector.")
+ contents = self.rdmc.app.getprops(
+ props=args, remread=readonly, nocontent=nocontent, insts=instances
+ )
+ uselist = False if readonly else uselist
+ except redfish.ris.rmc_helper.EmptyRaiseForEAFP:
+ LOGGER.error("EmptyRaiseForEAFP exception encountered, retrying getprops without remread.")
+ contents = self.rdmc.app.getprops(props=args, nocontent=nocontent)
+ for ind, content in enumerate(contents):
+ if "bios." in self.rdmc.app.selector.lower() and "Attributes" in list(content.keys()):
+ content.update(content["Attributes"])
+ del content["Attributes"]
+ LOGGER.debug("Flattened BIOS attributes in response.")
+ contents[ind] = OrderedDict(sorted(list(content.items()), key=lambda x: x[0]))
+ if len(args) > 0 and "Members" in args[0]:
+ uselist = False
+ if uselist:
+ for item in contents:
+ self.removereserved(item)
+ if results:
+ LOGGER.info("Returning retrieved contents.")
+ return contents
+
+ contents = contents[0] if len(contents) == 1 else contents
+
+ if options and options.json and contents:
+ LOGGER.debug("Printing output in JSON format.")
+ UI().print_out_json(contents)
+ elif contents:
+ LOGGER.debug("Printing output in human-readable format.")
+ UI().print_out_human_readable(contents)
+ else:
+ try:
+ if nocontent or not any(next(iter(contents))):
+ raise Exception()
+ except Exception:
+ strtoprint = ", ".join(str(val) for val in nocontent)
+ if not strtoprint and arg:
+ strtoprint = arg
+ LOGGER.error("No contents found for entry: %s", strtoprint)
+ raise NoContentsFoundForOperationError("No get contents found for entry: %s" % strtoprint)
+ else:
+ LOGGER.error("No contents found for selected type.")
+ raise NoContentsFoundForOperationError("No get contents found for " "selected type.")
+ if options.logout:
+ LOGGER.info("Logging out.")
+ self.auxcommands["logout"].run("")
+
+ def get_content(self, url, args, uselist, options):
+ """Get data from url"""
+ contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
+ if uselist:
+ # for get command removing @odata.* properties
+ args = [arg.lower() for arg in args if "@odata." not in arg.lower()]
+ else:
+ args = [arg.lower() for arg in args]
+
+ if contents and not args:
+ contents = [contents]
+ elif contents and args:
+ collect_data = []
+ for key, value in contents.items():
+ # Collect top-level keys based on args
+ if any(key.lower() == arg for arg in args):
+ collect_data.append({key: value})
+
+ if "attributes" == key.lower() and isinstance(value, dict) and "attributes" not in args:
+ attr_vals = {k.lower(): v for k, v in value.items()}
+ val_dict = {arg: attr_vals[arg.lower()] for arg in args if arg.lower() in attr_vals}
+ if val_dict:
+ collect_data.append(val_dict)
+
+ if key == "Members" and isinstance(value, list):
+ for member in value:
+ # Loop through each dict of Members
+ m_dict = {k.lower(): v for k, v in member.items()}
+ val_dict = {arg: m_dict[arg.lower()] for arg in args if arg.lower() in m_dict}
+ if val_dict:
+ collect_data.append(val_dict)
+
+ # collect_data = [dict(t) for t in {tuple(d.items()) for d in collect_data}]
+ contents = collect_data
+ else:
+ contents = []
+
+ return contents
+
+ def find_key_recursive(self, data, target_key):
+ """Recursively searches for a key in a nested dictionary (case-insensitive) and returns its value(s)."""
+ target_key_lower = target_key.lower() # Convert target_key to lowercase for case-insensitive matching
+
+ if isinstance(data, dict):
+ for key, value in data.items():
+ if key.lower() == target_key_lower: # Convert dictionary key to lowercase before comparison
+ yield value # Use yield to return multiple matches
+ elif isinstance(value, (dict, list)):
+ yield from self.find_key_recursive(value, target_key)
+
+ elif isinstance(data, list):
+ for item in data:
+ yield from self.find_key_recursive(item, target_key)
+
+ def removereserved(self, entry):
+ """function to remove reserved properties
+ :param entry: dictionary to remove reserved properties from
+ :type entry: dict.
+ """
+
+ for key, val in list(entry.items()):
+ if key.lower() in HARDCODEDLIST or "@odata" in key.lower():
+ del entry[key]
+ elif isinstance(val, list):
+ for item in entry[key]:
+ if isinstance(item, dict) and item:
+ self.removereserved(item)
+ if all([True if not test else False for test in entry[key]]):
+ del entry[key]
+ elif isinstance(val, dict):
+ self.removereserved(val)
+ if all([True if not test else False for test in entry[key]]):
+ del entry[key]
+
+ def checktoprint(self, options, contents, nocontent, arg):
+ """function to decide what/how to print
+ :param options: list of options
+ :type options: list.
+ :param contents: dictionary value returned by getprops.
+ :type contents: dict.
+ :param nocontent: props not found are added to the list.
+ :type nocontent: list.
+ :param arg: string of args
+ :type arg: string
+ """
+ if options and options.json and contents:
+ self.rdmc.ui.print_out_json(contents)
+ elif contents:
+ self.rdmc.ui.print_out_human_readable(contents)
+ else:
+ try:
+ if nocontent or not any(next(iter(contents))):
+ raise Exception()
+ except Exception:
+ strtoprint = ", ".join(str(val) for val in nocontent)
+ if not strtoprint and arg:
+ strtoprint = arg
+ raise NoContentsFoundForOperationError("No get contents " "found for entry: %s" % strtoprint)
+ else:
+ raise NoContentsFoundForOperationError("No get contents " "found for selected type.")
+
+ def collectandclear(self, contents, key, values):
+ """function to find and remove unneeded values from contents dictionary
+ :param contents: dictionary value returned by getprops
+ :type contents: dict.
+ :param key: string of keys
+ :type key: string.
+ :param values: list of values
+ :type values: list.
+ """
+ clearcontent = contents[0][key]
+ if isinstance(clearcontent, dict):
+ keyslist = list(clearcontent.keys())
+ else:
+ keyslist = [clearcontent]
+ clearedlist = keyslist
+ for arg in values:
+ for keys in keyslist:
+ if str(keys).lower() == str(arg).lower():
+ clearedlist.remove(arg)
+ contents = iterateandclear(contents, clearedlist)
+ return contents
+
+ def getvalidation(self, options):
+ """get method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run"
+ " the current command on. Use this flag when you wish to"
+ " select a type without entering another command, or if you"
+ " wish to work with a type that is different from the one"
+ " you currently have selected.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--filter",
+ dest="filter",
+ help="Optionally set a filter value for a filter attribute."
+ " This uses the provided filter for the currently selected"
+ " type. Note: Use this flag to narrow down your results. For"
+ " example, selecting a common type might return multiple"
+ " objects that are all of that type. If you want to modify"
+ " the properties of only one of those objects, use the filter"
+ " flag to narrow down results based on properties."
+ "\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
+ default=None,
+ )
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--noreadonly",
+ dest="noreadonly",
+ action="store_true",
+ help="Optionally include this flag if you wish to only show"
+ " properties that are not read-only. This is useful to see what "
+ "is configurable with the selected type(s).",
+ default=False,
+ )
+ customparser.add_argument(
+ "--refresh",
+ dest="ref",
+ action="store_true",
+ help="Optionally reload the data of selected type and clear " "patches from current selection.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/InfoCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/InfoCommand.py
new file mode 100644
index 0000000..e1898f8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/InfoCommand.py
@@ -0,0 +1,183 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Info Command for RDMC"""
+
+try:
+ from rdmc_helper import (
+ InfoMissingEntriesError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InfoMissingEntriesError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+try:
+ from rdmc_helper import HARDCODEDLIST
+except:
+ from ilorest.rdmc_helper import HARDCODEDLIST
+
+
+class InfoCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "info",
+ "usage": None,
+ "description": "Displays detailed "
+ "information about a property within a selected type"
+ "\n\texample: info property\n\n\tDisplays detailed "
+ "information for several properties\n\twithin a selected "
+ "type\n\texample: info property property/sub-property property\n\n\t"
+ "Run without arguments to display properties \n\tthat "
+ "are available for info command\n\texample: info",
+ "summary": "Displays detailed information about a property within a selected type.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, autotest=False, help_disp=False):
+ """Main info worker function
+
+ :param line: command line input
+ :type line: string.
+ :param autotest: flag to determine if running automatictesting
+ :type autotest: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.infovalidation(options)
+
+ if args:
+ for item in args:
+ if self.rdmc.app.selector.lower().startswith("bios.") and "attributes" not in item.lower():
+ if not (item.lower() in HARDCODEDLIST or "@odata" in item.lower()):
+ item = "Attributes/" + item
+
+ outdata = self.rdmc.app.info(props=item, dumpjson=options.json, latestschema=options.latestschema)
+
+ if autotest:
+ return outdata
+ if outdata and options.json:
+ self.rdmc.ui.print_out_json(outdata)
+ elif outdata:
+ self.rdmc.ui.printer(outdata)
+
+ if not outdata:
+ raise InfoMissingEntriesError("There are no valid " "entries for info in the current instance.")
+ else:
+ if len(args) > 1 and not item == args[-1]:
+ self.rdmc.ui.printer("\n************************************" "**************\n")
+ else:
+ results = set()
+ instances = self.rdmc.app.select()
+ for instance in instances:
+ currdict = instance.resp.dict
+ currdict = (
+ currdict["Attributes"]
+ if instance.maj_type.startswith(self.rdmc.app.typepath.defs.biostype)
+ and currdict.get("Attributes", None)
+ else currdict
+ )
+ results.update([key for key in currdict if key not in HARDCODEDLIST and "@odata" not in key.lower()])
+
+ if results and autotest:
+ return results
+ elif results:
+ self.rdmc.ui.printer("Info options:\n")
+ for item in results:
+ self.rdmc.ui.printer("%s\n" % item)
+ else:
+ raise InfoMissingEntriesError(
+ "No info items available for this selected type." " Try running with the --latestschema flag."
+ )
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def infovalidation(self, options):
+ """Info method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ if self.rdmc.opts.latestschema:
+ options.latestschema = True
+
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run"
+ " the current command on. Use this flag when you wish to"
+ " select a type without entering another command, or if you"
+ " wish to work with a type that is different from the one"
+ " you currently have selected.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--latestschema",
+ dest="latestschema",
+ action="store_true",
+ help="Optionally use the latest schema instead of the one "
+ "requested by the file. Note: May cause errors in some data "
+ "retrieval due to difference in schema versions.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ListCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ListCommand.py
new file mode 100644
index 0000000..0daa4e9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ListCommand.py
@@ -0,0 +1,180 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""List Command for RDMC"""
+
+try:
+ from rdmc_helper import (
+ UI,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ UI,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class ListCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "list",
+ "usage": None,
+ "description": "Displays the current values of the "
+ "properties within\n\ta selected type including"
+ " reserved properties\n\texample: list\n\n\tNOTE: If "
+ "you wish to not list all the reserved properties\n\t "
+ " run the get command instead",
+ "summary": "Displays the current value(s) of a"
+ " property(ies) within a selected type including"
+ " reserved properties.",
+ "aliases": ["ls"],
+ "auxcommands": ["GetCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for main list function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ selector = options.selector.lower() if options.selector else self.rdmc.app.selector.lower()
+
+ if "securityservice" in selector:
+ self.cmdbase.login_validation(self, options)
+ else:
+ self.listvalidation(options)
+
+ fvals = (None, None)
+
+ if options.filter:
+ try:
+ if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
+ options.filter = options.filter[1:-1]
+
+ (sel, val) = options.filter.split("=")
+ fvals = (sel.strip(), val.strip())
+ except:
+ raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
+ if options.selector:
+ if "storagecontroller." in options.selector.lower():
+ options.selector = "storagecontrollercollection"
+ self.rdmc.app.selector = options.selector
+ elif self.rdmc.app.selector:
+ options.selector = self.rdmc.app.selector
+ if "securityservice" in options.selector.lower():
+ url = "/redfish/v1/Managers/1/SecurityService/"
+ contents = self.rdmc.app.get_handler(url, service=True, silent=True).dict
+ security_contents = []
+
+ if options and options.json and contents:
+ UI().print_out_json(contents)
+ elif not args:
+ UI().print_out_human_readable(contents)
+ else:
+ attr = args[0]
+ contents_lower = {k.lower(): v for k, v in contents.items()}
+ security_contents.append({attr: contents_lower[attr.lower()]})
+ if security_contents:
+ UI().print_out_human_readable(security_contents)
+ else:
+ self.auxcommands["get"].getworkerfunction(args, options, filtervals=fvals, uselist=False)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def listvalidation(self, options):
+ """List data validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run"
+ " the current command on. Use this flag when you wish to"
+ " select a type without entering another command, or if you"
+ " wish to work with a type that is different from the one"
+ " you currently have selected.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--filter",
+ dest="filter",
+ help="Optionally set a filter value for a filter attribute."
+ " This uses the provided filter for the currently selected"
+ " type. Note: Use this flag to narrow down your results. For"
+ " example, selecting a common type might return multiple"
+ " objects that are all of that type. If you want to modify"
+ " the properties of only one of those objects, use the filter"
+ " flag to narrow down results based on properties."
+ "\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
+ default=None,
+ )
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--refresh",
+ dest="ref",
+ action="store_true",
+ help="Optionally reload the data of selected type and clear " "patches from current selection.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoadCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoadCommand.py
new file mode 100644
index 0000000..202b77f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoadCommand.py
@@ -0,0 +1,569 @@
+###
+# Copyright 2016-2023 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Load Command for RDMC"""
+
+import json
+import os
+import shlex
+import subprocess
+import sys
+from datetime import datetime
+
+from six.moves import queue
+
+import redfish.ris
+from redfish.ris.rmc_helper import LoadSkipSettingError
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ InvalidMSCfileInputError,
+ MultipleServerConfigError,
+ NoChangesFoundOrMadeError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ InvalidMSCfileInputError,
+ MultipleServerConfigError,
+ NoChangesFoundOrMadeError,
+ ReturnCodes,
+ )
+
+try:
+ from rdmc_helper import HARDCODEDLIST
+except:
+ from ilorest.rdmc_helper import HARDCODEDLIST
+
+# default file name
+__filename__ = "ilorest.json"
+
+
+class LoadCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "load",
+ "usage": None,
+ "description": "Run to load the default configuration"
+ " file\n\texample: load\n\n\tLoad configuration file from a "
+ "different file\n\tif any property values have changed, the "
+ "changes are committed and the user is logged out of the server"
+ "\n\n\texample: load -f output.json\n\n\tLoad configurations to "
+ "multiple servers\n\texample: load -m mpfilename.txt -f output."
+ "json\n\n\tNote: multiple server file format (1 server per new "
+ "line)\n\t--url -u admin -p password\n\t--url"
+ " -u admin -p password\n\t--url -u admin -p password",
+ "summary": "Loads the server configuration settings from a file.",
+ "aliases": [],
+ "auxcommands": ["CommitCommand", "SelectCommand", "RawPatchCommand"],
+ }
+ self.filenames = None
+ self.mpfilename = None
+ self.queue = queue.Queue()
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def securebootremovereadonly(self, tmp):
+ templist = [
+ "SecureBootCurrentBoot",
+ "@odata.etag",
+ "Id",
+ "AutoNeg",
+ "FQDN",
+ "FullDuplex",
+ "HostName",
+ "IPv6Addresses",
+ "IPv6DefaultGateway",
+ "LinkStatus",
+ "MaxIPv6StaticAddresses",
+ "Name",
+ "NameServers",
+ "PermanentMACAddress",
+ "ConfigurationSettings",
+ "InterfaceType",
+ "NICSupportsIPv6",
+ "DomainName",
+ "IPV6",
+ "DNSServers",
+ "MACAddress",
+ "VLAN",
+ "SpeedMbps",
+ "DateTime",
+ ]
+ remove_data = self.rdmc.app.removereadonlyprops(tmp, False, True, templist)
+ return remove_data
+
+ def run(self, line, help_disp=False):
+ """Main load worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.loadvalidation(options)
+ returnvalue = False
+
+ if options.mpfilename:
+ self.rdmc.ui.printer("Loading configuration for multiple servers...\n")
+ else:
+ self.rdmc.ui.printer("Loading configuration...\n")
+
+ for files in self.filenames:
+ if not os.path.isfile(files):
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist. Please create file by running save command." % files
+ )
+ if options.encryption:
+ with open(files, "rb") as myfile:
+ data = myfile.read()
+ data = Encryption().decrypt_file(data, options.encryption)
+ loadcontents = json.loads(data)
+ else:
+ try:
+ with open(files, "r") as myfile:
+ loadcontents = json.load(myfile)
+ except:
+ raise InvalidFileFormattingError("Invalid file formatting found in file %s" % files)
+
+ if options.mpfilename:
+ mfile = options.mpfilename
+ outputdir = None
+
+ if options.outdirectory:
+ outputdir = options.outdirectory
+
+ if self.runmpfunc(mpfile=mfile, lfile=files, outputdir=outputdir):
+ return ReturnCodes.SUCCESS
+ else:
+ raise MultipleServerConfigError("One or more servers failed to load given configuration.")
+
+ results = False
+ eth_conf = False
+ validation_errs = []
+
+ for loadcontent in loadcontents:
+ for content, loaddict in loadcontent.items():
+ inputlist = list()
+
+ if content == "Comments":
+ continue
+
+ if "EthernetInterface" in content:
+ if options.force_network_config:
+ import platform
+ import tempfile
+
+ patchpath = "/redfish/v1/Managers/1/EthernetInterfaces/1/"
+
+ try:
+ payload = loadcontent[content][patchpath]
+ except KeyError:
+ continue
+
+ payload = loadcontent[content][patchpath]
+ remove_odata = self.securebootremovereadonly(payload)
+ payload.update(remove_odata)
+ if "StaticNameServers" in payload:
+ payload["StaticNameServers"] = [
+ item for item in payload["StaticNameServers"] if item != "::"
+ ]
+ tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
+ temp_file = os.path.join(tempdir, "temp_patch.json")
+ out_file = open(temp_file, "w")
+ patch_payload = {patchpath: payload}
+ json.dump(patch_payload, out_file, indent=6)
+ out_file.close()
+ self.auxcommands["rawpatch"].run(temp_file + " --service")
+ os.remove(temp_file)
+ eth_conf = True
+ continue
+ else:
+ self.rdmc.ui.printer(
+ "Skipping network configurations as" " --force_network_config is not included.\n"
+ )
+ continue
+
+ inputlist.append(content)
+ if options.biospassword:
+ inputlist.extend(["--biospassword", options.biospassword])
+
+ self.auxcommands["select"].selectfunction(inputlist)
+ if self.rdmc.app.selector.lower() not in content.lower():
+ raise InvalidCommandLineError("Selector not found.\n")
+
+ try:
+ for _, items in loaddict.items():
+ remove_odata = self.securebootremovereadonly(items)
+ items.update(remove_odata)
+ try:
+ if self.rdmc.app.loadset(
+ seldict=items,
+ latestschema=options.latestschema,
+ uniqueoverride=options.uniqueoverride,
+ ):
+ results = True
+ except LoadSkipSettingError:
+ returnvalue = True
+ results = True
+ except:
+ raise
+ except redfish.ris.ValidationError as excp:
+ errs = excp.get_errors()
+ validation_errs.append({self.rdmc.app.selector: errs})
+ except:
+ raise
+
+ try:
+ if results:
+ self.auxcommands["commit"].commitfunction(options=options)
+ reboot_needed = False
+ for n in inputlist:
+ n = n.lower()
+ if "bios" in n or "boot" in n or "iscsi" in n:
+ reboot_needed = True
+ if reboot_needed:
+ self.rdmc.ui.printer("Reboot is required for settings to take effect.\n")
+ except NoChangesFoundOrMadeError as excp:
+ if returnvalue:
+ pass
+ else:
+ raise excp
+
+ if validation_errs:
+ for validation_err in validation_errs:
+ for err_type in validation_err:
+ self.rdmc.ui.error("Validation error(s) in type %s:\n" % err_type)
+ for err in validation_err[err_type]:
+ if isinstance(err, redfish.ris.RegistryValidationError):
+ self.rdmc.ui.error(err.message)
+ try:
+ if err.reg:
+ self.rdmc.ui.error(str(err.sel))
+ except:
+ pass
+ raise redfish.ris.ValidationError()
+
+ if not results:
+ if eth_conf:
+ continue
+ else:
+ self.rdmc.ui.printer("No differences found from current configuration.\n")
+
+ # Return code
+ if returnvalue:
+ return ReturnCodes.LOAD_SKIP_SETTING_ERROR
+
+ return ReturnCodes.SUCCESS
+
+ def loadvalidation(self, options):
+ """Load method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ if self.rdmc.opts.latestschema:
+ options.latestschema = True
+
+ try:
+ self.cmdbase.login_validation(self, options)
+ except Exception:
+ if options.mpfilename:
+ pass
+ else:
+ raise
+
+ # filename validations and checks
+ if options.filename:
+ self.filenames = options.filename
+ elif self.rdmc.config:
+ if self.rdmc.config.defaultloadfilename:
+ self.filenames = [self.rdmc.config.defaultloadfilename]
+
+ if not self.filenames:
+ self.filenames = [__filename__]
+
+ def verify_file(self, filedata, inputfile):
+ """Function used to handle oddly named files and convert to JSON
+
+ :param filedata: input file data
+ :type filedata: string.
+ :param inputfile: current input file
+ :type inputfile: string.
+ """
+ try:
+ tempholder = json.loads(filedata)
+ return tempholder
+ except:
+ raise InvalidFileFormattingError("Invalid file formatting found in file %s" % inputfile)
+
+ def get_current_selector(self, path=None):
+ """Returns current selected content minus hard coded list
+
+ :param path: current path
+ :type path: string.
+ """
+ contents = self.rdmc.app.monolith.path[path]
+
+ if not contents:
+ contents = list()
+
+ for content in contents:
+ for k in list(content.keys()):
+ if k.lower() in HARDCODEDLIST or "@odata" in k.lower():
+ del content[k]
+
+ return contents
+
+ def runmpfunc(self, mpfile=None, lfile=None, outputdir=None):
+ """Main worker function for multi file command
+
+ :param mpfile: configuration file
+ :type mpfile: string.
+ :param lfile: custom file name
+ :type lfile: string.
+ :param outputdir: custom output directory
+ :type outputdir: string.
+ """
+ # self.logoutobj.run("")
+ data = self.validatempfile(mpfile=mpfile, lfile=lfile)
+
+ if not data:
+ return False
+
+ processes = []
+ finalreturncode = True
+ outputform = "%Y-%m-%d-%H-%M-%S"
+
+ if outputdir:
+ if outputdir.endswith(('"', "'")) and outputdir.startswith(('"', "'")):
+ outputdir = outputdir[1:-1]
+
+ if not os.path.isdir(outputdir):
+ self.rdmc.ui.error("The give output folder path does not exist.\n")
+ raise InvalidCommandLineErrorOPTS("")
+
+ dirpath = outputdir
+ else:
+ dirpath = os.getcwd()
+
+ dirname = "%s_%s" % (datetime.now().strftime(outputform), "MSClogs")
+ createdir = os.path.join(dirpath, dirname)
+ os.mkdir(createdir)
+
+ oofile = open(os.path.join(createdir, "CompleteOutputfile.txt"), "w+")
+ self.rdmc.ui.printer("Create multiple processes to load configuration " "concurrently to all servers...\n")
+
+ while True:
+ if not self.queue.empty():
+ line = self.queue.get()
+ else:
+ break
+
+ finput = "\n" + "Output for " + line[line.index("--url") + 1] + ": \n\n"
+ urlvar = line[line.index("--url") + 1]
+
+ if "python" in os.path.basename(sys.executable.lower()):
+ # If we are running from source we have to add the python file to the command
+ listargforsubprocess = [sys.executable, sys.argv[0]] + line
+ else:
+ listargforsubprocess = [sys.executable] + line
+
+ if os.name != "nt":
+ listargforsubprocess = " ".join(listargforsubprocess)
+
+ urlfilename = urlvar.split("//")[-1]
+ logfile = open(os.path.join(createdir, urlfilename + ".txt"), "w+")
+ pinput = subprocess.Popen(listargforsubprocess, shell=True, stdout=logfile, stderr=logfile)
+
+ processes.append((pinput, finput, urlfilename, logfile))
+
+ for pinput, finput, urlfilename, logfile in processes:
+ pinput.wait()
+ returncode = pinput.returncode
+ finalreturncode = finalreturncode and not returncode
+
+ logfile.close()
+ logfile = open(os.path.join(createdir, urlfilename + ".txt"), "r+")
+ oofile.write(finput + str(logfile.read()))
+ oofile.write("-x+x-" * 16)
+ logfile.close()
+
+ if returncode == 0:
+ self.rdmc.ui.printer("Loading Configuration for {} : SUCCESS\n".format(urlfilename))
+ else:
+ self.rdmc.ui.error("Loading Configuration for {} : FAILED\n".format(urlfilename))
+ self.rdmc.ui.error(
+ "ILOREST return code : {}.\nFor more details please check "
+ "{}.txt under {} directory.\n".format(returncode, urlfilename, createdir)
+ )
+
+ oofile.close()
+
+ if finalreturncode:
+ self.rdmc.ui.printer("All servers have been successfully configured.\n")
+
+ return finalreturncode
+
+ def validatempfile(self, mpfile=None, lfile=None):
+ """Validate temporary file
+
+ :param mpfile: configuration file
+ :type mpfile: string.
+ :param lfile: custom file name
+ :type lfile: string.
+ """
+ self.rdmc.ui.printer("Checking given server information...\n")
+
+ if not mpfile:
+ return False
+
+ if not os.path.isfile(mpfile):
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist, please " "create file by running save command." % mpfile
+ )
+
+ try:
+ with open(mpfile, "r") as myfile:
+ data = list()
+ cmdtorun = ["load"]
+ cmdargs = ["-f", str(lfile)]
+ globalargs = ["-v", "--nocache"]
+
+ while True:
+ line = myfile.readline()
+
+ if not line:
+ break
+
+ if line.endswith(os.linesep):
+ line.rstrip(os.linesep)
+
+ args = shlex.split(line, posix=False)
+
+ if len(args) < 5:
+ self.rdmc.ui.error("Incomplete data in input file: {}\n".format(line))
+ raise InvalidMSCfileInputError("Please verify the " "contents of the %s file" % mpfile)
+ else:
+ linelist = globalargs + cmdtorun + args + cmdargs
+ line = str(line).replace("\n", "")
+ self.queue.put(linelist)
+ data.append(linelist)
+ except Exception as excp:
+ raise excp
+
+ if data:
+ return data
+
+ return False
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to use a different"
+ " filename than the default one. The default filename is"
+ " %s." % __filename__,
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "-m",
+ "--multiprocessing",
+ dest="mpfilename",
+ help="""use the provided filename to obtain data""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--outputdirectory",
+ dest="outdirectory",
+ help="""use the provided directory to output data for multiple server configuration""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--latestschema",
+ dest="latestschema",
+ action="store_true",
+ help="Optionally use the latest schema instead of the one "
+ "requested by the file. Note: May cause errors in some data "
+ "retrieval due to difference in schema versions.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--uniqueoverride",
+ dest="uniqueoverride",
+ action="store_true",
+ help="Override the measures stopping the tool from writing " "over items that are system unique.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--encryption",
+ dest="encryption",
+ help="Optionally include this flag to encrypt/decrypt a file " "using the key provided.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--force_network_config",
+ dest="force_network_config",
+ help="Use this flag to force set network configuration."
+ "Network settings will be skipped if the flag is not included.",
+ action="store_true",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoginCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoginCommand.py
new file mode 100644
index 0000000..5e36f67
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LoginCommand.py
@@ -0,0 +1,582 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Login Command for RDMC"""
+
+import getpass
+import os
+import socket
+import redfish.ris
+from redfish.hpilo.vnichpilo import AppAccount
+from redfish.rest.connections import ChifDriverMissingOrNotFound, VnicNotEnabledError
+import requests
+from requests.exceptions import SSLError, RequestException
+import platform
+from argparse import SUPPRESS
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ PathUnavailableError,
+ ReturnCodes,
+ UsernamePasswordRequiredError,
+ VnicExistsError,
+ VnicLoginError,
+ InactiveAppAccountTokenError,
+ NoAppAccountError,
+ GenBeforeLoginError,
+ AppAccountExistsError,
+ )
+except ModuleNotFoundError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ PathUnavailableError,
+ Encryption,
+ UsernamePasswordRequiredError,
+ VnicExistsError,
+ VnicLoginError,
+ InactiveAppAccountTokenError,
+ NoAppAccountError,
+ GenBeforeLoginError,
+ AppAccountExistsError,
+ )
+
+from redfish.rest.v1 import ServerDownOrUnreachableError
+
+
+class LoginCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "login",
+ "usage": None,
+ "description": "To login remotely run using iLO url and iLO credentials"
+ "\n\texample: login -u "
+ "-p \n\n\tTo login on a local server run without "
+ "arguments\n\texample: login"
+ "\n\n\tTo login through VNIC run using --force_vnic and iLO credentials "
+ "\n\texample: login --force_vnic -u -p "
+ "\n\nLogin using OTP can be done in 2 ways."
+ "\n\n\t To login implicitly, use the tag --wait_for_otp."
+ "\n\t\texample: login -u -p --wait_for_otp"
+ "\n\n\n\t To login explicitly, use the tag -o/--otp and enter OTP after."
+ "\n\t\texample: login -u -p -o "
+ "\n\n\tNOTE: A [URL] can be specified with "
+ "an IPv4, IPv6, or hostname address.",
+ "summary": "Connects to a server, establishes a secure session," " and discovers data from iLO.",
+ "aliases": [],
+ "auxcommands": ["LogoutCommand"],
+ "cert_data": {},
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.url = None
+ self.username = None
+ self.password = None
+ self.sessionid = None
+ self.biospassword = None
+ self.auxcommands = dict()
+ self.cert_data = dict()
+ self.login_otp = None
+ self.ilo_gen = 6
+
+ def run(self, line, help_disp=False):
+ """wrapper function for main login function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: flag to determine to display or not
+ :type help_disp: boolean
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ self.loginfunction(line)
+
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+
+ if not self.rdmc.app.monolith._visited_urls:
+ self.auxcommands["logout"].run("")
+ raise PathUnavailableError("The path specified by the --path flag is unavailable.")
+ except Exception:
+ raise
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def perform_login(self, options, skipbuild, user_ca_cert_data, args, app_obj):
+
+ ilo_ver = self.ilo_gen
+ if ilo_ver < 7 or (ilo_ver >= 7 and (options.usechif or options.noapptoken or args)):
+ login_response = self.rdmc.app.login(
+ username=self.username,
+ password=self.password,
+ sessionid=self.sessionid,
+ base_url=self.url,
+ path=options.path,
+ skipbuild=skipbuild,
+ includelogs=options.includelogs,
+ biospassword=self.biospassword,
+ is_redfish=self.rdmc.opts.is_redfish,
+ proxy=self.rdmc.opts.proxy,
+ user_ca_cert_data=user_ca_cert_data,
+ json_out=self.rdmc.json,
+ login_otp=self.login_otp,
+ log_dir=self.rdmc.log_dir,
+ )
+ else:
+ if options.hostappname or options.hostappid or options.salt:
+ if not (options.hostappname and options.hostappid and options.salt):
+ raise InvalidCommandLineError("Please enter all the necessary host application information.\n")
+
+ # Code to check if vnic is enabled.
+ vnic_enabled = self.rdmc.app.vexists(app_obj)
+ if not vnic_enabled:
+ raise VnicExistsError(
+ "Unable to access iLO using virtual NIC. "
+ "Please ensure virtual NIC is enabled in iLO. "
+ "Ensure that virtual NIC in the host OS is "
+ "configured properly. Refer to documentation for more information.\n"
+ )
+
+ # To check if appaccount exists. If not, error out.
+ try:
+ apptoken_exists = self.rdmc.app.token_exists(app_obj)
+ except Exception as excp:
+ raise AppAccountExistsError("Error occurred while checking if app account exists.\n")
+
+ # If appaccount doesn't exist
+ if not apptoken_exists:
+ raise NoAppAccountError(
+ "The app account is not available for this host application. "
+ "Please retry using the --no_app_account option or generate "
+ "an application account using the appaccount create command.\n"
+ )
+
+ try:
+ # Function will log in, retrieve session id, call build monolith, .save()
+ login_response = self.rdmc.app.vnic_login(
+ app_obj=app_obj,
+ path=options.path,
+ skipbuild=skipbuild,
+ includelogs=options.includelogs,
+ json_out=self.rdmc.json,
+ base_url=self.url,
+ username=self.username,
+ password=self.password,
+ log_dir=self.rdmc.log_dir,
+ login_otp=self.login_otp,
+ is_redfish=self.rdmc.opts.is_redfish,
+ proxy=self.rdmc.opts.proxy,
+ user_ca_cert_data=user_ca_cert_data,
+ biospassword=self.biospassword,
+ sessionid=self.sessionid,
+ )
+ except redfish.hpilo.vnichpilo.InactiveAppAccountTokenError:
+ LOGGER.error("Login failed due to an inactive or expired App Account token.")
+ raise InactiveAppAccountTokenError("Login failed due to an inactive or expired App Account token.")
+ except Exception as excp:
+ error_msg = """Error occurred while performing login using app account.
+ Check if application account exists using 'appaccount details' command.
+ Otherwise, try with --no_app_account option.\n"""
+ LOGGER.error(error_msg)
+ raise VnicLoginError(error_msg)
+
+ if options.session_id and login_response and hasattr(login_response, "_auth_key"):
+ self.rdmc.ui.printer(f"\nSessionID:{login_response._auth_key}\n\n")
+
+ def loginfunction(self, line, skipbuild=None, json_out=False):
+ """Main worker function for login class
+
+ :param line: entered command line
+ :type line: list.
+ :param skipbuild: flag to determine if monolith should be build
+ :type skipbuild: boolean.
+ :param json_out: flag to determine if json output neededd
+ :type skipbuild: boolean.
+ """
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Invalid command line arguments")
+
+ if platform.system() == "Darwin":
+ # Mac OS
+ app_obj = None
+ self.ilo_gen = 6
+ else:
+ app_obj = AppAccount(
+ appname=options.hostappname,
+ appid=options.hostappid,
+ salt=options.salt,
+ username=options.user,
+ password=options.password,
+ log_dir=self.rdmc.log_dir,
+ )
+
+ self.ilo_gen = self.get_ilover_beforelogin(args=args, app_obj=app_obj, options=options)
+
+ self.loginvalidation(options=options, args=args, app_obj=app_obj)
+
+ # if proxy server provided in command line as --useproxy, it will be used,
+ # otherwise it will the environment variable setting.
+ # else proxy will be set as None.
+ if self.rdmc.opts.proxy:
+ _ = self.rdmc.opts.proxy
+ elif "https_proxy" in os.environ and os.environ["https_proxy"]:
+ _ = os.environ["https_proxy"]
+ elif "http_proxy" in os.environ and os.environ["http_proxy"]:
+ _ = os.environ["http_proxy"]
+ else:
+ _ = self.rdmc.config.proxy
+
+ no_bundle = False
+
+ if getattr(options, "ca_cert_bundle", False):
+ user_ca_cert_data = {"ca_certs": options.ca_cert_bundle}
+ else:
+ user_ca_cert_data = {}
+ if getattr(options, "user_certificate", False):
+ no_bundle = True
+ user_ca_cert_data.update({"cert_file": options.user_certificate})
+ if getattr(options, "user_root_ca_key", False):
+ no_bundle = True
+ user_ca_cert_data.update({"key_file": options.user_root_ca_key})
+ if getattr(options, "user_root_ca_password", False):
+ no_bundle = True
+ user_ca_cert_data.update({"key_password": options.user_root_ca_password})
+
+ if not no_bundle:
+ if hasattr(user_ca_cert_data, "ca_certs"):
+ user_ca_cert_data.pop("ca_certs")
+
+ try:
+ if getattr(options, "force_vnic", False):
+ self.rdmc.ui.printer("\nAttempt to login with Vnic...\n")
+ try:
+ sock = socket.create_connection((args[0], 443))
+ if sock:
+ sock.close
+ except:
+ pass
+
+ self.sessionid = options.sessionid
+ self.login_otp = options.login_otp
+
+ if self.url and "blobstore" in self.url and (options.waitforOTP or options.login_otp):
+ options.waitforOTP = None
+ options.login_otp = None
+ self.rdmc.ui.printer(
+ "Warning: For local inband mode, TFA is not supported, options --wait_for_otp "
+ "and --otp will be ignored\n"
+ )
+
+ if options.waitforOTP:
+ try:
+ self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
+ except redfish.rest.connections.OneTimePasscodeError:
+ self.rdmc.ui.printer("One Time Passcode Sent to registered email.\n")
+ ans = input("Enter OTP: ")
+ self.login_otp = ans
+ self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
+ else:
+ self.perform_login(options, skipbuild, user_ca_cert_data, args, app_obj)
+ except ServerDownOrUnreachableError as excp:
+ self.rdmc.ui.printer("The following error occurred during login: '%s'\n" % str(excp.__class__.__name__))
+
+ self.username = None
+ self.password = None
+
+ # Warning for cache enabled, since we save session in plain text
+ if not self.rdmc.encoding:
+ self.rdmc.ui.warn("Cache is activated. Session keys are stored in plaintext.")
+
+ if self.rdmc.opts.debug:
+ self.rdmc.ui.warn("Logger is activated. Logging is stored in plaintext.")
+
+ if options.selector:
+ try:
+ self.rdmc.app.select(selector=options.selector)
+
+ if self.rdmc.opts.verbose:
+ self.rdmc.ui.printer(("Selected option: '%s'\n" % options.selector))
+ except Exception as excp:
+ raise redfish.ris.InstanceNotFoundError(excp)
+
+ def loginvalidation(self, options, args, app_obj):
+ """Login helper function for login validations
+
+ :param options: command line options
+ :type options: list.
+ :param args: command line arguments
+ :type args: list.
+ """
+ # Fill user name/password from config file
+ if not options.user:
+ options.user = self.rdmc.config.username
+ if not options.password:
+ options.password = self.rdmc.config.password
+ if not hasattr(options, "user_certificate"):
+ options.user_certificate = self.rdmc.config.user_cert
+ if not hasattr(options, "user_root_ca_key"):
+ options.user_root_ca_key = self.rdmc.config.user_root_ca_key
+ if not hasattr(options, "user_root_ca_password"):
+ options.user_root_ca_password = self.rdmc.config.user_root_ca_password
+
+ ilo_ver = self.ilo_gen
+
+ if (
+ options.user
+ and not options.password
+ and (
+ not hasattr(options, "user_certificate")
+ or not hasattr(options, "user_root_ca_key")
+ or hasattr(options, "user_root_ca_password")
+ )
+ ):
+ # Option for interactive entry of password
+ tempinput = getpass.getpass().rstrip()
+ if tempinput:
+ options.password = tempinput
+ else:
+ raise InvalidCommandLineError("Empty or invalid password was entered.")
+
+ if options.user:
+ self.username = options.user
+
+ if options.password:
+ self.password = options.password
+
+ if options.encode:
+ self.username = Encryption.decode_credentials(self.username).decode("utf-8")
+ self.password = Encryption.decode_credentials(self.password).decode("utf-8")
+
+ if options.biospassword:
+ self.biospassword = options.biospassword
+
+ if ilo_ver >= 7:
+ if not args:
+ if self.username and self.password and not options.noapptoken and not options.usechif:
+ options.noapptoken = True
+ if options.noapptoken:
+ if (
+ not self.username or not self.password
+ ): # Add another check to see if username and password is present in environment variables
+ raise UsernamePasswordRequiredError(
+ "Please enter username and password in order to login without app account.\n"
+ )
+ options.force_vnic = True
+
+ # Assignment of url in case no url is entered
+ if getattr(options, "force_vnic", False):
+ if not (getattr(options, "ca_cert_bundle", False) or getattr(options, "user_certificate", False)):
+ if not (self.username and self.password) and not options.sessionid:
+ raise UsernamePasswordRequiredError("Please provide credentials to login with virtual NIC.\n")
+ self.url = "https://16.1.15.1"
+ else:
+ if ilo_ver >= 7 and not options.usechif:
+ # TO DO
+ # self.url = self.rdmc.app.GetIPAddress()
+ self.url = "https://16.1.15.1"
+ else:
+ self.url = "blobstore://."
+
+ if args:
+ # Any argument should be treated as an URL
+ self.url = args[0]
+
+ # Verify that URL is properly formatted for https://
+ if "https://" not in self.url:
+ self.url = "https://" + self.url
+
+ if not (
+ hasattr(options, "user_certificate")
+ or hasattr(options, "user_root_ca_key")
+ or hasattr(options, "user_root_ca_password")
+ ):
+ if not (options.user and options.password):
+ raise InvalidCommandLineError("Empty username or password was entered.")
+ else:
+ # Check to see if there is a URL in config file
+ if self.rdmc.config.url:
+ self.url = self.rdmc.config.url
+
+ def get_ilover_beforelogin(self, args, app_obj, options):
+ """
+ Attempts to retrieve the iLO version before login.
+
+ :param args: List of command-line arguments; expects iLO IP/hostname at index 0.
+ :param app_obj: App object for fallback retrieval if args is empty.
+ :param options: Options object, may include flags like 'force_vnic' or 'usechif'.
+ :return: iLO version as an integer.
+ :raises: GenBeforeLoginError, VnicExistsError, ChifDriverMissingOrNotFound, VnicNotEnabledError
+ """
+ ip = path = None # Initialize to avoid UnboundLocalError
+ try:
+ if args:
+ ip = args[0]
+ path = f"https://{ip}/redfish/v1"
+
+ response = requests.get(path, verify=False, timeout=10)
+ response.raise_for_status()
+
+ try:
+ json_data = response.json()
+ oem_data = json_data.get("Oem", {}).get("Hpe") or json_data.get("Oem", {}).get("Hp")
+ if not oem_data:
+ raise KeyError("Oem->Hpe or Oem->Hp block missing in response.")
+
+ manager = oem_data.get("Manager", [{}])[0]
+ manager_type = manager.get("ManagerType")
+
+ if manager_type:
+ ilo_ver = int(manager_type.split(" ")[1])
+ else:
+ moniker = oem_data.get("Moniker", {})
+ prodgen = moniker.get("PRODGEN")
+ if not prodgen:
+ raise KeyError("PRODGEN key not found in Moniker.")
+ ilo_ver = int(prodgen.split(" ")[1])
+ except (KeyError, ValueError, IndexError) as e:
+ raise GenBeforeLoginError(
+ f"Failed to parse iLO version from response. Error: {e}\n"
+ "Ensure the iLO firmware is Redfish-compliant and responds correctly.\n"
+ )
+ else:
+ ilo_ver, _ = self.rdmc.app.getilover_beforelogin(app_obj)
+
+ return ilo_ver
+
+ except ChifDriverMissingOrNotFound:
+ if getattr(options, "force_vnic", False):
+ return 5
+ raise
+
+ except VnicNotEnabledError:
+ if getattr(options, "usechif", False):
+ return 7
+ raise VnicExistsError(
+ "Unable to access iLO using virtual NIC.\n"
+ "Please ensure that virtual NIC is enabled in iLO and "
+ "properly configured in the host OS.\n"
+ "Refer to the documentation for setup instructions.\n"
+ )
+
+ except SSLError:
+ raise GenBeforeLoginError(
+ f"SSL Error: Unable to connect to the server at {ip or '[unknown IP]'}.\n"
+ "Please verify the iLO IP and try accessing the GUI.\n"
+ )
+
+ except RequestException as e:
+ raise GenBeforeLoginError(
+ f"Connection Error: Could not retrieve data from {path or '[unknown path]'}.\n" f"Details: {e}\n"
+ )
+
+ except Exception as e:
+ raise GenBeforeLoginError(
+ f"An unexpected error occurred while retrieving the iLO version.\n"
+ f"Details: {e}\n"
+ "Verify if virtual NIC is enabled for iLO7 or that the CHIF driver "
+ "is installed for iLO5 and iLO6 based servers.\n"
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+
+ def remove_argument(parser, arg):
+ for action in parser._actions:
+ opts = action.option_strings
+ if (opts and opts[0] == arg) or action.dest == arg:
+ parser._remove_action(action)
+ break
+
+ for action in parser._action_groups:
+ for group_action in action._group_actions:
+ opts = group_action.option_strings
+ if (opts and opts[0] == arg) or group_action.dest == arg:
+ action._group_actions.remove(group_action)
+ return
+
+ if not customparser:
+ return
+
+ customparser.add_argument(
+ "--wait_for_otp",
+ dest="waitforOTP",
+ help="Optionally include this flag to implicitly wait for OTP.",
+ action="store_true",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(customparser)
+ remove_argument(customparser, "url")
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run"
+ " the current command on. Use this flag when you wish to"
+ " select a type without entering another command, or if you"
+ " wish to work with a type that is different from the one"
+ " you currently have selected.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--no_app_token",
+ "--no_app_account",
+ dest="noapptoken",
+ help="Include this parameter in order to login to iLO7 and above with credentials and not app account.",
+ default=None,
+ action="store_true",
+ )
+ customparser.add_argument("--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None)
+ customparser.add_argument(
+ "--hostappname", dest="hostappname", help="Parameter to specify hostappname", default=None
+ )
+ customparser.add_argument(
+ "--salt", dest="salt", help="Parameter to specify application owned salt", default=None
+ )
+ customparser.add_argument(
+ "--use_chif",
+ dest="usechif",
+ help="Include this parameter in order to operate iLO7 and above using chif.",
+ default=None,
+ action="store_true",
+ )
+ customparser.add_argument(
+ "--show_session_id",
+ "-s",
+ dest="session_id",
+ help=SUPPRESS,
+ default=None,
+ action="store_true",
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LogoutCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LogoutCommand.py
new file mode 100644
index 0000000..cc7b999
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/LogoutCommand.py
@@ -0,0 +1,79 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Logout Command for RDMC"""
+
+try:
+ from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class LogoutCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "logout",
+ "usage": None,
+ "description": "Run to end the current session and disconnect" " from the server\n\tExample: logout",
+ "summary": "Ends the current session and disconnects from the server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+
+ def logoutfunction(self, line):
+ """Main logout worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.rdmc.app.logout("")
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for main logout function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ self.rdmc.ui.printer("Logging session out.\n")
+ self.logoutfunction(line)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/PendingChangesCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/PendingChangesCommand.py
new file mode 100644
index 0000000..5ced8f1
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/PendingChangesCommand.py
@@ -0,0 +1,201 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Results Command for rdmc"""
+
+import copy
+import json
+import sys
+
+import jsondiff
+
+try:
+ from rdmc_helper import HARDCODEDLIST
+except:
+ from ilorest.rdmc_helper import HARDCODEDLIST
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class PendingChangesCommand:
+ """PendingChanges class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "pending",
+ "usage": None,
+ "description": "Run to show pending committed changes "
+ "that will be applied after a reboot.\n\texample: pending",
+ "summary": "Show the pending changes that will be applied on reboot.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Show pending changes of settings objects
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("Pending command does not take any arguments.")
+ self.pendingvalidation(options)
+
+ self.pendingfunction()
+ sys.stdout.write("\n")
+ self.cmdbase.logout_routine(self, options)
+ return ReturnCodes.SUCCESS
+
+ def pendingfunction(self):
+ """Main pending command worker function"""
+ settingsuri = []
+ ignorekeys = ["@odata.id", "@odata.etag", "@redfish.settings", "oem"]
+ ignoreuri = [str("hpsut*")]
+ ignorekeys.extend(HARDCODEDLIST)
+
+ resourcedir = self.rdmc.app.get_handler(self.rdmc.app.monolith._resourcedir, service=True, silent=True)
+
+ for resource in resourcedir.dict["Instances"]:
+ if (resource["@odata.id"].split("/").__len__() - 1) > 4:
+ splitstr = resource["@odata.id"].split("/")[5]
+ for element in ignoreuri:
+ if "/settings" in resource["@odata.id"] and not self.wildcard_str_match(element, splitstr):
+ settingsuri.append(resource["@odata.id"])
+
+ self.rdmc.ui.printer("Current Pending Changes:\n")
+
+ for uri in settingsuri:
+ diffprint = {}
+ baseuri = uri.split("settings")[0]
+
+ base = self.rdmc.app.get_handler(baseuri, service=True, silent=True)
+ settings = self.rdmc.app.get_handler(uri, service=True, silent=True)
+
+ typestring = self.rdmc.app.monolith.typepath.defs.typestring
+ currenttype = ".".join(base.dict[typestring].split("#")[-1].split(".")[:-1])
+
+ differences = json.loads(jsondiff.diff(base.dict, settings.dict, syntax="symmetric", dump=True))
+
+ diffprint = self.recursdict(differences, ignorekeys)
+
+ self.rdmc.ui.printer("\n%s:" % currenttype)
+ if not diffprint:
+ self.rdmc.ui.printer("\nNo pending changes found.\n")
+ else:
+ self.rdmc.ui.pretty_human_readable(diffprint)
+
+ def wildcard_str_match(self, first, second):
+ """
+ Recursive function for determining match between two strings. Accounts
+ for wildcard characters
+
+ :param first: comparison string (may contain '*' or '?' for wildcards)
+ :param type: str (unicode)
+ :param second: string value to be compared (must not contain '*' or '?')
+ :param type: str (unicode)
+ """
+
+ if not first and not second:
+ return True
+ if len(first) > 1 and first[0] == "*" and not second:
+ return False
+ if (len(first) > 1 and first[0] == "?") or (first and second and first[0] == second[0]):
+ return self.wildcard_str_match(first[1:], second[1:])
+ if first and first[0] == "*":
+ return self.wildcard_str_match(first[1:], second) or self.wildcard_str_match(first, second[1:])
+
+ return False
+
+ def recursdict(self, diff, ignorekeys):
+ """Recursively get dict ready for printing
+
+ :param diff: diff dict
+ :type options: dict.
+ """
+ diffprint = copy.deepcopy(diff)
+ for item in diff:
+ if item.lower() in ignorekeys:
+ diffprint.pop(item)
+ elif item == "$delete":
+ for ditem in diff[item]:
+ if isinstance(diff[item], list):
+ continue
+ if isinstance(diff[item][ditem], dict):
+ diffprint[item].pop(ditem)
+ if ditem.lower() in ignorekeys or ditem.isdigit():
+ continue
+ else:
+ diffprint.update({"removed": ditem})
+ diffprint.pop(item)
+ elif item == "$insert":
+ for ditem in diff[item]:
+ del diffprint[item][diffprint[item].index(ditem)]
+ diffprint.update({"changed index position": ditem[1]})
+ diffprint.pop(item)
+ elif isinstance(diff[item], dict):
+ diffprint[item] = self.recursdict(diff[item], ignorekeys)
+
+ elif isinstance(diff[item], list):
+ diffprint.update({item: {"Current": diff[item][0], "Pending": diff[item][1]}})
+ else:
+ continue
+
+ return diffprint
+
+ def pendingvalidation(self, options):
+ """Pending method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/ExitCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/ExitCommand.py
new file mode 100644
index 0000000..942343a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/ExitCommand.py
@@ -0,0 +1,80 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Exit Command for rdmc"""
+
+import sys
+
+try:
+ from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class ExitCommand:
+ """Exit class to handle exiting from interactive mode"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "exit",
+ "usage": None,
+ "description": "Run to exit from the interactive shell\n\texample: exit",
+ "summary": "Exits from the interactive shell.",
+ "aliases": ["quit"],
+ "auxcommands": ["LogoutCommand"],
+ }
+ # self.rdmc = rdmcObj
+ # self.logoutobj = rdmcObj.commands_dict["LogoutCommand"](rdmcObj)
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """If an argument is present, print help else exit
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (_, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not args or not line:
+ self.auxcommands["logout"].run("")
+
+ # System exit
+ sys.exit(ReturnCodes.SUCCESS)
+ else:
+ self.rdmc.ui.error("Exit command does not take any parameters.\n")
+ raise InvalidCommandLineErrorOPTS("Invalid command line arguments.")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/HelpCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/HelpCommand.py
new file mode 100644
index 0000000..a74ced4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/HelpCommand.py
@@ -0,0 +1,113 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Help Command for RDMC"""
+
+try:
+ from rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
+except ImportError:
+ from ilorest.rdmc_base_classes import RdmcCommandBase, RdmcOptionParser
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class HelpCommand(RdmcCommandBase):
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "help",
+ "usage": None,
+ "description": "help [COMMAND]\n\tFor more detailed command descriptions"
+ " use the help command feature\n\texample: help login\n",
+ "summary": "Displays command line syntax and help menus for individual commands." " Example: help login\n",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for help main function
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not args or not line:
+ RdmcOptionParser().print_help()
+ if self.rdmc:
+ cmddict = self.rdmc.get_commands()
+ sorted_keys = sorted(list(cmddict.keys()))
+
+ for key in sorted_keys:
+ if key[0] == "_":
+ continue
+ else:
+ self.rdmc.ui.printer("\n%s\n" % key)
+ for cmd in cmddict[key]:
+ self.rdmc.ui.printer(
+ "%-25s - %s\n"
+ % (
+ self.rdmc.commands_dict[cmd].ident["name"],
+ self.rdmc.commands_dict[cmd].ident["summary"],
+ )
+ )
+ else:
+ if self.rdmc:
+ cmddict = self.rdmc.get_commands()
+ sorted_keys = list(cmddict.keys())
+
+ for key in sorted_keys:
+ for cmd in cmddict[key]:
+ cmd_s = cmd.split("Command")
+ cmd_s = cmd_s[0]
+ if args[0].lower() == cmd_s.lower():
+ self.rdmc.ui.printer(self.rdmc.commands_dict[cmd].ident["description"] + "\n")
+ return ReturnCodes.SUCCESS
+ raise InvalidCommandLineError("Command '%s' not found." % args[0])
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/ExitCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/ExitCommand.cpython-310.pyc
new file mode 100644
index 0000000..08bb4e5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/ExitCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/HelpCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/HelpCommand.cpython-310.pyc
new file mode 100644
index 0000000..05c33da
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/HelpCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..b19776f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/REQUIREDCOMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ResultsCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ResultsCommand.py
new file mode 100644
index 0000000..31f04dc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/ResultsCommand.py
@@ -0,0 +1,156 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Results Command for rdmc"""
+
+from redfish.ris.resp_handler import ResponseHandler
+from redfish.ris.rmc_helper import EmptyRaiseForEAFP
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class ResultsCommand:
+ """Monolith class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "results",
+ "usage": None,
+ "description": "Run to show the results of the last" " changes after a server reboot.\n\texample: results",
+ "summary": "Show the results of changes which require a server reboot.",
+ "aliases": [],
+ "auxcommands": ["LoginCommand", "SelectCommand"],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Gather results of latest BIOS change
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("Results command does not take any arguments.")
+ self.resultsvalidation(options)
+ results = {}
+ if self.rdmc.app.typepath.defs.biospath[-1] == "/":
+ iscsipath = self.rdmc.app.typepath.defs.biospath + "iScsi/"
+ bootpath = self.rdmc.app.typepath.defs.biospath + "Boot/"
+ else:
+ iscsipath = self.rdmc.app.typepath.defs.biospath + "/iScsi"
+ bootpath = self.rdmc.app.typepath.defs.biospath + "/Boot"
+
+ try:
+ self.auxcommands["select"].selectfunction("SmartStorageConfig")
+ smartarray = self.rdmc.app.getprops()
+ sapaths = [path["@odata.id"].split("settings")[0] for path in smartarray]
+ except:
+ sapaths = None
+
+ biosresults = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.biospath, service=True, silent=True)
+ iscsiresults = self.rdmc.app.get_handler(iscsipath, service=True, silent=True)
+ bootsresults = self.rdmc.app.get_handler(bootpath, service=True, silent=True)
+ saresults = []
+ if sapaths:
+ saresults = [self.rdmc.app.get_handler(path, service=True, silent=True) for path in sapaths]
+ try:
+ results.update({"Bios:": biosresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
+ except Exception:
+ results.update({"Bios:": None})
+
+ try:
+ results.update({"Iscsi:": iscsiresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
+ except:
+ results.update({"Iscsi:": None})
+
+ try:
+ results.update({"Boot:": bootsresults.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
+ except:
+ results.update({"Boot:": None})
+ try:
+ for result in saresults:
+ loc = "SmartArray"
+ if saresults.index(result) > 0:
+ loc += " %d:" % saresults.index(result)
+ else:
+ loc += ":"
+ results.update({loc: result.dict[self.rdmc.app.typepath.defs.biossettingsstring]["Messages"]})
+ except:
+ results.update({"SmartArray:": None})
+
+ self.rdmc.ui.printer("Results of the previous reboot changes:\n\n")
+
+ for result in results:
+ self.rdmc.ui.printer("%s\n" % result)
+ try:
+ for msg in results[result]:
+ _ = ResponseHandler(
+ self.rdmc.app.validationmanager,
+ self.rdmc.app.typepath.defs.messageregistrytype,
+ ).message_handler(response_data=msg, message_text="", verbosity=0, dl_reg=False)
+ pass
+ except EmptyRaiseForEAFP as exp:
+ raise EmptyRaiseForEAFP(exp)
+ except Exception:
+ self.rdmc.ui.error("No messages found for %s.\n" % result[:-1])
+
+ self.cmdbase.logout_routine(self, options)
+ return ReturnCodes.SUCCESS
+
+ def resultsvalidation(self, options):
+ """Results method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SaveCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SaveCommand.py
new file mode 100644
index 0000000..55eb3f3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SaveCommand.py
@@ -0,0 +1,424 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Save Command for RDMC"""
+
+import json
+from collections import OrderedDict
+
+import redfish.ris
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ ReturnCodes,
+ iLORisCorruptionError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ ReturnCodes,
+ iLORisCorruptionError,
+ )
+
+# default file name
+__filename__ = "ilorest.json"
+
+
+class SaveCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "save",
+ "usage": None,
+ "description": "Run to save a selected type to a file"
+ "\n\texample: save --selector HpBios.\n\n\tChange the default "
+ "output filename\n\texample: save --selector HpBios. -f "
+ "output.json\n\n\tTo save multiple types in one file\n\texample: "
+ "save --multisave Bios.,ComputerSystem.",
+ "summary": "Saves the selected type's settings to a file.",
+ "aliases": [],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.filename = __filename__
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main save worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if options.multisave:
+ options.selector = ""
+ if (
+ hasattr(options, "selector")
+ and options.selector is not None
+ and "hpbaseconfigs" not in options.selector.lower()
+ and "hpebaseconfigs" not in options.selector.lower()
+ and "bios" not in options.selector.lower()
+ ):
+ self.savevalidation(options)
+ else:
+ # filename validations and checks
+ self.cmdbase.login_validation(self, options)
+ self.filename = None
+ if options.filename and len(options.filename) > 1:
+ raise InvalidCommandLineError("Save command doesn't support multiple filenames.")
+ elif options.filename:
+ self.filename = options.filename[0]
+ elif self.rdmc.config:
+ if self.rdmc.config.defaultsavefilename:
+ self.filename = self.rdmc.config.defaultsavefilename
+
+ if not self.filename:
+ self.filename = __filename__
+
+ # if args:
+ # raise InvalidCommandLineError("Save command takes no arguments.")
+
+ self.rdmc.ui.printer("Saving configuration...\n")
+ if options.filter:
+ try:
+ if (str(options.filter)[0] == str(options.filter)[-1]) and str(options.filter).startswith(("'", '"')):
+ options.filter = options.filter[1:-1]
+
+ (sel, val) = options.filter.split("=")
+ sel = sel.strip()
+ val = val.strip()
+ except:
+ raise InvalidCommandLineError("Invalid filter" " parameter format [filter_attribute]=[filter_value]")
+
+ instances = self.rdmc.app.select(
+ selector=self.rdmc.app.selector,
+ fltrvals=(sel, val),
+ path_refresh=True,
+ )
+ contents = self.saveworkerfunction(options, instances=instances)
+ else:
+ contents = self.saveworkerfunction(options)
+
+ if options.multisave:
+ for select in options.multisave:
+ try:
+ self.auxcommands["select"].run(select)
+ except:
+ pass
+ contents += self.saveworkerfunction(options)
+
+ if not contents:
+ raise redfish.ris.NothingSelectedError
+ else:
+ # TODO: Maybe add this to the command. Not sure we use it elsewhere in the lib
+ contents = self.add_save_file_header(contents)
+
+ if options.encryption:
+ with open(self.filename, "wb") as outfile:
+ outfile.write(
+ Encryption().encrypt_file(
+ json.dumps(contents, indent=2, cls=redfish.ris.JSONEncoder),
+ options.encryption,
+ )
+ )
+ else:
+ with open(self.filename, "w") as outfile:
+ outfile.write(json.dumps(contents, indent=2, cls=redfish.ris.JSONEncoder, sort_keys=True))
+ self.rdmc.ui.printer("Configuration saved to: %s\n" % self.filename)
+
+ self.cmdbase.logout_routine(self, options)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def saveworkerfunction(self, options, instances=None):
+ """Returns the currently selected type for saving
+
+ :param instances: list of instances from select to save
+ :type instances: list.
+ """
+ try:
+ if (
+ options.selector is not None
+ and (
+ "bios" in options.selector.lower()
+ or "hpbaseconfigs" in options.selector.lower()
+ or "hpebaseconfigs" in options.selector.lower()
+ )
+ ) and (options.multisave is None or options.multisave == ""):
+ raise KeyError
+ content = self.rdmc.app.getprops(insts=instances)
+ try:
+ contents = [{val[self.rdmc.app.typepath.defs.hrefstring]: val} for val in content]
+ except KeyError:
+ try:
+ contents = [{val["links"]["self"][self.rdmc.app.typepath.defs.hrefstring]: val} for val in content]
+ except KeyError:
+ raise iLORisCorruptionError(
+ "iLO Database seems to be corrupted. Please check. Reboot the server to " "restore\n"
+ )
+ except Exception:
+ config_path = None
+ contents = list()
+ if options.selector is not None:
+ if options.selector.lower() == "bios." or "bios.v" in options.selector.lower():
+ config_path = ["/redfish/v1/systems/1/bios/settings/"]
+ elif options.selector.lower() == "bios":
+ config_path = [
+ "/redfish/v1/systems/1/bios/settings/",
+ "/redfish/v1/systems/1/bios/mappings/",
+ "/redfish/v1/systems/1/bios/oem/hpe/mappings/",
+ ]
+ elif "hpbaseconfigs" in options.selector.lower() or "hpebaseconfigs" in options.selector.lower():
+ config_path = [
+ "/redfish/v1/systems/1/bios/oem/hpe/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/nvmeof/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/iscsi/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/tlsconfig/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/serverconfiglock/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/kmsconfig/baseconfigs/",
+ "/redfish/v1/systems/1/bios/oem/hpe/boot/baseconfigs/",
+ "/redfish/v1/systems/1/bios/baseconfigs/",
+ "/redfish/v1/systems/1/bios/nvmeof/baseconfigs/",
+ "/redfish/v1/systems/1/bios/iscsi/baseconfigs/",
+ "/redfish/v1/systems/1/bios/tlsconfig/baseconfigs/",
+ "/redfish/v1/systems/1/bios/serverconfiglock/baseconfigs/",
+ "/redfish/v1/systems/1/bios/kmsconfig/baseconfigs/",
+ "/redfish/v1/systems/1/bios/boot/baseconfigs/",
+ ]
+ elif "hpebiosmapping" in options.selector.lower():
+ config_path = [
+ "/redfish/v1/systems/1/bios/mappings",
+ "/redfish/v1/systems/1/bios/oem/hpe/mappings/",
+ ]
+ result = None
+ for b in config_path:
+ try:
+ result = self.rdmc.app.get_handler(b, silent=True, service=True)
+ except:
+ pass
+ if result is not None:
+ if result.status == 200:
+ d = {b: result.dict}
+ contents.append(d)
+
+ type_string = self.rdmc.app.typepath.defs.typestring
+
+ templist = list()
+ srnum_list = list()
+
+ for content in contents:
+ typeselector = None
+ pathselector = None
+
+ for path, values in content.items():
+ # if "Managers/1/EthernetInterfaces/1" not in path:
+ for dictentry in list(values.keys()):
+ if dictentry == type_string:
+ typeselector = values[dictentry]
+ pathselector = path
+ del values[dictentry]
+ if dictentry in [
+ "IPv4Addresses",
+ "IPv6Addresses",
+ "IPv6AddressPolicyTable",
+ "MACAddress",
+ "StaticNameServers",
+ # "AutoNeg",
+ # "FullDuplex",
+ "SpeedMbps",
+ ]:
+ del values[dictentry]
+ if dictentry in [
+ "IPv6StaticAddresses",
+ "IPv6StaticDefaultGateways",
+ "IPv4StaticAddresses",
+ ]:
+ if values[dictentry]:
+ del values[dictentry]
+ if dictentry in ["IPv4", "IPv6", "DHCPv6", "DHCPv4"]:
+ if "DNSServers" in values[dictentry]:
+ del values[dictentry]["DNSServers"]
+ del values["Oem"]["Hpe"][dictentry]["DNSServers"]
+ if "UseDNSServers" in values[dictentry]:
+ del values[dictentry]["UseDNSServers"]
+ del values["Oem"]["Hpe"][dictentry]["UseDNSServers"]
+
+ if values:
+ skip = False
+ if "SerialNumber" in values and values["SerialNumber"] is not None and values["SerialNumber"] != "":
+ if values["SerialNumber"] in srnum_list:
+ skip = True
+ else:
+ srnum_list.append(values["SerialNumber"])
+ tempcontents = dict()
+
+ if not skip:
+ if typeselector and pathselector:
+ tempcontents[typeselector] = {pathselector: values}
+ else:
+ raise InvalidFileFormattingError("Missing path or selector in input file.")
+
+ templist.append(tempcontents)
+
+ return templist
+
+ def nested_sort(self, data):
+ """Helper function to sort all dictionary key:value pairs
+
+ :param data: dictionary to sort
+ :type data: dict.
+ """
+
+ for key, value in data.items():
+ if isinstance(value, dict):
+ data[key] = self.nested_sort(value)
+
+ data = OrderedDict(sorted(list(data.items()), key=lambda x: x[0]))
+
+ return data
+
+ def savevalidation(self, options):
+ """Save method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ if options.multisave:
+ options.multisave = options.multisave.replace('"', "").replace("'", "")
+ options.multisave = options.multisave.replace(" ", "").split(",")
+ if not len(options.multisave) >= 1:
+ raise InvalidCommandLineError("Invalid number of types in multisave option.")
+ options.selector = options.multisave[0]
+ options.multisave = options.multisave[1:]
+
+ self.cmdbase.login_select_validation(self, options)
+
+ # filename validations and checks
+ self.filename = None
+
+ if options.filename and len(options.filename) > 1:
+ raise InvalidCommandLineError("Save command doesn't support multiple filenames.")
+ elif options.filename:
+ self.filename = options.filename[0]
+ elif self.rdmc.config:
+ if self.rdmc.config.defaultsavefilename:
+ self.filename = self.rdmc.config.defaultsavefilename
+
+ if not self.filename:
+ self.filename = __filename__
+
+ def add_save_file_header(self, contents):
+ """Helper function to retrieve the comments for save file
+
+ :param contents: current save contents
+ :type contents: list.
+ """
+ templist = list()
+
+ headers = self.rdmc.app.create_save_header()
+ templist.append(headers)
+
+ for content in contents:
+ templist.append(content)
+
+ return templist
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to use a different filename than the default one. "
+ "The default filename is %s." % __filename__,
+ action="append",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run the current command on. "
+ "Use this flag when you wish to select a type without entering another command, "
+ "or if you wish to work with a type that is different from the one currently "
+ "selected.",
+ default=None,
+ # required=True,
+ )
+ customparser.add_argument(
+ "--multisave",
+ dest="multisave",
+ help="Optionally include this flag to save multiple types to a single file. "
+ "Overrides the currently selected type.\n\t Usage: --multisave type1.,type2.,type3.",
+ default="",
+ )
+ customparser.add_argument(
+ "--filter",
+ dest="filter",
+ help="Optionally set a filter value for a filter attribute. This uses the provided "
+ "filter for the currently selected type. Note: Use this flag to narrow down your "
+ "results. For example, selecting a common type might return multiple objects that "
+ "are all of that type. If you want to modify the properties of only one of those "
+ "objects, use the filter flag to narrow down results based on properties."
+ "\n\t Usage: --filter [ATTRIBUTE]=[VALUE]",
+ default=None,
+ )
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the displayed output to "
+ "JSON format. Preserving the JSON data structure makes the information easier to "
+ "parse.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--encryption",
+ dest="encryption",
+ help="Optionally include this flag to encrypt/decrypt a file using the key provided.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SelectCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SelectCommand.py
new file mode 100644
index 0000000..0b7a799
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SelectCommand.py
@@ -0,0 +1,143 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Select Command for RDMC"""
+
+from redfish.ris import NothingSelectedError
+
+try:
+ from rdmc_helper import LOGGER, InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import LOGGER, InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class SelectCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "select",
+ "usage": None,
+ "description": "Selects the Redfish/HpRest type to be used.\nIn order to "
+ "remove the need of including the version while selecting you"
+ " can simply enter the type name until the first period\n\t"
+ "example: select HpBios.\n"
+ "Run without an argument to "
+ "display the currently selected type\n\texample: select",
+ "summary": "Selects the object type to be used.",
+ "aliases": ["sel"],
+ "auxcommands": ["LoginCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def selectfunction(self, line):
+ """Main select worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.selectvalidation(options)
+
+ if args:
+ if options.ref:
+ LOGGER.warning("Patches from current selection will be cleared.")
+ selector = args[0]
+ selections = self.rdmc.app.select(selector=selector, path_refresh=options.ref)
+
+ if "storage" in selector.lower():
+ storage_url = "/redfish/v1/Systems/1/Storage/"
+ list_storageid = self.rdmc.app.get_handler(storage_url, silent=True, service=True).dict["Members"]
+ for val in list_storageid:
+ storageid_fromurl = val["@odata.id"]
+ ctr = self.rdmc.app.get_handler(storageid_fromurl, silent=True, service=True).dict
+ if "Controllers" in ctr:
+ ctr_url = ctr["Controllers"]["@odata.id"]
+ self.rdmc.app.get_handler(ctr_url, silent=True, service=True).dict
+ if self.rdmc.opts.verbose and selections:
+ templist = list()
+ self.rdmc.ui.printer("Selected option(s): ")
+
+ for item in selections:
+ if item.type not in templist:
+ templist.append(item.type)
+
+ self.rdmc.ui.printer("%s\n" % ", ".join(map(str, templist)))
+
+ else:
+ selector = self.rdmc.app.selector
+
+ if selector:
+ sellist = [sel for sel in self.rdmc.app.monolith.typesadded if selector.lower() in sel.lower()]
+ self.rdmc.ui.printer("Current selection: ")
+ self.rdmc.ui.printer("%s\n" % ", ".join(map(str, sellist)))
+ else:
+ raise NothingSelectedError
+
+ self.cmdbase.logout_routine(self, options)
+
+ def selectvalidation(self, options):
+ """Select data validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ self.cmdbase.login_select_validation(self, options)
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for main select function
+
+ :param line: entered command line
+ :type line: list.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ self.selectfunction(line)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--refresh",
+ dest="ref",
+ action="store_true",
+ help="Optionally reload the data of selected type and clear " "patches from current selection.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SetCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SetCommand.py
new file mode 100644
index 0000000..c34dcd0
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/SetCommand.py
@@ -0,0 +1,472 @@
+###
+# Copyright 2016-2023 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Set Command for RDMC"""
+import json
+import os
+import sys
+
+import redfish.ris
+
+from redfish.rest.connections import SecurityStateError
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ )
+
+from redfish.ris.rmc_helper import NothingSelectedError
+
+
+class SetCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "set",
+ "usage": None,
+ "description": "Setting a "
+ "single level property example:\n\tset property=value\n\n\t"
+ "Setting multiple single level properties example:\n\tset "
+ "property=value property=value property=value\n\n\t"
+ "Setting a multi level property example:\n\tset property/"
+ "subproperty=value",
+ "summary": "Changes the value of a property within the" " currently selected type.",
+ "aliases": [],
+ "auxcommands": [
+ "CommitCommand",
+ "RebootCommand",
+ "RawPatchCommand",
+ "SelectCommand",
+ "LogoutCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def setfunction(self, line, skipprint=False):
+ """Main set worker function
+
+ :param line: command line input
+ :type line: string.
+ :param skipprint: boolean to determine output
+ :type skipprint: boolean.
+ """
+
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not self.rdmc.interactive and not self.rdmc.app.cache:
+ raise InvalidCommandLineError("The 'set' command is not useful in " "non-interactive and non-cache modes.")
+
+ self.setvalidation(options)
+ ilover = self.rdmc.app.getiloversion()
+ if ilover >= 7.000 and ("HighSecurity" in args[0] or "Production" in args[0]):
+ self.rdmc.ui.error(
+ "HighSecurity and Production modes are not supported on iLO 7 , Please use SecureStandard instead \n"
+ )
+ raise SecurityStateError(
+ "HighSecurity and Production modes are not supported on iLO 7 , Please use SecureStandard instead"
+ )
+ else:
+ if "SecureStandard" in args[0] and ilover < 7.000:
+ self.rdmc.ui.error(
+ "SecureStandard mode is not supported on iLO 6 and below , Please use HighSecurity instead \n"
+ )
+ raise SecurityStateError(
+ "SecureStandard mode is not supported on iLO 6 and below , Please use HighSecurity instead \n"
+ )
+ _ = args[0].find("HighSecurity")
+ if ilover >= 7.000:
+ _ = args[0].find("SecureStandard")
+ fsel = None
+ fval = None
+ if args:
+ if "ethernetinterface" in self.rdmc.app.selector.lower() and "ipv4addresses" in args[0].lower():
+ raise InvalidCommandLineError(
+ "For security reasons this request is discarded. "
+ "Use `rawpatch` or the `load --force_network_config` command "
+ "to modify the IP configuration of the ilO.\n"
+ )
+ if options.filter:
+ try:
+ (fsel, fval) = str(options.filter).strip("'\" ").split("=")
+ (fsel, fval) = (fsel.strip(), fval.strip())
+ except:
+ raise InvalidCommandLineError(
+ "Invalid filter" " parameter format [filter_attribute]=[filter_value]"
+ )
+
+ if any([s.lower().startswith("adminpassword=") for s in args]) and not any(
+ [s.lower().startswith("oldadminpassword=") for s in args]
+ ):
+ raise InvalidCommandLineError(
+ "'OldAdminPassword' must also "
+ "be set with the current password \nwhen "
+ "changing 'AdminPassword' for security reasons."
+ )
+ count = 0
+ for arg in args:
+ if arg:
+ if len(arg) > 2:
+ if ('"' in arg[0] and '"' in arg[-1]) or ("'" in arg[0] and "'" in arg[-1]):
+ args[count] = arg[1:-1]
+ elif len(arg) == 2:
+ if (arg[0] == '"' and arg[1] == '"') or (arg[0] == "'" and arg[1] == "'"):
+ args[count] = None
+ count += 1
+ if not self.rdmc.app.selector:
+ raise NothingSelectedError
+ if "." not in self.rdmc.app.selector:
+ self.rdmc.app.selector = self.rdmc.app.selector + "."
+ if self.rdmc.app.selector:
+ if self.rdmc.app.selector.lower().startswith("bios."):
+ if "attributes" not in arg.lower():
+ arg = "Attributes/" + arg
+
+ try:
+ (sel, val) = arg.split("=")
+ sel = sel.strip().lower()
+ val = val.strip("\"'")
+
+ if val.lower() == "true" or val.lower() == "false":
+ val = val.lower() in ("yes", "true", "t", "1")
+ except:
+ raise InvalidCommandLineError("Invalid set parameter format. [Key]=[Value]")
+
+ newargs = list()
+
+ if "/" in sel and "/" not in str(val):
+ newargs = sel.split("/")
+ elif "/" in sel:
+ items = arg.split("=", 1)
+ newargs = items[0].split("/")
+ if not isinstance(val, bool):
+ if val:
+ if val[0] == "[" and val[-1] == "]":
+ val = val[1:-1].split(",")
+ payload = {newargs[-1]: val} if newargs else {sel: val}
+ if newargs:
+ for key in newargs[:-1][::-1]:
+ payload = {key: payload}
+ try:
+ contents = self.rdmc.app.loadset(
+ seldict=payload,
+ latestschema=options.latestschema,
+ fltrvals=(fsel, fval),
+ uniqueoverride=options.uniqueoverride,
+ )
+ if not contents:
+ if not sel.lower() == "oldadminpassword":
+ raise InvalidOrNothingChangedSettingsError(
+ "Nothing changed "
+ "for attribute '%s'.\nPlease check if the attribute is Oem/Hpe Attribute or Read-only "
+ "or System Unique property or the value trying to set is same or invalid" % sel
+ )
+ elif contents == "No entries found":
+ raise InvalidOrNothingChangedSettingsError(
+ "No " "entries found in the current " "selection for the setting '%s'." % sel
+ )
+ elif contents == "reverting":
+ self.rdmc.ui.error("Removing previous patch and returning to the " "original value.\n")
+ else:
+ for content in contents:
+ self.rdmc.ui.printer("Added the following patch:\n")
+ self.rdmc.ui.print_out_json(content)
+
+ except redfish.ris.ValidationError as excp:
+ errs = excp.get_errors()
+
+ for err in errs:
+ if err.sel and err.sel.lower() == "adminpassword":
+ types = self.rdmc.app.monolith.types
+
+ for item in types:
+ for instance in types[item]["Instances"]:
+ if "hpbios." in instance.maj_type.lower():
+ _ = [
+ instance.patches.remove(patch)
+ for patch in instance.patches
+ if patch.patch[0]["path"] == "/OldAdminPassword"
+ ]
+
+ if isinstance(err, redfish.ris.RegistryValidationError):
+ self.rdmc.ui.printer(err.message)
+
+ raise redfish.ris.ValidationError(excp)
+
+ if options.commit:
+ self.auxcommands["commit"].commitfunction(options)
+
+ if options.reboot and not options.commit:
+ self.auxcommands["reboot"].run(options.reboot)
+
+ if options.logout:
+ self.auxcommands["logout"].run("")
+
+ else:
+ raise InvalidCommandLineError("Missing parameters for 'set' command.\n")
+
+ def patchfunction(self, line):
+ """Main set worker function
+ :param line: command line input
+ :type line: string.
+ :param skipprint: boolean to determine output
+ :type skipprint: boolean.
+ """
+ import platform
+ import tempfile
+
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not self.rdmc.interactive and not self.rdmc.app.cache:
+ raise InvalidCommandLineError("The 'set' command is not useful in " "non-interactive and non-cache modes.")
+
+ _ = self.rdmc.app.selector
+ self.setvalidation(options)
+ if args:
+ count = 0
+ for arg in args:
+ if arg:
+ if len(arg) > 2:
+ if ('"' in arg[0] and '"' in arg[-1]) or ("'" in arg[0] and "'" in arg[-1]):
+ args[count] = arg[1:-1]
+ elif len(arg) == 2:
+ if (arg[0] == '"' and arg[1] == '"') or (arg[0] == "'" and arg[1] == "'"):
+ args[count] = None
+ count += 1
+ if not self.rdmc.app.selector:
+ raise NothingSelectedError
+ if "." not in self.rdmc.app.selector:
+ self.rdmc.app.selector = self.rdmc.app.selector + "."
+ if self.rdmc.app.selector:
+ if self.rdmc.app.selector.lower().startswith("bios."):
+ if "attributes" not in arg.lower():
+ arg = "Attributes/" + arg
+
+ try:
+ (sel, val) = arg.split("=")
+ sel = sel.strip()
+ val = val.strip("\"'")
+
+ if val.lower() == "true" or val.lower() == "false":
+ val = val.lower() in ("yes", "true", "t", "1")
+ except:
+ raise InvalidCommandLineError("Invalid set parameter format. [Key]=[Value]")
+
+ newargs = list()
+
+ if "/" in sel and "/" not in str(val):
+ newargs = sel.split("/")
+ elif "/" in sel:
+ items = arg.split("=", 1)
+ newargs = items[0].split("/")
+
+ if not isinstance(val, bool):
+ if val:
+ if val[0] == "[" and val[-1] == "]":
+ val = val[1:-1].split(",")
+ if val.isdigit():
+ val = int(val)
+ patch_data = dict()
+ payload = {newargs[-1]: val} if newargs else {sel: val}
+ if "drive" in self.rdmc.app.selector.lower():
+ tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
+ temp_file = os.path.join(tempdir, "temp_patch.json")
+ out_file = open(temp_file, "w")
+ if getattr(options, "filter", None):
+ filt_arr = options.filter.split("=")
+ patchpath = filt_arr[-1]
+ else:
+ sys.stdout.write("Please provide the actual drive to modify using filter option\n")
+ return
+ patch_payload = {patchpath: payload}
+ json.dump(patch_payload, out_file, indent=6)
+ out_file.close()
+ sys.stdout.write("payload %s \n" % patch_payload)
+ self.auxcommands["rawpatch"].run(temp_file + " --service")
+ os.remove(temp_file)
+ elif newargs:
+ for key in newargs[:-1][::-1]:
+ if key == "PowerControl":
+ payload = {key: [payload]}
+ else:
+ payload = {key: payload}
+ patch_data.update(payload)
+ if "Oem" in key:
+ if (
+ "managernetworkprotocol." in self.rdmc.app.selector
+ or "thermal." in self.rdmc.app.selector
+ ):
+ tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
+ temp_file = os.path.join(tempdir, "temp_patch.json")
+ out_file = open(temp_file, "w")
+ if "managernetworkprotocol." in self.rdmc.app.selector:
+ patchpath = self.rdmc.app.typepath.defs.managerpath + "NetworkProtocol/"
+ elif "thermal." in self.rdmc.app.selector:
+ patchpath = "/redfish/v1/Chassis/1/Thermal/"
+ patch_payload = {patchpath: payload}
+ json.dump(patch_payload, out_file, indent=6)
+ out_file.close()
+ sys.stdout.write("payload %s \n" % patch_payload)
+ self.auxcommands["rawpatch"].run(temp_file + " --service")
+ self.auxcommands["select"].run(self.rdmc.app.selector + " --refresh")
+ os.remove(temp_file)
+ if "PowerControl" in payload.keys():
+ import platform
+ import tempfile
+
+ tempdir = "/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
+ temp_file = os.path.join(tempdir, "temp_patch.json")
+ out_file = open(temp_file, "w")
+ patch_path = "/redfish/v1/Chassis/1/Power"
+ patch_payload = {patch_path: payload}
+ json.dump(patch_payload, out_file, indent=6)
+ out_file.close()
+ self.auxcommands["rawpatch"].run(temp_file + " --service")
+ os.remove(temp_file)
+
+ def run(self, line, skipprint=False, help_disp=False):
+ """Main set function
+
+ :param line: command line input
+ :type line: string.
+ :param skipprint: boolean to determine output
+ :type skipprint: boolean.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ if (
+ ("Oem/Hpe/EnhancedDownloadPerformanceEnabled" in line[0])
+ or ("Oem/Hpe/ThermalConfiguration" in line[0])
+ or ("Oem/Hpe/FanPercentMinimum" in line[0])
+ or ("PowerControl" in line[0])
+ or (self.rdmc.app.selector is not None and "drive" in self.rdmc.app.selector.lower())
+ ):
+ self.patchfunction(line)
+ else:
+ try:
+ self.setfunction(line, skipprint=skipprint)
+ except SecurityStateError:
+ return ReturnCodes.V1_SECURITY_STATE_ERROR
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def setvalidation(self, options):
+ """Set data validation function"""
+
+ if self.rdmc.opts.latestschema:
+ options.latestschema = True
+ if self.rdmc.config.commit.lower() == "true":
+ options.commit = True
+ try:
+ self.cmdbase.login_select_validation(self, options)
+ except redfish.ris.NothingSelectedError:
+ raise redfish.ris.NothingSelectedSetError("")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--selector",
+ dest="selector",
+ help="Optionally include this flag to select a type to run"
+ " the current command on. Use this flag when you wish to"
+ " select a type without entering another command, or if you"
+ " wish to work with a type that is different from the one"
+ " you currently have selected.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--filter",
+ dest="filter",
+ help="Optionally set a filter value for a filter attribute."
+ " This uses the provided filter for the currently selected"
+ " type. Note: Use this flag to narrow down your results. For"
+ " example, selecting a common type might return multiple"
+ " objects that are all of that type. If you want to modify"
+ " the properties of only one of those objects, use the filter"
+ " flag to narrow down results based on properties."
+ "\t\t\t\t\t Usage: --filter [ATTRIBUTE]=[VALUE]",
+ default=None,
+ )
+ customparser.add_argument(
+ "--commit",
+ dest="commit",
+ action="store_true",
+ help="Use this flag when you are ready to commit all pending"
+ " changes. Note that some changes made in this way will be updated"
+ " instantly, while others will be reflected the next time the"
+ " server is started.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Use this flag to perform a reboot command function after"
+ " completion of operations. For help with parameters and"
+ " descriptions regarding the reboot flag, run help reboot.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--latestschema",
+ dest="latestschema",
+ action="store_true",
+ help="Optionally use the latest schema instead of the one "
+ "requested by the file. Note: May cause errors in some data "
+ "retrieval due to difference in schema versions.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--uniqueoverride",
+ dest="uniqueoverride",
+ action="store_true",
+ help="Override the measures stopping the tool from writing " "over items that are system unique.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/StatusCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/StatusCommand.py
new file mode 100644
index 0000000..ab96226
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/StatusCommand.py
@@ -0,0 +1,203 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Status Command for RDMC"""
+
+from functools import reduce
+
+from redfish.ris.utils import merge_dict
+
+try:
+ from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class StatusCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "status",
+ "usage": None,
+ "description": "Run to display all pending changes within"
+ " the currently\n\tselected type that need to be"
+ " committed\n\texample: status",
+ "summary": "Displays all pending changes within a selected type" " that need to be committed.",
+ "aliases": [],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main status worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.statusvalidation(options)
+ contents = self.rdmc.app.status()
+ selector = self.rdmc.app.selector
+
+ if contents and options.json:
+ self.jsonout(contents)
+ elif contents:
+ self.outputpatches(contents, selector)
+ else:
+ self.rdmc.ui.printer("No changes found\n")
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def jsonout(self, contents):
+ """Helper function to print json output of patches
+
+ :param contents: contents for the selection
+ :type contents: string.
+ """
+ self.rdmc.ui.printer("Current changes found:\n")
+ createdict = lambda y, x: {x: y}
+ totdict = {}
+ for item in contents:
+ for keypath, value in item.items():
+ path = keypath.split("(")[1].strip("()")
+ cont = {}
+ totdict[path] = cont
+ for content in value:
+ val = (
+ ["List Manipulation"]
+ if content["op"] == "move"
+ else [content["value"].strip("\"'")] if len(content["value"]) else [""]
+ )
+ cont = reduce(
+ createdict,
+ reversed([path] + content["path"].strip("/").split("/") + val),
+ )
+ merge_dict(totdict, cont)
+ self.rdmc.ui.print_out_json(totdict)
+
+ def outputpatches(self, contents, selector):
+ """Helper function for status for use in patches
+
+ :param contents: contents for the selection
+ :type contents: string.
+ :param selector: type selected
+ :type selector: string.
+ """
+ self.rdmc.ui.printer("Current changes found:\n")
+ for item in contents:
+ moveoperation = ""
+ for key, value in item.items():
+ if selector and key.lower().startswith(selector.lower()):
+ self.rdmc.ui.printer("%s (Currently selected)\n" % key)
+ else:
+ self.rdmc.ui.printer("%s\n" % key)
+
+ for content in value:
+ try:
+ if content["op"] == "move":
+ moveoperation = "/".join(content["path"].split("/")[1:-1])
+ continue
+ except:
+ if content[0]["op"] == "move":
+ moveoperation = "/".join(content[0]["path"].split("/")[1:-1])
+ continue
+ try:
+ if isinstance(content[0]["value"], int):
+ self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], content[0]["value"]))
+ elif not isinstance(content[0]["value"], bool) and not len(content[0]["value"]) == 0:
+ if content[0]["value"][0] == '"' and content[0]["value"][-1] == '"':
+ self.rdmc.ui.printer(
+ "\t%s=%s"
+ % (
+ content[0]["path"][1:],
+ content[0]["value"][1:-1],
+ )
+ )
+ else:
+ self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], content[0]["value"]))
+ else:
+ output = content[0]["value"]
+
+ if not isinstance(output, bool):
+ if len(output) == 0:
+ output = '""'
+
+ self.rdmc.ui.printer("\t%s=%s" % (content[0]["path"][1:], output))
+ except:
+ if isinstance(content["value"], int):
+ self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
+ elif (
+ content["value"]
+ and not isinstance(content["value"], bool)
+ and not len(content["value"]) == 0
+ ):
+ if content["value"][0] == '"' and content["value"][-1] == '"':
+ self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
+ else:
+ self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], content["value"]))
+ else:
+ output = content["value"]
+
+ if output and not isinstance(output, bool):
+ if len(output) == 0:
+ output = '""'
+
+ self.rdmc.ui.printer("\t%s=%s" % (content["path"][1:], output))
+ self.rdmc.ui.printer("\n")
+ if moveoperation:
+ self.rdmc.ui.printer("\t%s=List Manipulation\n" % moveoperation)
+
+ def statusvalidation(self, options):
+ """Status method validation function"""
+
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/TypesCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/TypesCommand.py
new file mode 100644
index 0000000..c11c110
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/TypesCommand.py
@@ -0,0 +1,125 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Types Command for RDMC"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class TypesCommand:
+ """Constructor"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "types",
+ "usage": None,
+ "description": "Run to display currently " "available selectable types\n\tExample: types",
+ "summary": "Displays all selectable types within the currently logged in server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def typesfunction(self, line, returntypes=False):
+ """Main types worker function
+
+ :param line: command line input
+ :type line: string.
+ :param returntypes: flag to determine if types should be printed
+ :type returntypes: boolean.
+ """
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.typesvalidation(options)
+
+ if not args:
+ typeslist = list()
+ typeslist = sorted(set(self.rdmc.app.types(options.fulltypes)))
+
+ if not returntypes:
+ self.rdmc.ui.printer("Type options:\n")
+
+ for item in typeslist:
+ self.rdmc.ui.printer("%s\n" % item)
+ else:
+ return typeslist
+ else:
+ raise InvalidCommandLineError("The 'types' command does not take any arguments.")
+
+ self.cmdbase.logout_routine(self, options)
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for types main function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ self.typesfunction(line)
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def typesvalidation(self, options):
+ """types method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--fulltypes",
+ dest="fulltypes",
+ action="store_true",
+ help="Optionally include this flag if you would prefer to "
+ "return the full type name instead of the simplified versions"
+ " (Redfish only option).",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/CommitCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/CommitCommand.cpython-310.pyc
new file mode 100644
index 0000000..0e97bb7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/CommitCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/DetectiLOCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/DetectiLOCommand.cpython-310.pyc
new file mode 100644
index 0000000..34f7153
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/DetectiLOCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/GetCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/GetCommand.cpython-310.pyc
new file mode 100644
index 0000000..05ff597
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/GetCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/InfoCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/InfoCommand.cpython-310.pyc
new file mode 100644
index 0000000..cecbb2a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/InfoCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ListCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ListCommand.cpython-310.pyc
new file mode 100644
index 0000000..3870bf0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ListCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoadCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoadCommand.cpython-310.pyc
new file mode 100644
index 0000000..0441327
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoadCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoginCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoginCommand.cpython-310.pyc
new file mode 100644
index 0000000..90c894e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LoginCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LogoutCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LogoutCommand.cpython-310.pyc
new file mode 100644
index 0000000..c66af3b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/LogoutCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/PendingChangesCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/PendingChangesCommand.cpython-310.pyc
new file mode 100644
index 0000000..9bd9aa9
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/PendingChangesCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ResultsCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ResultsCommand.cpython-310.pyc
new file mode 100644
index 0000000..9bf55ff
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/ResultsCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SaveCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SaveCommand.cpython-310.pyc
new file mode 100644
index 0000000..f137bff
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SaveCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SelectCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SelectCommand.cpython-310.pyc
new file mode 100644
index 0000000..281d150
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SelectCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SetCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SetCommand.cpython-310.pyc
new file mode 100644
index 0000000..0a56958
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/SetCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/StatusCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/StatusCommand.cpython-310.pyc
new file mode 100644
index 0000000..8a34d65
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/StatusCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/TypesCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/TypesCommand.cpython-310.pyc
new file mode 100644
index 0000000..db5e3ba
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/TypesCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..890aed4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/AdvancedPmmConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/AdvancedPmmConfigCommand.py
new file mode 100644
index 0000000..6922a6d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/AdvancedPmmConfigCommand.py
@@ -0,0 +1,453 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to apply specified configuration to PMM"""
+from __future__ import absolute_import # verify if python3 libs can handle
+
+from argparse import Action
+from copy import deepcopy
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ LOGGER,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ )
+
+from .lib.RestHelpers import RestHelpers
+
+
+class _ParseOptionsList(Action):
+ def __init__(self, option_strings, dest, nargs, **kwargs):
+ super(_ParseOptionsList, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_strings):
+ """
+ Callback to parse a comma-separated list into an array.
+ Then store the array in the option's destination
+ """
+ try:
+ setattr(namespace, self.dest, next(iter(values)).split(","))
+ except:
+ raise InvalidCommandLineError("Values in a list must be separated by a comma.")
+
+
+class AdvancedPmmConfigCommand:
+ """
+ Command to apply specified configuration to PMM
+ """
+
+ def __init__(self):
+ self.ident = {
+ "name": "provisionpmm",
+ "usage": None,
+ "description": "Applies specified configuration to PMM.\n" "\texample: provisionpmm -m 50 -i On -pid 1,2",
+ "summary": "Applies specified configuration to PMM.",
+ "aliases": ["provisionpmm"],
+ "auxcommands": [
+ "ShowPmemPendingConfigCommand",
+ "ClearPendingConfigCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self._rest_helpers = RestHelpers(rdmcObject=self.rdmc)
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("PMM: %s", self.ident["name"])
+
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+
+ if args:
+ self.validate_args(args, options)
+ self.validate_options(options)
+ # Raise exception if server is in POST
+ if self._rest_helpers.in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ self.configure_pmm(options)
+
+ return ReturnCodes.SUCCESS
+
+ @staticmethod
+ def validate_args(args, options):
+ """
+ Produces relevant error messages when unwanted
+ extra arguments are specified with flags.
+ """
+ error_message = (
+ "Chosen flag is invalid. Use the '-m | --memory-mode'" " flag to specify the size in percentage."
+ )
+ some_flag = options.memorymode or options.interleave or options.proc or options.force
+
+ case_a = options.force and not options.memorymode and not options.interleave and not options.proc
+ case_b = options.interleave and not options.memorymode and not options.force and not options.proc
+ case_c = options.proc and not options.memorymode and not options.force and not options.interleave
+ case_d = options.memorymode and not options.proc and not options.interleave and not options.force
+
+ if case_a or case_b or case_c:
+ raise InvalidCommandLineError(error_message)
+ if case_d or (some_flag not in args):
+ raise InvalidCommandLineError("Chosen flag is Invalid")
+
+ def validate_options(self, options):
+ """
+ Validate whether options specified by user are valid or not
+ :param options: options specified by the user with the 'provisionpmm' command
+ :type options: instance of OptionParser class
+ """
+ some_flag = options.memorymode or options.interleave or options.proc or options.force
+ if not some_flag:
+ raise InvalidCommandLineError("No flag specified.\n\nUsage: " + self.ident["usage"])
+
+ # If the memory mode option value is not valid.
+ resp = self._rest_helpers.retrieve_model(self.rdmc)
+ model = resp["Model"]
+
+ if "Gen10 Plus" in model:
+ if not (options.memorymode == 0 or options.memorymode == 100):
+ raise InvalidCommandLineError("Specify the correct value (0 or 100)" " to configure PMM")
+ else:
+ if options.memorymode < 0 or options.memorymode > 100:
+ raise InvalidCommandLineError("Specify the correct value (1-100)" " to configure PMM")
+ if options.interleave:
+ if options.interleave.lower() not in ["on", "off"]:
+ raise InvalidCommandLineError(
+ "Specify the correct value to set interleave"
+ " state of persistent memory regions\n\n" + self.ident["usage"]
+ )
+ options.interleave = options.interleave.lower()
+
+ if options.proc and not options.memorymode and not options.interleave:
+ raise InvalidCommandLineError(
+ "Use '-m|--memory-mode' flag to specify the size in percentage"
+ " or '-i|--pmem-interleave' flag to set interleave"
+ " state of persistent memory regions"
+ )
+
+ # If not 100% memorymode, -i flag is mandatory to interleave memroy regions.
+ if 0 <= options.memorymode < 100 and not options.interleave:
+ raise InvalidCommandLineError(
+ "Use '-i | --pmem-interleave' flag to" " specify the interleave state of persistent" " memory regions"
+ )
+ if options.proc:
+ for proc_id in options.proc:
+ if not proc_id.isdigit():
+ raise InvalidCommandLineError("Specify the correct processor id")
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-m",
+ "--memory-mode",
+ dest="memorymode",
+ type=int,
+ help="Percentage of the total capacity to configure all PMMs to MemoryMode."
+ " This flag is optional. If not specified,"
+ " by default configuration will be applied with 0%% Volatile."
+ " To interleave persistent memory regions, use this flag in conjunction with"
+ " the --pmem-interleave flag. To configure all the PMMs on specific processor,"
+ " use this flag in conjunction with the --proc flag.",
+ default=0,
+ )
+
+ customparser.add_argument(
+ "-i",
+ "--pmem-interleave",
+ dest="interleave",
+ help="Indicates whether the persistent memory regions should be interleaved or not.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "-pid",
+ "--proc",
+ nargs=1,
+ action=_ParseOptionsList,
+ dest="proc",
+ help="To apply selected configuration on specific processors, "
+ "supply a comma-separated list of Processor IDs (without spaces) where "
+ "p=processor id. This flag is optional. If not specified, "
+ "by default configuration will be applied to all the Processors.",
+ )
+
+ customparser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ dest="force",
+ help="Allow the user to force the configuration " "by automatically accepting any prompts.",
+ default=False,
+ )
+
+ @staticmethod
+ def warn_existing_chunks_and_tasks(self, memory_chunk_tasks, memory_chunks):
+ """
+ Checks for existing Memory Chunks and Pending Configuration Task resources on
+ a server where a user is trying to selected configuration
+ :param memory_chunk_tasks: Pending Configuration Tasks
+ :type memory_chunk_tasks: list
+ :memory_chunks: Memory Chunks in the existing configuration
+ :type memory_chunks: list
+ :returns: None
+ """
+ # If Memory Chunks exist, display Existing configuration warning
+ if memory_chunks:
+ self.rdmc.ui.warn(
+ "Existing configuration found. Proceeding with applying a new "
+ "configuration will result in overwriting the current configuration and "
+ "cause data loss.\n"
+ )
+ # If Pending Configuration Tasks exist, display warning
+ if memory_chunk_tasks:
+ self.rdmc.ui.printer(
+ "Warning: Pending configuration tasks found. Proceeding with applying "
+ "a new configuration will result in overwriting the pending "
+ "configuration tasks.\n"
+ )
+ # Raise a NoChangesFoundOrMade exception when either of the above conditions exist
+ if memory_chunks or memory_chunk_tasks:
+ # Line feed for proper formatting
+ raise NoChangesFoundOrMadeError(
+ "\nFound one or more of Existing Configuration or "
+ "Pending Configuration Tasks. Please use the "
+ "'--force | -f' flag with the same command to "
+ "approve these changes."
+ )
+ return None
+
+ def delete_existing_chunks_and_tasks(self, memory_chunk_tasks, memory_chunks):
+ """
+ Delete existing Memory Chunks and Pending Configuration Tasks
+ :param memory_chunk_tasks: Pending Configuration Tasks
+ :type memory_chunk_tasks: list
+ :memory_chunks: Memory Chunks in the existing configuration
+ :type memory_chunks: list
+ :returns: None
+ """
+ # Delete any pending configuration tasks
+ if memory_chunk_tasks:
+ self.auxcommands["clearpmmpendingconfig"].delete_tasks(memory_chunk_tasks)
+ # Delete any existing configuration
+ if memory_chunks:
+ for chunk in memory_chunks:
+ data_id = chunk.get("@odata.id")
+ resp = self._rest_helpers.delete_resource(data_id)
+ if not resp:
+ raise NoChangesFoundOrMadeError("Error occurred while deleting " "existing configuration")
+ return None
+
+ @staticmethod
+ def get_valid_processor_list(input_proc_list, domain_members):
+ """
+ Function to check processors specified by user are valid or not
+ :param domain_members: Memory Resources
+ :type input_proc_list: List
+ :type domain_members: Dict
+ :returns: List of valid processors in string format (ex: [PROC1MemoryDomain])
+ """
+ proc_list = []
+ index = 0
+ invalid_proc_list = []
+ # Create list with valid processors based on 'Id' attribute.
+ for member in domain_members:
+ proc_list.append(member["Id"])
+
+ for index, proc_id in enumerate(input_proc_list):
+ temp_proc_id = "PROC" + str(proc_id) + "MemoryDomain"
+ if temp_proc_id not in proc_list:
+ invalid_proc_list.append(proc_id)
+ else:
+ input_proc_list[index] = temp_proc_id
+
+ # Raise execption if given proc id is not exist.
+ if invalid_proc_list:
+ if len(invalid_proc_list) == 1:
+ raise NoChangesFoundOrMadeError(
+ "Specified processor number {proc_list} is invalid".format(proc_list=invalid_proc_list[0])
+ )
+ else:
+ raise NoChangesFoundOrMadeError(
+ "Specified processor numbers {proc_list} are invalid".format(proc_list=",".join(invalid_proc_list))
+ )
+
+ return input_proc_list
+
+ @staticmethod
+ def filter_memory_chunks(memory_chunks, domain_members, proc_list):
+ """
+ Filter memory chunks based on specified processor list
+ :param memory_chunks: list of all memory chunks
+ :param domain_members: dict of memory resources
+ :param proc list: List of specified processors
+ :Retrun: List of memory chunks corresponding to specified processors
+ """
+ all_chunks = list()
+ for member in domain_members:
+ proc_id = member["Id"]
+ if not proc_list or (proc_id in proc_list):
+ data_id = member.get("MemoryChunks").get("@odata.id")
+ all_chunks += [chunk for chunk in memory_chunks if data_id in chunk.get("@odata.id")]
+
+ return all_chunks
+
+ @staticmethod
+ def get_post_data(config_data, interleavable_memory_sets):
+ """
+ Create post body based on config data
+ :param config_data: dict with user input
+ :type config_data: dict
+ :param interleavable_memory_sets: List of interleavable sets
+ :type interleavable_memory_sets: List
+ :returns: Returns post data
+ """
+
+ body = {
+ "AddressRangeType": "PMEM",
+ "InterleaveSets": [],
+ "Oem": {"Hpe": {"MemoryChunkSizePercentage": 100 - config_data["size"]}},
+ }
+ # Get the list of interleave sets based on the configuration
+ if config_data["pmeminterleave"] == "on" or config_data["size"] == 100:
+ # If persistent memory regions are interleaved or if it's 100% MemoryMode,
+ # choose the list with maximum entries.
+ interleave_sets = [max(interleavable_memory_sets, key=lambda x: len(x["MemorySet"]))]
+ else:
+ # If persistent memory regions are not interleaved,
+ # choose all the lists with exactly one entry.
+ interleave_sets = [il_set for il_set in interleavable_memory_sets if len(il_set["MemorySet"]) == 1]
+
+ # Using in-place update to change the interleave sets format.
+ # Replace 'MemorySet' with 'Memory' for each MemorySet in interleave_sets.
+ for index, interleavableset in enumerate(interleave_sets):
+ interleave_sets[index] = [
+ {"Memory": {"@odata.id": str(memory_region["@odata.id"])}}
+ for memory_region in interleavableset["MemorySet"]
+ ]
+
+ # Create post body for each interleave sets from post body template.
+ post_data = []
+ for interleaveset in interleave_sets:
+ current_body = deepcopy(body)
+ current_body["InterleaveSets"] = interleaveset
+ post_data.append(current_body)
+
+ return post_data
+
+ def configure_pmm(self, options):
+ """
+ Applies selected configuration to the PMMs
+ :param options: options specified by user
+ :returns: None
+ """
+ # Retrieve Memory Chunks and Task Resources from server.
+ (
+ task_members,
+ domain_members,
+ memory_chunks,
+ ) = self._rest_helpers.retrieve_task_members_and_mem_domains()
+
+ # Filter Task Resources to include only Pending Configuration Tasks.
+ memory_chunk_tasks = self._rest_helpers.filter_task_members(task_members)
+
+ if not domain_members:
+ raise NoContentsFoundForOperationError(
+ "Failed to retrieve Memory Domain Resources, please check if persistent memory is present/configured"
+ )
+
+ # Dict with input config data
+ config_data = {
+ "size": options.memorymode,
+ "pmeminterleave": options.interleave,
+ "proc": options.proc,
+ }
+ # Check for given processor id list is valid or not.
+ if options.proc:
+ config_data["proc"] = self.get_valid_processor_list(config_data["proc"], domain_members)
+ memory_chunks = self.filter_memory_chunks(memory_chunks, domain_members, config_data["proc"])
+
+ # In case of 100% Volatile, Interleaving memory regions is not applicable.
+ if (
+ config_data["size"] == 100
+ and config_data["pmeminterleave"]
+ and config_data["pmeminterleave"].lower() == "on"
+ ):
+ raise InvalidCommandLineError(
+ "Selected configuration is invalid. " "Interleaving not supported in 100% volatile."
+ )
+
+ if options.force:
+ self.delete_existing_chunks_and_tasks(memory_chunk_tasks, memory_chunks)
+ else:
+ self.warn_existing_chunks_and_tasks(self, memory_chunk_tasks, memory_chunks)
+
+ for member in domain_members:
+ proc_id = member["Id"]
+ # If given proc list is not empty, applies configuration to selected processors
+ if not config_data["proc"] or (proc_id in config_data["proc"]):
+ path = member["MemoryChunks"].get("@odata.id")
+ data = self.get_post_data(config_data, member["InterleavableMemorySets"])
+ for body in data:
+ resp = self._rest_helpers.post_resource(path, body)
+ if resp is None:
+ raise NoChangesFoundOrMadeError("Error occurred while applying configuration")
+
+ # Display warning
+ self.rdmc.ui.printer("\nConfiguration changes require reboot to take effect.\n")
+
+ # Display pending configuration
+ self.auxcommands["showpmmpendingconfig"].show_pending_config(type("MyOptions", (object,), dict(json=False)))
+ return None
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ApplyPmemConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ApplyPmemConfigCommand.py
new file mode 100644
index 0000000..fd7ba0a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ApplyPmemConfigCommand.py
@@ -0,0 +1,357 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to apply a pre-defined configuration to PMM"""
+from __future__ import absolute_import
+
+from copy import deepcopy
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ LOGGER,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ )
+
+from .lib.DisplayHelpers import DisplayHelpers
+from .lib.RestHelpers import RestHelpers
+
+
+class ApplyPmemConfigCommand:
+ """
+ Command to apply a pre-defined configuration to PMM
+ """
+
+ # Available configIDs
+ config_ids = [
+ {
+ "name": "MemoryMode",
+ "description": "Configure all the PMMs to 100% Memory Mode.",
+ "totalPmemPercentage": 0,
+ "pmemIntereleaved": False,
+ },
+ {
+ "name": "PmemInterleaved",
+ "description": "Configure all PMMs to 100% Persistent. "
+ "Interleave the Persistent memory regions within each processor.",
+ "totalPmemPercentage": 100,
+ "pmemIntereleaved": True,
+ },
+ {
+ "name": "PmemNotInterleaved",
+ "description": "Configure all PMMs to 100% Persistent. "
+ "The Persistent memory regions are not interleaved.",
+ "totalPmemPercentage": 100,
+ "pmemIntereleaved": False,
+ },
+ ]
+
+ def __init__(self):
+ self.ident = {
+ "name": "applypmmconfig",
+ "usage": None,
+ "description": "Applies a pre-defined configuration to PMM\n"
+ "\texample: applypmmconfig --pmmconfig MemoryMode\n",
+ "summary": "Applies a pre-defined configuration to PMM.",
+ "aliases": [],
+ "auxcommands": [
+ "ShowPmemPendingConfigCommand",
+ "ClearPendingConfigCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ self._display_helpers = DisplayHelpers()
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-C",
+ "--pmmconfig",
+ action="store",
+ type=str,
+ dest="config",
+ help="Specify one of the pre-defined configIDs to apply" " to all Persistent Memory Modules.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "-L",
+ "--list",
+ action="store_true",
+ dest="list",
+ help="Display a list of available pre-defined configIDs" " along with a brief description.",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ dest="force",
+ help="Allow the user to force the configuration " "by automatically accepting any prompts.",
+ default=False,
+ )
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("PMM Apply Pre-Defined Configuration: %s", self.ident["name"])
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+ if args:
+ self.validate_args(options)
+ self.validate_options(options)
+ self.apply_pmm_config(options)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ @staticmethod
+ def validate_args(options):
+ """
+ Produces relevant error messages when unwanted extra arguments are specified with flags.
+ """
+ if options.config:
+ raise InvalidCommandLineError("'config | -C' expects a single argument")
+ else:
+ raise InvalidCommandLineError("Chosen flag doesn't expect additional arguments")
+
+ def validate_options(self, options):
+ """
+ Validate whether options specified by user are valid.
+ :param options: options specified by the user.
+ :type options: instance of OptionParser class.
+ """
+ # Set of valid configIDs
+ valid_config_ids = {config_id.get("name").lower() for config_id in self.config_ids}
+
+ if not options.config and not options.list and not options.force:
+ raise InvalidCommandLineError("No flag specified.\n\nUsage: " + self.ident["usage"])
+ if not options.config and options.force:
+ raise InvalidCommandLineError("'--force | -f' flag mandatorily requires the " "'--pmmconfig | -C' flag.")
+ if options.config and options.list:
+ raise InvalidCommandLineError("Only one of '--pmmconfig | -C' or '--list | -L' " "may be specified")
+ # Check whether the user entered configID is valid
+ if options.config and not options.config.lower() in valid_config_ids:
+ raise InvalidCommandLineError(
+ "Invalid configID.\nTo view a list of pre-defined " "configIDs, use 'applypmmconfig --list'"
+ )
+
+ def apply_pmm_config(self, options):
+ """
+ Calls relevant functions based on the flags specified by the user.
+ :param options: options specified by the user.
+ :type options: instance of OptionParser class.
+ """
+ if options.list:
+ self.show_predefined_config_options()
+ elif options.config:
+ # Raise exception if server is in POST
+ if RestHelpers(rdmcObject=self.rdmc).in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ self.apply_predefined_config(options)
+
+ def show_predefined_config_options(self):
+ """
+ Display the available pre-defined configIDs that the user can choose from to
+ apply to their Persistent memory Modules.
+ """
+ self.rdmc.ui.printer("\nAvailable Configurations:\n\n")
+ for config_id in ApplyPmemConfigCommand.config_ids:
+ self.rdmc.ui.printer(config_id.get("name") + "\n")
+ self.rdmc.ui.printer("\t" + config_id.get("description") + "\n")
+ self.rdmc.ui.printer("\n")
+
+ def warn_existing_chunks_and_tasks(self, memory_chunk_tasks, memory_chunks):
+ """
+ Checks for existing Memory Chunks and Pending Configuration Task resources on
+ a server where a user is trying to apply a pre-defined configuration
+ :param memory_chunk_tasks: Pending Configuration Tasks
+ :type memory_chunk_tasks: list
+ :memory_chunks: Memory Chunks in the existing configuration
+ :type memory_chunks: list
+ :returns: None
+ """
+ # If Memory Chunks exist, display Existing configuration warning
+ if memory_chunks:
+ self.rdmc.ui.printer(
+ "Warning: Existing configuration found. Proceeding with applying a new "
+ "configuration will result in overwriting the current configuration and "
+ "cause data loss.\n"
+ )
+ # If Pending Configuration Tasks exist, display warning
+ if memory_chunk_tasks:
+ self.rdmc.ui.printer(
+ "Warning: Pending configuration tasks found. Proceeding with applying "
+ "a new configuration will result in overwriting the pending "
+ "configuration tasks.\n"
+ )
+ # Raise a NoChangesFoundOrMade exception when either of the above conditions exist
+ if memory_chunks or memory_chunk_tasks:
+ # Line feed for proper formatting
+ raise NoChangesFoundOrMadeError(
+ "\nFound one or more of Existing Configuration or "
+ "Pending Configuration Tasks. Please use the "
+ "'--force | -f' flag with the same command to "
+ "approve these changes."
+ )
+ return None
+
+ def delete_existing_chunks_and_tasks(self, memory_chunk_tasks, memory_chunks):
+ """
+ Delete existing Memory Chunks and Pending Configuration Tasks
+ :param memory_chunk_tasks: Pending Configuration Tasks
+ :type memory_chunk_tasks: list
+ :memory_chunks: Memory Chunks in the existing configuration
+ :type memory_chunks: list
+ :returns: None
+ """
+ # Delete any pending configuration tasks
+ if memory_chunk_tasks:
+ self.auxcommands["clearpmmpendingconfig"].delete_tasks(memory_chunk_tasks)
+ # Delete any existing configuration
+ if memory_chunks:
+ for chunk in memory_chunks:
+ data_id = chunk.get("@odata.id")
+ resp = RestHelpers(rdmcObject=self.rdmc).delete_resource(data_id)
+ if not resp:
+ raise NoChangesFoundOrMadeError("Error occured while deleting " "existing configuration")
+ return None
+
+ @staticmethod
+ def get_post_data(config_data, interleavable_memory_sets):
+ """
+ Create post body based on config data
+ :param config_data: An entry of self.config_iD's list
+ :type config_data: dict
+ :param interleavable_memory_sets: List of interleavable sets
+ :type interleavable_memory_sets: List
+ :returns: Returns post data
+ """
+
+ body = {
+ "AddressRangeType": "PMEM",
+ "InterleaveSets": [],
+ "Oem": {"Hpe": {"MemoryChunkSizePercentage": config_data["totalPmemPercentage"]}},
+ }
+ # Get the list of interleave sets based on the configuration
+ if config_data["pmemIntereleaved"] or config_data["totalPmemPercentage"] == 0:
+ # If pmem regions are interleaved or if it MemoryMode, choose the list with maximum entries.
+ interleave_sets = [max(interleavable_memory_sets, key=lambda x: len(x["MemorySet"]))]
+ else:
+ # If pmem regions are NOT interleaved, choose all the lists with exactly one entry.
+ interleave_sets = [il_set for il_set in interleavable_memory_sets if len(il_set["MemorySet"]) == 1]
+
+ # Using in-place update to change the interleave sets format.
+ # Replace 'MemorySet' with 'Memory' for each MemorySet in interleave_sets.
+ # Get '@odata.id' for each memory region from original interleave_sets and add this to new structure.
+ for index, interleavableset in enumerate(interleave_sets):
+ interleave_sets[index] = [
+ {"Memory": {"@odata.id": str(memory_region["@odata.id"])}}
+ for memory_region in interleavableset["MemorySet"]
+ ]
+
+ # Create post body for each interleave sets from post body template.
+ post_data = []
+ for interleaveset in interleave_sets:
+ current_body = deepcopy(body)
+ current_body["InterleaveSets"] = interleaveset
+ post_data.append(current_body)
+
+ return post_data
+
+ def apply_predefined_config(self, options):
+ """
+ Apply the selected pre-defined configuration to Persistent Memory Modules.
+ :param options: option specified by the user.
+ :returns: None
+ """
+ # Retrieve Memory Chunks and Task Resources from server
+ (task_members, domain_members, memory_chunks) = RestHelpers(
+ rdmcObject=self.rdmc
+ ).retrieve_task_members_and_mem_domains()
+
+ # Filter Task Resources to include only Pending Configuration Tasks
+ memory_chunk_tasks = RestHelpers(rdmcObject=self.rdmc).filter_task_members(task_members)
+
+ if options.force:
+ self.delete_existing_chunks_and_tasks(memory_chunk_tasks, memory_chunks)
+ else:
+ self.warn_existing_chunks_and_tasks(memory_chunk_tasks, memory_chunks)
+
+ if not domain_members:
+ raise NoContentsFoundForOperationError("Failed to retrive Memory Domain Resources")
+
+ # Get the user specified configID
+ config_data = next(
+ (config_id for config_id in self.config_ids if config_id.get("name").lower() == options.config.lower()),
+ None,
+ )
+
+ for proc in domain_members:
+ path = proc["MemoryChunks"].get("@odata.id")
+ data = self.get_post_data(config_data, proc["InterleavableMemorySets"])
+ for body in data:
+ resp = RestHelpers(rdmcObject=self.rdmc).post_resource(path, body)
+ if resp is None:
+ raise NoChangesFoundOrMadeError("Error occured while applying configuration")
+
+ # display warning
+ self.rdmc.ui.printer("Configuration changes require reboot to take effect.\n")
+
+ # display pending configuration
+ self.auxcommands["showpmmpendingconfig"].show_pending_config(type("MyOptions", (object,), dict(json=False)))
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ClearPendingConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ClearPendingConfigCommand.py
new file mode 100644
index 0000000..0635343
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ClearPendingConfigCommand.py
@@ -0,0 +1,134 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to clear pending tasks"""
+from __future__ import absolute_import
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ NoChangesFoundOrMadeError,
+ LOGGER,
+ )
+
+from .lib.RestHelpers import RestHelpers
+
+
+class ClearPendingConfigCommand:
+ """Command to clear pending config tasks"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "clearpmmpendingconfig",
+ "usage": None,
+ "description": "Clear pmm pending config tasks\n" "\texample: clearpmmpendingconfig",
+ "summary": "Clear pending config tasks",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def get_memory_chunk_tasks(self):
+ """
+ Function to retrieve Memory Chunk Tasks
+ :returns: Retrieved Memory Chunk Tasks
+ """
+ # Retrieving tasks members
+ tasks = RestHelpers(rdmcObject=self.rdmc).retrieve_task_members()
+ if tasks:
+ # Filtering out Memory Chunk Tasks
+ memory_chunk_tasks = RestHelpers(rdmcObject=self.rdmc).filter_task_members(tasks)
+ if memory_chunk_tasks:
+ return memory_chunk_tasks
+ self.rdmc.ui.printer("No pending configuration tasks found.\n\n")
+ return []
+
+ def delete_tasks(self, memory_chunk_tasks, verbose=False):
+ """
+ Function to delete pending configuration tasks
+ :param memory_chunk_tasks: Pending confguration tasks.
+ :type memory_chunk_tasks: list
+ :param verbose: Toggles verbose mode, which print task IDs as
+ individual tasks are deleted.
+ :type verbose: Boolean
+ :returns: None
+ """
+ for task in memory_chunk_tasks:
+ data_id = task.get("@odata.id")
+ task_id = task.get("Id")
+ resp = RestHelpers(rdmcObject=self.rdmc).delete_resource(data_id)
+ if resp:
+ if verbose:
+ self.rdmc.ui.printer("Deleted Task #{}".format(task_id) + "\n")
+ else:
+ raise NoChangesFoundOrMadeError("Error occured while deleting " "task #{}".format(task_id))
+ return None
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("Clear Pending Configuration: %s", self.ident["name"])
+ # pylint: disable=unused-variable
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+ if args:
+ raise InvalidCommandLineError("Chosen flag doesn't expect additional arguments")
+ # Raise exception if server is in POST
+ if RestHelpers(rdmcObject=self.rdmc).in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ memory_chunk_tasks = self.get_memory_chunk_tasks()
+ self.delete_tasks(memory_chunk_tasks, verbose=True)
+
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/DisplaySecurityStateCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/DisplaySecurityStateCommand.py
new file mode 100644
index 0000000..f3c28b4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/DisplaySecurityStateCommand.py
@@ -0,0 +1,123 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+
+"""Command to Display Security State of Dimms"""
+
+# ---------Imports---------
+from __future__ import absolute_import
+
+from tabulate import tabulate
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ )
+
+from .lib.RestHelpers import RestHelpers
+
+# ---------End of imports---------
+
+
+class DisplaySecurityStateCommand:
+ """Command to Display Security State of Dimms"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "pmmsecuritystate",
+ "usage": None,
+ "description": "Displaying the Security state of dimms\n\texample: pmmsecuritystate",
+ "summary": "Displaying the Security state of dimms.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self._rest_helpers = None
+
+ def display_SecurityState(self):
+ resp = self._rest_helpers.retrieve_pmem_location()
+
+ pmemLocation = []
+ for item in resp["Members"]:
+ if item["MemoryType"] == "IntelOptane":
+ pmemLocation.append(item["Name"])
+
+ securityState = []
+ headers = ["Location", "State"]
+ for i in pmemLocation:
+ path = "/redfish/v1/Systems/1/Memory/" + i
+ resp = self._rest_helpers.retrieve_security_state(path)
+ state = resp["SecurityState"]
+ securityState.append(state)
+
+ merged_list = tuple(zip(pmemLocation, securityState))
+ print(tabulate(merged_list, headers, tablefmt="psql"))
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+ if args:
+ raise InvalidCommandLineError("Chosen flag doesn't expect additional arguments")
+
+ # Raise exception if server is in POST
+ self._rest_helpers = RestHelpers(rdmcObject=self.rdmc)
+ if self._rest_helpers.in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+
+ self.display_SecurityState()
+
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for smbios command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemCommand.py
new file mode 100644
index 0000000..e48cc83
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemCommand.py
@@ -0,0 +1,455 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to display information about Persistent Memory modules"""
+from __future__ import absolute_import, division
+
+import re
+from argparse import Action
+from enum import Enum
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ LOGGER,
+ )
+
+from .lib.DisplayHelpers import DisplayHelpers, OutputFormats
+from .lib.Mapper import Mapper
+from .lib.MapperRenderers import MappingTable
+from .lib.PmemHelpers import PmemHelpers
+from .lib.RestHelpers import RestHelpers
+
+
+class _Parse_Options_List(Action):
+ def __init__(self, option_strings, dest, nargs, **kwargs):
+ super(_Parse_Options_List, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_strings):
+ """
+ Callback to parse a comma-separated list into an array.
+ Then store the array in the option's destination
+ """
+ try:
+ setattr(namespace, self.dest, next(iter(values)).split(","))
+ except:
+ raise InvalidCommandLineError("Values in a list must be separated by a comma.")
+
+
+class DefaultAttributes(Enum):
+ """
+ Enum class containing default display attributes for various flags
+ """
+
+ device = ["Location", "Capacity", "Status", "DIMMStatus", "Life", "FWVersion"]
+ config = ["Location", "VolatileSize", "PmemSize", "PmemInterleaved"]
+ summary = ["TotalCapacity", "TotalVolatileSize", "TotalPmemSize"]
+ logical = ["PmemSize", "DimmIds"]
+
+
+def get_default_attributes(flag):
+ """
+ This method returns default attributes to be displayed
+ :param flag: name of flag
+ :type flag: string
+ :return: list of default attributes for the passed flag
+ """
+ if flag == "device":
+ return list(DefaultAttributes.device.value)
+ if flag == "config":
+ return list(DefaultAttributes.config.value)
+ if flag == "logical":
+ return list(DefaultAttributes.logical.value)
+ if flag == "summary":
+ return list(DefaultAttributes.summary.value)
+ return []
+
+
+class ShowPmemCommand:
+ """Command to display information about Persistent Memory modules"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "showpmm",
+ "usage": None,
+ "description": "Display information about " "Persistent Memory modules \n\texample: showpmm --device",
+ "summary": "Display information about Persistent Memory modules.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self._display_helpers = DisplayHelpers()
+ self._mapper = Mapper()
+ self._pmem_helpers = PmemHelpers()
+
+ def show_pmem_modules(self, options):
+ """
+ Command to display information about Persistent Memory modules
+ :param options: command options
+ :type options: options.
+ """
+ # Retrieving memory collection resources
+ if options.logical or options.config:
+ memory, domain_members, all_chunks = RestHelpers(rdmcObject=self.rdmc).retrieve_mem_and_mem_domains()
+ if not domain_members:
+ raise NoContentsFoundForOperationError("Failed to retrieve Memory " "Domain resources")
+ else:
+ memory = RestHelpers(rdmcObject=self.rdmc).retrieve_memory_resources()
+
+ if memory:
+ memory_members = memory.get("Members")
+ else:
+ raise NoContentsFoundForOperationError("Failed to retrieve Memory Resources")
+
+ # Get dimm ids of all memory objects
+ member_dimm_ids = set()
+ for member in memory_members:
+ member_dimm_ids.add(member.get("DeviceLocator"))
+
+ # Filtering Persistent Memory members
+ pmem_members, pmem_dimm_ids = self._pmem_helpers.get_pmem_members(memory_members)
+ if not pmem_members:
+ raise NoContentsFoundForOperationError("No Persistent Memory Modules found")
+
+ parsed_dimm_ids = list()
+ if options.dimm:
+ parsed_dimm_ids = self._pmem_helpers.parse_dimm_id(options.dimm)
+
+ for dimm_id in parsed_dimm_ids:
+ if dimm_id not in member_dimm_ids:
+ raise InvalidCommandLineError("One or more of the specified " "DIMM ID(s) are invalid")
+ elif dimm_id not in pmem_dimm_ids:
+ raise InvalidCommandLineError(
+ "One or more of the specified DIMM ID(s) " "are not Persistent Memory Modules"
+ )
+
+ # Creating a list of persistent memory members according to specified DIMM Ids
+ selected_pmem_members = list()
+ if parsed_dimm_ids:
+ for pmem_member in pmem_members:
+ if pmem_member.get("DeviceLocator") in parsed_dimm_ids:
+ selected_pmem_members.append(pmem_member)
+ else:
+ selected_pmem_members = pmem_members
+
+ # Call 'show_pmem_module_device()' when either the user specifies '--device' flag
+ # or specifies no flag at all
+ if (
+ not options.device and not options.config and not options.logical and not options.summary
+ ) or options.device:
+ self.show_pmem_module_device(selected_pmem_members, options)
+
+ elif options.config:
+ self.show_pmem_module_configuration(selected_pmem_members, all_chunks, options)
+
+ elif options.summary:
+ self.show_pmem_module_summary(selected_pmem_members, options)
+
+ elif options.logical:
+ if not all_chunks:
+ self.rdmc.ui.printer("No Persistent Memory regions found\n\n")
+ return
+ self.show_persistent_interleave_sets(selected_pmem_members, all_chunks, options)
+
+ def generate_display_output(self, members, options, flag, mapping_table, **resources):
+ """
+ :param members: list of members returned as a result of GET request
+ :type members: list of dictionaries
+ :param options: command options
+ :type options: options
+ :param mapping_table: Mapping table to be used to extract attributes
+ :param flag: name of flag
+ :type flag: string
+ :return: filtered list of dictionaries to be sent to Display Helpers for output
+ """
+ display_output_list = list()
+ # use default attributes for now
+ display_attributes = get_default_attributes(flag)
+
+ for member in members:
+ temp_dict = self._mapper.get_multiple_attributes(
+ member, display_attributes, mapping_table, output_as_json=options.json, **resources
+ )
+ display_output_list.append(temp_dict)
+ return display_output_list
+
+ def show_pmem_module_device(self, selected_pmem_members, options):
+ """
+ Command to display information about DIMMs when the
+ '--device' | '-D' flag is specified
+ :param selected_pmem_members: pmem members to be displayed
+ :type selected_pmem_members: list
+ :param options: command options
+ :type options: options
+ """
+ # generating the data to be printed
+ display_output = self.generate_display_output(
+ selected_pmem_members, options, "device", MappingTable.device.value
+ )
+ # Displaying output based on --json flag
+ if options.json:
+ self._display_helpers.display_data(display_output, OutputFormats.json)
+ else:
+ self._display_helpers.display_data(display_output, OutputFormats.table)
+
+ def show_pmem_module_configuration(self, selected_pmem_members, all_chunks, options=None):
+ """
+ Command to display information about DIMMs when the
+ '--pmmconfig' | '-C' flag is specified
+ :param selected_pmem_members: pmem members to be displayed
+ :type selected_pmem_members: list
+ :param all_chunks: list of memory chunks
+ :type all_chunks: list
+ :param options: command options
+ :type options: options
+ """
+ # generating the data to be printed
+ display_output = self.generate_display_output(
+ selected_pmem_members,
+ options,
+ "config",
+ MappingTable.config.value,
+ chunks=all_chunks,
+ )
+ # Displaying output based on --json flag
+ if options.json:
+ self._display_helpers.display_data(display_output, OutputFormats.json)
+ else:
+ self._display_helpers.display_data(display_output, OutputFormats.table)
+
+ def show_persistent_interleave_sets(self, selected_pmem_members, all_chunks, options=None):
+ """
+ Command to display information about the Persistent interleave
+ regions among the Persistent Memory Modules when the '--logical' | '-L' flag is
+ specified
+ :param selected_pmem_members: list of memory members retrieved via GET request
+ :type selected_pmem_members: list
+ :param all_chunks: list of memory chunks
+ :type all_chunks: list
+ :param options: command options
+ :type options: options
+ """
+ # generating the data to be printed
+ display_output = self.generate_display_output(
+ all_chunks,
+ options,
+ "logical",
+ MappingTable.logical.value,
+ memory=selected_pmem_members,
+ )
+ # Displaying output based on --json flag
+ if options.json:
+ self._display_helpers.display_data(display_output, OutputFormats.json)
+ else:
+ self.rdmc.ui.printer("\nInterleave Persistent Memory regions\n")
+ self._display_helpers.display_data(display_output, OutputFormats.table)
+
+ def show_pmem_module_summary(self, selected_pmem_members, options=None):
+ """
+ Command to display a summary of the current Persistent Memory
+ configuration when the '--summary' | '-M' flag is specified
+ :param selected_pmem_members: list of memory members retrieved via GET request
+ :type selected_pmem_members: list
+ :param options: command options
+ :type options: options
+ """
+ # getting default attributes for --summary flag
+ attribute_list = get_default_attributes("summary")
+ # generating the data to be printed
+ display_output = self._mapper.get_multiple_attributes(
+ selected_pmem_members,
+ attribute_list,
+ MappingTable.summary.value,
+ options.json,
+ )
+ # Displaying output based on --json flag
+ if options.json:
+ self._display_helpers.display_data(display_output, OutputFormats.json)
+ else:
+ self._display_helpers.print_properties([display_output])
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("PMM: %s", self.ident["name"])
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+
+ self.rdmc.login_select_validation(self, options)
+
+ if args:
+ self.validate_args(options)
+ self.validate_show_pmem_options(options)
+ # Raise exception if server is in POST
+ if RestHelpers(rdmcObject=self.rdmc).in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ self.show_pmem_modules(options)
+
+ return ReturnCodes.SUCCESS
+
+ @staticmethod
+ def validate_args(options):
+ """
+ Produces relevant error messages when unwanted extra arguments are specified with flags
+ """
+ some_flag = options.device or options.config or options.logical or options.summary
+ if options.logical or options.summary:
+ raise InvalidCommandLineError("Chosen flag doesn't expect additional arguments")
+ elif (options.device or options.config or not some_flag) and not options.dimm:
+ raise InvalidCommandLineError("Use the '--dimm | -I' flag to filter by DIMM IDs")
+ elif (options.device or options.config or not some_flag) and options.dimm:
+ raise InvalidCommandLineError("Values in a list must be comma-separated " "(no spaces)")
+
+ @staticmethod
+ def validate_show_pmem_options(options):
+ """
+ Validate whether options specified by user are valid
+ :param options: options specified by the user with the 'showpmm' command
+ :type options: instance of OptionParser class
+ """
+ # Usage/Error strings
+ usage_multiple_flags = (
+ "Only one of '--device | -D', '--pmmconfig | -C', " "'--logical | -L' or '--summary | -M' may be specified"
+ )
+ # usage_all_display = "Only one of '--all | -a' or '--display | -d' may be specified\n"
+ usage_dimm_flag = (
+ "'--dimm | -I' can only be specified with either the "
+ "'--device | -D' or '--pmmconfig | -C' flag\n"
+ " or without any flag"
+ )
+ error_dimm_format = "DIMM IDs should be of the form 'ProcessorNumber@SlotNumber'"
+ error_dimm_range = "One or more of the specified DIMM ID(s) are invalid"
+
+ views = [options.device, options.config, options.logical, options.summary]
+
+ if (options.logical or options.summary) and options.dimm:
+ raise InvalidCommandLineError(usage_dimm_flag)
+
+ if views.count(False) < 3:
+ raise InvalidCommandLineError(usage_multiple_flags)
+
+ if options.dimm:
+ for dimm_id in options.dimm:
+ # Regex match test for expressions of type 'num1@num2'
+ if not re.match(r"^\d+\@\d+$", dimm_id):
+ raise InvalidCommandLineError(error_dimm_format)
+ # Accepts expressions from '1@1' to '9@19'
+ if not re.match(r"^[1-9]\@([1-9]|[1]\d)$", dimm_id):
+ raise InvalidCommandLineError(error_dimm_range)
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ action="store_true",
+ dest="json",
+ help="Optionally include this flag to change the output to JSON format.",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "-D",
+ "--device",
+ action="store_true",
+ default=False,
+ dest="device",
+ help="Show information about the physical persistent memory modules."
+ " Default view shows information about all PMMs."
+ " To filter DIMMs, use this flag in conjunction with"
+ " the --dimm flag.",
+ )
+
+ customparser.add_argument(
+ "-C",
+ "--pmmconfig",
+ action="store_true",
+ default=False,
+ dest="config",
+ help="Show the current configuration of the persistent memory modules."
+ " Default view shows information about all PMMs."
+ " To filter DIMMs, use this flag in conjunction with"
+ " the --dimm flag.",
+ )
+
+ customparser.add_argument(
+ "-L",
+ "--logical",
+ action="store_true",
+ default=False,
+ dest="logical",
+ help="Show the Persistent Memory Regions.",
+ )
+
+ customparser.add_argument(
+ "-M",
+ "--summary",
+ action="store_true",
+ default=False,
+ dest="summary",
+ help="Show the summary of the persistent memory resources.",
+ )
+
+ customparser.add_argument(
+ "-I",
+ "--dimm",
+ type=str,
+ action=_Parse_Options_List,
+ metavar="IDLIST",
+ nargs=1,
+ dest="dimm",
+ help=" To view specific devices, supply a comma-separated list"
+ " of DIMM IDs in the format P@S (without spaces),"
+ " where P=processor and S=slot. Example: '1@1,1@12'",
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemPendingConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemPendingConfigCommand.py
new file mode 100644
index 0000000..a56251a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowPmemPendingConfigCommand.py
@@ -0,0 +1,170 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to show the pending configuration for PMM"""
+
+from __future__ import absolute_import
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ LOGGER,
+ NoContentsFoundForOperationError,
+ )
+
+from .lib.DisplayHelpers import DisplayHelpers, OutputFormats
+from .lib.Mapper import Mapper
+from .lib.MapperRenderers import MappingTable
+from .lib.PmemHelpers import PmemHelpers
+from .lib.RestHelpers import RestHelpers
+
+
+class ShowPmemPendingConfigCommand:
+ """
+ Command to show the pending configuration for PMM
+ """
+
+ def __init__(self):
+ self.ident = {
+ "name": "showpmmpendingconfig",
+ "usage": None,
+ "description": "Shows the pending tasks for configuring PMM\n" "\texample: showpmmpendingconfig --json",
+ "summary": "Shows the pending configuration for PMM.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self._mapper = Mapper()
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self._display_helpers = DisplayHelpers()
+ self._pmem_helpers = PmemHelpers()
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ action="store_true",
+ dest="json",
+ help="Optionally include this flag to change the output to JSON format.",
+ default=False,
+ )
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("PMM Pending Configuration: %s", self.ident["name"])
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+ if args:
+ raise InvalidCommandLineError("Chosen command or flag doesn't expect additional arguments")
+ # Raise exception if server is in POST
+ if RestHelpers(rdmcObject=self.rdmc).in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ self.show_pending_config(options)
+
+ return ReturnCodes.SUCCESS
+
+ def show_pending_config(self, options):
+ """
+ Command to display information about pending Persistent Memory Configuration
+ :param options: command options
+ :type options: options.
+ """
+ # retrieving task members
+ task_members = RestHelpers(rdmcObject=self.rdmc).retrieve_task_members()
+
+ # filtering task members
+ filtered_task_members = RestHelpers(rdmcObject=self.rdmc).filter_task_members(task_members)
+ if not filtered_task_members:
+ self.rdmc.ui.printer("No pending configuration tasks found.\n\n")
+ return None
+
+ # retrieving memory members
+ memory = RestHelpers(rdmcObject=self.rdmc).retrieve_memory_resources()
+ if memory:
+ memory_members = memory.get("Members")
+ else:
+ raise NoContentsFoundForOperationError("Failed to retrieve Memory Resources")
+
+ attributes = ["Operation", "PmemSize", "VolatileSize", "DimmIds"]
+ display_output = list()
+ for task in filtered_task_members:
+ # finding operation of task
+ operation = self._mapper.get_single_attribute(task, "Operation", MappingTable.tasks.value, True)
+ # displaying existing configuration for DELETE operation
+ if operation.get("Operation", "") == "DELETE":
+ target_uri = task.get("Payload").get("TargetUri")
+ data = RestHelpers(rdmcObject=self.rdmc).get_resource(target_uri)
+ table = MappingTable.delete_task.value
+ else:
+ task_type = self._mapper.get_single_attribute(task, "Type", MappingTable.tasks.value, True)
+ task_type = task_type.get("Type", "")
+ if task_type != "PMEM":
+ self.rdmc.ui.warn("Unsupported interleave set type found: " + task_type)
+ continue
+ data = task
+ table = MappingTable.tasks.value
+
+ task_output = self._mapper.get_multiple_attributes(
+ data,
+ attributes,
+ table,
+ output_as_json=options.json,
+ memory=memory_members,
+ )
+ display_output.append(task_output)
+
+ if options.json:
+ self._display_helpers.display_data(display_output, OutputFormats.json)
+ else:
+ self._display_helpers.display_data(display_output, OutputFormats.table)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowRecommendedConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowRecommendedConfigCommand.py
new file mode 100644
index 0000000..c634618
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/ShowRecommendedConfigCommand.py
@@ -0,0 +1,193 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Command to show recommended configurations"""
+from __future__ import absolute_import, division
+
+from collections import OrderedDict
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ LOGGER,
+ )
+
+from .lib.DisplayHelpers import DisplayHelpers, OutputFormats
+from .lib.Mapper import Mapper
+from .lib.MapperRenderers import MappingTable
+from .lib.PmemHelpers import PmemHelpers
+from .lib.RestHelpers import RestHelpers
+
+
+class ShowRecommendedConfigCommand:
+ """Show recommended configurations"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "showrecommendedpmmconfig",
+ "usage": None,
+ "description": "Show recommended configurations\n" "\texample: showrecommendedpmmconfig",
+ "summary": "Show Recommended Configuration",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self._display_helpers = DisplayHelpers()
+ self._mapper = Mapper()
+ self._pmem_helpers = PmemHelpers()
+
+ def definearguments(self, customparser):
+ """
+ Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ action="store_true",
+ dest="json",
+ help="Optionally include this flag to change the output to JSON format.",
+ default=False,
+ )
+
+ def run(self, line, help_disp=False):
+ """
+ Wrapper function for new command main function
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ LOGGER.info("Show Recommended Configuration: %s", self.ident["name"])
+ try:
+ (_, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineError("Failed to parse options")
+ if args:
+ raise InvalidCommandLineError("Chosen command doesn't expect additional arguments")
+ # Raise exception if server is in POST
+ if RestHelpers(rdmcObject=self.rdmc).in_post():
+ raise NoContentsFoundForOperationError(
+ "Unable to retrieve resources - " "server might be in POST or powered off"
+ )
+ self.show_recommended_config()
+
+ return ReturnCodes.SUCCESS
+
+ def show_recommended_config(self):
+ """
+ Show recommended pmm configuration
+ """
+ members = RestHelpers(rdmcObject=self.rdmc).retrieve_memory_resources().get("Members")
+
+ if not members:
+ raise NoContentsFoundForOperationError("Failed to retrieve memory resources")
+
+ # Retreving dimms
+ pmem_members = self._pmem_helpers.get_pmem_members(members)[0]
+
+ if not pmem_members:
+ raise NoContentsFoundForOperationError("No Persistent Memory Modules found")
+
+ # Retreving dram dimms
+ dram_members = self._pmem_helpers.get_non_aep_members(members)[0]
+
+ if not dram_members:
+ raise NoContentsFoundForOperationError("No DRAM DIMMs found")
+
+ # retrieving Total Capacity of PMEM dimms
+ attr = self._mapper.get_single_attribute(pmem_members, "TotalCapacity", MappingTable.summary.value, True)
+ pmem_size = attr.get("TotalCapacity", {}).get("Value", 0)
+
+ # retrieving Total Capacity of DRAM dimms
+ dram_size = self._mapper.get_single_attribute(dram_members, "TotalCapacity", MappingTable.summary.value, True)
+ dram_size = dram_size.get("TotalCapacity", {}).get("Value", 0)
+
+ display_output = list()
+ recommended_config = list()
+
+ # Add AppDirect Mode
+ temp_dict = OrderedDict()
+ temp_dict["MemoryModeTotalSize"] = 0
+ temp_dict["PmemTotalSize"] = pmem_size
+ temp_dict["CacheRatio"] = None
+ recommended_config.append(temp_dict)
+
+ # Add Memory Mode
+ temp_dict = OrderedDict()
+ temp_dict["MemoryModeTotalSize"] = pmem_size
+ temp_dict["PmemTotalSize"] = 0
+ temp_dict["CacheRatio"] = pmem_size / dram_size
+ recommended_config.append(temp_dict)
+
+ # Add Mixed Mode (BPS doesn't support it)
+ if "Gen10 Plus" not in RestHelpers(rdmcObject=self.rdmc).retrieve_model(self.rdmc)["Model"]:
+ stepsize = 32
+ appdirect_size = 0
+ step = 1
+ while appdirect_size < pmem_size:
+ appdirect_size = step * stepsize * len(pmem_members)
+ memorymode_size = pmem_size - appdirect_size
+ cache_ratio = memorymode_size / dram_size
+ if 2 <= cache_ratio <= 16:
+ temp_dict = OrderedDict()
+ temp_dict["MemoryModeTotalSize"] = memorymode_size
+ temp_dict["PmemTotalSize"] = appdirect_size
+ temp_dict["CacheRatio"] = cache_ratio
+ recommended_config.append(temp_dict)
+ step += 1
+
+ # Sorting based on MemoryModeTotalSize
+ recommended_config = sorted(recommended_config, key=lambda x: x["MemoryModeTotalSize"])
+
+ # Adding units and formating cache ratio
+ for output in recommended_config:
+ output["MemoryModeTotalSize"] = "%d GB" % output["MemoryModeTotalSize"]
+ output["PmemTotalSize"] = "%d GB" % output["PmemTotalSize"]
+ if output["CacheRatio"] is None:
+ output["CacheRatio"] = "N/A"
+ else:
+ output["CacheRatio"] = "1:%.1f" % output["CacheRatio"]
+ display_output.append(self._pmem_helpers.json_to_text(output)[0])
+
+ # Display output in table format
+ self._display_helpers.display_data(display_output, OutputFormats.table)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/AdvancedPmmConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/AdvancedPmmConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..bd2ea67
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/AdvancedPmmConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ApplyPmemConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ApplyPmemConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..e3b33ae
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ApplyPmemConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ClearPendingConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ClearPendingConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..dc44801
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ClearPendingConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/DisplaySecurityStateCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/DisplaySecurityStateCommand.cpython-310.pyc
new file mode 100644
index 0000000..7bbc296
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/DisplaySecurityStateCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemCommand.cpython-310.pyc
new file mode 100644
index 0000000..61ef44b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemPendingConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemPendingConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..52362f6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowPmemPendingConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowRecommendedConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowRecommendedConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..dc6141e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/ShowRecommendedConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..46b366b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/DisplayHelpers.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/DisplayHelpers.py
new file mode 100644
index 0000000..8649a9c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/DisplayHelpers.py
@@ -0,0 +1,187 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""There are the helper functions for displaying data on the console"""
+from __future__ import absolute_import
+
+from enum import Enum
+
+from tabulate import tabulate
+
+try:
+ from rdmc_helper import LOGGER, UI
+except ImportError:
+ from ilorest.rdmc_helper import LOGGER, UI
+
+
+class OutputFormats(Enum):
+ """Enum class representing output formats"""
+
+ table = "table"
+ list = "list"
+ json = "json"
+
+
+class DisplayHelpers(object):
+ """Helper functions for printing data to display on the console"""
+
+ output_dict = dict()
+
+ def __init__(self, width=20):
+ self.output_dict[OutputFormats.table] = self.print_table
+ self.output_dict[OutputFormats.list] = self.print_list
+ self.output_dict[OutputFormats.json] = self.output_to_json
+ self.max_width = width
+ self._ui = UI()
+
+ def display_data(self, data_to_display, output_format, unique_id_property_name=None):
+ """
+ Wrapper function for displaying data in the proper format: table, list, json, etc.
+ :param data_to_display: The data to display. Each object represents a row in a table
+ or an item in a list.
+ :type data_to_display: array of objects
+ :param output_format: Specifies which output format to use
+ :type output_format: enum
+ :param unique_id_property_name: Specifies the property which acts as the identifier
+ :type unique_id_property_name: string
+ """
+ if data_to_display is None or data_to_display == []:
+ LOGGER.info("DCPMEM: Empty data")
+ return None
+ if output_format not in self.output_dict:
+ LOGGER.info("Incorrect output format")
+ return None
+ self.output_dict[output_format](data_to_display, unique_id_property_name)
+ return None
+
+ def format_data(self, data, truncate=False):
+ """
+ Function to identify header properties and convert Redfish property names to friendly names.
+ It also truncate strings whenever required
+ :param data: The data from which header has to be identified.
+ :type data: array of objects
+ :param truncate: Specifies whether truncation of strings is to be done or not
+ :type truncate: Boolean
+ :return:2 arrays. First array is an array of properties.
+ Second array is an array of objects.
+ Each object is an array containing value of properties at corresponding index.
+ """
+ table = []
+ for item in data:
+ row = [":".join(x.split(":")[1:]).strip() for x in item.split("\n") if len(x.split(":")) >= 2]
+ table.append(row)
+ headers = [x.split(":")[0].strip() for x in data[0].split("\n") if len(x.split(":")) == 2]
+ not_found = [x for x in data[0].split("\n") if len(x.split(":")) < 2]
+ self._ui.printer("\n".join(not_found))
+ if not truncate:
+ return headers, table
+ truncated_headers = [self.truncate_lengthy(str(x), self.max_width) for x in headers]
+ truncated_data = [[self.truncate_lengthy(str(x), self.max_width) for x in row] for row in table]
+ return truncated_headers, truncated_data
+
+ # pylint: disable=unused-argument
+ def print_table(self, table_data, property_id=None):
+ """
+ This function prints data in table format
+ :param table_data: data to be printed in table format
+ :type array of objects
+ :param property_id: Specifies the property which acts as the identifier
+ :type string
+ """
+ headers, data = self.format_data(table_data, True)
+ self._ui.printer("\n")
+ self._ui.printer(tabulate(data, headers, tablefmt="plain"))
+ self._ui.printer("\n\n")
+ return
+
+ def print_list(self, list_data, property_id=None):
+ """
+ This function prints data in list format
+ :param list_data: data to be printed in list format
+ :type array of objects
+ :param property_id: Specifies the property which acts as the identifier
+ :type string
+ """
+
+ headers, data = self.format_data(list_data)
+ flag = 0
+ counter = 0
+ if property_id is None or property_id not in headers:
+ flag = 1
+
+ if flag == 0:
+ property_id_index = headers.index(property_id)
+ del headers[property_id_index]
+
+ for item in data:
+ if flag == 0:
+ self._ui.printer("--- " + property_id + ": " + str(item[property_id_index] + " ---"))
+ item.remove(item[property_id_index])
+ else:
+ counter += 1
+ self._ui.printer("--- " + str(counter) + " ---")
+ for prop in enumerate(headers):
+ self._ui.printer("\n" + headers[prop[0]] + ": " + str(item[prop[0]]))
+ self._ui.printer("\n\n")
+ return
+
+ def print_properties(self, data):
+ """
+ This function prints the data in list format without any header
+ :param data:data to be printed without header
+ :type array of string
+ """
+ if not data:
+ self._ui.printer("\n")
+ return
+ headers, data = self.format_data(data)
+ for item in data:
+ for prop in enumerate(headers):
+ self._ui.printer("\n" + headers[prop[0]] + ": " + str(item[prop[0]]))
+ self._ui.printer("\n")
+ self._ui.printer("\n")
+ return
+
+ # pylint: disable=unused-argument
+ def output_to_json(self, json_data, property_id=None):
+ """
+ This function prints data in json format
+ :param json_data: data to be printed in json format
+ :type json_data: array of objects
+ :param property_id: Specifies the property which acts as the identifier
+ :type property_id: string
+ """
+ self._ui.printer("\n")
+ self._ui.print_out_json(json_data)
+ self._ui.printer("\n")
+ return
+
+ @staticmethod
+ def truncate_lengthy(stringin, max_length):
+ """Truncate lengthy strings to a maximum size
+ :param stringin: string to truncate
+ :type stringin: string
+ :param max_length: maximum allowed length of a string
+ :type max_length: int
+ :return: return the truncated string
+ :rtype: string
+ """
+ if stringin:
+ if len(stringin) > max_length:
+ return stringin[: (max_length - 2)] + ".."
+ return stringin
+ return ""
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/Mapper.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/Mapper.py
new file mode 100644
index 0000000..ddd3443
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/Mapper.py
@@ -0,0 +1,96 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""There are helper functions used to get required data using mapping table"""
+from __future__ import absolute_import
+
+from jsonpointer import resolve_pointer
+
+
+class Mapper(object):
+ """Helper functions for getting data"""
+
+ def get_multiple_attributes(self, data, attributes_list, mapping_table, output_as_json=False, **resources):
+ """
+ Method used to get data for multiple attributes
+ :param data: json from which data is to be extracted
+ :param attributes_list: list of attributes to be extracted
+ :param mapping_table: table used to extract data
+ :param output_as_json: specifies whether the output should be in json or not
+ :param resources: additional resources required to compute attributes
+ :return: Either string or json containing extracted information for all the attributes
+ """
+ # loop over the list of attribute names provided
+ if output_as_json:
+ output = {}
+ for attribute in attributes_list:
+ output.update(
+ self.get_single_attribute(
+ data,
+ attribute,
+ mapping_table,
+ output_as_json=True,
+ resources=resources,
+ )
+ )
+ else:
+ output = ""
+ for attribute in attributes_list:
+ output += "\n" + self.get_single_attribute(data, attribute, mapping_table, resources=resources)
+ return output
+
+ @staticmethod
+ def get_single_attribute(data, attribute_name, mapping_table, output_as_json=False, resources=None):
+ """
+ Method used to get data for single attribute
+ :param data: json from which data is to be extracted
+ :param attribute_name: attribute to be extracted
+ :param mapping_table: table used to extract data
+ :param output_as_json: specifies whether the output should be in json or not
+ :param resources: additional resources required to compute attributes
+ :return: Either string or json containing extracted information for single attribute
+ """
+ output = {} if output_as_json else "No match found for '{}'".format(attribute_name)
+ # finding the attribute in the mapping table
+ mapping = mapping_table.get(attribute_name, None)
+ if mapping:
+ # attribute exists in mapping table
+ resolved_data = resolve_pointer(data, mapping["path"], None)
+
+ if resolved_data is not None:
+ # attribute_name is present in data
+ if "compute" in mapping:
+ resolved_data = mapping["compute"](data=resolved_data, resources=resources)
+ value = resolved_data
+
+ if output_as_json:
+ if "renderJSON" in mapping:
+ # calling the rendering function to generate the JSON output
+ value = mapping["renderJSON"](value)
+ if isinstance(value, bytes):
+ value = value.decode("utf-8")
+ output = {attribute_name: value}
+ else:
+ if "renderText" in mapping:
+ # calling the rendering function to generate the TEXT output
+ value = mapping["renderText"](value)
+ if isinstance(value, bytes):
+ value = value.decode("utf-8")
+ output = "{}: {}".format(attribute_name, value)
+
+ # attribute does not exist in mapping table or value is None
+ return output
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/MapperRenderers.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/MapperRenderers.py
new file mode 100644
index 0000000..57208b1
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/MapperRenderers.py
@@ -0,0 +1,478 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""These are helper functions used to render different attributes"""
+from __future__ import absolute_import, division
+
+from enum import Enum
+
+from .Mapper import Mapper
+from .PmemHelpers import PmemHelpers
+
+
+class MapperRenderers(object):
+ """Methods used to render different attributes"""
+
+ @staticmethod
+ def format_num(num):
+ """
+ Method to convert any number to GiB
+ Note: The result produced by the unit conversion is in GiB.
+ However, it is labeled as GB to be consistent with other products.
+ :param num: any number
+ :return: formatted number in string format
+ """
+ return str(PmemHelpers.py3_round(num / 1024, 2)) + " GB"
+
+ @staticmethod
+ def format_num_json(num):
+ """
+ Method to convert any number to GiB in json format
+ Note: The result produced by the unit conversion is in GiB.
+ However, it is labeled as GB to be consistent with other products.
+ :param num: any number
+ :return: formatted number in json format
+ """
+ return {"Units": "GB", "Value": PmemHelpers.py3_round(num / 1024, 2)}
+
+ @staticmethod
+ def format_percentage(num):
+ """
+ Method to append % symbol
+ :param num: any number
+ :return: formatted percentage in string format
+ """
+ return "{}%".format(num)
+
+ @staticmethod
+ def format_percentage_json(num):
+ """
+ Method to get formatted percentage
+ :param num: any number
+ :return: formatted percentage in json format
+ """
+ return {"Units": "%", "Value": num}
+
+ @staticmethod
+ def find_pmem_interleaved(**kwargs):
+ """
+ Method to find whether specified member is interleaved or not
+ :param kwargs:
+ data (dict): all the Memory REST data for the individual Memory to check whether
+ interleaved or not
+ resources (dict): a dict containing all required REST resources; the 'chunks' keyword
+ containing all MemoryChunk data is required for this method
+ :return: Yes if interleaved, No if not interleaved, N/A otherwise
+ """
+ member = kwargs.get("data", {})
+ chunks = kwargs.get("resources").get("chunks", [])
+ data_id = member.get("@odata.id", "")
+ for chunk in chunks:
+ interleave_sets = chunk.get("InterleaveSets")
+ if not interleave_sets:
+ continue
+ for mem_set in interleave_sets:
+ if PmemHelpers.compare_id(data_id, mem_set.get("Memory").get("@odata.id")):
+ if len(interleave_sets) > 1:
+ return "Yes"
+ return "No"
+ return "N/A"
+
+ @staticmethod
+ def calculate_total_capacity(**kwargs):
+ """
+ Method to calculate total capacity
+ :param kwargs:
+ data (list): all the memory members for which total capacity is to be calculated
+ :return: total capacity in MiB
+ """
+ members = kwargs.get("data", [])
+ total = 0
+ if members:
+ for member in members:
+ capacity = Mapper.get_single_attribute(
+ member, "Capacity", MappingTable.device.value, output_as_json=True
+ )
+ total += capacity.get("Capacity", {}).get("Value", 0)
+ # returning value in MiB
+ return total * 1024
+
+ @staticmethod
+ def calculate_total_pmem(**kwargs):
+ """
+ Method to calculate total pmem size
+ :param kwargs:
+ data (list): all the memory members for which total pmem size is to be calculated
+ :return: total pmem size in MiB
+ """
+ members = kwargs.get("data", [])
+ total = 0
+ if members:
+ for member in members:
+ capacity = Mapper.get_single_attribute(
+ member, "PmemSize", MappingTable.device.value, output_as_json=True
+ )
+ total += capacity.get("PmemSize", {}).get("Value", 0)
+ # returning value in MiB
+ return total * 1024
+
+ @staticmethod
+ def calculate_total_volatile(**kwargs):
+ """
+ Method to calculate total volatile size
+ :param kwargs:
+ data (list): all the members for which total volatile size is to be calculated
+ :return: total volatile size in MiB
+ """
+ members = kwargs.get("data", [])
+ total = 0
+ if members:
+ for member in members:
+ capacity = Mapper.get_single_attribute(
+ member, "VolatileSize", MappingTable.device.value, output_as_json=True
+ )
+ total += capacity.get("VolatileSize", {}).get("Value", 0)
+ # returning value in MiB
+ return total * 1024
+
+ @staticmethod
+ def find_dimm_ids(**kwargs):
+ """
+ Method to find DimmIds present in given chunk
+ :param kwargs:
+ data (dict): all the MemoryChunk REST data for the individual Memory chunk of which
+ dimmids is to be found
+ resources (dict): a dict containing all required REST resources; the 'memory' keyword
+ containing all memory resources is required for this method
+ :return: string containing dimmids
+ """
+ member = kwargs.get("data", {})
+ memory_members = kwargs.get("resources").get("memory", [])
+ interleave_sets = member.get("InterleaveSets", [])
+ locations = []
+ for interleave_set in interleave_sets:
+ for pmem in memory_members:
+ if PmemHelpers.compare_id(interleave_set.get("Memory").get("@odata.id"), pmem.get("@odata.id")):
+ location = Mapper.get_single_attribute(
+ pmem, "Location", MappingTable.device.value, output_as_json=True
+ )
+ locations.append(location.get("Location", ""))
+ return PmemHelpers.location_format_converter(locations)[0]
+
+ @staticmethod
+ def map_operation(**kwargs):
+ """
+ Method to map HTTP operations to user-friendly names
+ :param kwargs:
+ data (str): HTTP operation to be mapped to user-friendly name
+ :return: mapped operation
+ """
+ operation = kwargs.get("data", "")
+ operation_mapper = {"POST": "CREATE"}
+ return operation_mapper.get(operation, operation)
+
+ @staticmethod
+ def calculate_task_pmem_size(**kwargs):
+ """
+ Method to calculate pmem size for a task
+ :param kwargs:
+ data (dict): all the Task REST data for the individual task of which pmem size
+ is to be calculated
+ resources (dict): a dict containing all required REST resources; the 'memory' keyword
+ containing all memory resources is required for this method
+ :return: pmem size of task in MiB
+ """
+ size = 0
+ task = kwargs.get("data", {})
+ # finding memory chunk size
+ memory_chunk_size = Mapper.get_single_attribute(
+ task, "MemoryChunkSize", MappingTable.tasks.value, output_as_json=True
+ )
+ memory_chunk_size = memory_chunk_size.get("MemoryChunkSize", {}).get("Value", None)
+ if memory_chunk_size is not None:
+ size = memory_chunk_size
+ else:
+ memory_members = kwargs.get("resources", {}).get("memory", [])
+ interleave_sets = task.get("Payload").get("JsonBody").get("InterleaveSets", [])
+ selected_members = []
+ for interleave_set in interleave_sets:
+ for pmem in memory_members:
+ if PmemHelpers.compare_id(
+ interleave_set.get("Memory").get("@odata.id"),
+ pmem.get("@odata.id"),
+ ):
+ selected_members.append(pmem)
+ # finding total capacity
+ total_capacity = Mapper.get_single_attribute(
+ selected_members,
+ "TotalCapacity",
+ MappingTable.summary.value,
+ output_as_json=True,
+ )
+ total_capacity = total_capacity.get("TotalCapacity", {}).get("Value", 0)
+ # finding memory chunk size percentage
+ memory_chunk_size_percentage = Mapper.get_single_attribute(
+ task,
+ "MemoryChunkSizePercentage",
+ MappingTable.tasks.value,
+ output_as_json=True,
+ )
+ memory_chunk_size_percentage = memory_chunk_size_percentage.get("MemoryChunkSizePercentage", {}).get(
+ "Value", None
+ )
+ if memory_chunk_size_percentage is not None:
+ size = total_capacity * memory_chunk_size_percentage / 100
+ # returning value in MiB
+ return size * 1024
+
+ @staticmethod
+ def calculate_task_volatile_size(**kwargs):
+ """
+ Method to calculate volatile size for a task
+ :param kwargs:
+ data (dict): all the Task REST data for the individual task of which volatile size
+ is to be calculated
+ resources (dict): a dict containing all required REST resources; the 'memory' keyword
+ containing all memory resources is required for this method
+ :return: volatile size of task in MiB
+ """
+ task = kwargs.get("data", {})
+ memory_members = kwargs.get("resources", {}).get("memory", [])
+ interleave_sets = task.get("Payload").get("JsonBody").get("InterleaveSets", [])
+ selected_members = []
+ for interleave_set in interleave_sets:
+ for pmem in memory_members:
+ if PmemHelpers.compare_id(interleave_set.get("Memory").get("@odata.id"), pmem.get("@odata.id")):
+ selected_members.append(pmem)
+ # finding total capacity
+ total_capacity = Mapper.get_single_attribute(
+ selected_members,
+ "TotalCapacity",
+ MappingTable.summary.value,
+ output_as_json=True,
+ )
+ total_capacity = total_capacity.get("TotalCapacity", {}).get("Value", 0)
+ volatile_size = total_capacity
+ # finding memory chunk size
+ memory_chunk_size = Mapper.get_single_attribute(
+ task, "MemoryChunkSize", MappingTable.tasks.value, output_as_json=True
+ )
+ memory_chunk_size = memory_chunk_size.get("MemoryChunkSize", {}).get("Value", None)
+ if memory_chunk_size is not None:
+ size = memory_chunk_size
+ volatile_size = total_capacity - size
+ else:
+ # finding memory chunk size percentage
+ memory_chunk_size_percentage = Mapper.get_single_attribute(
+ task,
+ "MemoryChunkSizePercentage",
+ MappingTable.tasks.value,
+ output_as_json=True,
+ )
+ memory_chunk_size_percentage = memory_chunk_size_percentage.get("MemoryChunkSizePercentage", {}).get(
+ "Value", None
+ )
+ if memory_chunk_size_percentage is not None:
+ size = total_capacity * memory_chunk_size_percentage / 100
+ volatile_size = total_capacity - size
+ # returning value in MiB
+ return volatile_size * 1024
+
+ @staticmethod
+ def calculate_chunk_volatile_size(**kwargs):
+ """
+ Method to calculate volatile size for a chunk
+ :param kwargs:
+ data (dict): all the Task REST data for the individual chunk of which volatile size
+ is to be calculated
+ resources (dict): a dict containing all required REST resources; the 'memory' keyword
+ containing all memory resources is required for this method
+ :return: volatile size of chunk in MiB
+ """
+ chunk = kwargs.get("data", {})
+ memory_members = kwargs.get("resources", {}).get("memory", [])
+ interleave_sets = chunk.get("InterleaveSets", [])
+ selected_members = []
+ for interleave_set in interleave_sets:
+ for pmem in memory_members:
+ if PmemHelpers.compare_id(interleave_set.get("Memory").get("@odata.id"), pmem.get("@odata.id")):
+ selected_members.append(pmem)
+ # finding total capacity
+ total_capacity = Mapper.get_single_attribute(
+ selected_members,
+ "TotalCapacity",
+ MappingTable.summary.value,
+ output_as_json=True,
+ )
+ total_capacity = total_capacity.get("TotalCapacity", {}).get("Value", 0)
+ volatile_size = total_capacity
+ # finding memory chunk size
+ memory_chunk_size = Mapper.get_single_attribute(
+ chunk, "PmemSize", MappingTable.delete_task.value, output_as_json=True
+ )
+ memory_chunk_size = memory_chunk_size.get("PmemSize", {}).get("Value", None)
+ if memory_chunk_size is not None:
+ volatile_size = total_capacity - memory_chunk_size
+
+ # returning value in MiB
+ return volatile_size * 1024
+
+
+DEVICE_MAPPING_TABLE = {
+ "PmemSize": {
+ "path": "/PersistentRegionSizeLimitMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "Capacity": {
+ "path": "/CapacityMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "DIMMStatus": {"path": "/Oem/Hpe/DIMMStatus", "renderJSON": lambda data: data},
+ "FWVersion": {"path": "/FirmwareRevision", "renderJSON": lambda data: data},
+ "Life": {
+ "path": "/Oem/Hpe/PredictedMediaLifeLeftPercent",
+ "renderText": MapperRenderers.format_percentage,
+ "renderJSON": MapperRenderers.format_percentage_json,
+ },
+ "Location": {"path": "/DeviceLocator", "renderJSON": lambda data: data},
+ "Status": {"path": "/Status/Health", "renderJSON": lambda data: data},
+ "VolatileSize": {
+ "path": "/VolatileRegionSizeLimitMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+}
+
+CONFIG_MAPPING_TABLE = {
+ "PmemInterleaved": {"path": "", "compute": MapperRenderers.find_pmem_interleaved},
+ "PmemSize": {
+ "path": "/PersistentRegionSizeLimitMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "Location": {
+ "path": "/DeviceLocator",
+ "renderJSON": lambda data: data.encode("ascii", "ignore"),
+ },
+ "VolatileSize": {
+ "path": "/VolatileRegionSizeLimitMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+}
+
+SUMMARY_MAPPING_TABLE = {
+ "TotalCapacity": {
+ "path": "",
+ "compute": MapperRenderers.calculate_total_capacity,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "TotalPmemSize": {
+ "path": "",
+ "compute": MapperRenderers.calculate_total_pmem,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "TotalVolatileSize": {
+ "path": "",
+ "compute": MapperRenderers.calculate_total_volatile,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+}
+
+LOGICAL_MAPPING_TABLE = {
+ "PmemSize": {
+ "path": "/MemoryChunkSizeMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "DimmIds": {
+ "path": "",
+ "compute": MapperRenderers.find_dimm_ids,
+ "renderJSON": lambda data: data,
+ },
+}
+
+TASK_MAPPING_TABLE = {
+ "DimmIds": {
+ "path": "/Payload/JsonBody",
+ "compute": MapperRenderers.find_dimm_ids,
+ "renderJSON": lambda data: data,
+ },
+ "MemoryChunkSize": {
+ "path": "/Payload/JsonBody/MemoryChunkSizeMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "MemoryChunkSizePercentage": {
+ "path": "/Payload/JsonBody/Oem/Hpe/MemoryChunkSizePercentage",
+ "renderText": MapperRenderers.format_percentage,
+ "renderJSON": MapperRenderers.format_percentage_json,
+ },
+ "PmemSize": {
+ "path": "",
+ "compute": MapperRenderers.calculate_task_pmem_size,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "Operation": {
+ "path": "/Payload/HttpOperation",
+ "compute": MapperRenderers.map_operation,
+ },
+ "Type": {"path": "/Payload/JsonBody/AddressRangeType"},
+ "VolatileSize": {
+ "path": "",
+ "compute": MapperRenderers.calculate_task_volatile_size,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+}
+
+DELETE_TASK_MAPPING_TABLE = {
+ "DimmIds": {
+ "path": "",
+ "compute": MapperRenderers.find_dimm_ids,
+ "renderJSON": lambda data: data,
+ },
+ "Operation": {"path": "", "compute": lambda **kwargs: "DELETE"},
+ "PmemSize": {
+ "path": "/MemoryChunkSizeMiB",
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+ "VolatileSize": {
+ "path": "",
+ "compute": MapperRenderers.calculate_chunk_volatile_size,
+ "renderText": MapperRenderers.format_num,
+ "renderJSON": MapperRenderers.format_num_json,
+ },
+}
+
+
+class MappingTable(Enum):
+ """Enum class representing mapping tables"""
+
+ device = DEVICE_MAPPING_TABLE
+ summary = SUMMARY_MAPPING_TABLE
+ config = CONFIG_MAPPING_TABLE
+ logical = LOGICAL_MAPPING_TABLE
+ tasks = TASK_MAPPING_TABLE
+ delete_task = DELETE_TASK_MAPPING_TABLE
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/PmemHelpers.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/PmemHelpers.py
new file mode 100644
index 0000000..0dee0cf
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/PmemHelpers.py
@@ -0,0 +1,132 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""This module contains common helper functions used by several pmem commands"""
+
+
+class PmemHelpers(object):
+ """
+ Class containing common helper functions used by several pmem commands
+ """
+
+ @staticmethod
+ def py3_round(number, precision):
+ """
+ Rounds numbers in accordance with the Python 3 round specification
+ :param number: number to be rounded
+ :type number: floating point number
+ :param precision: Number of decimal places the number should be rounded off to
+ :type precision: integer
+ :return: rounded off number
+ """
+ if abs(round(number) - number) == 0.5:
+ return 2.0 * round(number / 2.0, precision)
+ return round(number, precision)
+
+ @staticmethod
+ def parse_dimm_id(dimm_id_list):
+ """
+ Converts DIMM IDs from the 'X@Y' format
+ to the 'PROC X DIMM Y' format
+ :param dimm_id_list: DIMM IDs in the 'X@Y' format
+ :type dimm_id_list: list
+ :return: list of DIMM IDs in the 'PROC X DIMM Y' format
+ """
+ parsed_list = list()
+ for dimm_id in dimm_id_list:
+ temp = dimm_id.split("@")
+ parsed_list.append("PROC " + temp[0] + " DIMM " + temp[1])
+ return parsed_list
+
+ @staticmethod
+ def get_pmem_members(memory_members):
+ """
+ Filters persistent memory members from memory resources
+ :param memory_members: members of memory collection resource
+ :type memory_members: list of members
+ :returns: list of persistent memory members if found else empty list
+ """
+ base_module_type = "PMM"
+ pmem_members = list()
+ pmem_dimm_id = set()
+ for member in memory_members:
+ memory_type = member.get("Oem").get("Hpe").get("BaseModuleType")
+ if memory_type == base_module_type:
+ pmem_members.append(member)
+ pmem_dimm_id.add(member.get("DeviceLocator"))
+ return pmem_members, pmem_dimm_id
+
+ @staticmethod
+ def get_non_aep_members(memory_members):
+ """
+ Filters dram memory members from memory resources
+ :param memory_members: members of memory collection resource
+ :type memory_members: list of members
+ :returns: list of dram memory members if found else empty list
+ """
+ base_module_type = "PMM"
+ dram_members = list()
+ dram_dimm_id = set()
+ for member in memory_members:
+ memory_type = member.get("Oem").get("Hpe").get("BaseModuleType")
+ if memory_type != base_module_type:
+ dram_members.append(member)
+ dram_dimm_id.add(member.get("DeviceLocator"))
+ return dram_members, dram_dimm_id
+
+ @staticmethod
+ def json_to_text(dictionary):
+ """
+ Converts json to string format
+ :param dictionary: json to be converted
+ :return: list containing the string
+ """
+ output = ""
+ for key, value in dictionary.items():
+ item = "\n" + key + ":" + str(value)
+ output += item
+ return [output]
+
+ @staticmethod
+ def location_format_converter(location_list):
+ """
+ Converts location format from 'PROC X DIMM Y' to 'X@Y'
+ :param location_list: list of locations of the format 'PROC X DIMM Y'
+ :type location_list: list of strings
+ :returns: string of locations in the 'X@Y' format (comma separated)
+ """
+ converted_str = ""
+ for location in location_list:
+ temp = location.split(" ")
+ converted_str += temp[1] + "@" + temp[3]
+ if location is not location_list[-1]:
+ converted_str += ", "
+ return converted_str, ("PROC " + converted_str[0])
+
+ @staticmethod
+ def compare_id(id1, id2):
+ """
+ Compares two ids
+ :param id1: first id to be compared
+ :param id2: second id to be compared
+ :return: True if ids are same else False
+ """
+ if id1[-1] == "/":
+ id1 = id1[:-1]
+ if id2[-1] == "/":
+ id2 = id2[:-1]
+ return id1 == id2
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/RestHelpers.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/RestHelpers.py
new file mode 100644
index 0000000..66d1ca3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/RestHelpers.py
@@ -0,0 +1,268 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""This is the helper class with functions that manipulate REST data"""
+from __future__ import absolute_import # check if python3 supported
+
+import json
+import multiprocessing
+from multiprocessing.dummy import Pool as ThreadPool
+
+
+class RestHelpers(object):
+ """This is the helper class with functions that manipulate REST data"""
+
+ def __init__(self, rdmcObject):
+ self.rdmc = rdmcObject # relies on the updated reference to the RDMC class object
+
+ def get_resource(self, url):
+ """
+ Perform a GET request for the specified URL
+ :param url: the URL of the resource to fetch
+ :type: string
+ :returns: object containing the REST responsed
+ :rtype: RestResponse object
+ """
+ accepted_status = [200, 202]
+ resp = self.rdmc.app.get_handler(url, service=True, silent=True)
+ if resp and resp.status in accepted_status and resp.dict:
+ return resp.dict
+ return None
+
+ def retrieve_memory_resources(self):
+ """
+ Retrieve the expanded Memory Collection resource
+ """
+ return self.get_resource("/redfish/v1/systems/1/memory?$expand=.")
+
+ def retrieve_mem_domain_resources(self, chunks_flag=True):
+ """
+ Retrieve Memory Domain Resources and All Chunks
+ """
+ mem_domain_resources = self.get_resource("/redfish/v1/systems/1/MemoryDomains?$expand=.")
+ domain_members = []
+ all_chunks = []
+ if mem_domain_resources:
+ domain_members = mem_domain_resources.get("Members")
+ if not chunks_flag:
+ return domain_members
+ chunk_id_list = list()
+ for member in domain_members:
+ chunk_id_list.append(member.get("MemoryChunks").get("@odata.id") + "?$expand=.")
+ if chunk_id_list:
+ chunks = self.concurrent_get(chunk_id_list)
+ else:
+ chunks = []
+ # combining all chunks
+ for chunk in chunks:
+ chunk_members = chunk.get("Members")
+ if chunk_members:
+ temp_chunks = [member for member in chunk_members]
+ else:
+ temp_chunks = []
+ all_chunks.extend(temp_chunks)
+ return domain_members, all_chunks
+
+ @staticmethod
+ def filter_task_members(task_members):
+ """
+ Filters new memory chunk task members
+ :param task_members: list of task members to be filtered
+ :return: list of filtered task members
+ """
+ members = list()
+ for member in task_members:
+ # retrieving only new tasks
+ if member.get("TaskState") == "New":
+ target_uri = member.get("Payload").get("TargetUri")
+ # checking if type Memory Chunk collection
+ if "MemoryChunks" in target_uri:
+ json_body = member.get("Payload").get("JsonBody")
+ if json_body:
+ member["Payload"]["JsonBody"] = json.loads(json_body)
+ members.append(member)
+ return members
+
+ def retrieve_task_members(self):
+ """
+ Retrieve task members
+ :returns: list of task members
+ """
+ # retrieving task resources
+ task_resources = self.get_resource("/redfish/v1/TaskService/Tasks?$expand=.")
+ # getting task members
+ if task_resources:
+ task_members = task_resources.get("Members")
+ if task_members:
+ return task_members
+ return []
+
+ def retrieve_mem_and_mem_domains(self):
+ """
+ Helper Function to retrieve Memory Resources and Memory Domain Resources
+ concurrently
+ :returns: memory resources, memory domain members and a list of memory chunks
+ """
+ # 'resource_list' is a list of functions that will be called to retrieve resources.
+ resource_list = [
+ self.retrieve_memory_resources,
+ self.retrieve_mem_domain_resources,
+ ]
+ # 'response_list' contains the list of responses received on calling the functions
+ # that are a part of 'resource_list', in the same order.
+ response_list = self.concurrent_retrieve(resource_list)
+ # Segregating individual responses from 'response_list':
+ # The 'get()' call is responsible for retrieving the value that a function returns
+ # from the 'AsyncResult' object returned by 'apply_async' in 'concurrent_retrieve'.
+ memory = response_list[0].get()
+ # domain_members and all chunks are returned as a tuple in 'response_list' as per the
+ # 'retrieve_mem_domain_resources()' function definition. They are unpacked here.
+ (domain_members, all_chunks) = response_list[1]
+ # Returns the values of resources obtained above.
+ return memory, domain_members, all_chunks
+
+ def retrieve_task_members_and_mem_domains(self):
+ """
+ Helper Function to retrieve Task Resources and Memory Domain Resources
+ concurrently
+ :returns: task members, memory domain members and a list of memory chunks
+ """
+ # 'resource_list' is a list of functions that will be called to retrieve resources.
+ resource_list = [self.retrieve_task_members, self.retrieve_mem_domain_resources]
+ # 'response_list' contains the list of responses received on calling the functions
+ # that are a part of 'resource_list', in the same order.
+ response_list = self.concurrent_retrieve(resource_list)
+ # Segregating individual responses from 'response_list':
+ # The 'get()' call is responsible for retrieving the value that a function returns
+ # from the 'AsyncResult' object returned by 'apply_async' in 'concurrent_retrieve'.
+ task_members = response_list[0].get()
+ # domain_members and all chunks are returned as a tuple in 'response_list' as per the
+ # 'retrieve_mem_domain_resources()' function definition. They are unpacked here.
+ (domain_members, all_chunks) = response_list[1]
+ # Returns the values of resources obtained above.
+ return task_members, domain_members, all_chunks
+
+ @staticmethod
+ def concurrent_retrieve(resource_list):
+ """
+ Concurrently retrieve Resources
+ :param resource_list: List of resources to retrieve concurrently
+ :type: List of functions
+ :returns: List of response objects obtained as a result of GET requests
+ :rtype: List of objects
+ """
+ response_list = list()
+ # Spawn a pool of worker threads based on the CPU core count and
+ # the number of functions to call.
+ # 'len(resource_list) - 1' ensures that the last function call happens
+ # on the main thread and not on a worker thread.
+ pool = ThreadPool(min(len(resource_list) - 1, multiprocessing.cpu_count()))
+ # Asynchronously call funtions from 'resource_list' on worker threads and
+ # append responses to 'response_list'. These responses will be 'AsyncResult'
+ # objects and the actual return value will have to be retrieved by a 'get()'
+ # call to the returned response.
+ for resource in resource_list[:-1]:
+ response_list.append(pool.apply_async(resource))
+ # Call the last function in 'resource_list' on the main thread so that
+ # the main thread is doing some useful work and not sitting idle waiting
+ # for all other threads to complete. This reduces the thread communication
+ # overhead as well. The response returned here will not be an 'AsyncResult'
+ # object and no 'get()' call is required to retrieve the actual return value.
+ temp_response = resource_list[-1]()
+ # Wait for all worker threads to complete execution.
+ pool.close()
+ pool.join()
+ # Once all threads have finished execution, append the response recieved
+ # on the main thread to the 'response_list'.
+ response_list.append(temp_response)
+ # Return list of responses obtained from the main and worker threads.
+ return response_list
+
+ def concurrent_get(self, uri_list):
+ """
+ Spawns a Threadpool and sends concurrent GET Requests
+ :param uri_list: List of URIs on which the GET Requests
+ are supposed to be made
+ :returns: List of responses from the GET Requests
+ """
+ # Spawn a pool of worker threads based on the CPU core count and
+ # the number of GET requests to be sent
+ pool = ThreadPool(min(multiprocessing.cpu_count(), len(uri_list)))
+ response_list = pool.map(self.get_resource, uri_list)
+ # Wait for worker threads to complete
+ pool.close()
+ pool.join()
+ # Return list of responses obtained via GET requests from worker threads
+ return response_list
+
+ def delete_resource(self, url):
+ """
+ Perform a delete request for the specified URL
+ :param url: the URL of the resource to delete
+ :type: string
+ :returns: status code
+ """
+ resp = self.rdmc.app.delete_handler(url, service=True, silent=True)
+ if resp and resp.status in [200, 202]:
+ return resp.status
+ return None
+
+ def post_resource(self, path, body):
+ """
+ Perform a post request for the specified path with body
+ :param path: the URL path
+ :type: string
+ :body: the body to be sent
+ :type: string
+ :returns: status code
+ """
+ accepted_status = [200, 201, 202, 204]
+ resp = self.rdmc.app.post_handler(path, body, service=True, silent=True)
+ if resp and resp.status in accepted_status:
+ return resp.status
+ return None
+
+ def in_post(self):
+ """
+ Check whether the server is in POST
+ :return: True if server is in Post, False otherwise
+ :rtype: Boolean
+ """
+ resp_body = self.get_resource("/redfish/v1/systems/1")
+ if resp_body:
+ if resp_body.get("Oem").get("Hpe").get("PostState") == "FinishedPost":
+ return False
+ return True
+
+ def retrieve_security_state(self, path):
+ """
+ Get the security state
+ """
+ return self.get_resource(path)
+
+ def retrieve_pmem_location(self):
+ """
+ Retrieve the Pmem location
+ """
+ return self.get_resource("/redfish/v1/Systems/1/Memory/?$expand=.#")
+
+ def retrieve_model(self, rdmcObj):
+ """
+ Retrieve the Model
+ """
+ self.rdmc = rdmcObj
+ return self.get_resource("/redfish/v1/Chassis/1")
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/DisplayHelpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/DisplayHelpers.cpython-310.pyc
new file mode 100644
index 0000000..13584dd
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/DisplayHelpers.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/Mapper.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/Mapper.cpython-310.pyc
new file mode 100644
index 0000000..101a7a3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/Mapper.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/MapperRenderers.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/MapperRenderers.cpython-310.pyc
new file mode 100644
index 0000000..a1e1c5e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/MapperRenderers.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/PmemHelpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/PmemHelpers.cpython-310.pyc
new file mode 100644
index 0000000..ab4d585
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/PmemHelpers.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/RestHelpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/RestHelpers.cpython-310.pyc
new file mode 100644
index 0000000..1d37a6f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/RestHelpers.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..67f0781
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/PERSISTENT_MEMORY_COMMANDS/lib/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawDeleteCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawDeleteCommand.py
new file mode 100644
index 0000000..046a34a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawDeleteCommand.py
@@ -0,0 +1,219 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawDelete Command for rdmc"""
+
+import sys
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class RawDeleteCommand:
+ """Raw form of the delete command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawdelete",
+ "usage": None,
+ "description": "Run to to delete data from"
+ ' the passed in path.\n\texample: rawdelete "/redfish/v1/'
+ 'Sessions/(session ID)"\n',
+ "summary": "Raw form of the DELETE command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw delete worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ headers = {}
+
+ if hasattr(options, "sessionid") and options.sessionid:
+ self.sessionvalidation(options)
+ else:
+ self.deletevalidation(options)
+
+ if options.path.startswith('"') and options.path.endswith('"'):
+ options.path = options.path[1:-1]
+
+ if options.expand:
+ options.path = options.path + "?$expand=."
+
+ try:
+ currentsess = self.rdmc.app.current_client.session_location
+ except:
+ currentsess = None
+
+ if options.headers:
+ extraheaders = options.headers.split(",")
+
+ for item in extraheaders:
+ header = item.split(":")
+
+ try:
+ headers[header[0]] = header[1]
+ except:
+ InvalidCommandLineError("Invalid format for --headers option.")
+
+ if currentsess and (options.path in currentsess):
+ self.rdmc.app.logout()
+ self.rdmc.ui.printer(
+ "Your session has been terminated (deleted).\nPlease log " "back in if you wish to continue.\n"
+ )
+ else:
+ returnresponse = False
+
+ if options.response or options.getheaders:
+ returnresponse = True
+
+ results = self.rdmc.app.delete_handler(
+ options.path,
+ headers=headers,
+ silent=options.silent,
+ service=options.service,
+ )
+
+ if returnresponse and results:
+ if options.getheaders:
+ self.rdmc.ui.print_out_json(dict(results.getheaders()))
+
+ if options.response:
+ sys.stdout.write(results.read)
+ self.rdmc.ui.printer("\n")
+ elif results.status == 404:
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+ elif results.status != 200:
+ return ReturnCodes.UI_CLI_USAGE_EXCEPTION
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def deletevalidation(self, options):
+ """Raw delete validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.rdmc.login_select_validation(self, options, skipbuild=True)
+
+ def sessionvalidation(self, options):
+ """Raw delete session validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ url = None
+ if options.user or options.password or options.url:
+ if options.url:
+ url = options.url
+ else:
+ if self.rdmc.app.config.get_url():
+ url = self.rdmc.app.config.get_url()
+ if url and "https://" not in url:
+ url = "https://" + url
+
+ return url
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Uri on iLO to be deleted.",
+ )
+ customparser.add_argument(
+ "--response",
+ dest="response",
+ action="store_true",
+ help="Use this flag to return the iLO response body.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--getheaders",
+ dest="getheaders",
+ action="store_true",
+ help="Use this flag to return the iLO response headers.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--headers",
+ dest="headers",
+ help="Use this flag to add extra headers to the request."
+ "\t\t\t\t\t Usage: --headers=HEADER:VALUE,HEADER:VALUE",
+ default=None,
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--expand",
+ dest="expand",
+ action="store_true",
+ help="""Use this flag to expand the path specified using the """ """expand notation '?$expand=.'""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawGetCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawGetCommand.py
new file mode 100644
index 0000000..c704d75
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawGetCommand.py
@@ -0,0 +1,322 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawGet Command for rdmc"""
+
+import json
+
+import redfish
+import requests
+from urllib.parse import urljoin
+import sys
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ResourceNotReadyError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ResourceNotReadyError,
+ ReturnCodes,
+ )
+
+
+class RawGetCommand:
+ """Raw form of the get command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawget",
+ "usage": None,
+ "description": "Run to to retrieve data from "
+ 'the passed in path.\n\tExample: rawget "/redfish/v1/'
+ 'systems/(system ID)"',
+ "summary": "Raw form of the GET command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw get worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ headers = {}
+
+ if getattr(options, "no_auth"):
+ pass
+ else:
+ if hasattr(options, "sessionid") and options.sessionid:
+ _ = self.sessionvalidation(options)
+ else:
+ self.getvalidation(options)
+
+ if options.path.endswith("?=."):
+ path = options.path
+ strip = path[:-3]
+ options.path = strip + "?$expand=."
+
+ if options.path.startswith('"') and options.path.endswith('"'):
+ options.path = options.path[1:-1]
+
+ if options.expand:
+ options.path = options.path + "?$expand=."
+
+ if options.headers:
+ extraheaders = options.headers.split(",")
+ for item in extraheaders:
+ header = item.split(":")
+
+ try:
+ headers[header[0]] = header[1]
+ except:
+ InvalidCommandLineError("Invalid format for --headers " "option.")
+
+ returnresponse = False
+ if options.response or options.getheaders:
+ returnresponse = True
+
+ extra_path = None
+ if "#" in options.path:
+ path_list = options.path.split("#")
+ options.path = path_list[0]
+ extra_path = path_list[1]
+ if "/" in extra_path:
+ extra_path_list = extra_path.split("/")
+ extra_path_list = list(filter(None, extra_path_list))
+ if getattr(options, "no_auth"):
+ if options.url:
+ url = "https://" + options.url.rstrip("/")
+ else:
+ url = "https://16.1.15.1"
+ path = urljoin(url, options.path.lstrip("/"))
+ try:
+ response = requests.get(path, verify=False)
+ if response.status_code == 200:
+ data = response.json()
+ self.rdmc.ui.printer("%s\n" % json.dumps(data, indent=4))
+ return ReturnCodes.SUCCESS
+ except Exception as e:
+ sys.stdout.write("Error: Failed to complete operation.\n")
+ return ReturnCodes.INVALID_COMMAND_LINE_ERROR
+
+ results = self.rdmc.app.get_handler(
+ options.path,
+ sessionid=options.sessionid,
+ headers=headers,
+ silent=options.silent,
+ service=options.service,
+ username=options.user,
+ password=options.password,
+ base_url=options.url,
+ noauth=options.no_auth,
+ stderr_flag=options.stderr_flag,
+ )
+ result = None
+ if results.dict:
+ if extra_path:
+ result = results.dict
+ for p in extra_path_list:
+ if p.isdigit():
+ p = int(p)
+ result = result[p]
+ else:
+ result = results.dict
+
+ if results and results.status == 200 and options.binfile:
+ output = results.read
+ filehndl = open(options.binfile[0], "wb")
+ filehndl.write(output)
+ filehndl.close()
+ elif results and returnresponse:
+ if options.getheaders:
+ self.rdmc.ui.printer(json.dumps(dict(results.getheaders())) + "\n")
+ if options.response:
+ self.rdmc.ui.printer(results.read + "\n")
+ elif results and results.status == 200:
+ if results.dict:
+ if options.filename:
+ output = json.dumps(
+ result,
+ indent=2,
+ cls=redfish.ris.JSONEncoder,
+ sort_keys=True,
+ )
+
+ filehndl = open(options.filename[0], "w")
+ filehndl.write(output)
+ filehndl.close()
+
+ self.rdmc.ui.printer("Results written out to '%s'.\n" % options.filename[0])
+ else:
+ if not result:
+ result = results.dict
+ if options.service:
+ self.rdmc.ui.printer("%s\n" % result)
+ else:
+ self.rdmc.ui.print_out_json(result)
+ else:
+ json_payload = json.loads(results._http_response.data)
+ try:
+ message_id = json_payload["error"]["@Message.ExtendedInfo"][0]["MessageId"]
+ self.rdmc.ui.error("%s" % message_id)
+ if "ResourceNotReadyRetry" in message_id:
+ raise ResourceNotReadyError("Resources are not ready in iLO, Please wait for some time and retry")
+ except:
+ self.rdmc.ui.error("An invalid or incomplete response was received: %s\n" % json_payload)
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def getvalidation(self, options):
+ """Raw get validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.rdmc.login_select_validation(self, options, skipbuild=True)
+
+ def sessionvalidation(self, options):
+ """Raw patch session validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ url = None
+ if options.user or options.password or options.url:
+ if options.url:
+ url = options.url
+ else:
+ if self.rdmc.app.redfishinst and self.rdmc.app.redfishinst.base_url:
+ url = self.rdmc.app.redfishinst.base_url
+ if url and "blobstore://" not in url and "https://" not in url:
+ url = "https://" + url
+
+ return url
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Uri on iLO",
+ )
+ customparser.add_argument(
+ "--response",
+ dest="response",
+ action="store_true",
+ help="Use this flag to return the iLO response body.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--getheaders",
+ dest="getheaders",
+ action="store_true",
+ help="Use this flag to return the iLO response headers.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--headers",
+ dest="headers",
+ help="Use this flag to add extra headers to the request." " example: --headers=HEADER:VALUE,HEADER:VALUE",
+ default=None,
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=False,
+ )
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="""Write results to the specified file.""",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "-b",
+ "--writebin",
+ dest="binfile",
+ help="""Write the results to the specified file in binary.""",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--expand",
+ dest="expand",
+ action="store_true",
+ help="""Use this flag to expand the path specified using the """ """expand notation '?$expand=.'""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--no_auth",
+ dest="no_auth",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--stderr_out",
+ "--stderr_flag",
+ dest="stderr_flag",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawHeadCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawHeadCommand.py
new file mode 100644
index 0000000..b534781
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawHeadCommand.py
@@ -0,0 +1,148 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawHead Command for rdmc"""
+
+import json
+
+import redfish
+
+try:
+ from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+
+
+class RawHeadCommand:
+ """Raw form of the head command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawhead",
+ "usage": None,
+ "description": "Run to to retrieve data from the "
+ 'passed in path\n\texample: rawhead "/redfish/v1/systems/'
+ '(system ID)"\n',
+ "summary": "Raw form of the HEAD command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw head worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.headvalidation(options)
+
+ if options.path.startswith('"') and options.path.endswith('"'):
+ options.path = options.path[1:-1]
+
+ results = self.rdmc.app.head_handler(options.path, silent=options.silent, service=options.service)
+
+ content = None
+ tempdict = dict()
+
+ if results and results.status == 200:
+ if results._http_response:
+ content = results.getheaders()
+ else:
+ content = results._headers
+
+ tempdict = dict(content)
+
+ if options.filename:
+ output = json.dumps(tempdict, indent=2, cls=redfish.ris.JSONEncoder, sort_keys=True)
+ filehndl = open(options.filename[0], "w")
+ filehndl.write(output)
+ filehndl.close()
+
+ self.rdmc.ui.printer("Results written out to '%s'.\n" % options.filename[0])
+ else:
+ if options.service:
+ self.rdmc.ui.printer("%s\n" % tempdict)
+ else:
+ self.rdmc.ui.print_out_json(tempdict)
+ else:
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def headvalidation(self, options):
+ """Raw head validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.rdmc.login_select_validation(self, options, skipbuild=True)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Uri on iLO to query HEAD.",
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="""Use the provided filename to perform operations.""",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPatchCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPatchCommand.py
new file mode 100644
index 0000000..1e23ff0
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPatchCommand.py
@@ -0,0 +1,264 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawPatch Command for rdmc"""
+
+import json
+import re
+import sys
+from collections import OrderedDict
+from urllib.parse import urljoin
+
+import requests
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+
+
+class RawPatchCommand:
+ """Raw form of the patch command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawpatch",
+ "usage": None,
+ "description": "Run to send a patch from the data"
+ " in the input file.\n\tMultiple PATCHes can be performed in sequence by "
+ "\n\tadding more path/body key/value pairs.\n"
+ "\n\texample: rawpatch rawpatch.txt"
+ '\n\n\tExample input file:\n\t{\n\t "/redfish/'
+ 'v1/systems/(system ID)":\n\t {\n\t '
+ '"AssetTag": "NewAssetTag"\n\t }\n\t}',
+ "summary": "Raw form of the PATCH command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw patch worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ headers = {}
+ results = []
+ if getattr(options, "no_auth"):
+ pass
+ else:
+ if hasattr(options, "sessionid") and options.sessionid:
+ _ = self.sessionvalidation(options)
+ else:
+ self.patchvalidation(options)
+
+ contentsholder = None
+ try:
+ with open(options.path, "r") as _if:
+ contentsholder = json.loads(_if.read(), object_pairs_hook=OrderedDict)
+ except IOError:
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist. " "Please create file by running 'save' command." % options.path
+ )
+ except ValueError:
+ raise InvalidFileFormattingError("Input file '%s' was not " "formatted properly." % options.path)
+
+ if options.headers:
+ extraheaders = options.headers.split(",")
+
+ for item in extraheaders:
+ header = item.split(":")
+
+ try:
+ headers[header[0]] = header[1]
+ except:
+ raise InvalidCommandLineError("Invalid format for --headers option.")
+ if getattr(options, "no_auth"):
+ if options.url:
+ url = "https://" + options.url.rstrip("/")
+ else:
+ url = "https://16.1.15.1"
+ path = urljoin(url, contentsholder["path"].lstrip("/"))
+ body = contentsholder["body"]
+ try:
+ response = requests.patch(path, json=body, verify=False)
+ if response.status_code == 200:
+ sys.stdout.write(response.content.decode("utf-8") + "\n")
+ return ReturnCodes.SUCCESS
+ except Exception as e:
+ sys.stdout.write("Error: Failed to complete operation.\n")
+ return ReturnCodes.INVALID_COMMAND_LINE_ERROR
+
+ if "path" in contentsholder and "body" in contentsholder:
+ results.append(
+ self.rdmc.app.patch_handler(
+ contentsholder["path"],
+ contentsholder["body"],
+ headers=headers,
+ silent=options.silent,
+ optionalpassword=options.biospassword,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+
+ elif all([re.match(r"^/(\S+/?)+$", key) for key in contentsholder]):
+ for path, body in contentsholder.items():
+ results.append(
+ self.rdmc.app.patch_handler(
+ path,
+ body,
+ headers=headers,
+ silent=options.silent,
+ optionalpassword=options.biospassword,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+ else:
+ raise InvalidFileFormattingError("Input file '%s' was not format properly." % options.path)
+
+ returnresponse = False
+
+ if options.response or options.getheaders:
+ returnresponse = True
+
+ if results and returnresponse:
+ for result in results:
+ if options.getheaders:
+ self.rdmc.ui.print_out_json(dict(result.getheaders()))
+
+ if options.response:
+ self.rdmc.ui.printer(result.read)
+ self.rdmc.ui.printer("\n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def patchvalidation(self, options):
+ """Raw patch validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.rdmc.login_select_validation(self, options, skipbuild=True)
+
+ def sessionvalidation(self, options):
+ """Raw patch session validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ url = None
+ if options.user or options.password or options.url:
+ if options.url:
+ url = options.url
+ else:
+ if self.rdmc.app.redfishinst.base_url:
+ url = self.rdmc.app.redfishinst.base_url
+ if url and "https://" not in url:
+ url = "https://" + url
+
+ return url
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Path to the JSON file containing the data to be patched.",
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--response",
+ dest="response",
+ action="store_true",
+ help="Use this flag to return the iLO response body.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--getheaders",
+ dest="getheaders",
+ action="store_true",
+ help="Use this flag to return the iLO response headers.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--headers",
+ dest="headers",
+ help="Use this flag to add extra headers to the request."
+ "\t\t\t\t\t Usage: --headers=HEADER:VALUE,HEADER:VALUE",
+ default=None,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--no_auth",
+ dest="no_auth",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPostCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPostCommand.py
new file mode 100644
index 0000000..730ae63
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPostCommand.py
@@ -0,0 +1,276 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawPost Command for rdmc"""
+
+import json
+import re
+import sys
+from collections import OrderedDict
+import requests
+from urllib.parse import urljoin
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+
+
+class RawPostCommand:
+ """Raw form of the post command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawpost",
+ "usage": None,
+ "description": "Run to send a post from "
+ "the data in the input file.\n\tMultiple POSTs can be performed in sequence by"
+ " \n\tadding more path/body key/value pairs.\n"
+ "\n\texample: rawpost rawpost."
+ 'txt\n\n\tExample input file:\n\t{\n\t "/'
+ "redfish/v1/systems/(system ID)/Actions/ComputerSystem."
+ 'Reset":\n\t {\n\t "ResetType": '
+ '"ForceRestart"\n\t }\n\t}',
+ "summary": "Raw form of the POST command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw patch worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ headers = {}
+ results = []
+ if getattr(options, "no_auth"):
+ pass
+ else:
+ if hasattr(options, "sessionid") and options.sessionid:
+ self.sessionvalidation(options)
+ else:
+ self.postvalidation(options)
+
+ contentsholder = None
+
+ try:
+ with open(options.path, "r") as _if:
+ contentsholder = json.loads(_if.read(), object_pairs_hook=OrderedDict)
+ except IOError:
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist. " "Please create file by running 'save' command." % options.path
+ )
+ except ValueError:
+ raise InvalidFileFormattingError("Input file '%s' was not " "formatted properly." % options.path)
+ if options.encode:
+ if (
+ "body" in contentsholder
+ and "UserName" in contentsholder["body"]
+ and "Password" in contentsholder["body"]
+ and len(list(contentsholder["body"].keys())) == 2
+ ):
+ encobj = Encryption()
+ contentsholder["body"]["UserName"] = encobj.decode_credentials(contentsholder["body"]["UserName"])
+ contentsholder["body"]["Password"] = encobj.decode_credentials(contentsholder["body"]["Password"])
+
+ if options.headers:
+ extraheaders = options.headers.split(",")
+ for item in extraheaders:
+ header = item.split(":")
+
+ try:
+ headers[header[0]] = header[1]
+ except:
+ raise InvalidCommandLineError("Invalid format for --headers option.")
+
+ if getattr(options, "no_auth"):
+ if options.url:
+ url = "https://" + options.url.rstrip("/")
+ else:
+ url = "https://16.1.15.1"
+ path = urljoin(url, contentsholder["path"].lstrip("/"))
+ body = contentsholder["body"]
+ try:
+ response = requests.post(path, json=body, verify=False)
+ if response.status_code == 200:
+ sys.stdout.write(response.content.decode("utf-8") + "\n")
+ # sys.stdout.write("Operation completed successfully.\n")
+ return ReturnCodes.SUCCESS
+ except Exception as e:
+ sys.stdout.write("Error: Failed to complete operation.\n")
+ return ReturnCodes.INVALID_COMMAND_LINE_ERROR
+
+ if "path" in contentsholder and "body" in contentsholder:
+ results.append(
+ self.rdmc.app.post_handler(
+ contentsholder["path"],
+ contentsholder["body"],
+ headers=headers,
+ silent=options.silent,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+ elif all([re.match(r"^/(\S+/?)+$", key) for key in contentsholder]):
+ for path, body in contentsholder.items():
+ results.append(
+ self.rdmc.app.post_handler(
+ path,
+ body,
+ headers=headers,
+ silent=options.silent,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+ else:
+ raise InvalidFileFormattingError("Input file '%s' was not " "formatted properly." % options.path)
+ returnresponse = False
+
+ if options.response or options.getheaders:
+ returnresponse = True
+
+ if results and returnresponse:
+ for result in results:
+ if options.getheaders:
+ self.rdmc.ui.print_out_json(dict(result.getheaders()))
+
+ if options.response:
+ if isinstance(result.ori, bytes):
+ self.rdmc.ui.printer(result.ori.decode("utf-8") + "\n")
+ else:
+ self.rdmc.ui.printer(result.ori + "\n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def postvalidation(self, options):
+ """Raw post validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options, skipbuild=True)
+
+ def sessionvalidation(self, options):
+ """Raw post session validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ url = None
+ if options.user or options.password or options.url:
+ if options.url:
+ url = options.url
+ else:
+ if self.rdmc.app.redfishinst.base_url:
+ url = self.rdmc.app.redfishinst.base_url
+ if url and "https://" not in url:
+ url = "https://" + url
+
+ return url
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Path to the JSON file containing the data to be patched.",
+ )
+ customparser.add_argument(
+ "--response",
+ dest="response",
+ action="store_true",
+ help="Use this flag to return the iLO response body.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--getheaders",
+ dest="getheaders",
+ action="store_true",
+ help="Use this flag to return the iLO response headers.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--headers",
+ dest="headers",
+ help="Use this flag to add extra headers to the request."
+ "\t\t\t\t\t Usage: --headers=HEADER:VALUE,HEADER:VALUE",
+ default=None,
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--no_auth",
+ dest="no_auth",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPutCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPutCommand.py
new file mode 100644
index 0000000..9e18f28
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/RawPutCommand.py
@@ -0,0 +1,268 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawPut Command for rdmc"""
+
+import json
+import re
+import sys
+from collections import OrderedDict
+from urllib.parse import urljoin
+
+import requests
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+
+
+class RawPutCommand:
+ """Raw form of the put command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "rawput",
+ "usage": None,
+ "description": "Run to send a post from "
+ "the data in the input file.\n\tMultiple PUTs can be performed in sequence by "
+ "\n\tadding more path/body key/value pairs.\n"
+ "\n\texample: rawput rawput."
+ 'txt\n\n\tExample input file:\n\t{\n\t "/redfish/'
+ 'v1/systems/(system ID)/bios/Settings/":\n\t {\n\t'
+ '\t"Attributes": {\n\t\t'
+ ' "BaseConfig": "default"\n\t\t}\n\t }\n\t}',
+ "summary": "Raw form of the PUT command.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw put worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ headers = {}
+ results = []
+
+ if hasattr(options, "sessionid") and options.sessionid:
+ self.sessionvalidation(options)
+ else:
+ self.putvalidation(options)
+
+ contentsholder = None
+
+ try:
+ with open(options.path, "r") as _if:
+ contentsholder = json.loads(_if.read(), object_pairs_hook=OrderedDict)
+ except IOError:
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist. " "Please create file by running 'save' command." % options.path
+ )
+ except ValueError:
+ raise InvalidFileFormattingError("Input file '%s' was not " "formatted properly." % options.path)
+
+ if options.headers:
+ extraheaders = options.headers.split(",")
+
+ for item in extraheaders:
+ header = item.split(":")
+
+ try:
+ headers[header[0]] = header[1]
+ except:
+ raise InvalidCommandLineError("Invalid format for --headers option.")
+
+ if getattr(options, "no_auth"):
+ if options.url:
+ url = "https://" + options.url.rstrip("/")
+ else:
+ url = "https://16.1.15.1"
+ path = urljoin(url, contentsholder["path"].lstrip("/"))
+ body = contentsholder["body"]
+ try:
+ response = requests.put(path, json=body, verify=False)
+ if response.status_code == 200:
+ sys.stdout.write(response.content.decode("utf-8") + "\n")
+ # sys.stdout.write("Operation completed successfully.\n")
+ return ReturnCodes.SUCCESS
+ except Exception as e:
+ sys.stdout.write("Error: Failed to complete operation.\n")
+ return ReturnCodes.INVALID_COMMAND_LINE_ERROR
+
+ if "path" in contentsholder and "body" in contentsholder:
+ results.append(
+ self.rdmc.app.put_handler(
+ contentsholder["path"],
+ contentsholder["body"],
+ headers=headers,
+ silent=options.silent,
+ optionalpassword=options.biospassword,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+ elif all([re.match(r"^/(\S+/?)+$", key) for key in contentsholder]):
+ for path, body in contentsholder.items():
+ results.append(
+ self.rdmc.app.put_handler(
+ path,
+ body,
+ headers=headers,
+ silent=options.silent,
+ optionalpassword=options.biospassword,
+ service=options.service,
+ noauth=options.no_auth,
+ )
+ )
+ else:
+ raise InvalidFileFormattingError("Input file '%s' was not " "formatted properly." % options.path)
+
+ returnresponse = False
+
+ if options.response or options.getheaders:
+ returnresponse = True
+
+ if results and returnresponse:
+ for result in results:
+ if options.getheaders:
+ sys.stdout.write(json.dumps(dict(result.getheaders())) + "\n")
+
+ if options.response:
+ if isinstance(result.read, bytes):
+ sys.stdout.write(result.read.decode("utf-8"))
+ else:
+ sys.stdout.write(result.read)
+ sys.stdout.write("\n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def putvalidation(self, options):
+ """Raw put validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.rdmc.login_select_validation(self, options, skipbuild=True)
+
+ def sessionvalidation(self, options):
+ """Raw put session validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+
+ url = None
+ if options.user or options.password or options.url:
+ if options.url:
+ url = options.url
+ else:
+ if getattr(self.rdmc.app.redfishinst, "base_url", False):
+ url = self.rdmc.app.redfishinst.base_url
+ if url and "https://" not in url:
+ url = "https://" + url
+
+ return url
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "path",
+ help="Path to the JSON file containing the data to be patched.",
+ )
+ customparser.add_argument(
+ "--response",
+ dest="response",
+ action="store_true",
+ help="Use this flag to return the iLO response body.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--getheaders",
+ dest="getheaders",
+ action="store_true",
+ help="Use this flag to return the iLO response headers.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--headers",
+ dest="headers",
+ help="Use this flag to add extra headers to the request."
+ "\t\t\t\t\t Usage: --headers=HEADER:VALUE,HEADER:VALUE",
+ default=None,
+ )
+ customparser.add_argument(
+ "--silent",
+ dest="silent",
+ action="store_true",
+ help="""Use this flag to silence responses""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--service",
+ dest="service",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
+ customparser.add_argument(
+ "--no_auth",
+ dest="no_auth",
+ action="store_true",
+ help="""Use this flag to enable service mode and increase the function speed""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawDeleteCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawDeleteCommand.cpython-310.pyc
new file mode 100644
index 0000000..139c6df
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawDeleteCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawGetCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawGetCommand.cpython-310.pyc
new file mode 100644
index 0000000..7018aa8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawGetCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawHeadCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawHeadCommand.cpython-310.pyc
new file mode 100644
index 0000000..a2d5e90
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawHeadCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPatchCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPatchCommand.cpython-310.pyc
new file mode 100644
index 0000000..01a227c
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPatchCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPostCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPostCommand.cpython-310.pyc
new file mode 100644
index 0000000..825dfa6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPostCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPutCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPutCommand.cpython-310.pyc
new file mode 100644
index 0000000..7ad912c
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/RawPutCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..adbb064
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/RAW_COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/ClearControllerConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/ClearControllerConfigCommand.py
new file mode 100644
index 0000000..b6284a2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/ClearControllerConfigCommand.py
@@ -0,0 +1,166 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Clear Controller Configuration Command for rdmc"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class ClearControllerConfigCommand:
+ """Drive erase/sanitize command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "clearcontrollerconfig",
+ "usage": None,
+ "description": "To clear a controller"
+ " config.\n\tExample: clearcontrollerconfig --controller=1"
+ '\n\texample: clearcontrollerconfig --controller="Slot0"',
+ "summary": "Clears smart array controller configuration.",
+ "aliases": [],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main disk inventory worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.clearcontrollerconfigvalidation(options)
+
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver >= 6.110:
+ self.rdmc.ui.printer(
+ "ClearController option is not supported for this device."
+ "\nUse factoryresetcontroller command to reset the controller,"
+ "\tthe action does erase or sanitize data on the drives.\n"
+ )
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ else:
+ self.auxcommands["select"].selectfunction("SmartStorageConfig.")
+ content = self.rdmc.app.getprops()
+
+ if not options.controller:
+ raise InvalidCommandLineError("You must include a controller to select.")
+ if options.controller:
+ controllist = []
+ contentsholder = {
+ "LogicalDrives": [],
+ "Actions": [{"Action": "ClearConfigurationMetadata"}],
+ "DataGuard": "Disabled",
+ }
+ try:
+ if options.controller.isdigit():
+ slotlocation = self.get_location_from_id(options.controller)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip()
+ for control in content:
+ if slotcontrol.lower() == control["Location"].lower().split("slot")[-1].lstrip():
+ controllist.append(control)
+ elif "slot" in options.controller.lower():
+ controllerid = options.controller.strip("Slot ")
+ slotlocation = self.get_location_from_id(controllerid)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip()
+ for control in content:
+ if slotcontrol.lower() == control["Location"].lower().split("slot")[-1].lstrip():
+ controllist.append(control)
+ if not controllist:
+ raise InvalidCommandLineError("")
+ except InvalidCommandLineError:
+ raise InvalidCommandLineError("Selected controller not found in the current " "inventory list.")
+ for controller in controllist:
+ # self.rdmc.ui.printer(
+ # "ClearController path and payload: %s, %s\n"
+ # % (controller["@odata.id"], contentsholder)
+ # )
+ self.rdmc.app.put_handler(controller["@odata.id"], contentsholder)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def get_storage_location_from_id(self, storage_id):
+ for sel in self.rdmc.app.select("StorageController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller_storage = sel.dict
+ if controller_storage["Id"] == str(storage_id):
+ return controller_storage["Location"]["PartLocation"]["ServiceLabel"]
+ return None
+
+ def get_location_from_id(self, controller_id):
+ for sel in self.rdmc.app.select("SmartStorageArrayController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller = sel.dict
+ if controller["Id"] == str(controller_id):
+ return controller["Location"]
+ return None
+
+ def clearcontrollerconfigvalidation(self, options):
+ """clear controller config validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller " "using either the slot number or index.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/CreateVolumeCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/CreateVolumeCommand.py
new file mode 100644
index 0000000..cfb18f3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/CreateVolumeCommand.py
@@ -0,0 +1,1167 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Create Volume Command for rdmc"""
+
+from argparse import RawDescriptionHelpFormatter
+
+from redfish.ris import IdTokenError
+from redfish.ris.rmc_helper import IloResponseError, InstanceNotFoundError
+
+try:
+ from rdmc_helper import (
+ IloLicenseError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidSmartArrayConfigurationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IloLicenseError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidSmartArrayConfigurationError,
+ ReturnCodes,
+ )
+
+
+class CreateVolumeCommand:
+ """Create volume command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "createvolume",
+ "usage": None,
+ "description": "Creates volumes on compatible HPE SSA RAID controllers\nTo view "
+ "help on specific sub-commands run: createvolume -h\n\n"
+ "NOTE: Refer http://www.hpe.com/info/scmo for additional information "
+ "on creating Volumes on Gen11 servers.\n\t"
+ "Also, when you select multiple physicaldrives you can select by both\n\t"
+ "physical drive name and by the location at the same time.\n\t"
+ "You can also select controllers by slot number as well as index.\n\t"
+ "For iLO6, storage id need to be specified using --storageid=DE00E000 "
+ "along with --controller=1",
+ "summary": "Creates a new volume on the selected controller.",
+ "aliases": ["createlogicaldrive"],
+ "auxcommands": ["SelectCommand", "StorageControllerCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main disk inventory worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.createvolumevalidation(options)
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver < 6.110:
+ try:
+ self.auxcommands["select"].selectfunction("StorageController.")
+ content = self.rdmc.app.getprops()
+ if options.command == "quickdrive":
+ options.sparedrives = None
+ raise InvalidCommandLineError(
+ "This controller is not compatible with quickdrive option. "
+ "Please use customdrive or volume option\n"
+ )
+ ilo_ver = 6.110
+ options.command = "volume"
+ except Exception:
+ pass
+
+ if ilo_ver >= 6.110:
+ if options.command == "customdrive" or options.command == "quickdrive":
+ raise InvalidCommandLineError("customdrive or quickdrive subcommand is not supported on iLO6(Gen11).\n")
+ if not options.storageid:
+ raise InvalidCommandLineError("--storageid option is mandatory for iLO6 or latest iLO5 onwards.\n")
+ else:
+ if options.command == "volume":
+ raise InvalidCommandLineError("volume subcommand is not supported on iLO5(Gen10).\n")
+ if options.controller:
+ if ilo_ver >= 6.110:
+ if options.storageid:
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ else:
+ raise InvalidCommandLineError("--storageid option is mandatory for iLO6 onwards.\n")
+ else:
+ try:
+ controllers = self.auxcommands["storagecontroller"].controllers(options, single_use=True)
+ if len(controllers) == 0:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory. Please input storageid as well so that "
+ "controllers/volumes can be identified.\n"
+ )
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ options.command = "volume"
+ ilo_ver = 6.110
+ except InstanceNotFoundError:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory. Please input storageid as well so that "
+ "controllers/volumes can be identified.\n"
+ )
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ options.command = "volume"
+ ilo_ver = 6.110
+ try:
+ controller = controllers[next(iter(controllers))]
+ (create_flag, newdrive) = self.createvolume(options, controller)
+ if create_flag:
+ if ilo_ver >= 6.110:
+ temp_odata = controller["@odata.id"]
+ volume_path = temp_odata.split("Controllers")[0] + "Volumes"
+ self.rdmc.ui.printer("CreateVolume path and payload: %s, %s\n" % (volume_path, newdrive))
+ result = self.rdmc.app.post_handler(volume_path, newdrive)
+ if result.status == 400:
+ self.rdmc.ui.error("iLO has thrown BadRequest for this command attempt \n")
+ return ReturnCodes.RIS_ILO_RESPONSE_ERROR
+
+ self.rdmc.ui.printer("Volume created successfully \n")
+ if options.sparedrives:
+ controller["physical_drives"] = self.auxcommands[
+ "storagecontroller"
+ ].storagephysical_drives(options, controller, options.storageid, single_use=True)
+ array = options.disks.split(",")
+ set_spare = False
+ newdrive1 = {"Links": {"DedicatedSpareDrives": [{}]}}
+ volume_path1 = volume_path + result.session_location.split("Volumes")[1]
+
+ sparedrives = options.sparedrives.split(",")
+ s_array = []
+ if len(controller["physical_drives"]) > 0:
+ for p_id in controller["physical_drives"]:
+ p_loc = self.convertloc(
+ controller["physical_drives"][str(p_id)]["PhysicalLocation"]["PartLocation"][
+ "ServiceLabel"
+ ]
+ )
+ l_loc = p_loc.split(":")[1:]
+ p_loc = ":".join(l_loc)
+
+ for sdrive in sparedrives:
+ if sdrive == p_loc and sdrive not in array:
+ s_array.append(
+ {
+ "@odata.id": controller["physical_drives"][str(p_id)]["@odata.id"],
+ }
+ )
+ set_spare = True
+ newdrive1["Links"]["DedicatedSpareDrives"] = s_array
+ if not set_spare:
+ raise InvalidCommandLineError(
+ "Invalid spare drive given, check whether given spare drive is different from "
+ "disks given or check whether spare drive belongs to same controller"
+ )
+
+ self.rdmc.app.patch_handler(volume_path1, newdrive1)
+ self.rdmc.ui.printer("Successfully added the sparedrives %s\n" % (newdrive1))
+
+ return ReturnCodes.SUCCESS
+
+ else:
+ temp_odata = controller["@odata.id"]
+ payload_dict = dict()
+ payload_dict["DataGuard"] = "Disabled"
+ if "settings" not in temp_odata:
+ temp_odata = temp_odata + "settings/"
+ settings_controller = self.rdmc.app.get_handler(temp_odata, service=False, silent=True)
+ # Fix for multiple logical creation at single reboot
+ if self.rdmc.app.typepath.defs.isgen9:
+ payload_dict["logical_drives"] = dict()
+ payload_dict["logical_drives"]["new"] = newdrive
+ else:
+ payload_dict["LogicalDrives"] = settings_controller.dict["LogicalDrives"]
+ payload_dict["LogicalDrives"].append(newdrive)
+ self.rdmc.ui.printer("CreateVolume path and payload: %s, %s\n" % (temp_odata, payload_dict))
+ self.rdmc.app.put_handler(
+ temp_odata,
+ payload_dict,
+ headers={"If-Match": self.getetag(temp_odata)},
+ )
+ self.rdmc.app.download_path([temp_odata], path_refresh=True, crawl=False)
+ self.rdmc.ui.printer(
+ "One or more properties were changed and will not take effect " "until system is reset \n"
+ )
+ except IloLicenseError:
+ self.rdmc.ui.error("License Error Occured while creating volume\n")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IdTokenError:
+ self.rdmc.ui.error("Insufficient Privilege to create volume\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except IloResponseError:
+ self.rdmc.ui.error("iLO threw iLOResponseError\n")
+ return ReturnCodes.RIS_ILO_RESPONSE_ERROR
+ except InvalidSmartArrayConfigurationError as excp:
+ self.rdmc.ui.error(excp.args[0])
+ return ReturnCodes.INVALID_SMART_ARRAY_PAYLOAD
+ except StopIteration:
+ self.rdmc.ui.error("Drive or Controller not exist, Please check drive or controller\n")
+ except InvalidCommandLineError as excp:
+ self.rdmc.ui.error(excp.args[0])
+ return ReturnCodes.INVALID_COMMAND_LINE_ERROR
+ except Exception as excp:
+ self.rdmc.ui.error(excp.args[0])
+
+ else:
+ self.rdmc.ui.error("Provide the controller \n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def convertloc(self, servicelabel):
+ loc = servicelabel.split(":")
+ temp_str = str(
+ loc[0].split("=")[1] + ":" + loc[1].split("=")[1] + ":" + loc[2].split("=")[1] + ":" + loc[3].split("=")[1]
+ )
+ return temp_str
+
+ def get_allowed_list(self, storage_id, attr):
+ url = "/redfish/v1/Systems/1/Storage/" + storage_id + "/Volumes/Capabilities"
+ attr_allowed_str = attr + "@Redfish.AllowableValues"
+
+ results = self.rdmc.app.get_handler(url, service=True, silent=True).dict
+ return results[attr_allowed_str]
+
+ def createvolume(self, options, controller):
+ """Create volume"""
+ global p_loc
+ ilo_ver = self.rdmc.app.getiloversion()
+ if options.command == "volume":
+ ilo_ver = 6.110
+ if ilo_ver >= 6.110:
+ try:
+ raidlvllist = self.get_allowed_list(options.storageid, "RAIDType")
+ except:
+ raidlvllist = [
+ "Raid0",
+ "Raid1",
+ "Raid1ADM",
+ "Raid10",
+ "Raid10ADM",
+ "Raid5",
+ "Raid50",
+ "Raid6",
+ "Raid60",
+ ]
+
+ else:
+ raidlvllist = [
+ "Raid0",
+ "Raid1",
+ "Raid1ADM",
+ "Raid10",
+ "Raid10ADM",
+ "Raid5",
+ "Raid50",
+ "Raid6",
+ "Raid60",
+ ]
+ interfacetypelist = ["SAS", "SATA", "NVMe"]
+ mediatypelist = ["SSD", "HDD"]
+ sparetypelist = ["Dedicated", "Roaming"]
+ acceltypelist = ["ControllerCache", "IOBypass", "None"]
+ locationtypelist = ["Internal", "External"]
+ legacylist = ["Primary", "Secondary", "All", "None"]
+ paritylist = ["Default", "Rapid"]
+ iOPerfModeEnabledlist = ["true", "false"]
+ if ilo_ver >= 6.110:
+ try:
+ readCachePolicylist = self.get_allowed_list(options.storageid, "ReadCachePolicy")
+ # writeCachePolicylist = self.get_allowed_list(options.storageid, "WriteCachePolicy")
+ writeCachePolicylist = [
+ "Off",
+ "WriteThrough",
+ "ProtectedWriteBack",
+ "UnprotectedWriteBack",
+ ]
+ except:
+ readCachePolicylist = ["Off", "ReadAhead"]
+ writeCachePolicylist = [
+ "Off",
+ "WriteThrough",
+ "ProtectedWriteBack",
+ "UnprotectedWriteBack",
+ ]
+ # InitMethodlist = self.get_allowed_list(options.storageid, "InitializeMethod")
+ WriteHoleProtectionPolicyList = ["Yes", "No"]
+ sparedrives = []
+ changes = False
+
+ if ilo_ver >= 6.110:
+ controller["physical_drives"] = self.auxcommands["storagecontroller"].storagephysical_drives(
+ options, controller, options.storageid, single_use=True
+ )
+ else:
+ try:
+ controller["physical_drives"] = self.auxcommands["storagecontroller"].physical_drives(
+ options, controller, single_use=True
+ )
+ except:
+ controller["physical_drives"] = self.auxcommands["storagecontroller"].storagephysical_drives(
+ options, controller, options.storageid, single_use=True
+ )
+ if ilo_ver >= 6.110:
+ controller["logical_drives"] = self.auxcommands["storagecontroller"].storagelogical_drives(
+ options, controller, options.storageid, single_use=True
+ )
+ else:
+ try:
+ controller["logical_drives"] = self.auxcommands["storagecontroller"].logical_drives(
+ options, controller, single_use=True
+ )
+ except:
+ controller["logical_drives"] = self.auxcommands["storagecontroller"].storagelogical_drives(
+ options, controller, options.storageid, single_use=True
+ )
+ if controller.get("Links"):
+ if ilo_ver >= 6.110:
+ newdrive = {"Links": {"Drives": {}}}
+ else:
+ newdrive = {"Links": {"DataDrives": {}}}
+ else:
+ if ilo_ver >= 6.110:
+ newdrive = {"Links": {"Drives": {}}}
+ else:
+ newdrive = {"links": {"DataDrives": {}}}
+
+ changes = False
+ itemadded = False
+
+ for item in raidlvllist:
+ if options.raid.lower() == item.lower():
+ if options.command == "customdrive" or options.command == "volume":
+ drivecount = len(options.disks.replace(", ", ",").split(","))
+ else:
+ try:
+ drivecount = int(options.disks)
+ except ValueError:
+ raise InvalidCommandLineError("Number of drives is not an integer.")
+ if self.raidvalidation(item.lower(), drivecount, options):
+ itemadded = True
+ if ilo_ver >= 6.110:
+ newdrive["RAIDType"] = options.raid.upper()
+ else:
+ newdrive["Raid"] = item
+ break
+
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid raid type or configuration.")
+ else:
+ itemadded = False
+
+ if options.command == "customdrive":
+ if options.sparedrives:
+ sparedrives = options.sparedrives.replace(", ", ",").split(",")
+ newdrive["SpareDrives"] = []
+ newdrive["SpareRebuildMode"] = "Dedicated"
+
+ drives = options.disks.replace(", ", ",").split(",")
+ newdrive["DataDrives"] = []
+
+ if len(controller["physical_drives"]) > 0:
+ for id in controller["physical_drives"]:
+ for drive in drives:
+ drv_id = controller["physical_drives"][str(id)]
+ if "Location" in drv_id:
+ drv_loc = drv_id["Location"]
+ else:
+ location = drv_id["PhysicalLocation"]["PartLocation"]["ServiceLabel"]
+ loc = location.split(":")
+ del loc[0]
+ if len(loc) == 3:
+ temp_str = str(
+ loc[0].split("=")[1] + ":" + loc[1].split("=")[1] + ":" + loc[2].split("=")[1]
+ )
+ drv_loc = temp_str
+
+ if drive == drv_loc:
+ newdrive["DataDrives"].append(drive)
+
+ for sdrive in sparedrives:
+ drv_id = controller["SpareDrives"][str(id)]
+ if "Location" in drv_id:
+ drv_loc = drv_id["Location"]
+ else:
+ location = drv_id["PhysicalLocation"]["PartLocation"]["ServiceLabel"]
+ loc = location.split(":")
+ del loc[0]
+ if len(loc) == 3:
+ temp_str = str(
+ loc[0].split("=")[1] + ":" + loc[1].split("=")[1] + ":" + loc[2].split("=")[1]
+ )
+ drv_loc = temp_str
+
+ if drive == drv_loc:
+ newdrive["DataDrives"].append(drive)
+ if sdrive == drv_id:
+ newdrive["SpareDrives"].append(sdrive)
+ else:
+ raise InvalidCommandLineError("No Physical Drives in this controller")
+
+ if drivecount > len(newdrive["DataDrives"]):
+ raise InvalidCommandLineError(
+ "Not all of the selected drives could " "be found in the specified locations."
+ )
+
+ if options.sparetype:
+ itemadded = False
+ for item in sparetypelist:
+ if options.sparetype.lower() == item.lower():
+ newdrive["SpareRebuildMode"] = item
+ itemadded = True
+ break
+
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid spare drive type.")
+
+ if options.drivename:
+ newdrive["LogicalDriveName"] = options.drivename
+
+ if options.capacitygib:
+ try:
+ capacitygib = int(options.capacitygib)
+ except ValueError:
+ raise InvalidCommandLineError("Capacity is not an integer.")
+ newdrive["CapacityGiB"] = capacitygib
+
+ if options.acceleratortype:
+ itemadded = False
+ for item in acceltypelist:
+ if options.acceleratortype.lower() == item.lower():
+ newdrive["Accelerator"] = item
+ itemadded = True
+ break
+
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid accelerator type.")
+
+ if options.legacyboot:
+ itemadded = False
+ for item in legacylist:
+ if options.legacyboot.lower() in item.lower():
+ newdrive["LegacyBootPriority"] = item
+ itemadded = True
+ break
+
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid legacy boot priority.")
+
+ if options.capacityblocks:
+ try:
+ capacityblocks = int(options.capacityblocks)
+ except ValueError:
+ raise InvalidCommandLineError("Capacity is not an integer.")
+
+ newdrive["CapacityBlocks"] = capacityblocks
+
+ if options.paritygroup:
+ try:
+ paritygroup = int(options.paritygroup)
+ except ValueError:
+ raise InvalidCommandLineError("Parity group is not an integer.")
+
+ newdrive["ParityGroupCount"] = paritygroup
+
+ if options.paritytype:
+ itemadded = False
+ for item in paritylist:
+ if options.paritytype.lower() == item.lower():
+ newdrive["ParityInitializationType"] = item
+ itemadded = True
+ break
+
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid parity type")
+
+ if options.blocksize:
+ try:
+ blocksize = int(options.blocksize)
+ except ValueError:
+ raise InvalidCommandLineError("Block size is not an integer.")
+
+ newdrive["BlockSizeBytes"] = blocksize
+
+ if options.stripsize:
+ try:
+ stripsize = int(options.stripsize)
+ except ValueError:
+ raise InvalidCommandLineError("Strip size is not an integer.")
+
+ newdrive["StripSizeBytes"] = stripsize
+
+ if options.stripesize:
+ try:
+ stripesize = int(options.stripesize)
+ except ValueError:
+ raise InvalidCommandLineError("Stripe size is not an integer.")
+
+ newdrive["StripeSizeBytes"] = stripesize
+ elif options.command == "quickdrive":
+ try:
+ numdrives = int(options.disks)
+ except ValueError:
+ raise InvalidCommandLineError("Number of drives is not an integer.")
+
+ newdrive["DataDrives"] = {
+ "DataDriveCount": numdrives,
+ "DataDriveMinimumSizeGiB": 0,
+ }
+ for item in mediatypelist:
+ if options.drivetype.lower() == item.lower():
+ newdrive["DataDrives"]["DataDriveMediaType"] = item
+ itemadded = True
+ break
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid media type.")
+ else:
+ itemadded = False
+ for item in interfacetypelist:
+ if options.interfacetype.lower() == item.lower():
+ newdrive["DataDrives"]["DataDriveInterfaceType"] = item
+ itemadded = True
+ break
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid interface type.")
+
+ if options.locationtype:
+ for item in locationtypelist:
+ if options.locationtype.lower() == item.lower():
+ newdrive["DataDrives"]["DataDriveLocation"] = item
+ break
+ if options.minimumsize:
+ try:
+ minimumsize = int(options.minimumsize)
+ except ValueError:
+ raise InvalidCommandLineError("Minimum size is not an integer.")
+ newdrive["DataDrives"]["DataDriveMinimumSizeGiB"] = minimumsize
+ newdrive["CapacityGiB"] = minimumsize
+ elif options.command == "volume":
+ idval = []
+ if len(controller["physical_drives"]) > 0:
+ array = options.disks.split(",")
+ DA = ["DA000000", "DA000001"]
+ for p_id in controller["physical_drives"]:
+ if p_id not in DA:
+ p_loc = self.convertloc(
+ controller["physical_drives"][str(p_id)]["PhysicalLocation"]["PartLocation"]["ServiceLabel"]
+ )
+
+ for ar in array:
+ if ar in p_loc:
+ idval.append(controller["physical_drives"][str(p_id)]["@odata.id"])
+ array.remove(ar)
+ newdrive["Links"]["Drives"] = []
+ if idval is not None:
+ for id in idval:
+ newdrive["Links"]["Drives"].append(
+ {
+ "@odata.id": id,
+ }
+ )
+ else:
+ raise InvalidCommandLineError(
+ "Disk location given is invalid , Kindly recheck and provide valid location"
+ )
+
+ if "capacitygib" in options and options.capacitygib:
+ options.capacitybytes = int(options.capacitygib) * 1024 * 1024 * 1024
+ if "capacitybytes" in options and options.capacitybytes is not None:
+ if options.capacitybytes:
+ try:
+ if isinstance(options.capacitybytes, list):
+ capacitybytes = int(options.capacitybytes[0])
+ else:
+ capacitybytes = int(options.capacitybytes)
+ except ValueError:
+ raise InvalidCommandLineError("Capacity is not an integer.")
+
+ newdrive["CapacityBytes"] = capacitybytes
+
+ if "iOPerfModeEnabled" in options and options.iOPerfModeEnabled:
+ for item in iOPerfModeEnabledlist:
+ if (
+ options.iOPerfModeEnabled.lower() == item.lower()
+ and options.iOPerfModeEnabled.lower() == "false"
+ ):
+ newdrive["IOPerfModeEnabled"] = eval(options.iOPerfModeEnabled)
+ itemadded = True
+ break
+ elif (
+ options.iOPerfModeEnabled.lower() == item.lower()
+ and options.iOPerfModeEnabled.lower() == "true"
+ ):
+ if "SSD" in controller["SupportedDeviceProtocols"]:
+ newdrive["IOPerfModeEnabled"] = eval(options.iOPerfModeEnabled)
+ itemadded = True
+ break
+ else:
+ raise InvalidCommandLineError(
+ " IOPerfModeEnabled can be true only when supported protocol is SSD"
+ )
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid IOPerfModeEnabled, Value should be either False or True")
+ else:
+ itemadded = False
+
+ if "ReadCachePolicy" in options and options.ReadCachePolicy is not None:
+ for item in readCachePolicylist:
+ if options.ReadCachePolicy.lower() == item.lower():
+ newdrive["ReadCachePolicy"] = item
+ itemadded = True
+ break
+ if not itemadded:
+ raise InvalidCommandLineError("Invalid ReadCachePolicy, Value should be 'Off' or 'ReadAhead'")
+ else:
+ itemadded = False
+
+ if "WriteCachePolicy" in options and options.WriteCachePolicy is not None:
+ for item in writeCachePolicylist:
+ if options.WriteCachePolicy.lower() == item.lower():
+ newdrive["WriteCachePolicy"] = item
+ itemadded = True
+ break
+ if not itemadded:
+ raise InvalidCommandLineError(
+ "Invalid WriteCachePolicy, Values should be 'Off', 'WriteThrough','ProtectedWriteBack',"
+ "'UnprotectedWriteBack'"
+ )
+ else:
+ itemadded = False
+
+ if "VROC" in controller["Model"]:
+ if options.WriteHoleProtectionPolicy is not None:
+ for item in WriteHoleProtectionPolicyList:
+ if (
+ options.WriteHoleProtectionPolicy.lower() == item.lower()
+ and options.WriteHoleProtectionPolicy.lower() == "yes"
+ ):
+ newdrive["WriteHoleProtectionPolicy"] = "Journaling"
+ newdrive["Links"]["JournalingMedia"] = idval
+ itemadded = True
+ break
+ if not itemadded:
+ raise InvalidCommandLineError(
+ "Invalid WriteHoleProtectionPolicy, Values can be either Yes or No"
+ )
+ else:
+ itemadded = False
+ if "drivename" in options and options.drivename:
+ options.DisplayName = options.drivename
+ if "DisplayName" in options and options.DisplayName is not None:
+ newdrive["DisplayName"] = options.DisplayName
+
+ if newdrive:
+ if options.command == "quickdrive":
+ newdrive_count = newdrive["DataDrives"]["DataDriveCount"]
+ elif options.command == "customdrive":
+ newdrive_count = len(newdrive["DataDrives"])
+ elif options.command == "volume":
+ newdrive_count = len(newdrive["Links"]["Drives"])
+
+ if len(controller["physical_drives"]) >= newdrive_count:
+ drives_avail = len(controller["physical_drives"])
+ accepted_drives = 0
+ for cnt, drive in enumerate(controller["physical_drives"]):
+ drivechecks = (False, False, False, False)
+ if drives_avail < newdrive_count:
+ raise InvalidSmartArrayConfigurationError(
+ "Unable to continue, requested number of drives not actually present under the storage ID"
+ " or the drives requested are in use by another volume\n"
+ )
+ else:
+ drivechecks = (True, False, False, False)
+
+ if options.command == "quickdrive":
+ if (
+ controller["physical_drives"][drive]["InterfaceType"]
+ == newdrive["DataDrives"]["DataDriveInterfaceType"]
+ ):
+ drivechecks = (True, True, False, False)
+ else:
+ drives_avail -= 1
+ continue
+ if (
+ controller["physical_drives"][drive]["MediaType"]
+ == newdrive["DataDrives"]["DataDriveMediaType"]
+ ):
+ drivechecks = (True, True, True, False)
+ else:
+ drives_avail -= 1
+ continue
+ else:
+ drivechecks = (True, True, True, False)
+ in_use = False
+ if controller["logical_drives"] is not None:
+ for existing_logical_drives in controller["logical_drives"]:
+ _logical_drive = controller["logical_drives"][existing_logical_drives]
+ if _logical_drive.get("LogicalDrives"):
+ for _data_drive in _logical_drive["LogicalDrives"]["DataDrives"]:
+ if drive == _logical_drive["LogicalDrives"]["DataDrives"][_data_drive]:
+ in_use = True
+ elif _logical_drive.get("Links"):
+ if not ilo_ver >= 6.110:
+ for _data_drive in _logical_drive["Links"]["DataDrives"]:
+ if drive == _logical_drive["Links"]["DataDrives"][_data_drive]:
+ in_use = True
+ else:
+ for _data_drive in _logical_drive["Links"]["Drives"]:
+ if drive in _data_drive["@odata.id"] and _logical_drive["RAIDType"] != "None":
+ in_use = True
+ elif _logical_drive.get("links"):
+ for _data_drive in _logical_drive["links"]["DataDrives"]:
+ if drive == _logical_drive["links"]["DataDrives"][_data_drive]:
+ in_use = True
+ if in_use:
+ drives_avail -= 1
+ continue
+ else:
+ drivechecks = (True, True, True, True)
+ if drivechecks[0] and drivechecks[1] and drivechecks[2]:
+ if controller.get("Links"):
+ pass
+ else:
+ if not ilo_ver >= 6.110:
+ newdrive["links"]["DataDrives"][int(drive)] = controller["physical_drives"][drive]
+ accepted_drives += 1
+ changes = True
+ if accepted_drives == newdrive_count:
+ break
+ else:
+ drives_avail -= 1
+
+ if changes:
+ if self.rdmc.app.typepath.defs.isgen9:
+ controller["logical_drives"]["new"] = newdrive
+ else:
+ if not ilo_ver >= 6.110:
+ try:
+ newdrive.pop("Links")
+ except KeyError:
+ newdrive.pop("links")
+ if not ilo_ver >= 6.110:
+ controller["LogicalDrives"].append(newdrive)
+ del controller["logical_drives"]
+ del controller["physical_drives"]
+
+ return (changes, newdrive)
+
+ def getetag(self, path):
+ """get etag from path"""
+ etag = None
+ instance = self.rdmc.app.monolith.path(path)
+ if instance:
+ etag = (
+ instance.resp.getheader("etag")
+ if "etag" in instance.resp.getheaders()
+ else instance.resp.getheader("ETag")
+ )
+ return etag
+
+ def raidvalidation(self, raidtype, numdrives, options):
+ """vaidation function for raid levels
+ :param raidtype: raid type
+ :type options: string.
+ :param numdrives: number of drives
+ :type numdrives: int.
+ :param options: command line options
+ :type options: list.
+ """
+
+ valid = True
+ if raidtype == "raid1":
+ if numdrives < 2: # or options.stripsize:
+ valid = False
+ elif raidtype == "raid5":
+ if numdrives < 3: # or options.stripsize:
+ valid = False
+ elif raidtype == "raid6":
+ if numdrives < 4: # or options.stripsize:
+ valid = False
+ elif raidtype == "raid50":
+ if numdrives < 6:
+ valid = False
+ elif raidtype == "raid60":
+ if numdrives < 8:
+ valid = False
+
+ return valid
+
+ def createvolumevalidation(self, options):
+ """Create volume validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ @staticmethod
+ def options_argument_group(parser):
+ """Define optional arguments group
+
+ :param parser: The parser to add the login option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+ group = parser.add_argument_group(
+ "GLOBAL OPTIONS",
+ "Options are available for all" "arguments within the scope of this command.",
+ )
+
+ group.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller "
+ "using either the slot number or index.\nexample: --controller=Slot 0 OR "
+ "--controller=1",
+ default=None,
+ required=True,
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ # self.options_argument_group(customparser)
+
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ subcommand_parser.required = True
+ qd_help = (
+ "Create a volume with a minimal number of arguments (utilizes default "
+ "values on the controller). This option is only for iLO5 or Gen10"
+ )
+ # quickdrive sub-parser
+ qd_parser = subcommand_parser.add_parser(
+ "quickdrive",
+ help=qd_help,
+ description=qd_help + "\n\texample: createvolume quickdrive "
+ " "
+ "--locationtype=Internal --minimumsize=0 --controller=1",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ qd_parser.add_argument(
+ "raid",
+ help="Specify the RAID level for the volume to be created.",
+ metavar="Raid_Level",
+ )
+ qd_parser.add_argument(
+ "disks",
+ help="For quick drive creation, specify number of disks.",
+ metavar="Drives",
+ )
+ qd_parser.add_argument(
+ "drivetype",
+ help="Specify the drive media type of the physical disk(s) (i.e. HDD or SSD)",
+ metavar="Drive_Media_Type",
+ )
+ qd_parser.add_argument(
+ "interfacetype",
+ help="Specify the interface type of the physical disk(s) (i.e. SATA or SAS or NVMe)",
+ metavar="Drive_Interface_Type",
+ )
+ qd_parser.add_argument(
+ "--locationtype",
+ dest="locationtype",
+ help="Optionally specify the location of the physical disks(s) (i.e. Internal or External)",
+ default=None,
+ )
+ qd_parser.add_argument(
+ "--minimumsize",
+ dest="minimumsize",
+ help="""Optionally include to set the minimum size of the drive """
+ """in GiB. (usable in quick creation only, use -1 for max size)""",
+ default=None,
+ )
+ qd_parser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller "
+ "using either the slot number or index.\nexample: --controller=Slot 0 OR "
+ "--controller=1",
+ default=None,
+ required=True,
+ )
+ qd_parser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storageid "
+ "using either the slot number or index.\nexample: --storageid=DE123234",
+ default=None,
+ required=False,
+ )
+ self.cmdbase.add_login_arguments_group(qd_parser)
+ # self.options_argument_group(qd_parser)
+
+ cd_help = (
+ "Create a customised volume using all available properties (as optional "
+ "arguments) for creation. This option is only for iLO5 or Gen10"
+ )
+ # customdrive sub-parser
+ cd_parser = subcommand_parser.add_parser(
+ "customdrive",
+ help=cd_help,
+ description=cd_help + "\n\texample: createvolume customdrive "
+ " --controller=1 "
+ "--name=drivename --spare-drives=1I:1:1,1I:1:3 "
+ "--spare-type=Dedicated --capacitygib=10 "
+ "--accelerator-type=None\n\n\tOPTIONS:\n\traid-level:\t\t"
+ "Raid0, Raid1, Raid1ADM, Raid10, Raid10ADM, Raid5, Raid50, "
+ "Raid6, Raid60\n\tphysicaldrivelocation(s):\tLocation, Drive-name\n\t"
+ "media-type:\t\tSSD,HDD\n\tinterface-type:"
+ "\t\tSAS, SATA, NVMe\n\tdrive-location:\t\tInternal, External\n\t"
+ "--spare-type:\t\tDedicated, Roaming\n\t--accelerator-type:\t"
+ "ControllerCache, IOBypass, None\n\t--paritytype:\t\tDefault, Rapid"
+ "\n\t--capacitygib:\t\t-1 (for Max Size)\n\t--capacityblocks:\t"
+ "-1 (for Max Size)\n\n\t",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ cd_parser.add_argument(
+ "raid",
+ help="Specify the RAID level for the volume to be created.",
+ metavar="Raid_Level",
+ )
+ cd_parser.add_argument(
+ "disks",
+ help="For custom drive, specify a comma separated physical disk locations.",
+ metavar="Drive_Indices",
+ )
+ cd_parser.add_argument(
+ "-n",
+ "--name",
+ dest="drivename",
+ help="""Optionally include to set the drive name (usable in """ """custom creation only).""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--spare-drives",
+ dest="sparedrives",
+ help="""Optionally include to set the spare drives by the """
+ """physical drive's location. (usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--capacitygib",
+ dest="capacitygib",
+ help="""Optionally include to set the capacity of the drive in """
+ """GiB. (usable in custom creation only, use -1 for max """
+ """size)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--accelerator-type",
+ dest="acceleratortype",
+ help="""Optionally include to choose the accelerator type.""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--spare-type",
+ dest="sparetype",
+ help="""Optionally include to choose the spare drive type. """ """(usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--minimumsize",
+ dest="minimumsize",
+ help="""Optionally include to set the minimum size of the drive """
+ """in GiB. (usable in quick creation only, use -1 for max size)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--legacy-boot",
+ dest="legacyboot",
+ help="""Optionally include to choose the legacy boot priority. """ """(usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storageid "
+ "using either the slot number or index.\nexample: --storageid=DE123234",
+ default=None,
+ required=False,
+ )
+ cd_parser.add_argument(
+ "--capacityblocks",
+ dest="capacityblocks",
+ help="""Optionally include to choose the capacity in blocks. """
+ """(use -1 for max size, usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--paritygroupcount",
+ dest="paritygroup",
+ help="""Optionally include to include the number of parity """
+ """groups to use. (only valid for certain RAID levels)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--paritytype",
+ dest="paritytype",
+ help="""Optionally include to choose the parity initialization"""
+ """ type. (usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--block-size-bytes",
+ dest="blocksize",
+ help="""Optionally include to choose the block size of the disk"""
+ """ drive. (usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--strip-size-bytes",
+ dest="stripsize",
+ help="""Optionally include to choose the strip size in bytes. """ """(usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--stripe-size-bytes",
+ dest="stripesize",
+ help="""Optionally include to choose the stripe size in bytes. """ """(usable in custom creation only)""",
+ default=None,
+ )
+ cd_parser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller "
+ "using either the slot number or index.\nexample: --controller=Slot 0 OR "
+ "--controller=1",
+ default=None,
+ required=True,
+ )
+ self.cmdbase.add_login_arguments_group(cd_parser)
+ # self.options_argument_group(cd_parser)
+ v_help = (
+ "Create a volume using all available properties (as optional "
+ "arguments) for creation on gen 11 or higher "
+ )
+ # volume sub-parser
+ v_parser = subcommand_parser.add_parser(
+ "volume",
+ help=v_help,
+ description=v_help + "\n\texample: createvolume volume "
+ " "
+ " "
+ " --storageid=DE009000 --controller=0 "
+ " "
+ "\n\n\t",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ v_parser.add_argument(
+ "raid",
+ help="Specify the RAID level for the volume to be created.",
+ metavar="Raid_Level",
+ )
+ v_parser.add_argument(
+ "disks",
+ help="For custom drive, specify a comma separated physical disk locations.",
+ metavar="Drive_Indices",
+ )
+ v_parser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storageid "
+ "using either the slot number or index.\nexample: --storageid=DE123234",
+ default=None,
+ required=True,
+ )
+ v_parser.add_argument(
+ "--DisplayName",
+ dest="DisplayName",
+ help="""Optionally include to set the drive name """,
+ default=None,
+ )
+ v_parser.add_argument(
+ "--iOPerfModeEnabled",
+ dest="iOPerfModeEnabled",
+ help="""Optionally include to choose the IOPerfModeEnabled . Allowed values are 'True', 'False'""",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--ReadCachePolicy",
+ dest="ReadCachePolicy",
+ help="""Optionally include to choose the ReadCachePolicy. """ """Allowed values are 'Off', 'ReadAhead'""",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--WriteCachePolicy",
+ dest="WriteCachePolicy",
+ help="Optionally include to set the WriteCachePolicy "
+ "Allowed values are 'Off', 'WriteThrough','ProtectedWriteBack','UnprotectedWriteBack'",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--WriteHoleProtectionPolicy",
+ dest="WriteHoleProtectionPolicy",
+ help="""Optionally include to choose the WriteHoleProtectionPolicy """
+ """this is applicable only for VROC, You can send either Yes or No as values""",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--sparedrives",
+ dest="sparedrives",
+ help="""Optionally include to set the spare drives by the """
+ """physical drive's location. (usable in custom creation only)""",
+ action="append",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--capacitybytes",
+ dest="capacitybytes",
+ help="""Optionally include to set the capacity of the drive in """
+ """bytes. (usable in custom creation only, use -1 for max """
+ """size)""",
+ action="append",
+ default=None,
+ )
+ v_parser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller "
+ "using either the slot number or index.\nexample: --controller=Slot 0 OR "
+ "--controller=1",
+ default=None,
+ required=True,
+ )
+
+ self.cmdbase.add_login_arguments_group(v_parser)
+ # self.options_argument_group(v_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DeleteVolumeCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DeleteVolumeCommand.py
new file mode 100644
index 0000000..98c1268
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DeleteVolumeCommand.py
@@ -0,0 +1,441 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Delete Volume Command for rdmc"""
+
+from six.moves import input
+
+from redfish.ris import InstanceNotFoundError
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class DeleteVolumeCommand:
+ """Delete volume command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "deletevolume",
+ "usage": None,
+ "description": "To delete a volume "
+ "to a controller by index.\n\texample: deletevolume "
+ "1 --controller=1\n\n\tTo delete multiple volumes by "
+ "index.\n\tExample: deletevolume 1,2 --controller=1"
+ "\n\n\tTo delete all volumes on a controller.\n\t"
+ "deletevolume --controller=1 --all\n\n\t"
+ 'deletevolume --controller="Slot1" --all\n\n\tNOTE: '
+ "You can also delete volumes by "
+ '"VolumeUniqueIdentifier".\n\n\t'
+ "For iLO6, storage id need to be specified using --storageid=DE00E000 along with --controller=1\n\n\t"
+ "You can also delete volumes by VolumeName",
+ "summary": "Deletes volumes from the selected controller.",
+ "aliases": ["deletelogicaldrive"],
+ "auxcommands": ["SelectCommand", "StorageControllerCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main disk inventory worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.deletevolumevalidation(options)
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver < 6.110:
+ try:
+ self.auxcommands["select"].selectfunction("StorageController.")
+ content = self.rdmc.app.getprops()
+ ilo_ver = 6.110
+ except Exception:
+ pass
+
+ if ilo_ver >= 6.110:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory for iLO6 along with --controller option.\n"
+ )
+ controller_logicaldrives = self.auxcommands["storagecontroller"].storagelogical_drives(
+ options, options.controller, options.storageid, single_use=True
+ )
+ if len(controller_logicaldrives) == 0:
+ raise InvalidCommandLineError("No Logical drives found.\n")
+ get_contrller = []
+ self.auxcommands["select"].selectfunction("StorageCollection.")
+ st_content = self.rdmc.app.getprops()
+ for st_controller in st_content:
+ path = st_controller["Members"]
+ for mem in path:
+ for val in mem.values():
+ if "DE" in val and options.storageid in val:
+ getval = self.rdmc.app.get_handler(val, silent=True, service=True).dict
+ controller = getval["Controllers"]
+ for ctl in controller.values():
+ ctl = ctl + "/0"
+ get_sel = self.rdmc.app.get_handler(ctl, silent=True, service=True)
+ get_contrller.append(get_sel)
+ else:
+ self.auxcommands["select"].selectfunction("SmartStorageConfig.")
+ content = self.rdmc.app.getprops()
+
+ if not args and not options.all:
+ raise InvalidCommandLineError("You must include a logical drive to delete.")
+ elif not options.controller:
+ raise InvalidCommandLineError("You must include a controller to select.")
+ else:
+ if len(args) > 1:
+ logicaldrives = args
+ elif len(args) == 1:
+ logicaldrives = args[0].replace(", ", ",").split(",")
+ else:
+ logicaldrives = None
+
+ controllist = []
+
+ try:
+ if ilo_ver >= 6.110:
+ if options.controller.isdigit():
+ slotlocation = self.storageget_location_from_id(options.controller, options.storageid)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip().strip("=")
+ for control in get_contrller:
+ cont_temp = control.dict
+ temp = cont_temp["Location"]["PartLocation"]["ServiceLabel"]
+ if "Location" in cont_temp and slotcontrol.lower() == temp.lower().split("slot")[
+ -1
+ ].lstrip().strip("="):
+ controllist.append(cont_temp)
+ elif "slot" in options.controller.lower():
+ slotlocation = options.controller
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip().strip("=")
+ for control in get_contrller:
+ cont_temp = control.dict
+ temp = cont_temp["Location"]["PartLocation"]["ServiceLabel"]
+ if "Location" in cont_temp and slotcontrol.lower() == temp.lower().split("slot")[
+ -1
+ ].lstrip().strip("="):
+ controllist.append(cont_temp)
+ if not controllist:
+ raise InvalidCommandLineError("")
+ else:
+ try:
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ options.command = "volume"
+ ilo_ver = 6.110
+ except InstanceNotFoundError:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory. Please input storageid as well so that "
+ "controllers/volumes can be identified.\n"
+ )
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ options.command = "volume"
+ ilo_ver = 6.110
+ if options.controller.isdigit():
+ slotlocation = self.get_location_from_id(options.controller)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip()
+ for control in content:
+ if slotcontrol.lower() == control["Location"].lower().split("slot")[-1].lstrip():
+ controllist.append(control)
+ if not controllist:
+ raise InvalidCommandLineError("")
+ except InvalidCommandLineError:
+ raise InvalidCommandLineError("Selected controller not found in the current inventory " "list.")
+ if ilo_ver >= 6.110:
+ self.storagedeletevolume(controller_logicaldrives, logicaldrives, options.all, options.force)
+ else:
+ self.deletevolume(controllist, logicaldrives, options.all, options.force)
+ self.rdmc.ui.printer(
+ "One or more properties were changed and will not take effect " "until system is reset \n"
+ )
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def get_location_from_id(self, controller_id):
+ # self.rdmc.ui.printer("Controller ID: %s\n" % (controller_id))
+ for sel in self.rdmc.app.select("SmartStorageArrayController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller = sel.dict
+ self.rdmc.ui.printer("Controller ID: %s\n" % (controller["Id"]))
+ if controller["Id"] == str(controller_id):
+ self.rdmc.ui.printer("Controller Location: %s\n" % (controller["Location"]))
+ return controller["Location"]
+ return None
+
+ def storageget_location_from_id(self, controller_id, storageid):
+ get_contrller = []
+ # self.auxcommands["select"].selectfunction("StorageCollection.")
+ st_content = self.rdmc.app.getprops()
+ for st_controller in st_content:
+ path = st_controller["Members"]
+ for mem in path:
+ for val in mem.values():
+ if "DE" in val and storageid in val:
+ getval = self.rdmc.app.get_handler(val, silent=True, service=True).dict
+ controller = getval["Controllers"]
+ for ctl in controller.values():
+ ctl = ctl + "/0"
+ get_sel = self.rdmc.app.get_handler(ctl, silent=True, service=True)
+ get_contrller.append(get_sel)
+ for sel in get_contrller:
+ if "Collection" not in sel.path:
+ controller = sel.dict
+ if controller["Id"] == str(controller_id):
+ return controller["Location"]["PartLocation"]["ServiceLabel"]
+ return None
+
+ def deletevolume(self, controllist, drivelist, allopt, force):
+ """Gets volumes ready for deletion
+
+ :param controllist: list of controllers
+ :type controllist: list.
+ :param drivelist: volumes to delete
+ :type drivelist: list.
+ :param allopt: flag for deleting all volumes
+ :type allopt: bool.
+ """
+
+ for controller in controllist:
+ changes = False
+
+ numdrives = len(controller["LogicalDrives"])
+ deletecount = 0
+
+ if allopt:
+ controller["LogicalDrives"] = []
+ controller["DataGuard"] = "Disabled"
+ if not force:
+ if not self.inputaccept(None):
+ return
+ self.lastlogicaldrive(controller)
+ changes = True
+ else:
+ for deldrive in drivelist:
+ found = False
+
+ if deldrive.isdigit():
+ deldrive = int(deldrive)
+
+ for idx, ldrive in enumerate(controller["LogicalDrives"]):
+ if deldrive == ldrive["LogicalDriveNumber"]:
+ if not force:
+ if not self.inputaccept(ldrive["LogicalDriveName"]):
+ return
+
+ controller["LogicalDrives"][idx]["Actions"] = [{"Action": "LogicalDriveDelete"}]
+
+ controller["DataGuard"] = "Permissive"
+ deletecount += 1
+
+ changes = True
+ found = True
+
+ break
+
+ if not found:
+ raise NoContentsFoundForOperationError("Logical " "drive %s not found." % str(deldrive))
+
+ if deletecount == numdrives:
+ self.lastlogicaldrive(controller)
+
+ if changes:
+ # self.rdmc.ui.printer(
+ # "DeleteVolume path and payload: %s, %s\n"
+ # % (controller["@odata.id"], controller)
+ # )
+ put_path = controller["@odata.id"]
+ if "settings" not in put_path:
+ put_path = put_path + "settings/"
+ controller["@odata.id"] = put_path
+ self.rdmc.app.put_handler(
+ put_path,
+ controller,
+ headers={"If-Match": self.getetag(controller["@odata.id"])},
+ )
+ self.rdmc.app.download_path([controller["@odata.id"]], path_refresh=True, crawl=False)
+
+ def lastlogicaldrive(self, controller):
+ """Special case that sets required properties after last drive deletion
+
+ :param controller: controller change settings on
+ :type controller: dict.
+ """
+ changelist = [
+ "PredictiveSpareRebuild",
+ "SurfaceScanAnalysisPriority",
+ "FlexibleLatencySchedulerSetting",
+ "DegradedPerformanceOptimization",
+ "CurrentParallelSurfaceScanCount",
+ "SurfaceScanAnalysisDelaySeconds",
+ "MonitorAndPerformanceAnalysisDelaySeconds",
+ "InconsistencyRepairPolicy",
+ "DriveWriteCache",
+ "ExpandPriority",
+ "EncryptionEULA",
+ "NoBatteryWriteCache",
+ "ReadCachePercent",
+ "WriteCacheBypassThresholdKiB",
+ "RebuildPriority",
+ "QueueDepth",
+ "ElevatorSort",
+ ]
+
+ for item in changelist:
+ if item in list(controller.keys()):
+ controller[item] = None
+
+ def inputaccept(self, drivename):
+ while True:
+ if drivename is None:
+ ans = input("Are you sure you would" " like to continue deleting all volumes" "? (y/n)")
+ else:
+ ans = input("Are you sure you would" " like to continue deleting volume" " %s? (y/n)" % drivename)
+
+ if ans.lower() == "y":
+ return 1
+ elif ans.lower() == "n":
+ self.rdmc.ui.printer("Stopping command without deleting volume/volumes.\n")
+ return 0
+
+ def storagedeletevolume(self, logical_drives, drivelist, allopt, force):
+ if allopt:
+ sorted_dict = sorted(logical_drives, reverse=True)
+ if not force:
+ if not self.inputaccept(None):
+ return
+ for drive in sorted_dict:
+ self.rdmc.app.delete_handler(logical_drives[drive]["@odata.id"], {})
+ else:
+ found = False
+ sorted_dict = sorted(logical_drives, reverse=True)
+ sorted_drivelist = sorted(drivelist, reverse=True)
+ for deldrive in sorted_drivelist:
+ found = False
+ changes = False
+ for drive in sorted_dict:
+ if logical_drives[drive]["Id"] == deldrive:
+ if not force:
+ if not self.inputaccept(logical_drives[drive]["Name"]):
+ return
+ self.rdmc.ui.printer("Setting volume %s " "for deletion\n" % logical_drives[drive]["Name"])
+ changes = True
+ found = True
+ break
+ try:
+ if changes:
+ # self.rdmc.ui.printer(
+ # "DeleteVolume path and payload: %s, %s\n"
+ # % (logical_drives[drive]["@odata.id"], "{}")
+ # )
+ self.rdmc.app.delete_handler(logical_drives[drive]["@odata.id"], {})
+ except:
+ self.rdmc.ui.printer(
+ "This volume cannot"
+ "be deleted without deleting "
+ "the higher volumes in the same Physical drive first.\n"
+ )
+ raise
+ if not found:
+ raise NoContentsFoundForOperationError("Logical " "drive not found.")
+
+ def getetag(self, path):
+ """get etag from path"""
+ etag = None
+ instance = self.rdmc.app.monolith.path(path)
+ if instance:
+ etag = (
+ instance.resp.getheader("etag")
+ if "etag" in instance.resp.getheaders()
+ else instance.resp.getheader("ETag")
+ )
+ return etag
+
+ def deletevolumevalidation(self, options):
+ """delete volume validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller " "using either the slot number or index.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storageid in iLO6 only.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--all",
+ dest="all",
+ help="""Use this flag to delete all volumes on a controller.""",
+ action="store_true",
+ default=False,
+ )
+ customparser.add_argument(
+ "--force",
+ dest="force",
+ help="""Use this flag to override the "are you sure?" text when deleting a volume.""",
+ action="store_true",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DriveSanitizeCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DriveSanitizeCommand.py
new file mode 100644
index 0000000..7881a0a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/DriveSanitizeCommand.py
@@ -0,0 +1,537 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Drive Erase/ Sanitize Command for rdmc"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class DriveSanitizeCommand:
+ """Drive erase/sanitize command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "drivesanitize",
+ "usage": None,
+ "description": "To sanitize a physical drive "
+ 'by index.\n\texample: drivesanitize "1I:1:1" --controller=1\n\n\tTo'
+ " sanitize multiple drives by specifying location.\n\texample: "
+ "drivesanitize 1I:1:1,1I:1:2 --controller=1 --mediatype HDD/SSD\n\texample: drivesanitize "
+ "1I:1:1,1I:1:2 --controller=Slot 1 --mediatype=HDD "
+ "if incorrect mediatype is specified, error will generated "
+ "For iLO6, storage id need to be specified using --storageid=DE00E000 along with --controller=1\n"
+ "Once drivesanitize function is performed in iLO6, It may take a while to complete.\n "
+ "To check the status of Sanitization, perform to following command-\n"
+ "drivesanitize 1I:1:1 --controller=1 --storageid=DE00900 --status\n"
+ "Once the process in 100% complete, use the --drivereset tag to reset the drive. Example-\n"
+ "drivesanitize 1I:1:1 --controller=1 --storageid=DE00900 --drivereset\n",
+ "summary": "Erase/Sanitize physical drive(s)",
+ "aliases": ["DriveEraseCommand"],
+ "auxcommands": ["SelectCommand", "RebootCommand", "StorageControllerCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main disk inventory worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.drivesanitizevalidation(options)
+
+ ilo_ver = self.rdmc.app.getiloversion()
+
+ if ilo_ver < 6.110:
+ try:
+ controllers = self.auxcommands["storagecontroller"].controllers(options, single_use=True)
+ if len(controllers) == 0:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory. Please input storageid as well so that "
+ "controllers/volumes can be identified.\n"
+ )
+ ilo_ver = 6.110
+ except:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory. Please input storageid as well so that "
+ "controllers/volumes can be identified.\n"
+ )
+
+ if ilo_ver >= 6.110:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory for iLO6" " along with --controller option.\n"
+ )
+ self.auxcommands["select"].selectfunction("StorageController")
+ content = self.rdmc.app.getprops()
+ controllers = self.auxcommands["storagecontroller"].storagecontroller(options, single_use=True)
+ if controllers:
+ for controller in controllers:
+ if (
+ getattr(options, "controller", False) == controller
+ or controllers[controller]["Location"]["PartLocation"]["ServiceLabel"][-1]
+ == getattr(options, "controller", False)[-1]
+ ):
+ controller_physicaldrives = self.auxcommands["storagecontroller"].storagephysical_drives(
+ options,
+ options.controller,
+ options.storageid,
+ single_use=True,
+ )
+ else:
+ self.auxcommands["select"].selectfunction("SmartStorageConfig")
+ content = self.rdmc.app.getprops()
+ controllers = self.auxcommands["storagecontroller"].controllers(options, single_use=True)
+ if controllers:
+ for controller in controllers:
+ if int(controller) == int(options.controller):
+ controller_physicaldrives = self.auxcommands["storagecontroller"].physical_drives(
+ options, controllers[controller], single_use=True
+ )
+
+ if not args and not options.all:
+ raise InvalidCommandLineError("You must include a physical drive to sanitize.")
+ elif not options.controller:
+ raise InvalidCommandLineError("You must include a controller to select.")
+ else:
+ if len(args) > 1:
+ physicaldrives = args
+ elif len(args) == 1:
+ physicaldrives = args[0].replace(", ", ",").split(",")
+ else:
+ physicaldrives = None
+
+ controllist = []
+
+ try:
+ if ilo_ver >= 6.110:
+ if options.controller.isdigit():
+ slotlocation = self.storageget_location_from_id(options.controller, options.storageid)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip().strip("=")
+ for control in controllers.values():
+ if "Location" in control and slotcontrol.lower() == control["Location"]["PartLocation"][
+ "ServiceLabel"
+ ].lower().split("slot")[-1].lstrip().strip("="):
+ controllist.append(control)
+ elif "slot" in options.controller.lower():
+ slotlocation = options.controller
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip().strip("=")
+ for control in controllers.values():
+ if "Location" in control and slotcontrol.lower() == control["Location"]["PartLocation"][
+ "ServiceLabel"
+ ].lower().split("slot")[-1].lstrip().strip("="):
+ controllist.append(control)
+ if not controllist:
+ raise InvalidCommandLineError("")
+ else:
+ if options.controller.isdigit():
+ slotlocation = self.get_location_from_id(options.controller)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip()
+ for control in content:
+ if slotcontrol.lower() == control["Location"].lower().split("slot")[-1].lstrip():
+ controllist.append(control)
+ if not controllist:
+ raise InvalidCommandLineError("")
+ except InvalidCommandLineError:
+ raise InvalidCommandLineError("Selected controller not found in the current inventory " "list.")
+
+ if ilo_ver >= 6.110:
+ if options.status:
+ if options.all:
+ for drive in controller_physicaldrives.values():
+ pdrive_inilo = self.convertloc(drive["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+ if len(drive["Operations"]) != 0:
+ self.rdmc.ui.printer(
+ "The drive is in %s state, %s percent complete.\n"
+ % (
+ drive["Operations"][0]["OperationName"],
+ drive["Operations"][0]["PercentageComplete"],
+ )
+ )
+ if int(drive["Operations"][0]["PercentageComplete"]) == 100:
+ self.rdmc.ui.printer("You can reset %s using --drivereset now.\n" % (pdrive_inilo))
+ elif len(drive["Operations"]) == 0:
+ mr_ct = drive["Status"]["State"]
+ self.rdmc.ui.printer("Sanitization is completed and updated drive status = %s\n" % (mr_ct))
+ else:
+ self.rdmc.ui.printer("Sanitization failed for drive %s.\n" % (pdrive_inilo))
+ else:
+ for drive in controller_physicaldrives.values():
+ pdrive_inilo = self.convertloc(drive["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+ for userdrive in physicaldrives:
+ if userdrive == pdrive_inilo:
+ if len(drive["Operations"]) != 0:
+ self.rdmc.ui.printer(
+ "The drive is in %s state, %s percent complete.\n"
+ % (
+ drive["Operations"][0]["OperationName"],
+ drive["Operations"][0]["PercentageComplete"],
+ )
+ )
+ if int(drive["Operations"][0]["PercentageComplete"]) == 100:
+ self.rdmc.ui.printer("You can reset %s using --drivereset now.\n" % (userdrive))
+ elif len(drive["Operations"]) == 0:
+ mr_ct = drive["Status"]["State"]
+ self.rdmc.ui.printer(
+ "Sanitization is completed and updated drive status = %s\n" % (mr_ct)
+ )
+
+ else:
+ self.rdmc.ui.printer("Sanitization failed for drive %s.\n" % (userdrive))
+ elif options.drivereset:
+ if options.all:
+ for drive in controller_physicaldrives.values():
+ pdrive_inilo = self.convertloc(drive["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+ if "Actions" in drive:
+ if "#Drive.Reset" in drive["Actions"]:
+ path = drive["@odata.id"] + "/Actions/Drive.Reset"
+ contentsholder = {"ResetType": "ForceOn"}
+ self.rdmc.ui.printer("DriveReset path and payload: %s, %s\n" % (path, contentsholder))
+ self.rdmc.app.post_handler(path, contentsholder)
+ else:
+ if len(drive["Operations"]) != 0:
+ self.rdmc.ui.printer(
+ "Sanitization is in progress for drive %s. Use --status to check it's status.\n"
+ % (pdrive_inilo)
+ )
+ else:
+ self.rdmc.ui.printer(
+ "Drive sanitization is not being performed on %s\n" % (pdrive_inilo)
+ )
+ else:
+ self.rdmc.ui.printer("Drive sanitization is not being performed on %s\n" % (pdrive_inilo))
+ else:
+ for drive in controller_physicaldrives.values():
+ pdrive_inilo = self.convertloc(drive["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+ for userdrive in physicaldrives:
+ if userdrive == pdrive_inilo:
+ if "Actions" in drive:
+ if "#Drive.Reset" in drive["Actions"]:
+ path = drive["@odata.id"] + "/Actions/Drive.Reset"
+ contentsholder = {"ResetType": "ForceOn"}
+ self.rdmc.ui.printer(
+ "DriveReset path and payload: %s, %s\n" % (path, contentsholder)
+ )
+ self.rdmc.app.post_handler(path, contentsholder)
+ else:
+ if len(drive["Operations"]) != 0:
+ self.rdmc.ui.printer(
+ "Santization is in progress for drive %s. Use --status to check its "
+ "status.\n" % (pdrive_inilo)
+ )
+ else:
+ self.rdmc.ui.printer(
+ "Drive sanitization is not being performed on %s\n" % (pdrive_inilo)
+ )
+ else:
+ self.rdmc.ui.printer(
+ "Drive sanitization is not being performed on %s\n" % (pdrive_inilo)
+ )
+ else:
+ if self.storagesanitizedrives(physicaldrives, controller_physicaldrives, options.all):
+ if options.reboot:
+ self.auxcommands["reboot"].run("ColdBoot")
+ self.rdmc.ui.printer("Preparing for sanitization...\n")
+ self.monitorsanitization()
+
+ else:
+ if options.status is True or options.drivereset is True:
+ raise InvalidCommandLineError("--status and --drivereset options are not supported on iLO 5\n")
+ if self.sanitizedrives(
+ controllist,
+ physicaldrives,
+ controller_physicaldrives,
+ options.mediatype,
+ options.all,
+ ):
+ if options.reboot:
+ self.auxcommands["reboot"].run("ColdBoot")
+ self.rdmc.ui.printer("Preparing for sanitization...\n")
+ self.monitorsanitization()
+ else:
+ self.rdmc.ui.printer("Sanitization will occur on the next system reboot.\n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def get_location_from_id(self, controller_id):
+ for sel in self.rdmc.app.select("SmartStorageArrayController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller = sel.dict
+ if controller["Id"] == str(controller_id):
+ return controller["Location"]
+ return None
+
+ def storageget_location_from_id(self, controller_id, storage_id):
+ for sel in self.rdmc.app.select("StorageController", path_refresh=True):
+ if "Collection" not in sel.maj_type and storage_id in sel.dict["@odata.id"]:
+ controller = sel.dict
+ if controller["Id"] == str(controller_id):
+ return controller["Location"]["PartLocation"]["ServiceLabel"]
+ return None
+
+ def sanitizedrives(self, controllist, drivelist, controller_drives, mediatype, optall):
+ """Gets drives ready for sanitization
+
+ :param controllist: list of controllers
+ :type controllist: list.
+ :param drivelist: physical drives to sanitize
+ :type drivelist: list.
+ :param optall: flag for sanitizing all drives
+ :type optall: bool.
+ """
+ sanitizedrivelist = []
+ logicaldrivelist = []
+ changes = False
+
+ for controller in controllist:
+ pdrivelist = [x["DataDrives"] for x in controller["LogicalDrives"]]
+
+ for plist in pdrivelist:
+ for drive in plist:
+ logicaldrivelist.append(drive)
+
+ if optall:
+ sanitizedrivelist = [x["Location"] for x in controller["PhysicalDrives"]]
+ else:
+ for erasedrive in drivelist:
+ try:
+ for idx, pdrive in enumerate(controller["PhysicalDrives"]):
+ if erasedrive == pdrive["Location"]:
+ if pdrive["Location"] in logicaldrivelist:
+ raise InvalidCommandLineError(
+ "Unable to"
+ " sanitize configured drive. Remove"
+ " any volume(s) associated "
+ "with drive %s and try again." % pdrive["Location"]
+ )
+
+ # Validate Media Type
+ if not (self.validate_mediatype(erasedrive, mediatype, controller_drives)):
+ raise InvalidCommandLineError(
+ "One or more of the drives given does not match the "
+ "mediatype %s which is specified" % mediatype
+ )
+ self.rdmc.ui.printer(
+ "Setting physical drive %s " "for sanitization\n" % pdrive["Location"]
+ )
+ sanitizedrivelist.append(pdrive["Location"])
+ break
+ except KeyError as excp:
+ raise NoContentsFoundForOperationError(
+ "The property '%s' is missing " "or invalid." % str(excp)
+ )
+
+ if sanitizedrivelist:
+ changes = True
+ if mediatype == "SSD":
+ erase_pattern_string = "SanitizeRestrictedBlockErase"
+ else:
+ erase_pattern_string = "SanitizeRestrictedOverwrite"
+
+ contentsholder = {
+ "Actions": [
+ {
+ "Action": "PhysicalDriveErase",
+ "ErasePattern": erase_pattern_string,
+ "PhysicalDriveList": sanitizedrivelist,
+ }
+ ],
+ "DataGuard": "Disabled",
+ }
+
+ # self.rdmc.ui.printer(
+ # "DriveSanitize path and payload: %s, %s\n"
+ # % (controller["@odata.id"], contentsholder)
+ # )
+ patch_path = controller["@odata.id"]
+ if "settings" not in patch_path:
+ patch_path = patch_path + "settings/"
+ controller["@odata.id"] = patch_path
+
+ self.rdmc.app.patch_handler(controller["@odata.id"], contentsholder)
+
+ return changes
+
+ def convertloc(self, servicelabel):
+ loc = servicelabel.split(":")
+ temp_str = str(loc[1].split("=")[1] + ":" + loc[2].split("=")[1] + ":" + loc[3].split("=")[1])
+ return temp_str
+
+ def storagesanitizedrives(self, drivelist, controller_drives, optall):
+ sanitizedrivelist = []
+ changes = False
+ plocation = ""
+ if len(controller_drives) != 0:
+ for plist in controller_drives.values():
+ logicaldrivelist = []
+ logicaldrivelist.extend(plist["Links"]["Volumes"])
+ plocation = self.convertloc(plist["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+
+ if optall:
+ if logicaldrivelist:
+ self.rdmc.ui.printer(
+ "Unable to"
+ " sanitize drive %s. Remove"
+ " any volume(s) associated "
+ "with this drive and try again.\n" % plocation
+ )
+ break
+ sanitizedrivelist.append(plocation)
+ else:
+ for erasedrive in drivelist:
+ try:
+ if erasedrive == plocation:
+ if logicaldrivelist:
+ self.rdmc.ui.printer(
+ "Unable to"
+ " sanitize drive %s. Remove"
+ " any volume(s) associated "
+ "with this drive and try again.\n" % plocation
+ )
+ self.rdmc.ui.printer("Setting physical drive %s " "for sanitization\n" % plocation)
+ sanitizedrivelist.append(plocation)
+ break
+ except KeyError as excp:
+ raise NoContentsFoundForOperationError(
+ "The property '%s' is missing " "or invalid.\n" % str(excp)
+ )
+
+ if sanitizedrivelist:
+ changes = True
+ path = plist["@odata.id"] + "/Actions/Drive.SecureErase"
+ contentsholder = {}
+
+ self.rdmc.ui.printer("DriveSanitize path and payload: %s, %s\n" % (path, contentsholder))
+
+ self.rdmc.app.post_handler(path, contentsholder)
+ sanitizedrivelist = []
+ self.rdmc.ui.printer("You can check the sanitization status of %s using --status\n" % (plocation))
+
+ return changes
+
+ def validate_mediatype(self, erasedrive, mediatype, controller_drives):
+ """validates media type as HDD or SSD"""
+ for idx in list(controller_drives.keys()):
+ phy_drive = controller_drives[idx]
+ if phy_drive["Location"] == erasedrive and phy_drive["MediaType"] == mediatype:
+ return True
+ return False
+
+ def monitorsanitization(self):
+ """monitors sanitization percentage"""
+ # TODO: Add code to give updates on sanitization
+
+ def drivesanitizevalidation(self, options):
+ """drive sanitize validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller " "using either the slot number or index.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storageid in iLO6 only.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--mediatype",
+ dest="mediatype",
+ help="""Use this flag to select the mediatype of the hard disk """,
+ default=None,
+ required=True,
+ )
+ customparser.add_argument(
+ "--reboot",
+ dest="reboot",
+ help="Include this flag to perform a coldboot command "
+ "function after completion of operations and monitor "
+ "sanitization.",
+ action="store_true",
+ default=False,
+ )
+ customparser.add_argument(
+ "--all",
+ dest="all",
+ help="""Use this flag to sanitize all physical drives on a """ """controller.""",
+ action="store_true",
+ default=False,
+ )
+ customparser.add_argument(
+ "--drivereset",
+ dest="drivereset",
+ help="""Use this flag to reset physical drives on a """ """controller.""",
+ action="store_true",
+ default=False,
+ )
+ customparser.add_argument(
+ "--status",
+ dest="status",
+ help="""Use this flag to check sanitization status of a """ """controller.""",
+ action="store_true",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/FactoryResetControllerCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/FactoryResetControllerCommand.py
new file mode 100644
index 0000000..3d53557
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/FactoryResetControllerCommand.py
@@ -0,0 +1,223 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Factory Reset Controller Command for rdmc"""
+
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class FactoryResetControllerCommand:
+ """Factory reset controller command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "factoryresetcontroller",
+ "usage": None,
+ "description": "Run without "
+ "arguments for the current controller options.\n\texample: "
+ "factoryresetcontroller\n\n\tTo factory reset a controller "
+ "by index.\n\texample: factoryresetcontroller --controller=2"
+ '\n\texample: factoryresetcontroller --controller="Slot 1" \n\n'
+ "\tTo factory reset a controller on Gen11 server\n"
+ "\texample: factoryresetcontroller --reset_type resetall --storageid DE000100\n"
+ "\texample: factoryresetcontroller --reset_type preservevolumes --storageid DE000100\n",
+ "summary": "Factory resets a controller by index or location.",
+ "aliases": [],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main disk inventory worker function
+
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.frcontrollervalidation(options)
+
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver >= 6.110:
+ if not options.storageid:
+ raise InvalidCommandLineError(
+ "--storageid option is mandatory for iLO6 along with --controller option.\n"
+ )
+ if (
+ (options.controller is not None)
+ and (options.storageid is not None)
+ and (options.reset_type is not None)
+ ):
+ raise InvalidCommandLineError("--controller is not supported in iLO6.")
+
+ elif (options.storageid is not None) and (options.reset_type is not None):
+ st_content = []
+ self.auxcommands["select"].selectfunction("StorageCollection.")
+ get_content = self.rdmc.app.getprops()
+ for st_controller in get_content:
+ path = st_controller["Members"]
+ for mem in path:
+ for val in mem.values():
+ if "DE" in val and options.storageid in val:
+ getval = self.rdmc.app.get_handler(val, silent=True, service=True).dict
+ if options.storageid:
+ if getval["Id"] == options.storageid:
+ st_content.append(getval)
+ else:
+ raise InvalidCommandLineError(
+ "Selected storage id not found in the current inventory " "list."
+ )
+ else:
+ st_content.append(getval)
+ for st_controller in st_content:
+ path = st_controller["Actions"]["#Storage.ResetToDefaults"]["target"]
+
+ if options.reset_type.lower() == "resetall":
+ actionitem = "ResetAll"
+ body = {"ResetType": actionitem}
+ elif options.reset_type.lower() == "preservevolumes":
+ actionitem = "PreserveVolumes"
+ body = {"ResetType": actionitem}
+ else:
+ raise InvalidCommandLineError("Invalid command.")
+
+ # self.rdmc.ui.printer(
+ # "FactoryReset path and payload: %s, %s\n"
+ # % (path, body)
+ # )
+ self.rdmc.app.post_handler(path, body)
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ elif (options.storageid is None) or (options.reset_type is None):
+ raise InvalidCommandLineError("Invalid command\n" "--reset_type and --storageid is required")
+
+ else:
+ self.auxcommands["select"].selectfunction("SmartStorageConfig.")
+ content = self.rdmc.app.getprops()
+ if options.controller and options.reset_type is None and options.storageid is None:
+ controllist = []
+
+ try:
+ if options.controller and options.controller.isdigit():
+ slotlocation = self.get_location_from_id(options.controller)
+ if slotlocation:
+ slotcontrol = slotlocation.lower().strip('"').split("slot")[-1].lstrip()
+ for control in content:
+ if slotcontrol.lower() == control["Location"].lower().split("slot")[-1].lstrip():
+ controllist.append(control)
+ # else:
+ # self.parser.print_help()
+ # return ReturnCodes.SUCCESS
+ if not controllist:
+ raise InvalidCommandLineError("")
+ except InvalidCommandLineError:
+ raise InvalidCommandLineError("Selected controller not found in the current inventory " "list.")
+ for controller in controllist:
+ contentsholder = {
+ "Actions": [{"Action": "FactoryReset"}],
+ "DataGuard": "Disabled",
+ }
+ self.rdmc.ui.printer(
+ "FactoryReset path and payload: %s, %s\n" % (controller["@odata.id"], contentsholder)
+ )
+ self.rdmc.app.patch_handler(controller["@odata.id"], contentsholder)
+
+ elif options.controller and (options.reset_type is not None) or (options.storageid is not None):
+ raise InvalidCommandLineError(
+ "Invalid command\n" "--reset_type and --storageid is not supported in iLO5"
+ )
+
+ for idx, val in enumerate(content):
+ self.rdmc.ui.printer("[%d]: %s\n" % (idx, val["Location"]))
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def get_location_from_id(self, controller_id):
+ for sel in self.rdmc.app.select("SmartStorageArrayController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller = sel.dict
+ if controller["Id"] == str(controller_id):
+ return controller["Location"]
+ return None
+
+ def frcontrollervalidation(self, options):
+ """Factory reset controller validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller " "using either the slot number or index.",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--reset_type",
+ dest="reset_type",
+ help="Use this flag to pass the reset type for storage controller in iLO6 ",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding storage id in iLO6",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/StorageControllerCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/StorageControllerCommand.py
new file mode 100644
index 0000000..961af60
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/StorageControllerCommand.py
@@ -0,0 +1,2200 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Storage Controller Command for rdmc"""
+import json
+import sys
+from argparse import RawDescriptionHelpFormatter
+
+import redfish
+
+try:
+ from rdmc_helper import (
+ UI,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ UI,
+ InvalidFileInputError,
+ Encryption,
+ )
+
+from redfish.ris.resp_handler import ResponseHandler
+
+# from redfish.ris.utils import iterateandclear
+
+__config_file__ = "storagecontroller_config.json"
+
+# from rdmc_base_classes import HARDCODEDLIST
+
+__subparsers__ = ["load", "save", "state"]
+
+
+class StorageControllerCommand:
+ """Storage controller command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "storagecontroller",
+ "usage": None,
+ "description": "\tTo Manage PLDM for RDE storage devices. For other storage devices (i.e NVMe),"
+ "use the `select type/get/set` paradigm or raw commands.\n\n"
+ "\tRun without arguments for the "
+ "current list of Storage controllers.\n\texample: "
+ "storagecontroller\n\n\tTo get more details on a specific controller "
+ "select it by index Note: On ILO6, --storageid is mandatory along with --controller.\n\t"
+ "example: storagecontroller --storageid=DE00E000 --controller=2"
+ "\n\n\tTo get more details on a specific controller select "
+ 'it by location.\n\texample: storagecontroller --storageid=DE00E000 --controller "Slot 0"'
+ "\n\n\tIn order to get a list of all physical drives for "
+ "each controller.\n\texample: storagecontroller --physicaldrives"
+ "\n\n\tTo obtain details about physical drives for a "
+ "specific controller.\n\texample: storagecontroller --storageid=DE00E000 --controller=3 "
+ "--physicaldrives\n\n\tTo obtain details about a specific "
+ "physical drive for a specific controller.\n\texample: storagecontroller "
+ "--storageid=DE00E000 --controller=3 --pdrive=1I:1:1\n\n\tIn order to get a list of "
+ "all volumes for the each controller.\n\texample: "
+ "storagecontroller --logicaldrives\n\n\tTo obtain details about "
+ "volumes for a specific controller.\n\texample: "
+ "storagecontroller --storageid=DE00E000 --controller=3 --logicaldrives\n\n\tTo obtain "
+ "details about a specific volume for a specific "
+ "controller.\n\texample: storagecontroller --storageid=DE00E000 --controller=3 --ldrive=1\n\n\tTo obtain "
+ "details about a specific Storage id for a specific "
+ "controllers.\n\texample: storagecontroller --storageid=DE00E000\n",
+ "summary": "Discovers all storage controllers installed in the " "server and managed by the SmartStorage.",
+ "aliases": ["smartarray"],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.config_file = None
+ self.fdata = None
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def file_handler(self, filename, operation, options, data=None, sk=None):
+ """
+ Wrapper function to read or write data to a respective file
+
+ :param data: data to be written to output file
+ :type data: container (list of dictionaries, dictionary, etc.)
+ :param file: filename to be written
+ :type file: string (generally this should be self.clone_file or tmp_clone_file
+ :param operation: file operation to be performed
+ :type operation: string ('w+', 'a+', 'r+')
+ :param sk: sort keys flag
+ :type sk: boolean
+ :param options: command line options
+ :type options: attribute
+ :returns: json file data
+ """
+ writeable_ops = ["w", "w+", "a", "a+"]
+ fdata = None
+
+ try:
+ if operation in writeable_ops:
+ if getattr(options, "encryption", False):
+ with open(filename, operation + "b") as outfile:
+ outfile.write(
+ Encryption().encrypt_file(
+ json.dumps(
+ data,
+ indent=2,
+ cls=redfish.ris.JSONEncoder,
+ sort_keys=sk,
+ ),
+ getattr(options, "encryption", False),
+ )
+ )
+ else:
+ with open(filename, operation) as outfile:
+ outfile.write(
+ json.dumps(
+ data,
+ indent=2,
+ cls=redfish.ris.JSONEncoder,
+ sort_keys=sk,
+ )
+ )
+ else:
+ if getattr(options, "encryption", False):
+ with open(filename, operation + "b") as file_handle:
+ fdata = json.loads(
+ Encryption().decrypt_file(
+ file_handle.read(),
+ getattr(options, "encryption", False),
+ )
+ )
+ else:
+ with open(filename, operation) as file_handle:
+ fdata = json.loads(file_handle.read())
+ return fdata
+ except Exception as excp:
+ raise InvalidFileInputError(
+ "Unable to open file: %s.\nVerify the file location " "and the file has a valid JSON format.\n" % excp
+ )
+
+ def controller_id(self, options):
+ """
+ Get iLO types from server and save storageclone URL get Controller
+ :parm options: command line options
+ :type options: attribute
+ :returns: returns list
+ """
+ self.auxcommands["select"].selectfunction("StorageControllerCollection.")
+ ctr_content = self.rdmc.app.getprops()
+ ctrl_data = []
+ all_ctrl = dict()
+ for ct_controller in ctr_content:
+ path = ct_controller["Members"]
+ for i in path:
+ res = i["@odata.id"]
+ ctrl_data.append(res)
+ ctrl_id_url = res + "?$expand=."
+ get_ctr = self.rdmc.app.get_handler(ctrl_id_url, silent=True, service=True).dict
+ _ = get_ctr["@odata.id"].split("/")
+ get_ctr = self.rdmc.app.removereadonlyprops(get_ctr, False, True)
+ all_ctrl[get_ctr["Name"]] = get_ctr
+ return all_ctrl
+
+ def get_volume(self, options):
+ """
+ Get iLO types from server and save storageclone URL get volumes
+ :parm options: command line options
+ :type options: attribute
+ :returns: returns list
+ """
+ self.auxcommands["select"].selectfunction("VolumeCollection.")
+ vol_content = self.rdmc.app.getprops()
+ vol_data = []
+ all_vol = dict()
+ for st_volume in vol_content:
+ path = st_volume["Members"]
+ for i in path:
+ res = i["@odata.id"]
+ if self.rdmc.opts.verbose:
+ sys.stdout.write("Saving properties of type %s \t\n" % res)
+ vol_data.append(res)
+ vol_id_url = res + "?$expand=."
+ get_vol = self.rdmc.app.get_handler(vol_id_url, silent=True, service=True).dict
+ # print("Assigned drive", get_vol["Links"]["Drives"])
+ get_vol = self.rdmc.app.removereadonlyprops(get_vol, False, True)
+ all_vol[get_vol["Name"]] = get_vol
+ return all_vol
+
+ def run(self, line, help_disp=False):
+ """Main sstorage controller worker function
+ :param help_disp: command line input
+ :type help_disp: bool.
+ :param line: command line input
+ :type line: string.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ ident_subparser = False
+ for cmnd in __subparsers__:
+ if cmnd in line:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ ident_subparser = True
+ break
+ if not ident_subparser:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line, default=True)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.storagecontrollervalidation(options)
+ ilo_ver = self.rdmc.app.getiloversion()
+
+ if options.command == "state":
+ if not options.storageid:
+ raise InvalidCommandLineError("Please provide --storageid option.\n")
+ if ilo_ver >= 6:
+ storage_ctlr = self.storagecontroller(options, print_ctrl=False, single_use=True)
+ for storage in storage_ctlr:
+ if storage_ctlr[storage].get("@Redfish.Settings"):
+ time = storage_ctlr[storage]["@Redfish.Settings"].get("Time", "Not Available")
+ sys.stdout.write("Last Configuration Attempt: %s\n" % str(time))
+ for message in storage_ctlr[storage]["@Redfish.Settings"].get("Messages", []):
+ ResponseHandler(
+ self.rdmc.app.validationmanager,
+ self.rdmc.app.typepath.defs.messageregistrytype,
+ ).message_handler(
+ response_data=message,
+ message_text="",
+ verbosity=2,
+ dl_reg=False,
+ )
+ else:
+ sys.stdout.write(
+ "Previous storage controller configuration status messages are "
+ "not available for controller '%s'\n" % (storage)
+ )
+ ctrl_data = dict()
+ ctrl_data.update({"Location": storage_ctlr[storage].get("Location", "Unknown")})
+ ctrl_data.update({"Model": storage_ctlr[storage].get("Model", "Unknown")})
+ UI().print_out_json(ctrl_data)
+
+ else:
+ controllers = self.controllers(options, print_ctrl=False, single_use=True)
+ for controller in controllers:
+ if controllers[controller].get("@Redfish.Settings"):
+ time = controllers[controller]["@Redfish.Settings"].get("Time", "Not Available")
+ sys.stdout.write("Last Configuration Attempt: %s\n" % str(time))
+ for message in controllers[controller]["@Redfish.Settings"].get("Messages", []):
+ ResponseHandler(
+ self.rdmc.app.validationmanager,
+ self.rdmc.app.typepath.defs.messageregistrytype,
+ ).message_handler(
+ response_data=message,
+ message_text="",
+ verbosity=2,
+ dl_reg=False,
+ )
+ else:
+ sys.stdout.write(
+ "Previous storage controller configuration status messages are "
+ "not available for controller '%s'\n" % (controller)
+ )
+ ctrl_data = dict()
+ ctrl_data.update({"Location": controllers[controller].get("Location", "Unknown")})
+ ctrl_data.update({"Model": controllers[controller].get("Model", "Unknown")})
+ UI().print_out_json(ctrl_data)
+
+ if options.command == "save":
+ if ilo_ver >= 6.110:
+ storage = {}
+ all_stgcntrl = {}
+ st_content = self.rdmc.app.get_handler("/redfish/v1/Systems/1/Storage/", silent=True, service=True).dict
+ sel = st_content["Members"]
+ for i in sel:
+ res = i["@odata.id"]
+ if "DE" in res:
+ storage_id_url = res + "?$expand=."
+ getval = self.rdmc.app.get_handler(storage_id_url, silent=True, service=True).dict
+ vol = self.get_volume(options)
+ ctr = self.controller_id(options)
+ getval = self.rdmc.app.removereadonlyprops(getval, False, True)
+ storage.update(getval)
+ getval["Controllers"]["Members"].append(ctr)
+ del getval["Controllers"]["Members"][0]
+ getval["Volumes"]["Members"].append(vol)
+ del getval["Volumes"]["Members"][0]
+ all_stgcntrl[getval["Id"]] = getval
+ self.file_handler(self.config_file, "w", options, all_stgcntrl, sk=True)
+ sys.stdout.write("Storage Controller configuration saved to '%s'.\n" % self.config_file)
+ else:
+ st_content = self.rdmc.app.get_handler("/redfish/v1/Systems/1/Storage/", silent=True, service=True).dict
+ sel = st_content["Members"]
+ for i in sel:
+ res = i["@odata.id"]
+ if "DE" in res:
+ break
+ else:
+ continue
+ storage = {}
+ all_stgcntrl = {}
+ storage_id_url = res + "?$expand=."
+ getval = self.rdmc.app.get_handler(storage_id_url, silent=True, service=True).dict
+ vol = self.get_volume(options)
+ ctr = self.controller_id(options)
+ getval = self.rdmc.app.removereadonlyprops(getval, False, True)
+ storage.update(getval)
+ getval["Controllers"]["Members"].append(ctr)
+ del getval["Controllers"]["Members"][0]
+ getval["Volumes"]["Members"].append(vol)
+ del getval["Volumes"]["Members"][0]
+ all_stgcntrl[getval["Id"]] = getval
+ self.file_handler(self.config_file, "w", options, all_stgcntrl, sk=True)
+ sys.stdout.write("Storage Controller configuration saved to '%s'.\n" % self.config_file)
+ controllers = self.controllers(options, print_ctrl=False, single_use=True)
+ for key, controller in controllers.items():
+ physical_drives = self.physical_drives(options, controller, print_ctrl=False, single_use=True)
+ logical_drives = self.logical_drives(options, controller, print_ctrl=False, single_use=True)
+ if self.rdmc.app.typepath.defs.isgen10:
+ for drivec_idx, drivec in enumerate(controller.get("PhysicalDrives", [])):
+ for drive in physical_drives:
+ if drivec["Location"] == physical_drives[drive]["Location"]:
+ controller["PhysicalDrives"][drivec_idx].update(physical_drives[drive])
+ break
+ for drivec_idx, drivec in enumerate(controller.get("LogicalDrives", [])):
+ for drive in logical_drives:
+ if drivec["LogicalDriveNumber"] == logical_drives[drive]["Id"]:
+ controller["LogicalDrives"][drivec_idx].update(logical_drives[drive])
+ break
+ if controller.get("Links"):
+ controller["Links"]["LogicalDrives"] = logical_drives
+ controller["Links"]["PhysicalDrives"] = physical_drives
+ else:
+ controller["links"]["LogicalDrives"] = logical_drives
+ controller["links"]["PhysicalDrives"] = physical_drives
+
+ self.file_handler(self.config_file, "w", options, controllers, sk=True)
+ sys.stdout.write("Storage Controller configuration saved to '%s'.\n" % self.config_file)
+
+ if options.command == "load":
+ if ilo_ver >= 6.110:
+ storage_load = self.file_handler(self.config_file, operation="rb", options=options)
+ for starageid in storage_load:
+
+ controller_id = storage_load[starageid]["Controllers"]
+ logical_id = storage_load[starageid]["Volumes"]
+ physical_id = storage_load[starageid]["Drives"]
+
+ if controller_id:
+ if "Members" in controller_id:
+ id_controller = "/redfish/v1/Systems/1/Storage/" + starageid
+ controllers = self.rdmc.app.get_handler(id_controller, silent=True).dict
+ readonly_removed_controllers = self.storagecontrollerremovereadonly(controllers)
+ readonly_removed = controller_id
+ self.load_property_helper(readonly_removed, readonly_removed_controllers, id_controller)
+ else:
+ for data in controller_id:
+ id_controller = controller_id[data].get("@odata.id")
+ controllers = self.rdmc.app.get_handler(id_controller, silent=True).dict
+ readonly_removed_controllers = self.storagecontrollerremovereadonly(controllers)
+ readonly_removed = storage_load["Controllers"]
+ for controller, val in readonly_removed.items():
+ for i in readonly_removed_controllers:
+ try:
+ if (i in val) and (controllers[i] != val[i]):
+ body = {str(i): val[i]}
+ self.rdmc.app.patch_handler(id_controller, body)
+ except:
+ sys.stdout.write("Unable to update the property " "for: '%s'.\n" % i)
+ continue
+ if logical_id:
+ if "Members" in logical_id:
+ id_logical = "/redfish/v1/Systems/1/Storage/" + starageid + "/Volumes/1"
+ logicals = self.rdmc.app.get_handler(id_logical, silent=True).dict
+ readonly_removed_logical = self.storagecontrollerremovereadonly(logicals)
+ readonly_logical = logical_id
+ self.load_property_helper(readonly_logical, readonly_removed_logical, id_logical)
+ else:
+ for logical_data in logical_id:
+ id_logical = logical_id[logical_data].get("@odata.id")
+ logicals = self.rdmc.app.get_handler(id_logical, silent=True).dict
+ readonly_removed_logical = self.storagecontrollerremovereadonly(logicals)
+ readonly_logical = storage_load["Volumes"]
+ for logical, val in readonly_logical.items():
+ for lg in readonly_removed_logical:
+ try:
+ if (lg in val) and (logicals[lg] != val[lg]):
+ body = {str(lg): val[lg]}
+ self.rdmc.app.patch_handler(id_logical, body)
+ except:
+ sys.stdout.write("Unable to update the property " "for: '%s'.\n" % lg)
+ continue
+ if physical_id:
+ if "Members" in physical_id:
+ id_physical = "/redfish/v1/Systems/1/Storage/" + starageid + "/Drives"
+ physicals = self.rdmc.app.get_handler(id_physical, silent=True).dict
+ readonly_removed_physical = self.storagecontrollerremovereadonly(physicals)
+ readonly_physical = physical_id
+ self.load_property_helper(readonly_physical, readonly_removed_physical, id_physical)
+ else:
+ for physical_data in physical_id:
+ id_physical = physical_id[physical_data].get("@odata.id")
+ physicals = self.rdmc.app.get_handler(id_physical, silent=True).dict
+ readonly_removed_physical = self.storagecontrollerremovereadonly(physicals)
+ readonly_physical = storage_load["Drives"]
+ for logical, val in readonly_physical.items():
+ for lg in readonly_removed_physical:
+ try:
+ if (lg in val) and (physicals[lg] != val[lg]):
+ body = {str(lg): val[lg]}
+ self.rdmc.app.patch_handler(id_physical, body)
+ except:
+ sys.stdout.write("Unable to update the property " "for: '%s'.\n" % lg)
+ continue
+ sys.stdout.write(
+ "Storage Controller configuration loaded. Reboot the server to " "finalize the configuration.\n"
+ )
+
+ else:
+ controllers = self.file_handler(self.config_file, operation="rb", options=options)
+
+ for controller in controllers:
+ put_path = controllers[controller]["@Redfish.Settings"]["SettingsObject"]["@odata.id"]
+ if controllers[controller].get("DataGuard"):
+ controllers[controller]["DataGuard"] = "Disabled"
+ readonly_removed = self.rdmc.app.removereadonlyprops(controllers[controller])
+ readonly_removed["LogicalDrives"] = controllers[controller]["LogicalDrives"]
+ readonly_removed["PhysicalDrives"] = controllers[controller]["PhysicalDrives"]
+ # self.rdmc.app.put_handler(controllers[controller]["@odata.id"], readonly_removed)
+ self.rdmc.app.put_handler(put_path, readonly_removed)
+
+ sys.stdout.write(
+ "Storage Controller configuration loaded. Reboot the server to "
+ "finalize the configuration.\n"
+ )
+ elif options.command == "default":
+ if ilo_ver >= 6:
+ if options.storageid and not options.controller:
+ self.storageid(options, print_ctrl=True, single_use=False)
+ elif options.storageid and options.controller:
+ self.storagecontroller(options, print_ctrl=True, single_use=False)
+ elif options.controller and not options.storageid:
+ raise InvalidCommandLineError(
+ "with --controller option, --storageid option is mandatory for 'iLO 6' and 'iLO 5'.\n"
+ )
+ elif (
+ options.physicaldrives or options.pdrive or options.logicaldrives or options.ldrive
+ ) and not options.storageid:
+ raise InvalidCommandLineError("--storageid option is mandatory for 'iLO 6' and 'iLO 5'.\n")
+ elif (
+ not options.storageid
+ and not options.controller
+ and not options.physicaldrives
+ and not options.pdrive
+ and not options.logicaldrives
+ and not options.ldrive
+ ):
+ self.list_all_storages(options, print_ctrl=True, single_use=False)
+ else:
+ self.controllers(options, print_ctrl=True, single_use=False)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def load_property_helper(self, readonly, readonly_removed, id):
+ for item, val in readonly.items():
+ for lg in readonly_removed:
+ try:
+ if (lg in val) and (item[lg] != val[lg]):
+ body = {str(lg): val[lg]}
+ self.rdmc.app.patch_handler(id, body)
+ except:
+ sys.stdout.write("Unable to update the property " "for: '%s'.\n" % lg)
+ continue
+
+ def list_all_storages(self, options, print_ctrl=False, single_use=False):
+ # self.auxcommands["select"].selectfunction("StorageCollection.")
+ st_url = "/redfish/v1/Systems/1/Storage/"
+ st_content = self.rdmc.app.get_handler(st_url, silent=True, service=True)
+ if "error" not in st_content.dict:
+ if st_content.dict["Members@odata.count"] == 0:
+ raise InvalidCommandLineError("Redfish Enabled Controllers not found in this server.\n")
+ if st_content.dict["Members"]:
+ st_content = st_content.dict["Members"]
+
+ if "json" in options and not options.json:
+ if self.rdmc.opts.verbose:
+ sys.stdout.write("---------------------------------\n")
+ sys.stdout.write("List of RDE storage devices\n")
+ sys.stdout.write("---------------------------------\n")
+ if options.json:
+ outjson = dict()
+ for st_controller in st_content:
+ for val in st_controller.values():
+ if "DE" in val:
+ st = self.rdmc.app.get_handler(val, silent=True, service=True).dict
+ health = ""
+ if "Health" in st["Status"]:
+ health = st["Status"]["Health"]
+ elif "HealthRollup" in st["Status"]:
+ health = st["Status"]["HealthRollup"]
+ if options.json:
+ outjson[st["Id"]] = dict()
+ outjson[st["Id"]]["Name"] = st["Name"]
+ outjson[st["Id"]]["Health"] = health
+ outjson[st["Id"]]["State"] = st["Status"]["State"]
+ else:
+ sys.stdout.write(
+ "%s: %s: Health %s: %s\n" % (st["Id"], st["Name"], health, st["Status"]["State"])
+ )
+
+ if options.json:
+ UI().print_out_json(outjson)
+ else:
+ raise InvalidCommandLineError(
+ "Storage controllers are not ready , Kindly re run the command after sometime\n"
+ )
+ else:
+ raise InvalidCommandLineError(
+ "Storage controllers are not ready , " "Kindly re run the command after sometime\n"
+ )
+
+ def storagecontrollerremovereadonly(self, controller):
+ templist = [
+ "Ports",
+ "CacheSummary",
+ "SupportedRAIDTypes",
+ "PCIeInterface",
+ "SupportedControllerProtocols",
+ "SupportedDeviceProtocols",
+ "SKU",
+ "SpeedGbps",
+ "SerialNumber",
+ "PartNumber",
+ "Model",
+ "@odata.etag",
+ "Status",
+ "Manufacturer",
+ "Location",
+ "Identifiers",
+ "FirmwareVersion",
+ "Name",
+ "Id",
+ "@odata.type",
+ "@odata.id",
+ "Links",
+ ]
+
+ remove_data = self.rdmc.app.removereadonlyprops(controller, False, True, templist)
+ return remove_data
+
+ def odataremovereadonly(self, tmp):
+ templist = [
+ "@odata.etag",
+ "@odata.type",
+ "@odata.context",
+ ]
+ remove_data = self.rdmc.app.removereadonlyprops(tmp, False, True, templist)
+ return remove_data
+
+ def controllers(self, options, print_ctrl=False, single_use=False):
+ """
+ Identify/parse volumes (and child properties) of a parent array controller
+
+ :param options: command line options (options.controller is a required attribute)
+ :type options: object attributes
+ :param print_ctrl: flag for console print enablement/disablement (default disabled)
+ :type print_ctrl: bool
+ :param single_use: singular usage - True or explore mode - False,
+ returns dictionary of results to calling function (default disabled) - see returns.
+ :type single_use: bool
+ :returns: None, dictionary of all controllers identified by 'Id'
+ """
+ no_need_ilo5 = False
+ if getattr(options, "controller", False):
+ controller_ident = False
+
+ if (
+ not (getattr(options, "json", False))
+ and not (getattr(options, "controller", False))
+ and print_ctrl
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.logicaldrives is None)
+ and (options.ldrive is None)
+ ):
+ if not options.storageid and not options.controllers:
+ sys.stdout.write("---------------------------------\n")
+ sys.stdout.write("List of RDE storage devices\n")
+ sys.stdout.write("---------------------------------\n")
+
+ st_flag = False
+ storage_data = {}
+ get_contrller = []
+ list_storageid = self.rdmc.app.get_handler("/redfish/v1/Systems/1/Storage/", silent=True, service=True)
+ if "error" not in list_storageid.dict:
+ if list_storageid.dict["Members@odata.count"] == 0:
+ raise InvalidCommandLineError("Redfish Enabled Controllers not found in this server.\n")
+ if len(list_storageid.dict["Members"]) > 0:
+ list_storageid = list_storageid.dict["Members"]
+ for val in list_storageid:
+ storageid_fromurl = val["@odata.id"]
+ # storageid_fromurl = val.split("/")[-2]
+ try:
+ stdout = "DE" in storageid_fromurl and options.storageid in storageid_fromurl
+ except:
+ stdout = "DE" in storageid_fromurl
+ if "DE" in storageid_fromurl and stdout:
+ st_flag = True
+ getval = self.rdmc.app.get_handler(storageid_fromurl, silent=True, service=True).dict
+ try:
+ controller = getval["Controllers"]
+ volumes = getval["Volumes"]
+ list_controllers = self.rdmc.app.get_handler(
+ controller["@odata.id"], silent=True, service=True
+ ).dict
+ list_volumes = self.rdmc.app.get_handler(
+ volumes["@odata.id"], silent=True, service=True
+ ).dict
+ except KeyError:
+ self.rdmc.ui.printer("Please check controller was Gen11.\n")
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+
+ for ctl in list_controllers["Members"]:
+ ctl = ctl["@odata.id"]
+ get_sel = self.rdmc.app.get_handler(ctl, silent=True, service=True)
+ get_contrller.append(get_sel)
+ else:
+ continue
+ if not st_flag:
+ raise InvalidCommandLineError(
+ "Storage ID {} not found or Storage ID is not Redfish enabled "
+ "and does not have DExxxxxx\n".format(options.storageid)
+ )
+ return
+ no_need_ilo5 = True
+ outjson = dict()
+ for sel in get_contrller:
+ if "Collection" not in sel.path:
+ controller = sel.dict
+ storage_id = controller["@odata.id"].split("/")
+ storage_id = storage_id[6]
+ if getattr(options, "controller", False):
+ if (
+ getattr(options, "controller", False) == controller["Id"]
+ or getattr(options, "controller", False)[-1]
+ == controller["Location"]["PartLocation"]["ServiceLabel"][-1]
+ ):
+ controller_ident = True
+ else:
+ continue
+ if (
+ print_ctrl
+ and not getattr(options, "controller", False)
+ and not getattr(options, "physicaldrives", False)
+ and not getattr(options, "pdrive", False)
+ and not getattr(options, "ldrive", False)
+ and not getattr(options, "logicaldrives", False)
+ and options.storageid is None
+ and not options.json
+ and not getattr(options, "controllers", False)
+ ):
+ health = ""
+ if "Health" in controller["Status"]:
+ health = controller["Status"]["Health"]
+ elif "HealthRollup" in controller["Status"]:
+ health = controller["Status"]["HealthRollup"]
+ sys.stdout.write(
+ "%s: %s: Health %s: %s\n"
+ % (storage_id, controller["Model"], health, controller["Status"]["State"])
+ )
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is None)
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.ldrive is None)
+ and (options.logicaldrives is None)
+ and options.storageid is None
+ and options.json
+ and not options.controllers
+ ):
+ tmp = dict()
+ tmp["Name"] = controller["Name"]
+ tmp["State"] = controller["Status"]["Health"]
+ tmp["Health"] = controller["Status"]["State"]
+ outjson[storage_id] = tmp
+
+ if (
+ not getattr(options, "json", False)
+ and options.storageid
+ and not getattr(options, "controllers", False)
+ and not getattr(options, "physicaldrives", False)
+ and not getattr(options, "logicaldrives", False)
+ and not getattr(options, "ldrive", False)
+ and not getattr(options, "pdrive", False)
+ and not getattr(options, "controller", False)
+ and options.command not in "state"
+ ):
+ sys.stdout.write("-----------------------------------\n")
+ sys.stdout.write("Details of Storage %s\n" % options.storageid)
+ sys.stdout.write("-----------------------------------\n")
+ sys.stdout.write("\tId: %s\n\t" % getval["Id"])
+ sys.stdout.write("Health: %s\n\t" % getval["Status"]["HealthRollup"])
+ sys.stdout.write("Name: %s\n\t" % getval["Name"])
+ sys.stdout.write("Number of Controllers: %s\n\t" % list_controllers["Members@odata.count"])
+ sys.stdout.write("Number of Volumes: %s\n\t" % list_volumes["Members@odata.count"])
+ sys.stdout.write("Number of Drives: %s\n\t\n" % getval["Drives@odata.count"])
+ if (
+ getattr(options, "json", False)
+ and options.storageid
+ and options.json
+ and not options.physicaldrives
+ and not options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ and not options.controller
+ and not options.controllers
+ ):
+ storage_data["Storage"] = dict()
+
+ storage_data["Storage"].update({"Id": getval["Id"]})
+ storage_data["Storage"].update({"Health": getval["Status"]["HealthRollup"]})
+ storage_data["Storage"].update({"Name": getval["Name"]})
+ storage_data["Storage"].update(
+ {"Number of Controllers": list_controllers["Members@odata.count"]}
+ )
+ storage_data["Storage"].update({"Number of Volumes": list_volumes["Members@odata.count"]})
+ storage_data["Storage"].update({"Number of Drives": getval["Drives@odata.count"]})
+ UI().print_out_json(storage_data)
+ elif (
+ # print_ctrl
+ getattr(options, "json", False)
+ and (options.controller is not None)
+ and (
+ (options.logicaldrives is not None)
+ or (options.physicaldrives is not None)
+ or (options.ldrive is not None)
+ or (options.pdrive is not None)
+ )
+ ):
+ pass
+
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is not None)
+ and (
+ (options.logicaldrives is not None)
+ or (options.physicaldrives is not None)
+ or (options.ldrive is not None)
+ or (options.pdrive is not None)
+ )
+ ):
+ pass
+ # controllers code with json and without json
+ elif (
+ getattr(options, "controllers", False)
+ and options.storageid
+ and options.controllers
+ and options.json is None
+ ):
+ sys.stdout.write("---------------------------------------------------\n")
+ sys.stdout.write("List of Controllers in this Storage ID %s\n" % options.storageid)
+ sys.stdout.write("---------------------------------------------------\n")
+ list_ctr = list_controllers["Members"]
+ for lst in list_ctr:
+ l_data = lst["@odata.id"]
+ get_ctr = self.rdmc.app.get_handler(l_data, silent=True, service=True).dict
+ sys.stdout.write("\tId: %s\n\t" % get_ctr["Id"])
+ sys.stdout.write("Health: %s\n\t" % get_ctr["Status"]["Health"])
+ sys.stdout.write("Name: %s\n\t" % get_ctr["Name"])
+ sys.stdout.write("FirmwareVersion: %s\n\t" % get_ctr["FirmwareVersion"])
+ sys.stdout.write("SerialNumber: %s\n\t\n" % get_ctr["SerialNumber"])
+ elif (
+ getattr(options, "json", False)
+ and options.storageid
+ and print_ctrl
+ and options.json
+ and options.controllers
+ ):
+ # sys.stdout.write("---------------------------------------------------\n")
+ # sys.stdout.write("List of Controllers in this Storage ID %s\n" % options.storageid)
+ # sys.stdout.write("---------------------------------------------------\n")
+ list_ctr = list_controllers["Members"]
+ for lst in list_ctr:
+ l_data = lst["@odata.id"]
+ get_ctr = self.rdmc.app.get_handler(l_data, silent=True, service=True).dict
+ ctr_data = dict()
+ ctr_data["Controllers"] = dict()
+ ctr_data["Controllers"].update({"Id": get_ctr["Id"]})
+ ctr_data["Controllers"].update({"Health": get_ctr["Status"]["Health"]})
+ ctr_data["Controllers"].update({"Name": get_ctr["Name"]})
+ ctr_data["Controllers"].update({"FirmwareVersion": get_ctr["FirmwareVersion"]})
+ ctr_data["Controllers"].update({"SerialNumber": get_ctr["SerialNumber"]})
+ UI().print_out_json(ctr_data)
+
+ elif (
+ getattr(options, "controller", False)
+ and print_ctrl
+ and (options.controller is not None)
+ and options.json is None
+ and not options.physicaldrives
+ and not options.pdrive
+ and not options.logicaldrives
+ and not options.ldrive
+ ):
+ controller_info = "---------------------------------------------\n"
+ controller_info += "Controller {} Details on Storage Id {}" "\n".format(
+ options.controller, storage_id
+ )
+ controller_info += "---------------------------------------------\n"
+ controller_info += "Id: %s\n" % controller["Id"]
+ controller_info += "StorageId: %s\n" % storage_id
+ controller_info += "Name: %s\n" % controller["Name"]
+ controller_info += "FirmwareVersion: %s\n" % controller["FirmwareVersion"]
+ controller_info += "Manufacturer: %s\n" % controller["Manufacturer"]
+ controller_info += "Model: %s\n" % controller["Model"]
+ controller_info += "PartNumber: %s\n" % controller["PartNumber"]
+ controller_info += "SerialNumber: %s\n" % controller["SerialNumber"]
+ try:
+ controller_info += "SKU: %s\n" % controller["SKU"]
+ except KeyError:
+ pass
+ controller_info += "Status: %s\n" % controller["Status"]
+ try:
+ controller_info += (
+ "SupportedDeviceProtocols: %s\n" % controller["SupportedDeviceProtocols"]
+ )
+ except KeyError:
+ pass
+ try:
+ controller_info += (
+ "SupportedControllerProtocols: %s\n" % controller["SupportedControllerProtocols"]
+ )
+ except KeyError:
+ pass
+ self.rdmc.ui.printer(controller_info, verbose_override=True)
+
+ elif getattr(options, "json", False) and (options.controller is not None):
+ outjson_data = dict()
+ outjson_data.update({"Id": controller["Id"]})
+ outjson_data.update({"StorageId": storage_id})
+ outjson_data.update({"Name": controller["Name"]})
+ outjson_data.update({"FirmwareVersion": controller["FirmwareVersion"]})
+ outjson_data.update({"Manufacturer": controller["Manufacturer"]})
+ outjson_data.update({"Model": controller["Model"]})
+ outjson_data.update({"PartNumber": controller["PartNumber"]})
+ outjson_data.update({"SerialNumber": controller["SerialNumber"]})
+ try:
+ outjson_data.update({"SKU": controller["SKU"]})
+ except KeyError:
+ pass
+ outjson_data.update({"Status": controller["Status"]})
+ try:
+ outjson_data.update(
+ {"SupportedDeviceProtocols": controller["SupportedDeviceProtocols"]}
+ )
+ except KeyError:
+ pass
+ try:
+ outjson_data.update(
+ {"SupportedControllerProtocols": controller["SupportedControllerProtocols"]}
+ )
+ except KeyError:
+ pass
+ UI().print_out_json(outjson_data)
+
+ if getattr(options, "logicaldrives", False) or getattr(options, "ldrive", False) or single_use:
+ self.storagelogical_drives(options, controller, storage_id, print_ctrl)
+
+ if getattr(options, "physicaldrives", False) or getattr(options, "pdrive", False) or single_use:
+ self.storagephysical_drives(options, controller, storage_id, print_ctrl)
+
+ if single_use:
+ storage_data[controller["Id"]] = controller
+ if getattr(options, "controller", False) and not controller_ident and print_ctrl:
+ raise InvalidCommandLineError(
+ "Controller in position '%s' was " "not found\n" % (getattr(options, "controller", False))
+ )
+ if single_use:
+ return storage_data
+
+ else:
+ if not no_need_ilo5:
+ controller_data = {}
+ for sel in self.rdmc.app.select("SmartStorageArrayController", path_refresh=True):
+ if "Collection" not in sel.maj_type:
+ controller = sel.dict
+ if getattr(options, "controller", False):
+ if (
+ getattr(options, "controller", False) == controller["Id"]
+ or getattr(options, "controller", False) == controller["Location"]
+ ):
+ controller_ident = True
+ else:
+ continue
+
+ if self.rdmc.app.typepath.defs.isgen10:
+ for g10controller in self.rdmc.app.select("SmartStorageConfig.", path_refresh=True):
+ if g10controller.dict.get("Location") == controller.get("Location"):
+ id = controller.get("Id")
+ controller.update(g10controller.dict)
+ if id: # iLO Bug - overwrite the Id from collection
+ controller["Id"] = id
+ break
+
+ if (
+ print_ctrl
+ and (options.controller is None)
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.ldrive is None)
+ and (options.logicaldrives is None)
+ ):
+ sys.stdout.write(
+ "[%s]: %s - %s\n"
+ % (
+ controller["Id"],
+ controller["Location"],
+ controller["Model"],
+ )
+ )
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is None)
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.ldrive is None)
+ and (options.logicaldrives is None)
+ ):
+ outjson = dict()
+ outjson["Controllers"] = dict()
+ outjson["Controllers"][controller["Id"]] = dict()
+ outjson["Controllers"][controller["Id"]]["Location"] = controller["Location"]
+ outjson["Controllers"][controller["Id"]]["Model"] = controller["Model"]
+ UI().print_out_json(outjson)
+
+ elif (
+ print_ctrl
+ and (options.controller is not None)
+ and ((options.ldrive is not None) or options.pdrive is not None)
+ ):
+ pass
+
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is not None)
+ and ((options.ldrive is not None) or (options.pdrive is not None))
+ ):
+ pass
+
+ elif print_ctrl and (options.controller is not None) and options.json is None:
+ controller_info = "------------------------------------------------\n"
+ controller_info += "Controller Info \n"
+ controller_info += "------------------------------------------------\n"
+ controller_info += "Id: %s\n" % controller["Id"]
+ controller_info += "AdapterType: %s\n" % controller["AdapterType"]
+ controller_info += "DataGuard: %s\n" % controller["DataGuard"]
+ controller_info += "Description: %s\n" % controller["Description"]
+ controller_info += "FirmwareVersion: %s\n" % controller["FirmwareVersion"]
+ controller_info += "Name: %s\n" % controller["Name"]
+ controller_info += "Model: %s\n" % controller["Model"]
+ controller_info += "Location: %s\n" % controller["Location"]
+ # controller_info += "Links: %s\n" % controller["Links"]
+ controller_info += "SerialNumber: %s\n" % controller["SerialNumber"]
+ controller_info += "Status: %s\n" % controller["Status"]
+ self.rdmc.ui.printer(controller_info, verbose_override=True)
+
+ elif getattr(options, "json", False) and (options.controller is not None):
+ outjson_data = dict()
+ outjson_data.update({"Id": controller["Id"]})
+ outjson_data.update({"AdapterType": controller["AdapterType"]})
+ outjson_data.update({"DataGuard": controller["DataGuard"]})
+ outjson_data.update({"Description": controller["Description"]})
+ outjson_data.update({"FirmwareVersion": controller["FirmwareVersion"]})
+ outjson_data.update({"Name": controller["Name"]})
+ outjson_data.update({"Model": controller["Model"]})
+ outjson_data.update({"Location": controller["Location"]})
+ # outjson_data.update({"Links": controller["Links"]})
+ outjson_data.update({"SerialNumber": controller["SerialNumber"]})
+ outjson_data.update({"Status": controller["Status"]})
+ UI().print_out_json(outjson_data)
+
+ if (
+ getattr(options, "logicaldrives", False)
+ or getattr(options, "ldrive", False)
+ or single_use
+ ):
+ self.logical_drives(options, controller, print_ctrl)
+
+ if (
+ getattr(options, "physicaldrives", False)
+ or getattr(options, "pdrive", False)
+ or single_use
+ ):
+ self.physical_drives(options, controller, print_ctrl)
+
+ if single_use:
+ controller_data[controller["Id"]] = controller
+
+ if getattr(options, "controller", False) and not controller_ident and print_ctrl:
+ raise InvalidCommandLineError(
+ "Controller in position '%s' was not found\n" % (getattr(options, "controller", False))
+ )
+ if getattr(options, "json", False) and not options.storageid:
+ if self.rdmc.opts.verbose:
+ sys.stdout.write("---------------------------------\n")
+ sys.stdout.write("List of RDE storage devices\n")
+ sys.stdout.write("---------------------------------\n")
+ UI().print_out_json(outjson)
+ if single_use:
+ return controller_data
+ else:
+ raise InvalidCommandLineError(
+ "Storage controller is not ready, Kindly re run the command after sometime\n"
+ )
+ else:
+ raise InvalidCommandLineError("Storage controller is not ready, Kindly re run the command after sometime\n")
+
+ def storagecontroller(self, options, print_ctrl=False, single_use=False):
+ if getattr(options, "controller", False):
+ controller_ident = False
+
+ if (
+ not (getattr(options, "json", False))
+ and not (getattr(options, "controller", False))
+ and print_ctrl
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.logicaldrives is None)
+ and (options.ldrive is None)
+ ):
+ sys.stdout.write("Controllers:\n")
+
+ storage_data = {}
+ get_contrller = []
+
+ list_storageid = self.rdmc.app.get_handler("/redfish/v1/Systems/1/Storage/", silent=True, service=True).dict[
+ "Members"
+ ]
+ for val in list_storageid:
+ storageid_fromurl = val["@odata.id"]
+ if "DE" in storageid_fromurl and options.storageid in storageid_fromurl:
+ getval = self.rdmc.app.get_handler(storageid_fromurl, silent=True, service=True).dict
+ try:
+ controller = getval["Controllers"]
+ list_controllers = self.rdmc.app.get_handler(
+ controller["@odata.id"], silent=True, service=True
+ ).dict
+ except KeyError:
+ self.rdmc.ui.printer("Please check controller was Gen11.\n")
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+
+ for ctl in list_controllers["Members"]:
+ ctl = ctl["@odata.id"]
+ get_sel = self.rdmc.app.get_handler(ctl, silent=True, service=True)
+ get_contrller.append(get_sel)
+
+ for sel in get_contrller:
+ if "Collection" not in sel.path:
+ controller = sel.dict
+ storage_id = controller["@odata.id"].split("/")
+ storage_id = storage_id[6]
+ if getattr(options, "controller", False):
+ if (
+ getattr(options, "controller", False) == controller["Id"]
+ or getattr(options, "controller", False)[-1]
+ == controller["Location"]["PartLocation"]["ServiceLabel"][-1]
+ ):
+ controller_ident = True
+ else:
+ continue
+ if (
+ print_ctrl
+ and (options.controller is None)
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.ldrive is None)
+ and (options.logicaldrives is None)
+ ):
+ sys.stdout.write(
+ "StorageId:%s - ControllerId:%s - %s - %s\n"
+ % (
+ storage_id,
+ controller["Id"],
+ controller["Location"]["PartLocation"]["ServiceLabel"],
+ controller["Model"],
+ )
+ )
+
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is None)
+ and (options.physicaldrives is None)
+ and (options.pdrive is None)
+ and (options.ldrive is None)
+ and (options.logicaldrives is None)
+ ):
+ outjson = dict()
+ outjson["Controllers"] = dict()
+ outjson["Controllers"][storage_id] = dict()
+ outjson["Controllers"][storage_id]["controllerid"] = controller["Id"]
+ outjson["Controllers"][storage_id]["controllerid"]["Location"] = controller["Location"][
+ "PartLocation"
+ ]["ServiceLabel"]
+ outjson["Controllers"][storage_id]["controllerid"]["Model"] = controller["Model"]
+ UI().print_out_json(outjson)
+
+ elif (
+ print_ctrl
+ and (options.controller is not None)
+ and (
+ (options.logicaldrives is not None)
+ or (options.physicaldrives is not None)
+ or (options.ldrive is not None)
+ or (options.pdrive is not None)
+ )
+ ):
+ pass
+
+ elif (
+ getattr(options, "json", False)
+ and (options.controller is not None)
+ and (
+ (options.logicaldrives is not None)
+ or (options.physicaldrives is not None)
+ or (options.ldrive is not None)
+ or (options.pdrive is not None)
+ )
+ ):
+ pass
+
+ elif print_ctrl and (options.controller is not None) and options.json is None:
+ controller_info = "---------------------------------------------\n"
+ controller_info += "Controller {} Details on Storage Id {}\n".format(options.controller, storage_id)
+ controller_info += "---------------------------------------------\n"
+ controller_info += "Id: %s\n" % controller["Id"]
+ controller_info += "StorageId: %s\n" % storage_id
+ controller_info += "Name: %s\n" % controller["Name"]
+ controller_info += "FirmwareVersion: %s\n" % controller["FirmwareVersion"]
+ controller_info += "Manufacturer: %s\n" % controller["Manufacturer"]
+ controller_info += "Model: %s\n" % controller["Model"]
+ controller_info += "PartNumber: %s\n" % controller["PartNumber"]
+ controller_info += "SerialNumber: %s\n" % controller["SerialNumber"]
+ try:
+ controller_info += "SKU: %s\n" % controller["SKU"]
+ except KeyError:
+ pass
+ controller_info += "Status: %s\n" % controller["Status"]
+ try:
+ controller_info += "SupportedDeviceProtocols: %s\n" % controller["SupportedDeviceProtocols"]
+ except KeyError:
+ pass
+ try:
+ controller_info += (
+ "SupportedControllerProtocols: %s\n" % controller["SupportedControllerProtocols"]
+ )
+ except KeyError:
+ pass
+ self.rdmc.ui.printer(controller_info, verbose_override=True)
+
+ elif getattr(options, "json", False) and (options.controller is not None):
+ outjson_data = dict()
+ outjson_data.update({"Id": controller["Id"]})
+ outjson_data.update({"StorageId": storage_id})
+ outjson_data.update({"Name": controller["Name"]})
+ outjson_data.update({"FirmwareVersion": controller["FirmwareVersion"]})
+ outjson_data.update({"Manufacturer": controller["Manufacturer"]})
+ outjson_data.update({"Model": controller["Model"]})
+ outjson_data.update({"PartNumber": controller["PartNumber"]})
+ outjson_data.update({"SerialNumber": controller["SerialNumber"]})
+ try:
+ outjson_data.update({"SKU": controller["SKU"]})
+ except KeyError:
+ pass
+ outjson_data.update({"Status": controller["Status"]})
+ try:
+ outjson_data.update({"SupportedDeviceProtocols": controller["SupportedDeviceProtocols"]})
+ except KeyError:
+ pass
+ try:
+ outjson_data.update(
+ {"SupportedControllerProtocols": controller["SupportedControllerProtocols"]}
+ )
+ except KeyError:
+ pass
+ UI().print_out_json(outjson_data)
+
+ if getattr(options, "logicaldrives", False) or getattr(options, "ldrive", False) or single_use:
+ self.storagelogical_drives(options, controller, storage_id, print_ctrl)
+
+ if getattr(options, "physicaldrives", False) or getattr(options, "pdrive", False) or single_use:
+ self.storagephysical_drives(options, controller, storage_id, print_ctrl)
+
+ if single_use:
+ storage_data[controller["Id"]] = controller
+ if getattr(options, "controller", False) and not controller_ident and print_ctrl:
+ raise InvalidCommandLineError(
+ "Controller in position '%s' was " "not found\n" % (getattr(options, "controller", False))
+ )
+ if single_use:
+ return storage_data
+
+ def storageid(self, options, print_ctrl=False, single_use=False):
+ st_url = "/redfish/v1/Systems/1/Storage/"
+ st_content = self.rdmc.app.get_handler(st_url, silent=True, service=True).dict["Members"]
+ st_flag = False
+ for st_controller in st_content:
+ url = st_controller["@odata.id"]
+ if options.storageid in url and "DE" in url:
+ st_flag = True
+ break
+ else:
+ continue
+ if not st_flag:
+ raise InvalidCommandLineError(
+ "\nStorage ID {} not found or Storage ID is not Redfish enabled and does not have DExxxxxx\n".format(
+ options.storageid
+ )
+ )
+ return
+
+ res_dict = dict()
+ res_dict["Storage"] = dict()
+
+ storage_id_url = "/redfish/v1/Systems/1/Storage/" + options.storageid + "?$expand=."
+
+ storage_resp = self.rdmc.app.get_handler(storage_id_url, silent=True, service=True).dict
+ if not storage_resp:
+ raise InvalidCommandLineError("\nStorage Id {} not found\n".format(options.storageid))
+ return
+ controllers = storage_resp["Controllers"]["Members"]
+ volumes = storage_resp["Volumes"]["Members"]
+ drives = storage_resp["Drives"]
+ stg_data = []
+ actual_no_drives = 0
+ for d in storage_resp["Drives"]:
+ if d["Status"]["State"] == "Enabled":
+ actual_no_drives += 1
+ if print_ctrl and storage_resp and options.json is None:
+ if (
+ options.storageid
+ and not options.controllers
+ and not options.physicaldrives
+ and not options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ ):
+ sys.stdout.write("-----------------------------------\n")
+ sys.stdout.write("Details of Storage %s\n" % options.storageid)
+ sys.stdout.write("-----------------------------------\n")
+ sys.stdout.write("\tId: %s\n\t" % storage_resp["Id"])
+ sys.stdout.write("Health: %s\n\t" % storage_resp["Status"]["HealthRollup"])
+ sys.stdout.write("Name: %s\n\t" % storage_resp["Name"])
+ sys.stdout.write("Number of Controllers: %s\n\t" % storage_resp["Controllers"]["Members@odata.count"])
+ sys.stdout.write("Number of Volumes: %s\n\t" % storage_resp["Volumes"]["Members@odata.count"])
+ sys.stdout.write("Number of Drives: %s\n\t\n" % actual_no_drives)
+ elif (
+ options.storageid
+ and not options.controllers
+ and options.physicaldrives
+ and not options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ ):
+ self.storagephysical_drives(options, options.controller, options.storageid, print_ctrl)
+ elif (
+ options.storageid
+ and not options.controllers
+ and not options.physicaldrives
+ and options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ ):
+ self.storagelogical_drives(options, options.controller, options.storageid, print_ctrl)
+ elif options.storageid and options.ldrive:
+ self.storagelogical_drives(options, options.controller, options.storageid, print_ctrl)
+ elif options.storageid and options.pdrive:
+ self.storagephysical_drives(options, options.controller, options.storageid, print_ctrl)
+
+ elif options.json is not None:
+ if options.logicaldrives or options.ldrive:
+ self.storagelogical_drives(options, options.controller, options.storageid, print_ctrl)
+ if options.physicaldrives or options.pdrive:
+ self.storagephysical_drives(options, options.controller, options.storageid, print_ctrl)
+
+ storage_data = dict()
+ storage_data.update({"Id": storage_resp["Id"]})
+ storage_data.update({"Health": storage_resp["Status"]["HealthRollup"]})
+ storage_data.update({"Name": storage_resp["Name"]})
+ storage_data.update({"Number of Controllers": storage_resp["Controllers"]["Members@odata.count"]})
+ storage_data.update({"Number of Volumes": storage_resp["Volumes"]["Members@odata.count"]})
+ storage_data.update({"Number of Drives": actual_no_drives})
+ stg_data.append(storage_data)
+
+ _ = self.print_list_controllers(options, controllers, print_ctrl)
+ _ = self.print_list_volumes(options, volumes, print_ctrl)
+ _ = self.print_list_drives(options, drives, print_ctrl)
+
+ # Append controller, volume and drive information to final dictionary
+ if (
+ not options.controllers
+ and res_dict
+ and options.json
+ and not options.physicaldrives
+ and not options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ ):
+ if (
+ options.storageid
+ and not options.controllers
+ and not options.physicaldrives
+ and not options.logicaldrives
+ and not options.ldrive
+ and not options.pdrive
+ ):
+ res_dict["Storage"].update(storage_data)
+ UI().print_out_json(res_dict)
+
+ elif options.controllers and options.storageid and options.json is None:
+ sys.stdout.write("---------------------------------------------------\n")
+ sys.stdout.write("List of Controllers in this Storage ID %s\n" % options.storageid)
+ sys.stdout.write("---------------------------------------------------\n")
+ list_ctr = controllers
+ for lst in list_ctr:
+ l_data = lst["@odata.id"]
+ get_ctr = self.rdmc.app.get_handler(l_data, silent=True, service=True).dict
+ sys.stdout.write("\tId: %s\n\t" % get_ctr["Id"])
+ sys.stdout.write("Health: %s\n\t" % get_ctr["Status"]["Health"])
+ sys.stdout.write("Name: %s\n\t" % get_ctr["Name"])
+ sys.stdout.write("FirmwareVersion: %s\n\t" % get_ctr["FirmwareVersion"])
+ sys.stdout.write("SerialNumber: %s\n" % get_ctr["SerialNumber"])
+
+ elif options.controllers and print_ctrl and options.json:
+ list_ctr = controllers
+ for lst in list_ctr:
+ l_data = lst["@odata.id"]
+ get_ctr = self.rdmc.app.get_handler(l_data, silent=True, service=True).dict
+ ctr_data = dict()
+ ctr_data["Controllers"] = dict()
+ ctr_data["Controllers"].update({"Id": get_ctr["Id"]})
+ ctr_data["Controllers"].update({"Health": get_ctr["Status"]["Health"]})
+ ctr_data["Controllers"].update({"Name": get_ctr["Name"]})
+ ctr_data["Controllers"].update({"FirmwareVersion": get_ctr["FirmwareVersion"]})
+ ctr_data["Controllers"].update({"SerialNumber": get_ctr["SerialNumber"]})
+ UI().print_out_json(ctr_data)
+
+ def print_list_controllers(self, options, controllers, print_ctrl):
+ c_data = []
+ if not options.json:
+ sys.stdout.write("Controllers Details:\n")
+ if not controllers:
+ sys.stdout.write("\tNone:\n\n")
+ for c in controllers:
+ ctrlr = c["@odata.id"]
+ getctrl = self.rdmc.app.get_handler(ctrlr, silent=True, service=True).dict
+ if print_ctrl and getctrl and options.json is None:
+ sys.stdout.write("\tId: %s\n\t" % getctrl["Id"])
+ sys.stdout.write("Health: %s\n\t" % getctrl["Status"]["Health"])
+ sys.stdout.write("Location: %s\n\t" % getctrl["Location"]["PartLocation"]["ServiceLabel"])
+ sys.stdout.write("Name: %s\n\t\n" % getctrl["Name"])
+ elif options.json is not None:
+ ctrl_data = dict()
+ ctrl_data.update({"Id": getctrl["Id"]})
+ ctrl_data.update({"Health": getctrl["Status"]["Health"]})
+ ctrl_data.update({"Location": getctrl["Location"]["PartLocation"]["ServiceLabel"]})
+ ctrl_data.update({"Name": getctrl["Name"]})
+ c_data.append(ctrl_data)
+ return c_data
+
+ def print_list_volumes(self, options, volumes, print_ctrl):
+ v_data = []
+ if "json" in options and options.json:
+ vol_data = dict()
+ if not options.json:
+ sys.stdout.write("\nVolume Details:\n")
+ if not volumes:
+ sys.stdout.write("\tNone\n\n")
+ for vol in volumes:
+ volume = vol["@odata.id"]
+ getvolumes = self.rdmc.app.get_handler(volume, silent=True, service=True).dict
+ if print_ctrl and getvolumes and options.json is None:
+ sys.stdout.write("\tId: %s\n\t" % getvolumes["Id"])
+ sys.stdout.write("Name: %s\n\t" % getvolumes["Name"])
+ sys.stdout.write("RAIDType: %s\n\t" % getvolumes["RAIDType"])
+ sys.stdout.write("VolumeUniqueId: %s\n\t" % getvolumes["Identifiers"][0]["DurableName"])
+ sys.stdout.write("Health: %s\n\n" % getvolumes["Status"]["Health"])
+ elif options.json is not None:
+ vol_data.update({"Id": getvolumes["Id"]})
+ vol_data.update({"Name": getvolumes["Name"]})
+ vol_data.update({"Health": getvolumes["Status"]["Health"]})
+ vol_data.update({"RAIDType": getvolumes["RAIDType"]})
+ vol_data.update({"VolumeUniqueId": getvolumes["Identifiers"][0]["DurableName"]})
+ v_data.append(vol_data)
+ return v_data
+
+ def print_list_drives(self, options, drives, print_ctrl):
+ d_data = []
+ if not options.json:
+ sys.stdout.write("Drive Details:\n\n")
+ if not drives:
+ sys.stdout.write("\tNone\n\n")
+ for drive in drives:
+ dve = drive["@odata.id"]
+ getdrives = self.rdmc.app.get_handler(dve, silent=True, service=True).dict
+ if print_ctrl and getdrives and options.json is None:
+ sys.stdout.write("\tId: %s\n\t" % getdrives["Id"])
+ sys.stdout.write("Location: %s\n\t" % getdrives["PhysicalLocation"]["PartLocation"]["ServiceLabel"])
+ sys.stdout.write("Health: %s\n\t" % getdrives["Status"].get("Health", "NA"))
+ sys.stdout.write("Name: %s\n\t" % getdrives["Name"])
+ sys.stdout.write("Model: %s\n\t" % getdrives.get("Model", "NA"))
+ sys.stdout.write("Revision: %s\n\t" % getdrives.get("Revision", "NA"))
+ sys.stdout.write("Serial Number: %s\n\t" % getdrives.get("SerialNumber", "NA"))
+ sys.stdout.write("Protocol: %s\n\t" % getdrives.get("Protocol", "NA"))
+ sys.stdout.write("MediaType: %s\n\t" % getdrives.get("MediaType", "NA"))
+ sys.stdout.write("CapacityBytes: %s\n\t\n" % getdrives.get("CapacityBytes", "NA"))
+
+ elif options.json is not None:
+ drives_data = dict()
+ drives_data.update({"Id": getdrives["Id"]})
+ drives_data.update({"Model": getdrives.get("Model", "NA")})
+ drives_data.update({"Revision": getdrives.get("Revision", "NA")})
+ drives_data.update({"Protocol": getdrives.get("Protocol", "NA")})
+ drives_data.update({"MediaType": getdrives.get("MediaType", "NA")})
+ drives_data.update({"CapacityBytes": getdrives.get("CapacityBytes", "NA")})
+ drives_data.update({"Name": getdrives["Name"]})
+ drives_data.update({"Health": getdrives["Status"].get("Health", "NA")})
+ drives_data.update({"Serial Number": getdrives.get("SerialNumber", "NA")})
+ drives_data.update({"Location": getdrives["PhysicalLocation"]["PartLocation"]["ServiceLabel"]})
+ d_data.append(drives_data)
+ return d_data
+
+ def storagephysical_drives(self, options, controller, storage_id, print_ctrl=False, single_use=False):
+ found_entries = False
+ if not getattr(options, "json", False) and print_ctrl:
+ sys.stdout.write("--------------------------------------------------\n")
+ if not getattr(options, "pdrive", False):
+ if controller:
+ sys.stdout.write("Drives on Controller %s and Storage %s\n" % (options.controller, storage_id))
+ else:
+ sys.stdout.write("Drives on Storage %s\n" % storage_id)
+ else:
+ if controller:
+ sys.stdout.write(
+ "Drive %s on Controller %s and Storage %s\n" % (options.pdrive, options.controller, storage_id)
+ )
+ else:
+ sys.stdout.write("Drive %s on Storage %s\n" % (options.pdrive, storage_id))
+ sys.stdout.write("--------------------------------------------------\n")
+ if getattr(options, "pdrive", False):
+ pdrive_ident = False
+ else:
+ pdrive_ident = True
+ if single_use:
+ physicaldrives = {}
+ if getattr(options, "json", False):
+ outjson = dict()
+ outjson["Drives"] = dict()
+ print_drives = []
+ storage_url = self.rdmc.app.typepath.defs.systempath + "Storage/" + storage_id
+ dd_list = self.rdmc.app.get_handler(storage_url, silent=True).dict.get("Drives", {})
+ if dd_list:
+ found_entries = True
+ for dd in dd_list:
+ drive_url = dd["@odata.id"]
+ drive_data = self.rdmc.app.get_handler(drive_url, silent=True).dict
+ location = drive_data["PhysicalLocation"]["PartLocation"]["ServiceLabel"]
+ loc = location.split(":")
+ del loc[0]
+ if len(loc) == 3:
+ temp_str = str(loc[0].split("=")[1] + ":" + loc[1].split("=")[1] + ":" + loc[2].split("=")[1])
+ location = temp_str
+ else:
+ pass
+
+ if getattr(options, "pdrive", False):
+ if options.pdrive != location:
+ continue
+ else:
+ pdrive_ident = True
+ if single_use:
+ physicaldrives.update({drive_data["Id"]: drive_data})
+ if (
+ print_ctrl
+ and pdrive_ident
+ and options.pdrive is None
+ and options.json is None
+ and (location not in print_drives)
+ ):
+ sys.stdout.write(
+ "\t[%s]: %s, Model %s, Location %s, Type %s, Serial %s - %s Bytes\n"
+ % (
+ location,
+ drive_data.get("Name", "NA"),
+ drive_data.get("Model", "NA"),
+ location,
+ drive_data.get("MediaType", "NA"),
+ drive_data.get("SerialNumber", "NA"),
+ drive_data.get("CapacityBytes", "NA"),
+ )
+ )
+ elif (
+ print_ctrl
+ and pdrive_ident
+ and options.pdrive
+ and options.json is None
+ and (location not in print_drives)
+ ):
+ sys.stdout.write(
+ "\tId: %s\n\t%s\n\tModel: %s\n\tLocation: %s\n\tType: %s\n\tSerial: %s\n\tCapacity: %s Bytes\n"
+ % (
+ drive_data["Id"],
+ drive_data["Name"],
+ drive_data.get("Model", "NA"),
+ location,
+ drive_data.get("MediaType", "NA"),
+ drive_data.get("SerialNumber", "NA"),
+ drive_data.get("CapacityBytes", "NA"),
+ )
+ )
+ break
+ elif getattr(options, "json", False) and pdrive_ident and (location not in print_drives):
+ outjson["Drives"][location] = dict()
+ outjson["Drives"][location]["Id"] = drive_data["Id"]
+ outjson["Drives"][location]["Name"] = drive_data["Name"]
+ outjson["Drives"][location]["Model"] = drive_data.get("Model", "NA")
+ outjson["Drives"][location]["Location"] = location
+ outjson["Drives"][location]["MediaType"] = drive_data.get("MediaType", "NA")
+ outjson["Drives"][location]["SerialNumber"] = drive_data.get("SerialNumber", "NA")
+ outjson["Drives"][location]["CapacityBytes"] = drive_data.get("CapacityBytes", "NA")
+ if options.pdrive:
+ break
+
+ print_drives.append(location)
+ if getattr(options, "json", False) and pdrive_ident:
+ UI().print_out_json(outjson)
+ if getattr(options, "pdrive", False) and not pdrive_ident and print_ctrl:
+ raise InvalidCommandLineError("\tPhysical drive in position '%s' was not found\n" % options.pdrive)
+ elif not found_entries and print_ctrl:
+ sys.stdout.write("\tPhysical drives not found.\n")
+
+ if single_use:
+ return physicaldrives
+
+ def storagelogical_drives(self, options, controller, storage_id, print_ctrl=False, single_use=False):
+ found_entries = False
+ printed = False
+ if not getattr(options, "json", False) and print_ctrl:
+ sys.stdout.write("--------------------------------------------------\n")
+ if not getattr(options, "ldrive", False):
+ sys.stdout.write("Volumes on Controller %s and Storage %s\n" % (options.controller, storage_id))
+ else:
+ if controller:
+ sys.stdout.write(
+ "Volume %s on Controller %s and Storage %s\n" % (options.ldrive, options.controller, storage_id)
+ )
+ else:
+ sys.stdout.write("Volume %s on Storage %s\n" % (options.ldrive, storage_id))
+ sys.stdout.write("--------------------------------------------------\n")
+ if getattr(options, "ldrive", False):
+ ldrive_ident = False
+ else:
+ ldrive_ident = True
+ if single_use:
+ logicaldrives = {}
+ if getattr(options, "json", False):
+ outjson = dict()
+ outjson["volumes"] = dict()
+ list_volumes = self.rdmc.app.get_handler(
+ "/redfish/v1/Systems/1/Storage/" + storage_id + "/Volumes/",
+ silent=True,
+ service=True,
+ ).dict["Members"]
+ found = False
+ for tmp in list_volumes:
+ if not found:
+ tmp = tmp["@odata.id"]
+ tmp = self.rdmc.app.get_handler(tmp, silent=True, service=True).dict
+ try:
+ std_v = options.storageid in tmp["@odata.id"]
+ except:
+ std_v = tmp["@odata.id"]
+ if std_v:
+ if getattr(options, "ldrive", False):
+ ldrive_ident = False
+ else:
+ ldrive_ident = True
+ if getattr(options, "ldrive", False):
+ if options.ldrive != tmp["Id"]:
+ continue
+ else:
+ ldrive_ident = True
+ found = True
+
+ if "Identifiers" in tmp:
+ durablename = tmp["Identifiers"]
+ if durablename is not None:
+ d_name = [name["DurableName"] for name in durablename]
+ found_entries = True
+
+ drives = []
+ d_data = tmp["Links"]["Drives"]
+ for dd in d_data:
+ drive = dd["@odata.id"]
+ drive_data = self.rdmc.app.get_handler(drive, silent=True).dict
+ location = drive_data["PhysicalLocation"]["PartLocation"]["ServiceLabel"]
+ loc = location.split(":")
+ del loc[0]
+ if len(loc) == 3:
+ temp_str = str(
+ loc[0].split("=")[1] + ":" + loc[1].split("=")[1] + ":" + loc[2].split("=")[1]
+ )
+ location = temp_str
+ drives.append(location)
+ volumes = ", ".join(str(item) for item in drives)
+ if print_ctrl and ldrive_ident and options.ldrive is None and not getattr(options, "json", False):
+ sys.stdout.write(
+ "\t[%s]: Name %s RAIDType %s VUID %s Capacity %s Bytes - Health %s\n"
+ % (
+ tmp["Id"],
+ tmp["Name"],
+ tmp["RAIDType"],
+ d_name[0],
+ tmp["CapacityBytes"],
+ tmp["Status"]["Health"],
+ )
+ )
+ elif getattr(options, "json", False) and ldrive_ident:
+ outjson["volumes"][tmp["Id"]] = dict()
+ outjson["volumes"][tmp["Id"]]["VolumeName"] = tmp["Name"]
+ outjson["volumes"][tmp["Id"]]["RAIDType"] = tmp["RAIDType"]
+ outjson["volumes"][tmp["Id"]]["VolumeUniqueIdentifier"] = d_name[0]
+ # if options.ldrive:
+ # outjson["volumes"][tmp["Id"]]["Drive Location"] = volumes
+ outjson["volumes"][tmp["Id"]]["Capacity"] = tmp["CapacityBytes"]
+ outjson["volumes"][tmp["Id"]]["Health"] = tmp["Status"]["Health"]
+ printed = True
+ elif print_ctrl and ldrive_ident and options.ldrive is not None:
+ sys.stdout.write(
+ "\tId: %s\n\tName: %s\n\tRaidType: %s\n\tVolumeUniqueId: %s\n\tCapacity: %s Bytes\n\n"
+ % (
+ tmp["Id"],
+ tmp["Name"],
+ tmp["RAIDType"],
+ d_name[0],
+ tmp["CapacityBytes"],
+ )
+ )
+ printed = True
+ if single_use:
+ logicaldrives.update({tmp["Id"]: tmp})
+ if getattr(options, "json", False) and ldrive_ident:
+ UI().print_out_json(outjson)
+ printed = True
+ if getattr(options, "ldrive", False) and not ldrive_ident:
+ sys.stdout.write("\tVolume '%s' was not found.\n" % options.ldrive)
+ elif not found_entries and print_ctrl:
+ sys.stdout.write("\tVolumes not found.\n")
+ if not getattr(options, "logicaldrives", False) and not printed:
+ if getattr(options, "ldrive", False):
+ raise InvalidCommandLineError("\tVolume '%s' does not exists.\n" % getattr(options, "ldrive", False))
+
+ if single_use:
+ return logicaldrives
+
+ def get_storage_data_drives(self, options, drives, print_ctrl=False, single_use=False):
+ if not (getattr(options, "json", False)) and print_ctrl:
+ sys.stdout.write("\tData Drives:\n\n")
+ if single_use:
+ subsetdrives = {drives["Id"]: drives}
+ found_entries = False
+ if print_ctrl:
+ self.tranverse_func(drives)
+ found_entries = True
+
+ elif getattr(options, "json", False):
+ UI().print_out_json(drives)
+
+ if not found_entries and print_ctrl:
+ sys.stdout.write("\t\tComponent drives not found.\n")
+
+ if single_use:
+ return subsetdrives
+
+ def tranverse_func(self, data, indent=0):
+ for key, value in data.items():
+ sys.stdout.write("\t %s :" % str(key))
+ if isinstance(value, dict):
+ self.tranverse_func(value, indent + 1)
+ else:
+ sys.stdout.write("%s \n" % str(value))
+
+ def physical_drives(self, options, content, print_ctrl=False, single_use=False):
+ """
+ Identify/parse physical drives (and child properties) of a parent array controller
+
+ :param options: command line options
+ :type options: object attributes
+ :param content: dictionary of physical drive href or @odata.id paths (expect @odata.type
+ "SmartStorageArrayController", @odata.id /Systems/1/SmartStorage/ArrayControllers/X/)
+ :type content: dict
+ :param print_ctrl: flag for console print enablement/disablement (default disabled)
+ :type print_ctrl: bool
+ :param single_use: singular usage - True or explore mode - False,
+ returns dictionary of results to calling function (default disabled) - see returns.
+ :type single_use: bool
+ :returns: None, dictionary of all physical drives identified by 'Id'
+ """
+ dd = ""
+ confd = content.get("PhysicalDrives")
+ try:
+ dd = content["links"]["PhysicalDrives"][self.rdmc.app.typepath.defs.hrefstring]
+ except:
+ dd = content["Links"]["PhysicalDrives"][self.rdmc.app.typepath.defs.hrefstring]
+ finally:
+ if not getattr(options, "json", False) and print_ctrl:
+ sys.stdout.write("Physical Drives:\n")
+ if getattr(options, "pdrive", False):
+ pdrive_ident = False
+ else:
+ pdrive_ident = True
+ if single_use:
+ physicaldrives = {}
+ found_entries = False
+ for data in self.rdmc.app.get_handler(dd, silent=True).dict.get("Members", {}):
+ try:
+ tmp = data[self.rdmc.app.typepath.defs.hrefstring]
+ except:
+ tmp = data[next(iter(data))]
+ finally:
+ tmp = self.rdmc.app.get_handler(tmp, silent=True).dict
+ found_entries = True
+ if confd:
+ for confdd in confd:
+ if confdd.get("Location") == tmp.get("Location"):
+ tmp.update(confdd)
+ break
+ if getattr(options, "pdrive", False):
+ if options.pdrive != tmp["Location"]:
+ continue
+ else:
+ pdrive_ident = True
+ if single_use:
+ physicaldrives[tmp["Id"]] = tmp
+ if print_ctrl and pdrive_ident and options.pdrive is None:
+ sys.stdout.write(
+ "\t[%s]: Model %s, Location %s, Type %s, Serial %s - %s MiB\n"
+ % (
+ tmp["Location"],
+ tmp.get("Model", "NA"),
+ tmp.get("Location", "NA"),
+ tmp.get("MediaType", "NA"),
+ tmp.get("SerialNumber", "NA"),
+ tmp.get("CapacityMiB", "NA"),
+ )
+ )
+ elif getattr(options, "json", False) and pdrive_ident and options.pdrive is None:
+ outjson = dict()
+ outjson["PhysicalDrives"] = dict()
+ outjson["PhysicalDrives"][tmp["Location"]] = dict()
+ outjson["PhysicalDrives"][tmp["Location"]]["Model"] = tmp.get("Model", "NA")
+ outjson["PhysicalDrives"][tmp["Location"]]["Location"] = tmp.get("Location", "NA")
+ outjson["PhysicalDrives"][tmp["Location"]]["MediaType"] = tmp.get("MediaType", "NA")
+ outjson["PhysicalDrives"][tmp["Location"]]["SerialNumber"] = tmp.get("SerialNumber", "NA")
+ outjson["PhysicalDrives"][tmp["Location"]]["CapacityMiB"] = tmp.get("CapacityMiB", "NA")
+ UI().print_out_json(outjson)
+
+ elif print_ctrl and pdrive_ident and options.pdrive is not None:
+ ctl_logical = dict()
+ contoller_loc = content["Location"] + "-" + content["Model"]
+ ctl_logical.update({"Controller": contoller_loc})
+ remove_odata = self.odataremovereadonly(tmp)
+ ctl_logical.update(remove_odata)
+ location = ctl_logical["Location"]
+ if print_ctrl and ctl_logical and options.ldrive is None and not getattr(options, "json", False):
+ sys.stdout.write(
+ "\tId:\t%s\n\tModel:\t%s\n\tLocation:\t%s\n\tType:\t%s\n\tSerial:\t%s\n\tCapacity:\t%s "
+ "Bytes\n"
+ % (
+ ctl_logical["Id"],
+ ctl_logical["Model"],
+ ctl_logical["Location"],
+ ctl_logical["MediaType"],
+ ctl_logical["SerialNumber"],
+ ctl_logical["CapacityLogicalBlocks"],
+ )
+ )
+ elif getattr(options, "json", False) and ctl_logical:
+ outjson = dict()
+ outjson["Drives"] = dict()
+ outjson["Drives"][location] = dict()
+ outjson["Drives"][location]["Id"] = ctl_logical["Id"]
+ outjson["Drives"][location]["Model"] = ctl_logical["Model"]
+ outjson["Drives"][location]["Location"] = location
+ outjson["Drives"][location]["MediaType"] = ctl_logical["MediaType"]
+ outjson["Drives"][location]["SerialNumber"] = ctl_logical["SerialNumber"]
+ outjson["Drives"][location]["CapacityLogicalBlocks"] = ctl_logical["CapacityLogicalBlocks"]
+ UI().print_out_json(outjson)
+
+ elif getattr(options, "json", False) and pdrive_ident and options.pdrive is not None:
+ ctl_logical = dict()
+ contoller_loc = content["Location"] + "-" + content["Model"]
+ ctl_logical.update({"Controller": contoller_loc})
+ remove_odata = self.odataremovereadonly(tmp)
+ ctl_logical.update(remove_odata)
+ UI().print_out_json(ctl_logical)
+
+ if getattr(options, "pdrive", False) and pdrive_ident:
+ break
+ if getattr(options, "pdrive", False) and not pdrive_ident and print_ctrl:
+ raise InvalidCommandLineError("\tPhysical drive in position '%s' was not found \n" % options.pdrive)
+ elif not found_entries and print_ctrl:
+ sys.stdout.write("\tPhysical drives not found.\n")
+
+ if single_use:
+ return physicaldrives
+
+ def logical_drives(self, options, storage_id, content, print_ctrl=False, single_use=False):
+ """
+ Identify/parse volumes (and child properties) of an associated array controller
+
+ :param options: command line options
+ :type options: object attributes
+ :param content: dictionary of volume href or @odata.id paths (expect @odata.type
+ "SmartStorageArrayController", @odata.id /Systems/1/SmartStorage/ArrayControllers/X/)
+ :type content: dict
+ :param print_ctrl: flag for console print enablement/disablement (default disabled)
+ :type print_ctrl: bool
+ :param single_use: singular usage - True or explore mode - False,
+ returns dictionary of results to calling function (default disabled) - see returns.
+ :type single_use: bool
+ :returns: None, dictionary of all volumes identified by 'Id'
+ """
+
+ confd = content.get("LogicalDrives")
+ try:
+ _ = content["links"]["LogicalDrives"][self.rdmc.app.typepath.defs.hrefstring]
+ except:
+ _ = content["Links"]["LogicalDrives"][self.rdmc.app.typepath.defs.hrefstring]
+ finally:
+ if not getattr(options, "json", False) and print_ctrl:
+ sys.stdout.write("Volumes:\n")
+ if getattr(options, "ldrive", False):
+ ldrive_ident = False
+ else:
+ ldrive_ident = True
+ if single_use:
+ logicaldrives = {}
+ found_entries = False
+ try:
+ list_volumes = self.rdmc.app.get_handler(
+ "/redfish/v1/Systems/1/Storage/" + options.storageid + "/Volumes/",
+ silent=True,
+ service=True,
+ ).dict["Members"]
+ except:
+ list_volumes = self.rdmc.app.get_handler(
+ "/redfish/v1/Systems/1/Storage/" + storage_id + "/Volumes/",
+ silent=True,
+ service=True,
+ ).dict["Members"]
+ for data in list_volumes:
+ try:
+ tmp = data[self.rdmc.app.typepath.defs.hrefstring]
+ except:
+ tmp = data[next(iter(data))]
+ finally:
+ tmp = self.rdmc.app.get_handler(tmp, silent=True).dict
+ found_entries = True
+ if confd:
+ for confdd in confd:
+ if confdd.get("LogicalDriveNumber") == tmp.get("LogicalDriveNumber"):
+ tmp.update(confdd)
+ break
+ if getattr(options, "ldrive", False):
+ if options.ldrive != tmp["Id"]:
+ continue
+ else:
+ ldrive_ident = True
+ if print_ctrl and ldrive_ident and options.ldrive is None:
+ sys.stdout.write(
+ "\t[%s]: Name %s Raid %s VUID %s - %s MiB\n"
+ % (
+ tmp["Id"],
+ tmp["LogicalDriveName"],
+ tmp["Raid"],
+ tmp["VolumeUniqueIdentifier"],
+ tmp["CapacityMiB"],
+ )
+ )
+ elif getattr(options, "json", False) and ldrive_ident and options.ldrive is None:
+ outjson = dict()
+ outjson["LogicalDrives"] = dict()
+ outjson["LogicalDrives"][tmp["Id"]] = dict()
+ outjson["LogicalDrives"][tmp["Id"]]["LogicalDriveName"] = tmp["LogicalDriveName"]
+ outjson["LogicalDrives"][tmp["Id"]]["Raid"] = tmp["Raid"]
+ outjson["LogicalDrives"][tmp["Id"]]["VolumeUniqueIdentifier"] = tmp["VolumeUniqueIdentifier"]
+ outjson["LogicalDrives"][tmp["Id"]]["CapacityMiB"] = tmp["CapacityMiB"]
+ UI().print_out_json(outjson)
+ elif print_ctrl and ldrive_ident and options.ldrive is not None:
+ ctl_logical = dict()
+ contoller_loc = content["Location"] + "-" + content["Model"]
+ ctl_logical.update({"Controller": contoller_loc})
+ remove_odata = self.odataremovereadonly(tmp)
+ ctl_logical.update(remove_odata)
+ id = ctl_logical["Id"]
+ if print_ctrl and ctl_logical and options.json is None:
+ sys.stdout.write("\tId: %s\n\t" % ctl_logical["Id"])
+ sys.stdout.write("Name: %s\n\t" % ctl_logical["LogicalDriveName"])
+ sys.stdout.write("RAIDType: %s\n\t" % ctl_logical["Raid"])
+ sys.stdout.write("VolumeUniqueId: %s\n\t" % ctl_logical["VolumeUniqueIdentifier"])
+ sys.stdout.write("Capacity: %s\n\t" % ctl_logical["CapacityBlocks"])
+ sys.stdout.write("Health: %s\n\n" % ctl_logical["Status"]["Health"])
+ # UI().print_out_json(ctl_logical)
+ elif getattr(options, "json", False) and ctl_logical:
+ outjson = dict()
+ outjson["Volumes"] = dict()
+ outjson["Volumes"][id] = dict()
+ # outjson["Volumes"][id]["Id"] = ctl_logical["Id"]
+ outjson["Volumes"][id]["VolumeName"] = ctl_logical["LogicalDriveName"]
+ outjson["Volumes"][id]["RAIDType"] = ctl_logical["Raid"]
+ outjson["Volumes"][id]["Health"] = ctl_logical["Status"]["Health"]
+ outjson["Volumes"][id]["Capacity"] = ctl_logical["CapacityBlocks"]
+ outjson["Volumes"][id]["VolumeUniqueIdentifier"] = ctl_logical["VolumeUniqueIdentifier"]
+ UI().print_out_json(outjson)
+ elif getattr(options, "json", False) and ldrive_ident and options.ldrive is not None:
+ ctl_logical = dict()
+ contoller_loc = content["Location"] + "-" + content["Model"]
+ ctl_logical.update({"Controller": contoller_loc})
+ remove_odata = self.odataremovereadonly(tmp)
+ ctl_logical.update(remove_odata)
+ UI().print_out_json(ctl_logical)
+
+ elif getattr(options, "json", False) and ldrive_ident:
+ UI().print_out_json(tmp)
+ if single_use:
+ logicaldrives[tmp["Id"]] = tmp
+ if getattr(options, "ldrive", False) and ldrive_ident:
+ break
+ if getattr(options, "ldrive", False) and not ldrive_ident:
+ sys.stdout.write("\tVolume '%s' was not found \n" % options.pdrive)
+ elif not found_entries and print_ctrl:
+ sys.stdout.write("\tVolumes not found.\n")
+
+ if single_use:
+ return logicaldrives
+
+ def get_data_drives(self, options, drives, print_ctrl=False, single_use=False):
+ """
+ Identify/parse a physical component drive collection of a respective volume. The
+ physical disk properties as well as some logical RAID parameters within each respective
+ member.
+
+ :param options: command line options
+ :type options: object attributes
+ :param content: collection of data drives href or @odata.id paths (as members) as attributed
+ to a respective volume.
+ :type content: dict
+ :param print_ctrl: flag for console print enablement/disablement (default disabled)
+ :type print_ctrl: bool
+ :param single_use: singular usage, returns dictionary of results to calling function
+ (default disabled)
+ :type single_use: bool
+ :returns: None, dictionary of all physical drives composing a parent volume,
+ each instance identified by 'Id'
+ """
+
+ if not (getattr(options, "json", False)) and print_ctrl:
+ sys.stdout.write("\tData Drives:\n\n")
+ if single_use:
+ subsetdrives = {}
+ found_entries = False
+ for member in drives.get("Members", {}):
+ try:
+ tmp = member[self.rdmc.app.typepath.defs.hrefstring]
+ except:
+ tmp = member[next(iter(member))]
+ finally:
+ tmp = self.rdmc.app.get_handler(tmp, silent=True).dict
+ found_entries = True
+ if single_use:
+ subsetdrives[tmp["Id"]] = tmp
+ if print_ctrl:
+ self.tranverse_func(tmp)
+ elif getattr(options, "json", False):
+ UI().print_out_json(tmp)
+ if not found_entries and print_ctrl:
+ sys.stdout.write("\t\tComponent drives not found.\n")
+
+ if single_use:
+ return subsetdrives
+
+ def storagecontrollervalidation(self, options):
+ """Storage controller validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ if "json" in options and options.json:
+ self.rdmc.json = True
+ self.cmdbase.login_select_validation(self, options)
+
+ if getattr(options, "sa_conf_filename", False):
+ if options.command == "save":
+ open(options.sa_conf_filename, "w+b")
+ if options.command == "load":
+ open(options.sa_conf_filename, "r+b")
+ self.config_file = options.sa_conf_filename
+ else:
+ self.config_file = __config_file__
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+
+ default_parser = subcommand_parser.add_parser(
+ "default",
+ help="Running without any sub-command will return storage controller configuration data\n"
+ "on the currently logged in server. Additional optional arguments will narrow the\n"
+ "scope of returned data to individual controllers, physical or volumes",
+ )
+ default_parser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller using either the slot "
+ "number or index. \n\tExamples:\n\t1. To get more details on a specific "
+ "controller, select it by index.\tstoragecontroller --controller=2"
+ "\n\t2. To get more details on a specific controller select "
+ "it by location.\tstoragecontroller --controller='Slot 0'",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--storage_id",
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding controller using either the storageid "
+ "id. \n\tExamples:\n\t1. To get more details on a specific "
+ "controller, select it by storageid.\tstoragecontroller --storageid=DE00E000"
+ "\n\t2. To get more details on a specific controller select "
+ "it by location.\tstoragecontroller --storageid='DE00E000'",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--controllers",
+ dest="controllers",
+ action="store_true",
+ help="Use this flag to return the controllers for the storageid selected."
+ "\n\tExamples:\n\t1. storagecontroller --controllers\n",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--physicaldrives",
+ "--drives",
+ dest="physicaldrives",
+ action="store_true",
+ help="Use this flag to return the physical drives for the controller selected."
+ "\n\tExamples:\n\t1. storagecontroller --drives\n\t2. To obtain details about "
+ "physical drives for a specific controller.\tstoragecontroller --controller=3 "
+ "--physicaldrives",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--logicaldrives",
+ "--volumes",
+ dest="logicaldrives",
+ action="store_true",
+ help="Use this flag to return the volumes for the controller selected.\n\t "
+ "\n\tExamples:\n\t1. storagecontroller --logicaldrives\n\t2. To obtain details about "
+ "volumes for a specific controller.\tstoragecontroller --controller=3 "
+ "--logicaldrives",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--pdrive",
+ "--drive",
+ dest="pdrive",
+ help="Use this flag to select the corresponding physical disk.\n\tExamples:\n\t "
+ "1. To obtain details about a specific physical drive for a specific controller."
+ "\tstoragecontroller --controller=3 --pdrive=1I:1:1",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--ldrive",
+ "--volume",
+ dest="ldrive",
+ help="Use this flag to select the corresponding logical disk.\n\tExamples:\n\t "
+ "1. To obtain details about a specific physical drive for a specific controller."
+ "\tstoragecontroller --controller=3 --ldrive=1",
+ default=None,
+ )
+ default_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="""Use this flag to output data in JSON format.""",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(default_parser)
+
+ state_help = "Print state/event information from @Redfish.Settings (if available)"
+ state_parser = subcommand_parser.add_parser(
+ "state",
+ help=state_help,
+ description=state_help + "\n\tExample: storagecontroller state",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ state_parser.add_argument(
+ "--controller",
+ dest="controller",
+ help="Use this flag to select the corresponding controller using either the slot "
+ "number or index. \n\tExamples:\n\t1. To get more details on a specific "
+ "controller, select it by index.\tstoragecontroller state --controller=2"
+ "\n\t2. To get more details on a specific controller select "
+ "it by location.\tstoragecontroller state --controller='Slot 0'",
+ default=None,
+ )
+ state_parser.add_argument(
+ "--storage_id",
+ "--storageid",
+ dest="storageid",
+ help="Use this flag to select the corresponding controller using either the storageid "
+ "id. \n\tExamples:\n\t1. To get more details on a specific "
+ "controller, select it by storageid.\tstoragecontroller state --storageid=DE00E000"
+ "\n\t2. To get more details on a specific controller select "
+ "it by location.\tstoragecontroller state --storageid='DE00E000'",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(state_parser)
+ json_save_help = (
+ "Save a JSON file with all current configurations (all controllers, logical and \nphysical drives)."
+ )
+ # json save sub-parser
+ json_save_parser = subcommand_parser.add_parser(
+ "save",
+ help=json_save_help,
+ description=json_save_help + "\n\tExample: storagecontroller save -f ",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ json_save_parser.add_argument(
+ "-f",
+ dest="sa_conf_filename",
+ help="Specify a filename for saving the storagecontroller configuration. (Default: "
+ "'storagecontroller_config.json')",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(json_save_parser)
+ json_load_help = (
+ "Load a JSON file with modified storagecontroller configurations (All read-only "
+ "properties\nare discarded)."
+ )
+ # json load sub-parser
+ json_load_parser = subcommand_parser.add_parser(
+ "load",
+ help=json_load_help,
+ description=json_load_help + "\n\tExample: storagecontroller load -f ",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ json_load_parser.add_argument(
+ "-f",
+ dest="sa_conf_filename",
+ help="Specify a filename for loading a storagecontroller configuration. (Default: "
+ "'storagecontroller_config.json')",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(json_load_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/ClearControllerConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/ClearControllerConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..13ce644
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/ClearControllerConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/CreateVolumeCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/CreateVolumeCommand.cpython-310.pyc
new file mode 100644
index 0000000..9ee9df1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/CreateVolumeCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DeleteVolumeCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DeleteVolumeCommand.cpython-310.pyc
new file mode 100644
index 0000000..9030531
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DeleteVolumeCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DriveSanitizeCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DriveSanitizeCommand.cpython-310.pyc
new file mode 100644
index 0000000..cb04d95
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/DriveSanitizeCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/FactoryResetControllerCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/FactoryResetControllerCommand.cpython-310.pyc
new file mode 100644
index 0000000..ab05be6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/FactoryResetControllerCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/StorageControllerCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/StorageControllerCommand.cpython-310.pyc
new file mode 100644
index 0000000..ceea9b5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/StorageControllerCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..519d96b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_ARRAY_COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/SmartNicCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/SmartNicCommand.py
new file mode 100644
index 0000000..9907da5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/SmartNicCommand.py
@@ -0,0 +1,720 @@
+###
+# Copyright 2016-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Smart Nic Command for rdmc"""
+import os
+import time
+
+try:
+ from rdmc_helper import (
+ UI,
+ FirmwareUpdateError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ UploadError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ UI,
+ FirmwareUpdateError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ UploadError,
+ )
+
+
+class SmartNicCommand:
+ """Smart nic command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "smartnic",
+ "usage": None,
+ "description": "\tRun without arguments for the "
+ "current list of smartnic including json format.\n\texample: "
+ "smartnic/smartnic -j\n\n"
+ "Show all available options\n\n\t"
+ "example: smartnic --id \n\t"
+ "smartnic --id -j\n\t"
+ "smartnic --id --logs\n\t"
+ "smartnic --id --bootprogress\n\t"
+ "smartnic --id --clearlog\n\t"
+ "smartnic --id --update_fw \n\t"
+ "smartnic --id --reset < GracefulShutdown / ForceRestart / Nmi / GracefulRestart >\n",
+ "summary": "Discovers all pensando nic installed in the " "server",
+ "aliases": [],
+ "auxcommands": ["SelectCommand"],
+ }
+ self.config_file = None
+ self.fdata = None
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Smartnic command"""
+
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+ if args:
+ raise InvalidCommandLineError("servernic command takes no " "arguments.")
+
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver < 5.268:
+ raise IncompatibleiLOVersionError(
+ "Please upgrade to iLO 5 2.68 or " "greater to ensure correct flash of this firmware."
+ )
+
+ self.smartnicvalidation(options)
+
+ path = self.rdmc.app.typepath.defs.systempath
+
+ info = self.gatherinfo(path)
+
+ if not info:
+ raise InvalidCommandLineError("No SmartNic available.")
+
+ fw_version = self.get_fw_version()
+ options_system = True
+
+ if options.json and options.id is not None:
+ string_id = options.id
+ to_list = list(string_id.split(","))
+ for id in to_list:
+ if options.bootprogress:
+ if id == info["Id"]:
+ json_content = self.print_get_bootprogress(info)
+ UI().print_out_json(json_content)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("No bootprogress present for given smartnic id %s \n" % id)
+
+ elif options.logs:
+ if id == info["Id"]:
+ json_content = self.print_get_logs(info)
+ UI().print_out_json(json_content)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("No logs present for given smartnic id %s \n" % id)
+
+ elif options_system:
+ if id == info["Id"] and info["SystemType"] == "DPU":
+ json_content = self.build_json_out(info, fw_version)
+ UI().print_out_json(json_content)
+ elif id != info["Id"] or info["SystemType"] != "DPU":
+ self.rdmc.ui.printer("System %s:\n" % id)
+ self.rdmc.ui.printer("\tNo SmartNic present in given id %s \n" % id)
+
+ elif options.json and options.id is None and options_system and not options.logs and not options.bootprogress:
+ if info["SystemType"] == "DPU":
+ json_content = self.build_json_out(info, fw_version)
+ UI().print_out_json(json_content)
+ if info["SystemType"] != "DPU":
+ self.rdmc.ui.printer("System %s:\n" % id)
+ self.rdmc.ui.printer("\tNo SmartNic present in given id %s \n" % id)
+
+ elif (options.json and options.logs and options.id is None) or (
+ options.logs and not options.json and options.id is None
+ ):
+ raise InvalidCommandLineError(
+ "No command --logs for smartnic\n"
+ "usage: smartnic --id --logs \n"
+ "\t smartnic --id --logs -j"
+ )
+
+ elif (options.bootprogress and options.json and options.id is None) or (
+ options.bootprogress and not options.json and options.id is None
+ ):
+ raise InvalidCommandLineError(
+ "No command --bootprogress for smartnic\n"
+ "usage: smartnic --id --bootprogress\n"
+ "\t smartnic --id --bootprogress -j"
+ )
+
+ else:
+ if options.id is not None:
+ string_id = options.id
+ str_lst = list(string_id.split(","))
+ if options.update_fw is None and options.reset is None:
+ for id in str_lst:
+ if options.bootprogress:
+ if id == info["Id"]:
+ self.get_bootprogress(info)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("No bootprogress present for given smartnic id %s\n" % id)
+
+ elif options.logs:
+ if id == info["Id"]:
+ self.get_logs(info)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("No logs present for given smartnic id %s \n" % id)
+
+ elif options_system and not options.clearlog and not options.logs:
+ if id == info["Id"] and info["SystemType"] == "DPU":
+ self.prettyprintinfo(info, fw_version)
+ elif id != info["Id"] or info["SystemType"] != "DPU":
+ self.rdmc.ui.printer("System %s:\n" % id)
+ self.rdmc.ui.printer("\tNo SmartNic present in given id %s \n" % id)
+
+ elif options.clearlog and options_system:
+ try:
+ for id in str_lst:
+ if id == info["Id"]:
+ self.clearlog(options, info, id)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("Given smartnic id is not present to clear the logs.")
+ except Exception as excp:
+ raise excp
+
+ elif options.update_fw is not None:
+ try:
+ self.upload_firmware(options, info)
+ except Exception as excp:
+ # if SessionExpired:
+ # time.sleep(320) # wait till reboot the server
+ # self.smartnicvalidation(options)
+ # self.cmdbase.login_select_validation(self, options)
+ # else:
+ raise excp
+
+ elif options.reset is not None:
+ try:
+ for id in str_lst:
+ if id == info["Id"]:
+ self.get_resettype(options, info, id)
+ elif id != info["Id"]:
+ self.rdmc.ui.printer("No reset type present for given smartnic id %s \n" % id)
+
+ except Exception as excp:
+ raise excp
+
+ else:
+ if options.clearlog and options_system:
+ raise InvalidCommandLineError(
+ "No command --clearlog for smartnic\n" "usage: smartnic --id --clearlog"
+ )
+
+ if options_system and not options.clearlog:
+ if info["SystemType"] == "DPU":
+ self.prettyprintinfo(info, fw_version)
+ elif info["SystemType"] != "DPU":
+ self.rdmc.ui.printer("No SmartNic available.")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def get_fw_version(self):
+ # to get pensando firmware version
+ name = []
+ f_version = ""
+ fw_path = "/redfish/v1/UpdateService/FirmwareInventory/?$expand=."
+ fw_result = self.rdmc.app.get_handler(fw_path, service=True, silent=True)
+ fw_result = fw_result.dict
+ members = fw_result["Members"]
+ for n in members:
+ name_version = n["Name"], n["Version"]
+ name.append(name_version)
+
+ for fw_name in name:
+ if "Pensando" in fw_name[0]:
+ f_version += fw_name[1]
+ return f_version
+
+ def clearlog(self, options, info, id):
+ try:
+ log_service = info["LogServices"]
+ for log_id in log_service.values():
+ data = self.rdmc.app.get_handler(log_id, service=True, silent=True).dict
+ members = data["Members"]
+ for val in members:
+ for dpu in val.values():
+ dpu_data = self.rdmc.app.get_handler(dpu, service=True, silent=True).dict
+ path = dpu_data["Actions"]["#LogService.ClearLog"]["target"]
+ action = path.split("/")[-2]
+ action = {"Action": action}
+ self.rdmc.app.post_handler(path, action)
+
+ except Exception as excp:
+ raise excp
+
+ def get_resettype(self, options, info, id):
+ self.printreboothelp(options.reset)
+ time.sleep(3)
+
+ if id == info["Id"]:
+ put_path = info["@odata.id"]
+
+ for item in info["Actions"]:
+ if "Reset" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ # action = item.split("#")[-1]
+ put_path = info["Actions"][item]["target"]
+ break
+
+ if options.reset.lower() == "gracefulshutdown":
+ body = {"ResetType": "GracefulShutdown"}
+ elif options.reset.lower() == "forcerestart":
+ body = {"ResetType": "ForceRestart"}
+ elif options.reset.lower() == "nmi":
+ body = {"ResetType": "Nmi"}
+ elif options.reset.lower() == "gracefulrestart":
+ body = {"ResetType": "GracefulRestart"}
+
+ self.rdmc.app.post_handler(put_path, body)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def printreboothelp(self, flag):
+ if flag.upper() == "FORCERESTART":
+ self.rdmc.ui.warn(
+ "\nForcing a server restart. Note, the current session will be "
+ "terminated.\nPlease wait for the server to boot completely before logging in "
+ "again.\nRebooting the server in 3 seconds...\n"
+ )
+ elif flag.upper() == "NMI":
+ self.rdmc.ui.warn(
+ "\nA non-maskable interrupt will be issued to this server. Note, the "
+ "current session will be terminated.\nIssuing interrupt in 3 seconds...\n"
+ )
+
+ elif flag.upper() == "GRACEFULSHUTDOWN":
+ self.rdmc.ui.warn(
+ "\nThe server will be graceful shutdown. Note, the current session will be "
+ "terminated.\nPlease wait for the server to boot completely before logging in "
+ "again.\nRebooting the server in 3 seconds...\n"
+ )
+
+ elif flag.upper() == "GRACEFULRESTART":
+ self.rdmc.ui.warn(
+ "\nGraceful server restart. Note, the current session will be "
+ "terminated.\nPlease wait for the server to boot completely before logging in "
+ "again.\nRebooting the server in 3 seconds...\n"
+ )
+
+ def upload_firmware(self, options, info):
+ try:
+ string_id = options.id
+ to_list = list(string_id.split(","))
+ for id in to_list:
+ for info_id in info["Id"]:
+ if id == info_id:
+ target = info["@odata.id"]
+ fw_path = options.update_fw
+ results = self.rdmc.app.select(selector="UpdateService.", path_refresh=True)[0].dict
+ results = results["Actions"]["#UpdateService.SimpleUpdate"]["target"]
+ string_split = fw_path.split("/")
+ for tar in string_split:
+ if tar.endswith("tar"):
+ self.rdmc.ui.printer("Uploading firmware: %s\n" % os.path.basename(tar))
+
+ res = self.rdmc.app.post_handler(results, {"ImageURI": fw_path, "Targets": [target]})
+
+ # Taskmonitor to find the firmware status
+ task_id = res.dict["TaskMonitor"]
+
+ if res.status == 202:
+ status = self.wait_for_state_change(task_id, options)
+ if status:
+ self.rdmc.ui.printer(
+ "Component " + tar + " uploaded successfully.\n"
+ "A reboot may be required for firmware changes to take effect.\n"
+ )
+
+ if not status:
+ # Failed to upload the component.
+ raise UploadError("Error while processing the component.")
+
+ elif (res.status == 404) or (res.status == 402):
+ return ReturnCodes.FAILED_TO_UPLOAD_COMPONENT
+
+ except (FirmwareUpdateError, UploadError) as excp:
+ raise excp
+
+ def wait_for_state_change(self, taskid, options, wait_time=4800):
+ """Wait for the iLO UpdateService to a move to terminal state.
+ :param options: command line options
+ :type options: list.
+ :param wait_time: time to wait on upload
+ :type wait_time: int.
+ """
+ total_time = 0
+ spinner = ["|", "/", "-", "\\"]
+ state = ""
+ self.rdmc.ui.printer("Waiting for iLO UpdateService to finish flashing the firmware \n")
+
+ while total_time < wait_time:
+ state, _ = self.get_update_service_state(taskid)
+ if state == "ERROR":
+ return False
+ elif (
+ (state == "Running")
+ or (state == "New")
+ or (state == "Starting")
+ or (state == "Interrupted")
+ or (state == "Suspended")
+ ):
+ # Lets try again after 8 seconds
+ count = 0
+ # fancy spinner
+ while count <= 32:
+ self.rdmc.ui.printer("Updating: %s\r" % spinner[count % 4])
+ time.sleep(0.25)
+ count += 1
+ total_time += 8
+ elif state == "Completed":
+ break
+
+ if total_time >= wait_time:
+ raise FirmwareUpdateError("UpdateService in " + state + " state for " + str(wait_time) + "s")
+
+ return True
+
+ def get_update_service_state(self, taskid):
+ results = self.rdmc.app.get_handler(taskid, service=True, silent=True)
+ if results.status == 202:
+ return results.dict["TaskState"], results.dict
+
+ if results and results.status == 200 and results.dict:
+ output = results.dict
+ err = output["error"]["@Message.ExtendedInfo"]
+ for e in err:
+ msgsrg = e["MessageArgs"]
+ return msgsrg[0], results.dict
+ else:
+ return "UNKNOWN", {}
+
+ def print_get_bootprogress(self, info):
+ try:
+ if info is not None:
+ for id in info["Id"]:
+ content = {"Id": id}
+ content.update({"LastState": info["BootProgress"]["LastState"]})
+ content.update({"OemLastState": info["BootProgress"]["OemLastState"]})
+ content_json = {"Boot Progress": content}
+ return content_json
+ except Exception as excp:
+ raise excp
+
+ def get_bootprogress(self, info):
+ boot_output = ""
+ boot_output = "------------------------------------------------\n"
+ boot_output += "Boot Progress \n"
+ boot_output += "------------------------------------------------\n"
+ if info is not None:
+ for id in info["Id"]:
+ boot_output += "Id: %s\n" % id
+ boot_output += "LastState: %s\n" % info["BootProgress"]["LastState"]
+ boot_output += "OemLastState: %s\n" % info["BootProgress"]["OemLastState"]
+ self.rdmc.ui.printer(boot_output, verbose_override=True)
+
+ def print_get_logs(self, info):
+ log_service = info["LogServices"]
+ for log_id in log_service.values():
+ data = self.rdmc.app.get_handler(log_id, service=True, silent=True).dict
+ members = data["Members"]
+ for val in members:
+ for dpu in val.values():
+ dpu_data = self.rdmc.app.get_handler(dpu, service=True, silent=True).dict
+ entry = dpu_data["Entries"]
+ for dpu_entry in entry.values():
+ result = self.rdmc.app.get_handler(dpu_entry, service=True, silent=True).dict
+ result = result["Members"]
+ if not result:
+ content = {}
+ else:
+ for member in result:
+ get_res = member["@odata.id"]
+ get_result = self.rdmc.app.get_handler(get_res, service=True, silent=True).dict
+ content = {"Id": get_result["Id"]}
+ content.update({"Name": get_result["Name"]})
+ content.update({"Created": get_result["Created"]})
+ content.update({"EntryType": get_result["EntryType"]})
+ content.update({"Severity": get_result["Severity"]})
+ message = get_result["Message"]
+ message = message.split(":")
+ content.update({message[0]: message[1]})
+ content_json = {"Logs View": content}
+ return content_json
+
+ def get_logs(self, info):
+ log_output = ""
+ log_service = info["LogServices"]
+ for log_id in log_service.values():
+ data = self.rdmc.app.get_handler(log_id, service=True, silent=True).dict
+ members = data["Members"]
+ log_output = "------------------------------------------------\n"
+ log_output += "Logs View \n"
+ log_output += "------------------------------------------------\n"
+ for val in members:
+ for dpu in val.values():
+ dpu_data = self.rdmc.app.get_handler(dpu, service=True, silent=True).dict
+ entry = dpu_data["Entries"]
+ for dpu_entry in entry.values():
+ result = self.rdmc.app.get_handler(dpu_entry, service=True, silent=True).dict
+ result = result["Members"]
+ for member in result:
+ get_res = member["@odata.id"]
+ get_result = self.rdmc.app.get_handler(get_res, service=True, silent=True).dict
+ log_output += "Id: %s\n" % get_result["Id"]
+ log_output += "Name: %s\n" % get_result["Name"]
+ log_output += "Created: %s\n" % get_result["Created"]
+ log_output += "EntryType: %s\n" % get_result["EntryType"]
+ log_output += "Severity: %s\n" % get_result["Severity"]
+ log_output += "%s\n" % get_result["Message"]
+ self.rdmc.ui.printer(log_output, verbose_override=True)
+
+ def prettyprintinfo(self, info, fw_version):
+ output = ""
+ output = "------------------------------------------------\n"
+ output += "SmartNic Information\n"
+ output += "------------------------------------------------\n"
+
+ if info is not None:
+ for id in info["Id"]:
+ output += "System %s:\n" % id
+ output += "\tModel: %s\n" % info["Model"]
+ output += "\tManufacturer: %s\n" % info["Manufacturer"]
+ output += "\tName: %s\n" % info["Name"]
+ output += "\tFirmware Version: %s\n" % fw_version
+ output += "\tPowerState: %s\n" % info["PowerState"]
+ output += "\tSerialNumber: %s\n" % info["SerialNumber"]
+ output += "\tSystemType: %s\n" % info["SystemType"]
+ try:
+ output += "\tHealth: %s\n" % info["Status"]["Health"]
+ except KeyError:
+ pass
+ try:
+ output += "\tState: %s\n" % info["Status"]["State"]
+ except KeyError:
+ pass
+ try:
+ output += (
+ "\tOperating System: %s \n"
+ % info["Oem"][self.rdmc.app.typepath.defs.oemhp]["OperatingSystem"]["Kernel"]["Name"]
+ )
+ except KeyError:
+ pass
+ try:
+ output += (
+ "\tOS Version: %s \n"
+ % info["Oem"][self.rdmc.app.typepath.defs.oemhp]["OperatingSystem"]["Kernel"]["Version"]
+ )
+ except KeyError:
+ pass
+ try:
+ avail_sys = info["Oem"][self.rdmc.app.typepath.defs.oemhp]["AvailableSystemCapabilities"]
+ avail_sys = str(avail_sys).strip("[]''")
+ output += "\tAvailable SystemCapabilities: %s \n" % avail_sys
+ except KeyError:
+ pass
+ try:
+ enable_sys = info["Oem"][self.rdmc.app.typepath.defs.oemhp]["EnabledSystemCapabilities"]
+ enable_sys = str(enable_sys).strip("[]''")
+ output += "\tEnable SystemCapabilities: %s \n" % enable_sys
+ except KeyError:
+ pass
+ try:
+ integration_con = info["Oem"][self.rdmc.app.typepath.defs.oemhp]["IntegrationConfig"]
+ integration_con = str(integration_con).strip("'{} ")
+ integration_con = integration_con.replace("'", "")
+ output += "\tIntegration Config: %s \n" % integration_con
+ except KeyError:
+ pass
+
+ output += "\tUUID: %s\n" % info["UUID"]
+
+ self.rdmc.ui.printer(output, verbose_override=True)
+
+ def build_json_out(self, info, fw_version):
+ if info is not None:
+ for id in info["Id"]:
+ content = {"model": info["Model"]}
+ content.update({"Manufacturer": info["Manufacturer"]})
+ content.update({"Name": info["Name"]})
+ content.update({"Firmware Version": fw_version})
+ content.update({"PowerState": info["PowerState"]})
+ content.update({"SerialNumber": info["SerialNumber"]})
+ content.update({"SystemType": info["SystemType"]})
+ try:
+ content.update({"Health": info["Status"]["Health"]})
+ except KeyError:
+ pass
+ try:
+ content.update({"State": info["Status"]["State"]})
+ except KeyError:
+ pass
+ try:
+ content.update(
+ {
+ "Operating System": info["Oem"][self.rdmc.app.typepath.defs.oemhp]["OperatingSystem"][
+ "Kernel"
+ ]["Name"]
+ }
+ )
+ except KeyError:
+ pass
+ try:
+ content.update(
+ {
+ "OS Version": info["Oem"][self.rdmc.app.typepath.defs.oemhp]["OperatingSystem"]["Kernel"][
+ "Version"
+ ]
+ }
+ )
+ except KeyError:
+ pass
+ try:
+ content.update(
+ {
+ "Available SystemCapabilities": info["Oem"][self.rdmc.app.typepath.defs.oemhp][
+ "AvailableSystemCapabilities"
+ ]
+ }
+ )
+ except KeyError:
+ pass
+ try:
+ content.update(
+ {
+ "Enable SystemCapabilities": info["Oem"][self.rdmc.app.typepath.defs.oemhp][
+ "EnabledSystemCapabilities"
+ ]
+ }
+ )
+ except KeyError:
+ pass
+ try:
+ content.update(
+ {"Integration Config": info["Oem"][self.rdmc.app.typepath.defs.oemhp]["IntegrationConfig"]}
+ )
+ except KeyError:
+ pass
+ content.update({"UUID": info["UUID"]})
+ system = {"System %s" % id: content}
+ content_json = {"SmartNic Information": system}
+ return content_json
+
+ def gatherinfo(self, path):
+ try:
+ # info = {}
+ results = ""
+ csysresults = self.rdmc.app.select(selector="ComputerSystemCollection.")
+ csysresults = csysresults[0].dict
+ members = csysresults["Members"]
+ for id in members:
+ for mem_id in id.values():
+ if path != mem_id:
+ results = self.rdmc.app.get_handler(mem_id, service=True, silent=True).dict
+ return results
+ except:
+ pass
+
+ def smartnicvalidation(self, options):
+ """smartnic validation function.
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "--id",
+ dest="id",
+ help="Optionally include this flag to retrive the selected smartnic .",
+ default=None,
+ )
+
+ customparser.add_argument(
+ "--logs",
+ dest="logs",
+ action="store_true",
+ help="Optionally include this flag to list all the logs .",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "--bootprogress",
+ dest="bootprogress",
+ action="store_true",
+ help="Optionally include this flag to list the bootprogress information",
+ default=False,
+ )
+
+ customparser.add_argument(
+ "--update_fw",
+ dest="update_fw",
+ help="Include this flag to update firmware to pensando card",
+ default=None,
+ )
+ # customparser.add_argument(
+ # "--update_os",
+ # dest="update_os",
+ # help="Include this flag to update OS to pensando card",
+ # default=None,
+ # )
+
+ customparser.add_argument(
+ "--reset",
+ dest="reset",
+ help="Add this flag to reset the server",
+ nargs="?",
+ type=str,
+ const="GracefulRestart",
+ )
+
+ customparser.add_argument(
+ "--clearlog",
+ dest="clearlog",
+ action="store_true",
+ help="Optionally include this flag to clear log",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/SmartNicCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/SmartNicCommand.cpython-310.pyc
new file mode 100644
index 0000000..996cfa0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/SmartNicCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..1b0f034
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/SMART_NIC_COMMANDS/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/__init__.py
new file mode 100644
index 0000000..60ddfb7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/__init__.py
@@ -0,0 +1,32 @@
+"""find and add dynamic extensions"""
+
+import os
+
+tl = []
+classNames = []
+Commands = {}
+
+extensionDir = os.path.dirname(__file__)
+
+if os.name != "nt":
+ replacement = "/"
+else:
+ replacement = "\\"
+
+for cwd, dirs, filenames in os.walk(extensionDir):
+ dirs[:] = [d for d in dirs if not d[0] == "."]
+ tl.append((cwd, [files for files in filenames if not files[0] == "."]))
+
+for cwd, names in tl:
+ cn = cwd.split("extensions")[-1]
+ cn = cn.replace(replacement, ".")
+ comms = []
+ for name in names:
+ if name.endswith(".pyc") and "__" not in name:
+ name = name.replace(".pyc", "")
+ classNames.append(cn + "." + name + "." + name)
+ elif name.endswith(".py") and "__" not in name:
+ name = name.replace(".py", "")
+ if name + ".pyc" in names:
+ continue
+ classNames.append(cn + "." + name + "." + name)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..3454b84
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AHSdiagCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AHSdiagCommand.py
new file mode 100644
index 0000000..4136b90
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AHSdiagCommand.py
@@ -0,0 +1,519 @@
+###
+# Copyright 2017 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""AHS diag Command for rdmc"""
+
+import os
+from ctypes import POINTER, c_char_p, c_int, c_ubyte, create_string_buffer
+
+import redfish.hpilo.risblobstore2 as risblobstore2
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ LOGGER,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+from redfish.hpilo.rishpilo import HpIlo
+
+if os.name != "nt":
+ from _ctypes import dlclose
+
+
+class AHSdiagCommand:
+ """Add Sign/Marker posts into the AHS logs of the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "ahsdiag",
+ "usage": None,
+ "description": "Adding sign or Marker Post into AHS logs."
+ "\n\tahsdiag --WriteSignPost \n\tahsdiag --WriteMarkerPost"
+ ' --instance 1 --markervalue 3 --markertext "DIMM Test Start"',
+ "summary": "Adding sign or Marker Post into AHS logs.",
+ "aliases": ["ahsops"],
+ "auxcommands": [
+ "LoginCommand",
+ "SelectCommand",
+ "LogoutCommand",
+ "ServerlogsCommand",
+ ],
+ }
+
+ self.dynamicclass = -1
+ self.lib = None
+ self.channel = None
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main ahsdiag function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.ahsdiagvalidation(options)
+ self.ahsdiagworkerfunction(options)
+ self.cmdbase.logout_routine(self, options)
+ return ReturnCodes.SUCCESS
+
+ def ahsdiagworkerfunction(self, options):
+ """Main ahsdig worker function
+
+ :param options: command line options
+ :type options: list.
+ """
+ if not options:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if self.lib is None and self.channel is None:
+ self.resetilo()
+
+ if options.signpost and options.markerpost:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if options.signpost:
+ if options.inst or options.mval or options.mtext:
+ raise InvalidCommandLineError("")
+ self.addsignpost(options)
+ elif options.markerpost:
+ if options.inst and options.mval and options.mtext:
+ self.addmarkerpost(options)
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+ else:
+ self.rdmc.ui.printer("Choose an operation!\n")
+ raise InvalidCommandLineErrorOPTS("")
+
+ def addsignpost(self, options):
+ """Function to add signpost
+
+ :param options: command line options
+ :type options: list.
+ """
+ excp = None
+ for _ in range(0, 3): # channel loop for iLO
+ self.rdmc.ui.printer("Start writing sign post ... \n")
+ try:
+ if not self.spregister():
+ raise
+ self.rdmc.ui.printer("Successfully registered.\n")
+ fail = True
+
+ for _ in range(3):
+ if self.signpostregister():
+ self.rdmc.ui.printer("Signpost was successful!\n")
+ fail = False
+ break
+ else:
+ self.rdmc.ui.printer("Signpost attempt failed!\n")
+
+ if options.postIML and fail:
+ self.addimlpost(options)
+
+ self.unregister()
+ return
+ except Exception:
+ if self.rdmc.opts.verbose:
+ self.rdmc.ui.printer("Retrying with new channel...\n")
+ self.resetilo()
+ raise excp
+
+ def spregister(self):
+ """Function that registers the signpost"""
+ self.bb_class_allocate()
+
+ if self.dynamicclass == -1:
+ return False
+
+ self.lib.bb_register_wrapper(self.dynamicclass)
+ size = self.lib.sizeofbbregister()
+ rcode = self.dowriteread(size)
+
+ if rcode == 0:
+ self.lib.bb_descriptor_code_wrapper(self.dynamicclass)
+ size = self.lib.sizeofbbdescriptorcode()
+ rcode = self.dowriteread(size)
+
+ # if rcode > 0:
+ # return True
+ # else:
+ # return False
+ return True
+
+ def bb_class_allocate(self):
+ """Function to obtain a handle for further operations"""
+ self.lib.bb_class_allocate_wrapper()
+ self.lib.getreq.restype = POINTER(c_ubyte)
+ ptrreq = self.lib.getreq()
+
+ # sizeofbbchifresp = self.lib.sizeofBBCHIFRESP()
+ size = self.lib.sizeofbbclassalloc()
+ data = bytearray(ptrreq[:size][:])
+
+ if self.rdmc.opts.debug:
+ LOGGER.debug("Sending data to chif:{0}\n".format(bytes(data)))
+
+ resp = self.channel.chif_packet_exchange(data)
+
+ resp = create_string_buffer(bytes(resp))
+ self.lib.setresp.argtypes = [c_char_p]
+ self.lib.setresp(resp)
+
+ if len(resp) != 0:
+ self.dynamicclass = self.lib.respmsgrcode()
+ self.rdmc.ui.printer("Do Read Return error code:{0}\n".format(self.dynamicclass))
+ else:
+ self.dynamicclass = 0
+
+ def unregister(self):
+ """Function to unregister the handle"""
+ self.lib.bb_unregister_wrapper()
+ size = self.lib.sizeinunregister()
+ rcode = self.dowriteread(size)
+
+ if rcode == 0:
+ self.rdmc.ui.printer("Unregistration successful!\n")
+ else:
+ self.rdmc.ui.printer("Failed to unregister with code:{0}.\n".format(rcode))
+
+ def dowriteread(self, size):
+ """Function responsible for communication with the iLO
+
+ :param size: size of the buffer to be read or written
+ :type size: int
+ """
+ ptrreq = self.lib.getreq()
+ data = bytearray(ptrreq[:size][:])
+ if self.rdmc.opts.debug:
+ LOGGER.debug("Sending data to chif:{0}\n".format(data))
+ try:
+ resp = self.channel.chif_packet_exchange(data)
+ except Exception:
+ self.resetilo()
+ return -1
+
+ self.lib.setresp.argtypes = [c_char_p]
+ resp = create_string_buffer(bytes(resp))
+ self.lib.setresp(resp)
+ rcode = self.lib.respmsgrcode()
+
+ if rcode == 0:
+ self.lib.updatehandle()
+
+ return rcode
+
+ def signpostregister(self):
+ """Worker function of signpost register"""
+ self.lib.bb_code_set_wrapper()
+ size = self.lib.sizeinbbcodeset()
+ returnval = self.dowriteread(size)
+
+ if returnval != 0:
+ self.printsignpostregister(returnval)
+ return False
+
+ self.lib.bb_log_wrapper()
+ size = self.lib.sizeinbblog()
+ returnval = self.dowriteread(size)
+
+ if returnval != 0:
+ self.printsignpostregister(returnval)
+ return False
+
+ self.lib.bb_log_static_wrapper()
+ size = self.lib.sizeinbblog()
+ returnval = self.dowriteread(size)
+
+ if returnval != 0:
+ self.printsignpostregister(returnval)
+ return False
+
+ return True
+
+ def printsignpostregister(self, returnval):
+ """Commonly used output statement.
+
+ :param returnval: return code for signpost
+ :type returnval: int
+ """
+ self.rdmc.ui.printer("return signpost register failed={0}\n".format(returnval))
+
+ def addmarkerpost(self, options):
+ """Function to add marker post
+
+ :param options: command line options
+ :type options: list.
+ """
+ excp = None
+ for _ in range(0, 3): # channel loop for iLO
+ self.rdmc.ui.printer("Start writing marker post ... \n")
+
+ try:
+ for i in range(3):
+ if self.mpregister():
+ break
+
+ self.rdmc.ui.printer("attempting to register Marker Post..." "failed {0} times\n".format(i + 1))
+
+ self.resetilo()
+
+ self.rdmc.ui.printer("Successfully registered.\n")
+ fail = True
+
+ for _ in range(3):
+ if self.markerpostregister(options):
+ self.rdmc.ui.printer("Marker Post was successful!\n")
+ fail = False
+ break
+ else:
+ self.rdmc.ui.printer("Marker Post attempt failed!\n")
+
+ if options.postIML and fail:
+ self.addimlpost(options)
+
+ self.unregister()
+ return
+ except Exception:
+ if self.rdmc.opts.verbose:
+ self.rdmc.ui.printer("Retrying with new channel...\n")
+ self.resetilo()
+ raise excp
+
+ def mpregister(self):
+ """Function that registers the marker post"""
+ self.bb_class_allocate()
+ self.rdmc.ui.printer("return code(dynamic class)={0}.\n".format(self.dynamicclass))
+
+ if self.dynamicclass == -1:
+ return False
+
+ self.lib.bb_register_mwrapper(self.dynamicclass)
+ size = self.lib.sizeofbbregister()
+ rcode = self.dowriteread(size)
+
+ if rcode == 0:
+ self.lib.bb_descriptor_code_mwrapper(self.dynamicclass)
+ size = self.lib.sizeofbbdescriptorcode()
+ rcode = self.dowriteread(size)
+
+ if rcode == -1:
+ return False
+ self.lib.bb_descriptor_field_mwrapper(self.dynamicclass)
+ size = self.lib.sizeofbbdescriptorcode()
+ rcode = self.dowriteread(size)
+ if rcode == -1:
+ return False
+
+ return True
+
+ def markerpostregister(self, options):
+ """Worker function of marker post register
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.lib.bb_code_set_mwrapper()
+ size = self.lib.sizeinbbcodeset()
+ returnval = self.dowriteread(size)
+
+ if returnval != 0:
+ return False
+
+ mval = int(options.mval)
+ minst = int(options.inst)
+ mtext = create_string_buffer(bytes(options.mtext, "utf-8"))
+
+ self.lib.markerpost_wrapper.argtypes = [c_int, c_int, c_char_p]
+ self.lib.markerpost_wrapper(mval, minst, mtext)
+
+ size = self.lib.sizeinbblogm()
+ returnval = self.dowriteread(size)
+
+ if returnval != 0:
+ return False
+
+ return True
+
+ def resetilo(self):
+ """Function to reset iLO"""
+ if self.channel:
+ self.channel.close()
+ self.loadlib()
+ self.channel = HpIlo(dll=self.lib)
+ self.lib.ChifDisableSecurity()
+
+ def loadlib(self):
+ """Function to load the so library"""
+ self.closedll()
+ try:
+ if os.name == "nt":
+ self.rdmc.ui.printer("Operation can be performed only on Unix based systems!\n")
+ raise InvalidCommandLineErrorOPTS("")
+ else:
+ self.lib = risblobstore2.BlobStore2.gethprestchifhandle()
+ except Exception as excp:
+ raise InvalidCommandLineErrorOPTS(excp)
+
+ def closedll(self):
+ """Deallocate dll handle."""
+ try:
+ dlclose(self.lib)
+ except Exception:
+ pass
+
+ def addimlpost(self, options):
+ """Adding maintenance post from serverlogs.
+
+ :param options: command line options
+ :type options: list.
+ """
+ if options.signpost:
+ imltext = "Signpost Writing Failed"
+ elif options.markerpost:
+ imltext = "Markerpost Writing Failed"
+
+ options.service = "IML"
+ options.clearlog = None
+ options.mainmes = imltext
+
+ path = self.serverlogsobj.returnimlpath()
+ self.serverlogsobj.addmaintenancelogentry(options, path=path)
+
+ def ahsdiagvalidation(self, options):
+ """ahsdiag method validation function"""
+ client = None
+
+ try:
+ self.cmdbase.login_select_validation(self, options)
+ client = self.rdmc.app.current_client
+ except Exception:
+ if client:
+ if not client.base_url == "blobstore://.":
+ raise InvalidCommandLineError("ahsdiag command " "available in local mode only.\n")
+
+ if self.rdmc.config.url:
+ raise InvalidCommandLineError("ahsdiag command " "available in local mode only.\n")
+
+ if not client:
+ self.lobobj.run("")
+
+ def definearguments(self, customparser):
+ """Defines the required arguments for ahsdiag command.
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--selectlog",
+ dest="service",
+ help="""Read log from the given log service. Options: IML, """ """IEL or AHS.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--clearlog",
+ "-c",
+ dest="clearlog",
+ action="store_true",
+ help="""Clears the logs for a the selected option.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--maintenancemessage",
+ "-m",
+ dest="mainmes",
+ help="""Maintenance message to be inserted into the log. """ """(IML LOGS ONLY FEATURE)""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--customiseAHS",
+ dest="customiseAHS",
+ help="""Allows customised AHS log data to be downloaded.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-s",
+ "--WriteSignPost",
+ dest="signpost",
+ action="store_true",
+ help="""Writes a sign post into the AHS log.""",
+ default=False,
+ )
+ customparser.add_argument(
+ "-r",
+ "--WriteMarkerPost",
+ dest="markerpost",
+ action="store_true",
+ help="""Writes a marker post into the AHS log.""",
+ default=False,
+ )
+ customparser.add_argument(
+ "-i",
+ "--instance",
+ dest="inst",
+ help="""Argument required by marker post.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-l",
+ "--markervalue",
+ dest="mval",
+ help="""Argument required by marker post.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-t",
+ "--markertext",
+ dest="mtext",
+ help="""Argument required by marker post.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-w",
+ "--WriteIMLPost",
+ dest="postIML",
+ action="store_true",
+ help="""Writes an IML entry if failure occurs.""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AutomaticTestingCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AutomaticTestingCommand.py
new file mode 100644
index 0000000..53fc9f3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/AutomaticTestingCommand.py
@@ -0,0 +1,2782 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+
+# -*- coding: utf-8 -*-
+"""Automatic Testing for rdmc"""
+
+import datetime
+import json
+import os
+import platform
+import random
+import re
+import string
+import sys
+import time
+import traceback
+from logging import FileHandler
+from zipfile import ZipFile
+
+from six.moves.urllib.request import urlopen, urlretrieve
+
+try:
+ from rdmc_helper import (
+ HARDCODEDLIST,
+ CommandNotEnabledError,
+ Encryption,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ PathUnavailableError,
+ ReturnCodes,
+ )
+except:
+ from ilorest.rdmc_helper import (
+ HARDCODEDLIST,
+ CommandNotEnabledError,
+ Encryption,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidOrNothingChangedSettingsError,
+ NoChangesFoundOrMadeError,
+ NoContentsFoundForOperationError,
+ PathUnavailableError,
+ ReturnCodes,
+ )
+
+from redfish.ris.rmc_helper import IloResponseError
+
+__error_logfile__ = "automatictesting_error_logfile.log"
+__pass_fail__ = "passfail"
+__last_line__ = "lastline"
+
+
+def log_decor(func):
+ """decorator to gather and log traceback while continuing testing"""
+
+ def func_wrap(*args, **kwargs):
+ """decorator wrapper function"""
+ try:
+ func(*args, **kwargs)
+ except Exception as exp:
+ if isinstance(exp, dict) or isinstance(exp, list):
+ # DOES NOT WORK YET. For errors which are captured and gathered before a raise, there
+ # is no point in showing the DEBUG log.
+ logging(command=func.__name__, error=exp, logfile_ignore=True)
+ elif args:
+ logging(command=func.__name__, error=exp, args=args[0])
+ else:
+ logging(command=func.__name__, error=exp)
+ else:
+ logging(command=func.__name__, args=args[0])
+
+ return func_wrap
+
+
+def logging(command, error=None, args=None, logfile_ignore=False):
+ """
+ handler for logging errors as well as the pass/fail log.
+
+ :param command: string representing the command/function which was tested
+ :param error: error string (if provided in the event of an exception)
+ :param args: arguments provided to the command/function which was tested. May need this
+ for various purposes (file handles, references to objects, references to object attributes)
+
+ """
+ debug_ret_err = False
+ debug_log_errs = []
+ data2 = []
+ data_passed_failed = {}
+ lockout = False
+ pass_flag = True
+
+ error_strs = ["Code:400", "Code:401", "Exception", "EXCEPTION", "Warning", "WARNING"]
+ if not error:
+ try:
+ if args:
+ if args.rdmc.opts.debug:
+ for handle in args.rdmc.app.logger.parent.handlers:
+ if isinstance(handle, FileHandler):
+ # obtain last 25 lines of the debug log (if '-d' is used)
+ with open(handle.baseFilename, "rb") as dlfh:
+ debug_log_errs = tail_debug_log(dlfh, 25)
+ for line in debug_log_errs:
+ for err in error_strs:
+ if err in line:
+ with open(__last_line__, "r") as ll_file:
+ tmp_err = ll_file.readline()
+ if err not in tmp_err:
+ with open(__last_line__, "w+") as ll_file:
+ ll_file.write(line)
+ error = line
+ pass_flag = False
+ break
+ if error:
+ break
+ except:
+ debug_ret_err = True
+
+ error_strs = ["Code:400", "Code:401", "Exception", "EXCEPTION", "Warning", "WARNING"]
+ if not error:
+ try:
+ if args:
+ if args.rdmc.opts.debug:
+ for handle in args.rdmc.app.logger.parent.handlers:
+ if isinstance(handle, FileHandler):
+ # obtain last 25 lines of the debug log (if '-d' is used)
+ with open(handle.baseFilename, "rb") as dlfh:
+ debug_log_errs = tail_debug_log(dlfh, 25)
+ for line in debug_log_errs:
+ for err in error_strs:
+ if err in line:
+ with open(__last_line__, "r") as ll_file:
+ tmp_err = ll_file.readline()
+ if err not in tmp_err:
+ with open(__last_line__, "w+") as ll_file:
+ ll_file.write(line)
+ error = line
+ pass_flag = False
+ break
+ if error:
+ break
+ except:
+ debug_ret_err = True
+
+ try:
+ fhandle = open(__pass_fail__, "r")
+ data = fhandle.readlines()
+ fhandle.close()
+ for itr in data:
+ if "Total: " in itr:
+ data2.append("Total: " + str(int(itr.split(":")[-1].split("\n")[0]) + 1))
+ continue
+ elif "Fail:" in itr and error:
+ data2.append("Fail: " + str(int(itr.split(":")[-1].split("\n")[0]) + 1))
+ data_passed_failed[command] = "'{}' Command - Failed".format(command)
+ elif "Pass:" in itr and pass_flag:
+ data2.append("Pass: " + str(int(itr.split(":")[-1].split("\n")[0]) + 1))
+ if command not in data_passed_failed:
+ data_passed_failed[command] = "'{}' Command - Passed".format(command)
+ elif "Command - " in itr:
+ resultdata = itr.split("'")
+ data_passed_failed[(resultdata[1])] = itr
+ elif not lockout:
+ lockout = True
+ data2.append(itr.split("\n")[0])
+ except Exception:
+ sys.stderr.write("An error occurred with Pass/Fail log counter.\n")
+ data2 = None
+ pass
+ finally:
+ fhandle = open(__pass_fail__, "w+")
+ for element in data2:
+ fhandle.writelines(element + "\n")
+ fhandle.writelines("\n")
+ for k, v in list(data_passed_failed.items()):
+ if "\n" in v:
+ fhandle.writelines(v)
+ else:
+ fhandle.writelines(v + "\n")
+ fhandle.close()
+
+ if error:
+ sys.stderr.write("An error occurred in: %s - %s\n" % (command, error))
+ with open(__error_logfile__, "a+") as fhandle:
+ fhandle.write("\n" + command + " - Test#: ")
+ if data[0]:
+ fhandle.write(str(int(data[0].split(":")[-1].split("\n")[0]) + 1))
+ else:
+ fhandle.write(" 1")
+
+ fhandle.write("\nSimplified Error: " + str(error) + "\n")
+ # if args:
+ fhandle.write("\nTraceback:\n------------------------\n")
+ fhandle.write(str(traceback.format_exc()) + "\n")
+ if not debug_ret_err and not logfile_ignore:
+ fhandle.write("\nTail of Debug Logfile:")
+ fhandle.write("\n------------------------\n")
+ for item in debug_log_errs:
+ fhandle.write(item)
+ fhandle.write("\n------END OF DEBUG------\n")
+
+
+def tail_debug_log(f, lines=1, _buffer=4098):
+ """Returns last n lines from the filename.
+
+ :param f: filename for 'tail' retrieval
+ :type f: file handle
+ :param lines: number of lines to retrieve
+ :type lines: int
+ :param _buffer: buffer space for blocks
+ :type _buffer: bytes
+ """
+ # place holder for the lines found
+ lines_found = []
+
+ # block counter will be multiplied by buffer
+ # to get the block size from the end
+ block_counter = -1
+
+ # loop until we find X lines
+ while len(lines_found) < lines:
+ try:
+ f.seek(block_counter * _buffer, os.SEEK_END)
+ except IOError: # either file is too small, or too many lines requested
+ f.seek(0)
+ lines_found = f.readlines()
+ break
+
+ lines_found = f.readlines()
+ block_counter -= 1
+
+ return lines_found[-lines:]
+
+
+class AutomaticTestingCommand:
+ """Automatic testing class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "automatictesting",
+ "usage": None,
+ "description": "Automatic testing command, not customer facing.",
+ "summary": "Automatic testing command, not customer facing.",
+ "aliases": [],
+ "auxcommands": [
+ "LoginCommand",
+ "LogoutCommand",
+ "TypesCommand",
+ "SelectCommand",
+ "GetCommand",
+ "ListCommand",
+ "InfoCommand",
+ "SetCommand",
+ "StatusCommand",
+ "CommitCommand",
+ "SaveCommand",
+ "LoadCommand",
+ "RawDeleteCommand",
+ "RawHeadCommand",
+ "RawGetCommand",
+ "RawPatchCommand",
+ "RawPutCommand",
+ "RawPostCommand",
+ "IscsiConfigCommand",
+ "FirmwareUpdateCommand",
+ "IloResetCommand",
+ "ISToolCommand",
+ "ResultsCommand",
+ "BootOrderCommand",
+ "ServerStateCommand",
+ "VirtualMediaCommand",
+ "IloAccountsCommand",
+ "IloFederationCommand",
+ "ClearRestApiStateCommand",
+ "CertificateCommand",
+ "SigRecomputeCommand",
+ "ESKMCommand",
+ "SendTestCommand",
+ "SingleSignOnCommand",
+ "ServerlogsCommand",
+ "AHSdiagCommand",
+ "SMBiosCommand",
+ "IloLicenseCommand",
+ "DeleteComponentCommand",
+ "DownloadComponentCommand",
+ "InstallSetCommand",
+ "ListComponentCommand",
+ "UpdateTaskQueueCommand",
+ "UploadComponentCommand",
+ "HpGooeyCommand",
+ "PendingChangesCommand",
+ "ServerInfoCommand",
+ "MaintenanceWindowCommand",
+ "FwpkgCommand",
+ "RebootCommand",
+ "IPProfilesCommand",
+ "FirmwareIntegrityCheckCommand",
+ "SetPasswordCommand",
+ "BiosDefaultsCommand",
+ "FwpkgCommand",
+ "ServerCloneCommand",
+ "FactoryDefaultsCommand",
+ "DirectoryCommand",
+ "SmartArrayCommand",
+ ],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self.ret_code = ReturnCodes.SUCCESS
+
+ def run(self, line, help_disp=False):
+ """Main automatic testing worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ if sys.platform == "darwin":
+ raise CommandNotEnabledError("'%s' command is not supported on MacOS" % str(self.name))
+ elif "VMkernel" in platform.uname():
+ raise CommandNotEnabledError("'%s' command is not supported on VMWare" % str(self.name))
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.getautomatictestingvalidation(options)
+ with open(__last_line__, "w+") as ll_file:
+ ll_file.write("")
+ with open(__pass_fail__, "w+") as pf_file:
+ pf_file.write("Total: 0\nPass: 0\nFail: 0\n")
+ with open(__error_logfile__, "w+") as log_file:
+ log_file.write("")
+
+ self.rdmc.ui.printer("***************************************************" "******************************\n")
+ self.rdmc.ui.printer("****************************AUTOMATIC TESTING " "STARTING***************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.options = options
+ if options.sumtest:
+ self.testsumcode()
+ return ReturnCodes.SUCCESS
+ if options.ahsdiagtest:
+ self.ahsdiagtesting(options.local)
+
+ # erase iLORest Debug Log (if debugging enabled)
+ if self.rdmc.opts.debug:
+ for handle in self.rdmc.app.logger.parent.handlers:
+ if isinstance(handle, FileHandler):
+ with open(handle.baseFilename, "w+") as dlfh:
+ dlfh.write("")
+ if self.rdmc.config:
+ if self.rdmc.config._ac__savefile:
+ self.logfile = self.rdmc.config._ac__savefile
+
+ self.helpmenusautomatictesting()
+ self.loginautomatictesting()
+ self.typesandselectautomatictesting(options.local, options.latestschema)
+ self.getandsetautomatictesting(options.latestschema)
+ self.saveandloadautomatictesting(options.local, options.latestschema)
+ self.iscsiautomatictesting()
+ self.istoolautomatictesting()
+ self.resultsautomatictesting()
+ self.bootorderautomatictesting()
+ self.serverstateautomatictesting()
+ self.virtualmediaautomatictesting()
+ self.accountautomatictesting()
+ self.federationautomatictesting()
+ self.certificateautomatictesting()
+ self.eskmautomatictesting()
+ self.sendtestautomatictesting()
+ self.ssoautomatictesting()
+ self.sigrecomputeautomatictesting()
+ self.serverlogstesting()
+ self.ilolicensetesting()
+ self.setbiospasswordautomatictesting(options.local)
+ self.smbiostesting()
+ self.cloningautomatictesting()
+ self.pendingautomatictesting()
+ self.serverinfoautomatictesting()
+ self.directoryautomatictesting()
+ if options.complete:
+ self.serverclonecommandautomatictesting(options.local)
+ self.fwpkgcommandautomatictesting()
+ self.ipprofilesautomatictesting()
+ self.fwintegritycheckautomatictesting()
+ self.uploadcomptesting()
+ self.listcomptesting()
+ self.updatetaskqueuetesting()
+ self.installsettesting()
+ self.maintenancewindowautomatictesting()
+ self.listanddownloadcomptesting()
+ self.deletecomptesting()
+ if options.complete:
+ self.setbiosdefaultsautomatictesting() # Sets bios default
+ self.rawautomatictesting(options.local)
+ if options.complete:
+ self.smartarrayautomatictesting()
+ self.firmwareautomatictesting()
+
+ self.rdmc.ui.printer("***************************************************" "******************************\n")
+ self.rdmc.ui.printer("******************************AUTOMATIC TESTING " "DONE*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ # Show PASS/FAIL
+ with open(__pass_fail__, "r") as fhandle:
+ data = fhandle.readlines()
+ self.rdmc.ui.printer("Results are:\n")
+ for item in data:
+ if item == "\n":
+ break
+ else:
+ self.rdmc.ui.printer(item)
+
+ self.rdmc.ui.printer("Review logfiles: '%s', '%s',\n" % (__pass_fail__, __error_logfile__))
+
+ if options.archive:
+ self.archive_handler(options.archive[0])
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ self.rdmc.ui.printer("\n")
+ return self.ret_code
+
+ def getautomatictestingvalidation(self, options):
+ """get inventory validation function
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ @log_decor
+ def helpmenusautomatictesting(self):
+ """handler for help menu auto test"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**********************STARTING HELP MENU AUTOMATIC " "TESTING***********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ # Argparse will attempt a force exit when using '-h'? Maybe we need to re-add the help
+ # command again...
+ # try:
+ # self.rdmc.run(['--h'])
+ # except Exception as exp:
+ # pass
+
+ for command in self.auxcommands:
+ self.auxcommands[command].run("-h")
+ self.rdmc.ui.printer(
+ "\n*********************************************" "************************************\n"
+ )
+ self.rdmc.ui.printer(
+ "***********************************************" "**********************************\n"
+ )
+ self.rdmc.ui.printer(
+ "***********************************************" "**********************************\n\n"
+ )
+
+ @log_decor
+ def loginautomatictesting(self):
+ """handler for login auto test"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("************************STARTING LOGIN AUTOMATIC " "TESTING*************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ loopcount = 10
+
+ for xint in range(loopcount):
+ self.rdmc.ui.printer("Login operation counter: %d\n" % xint)
+ self.auxcommands["login"].loginfunction("")
+
+ self.auxcommands["logout"].logoutfunction("")
+ self.rdmc.ui.printer("\n")
+
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ @log_decor
+ def typesandselectautomatictesting(self, local, latestschema):
+ """handler for types and select auto test
+
+ :param local: flag to enable local login
+ :type local: boolean.
+ :param latestschema: flag to determine if we should use smart schema.
+ :type latestschema: boolean.
+ """
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*******************STARTING TYPES AND SELECT " "AUTOMATIC TESTING*******************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ if local:
+ self.auxcommands["login"].loginfunction("")
+
+ typeslist = self.auxcommands["types"].typesfunction("", returntypes=True)
+
+ for item in typeslist:
+ if item and not item.startswith("Session."):
+ self.rdmc.ui.printer("Selected option: '%s'\n" % item)
+ self.auxcommands["select"].selectfunction(item)
+ self.auxcommands["select"].selectfunction("")
+
+ self.getandinfoautomatictesting(latestschema=latestschema)
+
+ self.rdmc.ui.printer(
+ "\n*****************************************" "***************************************\n"
+ )
+ self.rdmc.ui.printer(
+ "*******************************************" "*************************************\n"
+ )
+ self.rdmc.ui.printer(
+ "*******************************************" "*************************************\n\n"
+ )
+
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def getandsetautomatictesting(self, latestschema):
+ """handler for get and set auto test
+
+ :param latestschema: flag to determine if we should use smart schema.
+ :type latestschema: boolean.
+ """
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING GET AND SET AUTOMATIC" " TESTING**********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].loginfunction("")
+ self.rdmc.ui.printer("\n")
+
+ if self.rdmc.app.typepath.flagiften:
+ biosinputlist = [
+ "Attributes/BootMode",
+ "Attributes/MissingItem",
+ "Attributes/AdminName",
+ "Attributes/AdminEmail",
+ "Attributes/AdminPhone",
+ "Attributes/CustomPostMessage",
+ "Attributes/ServerAssetTag",
+ "Attributes/ServerName",
+ "Attributes/ServerOtherInfo",
+ "Attributes/ServerPrimaryOs",
+ "Attributes/ServiceEmail",
+ "Attributes/ServiceName",
+ "Attributes/ServiceOtherInfo",
+ "Attributes/ServicePhone",
+ ]
+ else:
+ biosinputlist = [
+ "BootMode",
+ "MissingItem",
+ "AdminName",
+ "AdminEmail",
+ "AdminPhone",
+ "CustomPostMessage",
+ "ServerAssetTag",
+ "ServerName",
+ "ServerOtherInfo",
+ "ServerPrimaryOs",
+ "ServiceEmail",
+ "ServiceName",
+ "ServiceOtherInfo",
+ "ServicePhone",
+ ]
+
+ self.setautomatichelper(biosinputlist, "HpBios.", latestschema)
+
+ self.rdmc.ui.printer("\n*************************************************" "*******************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n\n")
+
+ biosinputlist = ["Boot/BootSourceOverrideTarget", "AssetTag"]
+ self.setautomatichelper(biosinputlist, "ComputerSystem.", latestschema)
+
+ self.rdmc.ui.printer("\n*************************************************" "*******************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n\n")
+ self.auxcommands["status"].run("")
+
+ self.rdmc.ui.printer("\n*************************************************" "*******************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "*****************************\n\n")
+ self.auxcommands["commit"].run("")
+
+ @log_decor
+ def setautomatichelper(self, inputlist, selector, latestschema):
+ """handler for set auto test
+
+ :param inputlist: list of items to be set.
+ :type inputlist: list.
+ :param selector: type to be select for items that will be set.
+ :type selector: str.
+ :param latestschema: flag to determine if we should use smart schema.
+ :type latestschema: boolean.
+ """
+ self.rdmc.ui.printer("STARTING TEST FOR SETTINGS ON '%s'.\n" % selector)
+ self.auxcommands["select"].selectfunction([selector])
+
+ for item in inputlist:
+ listresults = self.auxcommands["get"].getworkerfunction(item, self.options, results=True)
+ for getresults in listresults:
+ if getresults and isinstance(getresults[next(iter(getresults))], dict):
+ for key in getresults:
+ getresults = getresults[key]
+
+ for key, value in list(getresults.items()):
+ if key == "BootSourceOverrideTarget":
+ if value == "None":
+ randstring = "Cd"
+ else:
+ randstring = "None"
+ else:
+ randstring = "".join(
+ random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)
+ )
+
+ if value == randstring:
+ value = value[::-1]
+
+ self.rdmc.ui.printer("Setting: '%s'\t\tBefore:'%s'\tAfter:'%s" "'\n" % (key, value, randstring))
+
+ inputline = item + "=" + randstring
+
+ if latestschema:
+ inputline += " --latestschema"
+
+ try:
+ self.auxcommands["set"].run(inputline, skipprint=True)
+ except:
+ pass
+ elif getresults:
+ for key, value in list(getresults.items()):
+ randstring = "".join(
+ random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)
+ )
+
+ if value == randstring:
+ value = value[::-1]
+
+ self.rdmc.ui.printer("Setting: '%s'\t\tBefore:'%s'\tAfter:'%s" "'\n" % (key, value, randstring))
+
+ inputline = key + "=" + randstring
+
+ if latestschema:
+ inputline += " --latestschema"
+
+ try:
+ self.auxcommands["set"].run(inputline, skipprint=True)
+ except:
+ pass
+ else:
+ InvalidCommandLineError("Variable '%s' was not found in the " "current selection.\n" % item)
+
+ @log_decor
+ def getandinfoautomatictesting(self, item="", latestschema=False, getval=None, already_checked=[]):
+ """handler for get and info auto test
+
+ :param item: list of items to be set.
+ :type item: list.
+ :param latestschema: flag to determine if we should use smart schema.
+ :type latestschema: boolean.
+ :param selectitems: list of selected items.
+ :type selectitems: list.
+ :param origin: location for current item.
+ :type origin: str.
+ :param already_checked: List of keys already checked.
+ :type already_checked: list.
+ """
+ listdata = self.auxcommands["get"].getworkerfunction(item, self.options, results=True)
+ if not listdata:
+ self.rdmc.ui.warn("(GET) No list contents found for section: '%s'\n" % item)
+
+ getdata = self.auxcommands["get"].getworkerfunction(item, self.options, uselist=True, results=True)
+ if getdata and item and self.rdmc.opts.verbose:
+ self.rdmc.ui.printer("Contents found for section: '%s'\n" % item)
+ elif not getdata:
+ self.rdmc.ui.warn("(LIST) No contents found for section: '%s'\n" % item)
+
+ cline = item + " --latestschema" if latestschema else item
+ try:
+ if not self.auxcommands["info"].run(cline, autotest=True):
+ logging("info", cline, add_traceback=False)
+ raise KeyError
+ except:
+ self.rdmc.ui.warn("(LIST) No info contents found for section: '%s'\n" % cline)
+
+ if not getdata:
+ return
+
+ getdata = getval if getval is not None or item else getdata
+ if isinstance(getdata, dict):
+ for key, val in list(getdata.items()):
+ if key.lower() in HARDCODEDLIST or key.lower() in already_checked:
+ continue
+ key = (item + "/" + key) if item else key
+ already_checked.append(key)
+ self.getandinfoautomatictesting(
+ item=key,
+ latestschema=latestschema,
+ getval=val,
+ already_checked=already_checked,
+ )
+ elif isinstance(getdata, (list, tuple)):
+ for kii in getdata:
+ if not isinstance(kii, dict):
+ continue
+ for key, val in list(kii.items()):
+ if key.lower() in HARDCODEDLIST:
+ continue
+ key = (item + "/" + key) if item else key
+ if key in already_checked:
+ continue
+ else:
+ already_checked.append(key)
+ self.getandinfoautomatictesting(
+ item=key,
+ latestschema=latestschema,
+ getval=val,
+ already_checked=already_checked,
+ )
+
+ @log_decor
+ def saveandloadautomatictesting(self, local, latestschema):
+ """handler for save and load auto test
+
+ :param local: flag to enable local login
+ :type local: boolean.
+ :param latestschema: flag to determine if we should use smart schema.
+ :type latestschema: boolean.
+ """
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("********************STARTING SAVE AND LOAD " "AUTOMATIC TESTING*********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ biosinputlist = [
+ "AdminName",
+ "AdminEmail",
+ "AdminPhone",
+ "CustomPostMessage",
+ "ServerAssetTag",
+ "ServerName",
+ "ServerOtherInfo",
+ "ServerPrimaryOs",
+ "ServiceEmail",
+ "ServiceName",
+ "ServiceOtherInfo",
+ "ServicePhone",
+ ]
+ # TODO: Verify the data in file matches server after load
+ # TODO: Add multi select save/load checking
+ if local:
+ self.auxcommands["login"].loginfunction("")
+
+ self.auxcommands["save"].run("--selector=HpBios. -f ilorest.json")
+
+ fileh = open("ilorest.json", "r")
+ tempholder = fileh.read()
+
+ for item in biosinputlist:
+ restring = '"' + item + '": ".*?"'
+ randstring = "".join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
+
+ replacestring = '"' + item + '": "' + randstring + '"'
+ tempholder = re.sub(restring, replacestring, tempholder, flags=re.DOTALL)
+
+ outfile = open("ilorest.json", "w")
+ outfile.write(tempholder)
+ outfile.close()
+
+ self.rdmc.ui.printer("\n")
+
+ if latestschema:
+ self.auxcommands["load"].run("--latestschema")
+ else:
+ self.auxcommands["load"].run("-f ilorest.json")
+
+ self.rdmc.ui.printer("\n")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def iscsiautomatictesting(self):
+ """iscsi automatic testing"""
+ self.rdmc.ui.printer("***************************************************" "******************************\n")
+ self.rdmc.ui.printer("*****************STARTING ISCSI CONFIGURATION " "AUTOMATIC TESTING******************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ found = 0
+ testfile = None
+ self.auxcommands["iscsiconfig"].run("")
+
+ try:
+ self.auxcommands["iscsiconfig"].run("--delete 1")
+ except:
+ pass
+
+ self.auxcommands["iscsiconfig"].run("--add [1]")
+ self.auxcommands["iscsiconfig"].run("--list")
+ self.auxcommands["iscsiconfig"].run("--list -f TESTFILE")
+
+ try:
+ testfile = open("TESTFILE")
+ for line in testfile:
+ if '"Attempt 1"' in line:
+ found = 1
+ self.rdmc.ui.printer("Output Validated.\n")
+ break
+
+ if found == 0:
+ raise NoContentsFoundForOperationError("Change not found.")
+ except Exception as excp:
+ raise excp
+
+ try:
+ testfile.seek(0)
+ found = 1
+ self.auxcommands["iscsiconfig"].run("--delete 1")
+ self.auxcommands["iscsiconfig"].run("--list -f TESTFILE")
+
+ for line in testfile:
+ if '"Attempt 1"' in line:
+ raise NoChangesFoundOrMadeError("Not deleted")
+
+ if found == 0:
+ self.rdmc.ui.printer("Attempt successfully deleted.\n")
+ except Exception as excp:
+ raise excp
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def rawautomatictesting(self, local):
+ """raw commands automatic testing
+
+ :param local: flag to enable local login
+ :type local: boolean.
+ """
+ if local:
+ self.auxcommands["login"].loginfunction("")
+
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING RAW_COMMANDS " "AUTOMATIC TESTING*********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["rawhead"].run('"/rest/v1/systems/1" -f TESTFILE')
+
+ try:
+ testfile = open("TESTFILE")
+ json.loads(testfile.read())
+ except Exception as excp:
+ raise excp
+ finally:
+ testfile.close()
+
+ self.rdmc.ui.printer("json return validated.\n")
+ self.auxcommands["rawget"].run('"/rest/v1/systems/1/bios/Settings" -f TESTFILE')
+
+ try:
+ testfile = open("TESTFILE")
+ json.loads(testfile.read())
+ except Exception as excp:
+ raise excp
+ finally:
+ testfile.close()
+
+ self.rdmc.ui.printer("json return validated.\n")
+
+ try:
+ writefile = open("WRITEFILE", "w")
+ writefile.write(
+ '{\n\t"path": "/rest/v1/systems/1/bios/Settings",' '\n\t"body": {\n\t\t"AdminName": "RexySmith"\n\t}\n}'
+ )
+ writefile.close()
+
+ self.auxcommands["rawpatch"].run("WRITEFILE")
+ self.auxcommands["rawget"].run('"/rest/v1/systems/1/bios/Settings" -f TESTFILE')
+ testfile = open("TESTFILE")
+ found = 0
+
+ for line in testfile:
+ if '"AdminName"' in line:
+ if '"RexySmith"' in line:
+ found = 1
+ self.rdmc.ui.printer("json return validated.\n")
+ break
+
+ if found == 0:
+ raise NoContentsFoundForOperationError("Change not found.")
+
+ except Exception as excp:
+ raise excp
+
+ finally:
+ testfile.close()
+
+ # ******************RAW PUT*************************
+ try:
+ writefile = open("WRITEFILE", "w")
+ if self.rdmc.app.typepath.flagiften:
+ writefile.write(
+ '{\n\t"path":"/rest/v1/systems/1/bios/Settings",\n'
+ '\t"body":{\n\t\t"Attributes":{\n\t\t\t"BaseConfig":'
+ ' "default"\n\t\t}\n\t}\n}'
+ )
+ else:
+ writefile.write(
+ '{\n\t"path": "/rest/v1/systems/1/bios/Settings",\n'
+ '\t"body":{\n\t\t"BaseConfig": "default"'
+ "\n\t}\n}"
+ )
+ writefile.close()
+
+ self.auxcommands["rawput"].run("WRITEFILE")
+ self.auxcommands["rawget"].run('"/rest/v1/systems/1/bios/Settings" -f TESTFILE')
+ testfile = open("TESTFILE")
+ found = 0
+
+ for line in testfile:
+ if '"BaseConfig"' in line:
+ if '"default"' in line:
+ found = 1
+ self.rdmc.ui.printer("json return validated\n")
+ break
+
+ if found == 0:
+ raise NoContentsFoundForOperationError("Change not found.")
+ except Exception as excp:
+ raise excp
+ finally:
+ testfile.close()
+
+ # ******************RAW POST************************
+ if not local:
+ try:
+ writefile = open("WRITEFILE", "w")
+
+ if self.rdmc.app.typepath.flagiften:
+ writefile.write(
+ '{\n\t"path": "/redfish/v1/Systems/1/'
+ 'Actions/ComputerSystem.Reset/",\n\t"body": '
+ '{\n\t\t"ResetType": "ForceRestart"\n\t}\n}'
+ )
+ else:
+ writefile.write(
+ '{\n\t"path": "/rest/v1/Systems/1",\n'
+ '\t"body": {\n\t\t"Action": "Reset",'
+ '\n\t\t"ResetType": "ForceRestart"\n\t}\n}'
+ )
+
+ writefile.close()
+
+ self.auxcommands["rawpost"].run("WRITEFILE")
+ self.rdmc.ui.printer("Waiting for BIOS to reset. See you in 7 mins.\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("6 minutes remaining...\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("5 minutes remaining...\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("4 minutes remaining...\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("3 minutes remaining...\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("2 minutes remaining...\n")
+ time.sleep(60)
+ self.rdmc.ui.printer("1 minute remaining...\n")
+ time.sleep(60)
+
+ self.auxcommands["rawget"].run('"/rest/v1/systems/1/bios/Settings" -f TESTFILE')
+ testfile = open("TESTFILE")
+ found = 0
+
+ # may error if bios don't update upon restart
+ for line in testfile:
+ if '"AdminEmail"' in line:
+ if '""' in line:
+ found = 1
+ self.rdmc.ui.printer("reset validated\n")
+ break
+
+ if found == 0:
+ raise NoContentsFoundForOperationError("Change not found.")
+ except Exception as excp:
+ raise excp
+ finally:
+ testfile.close()
+
+ # ******************RAW DELETE**********************
+ self.auxcommands["rawget"].run('"/rest/v1/Sessions" -f TESTFILE')
+
+ try:
+ testfile = open("TESTFILE")
+ next_item = False
+
+ for line in testfile:
+ if next_item:
+ session = line
+ break
+
+ if '"MySession":' in line and '"MySession": false' not in line and '"MySession": true' not in line:
+ next_item = True
+
+ session = session.split('"')
+
+ for item in session:
+ if "/Sessions/" in item:
+ session = '"%s"' % item
+ break
+ except Exception as excp:
+ raise excp
+ finally:
+ testfile.close()
+
+ try:
+ self.auxcommands["rawdelete"].run(session)
+ self.rdmc.ui.printer("Session Deleted\n")
+ except Exception as excp:
+ raise excp
+
+ ''' Rework to configure firmware update test.
+ @log_decor
+ def firmwareautomatictesting(self):
+ """ firmware update automatic testing """
+
+ self.rdmc.ui.printer("\n*************************************************" \
+ "********************************\n")
+ self.rdmc.ui.printer("*********************SKIPING FIRMWARE " \
+ "AUTOMATIC TESTING***************************\n")
+ self.rdmc.ui.printer("***************************************************" \
+ "******************************\n\n")
+
+ fw_url = "http://infinitymaster.us.rdlabs.hpecorp.net:1051/automatic_testing/components"\
+ "/FIRMWARE/blah"
+ tmp = urlopen(fw_url)
+ rom_url = fw_url + "/ROM/"
+ ilo_url = fw_url + "/iLO/"
+
+# rom_fw_dict = []
+# ilo_fw_dict = []
+
+ rom_fw_arr = self.automatictesting_helper_fb(rom_url)
+# ilo_fw_arr = self.automatictesting_helper_fb(ilo_url)
+
+ try:
+ for i in rom_fw_arr:
+ #tmparr = path.split('/')
+ #urlretrieve(fw_url + path, tmparr[-1])
+ (fmw, _) = urlretrieve(rom_url + i, i)
+ self.auxcommands['flashfwpkg'].run(fmw + ' --forceupload --ignorechecks')
+
+ except Exception as excp:
+ self.rdmc.ui.printer("A General Error Occured: %s" %excp)
+ block = True
+
+ #self.auxcommands['login'].run("")
+
+# if local:
+# return
+
+# self.auxcommands['login'].run("")
+# tests = {'rom': [], 'firmware': firmware}
+# (romfamily, _) = self.rdmc.app.getbiosfamilyandversion()
+
+# for version in rom:
+# if romfamily.lower() in version.lower():
+# tests['rom'].append(version)
+
+# for test in tests:
+# for link in tests[test]:
+# counter = 0
+# keep_going = True
+
+# self.auxcommands['logout'].run("")
+# self.auxcommands['login'].run("")
+# self.auxcommands['firmwareupdate'].run(link)
+
+# if test == 'rom':
+# self.auxcommands['login'].run("")
+# self.auxcommands['iloreset'].run("")
+
+# self.rdmc.ui.printer("Waiting for iLO...\n")
+# time.sleep(60)
+# self.rdmc.ui.printer("1 minute remaining...\n")
+# time.sleep(60)
+
+# while keep_going:
+# try:
+# if counter > 50:
+# raise
+
+# counter += 1
+# keep_going = False
+# self.auxcommands['login'].run("")
+# except Exception as excp:
+# if counter > 50:
+# raise excp
+# else:
+# keep_going = True
+
+ '''
+
+ @log_decor
+ def istoolautomatictesting(self):
+ """istool automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING ISTOOL " "AUTOMATIC TESTING***************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["istool"].run("")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def resultsautomatictesting(self):
+ """results automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING RESULTS " "AUTOMATIC TESTING**************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["results"].run("")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def bootorderautomatictesting(self):
+ """boot order automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING BOOT ORDER " "AUTOMATIC TESTING***********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ try:
+ self.auxcommands["bootorder"].run("--disablebootflag --commit")
+ except:
+ pass
+
+ self.auxcommands["bootorder"].run("[5,4,3,2,1]")
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["bootorder"].run("--onetimeboot=Cd")
+ self.auxcommands["commit"].run("")
+ btype, target = self.bootorderhelper()
+
+ if not btype == "Once" or not target == "Cd":
+ raise InvalidOrNothingChangedSettingsError
+
+ self.auxcommands["bootorder"].run("--onetimeboot=Hdd")
+ self.auxcommands["bootorder"].run("--continuousboot=Hdd --commit")
+ btype, target = self.bootorderhelper()
+
+ if not btype == "Continuous" or not target == "Hdd":
+ raise InvalidOrNothingChangedSettingsError
+
+ # self.auxcommands['bootorder'].run("--onetimeboot=Cd")
+ # self.auxcommands['bootorder'].run("--continuousboot=Hdd")
+ # self.auxcommands['bootorder'].run("--disablebootflag --commit")
+ # btype, target = self.bootorderhelper()
+ #
+ # if not btype == 'Disabled' or not target == 'None':
+ # raise InvalidOrNothingChangedSettingsError
+
+ self.auxcommands["bootorder"].run("--disablebootflag")
+ # try:
+ # self.auxcommands['bootorder'].run("--disablebootflag")
+ # except:
+ # self.rdmc.ui.printer("Entry is the current boot setting.\n")
+
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["bootorder"].run("--securebootkeys=deletepk")
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["bootorder"].run("--securebootkeys=defaultkeys")
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["bootorder"].run("--securebootkeys=deletekeys")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def bootorderhelper(self):
+ """boot order helper for automatic testing"""
+ self.auxcommands["login"].run("")
+ results = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.systempath, service=True, silent=True)
+
+ btype = results.dict["Boot"]["BootSourceOverrideEnabled"]
+ target = results.dict["Boot"]["BootSourceOverrideTarget"]
+
+ return btype, target
+
+ @log_decor
+ def serverstateautomatictesting(self):
+ """server state automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************SERVER STATE " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["serverstate"].run("")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def virtualmediaautomatictesting(self):
+ """virtual media automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************VIRTUAL MEDIA " "AUTOMATIC TESTING*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+ return
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["virtualmedia"].run("")
+
+ try:
+ self.auxcommands["virtualmedia"].run("2 --remove")
+ except:
+ pass
+
+ # Needs to be updated
+ self.auxcommands["virtualmedia"].run("2 http://10.0.0.1/vm.iso --bootnextreset")
+ results = self.rdmc.app.get_handler("/rest/v1/Managers/1/VirtualMedia/2", service=True, silent=True).dict
+
+ if not results["Inserted"] or not results["Oem"][self.rdmc.app.typepath.defs.oemhp]["BootOnNextServerReset"]:
+ raise InvalidOrNothingChangedSettingsError("VM not found.")
+
+ self.auxcommands["virtualmedia"].run("2 --remove")
+
+ results = self.rdmc.app.get_handler("/rest/v1/Managers/1/VirtualMedia/2", service=True, silent=True).dict
+
+ if results["Inserted"] or results["Oem"][self.rdmc.app.typepath.defs.oemhp]["BootOnNextServerReset"]:
+ raise InvalidOrNothingChangedSettingsError("VM not removed.")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def accountautomatictesting(self):
+ """iLO manager account automatic testing"""
+ """ - add, modify privs, changepass, delete"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**************************ACCOUNT " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["iloaccounts"].run("")
+ errors = []
+ priv_dict = {
+ "LoginPriv": 1,
+ "RemoteConsolePriv": 2,
+ "UserConfigPriv": 3,
+ "iLOConfigPriv": 4,
+ "VirtualMediaPriv": 4,
+ "VirtualPowerAndResetPriv": 5,
+ "HostNICConfigPriv": 6,
+ "HostBIOSConfigPriv": 7,
+ "HostStorageConfigPriv": 9,
+ "SystemRecoveryConfigPriv": 7,
+ }
+ for i in range(5):
+ randname = "".join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(6))
+ accid = None
+ rand_privs = random.sample(priv_dict, 3)
+ rand_priv_str = "--addprivs "
+ for rp in enumerate(rand_privs):
+ if rp[1] == "SystemRecoveryConfigPriv":
+ continue
+ elif rp[0] == (len(rand_privs) - 1):
+ rand_priv_str += str(priv_dict[rp[1]])
+ else:
+ rand_priv_str += str(priv_dict[rp[1]]) + ","
+ acct_str = "add " + randname + " " + randname + " testpassword " + rand_priv_str
+ try:
+ self.auxcommands["iloaccounts"].run(acct_str)
+ except Exception as excp:
+ if "rand_priv" in locals():
+ errors.append("Unable to create account '{}', " "'{}': {}".format(randname, acct_str, str(excp)))
+
+ results = self.rdmc.app.get_handler(
+ self.rdmc.app.typepath.defs.accountspath, service=True, silent=True
+ ).dict[self.rdmc.app.typepath.defs.collectionstring]
+
+ if "Id" not in list(results[0].keys()):
+ newresults = []
+
+ for acct in results:
+ acct = self.rdmc.app.get_handler(
+ acct[self.rdmc.app.typepath.defs.hrefstring],
+ service=True,
+ silent=True,
+ ).dict
+ newresults.append(acct)
+
+ results = newresults
+
+ for acct in results:
+ if acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["LoginName"] != randname:
+ continue
+ else:
+ self.rdmc.ui.printer("Created account found.\n")
+ accid = acct["Id"]
+ privs = acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["Privileges"]
+ try:
+ rand_priv = next(iter(random.sample(privs, 1)))
+ privstr = ""
+ if privs[rand_priv]:
+ privstr = "--removeprivs=" + str(priv_dict[rand_priv])
+ else:
+ privstr = "--addprivs=" + str(priv_dict[rand_priv])
+ self.auxcommands["iloaccounts"].run("modify " + randname + " " + privstr)
+ except Exception as excp:
+ if "rand_priv" in locals():
+ errors.append(
+ "Unable to modify privilege '{}' for account "
+ "'{}': {}".format(rand_priv, randname, str(excp))
+ )
+ else:
+ errors.append(
+ "Unable to retrieve privileges for account '{}' " ": {}".format(randname, excp)
+ )
+ break
+ self.auxcommands["iloaccounts"].run("changepass " + randname + " newpassword")
+ self.auxcommands["iloaccounts"].run("delete " + accid)
+
+ if not accid:
+ raise NoChangesFoundOrMadeError("Created account not found.")
+
+ self.auxcommands["logout"].run("")
+ if errors:
+ raise Exception("The following errors occurred while testing accounts: %s" % errors)
+
+ @log_decor
+ def federationautomatictesting(self):
+ """federation automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************ADDFEDERATION " "AUTOMATIC TESTING*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["ilofederation"].run("")
+
+ redfish = self.rdmc.app.monolith.is_redfish
+ randname = "".join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(6))
+ foundfed = False
+
+ self.auxcommands["ilofederation"].run("add " + randname + " password")
+ self.auxcommands["ilofederation"].run("")
+
+ path = self.rdmc.app.typepath.defs.federationpath
+ results = self.rdmc.app.get_handler(path, service=True, silent=True).dict
+
+ if redfish:
+ results = results["Members"]
+ else:
+ results = results["links"]["Member"]
+
+ newresults = []
+ for fed in results:
+ fed = self.rdmc.app.get_handler(fed[self.rdmc.app.typepath.defs.hrefstring], service=True, silent=True).dict
+
+ newresults.append(fed)
+ results = newresults
+
+ for fed in results:
+ if fed["Id"] == randname:
+ self.rdmc.ui.printer("Created federation found.\n")
+ foundfed = True
+ break
+
+ if not foundfed:
+ raise NoChangesFoundOrMadeError("Created federation not found.")
+
+ self.auxcommands["ilofederation"].run("changekey " + randname + " newpassword")
+ self.auxcommands["ilofederation"].run("delete " + randname)
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def serverlogstesting(self):
+ """server logs automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************STARTING SERVERLOGS " "AUTOMATIC TESTING***********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].loginfunction("")
+ self.auxcommands["serverlogs"].run("--selectlog=IEL -f IELlog.txt")
+ self.auxcommands["serverlogs"].run("--selectlog=IML -f IMLlog.txt")
+ self.auxcommands["serverlogs"].run("--selectlog=AHS --downloadallahs")
+ self.auxcommands["serverlogs"].run("--selectlog=AHS --clearlog")
+ self.auxcommands["serverlogs"].run("--selectlog=IEL --clearlog")
+ self.auxcommands["serverlogs"].run("--selectlog=IML --clearlog")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def ahsdiagtesting(self, local):
+ """ahs diags automatic testing"""
+ if local and os.name != "nt":
+ self.rdmc.ui.printer(
+ "\n*********************************************" "************************************\n"
+ )
+ self.rdmc.ui.printer(
+ "*********************STARTING AHSDIAG " "AUTOMATIC TESTING**************************\n"
+ )
+ self.rdmc.ui.printer(
+ "***********************************************" "**********************************\n\n"
+ )
+ pass
+
+ self.auxcommands["login"].loginfunction("")
+ self.auxcommands["ahsdiag"].run("--WriteSignPost")
+ self.auxcommands["ahsdiag"].run(
+ "--WriteMarkerPost --instance 1 --markervalue" ' 3 --markertext "Automatictesting"'
+ )
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def crapistesting(self):
+ """clear rest api automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************CLEARRESTAPI " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["clearrestapistate"].run("")
+
+ if self.rdmc.app.monolith.is_redfish:
+ path = "/redfish/v1/registries/"
+ else:
+ path = "/rest/v1/registries"
+
+ results = self.rdmc.app.get_handler(path, service=True, silent=True).dict
+
+ for item in results[self.rdmc.app.typepath.dict.collectionstring]:
+ if "attributereg" in item[self.rdmc.app.typepath.defs.hrefstring]:
+ raise NoChangesFoundOrMadeError("No changes found.")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def certificateautomatictesting(self):
+ """generate csr automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*******************SSO AND TLS CERTIFICATE " "AUTOMATIC TESTING*********************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ error_dict = dict()
+ delete_items = list()
+
+ url_path = "http://infinitymaster.us.rdlabs.hpecorp.net:1051/automatic_testing/x509_certs/"
+
+ orgname = " HPE"
+ orgunit = " _iLOrest_Team"
+ commonname = " JustSomeGuys"
+ country = " USA"
+ state = " Tejas"
+ city = " Houston"
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["certificate"].run("csr" + orgname + orgunit + commonname + country + state + city)
+
+ for item in self.automatictesting_helper_fb(url_path):
+ fld = urlopen(url_path + item)
+ data = fld.read()
+ with open(item, "w+b") as certfile:
+ certfile.write(data)
+ delete_items.append(item)
+ try:
+ self.auxcommands["certificate"].run("tls " + item)
+ except Exception:
+ error_dict[file] = "tls " + item
+ self.rdmc.ui.printer(
+ "iLO flagged an error while uploading the" " TLS certificate file to iLO: %s\n" % item
+ )
+
+ try:
+ self.auxcommands["singlesignon"].run("importcert " + item)
+ except Exception:
+ error_dict[file] = "tls " + item
+ self.rdmc.ui.printer(
+ "iLO flagged an error while uploading the" " SSO certificate file to iLO: %s\n" % item
+ )
+
+ self.automatictesting_helper_fd(delete_items)
+ self.auxcommands["logout"].run("")
+
+ if error_dict:
+ try:
+ raise ValueError
+ except ValueError as err:
+ if not err.args:
+ err.args = ("iLO flagged an error with components: ",)
+ for item in error_dict:
+ err.args = err.args + ("Component: %s, Full String: %s" % (item, error_dict[item]),)
+ raise
+
+ @log_decor
+ def eskmautomatictesting(self):
+ """eskm automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************ESKM " "AUTOMATIC TESTING**********************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+ pass
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["eskm"].run("testconnections")
+ self.auxcommands["eskm"].run("clearlog")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def sigrecomputeautomatictesting(self):
+ """sigrecompute automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************SIGRECOMPUTE " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+
+ try:
+ self.auxcommands["sigrecompute"].run("")
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.printer("Server is redfish. Skipping sigrecompute.\n")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def sendtestautomatictesting(self):
+ """results automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************SENDTEST " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+ self.auxcommands["login"].run("")
+ try:
+ self.auxcommands["select"].run(self.rdmc.app.typepath.defs.snmpservice)
+ self.auxcommands["select"].run(self.rdmc.app.typepath.defs.managernetworkservicetype)
+ except:
+ self.rdmc.ui.printer("Skipping sendtest testing, resource not available.\n")
+ self.auxcommands["logout"].run("")
+ return
+
+ try:
+ # setup for snmpalert
+ # set SNMPAlertProtocol=SNMPv1Trap
+ self.auxcommands["select"].run(self.rdmc.app.typepath.defs.snmpservice)
+ self.auxcommands["set"].run("AlertsEnabled=true")
+ self.auxcommands["set"].run("AlertDestinations=[testdns.newdnststr] --commit")
+ except:
+ pass
+
+ try:
+ # setup for alertmail
+ # {"Oem": { "Hpe":{"AlertMailEnabled": true, "AlertMailEmail": "bob@bob.bob",
+ # "AlertMailSenderDomain": "domain", "AlertMailSMTPServer": "1.35.35.35"}}}
+ self.auxcommands["select"].run(self.rdmc.app.typepath.defs.managernetworkservicetype)
+
+ if self.rdmc.app.typepath.flagiften:
+ oem = "Oem/Hpe"
+ else:
+ oem = "Oem/Hp"
+
+ self.auxcommands["set"].run(
+ oem
+ + "/AlertMailEmail=test@test.test "
+ + oem
+ + "/AlertMailEnabled=True "
+ + oem
+ + "/AlertMailSMTPServer=testserver.test.test "
+ + oem
+ + "/AlertMailSenderDomain=testdomain.test.test --commit"
+ )
+ except:
+ pass
+
+ try:
+ # setup for syslog
+ self.auxcommands["select"].run(self.rdmc.app.typepath.defs.managernetworkservicetype)
+ self.auxcommands["set"].run(
+ oem + "/RemoteSyslogServer=testserver.test.svrtest " + oem + "/RemoteSyslogEnabled=True --commit"
+ )
+ except:
+ pass
+
+ self.auxcommands["login"].run("")
+
+ self.auxcommands["sendtest"].run("syslog")
+ self.auxcommands["sendtest"].run("alertmail")
+ self.auxcommands["sendtest"].run("snmpalert")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def ssoautomatictesting(self):
+ """sso automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*********************SINGLESIGNON " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["singlesignon"].run("importdns newdnsname.dnstest")
+ self.auxcommands["singlesignon"].run("deleterecord 1")
+ self.auxcommands["singlesignon"].run("importdns newdnsname.dnstest")
+ self.auxcommands["singlesignon"].run("deleterecord all")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def ilolicensetesting(self):
+ """iLO license automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("***********************ILOLICENSE " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+
+ try:
+ self.auxcommands["ilolicense"].run("xx-xx-xx-xx")
+ except IloResponseError:
+ pass
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def smbiostesting(self):
+ """smbios automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("***************************SMBIOS " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ if self.rdmc.app.typepath.flagiften:
+ self.auxcommands["login"].run("")
+ self.auxcommands["smbios"].run("smbios")
+ self.auxcommands["logout"].run("")
+ else:
+ self.rdmc.ui.printer("Skipping smbios testing, server not gen10.\n")
+
+ @log_decor
+ def directoryautomatictesting(self):
+ """Directory command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************DIRECTORY " "AUTOMATIC TESTING*****************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.rdmc.ui.printer("Testing settings output.\n")
+ self.auxcommands["directory"].run("kerberos")
+ self.auxcommands["directory"].run("ldap")
+ self.auxcommands["directory"].run("kerberos -j")
+ self.auxcommands["directory"].run("ldap -j")
+
+ self.rdmc.ui.printer("Testing setting properties.\n")
+ self.auxcommands["directory"].run("kerberos --serviceaddress test.account --port 1337 --realm " "testrealm")
+ self.auxcommands["directory"].run("ldap testusername testpassword --enable")
+ self.auxcommands["directory"].run(
+ "ldap --serviceaddress test2.account --addsearch autotestsearch," "autotestsearch2"
+ )
+
+ self.rdmc.ui.printer("Testing adding roles.\n")
+ self.auxcommands["directory"].run(
+ 'kerberos --addrole "Administrator:a test,' 'ReadOnly:another test" --disable'
+ )
+
+ self.rdmc.ui.printer("Validating changes...\n")
+
+ results = self.rdmc.app.select(selector="AccountService.", path_refresh=True)[0].dict
+
+ if results["LDAP"]["Authentication"]["Username"] == "testusername":
+ self.rdmc.ui.printer("Validated Username.\n")
+ else:
+ sys.stderr.write("Username not changed.\n")
+ if (
+ results["ActiveDirectory"]["ServiceAddresses"][0] == "test.account:1337"
+ and results["LDAP"]["ServiceAddresses"][0] == "test2.account"
+ ):
+ self.rdmc.ui.printer("Validated Service addresses.\n")
+ else:
+ sys.stderr.write("Service addresses not changed.\n")
+ raise IloResponseError("")
+ if (
+ results["Oem"]["Hpe"]["DirectorySettings"]["LdapServerPort"] == 55
+ and results["Oem"]["Hpe"]["KerberosSettings"]["KDCServerPort"] == 1337
+ ):
+ self.rdmc.ui.printer("Validated Ports.\n")
+ else:
+ sys.stderr.write("Ports not changed.\n")
+ raise IloResponseError("")
+ if results["Oem"]["Hpe"]["KerberosSettings"]["KerberosRealm"] == "testrealm":
+ self.rdmc.ui.printer("Validated Realm.\n")
+ else:
+ sys.stderr.write("Realm not changed.\n")
+ raise IloResponseError("")
+ if (
+ "autotestsearch" in results["LDAP"]["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]
+ and "autotestsearch2" in results["LDAP"]["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]
+ ):
+ self.rdmc.ui.printer("Validated SearchSettings.\n")
+ else:
+ sys.stderr.write("SearchSettings not changed.\n")
+ if results["LDAP"]["ServiceEnabled"] and not results["ActiveDirectory"]["ServiceEnabled"]:
+ self.rdmc.ui.printer("Validated ServiceEnabled.\n")
+ else:
+ sys.stderr.write("Service not enabled/disabled.\n")
+ raise IloResponseError("")
+ rolecount = 0
+ for role in results["LDAP"]["RemoteRoleMapping"]:
+ if role["RemoteGroup"] == "a test" or role["RemoteGroup"] == "another test":
+ rolecount += 1
+ if rolecount == 2:
+ self.rdmc.ui.printer("Validated Role mappings.\n")
+ else:
+ sys.stderr.write("Remote roles not changed.\n")
+ raise IloResponseError("")
+
+ self.rdmc.ui.printer("Removing changes...\n")
+ self.auxcommands["directory"].run('kerberos --serviceaddress "" --realm ""')
+ self.auxcommands["directory"].run(
+ 'ldap --serviceaddress "" --disable --removesearch ' "autotestsearch,autotestsearch2"
+ )
+ self.auxcommands["directory"].run('ldap --removerole "dirgroupa test,dirgroupanother test"')
+
+ self.rdmc.ui.printer("Validating removal.\n")
+
+ results = self.rdmc.app.select(selector="AccountService.", path_refresh=True)[0].dict
+
+ if not results["LDAP"]["ServiceEnabled"]:
+ self.rdmc.ui.printer("Validated Service disabled.\n")
+ else:
+ sys.stderr.write("Service not disabled.\n")
+ raise IloResponseError("")
+ if not results["ActiveDirectory"]["ServiceAddresses"][0] and not results["LDAP"]["ServiceAddresses"][0]:
+ self.rdmc.ui.printer("Validated Service addresses.\n")
+ else:
+ sys.stderr.write("Service addresses not removed.\n")
+ raise IloResponseError("")
+ if not results["Oem"]["Hpe"]["KerberosSettings"]["KerberosRealm"]:
+ self.rdmc.ui.printer("Validated Realm.\n")
+ else:
+ sys.stderr.write("Realm not removed.\n")
+ raise IloResponseError("")
+ if (
+ not "autotestsearch" in results["LDAP"]["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]
+ and not "autotestsearch2" in results["LDAP"]["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]
+ ):
+ self.rdmc.ui.printer("Validated SearchSettings.\n")
+ else:
+ sys.stderr.write("SearchSettings not removed.\n")
+ raise IloResponseError("")
+ rolecount = 0
+ for role in results["LDAP"]["RemoteRoleMapping"]:
+ if role["RemoteGroup"] == "a test" or role["RemoteGroup"] == "another test":
+ rolecount += 1
+ if rolecount == 0:
+ self.rdmc.ui.printer("Validated Role mappings.\n")
+ else:
+ sys.stderr.write("Remote roles not removed.\n")
+ raise IloResponseError("")
+
+ @log_decor
+ def serverclonecommandautomatictesting(self, local):
+ """copy command automatic testing"""
+
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**************************SERVER CLONE " "AUTOMATIC TESTING*************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ ue_clone_file_name = "ilorest_clone_ue.json"
+ enc_clone_file_name = "ilorest_clone_enc.json"
+ delete_list = [ue_clone_file_name, enc_clone_file_name]
+ error = {}
+
+ self.auxcommands["login"].loginfunction("")
+ try:
+ self.rdmc.ui.printer("Testing 'save' operation (unencrypted)...\n")
+ line_str = "save --nobios --silent -f " + ue_clone_file_name
+ self.auxcommands["serverclone"].run(line_str)
+
+ self.rdmc.ui.printer("Testing 'load' operation (unencrypted)...\n")
+ line_str = "load --silent -f "
+ tmp_file = self.serverclone_helper(ue_clone_file_name, line_str)
+ delete_list.append(tmp_file)
+ self.auxcommands["serverclone"].run(line_str + tmp_file)
+ self.rdmc.ui.printer("Unencrypted Clone Test Complete\n")
+
+ except Exception as excp:
+ self.rdmc.ui.printer("Unencrypted Clone Test Failed\n")
+ with open("clone_error_logfile.log", "r") as err_logfile:
+ error_log = err_logfile.read()
+ with open("changelog.log", "r") as chng_logfile:
+ try:
+ chng_log = json.loads(chng_logfile.read())
+ except ValueError:
+ chng_log = chng_logfile.read()
+ error["unencrypted_clone"] = {
+ "base_error": "An error occurred working with serverclone unencrypted save/load: {}".format(excp),
+ "command": line_str + tmp_file,
+ "traceback": traceback.format_exc(),
+ "clone_error_logfile": error_log,
+ "change_log": chng_log,
+ }
+
+ try:
+ self.rdmc.ui.printer("Testing 'save' operation (encrypted)...\n")
+ line_str = "save --encryption HPESecretAESKey1 --silent --nobios -f " + enc_clone_file_name
+ self.auxcommands["serverclone"].run(line_str)
+
+ self.rdmc.ui.printer("Testing 'load' operation (encrypted)...\n")
+ line_str = "load --silent --encryption HPESecretAESKey1 -f "
+ tmp_file = self.serverclone_helper(enc_clone_file_name, line_str)
+ delete_list.append(tmp_file)
+ self.auxcommands["serverclone"].run(line_str)
+ self.rdmc.ui.printer("Encrypted Clone Test Complete\n")
+
+ except Exception as excp:
+ self.rdmc.ui.printer("Encrypted Clone Test Failed\n")
+ with open("clone_error_logfile.log", "r") as err_logfile:
+ error_log = err_logfile.read()
+ with open("changelog.log", "r") as chng_logfile:
+ try:
+ chng_log = json.loads(chng_logfile.read())
+ except ValueError:
+ chng_log = chng_logfile.read()
+ error["encrypted_clone"] = {
+ "base_error": "An error occurred working with serverclone unencrypted save/load: {}".format(excp),
+ "command": line_str + tmp_file,
+ "traceback": traceback.format_exc(),
+ "clone_error_logfile": error_log,
+ "change_log": chng_log,
+ }
+
+ """ #DO NOT OPEN UNTIL CHRISTMAS (Ok maybe earlier)
+ self.rdmc.ui.printer("Attempting load of clone files from NFS server...\n")
+ for item in self.automatictesting_helper_fb(url_path):
+ f_name = ""
+ clone_file = urlopen(url_path + item)
+ data = clone_file.read()
+ with open(item, 'w+b') as target:
+ target.write(data)
+ if isinstance(item, six.string_types):
+ f_name += item
+ delete_list.append(f_name)
+ self.auxcommands['serverclone'].run("load --silent -f " + f_name)
+
+ self.rdmc.ui.printer("Test Complete...cleaning up.\n")
+ """
+
+ try:
+ err_list, err_str = self.automatictesting_helper_fd(delete_list)
+ if err_list:
+ raise Exception(err_str)
+ except Exception as excp:
+ error["delete_cfs"] = "An error occurred deleting clone files: '%s'" % str(excp)
+
+ if error:
+ raise Exception("The following exceptions occured in ServerClone:\n {}".format(error))
+
+ @log_decor
+ def cloningautomatictesting(self):
+ """cloning automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**************************CLONING " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping iloclone command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["login"].run("")
+ self.cloneobj.run("save -f CLONETEST.json", testing=True)
+ self.auxcommands["logout"].run("")
+ self.auxcommands["login"].run("")
+ self.cloneobj.run("load -f CLONETEST.json", testing=True)
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def pendingautomatictesting(self):
+ """pending automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**************************PENDING " "AUTOMATIC TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["pending"].run("")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def deletecomtesting(self):
+ """Delete component command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************DELETE " "COMPONENT TESTING******************************\n")
+ self.rdmc.ui.printer(
+ "***************************************************" "********************************\n\n"
+ )
+
+ self.auxcommands["login"].run("")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping delete component command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["deletecomp"].run("-a")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def uploadcomptesting(self):
+ """Upload component command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("***************************UPLOAD " "COMPONENT TESTING******************************\n")
+ self.rdmc.ui.printer("*************************************************" "********************************\n\n")
+
+ error_dict = dict()
+ self.auxcommands["login"].run("")
+ url_path = "http://infinitymaster.us.rdlabs.hpecorp.net:1051/automatic_testing/components" "/COMPONENTS/"
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping upload component command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ # skip = False
+ # compname = "cp029917.exe"
+ # compsigname_1 = "cp029917_part1.compsig"
+ # compsigname_2 = "cp029917_part2.compsig"
+ # compnamepath = os.path.join(os.getcwd(), compname)
+ # compsigname_1path = os.path.join(os.getcwd(), compsigname_1)
+ # compsigname_2path = os.path.join(os.getcwd(), compsigname_2)
+
+ components_dict = self.automatictesting_helper_fb(url_path, True)
+ delete_list = []
+ for _, val in list(components_dict.items()):
+ compy = None
+ try:
+ uploadstr = ""
+ for file in val:
+ if len(file.split(".")) <= 2:
+ tmparr = file.split(".")
+ fld = urlopen(url_path + file)
+ data = fld.read()
+ with open(file, "w+b") as target:
+ target.write(data)
+ delete_list.append(file)
+ if "compsig" in tmparr[-1]:
+ uploadstr += " --compsig="
+ else:
+ uploadstr += " --component="
+ compy = file
+ uploadstr += file
+
+ self.auxcommands["uploadcomp"].run(uploadstr + " --forceupload")
+
+ except Exception:
+ error_dict[compy] = uploadstr
+ self.rdmc.ui.printer("iLO flagged an error while uploading the" " previous files: %s\n" % uploadstr)
+ self.rdmc.ui.printer("Check for correct file type and compsig.\n")
+ continue
+
+ # cleanup routine
+ self.automatictesting_helper_fd(delete_list)
+
+ if error_dict:
+ try:
+ raise ValueError
+ except ValueError as err:
+ if not err.args:
+ err.args = ("iLO flagged an error with components: ",)
+ for item in error_dict:
+ err.args = err.args + ("Component: %s, Full String: %s" % (item, error_dict[item]),)
+ raise
+
+ # try:
+ # if not os.path.isfile(compnamepath):
+ # (compname, _) = urlretrieve("http://infinitymaster:81"\
+ # "/automatic_testing/" \
+ # + compname, compname)
+ #
+ # if not os.path.isfile(compsigname_1path):
+ # (compsigname_1, _) = urlretrieve(\
+ # "http://16.83.62.70/jack/" + compsigname_1, compsigname_1)
+ #
+ # if not os.path.isfile(compsigname_2path):
+ # (compsigname_2, _) = urlretrieve(\
+ # "http://16.83.62.70/jack/" + compsigname_2, compsigname_2)
+ # except:
+ # skip = True
+ #
+ # if not skip:
+ # self.auxcommands['uploadcomp'].run("--component={0} --compsig={1} " \
+ # "--forceupload".format(compname, compsigname_1))
+ # #self.auxcommands['uploadcomp'].run("--component=firmware-nic-qlogic-nx2-2." \
+ # # "19.6-1.1.x86_64.rpm --compsig=firmware-" \
+ # # "nic-qlogic-nx2-2.19.6-1.1.x86_64.compsig")
+ #
+ # self.auxcommands['logout'].run("")
+ # else:
+ # self.rdmc.ui.printer("Could not complete test due to missing test " \
+ # "files.\n")
+
+ @log_decor
+ def listanddownloadcomptesting(self):
+ """List and Download component command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************DOWNLOAD " "COMPONENT TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ delete_list = list()
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping download component command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["login"].run("")
+
+ self.rdmc.ui.printer("Components found in iLO Repository:\n\n")
+ self.auxcommands["listcompt"].run("")
+ comps = self.rdmc.app.getcollectionmembers("/redfish/v1/UpdateService/ComponentRepository/")
+
+ for item in comps:
+ uri_str = ""
+ uri_str += "/fwrepo/" + item["Filename"]
+ self.rdmc.ui.printer("Downloading component: '%s'.\n" % item["Filename"])
+ self.auxcommands["downloadcomp"].run(uri_str)
+ self.rdmc.ui.printer("Successfully downloaded '%s' from iLO " "Repository.\n" % item["Filename"])
+ delete_list.append(item["Filename"])
+
+ self.rdmc.ui.printer("Test Complete...cleaning up.\n")
+ self.automatictesting_helper_fd(delete_list)
+ self.auxcommands["logout"].run("")
+ return ReturnCodes.SUCCESS
+
+ @log_decor
+ def deletecomptesting(self):
+ """Delete from component repository testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("***************************DELETE " "COMPONENT TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+ return
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping download component command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["login"].run("")
+ self.rdmc.ui.printer("Components found in iLO Repository:\n\n")
+ self.auxcommands["listcompt"].run("")
+
+ comps = self.rdmc.app.getcollectionmembers("/redfish/v1/UpdateService/ComponentRepository/")
+
+ for item in comps:
+ # str = ""
+ # str += "/fwrepo/" + item['Filename']
+ self.rdmc.ui.printer("Deleting component: '%s'.\n" % item["Filename"])
+ self.auxcommands["deletecomp"].run(item["Filename"])
+ self.rdmc.ui.printer("Successfully downloaded '%s' from iLO " "Repository.\n" % item["Filename"])
+
+ self.rdmc.ui.printer("Test Complete...\n")
+ self.auxcommands["logout"].run("")
+ return ReturnCodes.SUCCESS
+
+ @log_decor
+ def installsettesting(self):
+ """Install set command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************INSTALL " "SET COMMAND TESTING*****************************\n")
+ self.rdmc.ui.printer("**************************************************" "*******************************\n\n")
+
+ self.auxcommands["login"].run("")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping install set command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ installsetlist = []
+ url = "http://infinitymaster.us.rdlabs.hpecorp.net:1051/automatic_testing/install_sets/"
+
+ # skip MakeInstallSet
+ installsetlist = self.automatictesting_helper_fb(url)
+ i = 0
+
+ self.auxcommands["login"].loginfunction("")
+
+ for installset in installsetlist:
+ try:
+ (installsetfile, _) = urlretrieve(url + installset, installset)
+ i = i + 1
+ self.rdmc.ui.printer("Uploading Installset: %s\n" % installsetfile)
+ self.auxcommands["installset"].run("add " + installset)
+ self.rdmc.ui.printer("Invoking Installset: %s\n" % installsetfile)
+ self.auxcommands["installset"].run("invoke --name=TestSet" + str(i) + " --cleartaskqueue")
+ self.rdmc.ui.printer("Removing Installset: %s\n" % installsetfile)
+ self.auxcommands["installset"].run("delete --name=TestSet" + str(i))
+
+ except Exception as excp:
+ self.rdmc.ui.printer(
+ "A general error occured while attempting to "
+ "use the file: %s. The following error was "
+ "logged: %s\n" % (installsetfile, excp)
+ )
+ self.rdmc.ui.printer("Check for missing test files\n")
+ continue
+
+ self.rdmc.ui.printer("Removing any remaining installsets\n")
+ self.auxcommands["installset"].run("--removeall")
+ self.auxcommands["logout"].logoutfunction("")
+
+ for installset in installsetlist:
+ try:
+ if os.path.exists(installsetfile):
+ self.rdmc.ui.printer("Removing local file: %s\n" % installsetfile)
+ os.remove(installsetfile)
+ except Exception as excp:
+ self.rdmc.ui.printer(
+ "An error occured attempting to remove the " "file: %s, logged: %s\n" % (installsetfile, excp)
+ )
+ continue
+
+ return ReturnCodes.SUCCESS
+
+ @log_decor
+ def updatetaskqueuetesting(self):
+ """Update task queue command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**************************UPDATE " "TASK QUEUE TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping taskqueue command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["taskqueue"].run("")
+ self.auxcommands["taskqueue"].run("create 30")
+ self.auxcommands["taskqueue"].run("")
+
+ self.auxcommands["taskqueue"].run("create reboot")
+ self.auxcommands["taskqueue"].run("")
+ self.auxcommands["taskqueue"].run("-r")
+ self.auxcommands["taskqueue"].run("")
+
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def listcomptesting(self):
+ """List component command testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*****************************LIST " "COMPONENT TESTING******************************\n")
+ self.rdmc.ui.printer("***************************************************" "******************************\n\n")
+
+ self.auxcommands["login"].run("")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping list component command, server is not gen10.\n")
+ return ReturnCodes.SUCCESS
+
+ self.auxcommands["listcompt"].run("")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def testsumcode(self):
+ """SUM commands batch testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************SUM " "COMMANDS TESTING******************************\n")
+ self.rdmc.ui.printer(
+ "***************************************************" "********************************\n\n"
+ )
+
+ self.auxcommands["login"].run("")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping list component command, server is not gen10.")
+ return ReturnCodes.SUCCESS
+
+ # TODO:verification of cache to be done manually for now
+ if not self.rdmc.app.typepath.flagiften:
+ self.auxcommands["rawget"].run("/rest/v1/Chassis/1/Power")
+ self.auxcommands["rawget"].run("/redfish/v1/Chassis/1/Power/")
+ self.auxcommands["rawget"].run("/rest/v1/Systems/1")
+ self.auxcommands["rawget"].run("/redfish/v1/Systems/1/")
+
+ self.auxcommands["hpgooey"].run(" --list --namespace perm")
+ # self.auxcommands['hpgooey'].run(" --read --key ipmanager --namespace perm -f " \
+ # "c:\ipman.json")
+ self.auxcommands["logout"].run("")
+ self.auxcommands["login"].run("")
+
+ # Update the password in session_payload.json file.
+ # self.auxcommands['rawpost'].run("session_payload.json --getheaders --service")
+
+ session_key = ""
+ for client in self.rdmc.app._rmc_clients:
+ session_key = client.get_authorization_key()
+ session_key = " --sessionid={0}".format(session_key)
+ break
+ self.auxcommands["rawpost"].run(
+ "sut_provider_registration_payload.json "
+ "--getheaders --response --service --providerid"
+ "=SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_registry_payload.json --getheaders --response" " --service --providerid=SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_schema.json --getheaders --response --service " "--providerid=SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_settings_schema.json --getheaders --response " "--service --providerid=SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_tasksettings_schema.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_systeminventory_schema.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+ self.auxcommands["rawput"].run("HPSUT_rest.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["rawput"].run("HPSUTSettings_rest.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["rawpatch"].run("HPSUTSettings_rest.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["logout"].run("")
+
+ if self.rdmc.app.typepath.flagiften:
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/")
+ self.auxcommands["rawget"].run("/redfish/v1/Chassis/1/Power/")
+ self.auxcommands["rawget"].run("/redfish/v1/Systems/1/")
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/UpdateService")
+ self.auxcommands["rawget"].run("/redfish/v1/Chassis/")
+
+ self.auxcommands["hpgooey"].run(" --list --namespace perm")
+ # self.auxcommands['hpgooey'].run(" --read --key ipmanager --namespace perm -f " \
+ # "c:\ipman.json")
+
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/SecurityService/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/FirmwareInventory/ --expand")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/SoftwareInventory/ --expand")
+ self.auxcommands["rawget"].run("/redfish/v1/updateService/installsets/ --expand")
+
+ # Check if the uploadcomp commands are necessary for the continuation of the code
+ # compname = "xxx"
+ # compsigname = "yyy"
+ compname = "cp029917.exe"
+ compsigname_1 = "cp029917_part1.compsig"
+ self.auxcommands["uploadcomp"].run(
+ "--component={0} --compsig={1} " "--forceupload".format(compname, compsigname_1)
+ )
+
+ self.auxcommands["rawget"].run("/redfish/v1/updateService/ComponentRepository/ --expand")
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/EthernetInterfaces/ --expand")
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["login"].run("")
+ # TODO:update the password in session_payload_gen10.json file.
+ self.auxcommands["rawpost"].run("session_payload_gen10.json --getheaders --service")
+ session_key = ""
+ for client in self.rdmc.app._rmc_clients:
+ session_key = client.get_authorization_key()
+ session_key = " --sessionid={0}".format(session_key)
+ break
+ self.auxcommands["rawdelete"].run(
+ " "
+ "--getheaders --response --service --providerid="
+ "SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawpost"].run(
+ "sut_provider_registration_payload_gen10.json "
+ "--getheaders --response --service --providerid="
+ "SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_registry_payload_gen10.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_schema_gen10.json --getheaders --response " "--service --providerid=SUT-PROVIDER" + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_settings_schema_gen10.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_tasksettings_schema_gen10.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+ self.auxcommands["rawput"].run(
+ "sut_systeminventory_schema_gen10.json --getheaders "
+ "--response --service --providerid=SUT-PROVIDER " + session_key
+ )
+
+ self.auxcommands["rawput"].run("HPSUT_redfish.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["rawput"].run("HPSUTSettings_redfish.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["rawpatch"].run("HPSUTSettings_redfish.json --providerid=HPSUT-PROVIDER")
+ self.auxcommands["logout"].run("")
+
+ self.auxcommands["login"].run("")
+ self.auxcommands["rawget"].run("/redfish/v1/Chassis/1/Power/")
+ self.auxcommands["rawget"].run("/redfish/v1/Systems/1/")
+ self.auxcommands["rawget"].run("/redfish/v1/updateService/installsets/")
+ self.auxcommands["rawget"].run("/redfish/v1/updateService/updatetaskqueue/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/FirmwareInventory/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/SoftwareInventory/")
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/SecurityService/CertificateAuthentication/")
+ self.auxcommands["rawget"].run("/redfish/v1/Managers/1/DateTime/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/ComponentRepository/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/FirmwareInventory/")
+ self.auxcommands["rawget"].run("/redfish/v1/UpdateService/UpdateTaskQueue/ --expand")
+
+ # compname = "xxx"
+ # compsigname = "yyy"
+ compname = "cp029917.exe"
+ compsigname_1 = "cp029917_part1.compsig"
+ self.auxcommands["uploadcomp"].run(
+ "--component={0} --compsig={1} " "--forceupload".format(compname, compsigname_1)
+ )
+ self.auxcommands["rawget"].run("/redfish/v1/updateService/ComponentRepository/")
+ # Component uri returned by the rawget above
+ # self.auxcommands['downloadcomp'].run("Component uri returned by the rawget above")
+ self.auxcommands["logout"].run("")
+
+ @log_decor
+ def maintenancewindowautomatictesting(self):
+ """Maintenance window command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("******************MAINTENANCE WINDOW " "AUTOMATIC TESTING***************************\n")
+ self.rdmc.ui.printer("***********************************************" "**********************************\n\n")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping... Not available on Gen 9.")
+ return
+ self.auxcommands["login"].loginfunction("")
+
+ for entry_ in range(0, 10):
+ startoffset = random.randint(1, 10)
+ endoffset = random.randint(1, 10) + startoffset
+
+ startdatetime = datetime.datetime.now() + datetime.timedelta(days=startoffset)
+ enddatetime = datetime.datetime.now() + datetime.timedelta(days=endoffset)
+
+ maintenancewindow_start_time = startdatetime.strftime("%Y-%m-%dT%H:%M:%S")
+ maintenancewindow_descr = '"This is a test for planned maintenance"'
+ maintenancewindow_end_time = enddatetime.strftime("%Y-%m-%dT%H:%M:%S")
+ maintenancewindow_str = (
+ "add "
+ + maintenancewindow_start_time
+ + " --expire="
+ + maintenancewindow_end_time
+ + " --name=TestMaintenanceEntry"
+ + str(entry_)
+ + " --description "
+ + maintenancewindow_descr
+ )
+
+ self.rdmc.ui.printer("Adding Maintenance Window: " + maintenancewindow_str + "\n")
+ self.auxcommands["maintenancewindow"].run(maintenancewindow_str)
+
+ self.auxcommands["maintenancewindow"].run("")
+
+ maintenancewindow_str = "delete TestMaintenanceEntry" + str(entry_)
+
+ self.rdmc.ui.printer("Removing Maintenance Window: " + maintenancewindow_str + "\n")
+ self.auxcommands["maintenancewindow"].run(maintenancewindow_str)
+
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def serverinfoautomatictesting(self):
+ """Serverinfo command automatic testing"""
+ info_list = {
+ "--fans",
+ "--processor",
+ "--memory",
+ "--thermals",
+ "--power",
+ "--system",
+ }
+ info_string = "--fans --processor --memory --thermals --power --system --showabsent"
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*************************SERVER INFO AUTOMATIC " "TESTING***************************\n")
+ self.rdmc.ui.printer("***********************************************" "**********************************\n\n")
+
+ self.auxcommands["login"].loginfunction("")
+
+ for item in info_list:
+ self.auxcommands["serverinfo"].run(item)
+
+ self.auxcommands["serverinfo"].run(info_string)
+
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def fwpkgcommandautomatictesting(self):
+ """Fwpkg command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("************************FIRMWARE PACKAGE AUTOMATIC " "TESTING***********************\n")
+ self.rdmc.ui.printer("*************************************************" "********************************\n\n")
+
+ self.rdmc.ui.printer("Skipping FW Package testing\n")
+ # self.fwpackage.run("")
+
+ @log_decor
+ def ipprofilesautomatictesting(self):
+ """ipprofiles command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("*****************************IP PROFILES AUTOMATIC " "TESTING***********************\n")
+ self.rdmc.ui.printer("*************************************************" "********************************\n\n")
+
+ self.auxcommands["login"].loginfunction("")
+ try:
+ self.auxcommands["ipprofiles"].run("") # expect a non-empty string or buffer
+ # self.rdmc.ui.printer("Update with JSON:\n")
+ # self.rdmc.ui.printer(repr(self.auxcommands['ipprofiles']obj.run("ipprofiles" + \
+ # ipprofile.json)))
+ # self.rdmc.ui.printer(repr(self.auxcommands['ipprofiles']obj.run("ipprofiles")))
+ except PathUnavailableError:
+ self.rdmc.ui.printer("Skipping IP Profiles test, IP provider is unavailable.\n")
+
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def fwintegritycheckautomatictesting(self):
+ """fwintegrity command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("******************FIRMWARE INTEGRITY CHECK AUTOMATIC " "TESTING*********************\n")
+ self.rdmc.ui.printer("*************************************************" "********************************\n\n")
+
+ if not self.rdmc.app.typepath.flagiften:
+ self.rdmc.ui.printer("Skipping firmware integrity check, only available on Gen 10.")
+ else:
+ self.auxcommands["login"].loginfunction("")
+ self.auxcommands["fwintegritycheck"].run("--results")
+ self.auxcommands["fwintegritycheck"].run("")
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def setbiospasswordautomatictesting(self, local):
+ """biospassword command automatic testing"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("**********************SET BIOS PASSWORD AUTOMATIC " "TESTING************************\n")
+ self.rdmc.ui.printer("***********************************************" "**********************************\n\n")
+
+ if not local:
+ self.auxcommands["login"].loginfunction("")
+ self.auxcommands["set"].run("testpassword " + '""')
+ self.auxcommands["set"].run('""' + " testpassword")
+ self.auxcommands["logout"].logoutfunction("")
+ else:
+ self.rdmc.ui.printer("Skipping setting bios password testing in local.\n")
+
+ @log_decor
+ def setbiosdefaultsautomatictesting(self):
+ """biosdefault command automatictesting"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("************************SET BIOS DEFAULTS AUTOMATIC " "TESTING**********************\n")
+ self.rdmc.ui.printer("***********************************************" "**********************************\n\n")
+
+ self.auxcommands["login"].loginfunction("")
+ try:
+ self.rdmc.ui.printer("Setting BIOS Defaults:\n")
+ self.rdmc.ui.printer(repr(self.auxcommands["biosdefaults"].run("--manufacturingdefaults")))
+ except:
+ self.rdmc.ui.printer("Unable to set BIOS Defaults\n")
+ self.auxcommands["logout"].logoutfunction("")
+
+ @log_decor
+ def smartarrayautomatictesting(self):
+ """smart array command automatictesting"""
+ self.rdmc.ui.printer("\n*************************************************" "********************************\n")
+ self.rdmc.ui.printer("************************SMART ARRAY AUTOMATIC " "TESTING*******************\n")
+ self.rdmc.ui.printer("***********************************************" "********************************\n\n")
+
+ self.auxcommands["select"].selectfunction("SmartStorageConfig.")
+ content = self.rdmc.app.getprops()
+
+ if content:
+ self.auxcommands["smartarray"].selection_output(dict(controller=None), content)
+ else:
+ raise Exception("This system does not have a valid Smart Storage Controller")
+
+ # helper functions
+ def automatictesting_helper_fd(self, data):
+ """
+ Helper function to delete files pulled from remote to local machine.
+
+ :param data: dictionary or list of files to be removed
+ :type data: dictonary or list
+ """
+ errors = []
+
+ if isinstance(data, dict):
+ for _, d_file in list(data.items()):
+ try:
+ if os.path.exists(d_file):
+ os.remove(d_file)
+ except Exception as excp:
+ errors.append(
+ "An error occured attempting to remove the file: %s, logged: %s\n"
+ % (filename.split(".")[0], excp)
+ )
+
+ elif isinstance(data, list):
+ for d_file in data:
+ try:
+ if os.path.exists(d_file):
+ os.remove(d_file)
+ except Exception as excp:
+ errors.append(
+ "An error occured attempting to remove the file: %s, logged: %s\n"
+ % (filename.split(".")[0], excp)
+ )
+
+ err_str = "Errors occurred deleting: "
+ for blah in errors:
+ err_str += "{},".format(blah)
+ return errors
+
+ def serverclone_helper(self, filename=None, line=None):
+ """
+ Helper function to modify and remove properties from a serverclone file
+
+ :param filename: filename of the clone file
+ :type filename: str
+ """
+ errors = []
+ delete_dict = []
+ data = {}
+ try:
+ with open(filename, "r") as cf:
+ if "--encryption" in line:
+ entries_list = [(pos.start(), pos.end()) for pos in list(re.finditer(ending, path))]
+ encryption_key = None
+ data = json.loads(Encryption().decrypt_file(cf.read(), encryption_key))
+ else:
+ data = json.loads(cf.read())
+ except Exception as excp:
+ errors.append("An error occurred opening the clone file '%s': %s" % (filename, excp))
+
+ try:
+ for type in data:
+ if "AccountService" in type:
+ for path in list(data[type].keys()):
+ if "MinPasswordLength" in data[type][path]:
+ data[type][path]["MinPasswordLength"] = random.randint(8, 16)
+ if "AuthFailureDelayTimeSeconds" in data[type][path]:
+ data[type][path]["AuthFailureDelayTimeSeconds"] = random.randint(12, 40)
+ if "ComputerSystem." in type:
+ for path in list(data[type].keys()):
+ if "IndicatorLED" in data[type][path]:
+ data[type][path]["IndicatorLED"] = "Blinking"
+ if "AssetTag" in data[type][path]:
+ data[type][path]["AssetTag"] = "AutomaticTestAsset".join(
+ random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(6)
+ )
+ if "EthernetInterface." in type:
+ for path in list(data[type].keys()):
+ if "SpeedMbps" in data[type][path]:
+ if data[type][path]["FullDuplex"]:
+ data[type][path]["FullDuplex"] = False
+ else:
+ data[type][path]["FullDuplex"] = True
+ if "AutoNeg" in data[type][path]:
+ if data[type][path]["AutoNeg"]:
+ data[type][path]["AutoNeg"] = False
+ else:
+ data[type][path]["AutoNeg"] = True
+ if "SpeedMbps" in data[type][path]:
+ if data[type][path]["SpeedMbps"] == 1000:
+ data[type][path]["SpeedMbps"] = 100
+ else:
+ data[type][path]["SpeedMbps"] = 1000
+ if "ManagerAccount." in type:
+ for path in list(data[type].keys()):
+ if "Password" in data[type][path]:
+ data[type][path]["Password"] = "password"
+ if "iLOLicense" in type:
+ delete_dict.append(type)
+ if "SecureBoot" in type:
+ for path in list(data[type].keys()):
+ if "SecureBootEnable" in data[type][path]:
+ data[type][path]["SecureBootEnable"] = False
+ except (KeyError, ValueError):
+ pass
+
+ for item in delete_dict:
+ try:
+ del data[item]
+ except KeyError:
+ pass
+
+ try:
+ filename2 = filename.split(".")[0] + "_edited" + ".json"
+ with open(filename2, "wb") as cf:
+ if "--encryption" in line:
+ [(pos.start(), pos.end()) for pos in list(re.finditer(ending, path))]
+ encryption_key = None
+ cf.write(Encryption().decrypt_file(json.dumps(data, indent=2), encryption_key))
+ else:
+ cf.write(json.dumps(data, indent=2))
+ except Exception as excp:
+ errors.append("An error occurred writing the clone file '%s': %s" % (filename2, excp))
+
+ if errors:
+ raise
+ else:
+ return filename2
+
+ def automatictesting_helper_fb(self, url, components=False):
+ """
+ Helper function for browsing and creating file dictionary from a
+ file server index. Designed around the output display of Node.js
+ v10.10.0 which places files into an html table.
+
+ :param url: path to the file server.
+ :type url: string
+ :param components: flag if file components will be needed
+ (multiple associated files)
+ :type components: boolean
+ """
+
+ # list of ignored files and paths
+ ignore_list = ["..", ".", "README"]
+
+ table_row_list = []
+ prev_tr = 7
+ block = False
+ try:
+ source_url = urlopen(url)
+ read_url = source_url.read()
+ read_url = read_url[read_url.find("") : read_url.find("")]
+ while read_url.find("") > 0:
+ current_str = read_url[prev_tr : prev_tr + read_url.find("
")]
+ current_str = current_str[
+ current_str.find("")
+ ]
+ current_str = current_str[
+ current_str.find('="') + 2 : current_str.find('="') + 2 + current_str.find('">')
+ ]
+ current_str = current_str[: current_str.find(">") - 1]
+ if current_str.split("/")[-1] in ignore_list or current_str.split("/")[-2] in ignore_list:
+ self.rdmc.ui.printer("Ignoring url: %s\n" % current_str)
+ else:
+ # data[current_str.split('/')[-1]] = [table_row_list.append(current_str)
+ table_row_list.append(current_str.split("/")[-1])
+
+ read_url = read_url[read_url.find("") + 1 :]
+ prev_tr = 0
+
+ source_url.close()
+
+ if components:
+ components_dict = {}
+
+ while len(table_row_list) > 0:
+ for url in table_row_list:
+ split_url = re.split("/", url)
+ last_element = split_url[-1]
+ if last_element.count(".") > 2:
+ sys.stderr.write("Invalid Filetype...skipping\n")
+ table_row_list.remove(url)
+ break
+ else:
+ (compname, ext) = last_element.split(".")
+ if "compsig" in last_element:
+ if components_dict.get(compname) is not None and compname in last_element:
+ components_dict[compname].append(url)
+ table_row_list.remove(url)
+ continue
+ if "part" in last_element and "_" in last_element:
+ (split_compname, _) = compname.split("_")
+ if split_compname in components_dict and url not in components_dict[split_compname]:
+ components_dict[split_compname].append(url)
+ table_row_list.remove(url)
+ else:
+ components_dict.setdefault(compname.split("_")[0], [])
+ continue
+ else:
+ components_dict.setdefault(compname, []).append(url)
+ table_row_list.remove(url)
+
+ return components_dict
+
+ else:
+ return table_row_list
+
+ except ValueError or TypeError:
+ self.rdmc.ui.printer("The web server may not be available")
+ block = True
+ except Exception as excp:
+ self.rdmc.ui.printer("A General Error Occured: %s" % excp)
+ block = True
+
+ def archive_handler(self, archive_file):
+ """
+ Handles archiving of data for bug tracking and reporting
+ """
+
+ packlist = [__error_logfile__, __pass_fail__]
+
+ if self.rdmc.opts.debug:
+ for handle in self.rdmc.app.logger.parent.handlers:
+ if isinstance(handle, FileHandler):
+ ilorestlog = handle.baseFilename
+
+ if ilorestlog:
+ packlist.append(ilorestlog)
+
+ with ZipFile(archive_file, "w") as zip_arch:
+ for _file in packlist:
+ try:
+ zip_arch.write(_file)
+ os.remove(_file)
+ except:
+ pass
+
+ self.rdmc.ui.printer("Logifles archived in '%s'.\n" % archive_file)
+ zip_arch.printdir()
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--latestschema",
+ dest="latestschema",
+ action="store_true",
+ help="Optionally use the latest schema instead of the one "
+ "requested by the file. Note: May cause errors in some data "
+ "retrieval due to difference in schema versions.",
+ default=None,
+ )
+ customparser.add_argument(
+ "--complete",
+ dest="complete",
+ action="store_true",
+ help="Run all available tests.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--sumtest",
+ dest="sumtest",
+ action="store_true",
+ help="Optionally use run only the sum command tests.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--local",
+ dest="local",
+ action="store_true",
+ help="""Use to perform a local login for every operation.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--ahsdiagtest",
+ dest="ahsdiagtest",
+ action="store_true",
+ help="Optionally use run only the sum command tests.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--archive",
+ dest="archive",
+ help="Optionally archive the logfiles",
+ action="append",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ExportAllConfigCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ExportAllConfigCommand.py
new file mode 100644
index 0000000..8f6bb17
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ExportAllConfigCommand.py
@@ -0,0 +1,106 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""ExportAllConfig Command for rdmc"""
+
+import json
+
+try:
+ from rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import InvalidCommandLineErrorOPTS, ReturnCodes
+from redfish.ris import UndefinedClientError
+
+
+class ExportAllConfigCommand:
+ """exportallconfig class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "exportallconfig",
+ "usage": None,
+ "description": "Saves all the config on to json file",
+ "summary": "Saves all the config on to json file. Usage: exportallconfig -f filename.json",
+ "aliases": ["allconfig"],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main exportallconfig worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if hasattr(self.rdmc.app, "config") and self.rdmc.app.config._ac__format.lower() == "json":
+ options.json = True
+ if not options.excludeurl:
+ options.excludeurl = "nothing to exclude"
+
+ if self.rdmc.app.current_client:
+ results = self.rdmc.app.monolith.captureallconfig(exclueurl=options.excludeurl[0])
+ else:
+ raise UndefinedClientError()
+
+ filepath = options.filename[0] if options.filename else "AllConfig.json"
+
+ with open(filepath, "w") as allconfig:
+ json.dump(results, allconfig, indent=2)
+ self.rdmc.ui.printer("File is loaded successfully.\n")
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to save the content to a file, "
+ "if no file given it will get saved to AllConfig.json",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "-e",
+ "--excludeurl",
+ dest="excludeurl",
+ action="append",
+ help="Use this flag is given with a url that url will be skipped",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/GetInventoryCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/GetInventoryCommand.py
new file mode 100644
index 0000000..3f57efe
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/GetInventoryCommand.py
@@ -0,0 +1,315 @@
+# ##
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# BMN
+# ##
+
+# -*- coding: utf-8 -*-
+"""Get Inventory Command for rdmc"""
+
+
+try:
+ from rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+from redfish.ris.resp_handler import ResponseHandler
+
+
+class GetInventoryCommand:
+ """GetInventory command class"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "getinventory",
+ "usage": None,
+ "description": "Get complete inventory"
+ "data from the iLO, including iLO Repo and install set."
+ "\n\texample: getinventory all",
+ "summary": "Get complete inventory data from the iLO.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self.response_handler = None
+
+ def run(self, line, help_disp=False):
+ """Main GetInventory worker function
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.getinventoryvalidation(options)
+ self.response_handler = ResponseHandler(
+ self.rdmc.app.validationmanager,
+ self.rdmc.app.typepath.defs.messageregistrytype,
+ )
+ if self.rdmc.app.typepath.defs.isgen9:
+ raise IncompatibleiLOVersionError("GetInventory command is " "available only on iLO 5 node.")
+
+ alldata = {}
+ results = {}
+
+ # If both are not set, then we will get both inventory and repo data
+ if not options.inventory and not options.repo_data and not options.sut:
+ options.inventory = True
+ options.repo_data = True
+ options.sut = True
+
+ if options.inventory:
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/updateservice/firmwareinventory", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ collectiondata.update({"Members": members})
+ alldata.update({"firmwareInventory": collectiondata})
+ except:
+ alldata.update({"firmwareInventory": {}})
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/updateservice/softwareinventory", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ collectiondata.update({"Members": members})
+ alldata.update({"softwareInventory": collectiondata})
+ except:
+ alldata.update({"softwareInventory": {}})
+
+ try:
+ members = self.rdmc.app.get_handler("/redfish/v1/systems/", service=False, silent=True).dict["Members"]
+ systemdata = {}
+ rootsystem = {}
+ for id in members:
+ for mem_id in id.values():
+ results = self.rdmc.app.get_handler(mem_id, service=True, silent=True).dict
+ id = results["Id"]
+ if id != "1":
+ rootsystem.update({"systems%s" % id: results})
+ systemdata.update({"systems": rootsystem})
+ alldata.update(systemdata)
+ if id == "1":
+ alldata.update({"systems1": results})
+ except:
+ alldata.update({"systems": {}})
+
+ try:
+ sutlocation = alldata.get("systems1").get("Oem").get("Hpe").get("Links")
+ except:
+ if results:
+ sutlocation = next(iter(results)).dict.get("Links")
+ for key, value in list(sutlocation.items()):
+ if key == "SUT":
+ result2 = self.rdmc.app.get_handler(value.get("@odata.id"), service=False, silent=True)
+
+ if result2.status == 200:
+ alldata["SUT"] = result2.dict
+ elif result2.status == 204:
+ pass
+ else:
+ return self.printlastfailedresult(result2)
+ break
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/Managers/1/EthernetInterfaces/", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ collectiondata.update({"Members": members})
+ alldata.update({"EthernetInterfaces": collectiondata})
+ except:
+ alldata.update({"EthernetInterfaces": {}})
+
+ try:
+ results = self.rdmc.app.select(selector="HpeiLODateTime.", path_refresh=True)
+ alldata["DateTime"] = next(iter(results)).resp.dict
+ except:
+ alldata.update({"DateTime": {}})
+
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/UpdateService/", service=False, silent=True
+ ).dict
+ alldata.update({"DowngradePolicy": collectiondata})
+ except:
+ alldata.update({"DowngradePolicy": {}})
+
+ if options.sut:
+ if "system1" not in alldata:
+ try:
+ results = self.rdmc.app.get_handler("/redfish/v1/systems/", service=False, silent=True).dict
+ members = results["Members"]
+ for id in members:
+ for mem_id in id.values():
+ results = self.rdmc.app.get_handler(mem_id, service=True, silent=True).dict
+ id = results["Id"]
+ if id == "1":
+ alldata.update({"systems1": results})
+ except:
+ alldata.update({"systems1": {}})
+ else:
+ _ = alldata["systems1"]
+
+ if alldata.get("systems1"):
+ try:
+ sutlocation = alldata.get("systems1").get("Oem").get("Hpe").get("Links")
+ except:
+ if results:
+ sutlocation = next(iter(results)).dict.get("Links")
+ for key, value in list(sutlocation.items()):
+ if key == "SUT":
+ result2 = self.rdmc.app.get_handler(value.get("@odata.id"), service=False, silent=True)
+
+ if result2.status == 200:
+ alldata["SUT"] = result2.dict
+ elif result2.status == 204:
+ pass
+ else:
+ return self.printlastfailedresult(result2)
+ break
+
+ if options.repo_data:
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/UpdateService/installsets", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ if len(members) == 0:
+ members = []
+ collectiondata.update({"Members": members})
+ alldata.update({"installsets": collectiondata})
+ except:
+ alldata.update({"installsets": []})
+
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/UpdateService/updatetaskqueue", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ if len(members) == 0:
+ members = []
+ collectiondata.update({"Members": members})
+ alldata.update({"updatetaskqueue": collectiondata})
+ except:
+ alldata.update({"updatetaskqueue": []})
+
+ if self.rdmc.app.getiloversion(skipschemas=True) >= 5.130:
+ try:
+ collectiondata = self.rdmc.app.get_handler(
+ "/redfish/v1/UpdateService/maintenancewindows", service=False, silent=True
+ ).dict
+ members = self.rdmc.app.getcollectionmembers(collectiondata.get("@odata.id"))
+ if len(members) == 0:
+ members = []
+ collectiondata.update({"Members": members})
+ alldata.update({"maintenancewindows": collectiondata})
+ except:
+ alldata.update({"maintenancewindows": []})
+
+ comp_repo_url = "/redfish/v1/UpdateService/ComponentRepository/" + "?$expand=."
+ try:
+ members = self.rdmc.app.get_handler(comp_repo_url, silent=True, service=True).dict
+ alldata.update({"ComponentRepository": members})
+ except:
+ alldata.update({"ComponentRepository": {}})
+
+ self.rdmc.ui.print_out_json(alldata)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def combineinstances(self, data, alldata, name, coll=None):
+ """combine the data into one json dictionary"""
+ if coll:
+ coll = coll.resp.dict
+ finaldata = []
+ if "Members" in coll:
+ for member in data:
+ finaldata.append(member.resp.dict)
+ coll["Members"] = finaldata
+ alldata[name] = coll
+ elif len(data) > 1:
+ alldata.update({name: {"Members": []}})
+ for item in data:
+ alldata[name]["Members"].append(item.resp.dict)
+ else:
+ alldata.update({name: {"Members": next(iter(data)).resp.dict}})
+
+ def printlastfailedresult(self, results):
+ """print last failed result function"""
+ self.response_handler.output_resp(results)
+
+ return ReturnCodes.NO_CONTENTS_FOUND_FOR_OPERATION
+
+ def getinventoryvalidation(self, options):
+ """get inventory validation function
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-i",
+ "--inventory",
+ dest="inventory",
+ action="store_true",
+ help="""Use this option to get only the inventory data.""",
+ default=False,
+ )
+ customparser.add_argument(
+ "-s",
+ "--sut",
+ dest="sut",
+ action="store_true",
+ help="""Use this option to get only SUT data from iLO.""",
+ default=False,
+ )
+ customparser.add_argument(
+ "-r",
+ "--repo_data",
+ dest="repo_data",
+ action="store_true",
+ help="""Use this option to get only iLO repository, install set """ """and Task queue details.""",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/HpGooeyCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/HpGooeyCommand.py
new file mode 100644
index 0000000..0b214dc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/HpGooeyCommand.py
@@ -0,0 +1,1071 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Hp Gooey Command for rdmc"""
+
+import ctypes
+import gzip
+import itertools
+import os
+import platform
+import string
+import struct
+import subprocess
+import sys
+import tempfile
+import time
+import logging
+import xml.etree.ElementTree as et
+
+from six import BytesIO, StringIO
+
+import redfish.hpilo.risblobstore2 as risblobstore2
+
+try:
+ from rdmc_helper import (
+ BirthcertParseError,
+ CommandNotEnabledError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ PartitionMoutingError,
+ ReturnCodes,
+ StandardBlobErrorHandler,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ BirthcertParseError,
+ CommandNotEnabledError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ PartitionMoutingError,
+ ReturnCodes,
+ StandardBlobErrorHandler,
+ )
+
+if os.name == "nt":
+ import win32api
+elif sys.platform != "darwin" and "VMkernel" not in platform.uname():
+ import pyudev
+
+LOGGER = logging.getLogger(__name__)
+
+
+class HpGooeyCommand:
+ """Hp Gooey class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "hpgooey",
+ "usage": None,
+ "description": "Directly writes/reads from blobstore"
+ "\n\tBlobstore read example:\n\thpgooey --read "
+ "--key keyexample --namespace perm -f "
+ "\n\n\tBlobstore write example:\n\thpgooey --write"
+ " --key keyexample --namespace perm -f \n\n\tBlobstore delete example:\n\thpgooey "
+ "--delete --key keyexample --namespace perm\n\n\t"
+ "Blobstore list example:\n\thpgooey --list "
+ "--namespace perm\n\n\tNAMESPACES:\n\tperm, "
+ "tmp, dropbox, sfw, ris, volatile",
+ "summary": "directly writes/reads from blobstore",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ try:
+ self.lib = risblobstore2.BlobStore2.gethprestchifhandle()
+ except:
+ self.lib = None
+
+ def run(self, line, help_disp=False):
+ """Access blobstore directly and perform desired function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ if sys.platform == "darwin":
+ raise CommandNotEnabledError("'%s' command is not supported on MacOS" % str(self.name))
+ elif "VMkernel" in platform.uname():
+ raise CommandNotEnabledError("'%s' command is not supported on VMWare" % str(self.name))
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.hpgooeyvalidation(options)
+
+ if options.key == "hpmbirthcert":
+ self.remote_run(options)
+ else:
+ if self.rdmc.app.current_client.base_url.startswith("blobstore"):
+ self.local_run(options)
+ else:
+ self.remote_run(options)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def remote_run(self, options):
+ path = "/blob"
+ if options.namespace:
+ path += "/%s" % options.namespace
+ if options.key:
+ path += "/%s" % options.key
+
+ if options.write:
+ if not (options.key and options.namespace):
+ raise InvalidCommandLineError("Key and namespace are required for hpblob operations.")
+ if not options.filename or not os.path.isfile(options.filename[0]):
+ raise InvalidFileInputError("Please provide a file with input data or given file does not exist")
+
+ blobfiledata = None
+ if options.binfile:
+ _read_mode = "rb"
+ else:
+ _read_mode = "r"
+
+ with open(options.filename[0], _read_mode) as bfh:
+ blobfiledata = bfh.read()
+
+ if options.key == "birthcert" or options.key == "hpmbirthcert":
+ try:
+ filedatastr = bytes(self.remote_read(path))
+ except (StandardBlobErrorHandler, risblobstore2.BlobNotFoundError) as e:
+ filedatastr = ""
+ if not isinstance(filedatastr, bytes):
+ blobdata = filedatastr.encode("utf-8", "ignore")
+ else:
+ blobdata = filedatastr
+
+ blobfiledata = self.writebirthcert(blobfiledata=blobfiledata, blobdata=blobdata, key=options.key)
+ self.remote_write(path, blobfiledata)
+
+ elif "SMARTSTART" in options.key:
+ # compressed_data = self.writesmartstart(blobfiledata)
+ # self.remote_write(path, compressed_data)
+ self.remote_write(path, blobfiledata)
+
+ elif options.read:
+ if not (options.key and options.namespace):
+ raise InvalidCommandLineError("Key and namespace are required for hpblob operations.")
+
+ filedata = BytesIO()
+
+ filedatastr = bytes(self.remote_read(path))
+
+ if options.key == "birthcert" or options.key == "hpmbirthcert":
+ filedatastr = self.readbirthcert(filedatastr, options.key)
+ # elif "SMARTSTART" in options.key:
+ # uncompressed_data = self.readsmartstart(filedatastr)
+ # filedatastr = uncompressed_data
+
+ if not isinstance(filedatastr, bytes):
+ filedatastr = filedatastr.encode("utf-8", "ignore")
+ filedata.write(filedatastr)
+ if options.filename:
+ self.rdmc.ui.printer("Writing data to %s..." % options.filename[0])
+
+ with open(options.filename[0], "wb") as outfile:
+ outfile.write(filedata.getvalue())
+
+ self.rdmc.ui.printer("Done\n")
+ else:
+ filedata_value = filedata.getvalue()
+ if isinstance(filedata_value, bytes):
+ filedata_value = filedata_value.decode("utf-8")
+ if options.key == "hpmbirthcert" and filedata_value == "":
+ self.rdmc.ui.printer("HPM Birth Certificate does not exist!!\n")
+ elif "SMARTSTART" in options.key and filedata_value == "":
+ self.rdmc.ui.printer("SMARTSTART data does not exist!!\n")
+ else:
+ self.rdmc.ui.printer("%s\n" % filedata_value)
+
+ elif options.delete:
+ if not (options.key and options.namespace):
+ raise InvalidCommandLineError("Key and namespace are required" " for hpblob operations.")
+ self.remote_delete(path)
+
+ elif options.list:
+ if not options.namespace:
+ raise InvalidCommandLineError("Namespace is required for hpblob operations.")
+ bs2 = risblobstore2.BlobStore2()
+ recvpacket = bs2.list(options.namespace)
+ errorcode = struct.unpack(" 3:
+ found, path = self.manualmount(label)
+ if found:
+ return True, path
+
+ count = count + 1
+ time.sleep(1)
+
+ raise PartitionMoutingError("Partition with label %s not found on the NAND, so not able to mount" % label)
+
+ def check_flat_path(self):
+ """Check flat path directory."""
+ context = pyudev.Context()
+ count = 0
+
+ while count < 20:
+ for dev in context.list_devices(subsystem="block"):
+ if str(dev.get("ID_SERIAL")).startswith("HP_iLO_LUN"):
+ path = dev.get("DEVNAME")
+ return True, path
+
+ count = count + 1
+ time.sleep(1)
+
+ raise PartitionMoutingError("iLO not responding to request for mounting partition")
+
+ def manualmount(self, label):
+ """Manually mount after fixed time."""
+ context = pyudev.Context()
+
+ for device in context.list_devices(subsystem="block"):
+ if device.get("ID_FS_LABEL") == label:
+ dirpath = os.path.join(tempfile.gettempdir(), label)
+
+ if not os.path.exists(dirpath):
+ try:
+ os.makedirs(dirpath)
+ except Exception as excp:
+ raise excp
+
+ pmount = subprocess.Popen(
+ ["mount", device.device_node, dirpath],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ _, _ = pmount.communicate()
+ return True, dirpath
+
+ return False, None
+
+ def get_available_drives(self):
+ """Obtain all drives"""
+ if "Windows" not in platform.system():
+ return []
+
+ drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
+ return list(
+ itertools.compress(
+ string.ascii_uppercase,
+ [ord(drive) - ord("0") for drive in bin(drive_bitmask)[:1:-1]],
+ )
+ )
+
+ def detecttype(self, readdata):
+ """Function to detect a packets encryption
+
+ :param readdata: data read from the call
+ :type readdata: str.
+ """
+ magic_dict = {
+ "\x1f\x8b\x08": "gz",
+ "\x42\x5a\x68": "bz2",
+ "\x50\x4b\x03\x04": "zip",
+ }
+ max_len = max(len(x) for x in magic_dict)
+ file_start = readdata[:max_len]
+
+ for magic, filetype in list(magic_dict.items()):
+ if file_start.startswith(magic):
+ return filetype
+
+ return "no match"
+
+ def osunmount(self, labels=None):
+ """Function to unmount media using labels
+
+ :param labels: list of labels to unmount
+ :type labels: list.
+ """
+ if labels:
+ for label in labels:
+ try:
+ (_, path) = self.check_mount_path(label)
+ except PartitionMoutingError:
+ if self.rdmc.opts.verbose:
+ self.rdmc.ui.printer("Unable to find {0} partition.".format(label))
+ continue
+ pumount = subprocess.Popen(["umount", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ _, _ = pumount.communicate()
+
+ def readbirthcert(self, blobdata, key):
+ """Function to read the birth certificate
+
+ :param blobdata: data read from birth certificate call
+ :type blobdata: str.
+ """
+ if key != "hpmbirthcert":
+ if "blobstore" in self.rdmc.app.redfishinst.base_url:
+ blobio = BytesIO(blobdata)
+ filehand = gzip.GzipFile(mode="rb", fileobj=blobio)
+
+ data = filehand.read()
+ filehand.close()
+ else:
+ data = blobdata
+ else:
+ blobio = BytesIO(blobdata)
+ filehand = gzip.GzipFile(mode="rb", fileobj=blobio)
+
+ data = filehand.read()
+ filehand.close()
+ return data
+
+ def readsmartstart(self, blobdata):
+ blobio = BytesIO(blobdata)
+ filehand = gzip.GzipFile(mode="rb", fileobj=blobio)
+
+ data = filehand.read()
+ filehand.close()
+ return data
+
+ def writebirthcert(self, blobdata, blobfiledata, key):
+ """Function to read the birth certificate
+
+ :param blobdata: data to be written to birth certificate call
+ :type blobdata: str.
+ :param blobfiledata: data read from birth certificate call
+ :type blobfiledata: str.
+ """
+ filetype = self.detecttype(blobfiledata)
+ if filetype != "no match":
+ raise StandardBlobErrorHandler
+
+ blobdataunpacked = self.readbirthcert(blobdata, key)
+
+ totdata = self.parsebirthcert(blobdataunpacked, blobfiledata)
+
+ databuf = BytesIO()
+
+ filehand = gzip.GzipFile(mode="wb", fileobj=databuf)
+ filehand.write(totdata)
+ filehand.close()
+
+ compresseddata = databuf.getvalue()
+ return compresseddata
+
+ def writesmartstart(self, blobfiledata):
+ """Compresses the given data using gzip and returns compressed data."""
+ try:
+ if not isinstance(blobfiledata, bytes):
+ blobfiledata = blobfiledata.encode("utf-8") # Convert to bytes if not already
+
+ with BytesIO() as databuf:
+ with gzip.GzipFile(mode="wb", fileobj=databuf) as filehand:
+ filehand.write(blobfiledata)
+
+ compressed_data = databuf.getvalue()
+
+ LOGGER.info(
+ f"Successfully compressed data. Original size: {len(blobfiledata)}, "
+ f"Compressed size: {len(compressed_data)}"
+ )
+ return compressed_data
+
+ except Exception as e:
+ LOGGER.error(f"Failed to compress data: {str(e)}", exc_info=True)
+ raise # Re-raise the exception
+
+ def parsebirthcert(self, blobdataunpacked=None, blobfiledata=None):
+ """Parse birth certificate function."""
+ filedata = StringIO(blobfiledata)
+ if blobdataunpacked:
+ if isinstance(blobdataunpacked, bytes) or isinstance(blobdataunpacked, bytearray):
+ blobdataunpacked = blobdataunpacked.decode("utf-8")
+ readdata = StringIO(blobdataunpacked)
+
+ try:
+ readtree = et.parse(readdata)
+ readroot = readtree.getroot()
+ readstr = b""
+
+ if readroot.tag == "BC":
+ for child in readroot:
+ readstr += et.tostring(child)
+
+ if isinstance(readstr, bytes):
+ readstr = readstr.decode("utf-8")
+ totstr = readstr + blobfiledata
+ totstrdata = StringIO(totstr)
+ iterdata = itertools.chain("", totstrdata, "")
+ readroot = et.fromstringlist(iterdata)
+ totdata = et.tostring(readroot)
+ else:
+ raise
+ except Exception as excp:
+ self.rdmc.ui.error("Error while parsing birthcert.\n", excp)
+ raise BirthcertParseError(excp)
+ else:
+ iterdata = itertools.chain("", filedata, "")
+ newroot = et.fromstringlist(iterdata)
+ totdata = et.tostring(newroot)
+
+ return totdata
+
+ def birthcertdelete(self, options=None, compdata=None):
+ """Delete birth certificate function."""
+ totdata = ""
+ databuf = StringIO()
+ filehand = gzip.GzipFile(mode="wb", fileobj=databuf)
+
+ filehand.write(totdata)
+ filehand.close()
+ compresseddata = databuf.getvalue()
+
+ if compdata:
+ compresseddata = compdata
+
+ bs2 = risblobstore2.BlobStore2()
+ risblobstore2.BlobStore2.initializecreds(options.user, options.password)
+
+ errorcode = bs2.write(options.key, options.namespace, compresseddata)
+
+ if not (
+ errorcode == risblobstore2.BlobReturnCodes.SUCCESS or errorcode == risblobstore2.BlobReturnCodes.NOTMODIFIED
+ ):
+ raise StandardBlobErrorHandler(errorcode)
+
+ return errorcode
+
+ def hpgooeyvalidation(self, options):
+ """Download command method validation function
+
+ :param options: command options
+ :type options: options.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="""Use the provided filename to perform operations.""",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "-r",
+ "--read",
+ dest="read",
+ action="store_true",
+ help="""read data into the provided filename""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-w",
+ "--write",
+ dest="write",
+ action="store_true",
+ help="""use the provided filename to output data""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-d",
+ "--delete",
+ dest="delete",
+ action="store_true",
+ help="""delete the file from the provided namespace""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-l",
+ "--list",
+ dest="list",
+ action="store_true",
+ help="""list the files from the provided namespace""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-k",
+ "--key",
+ dest="key",
+ help="""blobstore key name to use for opetations with no """ """spaces and 32 character limit""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-n",
+ "--namespace",
+ dest="namespace",
+ help="""namespace where operation is to be performed""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--mountabsr",
+ dest="mountabsr",
+ action="store_true",
+ help="""use this flag to mount absaroka repo""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--mountgaius",
+ dest="mountgaius",
+ action="store_true",
+ help="""use this flag to mount gaius""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--mountvid",
+ dest="mountvid",
+ action="store_true",
+ help="""use this flag to mount vid""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--mountflat",
+ dest="mountflat",
+ action="store_true",
+ help="""use this flag to mount flat mode""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--unmountabsr",
+ dest="unmountabsr",
+ action="store_true",
+ help="""use this flag to unmount absaroka media""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--unmountvid",
+ dest="unmountvid",
+ action="store_true",
+ help="""use this flag to vid media""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--unmountgaius",
+ dest="unmountgaius",
+ action="store_true",
+ help="""use this flag to unmount gaius media""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--unmountmedia",
+ dest="unmountmedia",
+ action="store_true",
+ help="""use this flag to unmount all NAND partitions""",
+ default=None,
+ )
+ customparser.add_argument(
+ "--binfile",
+ dest="binfile",
+ action="store_true",
+ help="""use this flag to write and read binary files""",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ISToolCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ISToolCommand.py
new file mode 100644
index 0000000..1d75c73
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/ISToolCommand.py
@@ -0,0 +1,186 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""ISTool Command for rmdc"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class ISToolCommand:
+ """ISTool class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "istool",
+ "usage": None,
+ "description": "Displays ilo data useful in debugging",
+ "summary": "displays ilo data useful in debugging",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Debug helper for iLO
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ ilodata = ""
+ reglist = []
+ foundreg = False
+
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("Istool command does not take any arguments.")
+
+ self.istoolvalidation(options)
+
+ if self.rdmc.app.monolith.is_redfish:
+ regstr = "/redfish/v1/Registries/"
+ ilostr = "/redfish/v1/Managers/1/"
+ else:
+ regstr = "/rest/v1/Registries"
+ ilostr = "/rest/v1/Managers/1"
+
+ try:
+ biosresults = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.biospath, service=True, silent=True)
+ regresults = self.rdmc.app.get_handler(regstr, service=True, silent=True)
+ romresults = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.systempath, service=True, silent=True)
+ iloresults = self.rdmc.app.get_handler(ilostr, silent=True, service=True)
+ except:
+ raise
+
+ if biosresults.dict:
+ try:
+ biosreg = biosresults.dict["AttributeRegistry"]
+ self.rdmc.ui.printer("System has attribute registry: %s\n" % biosreg)
+ except Exception as excp:
+ biosreg = None
+ self.rdmc.ui.error("Attribute registry not found in BIOS.\n", excp)
+
+ if regresults.dict:
+ items = regresults.dict[self.rdmc.app.typepath.defs.collectionstring]
+
+ for item in items:
+ try:
+ schema = item["Schema"]
+ except:
+ item = self.rdmc.app.get_handler(
+ item[self.rdmc.app.typepath.defs.hrefstring],
+ service=True,
+ silent=True,
+ )
+
+ item = item.dict
+ if "Schema" in item:
+ schema = item["Schema"]
+ else:
+ schema = item["Registry"]
+
+ if schema:
+ available = True
+
+ locationdict = (
+ self.rdmc.app.validationmanager.geturidict(item["Location"][0])
+ if self.rdmc.app.validationmanager
+ else None
+ )
+ extref = (
+ self.rdmc.app.get_handler(locationdict, service=True, silent=True) if locationdict else None
+ )
+
+ if not extref:
+ available = False
+
+ if available:
+ val = schema + " : Available"
+ reglist.append(val)
+ else:
+ val = schema + " : Not Available"
+ reglist.append(val)
+
+ if schema == biosreg:
+ foundreg = True
+
+ self.rdmc.ui.printer(
+ "The following attribute registries are in the registry \
+ repository:\n"
+ )
+ for item in reglist:
+ self.rdmc.ui.printer("%s\n" % item)
+
+ if foundreg:
+ self.rdmc.ui.printer("The system attribute registry was found in the " "registry repository.\n")
+ else:
+ self.rdmc.ui.error("The system attribute registry was not found in " "the registry repository.\n")
+
+ if iloresults.dict:
+ try:
+ ilodata = iloresults.dict["FirmwareVersion"]
+ self.rdmc.ui.printer("iLO Version: %s\n" % ilodata)
+ except Exception as excp:
+ self.rdmc.ui.error("Unable to find iLO firmware data.\n", excp)
+
+ if romresults.dict:
+ try:
+ biosversion = romresults.dict["BiosVersion"]
+ self.rdmc.ui.printer("BIOS Version: %s\n" % biosversion)
+ except Exception as excp:
+ self.rdmc.ui.error("Unable to find ROM data.\n", excp)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def istoolvalidation(self, options):
+ """ISTool validation function"""
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/MonolithCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/MonolithCommand.py
new file mode 100644
index 0000000..916b730
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/MonolithCommand.py
@@ -0,0 +1,120 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Monolith Command for rdmc"""
+
+import json
+
+try:
+ from rdmc_helper import UI, InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_helper import UI, InvalidCommandLineErrorOPTS, ReturnCodes
+from redfish.rest.containers import JSONEncoder
+from redfish.ris import UndefinedClientError
+
+
+class MonolithCommand:
+ """Monolith class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "monolith",
+ "usage": None,
+ "description": "Displays entire cached data structure available",
+ "summary": "displays entire cached data structure available",
+ "aliases": ["mono"],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main monolith worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if hasattr(self.rdmc.app, "config") and self.rdmc.app.config._ac__format.lower() == "json":
+ options.json = True
+
+ if self.rdmc.app.current_client:
+ results = self.rdmc.app.monolith.capture(redmono=options.redmono)
+ else:
+ raise UndefinedClientError()
+
+ if options.filename:
+ with open(options.filename[0], "w") as monolith:
+ if options.json:
+ json.dump(results, monolith, indent=2, cls=JSONEncoder)
+ else:
+ monolith.write(str(results))
+
+ elif options.json:
+ UI().print_out_json(results) # .reduce())
+ else:
+ UI().print_out_human_readable(results) # .reduce())
+
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ customparser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to save the monolith" " into a file with the given filename.",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "-r",
+ "--reduced",
+ dest="redmono",
+ action="store_true",
+ help="Use this flag if you wish to save the reduced monolith" " into a file with the given filename.",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SMBiosCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SMBiosCommand.py
new file mode 100644
index 0000000..87c9b2d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SMBiosCommand.py
@@ -0,0 +1,221 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+
+# -*- coding: utf-8 -*-
+"""New Command for RDMC"""
+
+import json
+import struct
+
+import redfish.ris
+
+try:
+ from rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+__filename__ = "smbios.json"
+
+
+class SMBiosCommand:
+ """Main smbios command class"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "smbios",
+ "usage": None,
+ "description": "Run to get the smbios for the system." "\n\texample write smbios data to file: smbios",
+ "summary": "Gets the smbios for the currently logged in server and "
+ "write results to a file in json format.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.rdmc = None
+ self.filename = None
+
+ def smbiosfunction(self, options):
+ """Main smbios command worker function
+
+ :param options: command options
+ :type options: options.
+ """
+
+ sysresp = self.rdmc.app.get_handler(self.rdmc.app.typepath.defs.systempath, silent=True, service=True)
+
+ try:
+ path = sysresp.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["SMBIOS"]["extref"]
+ except:
+ raise NoContentsFoundForOperationError("Unable to find ComputerSystems")
+
+ resp = self.rdmc.app.get_handler(path, silent=True, service=True)
+
+ if resp and resp.status == 200:
+ data = self.unpackdata(resp.ori)
+ data = {"smbios data": data}
+
+ outfile = open(self.filename, "w")
+ outfile.write(json.dumps(data, indent=2, cls=redfish.ris.JSONEncoder, sort_keys=True))
+ outfile.close()
+
+ self.rdmc.ui.printer("Smbios saved to: %s\n" % self.filename)
+ else:
+ raise NoContentsFoundForOperationError("Unable to find smbios.")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def unpackdata(self, data):
+ """Unpacks and returns json formatted data
+
+ :param data: data to unpack
+ :type data: string.
+ """
+
+ dataptr = 0
+ fulldata = []
+ if not isinstance(data, (bytes, bytearray)):
+ data = data.encode()
+
+ try:
+ while True:
+ record_header = struct.unpack_from("BBH", data[dataptr : dataptr + 4])
+
+ record_type = record_header[0]
+ record_length = record_header[1]
+ record_handle = record_header[2]
+
+ # skip to the strings
+ dataptr += record_length
+
+ # read strings from end of record
+ record_strings = []
+
+ while True:
+ tempstr = ""
+
+ while data[dataptr] != 0:
+ tempstr += chr(data[dataptr])
+ dataptr += 1
+
+ dataptr += 1
+
+ if len(tempstr) == 0:
+ break
+
+ record_strings.append(str(tempstr))
+
+ while data[dataptr] == 0:
+ dataptr += 1
+
+ fulldata.append(
+ {
+ "Type": str(record_type),
+ "Length": str(record_length),
+ "Handle": str(record_handle),
+ }
+ )
+
+ if len(record_strings):
+ fulldata[-1]["Strings"] = str(record_strings)
+ except IndexError:
+ pass
+ return fulldata
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for smbios command main function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if self.rdmc.app.typepath.url:
+ if "http" not in self.rdmc.app.typepath.url:
+ options.logout = True
+ self.cmdbase.logout_routine(self, options)
+ else:
+ options.logout = True
+ self.cmdbase.logout_routine(self, options)
+ self.smbiosvalidation(options)
+
+ if not self.rdmc.app.typepath.flagiften:
+ raise IncompatibleiLOVersionError("smbios command is RedFish only.")
+
+ self.smbiosfunction(options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def smbiosvalidation(self, options):
+ """smbios command method validation function
+
+ :param options: command options
+ :type options: options.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ # filename validations and checks
+ self.filename = None
+
+ if options.filename:
+ self.filename = options.filename[0]
+
+ if not self.filename:
+ self.filename = __filename__
+
+ def definearguments(self, customparser):
+ """Wrapper function for smbios command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to use a different"
+ " filename than the default one. The default filename is"
+ " %s." % __filename__,
+ action="append",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SecurityStatusCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SecurityStatusCommand.py
new file mode 100644
index 0000000..96a32a4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/SecurityStatusCommand.py
@@ -0,0 +1,179 @@
+###
+# Copyright 2016 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+# -*- coding: utf-8 -*-
+"""Security Status Command for rdmc"""
+
+import struct
+from argparse import SUPPRESS
+from ctypes import POINTER, c_char_p, c_ubyte, create_string_buffer
+
+try:
+ from rdmc_base_classes import RdmcCommandBase
+ from rdmc_helper import Encryption, InvalidCommandLineErrorOPTS, ReturnCodes
+except ImportError:
+ from ilorest.rdmc_base_classes import RdmcCommandBase
+ from ilorest.rdmc_helper import Encryption, InvalidCommandLineErrorOPTS, ReturnCodes
+from redfish.hpilo.risblobstore2 import BlobStore2
+from redfish.hpilo.rishpilo import BlobReturnCodes, HpIloChifAccessDeniedError
+
+
+class SecurityStatusCommand(RdmcCommandBase):
+ """Security Status class command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "securitystatus",
+ "usage": None,
+ "description": "Security status example:\n\tsecuritystatus",
+ "summary": "command to retrieve the current system security status and validate " "credentials via chif.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Access blobstore directly and perform desired function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if bool(options.user) ^ bool(options.password):
+ self.rdmc.ui.error("Credentials: Missing\n")
+
+ elif options.user and options.password:
+ if options.encode:
+ options.user = Encryption.decode_credentials(options.user)
+ if isinstance(options.user, bytes):
+ options.user = options.user.decode("utf-8")
+ options.password = Encryption.decode_credentials(options.password)
+ if isinstance(options.password, bytes):
+ options.password = options.password.decode("utf-8")
+
+ result = self.validate_creds(options.user, options.password)
+ # self.rdmc.ui.printer("Validate Creds is {}...\n".format(result))
+ if result:
+ secstate = BlobStore2().get_security_state()
+ if isinstance(secstate, bytes):
+ if secstate == b"\x00":
+ state = struct.unpack("B", secstate)
+ state = str(state).strip(",( )")
+ self.rdmc.ui.printer("Security State is {}\n".format(state))
+ else:
+ secstate = secstate.decode("utf-8")
+ self.rdmc.ui.printer("Security State is {}\n".format(secstate))
+ else:
+ self.rdmc.ui.printer("Security State is {}\n".format(secstate))
+ self.rdmc.ui.printer("Credentials: Valid\n")
+ else:
+ self.rdmc.ui.error("Credentials: Invalid\n")
+
+ else:
+ secstate = BlobStore2().get_security_state()
+ if isinstance(secstate, bytes):
+ if secstate == b"\x00":
+ state = struct.unpack("B", secstate)
+ state = str(state).strip(",( )")
+ self.rdmc.ui.printer("Security State is {}\n".format(state))
+ else:
+ secstate = secstate.decode("utf-8")
+ self.rdmc.ui.printer("Security State is {}\n".format(secstate))
+ else:
+ self.rdmc.ui.printer("Security State is {}\n".format(secstate))
+
+ return ReturnCodes.SUCCESS
+
+ def validate_creds(self, user, passwrd):
+ """Validates credentials via CHIF
+
+ :param user: username to validate
+ :type user: str.
+ :param passwrd: password to validate
+ :type passwrd: str.
+ """
+
+ valid = False
+ dll = BlobStore2.gethprestchifhandle()
+ dll.ChifInitialize(None)
+ sec_support = dll.ChifGetSecuritySupport()
+ if sec_support <= 1:
+ dll.ChifEnableSecurity()
+ dll.initiate_credentials.argtypes = [c_char_p, c_char_p]
+ dll.initiate_credentials.restype = POINTER(c_ubyte)
+
+ usernew = create_string_buffer(user.encode("utf-8"))
+ passnew = create_string_buffer(passwrd.encode("utf-8"))
+
+ dll.initiate_credentials(usernew, passnew)
+ credreturn = dll.ChifVerifyCredentials()
+ if credreturn == 0:
+ valid = True
+ else:
+ valid = False
+ if not credreturn == BlobReturnCodes.CHIFERR_AccessDenied:
+ raise HpIloChifAccessDeniedError(
+ "Error %s - Chif Access Denied occurred while trying " "to open a channel to iLO." % credreturn
+ )
+
+ BlobStore2.unloadchifhandle(dll)
+ return valid
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ customparser.add_argument(
+ "-u",
+ "--user",
+ dest="user",
+ help="If you are not logged in yet, including this flag along"
+ " with the password and URL flags can be used to login to a"
+ " server in the same command."
+ "",
+ default=None,
+ )
+ customparser.add_argument(
+ "-p",
+ "--password",
+ dest="password",
+ help="""Use the provided iLO password to log in.""",
+ default=None,
+ )
+ customparser.add_argument(
+ "-e",
+ "--enc",
+ dest="encode",
+ action="store_true",
+ help=SUPPRESS,
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__init__.py b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AHSdiagCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AHSdiagCommand.cpython-310.pyc
new file mode 100644
index 0000000..58a27e5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AHSdiagCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AutomaticTestingCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AutomaticTestingCommand.cpython-310.pyc
new file mode 100644
index 0000000..2d17907
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/AutomaticTestingCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ExportAllConfigCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ExportAllConfigCommand.cpython-310.pyc
new file mode 100644
index 0000000..38b222f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ExportAllConfigCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/GetInventoryCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/GetInventoryCommand.cpython-310.pyc
new file mode 100644
index 0000000..26d672f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/GetInventoryCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/HpGooeyCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/HpGooeyCommand.cpython-310.pyc
new file mode 100644
index 0000000..3ff40cd
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/HpGooeyCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ISToolCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ISToolCommand.cpython-310.pyc
new file mode 100644
index 0000000..acd992e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/ISToolCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/MonolithCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/MonolithCommand.cpython-310.pyc
new file mode 100644
index 0000000..608c3e4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/MonolithCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SMBiosCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SMBiosCommand.cpython-310.pyc
new file mode 100644
index 0000000..1f3026f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SMBiosCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SecurityStatusCommand.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SecurityStatusCommand.cpython-310.pyc
new file mode 100644
index 0000000..b1636df
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/SecurityStatusCommand.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..108217a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/ilorest/extensions/_hidden_commands/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/AppAccountCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/AppAccountCommand.py
new file mode 100644
index 0000000..4f84adb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/AppAccountCommand.py
@@ -0,0 +1,693 @@
+###
+# Copyright 2016-2024 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+""" AppAccount command for rdmc """
+import ctypes
+import os
+from argparse import RawDescriptionHelpFormatter
+from redfish.hpilo.vnichpilo import AppAccount
+from redfish.rest.connections import ChifDriverMissingOrNotFound, VnicNotEnabledError
+import redfish
+
+from redfish.ris.rmc_helper import UserNotAdminError
+
+try:
+ from rdmc_helper import (
+ LOGGER,
+ GenerateAndSaveAccountError,
+ AppAccountExistsError,
+ ReactivateAppAccountTokenError,
+ ReturnCodes,
+ InvalidCommandLineErrorOPTS,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ UsernamePasswordRequiredError,
+ NoAppAccountError,
+ VnicExistsError,
+ SavinginTPMError,
+ SavinginiLOError,
+ GenBeforeLoginError,
+ AppIdListError,
+ UI,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ LOGGER,
+ GenerateAndSaveAccountError,
+ AppAccountExistsError,
+ ReactivateAppAccountTokenError,
+ ReturnCodes,
+ InvalidCommandLineErrorOPTS,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ UsernamePasswordRequiredError,
+ NoAppAccountError,
+ VnicExistsError,
+ SavinginTPMError,
+ SavinginiLOError,
+ GenBeforeLoginError,
+ AppIdListError,
+ UI,
+ )
+
+
+class AppAccountCommand:
+ """Main command template"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "appaccount",
+ "usage": "appaccount\n\n",
+ "description": "Manages application accounts in iLO and TPM, allowing creation,"
+ "deletion, and verification with appaccount create, appaccount delete, "
+ "and appaccount exists."
+ "Retrieves details of all application accounts using appaccount details.\n"
+ "Supported only on VNIC-enabled iLO7 servers.\n"
+ "For help on specific subcommands, run: appaccount -h.\n\n",
+ "summary": "Creates/Deletes application account, Checks the existence of an"
+ " application account, Provides details on all app accounts present in the server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+ # Check for admin privileges
+ if "blobstore" in self.rdmc.app.current_client.base_url or "16.1.15." in self.rdmc.app.current_client.base_url:
+ if os.name == "nt":
+ if not ctypes.windll.shell32.IsUserAnAdmin() != 0:
+ self.rdmc.app.typepath.adminpriv = False
+ elif not os.getuid() == 0:
+ self.rdmc.app.typepath.adminpriv = False
+
+ if self.rdmc.app.typepath.adminpriv is False:
+ raise UserNotAdminError("")
+
+ client = self.appaccountvalidation(options)
+ if client:
+ if "16.1.15.1" not in client.base_url:
+ raise VnicExistsError(
+ "Appaccount command can only be executed " "from the host OS of a VNIC-enabled iLO7 based server.\n"
+ )
+
+ # To populate the correct host app information
+ if "self_register" in options:
+ if options.self_register:
+ if (
+ ("hostappname" in options and options.hostappname)
+ or ("hostappid" in options and options.hostappid)
+ or ("salt" in options and options.salt)
+ ):
+ raise InvalidCommandLineError(
+ "The parameters provided in the command are invalid."
+ " You may include either the --self tag "
+ "or the combination of --hostappid, --hostappname, and --salt tags,"
+ " but not both.\n"
+ )
+ else:
+ if options.command:
+ if options.command.lower() == "create":
+ if not (options.hostappname and options.hostappid and options.salt):
+ raise InvalidCommandLineError(
+ "Please provide all the required host application"
+ " information.\nTo proceed without entering host "
+ "application details, include "
+ "--self in the command.\n"
+ )
+ elif options.command.lower() == "delete":
+ # Validate hostappid is provided
+ if not options.hostappid:
+ raise InvalidCommandLineError(
+ "--hostappid is a required parameter for the appaccount delete command.\n"
+ )
+
+ # Check if hostappid is 00b5 (self-registered) - treat as --self
+ is_self_registered_id = options.hostappid and "00b5" in options.hostappid.lower()
+
+ # For self-registered accounts (--self or 00b5), no credentials required
+ if not options.self_register and not is_self_registered_id:
+ # For delete, require either credentials OR host app info (but not both are mandatory)
+ has_credentials = all([options.user, options.password])
+ has_app_info = all([options.hostappname, options.salt])
+
+ # If hostappid is provided, user must provide EITHER credentials OR app info
+ if not has_credentials and not has_app_info:
+ raise InvalidCommandLineError(
+ "For deleting application accounts, please provide either:\n"
+ " 1. Username and password (using -u and -p flags), OR\n"
+ " 2. Host application information (using --hostappname and --salt flags)\n"
+ )
+
+ # If partial credentials or partial app info, validate completeness
+ if (options.user or options.password) and not has_credentials:
+ raise InvalidCommandLineError("Please provide both username and password\n")
+ if (options.hostappname or options.salt) and not has_app_info:
+ raise InvalidCommandLineError(
+ "Please provide all the required host application"
+ " information.\nTo proceed without entering host "
+ "application details, include "
+ "--self in the command.\n"
+ )
+ elif options.command.lower() == "exists":
+ if not options.hostappid:
+ raise InvalidCommandLineError(
+ "Please provide hostappid."
+ " To proceed without entering the ID,"
+ " include --self in the command.\n"
+ )
+ elif options.command.lower() == "details":
+ if not options.hostappid and not options.self_register:
+ raise InvalidCommandLineError(
+ "Please provide hostappid using --hostappid or --hostappid all."
+ " To view self-registered account, use --self.\n"
+ )
+ elif options.command.lower() == "reactivate":
+ if not (options.user and options.password):
+ LOGGER.error("Please provide both username and password.\n")
+ raise InvalidCommandLineError("Please provide both username and password.\n")
+
+ if not (options.hostappname and options.hostappid and options.salt):
+ LOGGER.error(
+ "Please provide all the required host application"
+ " information.\nTo proceed without entering host "
+ "application details, include "
+ "--self in the command.\n"
+ )
+ raise InvalidCommandLineError(
+ "Please provide all the required host application"
+ " information.\nTo proceed without entering host "
+ "application details, include "
+ "--self in the command.\n"
+ )
+ else:
+ raise InvalidCommandLineError("The command you have entered is invalid.\n")
+
+ try:
+ # For details command, no credentials needed (read-only operation)
+ # Only delete command for non-00b5 accounts needs credentials
+ if options.command and options.command.lower() == "details":
+ # Handle hostappid="all" - don't pass to AppAccount as it's not a real app ID
+ hostappid_value = getattr(options, "hostappid", None) if "hostappid" in options else None
+ if hostappid_value and hostappid_value.lower() == "all":
+ hostappid_value = None
+
+ app_obj = AppAccount(
+ appname=getattr(options, "hostappname", None) if "hostappname" in options else "self_register",
+ appid="self_register",
+ salt=getattr(options, "salt", None) if "salt" in options else "self_register",
+ username=None,
+ password=None,
+ log_dir=self.rdmc.log_dir,
+ )
+ else:
+ # Handle hostappid="all" for non-details commands too (though it shouldn't be used)
+ hostappid_value = options.hostappid if "hostappid" in options else None
+ if hostappid_value and hostappid_value.lower() == "all":
+ hostappid_value = None
+
+ app_obj = AppAccount(
+ appname=options.hostappname if "hostappname" in options else "self_register",
+ appid=options.hostappid if "hostappid" in options else "self_register",
+ salt=options.salt if "salt" in options else "self_register",
+ username=options.user,
+ password=options.password,
+ log_dir=self.rdmc.log_dir,
+ )
+ except Exception as excp:
+ raise NoAppAccountError(
+ "Error occured while locating application" " account. Please recheck the entered inputs.\n"
+ )
+
+ # Function to find out the iLO Generation
+ self.get_ilover_beforelogin(app_obj)
+
+ # Check if app account exists - skip for details command
+ already_exists = False
+ command_lower = options.command.lower() if options.command else ""
+ hostappid_option = getattr(options, "hostappid", None)
+ skip_token_check = (
+ command_lower == "details"
+ )
+
+ if not skip_token_check:
+ try:
+ # For short app IDs (4 chars), check using ListAppIds
+ if (
+ command_lower in ["delete", "exists"]
+ and getattr(options, "hostappid", None)
+ and len(options.hostappid) == 4
+ ):
+ try:
+ list_of_appids = self.rdmc.app.ListAppIds(app_obj)
+ for app_id in list_of_appids:
+ if app_id["ApplicationID"][-4:].lower() == options.hostappid.lower():
+ already_exists = True
+ break
+ except Exception:
+ already_exists = self.rdmc.app.token_exists(app_obj)
+ else:
+ already_exists = self.rdmc.app.token_exists(app_obj)
+ except Exception as excp:
+ if command_lower != "details":
+ raise AppAccountExistsError("Error occurred while checking if application account exists.\n")
+
+ if options.command:
+ if options.command.lower() == "create":
+ if not options.user or not options.password: # Check if this is the correct variable
+ raise UsernamePasswordRequiredError("Please enter Username and Password.\n")
+
+ try:
+ errorcode = self.rdmc.app.generate_save_token(app_obj)
+ if errorcode == 0:
+ self.rdmc.ui.printer("Application account has been generated and saved successfully.\n")
+ return ReturnCodes.SUCCESS
+ except redfish.hpilo.vnichpilo.AppAccountExistsError:
+ self.rdmc.ui.printer("Application account already exists for the specified host application.\n")
+ return ReturnCodes.SUCCESS
+ except redfish.hpilo.vnichpilo.SavinginTPMError: # Check for specific error messages
+ raise SavinginTPMError(
+ "Failed to save the app account in TPM. "
+ "Please execute the appaccount delete command"
+ " with the same host application information and "
+ "attempt to create the app account again.\n"
+ "Alternatively, you can use the --no_app_account "
+ "option in the Login Command to log in using your iLO user account credentials.\n"
+ )
+ except redfish.hpilo.vnichpilo.SavinginiLOError:
+ raise SavinginiLOError(
+ "Failed to save app account in iLO. "
+ "Please execute the appaccount delete command"
+ " with the same host application information and "
+ "attempt to create the app account again.\n"
+ "Alternatively, you can use the --no_app_account "
+ "option in the Login Command to log in using your iLO user account credentials.\n"
+ )
+ except redfish.rest.v1.InvalidCredentialsError:
+ raise redfish.rest.v1.InvalidCredentialsError(0)
+ except redfish.hpilo.vnichpilo.GenerateAndSaveAccountError:
+ raise GenerateAndSaveAccountError(
+ "Error occurred while generating and saving app account. "
+ "Please retry after sometime.\n"
+ "Alternatively, you can use the --no_app_account "
+ "option in the Login Command to log in using your iLO user account credentials.\n"
+ )
+
+ elif options.command.lower() == "delete":
+ is_self = getattr(options, "self_register", False)
+ has_credentials = options.user and options.password
+ has_app_info = options.hostappname and options.salt
+
+ # Try to delete from both TPM and iLO
+ deleted_from_tpm = False
+ deleted_from_ilo = False
+ app_id_to_delete = None
+
+ # Step 1: Try to delete from TPM (may not exist after TPM reset)
+ try:
+ errorcode = self.rdmc.app.delete_token(app_obj)
+ if errorcode == 0:
+ deleted_from_tpm = True
+ LOGGER.debug("Successfully deleted app account from TPM")
+ except Exception as tpm_error:
+ # TPM deletion failed (token may not exist in TPM), continue to try iLO deletion
+ LOGGER.debug("TPM deletion failed: %s", str(tpm_error))
+ pass
+
+ # Step 2: Try to delete from iLO - requires credentials or authenticated session
+ if has_credentials or is_self or has_app_info:
+ try:
+ # Get the app ID to delete
+ list_of_appids = self.rdmc.app.ListAppIds(app_obj)
+
+ if is_self:
+ for app_account in list_of_appids:
+ if "00b5" in app_account.get("ApplicationID", "").lower():
+ app_id_to_delete = app_account["ApplicationID"]
+ break
+ elif options.hostappid:
+ for app_account in list_of_appids:
+ full_id = app_account["ApplicationID"]
+ # Match full ID or last 4 chars (short ID)
+ if full_id == options.hostappid or (
+ len(options.hostappid) == 4 and full_id[-4:].lower() == options.hostappid.lower()
+ ):
+ app_id_to_delete = full_id
+ break
+
+ # Delete from iLO via REST API
+ if app_id_to_delete:
+ delete_url = (
+ f"/redfish/v1/AccountService/Accounts/{app_id_to_delete}"
+ )
+ try:
+ self.rdmc.app.delete_handler(delete_url)
+ deleted_from_ilo = True
+ LOGGER.debug("Successfully deleted app account from iLO")
+ except Exception as ilo_error:
+ LOGGER.debug("iLO deletion failed: %s", str(ilo_error))
+ except Exception as e:
+ LOGGER.debug("Error during iLO deletion: %s", str(e))
+
+ # Report success if deleted from either TPM or iLO
+ if deleted_from_tpm or deleted_from_ilo:
+ self.rdmc.ui.printer("Application account has been deleted successfully.\n")
+ return ReturnCodes.SUCCESS
+ else:
+ raise NoAppAccountError(
+ "The application account you are trying to delete does not exist.\n"
+ )
+
+ # Command to check if apptoken exists
+ elif options.command.lower() == "exists":
+ if already_exists:
+ self.rdmc.ui.printer("Application account exists for this host application.\n")
+ return ReturnCodes.SUCCESS
+ else:
+ self.rdmc.ui.printer("Application account does not exist for this hostapp.\n")
+ return ReturnCodes.ACCOUNT_DOES_NOT_EXIST_ERROR
+
+ # Command to list appids and if they are present in iLO and TPM
+ elif options.command.lower() == "details":
+ try:
+ list_of_appids = self.rdmc.app.ListAppIds(app_obj)
+ except Exception:
+ raise AppIdListError("Error occured while retrieving list of App Ids.\n")
+
+ selfdict = list()
+ if "self_register" in options and options.self_register:
+ for app_id in list_of_appids:
+ app_id_value = app_id["ApplicationID"]
+ # Check only for 00b5 identifier for true self-registered iLORest accounts
+ # (00b5 is the reserved ID prefix for iLORest self-registration)
+ if "00b5" in app_id_value.lower():
+ selfdict = [app_id]
+ break
+
+ # If no self-registered app account found, inform user
+ if not selfdict:
+ self.rdmc.ui.printer("No self-registered iLORest app account found.\n")
+ self.rdmc.ui.printer("Use 'appaccount details --hostappid all' to see all app accounts.\n")
+ return ReturnCodes.SUCCESS
+
+ elif options.hostappid:
+ if options.hostappid.lower() == "all":
+ selfdict = list_of_appids
+ if (
+ "onlytoken" in options
+ and options.onlytoken
+ or "onlyaccount" in options
+ and options.onlyaccount
+ ):
+ for i in range(len(list_of_appids)):
+ if "onlytoken" in options and options.onlytoken:
+ del selfdict[i]["ExistsIniLO"]
+ elif "onlyaccount" in options and options.onlyaccount:
+ del selfdict[i]["ExistsInTPM"]
+ else:
+ # Expand short app ID (4 chars) to full ID like master code does
+ target_appid = options.hostappid
+ if len(options.hostappid) == 4:
+ try:
+ target_appid = self.rdmc.app.ExpandAppId(app_obj, options.hostappid)
+ except Exception:
+ # If expansion fails, try matching with last 4 chars
+ pass
+
+ # Handle both full and short (4 char) app IDs
+ for i in range(len(list_of_appids)):
+ full_id = list_of_appids[i]["ApplicationID"]
+ # Match full ID or last 4 chars for short ID
+ if full_id == target_appid or (
+ len(options.hostappid) == 4
+ and full_id[-4:].lower() == options.hostappid.lower()
+ ):
+ selfdict = [list_of_appids[i]]
+ if "onlytoken" in options and options.onlytoken:
+ del selfdict[0]["ExistsIniLO"]
+ elif "onlyaccount" in options and options.onlyaccount:
+ del selfdict[0]["ExistsInTPM"]
+ break
+ if not selfdict:
+ raise AppAccountExistsError(
+ "There is no application account exists for the given hostappid. "
+ "Please recheck the entered value.\n"
+ )
+ else:
+ # No --hostappid provided - list all app accounts (master behavior)
+ selfdict = list_of_appids
+ if (
+ "onlytoken" in options and options.onlytoken
+ or "onlyaccount" in options and options.onlyaccount
+ ):
+ for i in range(len(list_of_appids)):
+ if "onlytoken" in options and options.onlytoken:
+ del selfdict[i]["ExistsIniLO"]
+ elif "onlyaccount" in options and options.onlyaccount:
+ del selfdict[i]["ExistsInTPM"]
+
+ if options.json:
+ tempdict = self.print_json_app_details(selfdict)
+ UI().print_out_json(tempdict)
+ else:
+ self.print_app_details(selfdict)
+
+ return ReturnCodes.SUCCESS
+ elif options.command.lower() == "reactivate":
+ if not already_exists:
+ LOGGER.error("The application account you are trying to reactivate does not exist.")
+ raise NoAppAccountError("The application account you are trying to reactivate does not exist.\n")
+ try:
+ return_code = self.rdmc.app.reactivate_token(app_obj)
+ if return_code == 0:
+ self.rdmc.ui.printer("Application account has been reactivated successfully.\n")
+ return ReturnCodes.SUCCESS
+ except redfish.rest.v1.InvalidCredentialsError:
+ LOGGER.error("Please enter valid credentials.")
+ raise redfish.rest.v1.InvalidCredentialsError(0)
+ except Exception as excp:
+ LOGGER.error("Error occurred while reactivating application account.")
+ raise ReactivateAppAccountTokenError("Error occurred while reactivating application account.\n")
+ else:
+ raise InvalidCommandLineError("The command you have entered is invalid.\n")
+
+ else:
+ raise InvalidCommandLineError("The command you have entered is invalid.\n")
+
+ def appaccountvalidation(self, options):
+ """appaccount validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ return self.rdmc.login_select_validation(self, options)
+
+ def print_json_app_details(self, selfdict):
+ for i in range(len(selfdict)):
+ selfdict[i]["ApplicationID"] = "**" + selfdict[i]["ApplicationID"][-4:]
+ return selfdict
+
+ def print_app_details(self, printdict):
+ final_output = ""
+ for i in range(len(printdict)):
+ final_output += "Application Name: "
+ final_output += printdict[i]["ApplicationName"]
+ final_output += "\n"
+ final_output += "Application Id: **"
+ final_output += printdict[i]["ApplicationID"][-4:]
+ final_output += "\n"
+ if "ExistsInTPM" in printdict[i]:
+ final_output += "App account exists in TPM: "
+ if printdict[i]["ExistsInTPM"]:
+ final_output += "yes\n"
+ else:
+ final_output += "no\n"
+ if "ExistsIniLO" in printdict[i]:
+ final_output += "App account exists in iLO: "
+ if printdict[i]["ExistsIniLO"]:
+ final_output += "yes\n"
+ else:
+ final_output += "no\n"
+ final_output += "\n"
+
+ self.rdmc.ui.printer(final_output)
+
+ def get_ilover_beforelogin(self, app_obj):
+ try:
+ ilo_ver, sec_state = self.rdmc.app.getilover_beforelogin(app_obj)
+ if ilo_ver < 7:
+ raise ChifDriverMissingOrNotFound()
+ except ChifDriverMissingOrNotFound:
+ raise IncompatibleiLOVersionError("This feature is only available for iLO 7 or higher.\n")
+ except VnicNotEnabledError:
+ raise VnicExistsError(
+ "Unable to access iLO using virtual NIC. "
+ "Please ensure virtual NIC is enabled in iLO. "
+ "Ensure that virtual NIC in the host OS is "
+ "configured properly. Refer to documentation for more information.\n"
+ )
+ except redfish.hpilo.vnichpilo.InvalidCommandLineError:
+ raise InvalidCommandLineError(
+ "There is no app account present for the given hostappid." " Please recheck the entered value.\n"
+ )
+ except Exception:
+ raise GenBeforeLoginError(
+ "An error occurred while retrieving the iLO generation. "
+ "Please ensure that the virtual NIC is enabled for iLO7 based "
+ "servers, or that the CHIF driver is installed for iLO5 and iLO6 "
+ "based servers.\n "
+ "Note: appaccount command can only be executed from the host OS of a VNIC-enabled iLO7 server.\n"
+ )
+
+ def definearguments(self, customparser):
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+
+ # Create apptoken command arguments
+ help_text = "To generate and save Application account"
+ create_parser = subcommand_parser.add_parser(
+ "create",
+ help=help_text,
+ description="appaccount create --username temp_user --password "
+ "pasxx --hostappname xxx --hostappid xxx --salt xxx",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+
+ create_parser.add_argument("--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None)
+ create_parser.add_argument(
+ "--hostappname", dest="hostappname", help="Parameter to specify hostappname", default=None
+ )
+ create_parser.add_argument(
+ "--salt", dest="salt", help="Parameter to specify application owned salt", default=None
+ )
+ help_text = "Self tag for customers with no access to host information."
+ create_parser.add_argument("--self", dest="self_register", help=help_text, action="store_true", default=False)
+ self.cmdbase.add_login_arguments_group(create_parser)
+
+ # Delete apptoken command arguments
+ help_text = "To delete Application account"
+ delete_parser = subcommand_parser.add_parser(
+ "delete",
+ help=help_text,
+ description="appaccount delete --hostappname xxx -u user123 -p passxx",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ delete_parser.add_argument("--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None)
+ delete_parser.add_argument(
+ "--hostappname", dest="hostappname", help="Parameter to specify hostappname", default=None
+ )
+ delete_parser.add_argument(
+ "--salt", dest="salt", help="Parameter to specify application owned salt", default=None
+ )
+ help_text = "Self tag for customers with no access to host information."
+ delete_parser.add_argument("--self", dest="self_register", help=help_text, action="store_true", default=False)
+ self.cmdbase.add_login_arguments_group(delete_parser)
+
+ # token exists command arguments
+ help_text = "To check if Application account exists"
+ exists_parser = subcommand_parser.add_parser(
+ "exists",
+ help=help_text,
+ description="appaccount exists --hostappid xxx",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ exists_parser.add_argument("--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None)
+
+ help_text = "Self tag for customers with no access to host information."
+ exists_parser.add_argument("--self", dest="self_register", help=help_text, action="store_true", default=False)
+ self.cmdbase.add_login_arguments_group(exists_parser)
+
+ # Details command arguments
+ help_text = "To list details of app accounts present in TPM and iLO."
+ details_parser = subcommand_parser.add_parser(
+ "details",
+ help=help_text,
+ description="appaccount details --hostappid xxx",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ details_parser.add_argument(
+ "--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None
+ )
+ details_parser.add_argument(
+ "--only_token",
+ dest="onlytoken",
+ help="Parameter provides details of app account in TPM",
+ action="store_true",
+ default=False,
+ )
+ details_parser.add_argument(
+ "--only_account",
+ dest="onlyaccount",
+ help="Parameter provides details of app account in iLO.",
+ action="store_true",
+ default=False,
+ )
+ help_text = "Self tag for customers with no access to host information."
+ details_parser.add_argument("--self", dest="self_register", help=help_text, action="store_true", default=False)
+ details_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(details_parser)
+
+ # Reactivate apptoken command arguments
+ help_text = "To reactivate Application account"
+ reactivate_parser = subcommand_parser.add_parser(
+ "reactivate",
+ help=help_text,
+ description="appaccount reactivate --username temp_user --password pasxx",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+
+ reactivate_parser.add_argument(
+ "--hostappid", dest="hostappid", help="Parameter to specify hostappid", default=None
+ )
+ reactivate_parser.add_argument(
+ "--hostappname", dest="hostappname", help="Parameter to specify hostappname", default=None
+ )
+ reactivate_parser.add_argument(
+ "--salt", dest="salt", help="Parameter to specify application owned salt", default=None
+ )
+ help_text = "Self tag for customers with no access to host information."
+ reactivate_parser.add_argument(
+ "--self", dest="self_register", help=help_text, action="store_true", default=False
+ )
+ self.cmdbase.add_login_arguments_group(reactivate_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/CertificateCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/CertificateCommand.py
new file mode 100644
index 0000000..969268d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/CertificateCommand.py
@@ -0,0 +1,1481 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Certificates Command for rdmc"""
+
+import time
+from argparse import RawDescriptionHelpFormatter
+
+try:
+ from rdmc_helper import (
+ IloLicenseError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ ScepenabledError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IloLicenseError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ ScepenabledError,
+ )
+
+__filename__ = "certificate.txt"
+
+from redfish.ris import IdTokenError
+
+
+class CertificateCommand:
+ """Commands Certificates actions to the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "certificate",
+ "usage": None,
+ "description": "Generate a certificate signing request (CSR) or import an X509 formatted"
+ " TLS or CA certificate.\nImport Scep Certificate.\nInvoke Auto Enroll of certificate generation\n\n"
+ "NOTE: Use quotes to include parameters which contain whitespace when "
+ 'generating a CSR.\nexample: certificate gen_csr "Hewlett Packard Enterprise"'
+ '"iLORest Group" "CName"\n"United States" "Texas" "Houston" "False or True"\n\n'
+ "NOTE: unifiedcertificate command which was augmenting certificate command is merged"
+ " into single command certificate",
+ "summary": "Command for importing both iLO and login authorization "
+ "certificates as well as generating iLO certificate signing requests (CSR)\n",
+ "aliases": ["unifiedcertificate"],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self.view = None
+ self.importcert = None
+ self.exportcert = None
+ self.delete = None
+ self.gencsr = None
+ self.autoenroll = None
+
+ def run(self, line, help_disp=False):
+ """Main Certificates Command function
+
+ :param options: list of options
+ :type options: list.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.certificatesvalidation(options)
+
+ returnCode = None
+
+ if options.command == "csr":
+ self.rdmc.ui.printer(
+ "This command has been deprecated. Please use the following command: "
+ 'certificate gen_csr "Hewlet Packard Enterprice" "ILORestGroup" ...\n'
+ "It performs the same function.\n"
+ "Check --help for more information.\n"
+ )
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return returnCode
+ elif options.command == "ca":
+ self.rdmc.ui.printer(
+ "This command has been deprecated. Please use the following "
+ "command: certificate import --ca_cert \n"
+ "It performs the same function.\n"
+ "Check --help for more information.\n"
+ )
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return returnCode
+ elif options.command == "getcsr":
+ returnCode = self.get_csr_helper(options)
+ elif options.command == "crl":
+ self.rdmc.ui.printer(
+ "This command has been deprecated. Please use the following command: "
+ "certificate import --crl_cert \n"
+ "It performs the same function.\n"
+ "Check --help for more information.\n"
+ )
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return returnCode
+ elif options.command == "tls":
+ self.rdmc.ui.printer(
+ "This command has been deprecated. Please use the following command: "
+ "certificate import --tls_cert \n"
+ "It performs the same function.\n"
+ "Check --help for more information.\n"
+ )
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return returnCode
+ if "view" in options.command.lower():
+ self.view = True
+ self.importcert = False
+ self.delete = False
+ self.gencsr = False
+ self.autoenroll = False
+ self.exportcert = False
+ elif "import" in options.command.lower():
+ self.view = False
+ self.importcert = True
+ self.delete = False
+ self.gencsr = False
+ self.autoenroll = False
+ self.exportcert = False
+ elif "export" in options.command.lower():
+ self.view = False
+ self.importcert = False
+ self.delete = False
+ self.gencsr = False
+ self.autoenroll = False
+ self.exportcert = True
+ elif "delete" in options.command.lower():
+ self.view = False
+ self.importcert = False
+ self.delete = True
+ self.gencsr = False
+ self.autoenroll = False
+ self.exportcert = False
+ elif "gen_csr" in options.command.lower():
+ self.view = False
+ self.importcert = False
+ self.delete = False
+ self.gencsr = True
+ self.autoenroll = False
+ self.exportcert = False
+ elif "auto_enroll" in options.command.lower():
+ self.view = False
+ self.importcert = False
+ self.delete = False
+ self.gencsr = False
+ self.autoenroll = True
+ self.exportcert = False
+
+ if self.view:
+ returnCode = self.viewfunction(options)
+ elif self.importcert:
+ returnCode = self.importfunction(options)
+ elif self.exportcert:
+ returnCode = self.exportfunction(options)
+ elif self.delete:
+ returnCode = self.deletefunction(options)
+ elif self.gencsr:
+ returnCode = self.gencsrfunction(options)
+ elif self.autoenroll:
+ if self.rdmc.app.typepath.defs.isgen10:
+ returnCode = self.autoenrollfunction(options)
+ else:
+ self.rdmc.ui.printer("Gen 9 doesnt support this feature\n")
+ returnCode = ReturnCodes.SUCCESS
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return returnCode
+
+ def autoenrollfunction(self, options):
+ """Automatic Scep cert enrollement process
+
+ :param options: list of options
+ :type options: list.
+ """
+ select = self.rdmc.app.typepath.defs.securityservice
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ for item in bodydict["Links"]:
+ if "AutomaticCertificateEnrollment" in item:
+ path = bodydict["Links"]["AutomaticCertificateEnrollment"]["@odata.id"]
+ break
+ except:
+ path = path + "AutomaticCertificateEnrollment"
+
+ body = {
+ "AutomaticCertificateEnrollmentSettings": {
+ "ServiceEnabled": eval(options.autoenroll_ScepService.strip('"')),
+ "ServerUrl": options.autoenroll_scep_enrollAddress.strip('"'),
+ "ChallengePassword": options.autoenroll_challengepassword.strip('"'),
+ },
+ "HttpsCertCSRSubjectValue": {
+ "OrgName": options.autoenroll_orgname.strip('"'),
+ "OrgUnit": options.autoenroll_orgunit.strip('"'),
+ "CommonName": options.autoenroll_commonname.strip('"'),
+ "Country": options.autoenroll_country.strip('"'),
+ "State": options.autoenroll_state.strip('"'),
+ "City": options.autoenroll_city.strip('"'),
+ "IncludeIP": eval(options.autoenroll_includeIP.strip('"')),
+ },
+ }
+
+ try:
+ results = self.rdmc.app.patch_handler(path, body, silent=True)
+
+ i = 0
+ results1 = self.rdmc.app.get_handler(path, silent=True)
+ while i < 9 and (
+ results1.dict["AutomaticCertificateEnrollmentSettings"]["CertificateEnrollmentStatus"] == "InProgress"
+ ):
+ results1 = self.rdmc.app.get_handler(path, silent=True)
+ time.sleep(1)
+ i = i + 1
+
+ if results.status == 200 and (
+ not (results1.dict["AutomaticCertificateEnrollmentSettings"]["CertificateEnrollmentStatus"] == "Failed")
+ ):
+ self.rdmc.ui.printer("Auto enrolment Enable/Disable operation successful \n")
+ return ReturnCodes.SUCCESS
+ elif results.status == 400:
+ self.rdmc.ui.error(
+ "There was a problem with auto enroll, Please check whether Scep CA cert is imported \t\n"
+ )
+
+ return ReturnCodes.SCEP_ENABLED_ERROR
+ else:
+ self.rdmc.ui.error(
+ "There was a problem with auto enroll, Plese Check the Url/Password whether it is correct\n"
+ )
+ return ReturnCodes.SCEP_ENABLED_ERROR
+ except IloLicenseError:
+ self.rdmc.ui.error("License Error Occured while auto enroll\n")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to auto enroll scep certificate process\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+
+ def gencsrfunction(self, options):
+ """Main Certificates Command function
+
+ :param options: list of options
+ :type options: list.
+ """
+ try:
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select)
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ for item in bodydict["Actions"]:
+ if "GenerateCSR" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "GenerateCSR"
+
+ path = bodydict["Actions"][item]["target"]
+ break
+ except:
+ action = "GenerateCSR"
+ except:
+ path = "redfish/v1/Managers/1/SecurityService/HttpsCert"
+ action = "GenerateCSR"
+
+ body = {
+ "Action": action,
+ "OrgName": options.gencsr_orgname.strip('"'),
+ "OrgUnit": options.gencsr_orgunit.strip('"'),
+ "CommonName": options.gencsr_commonname.strip('"'),
+ "Country": options.gencsr_country.strip('"'),
+ "State": options.gencsr_state.strip('"'),
+ "City": options.gencsr_city.strip('"'),
+ "IncludeIP": eval(options.gencsr_parser_includeIP.strip('"')),
+ }
+
+ try:
+ results = self.rdmc.app.post_handler(path, body)
+ if results.status == 200:
+ return ReturnCodes.SUCCESS
+ except ScepenabledError:
+ self.rdmc.ui.printer("SCEP is enabled , CSR cant be generated \n")
+ return ReturnCodes.SCEP_ENABLED_ERROR
+ except IloLicenseError:
+ self.rdmc.ui.error("License Error Occured while generating CSR")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to generate CSR\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+
+ def deletefunction(self, options):
+ """Certificate Delete Function
+
+ :param options: list of options
+ :type options: list.
+ """
+
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ try:
+ results = self.rdmc.app.delete_handler(path, silent=True)
+ if results.status == 200:
+ self.rdmc.ui.printer("Deleted the https certifcate successfully\n")
+ return ReturnCodes.SUCCESS
+ if results.status == 403:
+ self.rdmc.ui.error("Insufficient Privilege to delete certificate\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ if results.status == 400:
+ self.rdmc.ui.error("SCEP is enabled , Cant delete the certificate\n")
+ return ReturnCodes.SCEP_ENABLED_ERROR
+ except IloLicenseError:
+ self.rdmc.ui.error("License Error Occured while delete")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def viewfunction(self, options):
+ """View scep certifcates or https certificates
+
+ :param options: list of options
+ :type options: list.
+ """
+
+ if options.scep_cert:
+ if self.rdmc.app.typepath.defs.isgen10:
+ self.view_scepcertificate() # View scep certificates
+ else:
+ self.rdmc.ui.printer("Feature not supported on Gen 9\n")
+ return ReturnCodes.SUCCESS
+ if options.https_cert:
+ self.view_httpscertificate() # View https certificates
+
+ def view_scepcertificate(self):
+ """
+ View Scep certificate
+ """
+ select = self.rdmc.app.typepath.defs.securityservice
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ for item in bodydict["Links"]:
+ if "AutomaticCertificateEnrollment" in item:
+ path = bodydict["Links"]["AutomaticCertificateEnrollment"]["@odata.id"]
+ break
+ except:
+ path = path + "AutomaticCertificateEnrollment"
+
+ try:
+ results = self.rdmc.app.get_handler(path, silent=True)
+ if results.status == 200:
+ self.rdmc.ui.printer("Scep Certificate details ...\n")
+ results = results.dict
+ self.print_cert_info(results)
+ return ReturnCodes.SUCCESS
+
+ except IloLicenseError:
+ self.rdmc.ui.error("Error Occured while Uninstall")
+ return ReturnCodes.ILO_LICENSE_ERROR
+
+ def print_cert_info(self, results):
+ """
+ Prints the cert info
+ """
+ for key, value in results.items():
+ if "@odata" not in key:
+ if type(value) is dict:
+ self.print_cert_info(value)
+ else:
+ self.rdmc.ui.printer(key + ":" + str(value) + "\n")
+
+ def view_httpscertificate(self):
+ """
+ View Https certificate
+ """
+ try:
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+ except:
+ path = "redfish/v1/Managers/1/SecurityService/HttpsCert"
+
+ try:
+ results = self.rdmc.app.get_handler(path, silent=True)
+ if results.status == 200:
+ self.rdmc.ui.printer("Https Certificate details ...\n")
+ results = results.dict
+ self.print_cert_info(results)
+ return ReturnCodes.SUCESS
+ except IloLicenseError:
+ self.rdmc.ui.error("Error Occured while Uninstall")
+ return ReturnCodes.ILO_LICENSE_ERROR
+
+ def exportfunction(self, options):
+ result = self.exportplatformhelper(options)
+
+ if result:
+ if options.filename:
+ self.file_handler(next(iter(options.filename)), result, options, "wb")
+ self.rdmc.ui.printer("The certificate was saved to: %s\n" % next(iter(options.filename)))
+ return ReturnCodes.SUCCESS
+ else:
+ self.rdmc.ui.printer("The certificate retrieved is as follows:\n%s\n" % result)
+ return ReturnCodes.SUCCESS
+ else:
+ raise NoContentsFoundForOperationError("An error occurred retrieving the requested certificate.\n")
+
+ def exportplatformhelper(self, options):
+ """Helper function for exporting a platform certificate
+ :param options: list of options
+ :type options: list.
+ """
+
+ str_type = "PlatformCert"
+ ss_instance = next(iter(self.rdmc.app.select("SecurityService." + ".", path_refresh=True)))
+ if options.ldevid_cert:
+ str_type = "iLOLDevID"
+ elif options.idevid_cert:
+ str_type = "iLOIDevID"
+ elif options.systemiak_cert:
+ str_type = "SystemIAK"
+ elif options.systemidevid_cert:
+ str_type = "SystemIDevID"
+ instance_path_uri = (
+ (ss_instance.dict[str_type]["Certificates"][self.rdmc.app.typepath.defs.hrefstring])
+ if ss_instance.dict.get("SystemIAK")
+ else None
+ )
+ instance_data = self.rdmc.app.get_handler(instance_path_uri, silent=True)
+ cert = None
+ if instance_data.dict.get("Members"):
+ cert = self.rdmc.app.get_handler(
+ instance_data.dict["Members"][getattr(options, "id", 0) - 1].get(
+ self.rdmc.app.typepath.defs.hrefstring
+ ),
+ silent=True,
+ ).dict
+ return cert.get("CertificateString")
+ else:
+ raise NoContentsFoundForOperationError(
+ "Unable to find specified certificate at " "position %s." % getattr(options, "id", 0)
+ )
+
+ def file_handler(self, filename, data, options, operation="rb"):
+ """
+ Wrapper function to read or write data to a respective file
+ :param data: data to be written to output file
+ :type data: container (list of dictionaries, dictionary, etc.)
+ :param file: filename to be written
+ :type file: string (generally this should be self.clone_file or tmp_clone_file
+ :param operation: file operation to be performed
+ :type operation: string ('w+', 'a+', 'r+')
+ :param options: command line options
+ :type options: attribute
+ :returns: json file data
+ """
+ writeable_ops = ["wb", "w", "w+", "a", "a+"]
+
+ if operation in writeable_ops:
+ with open(filename, operation) as fh:
+ try:
+ data = data.encode("UTF-8")
+ except IOError:
+ raise InvalidFileInputError("Unable to write to file '%s'" % filename)
+ except UnicodeEncodeError:
+ pass
+ finally:
+ fh.write(data)
+ else:
+ with open(filename, operation) as fh:
+ fdata = fh.read()
+ try:
+ return fdata.decode("UTF-8")
+ except UnicodeDecodeError:
+ return fdata
+ except IOError:
+ raise InvalidFileInputError("Unable to read from file '%s'" % filename)
+
+ def importfunction(self, options):
+ if self.rdmc.app.typepath.defs.isgen10:
+ return self.importfunctionhelper(options)
+ else:
+ self.rdmc.ui.printer("Gen 9 doesnt support this feature\n")
+ return ReturnCodes.SUCCESS
+
+ def importscephelper(self, options):
+ """
+ Import Scep certificate
+ """
+ select = self.rdmc.app.typepath.defs.securityservice
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ for item in bodydict["Links"]:
+ if "AutomaticCertificateEnrollment" in item:
+ path = bodydict["Links"]["AutomaticCertificateEnrollment"]["@odata.id"]
+ break
+ except:
+ path = path + "AutomaticCertificateEnrollment"
+
+ path = path + "Actions/HpeAutomaticCertEnrollment.ImportCACertificate"
+
+ action = "HpeAutomaticCertEnrollment.ImportCACertificate"
+
+ certdata = None
+ scep_CACert = options.certfile
+
+ try:
+ with open(scep_CACert) as certfile:
+ certdata = certfile.read()
+ certfile.close()
+ except:
+ pass
+
+ body = {"Action": action, "Certificate": certdata}
+
+ try:
+ result = self.rdmc.app.post_handler(path, body)
+ if result.status == 200:
+ self.rdmc.ui.printer("Imported the scep certificate successfully\n")
+ return ReturnCodes.SUCCESS
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to import scep CA certificate\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except IloLicenseError:
+ self.rdmc.ui.error("Error Occured while importing scep certificate")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def importfunctionhelper(self, options):
+ result = None
+ if getattr(options, "scep_cert"):
+ result = self.importscephelper(options)
+ elif getattr(options, "ca_cert"):
+ result = self.importcahelper(options)
+ elif getattr(options, "crl_cert"):
+ result = self.importcrlhelper(options)
+ elif getattr(options, "tls_cert"):
+ result = self.importtlshelper(options)
+ elif (
+ getattr(options, "ldevid_cert")
+ or getattr(options, "idevid_cert")
+ or getattr(options, "systemiak_cert")
+ or getattr(options, "systemidevid_cert")
+ or getattr(options, "platform_cert")
+ ):
+ result = self.importplatformhelper(options)
+
+ return result
+
+ def generatecerthelper(self, options):
+ """Main Certificates Command function
+
+ :param options: list of options
+ :type options: list.
+ """
+ self.rdmc.ui.printer(
+ "Warning:Command CSR has been replaced with Gen_csr comamnd and "
+ "CSR command will be deprecated in next release of our tool. Please plan to use gen_csr command instead \n"
+ )
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ for item in bodydict["Actions"]:
+ if "GenerateCSR" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "GenerateCSR"
+
+ path = bodydict["Actions"][item]["target"]
+ break
+ except:
+ action = "GenerateCSR"
+
+ body = {
+ "Action": action,
+ "OrgName": options.csr_orgname.strip('"'),
+ "OrgUnit": options.csr_orgunit.strip('"'),
+ "CommonName": options.csr_commonname.strip('"'),
+ "Country": options.csr_country.strip('"'),
+ "State": options.csr_state.strip('"'),
+ "City": options.csr_city.strip('"'),
+ }
+
+ self.rdmc.ui.printer(
+ "iLO is creating a new certificate signing request. " "This process can take up to 10 minutes.\n"
+ )
+
+ try:
+ self.rdmc.app.post_handler(path, body)
+ return ReturnCodes.SUCCESS
+ except ScepenabledError:
+ self.rdmc.ui.printer("SCEP is enabled , operation not allowed \n")
+ return ReturnCodes.SCEP_ENABLED_ERROR
+
+ def getcerthelper(self, options):
+ """Helper function for importing CRL certificate
+
+ :param options: list of options
+ :type options: list.
+ """
+
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select, path_refresh=True)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ try:
+ csr = results.resp.dict["CertificateSigningRequest"]
+ if not csr:
+ raise ValueError
+ except (KeyError, ValueError):
+ raise NoContentsFoundForOperationError(
+ "Unable to find a valid certificate. If "
+ "you just generated a new certificate "
+ "signing request the process may take "
+ "up to 10 minutes."
+ )
+
+ if not options.filename:
+ filename = __filename__
+ else:
+ filename = options.filename[0]
+
+ outfile = open(filename, "w")
+ outfile.write(csr)
+ outfile.close()
+
+ self.rdmc.ui.printer("Certificate saved to: %s\n" % filename)
+ return ReturnCodes.SUCCESS
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ def get_csr_helper(self, options):
+ result = None
+ if options.TLSCERT:
+ instance = next(iter(self.rdmc.app.select("HttpsCert.", path_refresh=True)))
+ result = instance.dict.get("CertificateSigningRequest")
+ elif options.PLATFORM:
+ tmp = self.gen_csr_helper(options)
+ if tmp:
+ result = tmp.dict.get("CSRString")
+
+ if result:
+ if not options.filename:
+ filename = __filename__
+ else:
+ filename = options.filename[0]
+
+ outfile = open(filename, "w")
+ outfile.write(result)
+ outfile.close()
+ self.rdmc.ui.printer("Certificate saved to: %s\n" % filename)
+ return ReturnCodes.SUCCESS
+ else:
+ self.rdmc.ui.error(
+ "An error occurred retrieving a CSR. Check whether CSR is requested , "
+ "if not kindly run gen_csr command and generate CSR"
+ )
+
+ def gen_csr_helper(self, options):
+ """
+ :param options: list of options
+ :type options: attributes.
+ """
+ body = None
+ path = None
+ action = "GenerateCSR"
+ if options.TLSCERT:
+ instance = next(iter(self.rdmc.app.select("HttpsCert.", path_refresh=True)))
+ body = {
+ "Action": action,
+ "OrgName": options.csr_orgname.strip('"'),
+ "OrgUnit": options.csr_orgunit.strip('"'),
+ "CommonName": options.csr_commonname.strip('"'),
+ "Country": options.csr_country.strip('"'),
+ "State": options.csr_state.strip('"'),
+ "City": options.csr_city.strip('"'),
+ }
+
+ elif options.PLATFORM:
+ # There is seemingly no way to update the Certificate subject data which
+ # appears problematic.
+ _ = next(iter(self.rdmc.app.select("CertificateService.", path_refresh=True)))
+ ss_instance = next(iter(self.rdmc.app.select("SecurityService.", path_refresh=True)))
+ cert_obtain_path = ss_instance.dict.get("iLOLDevID")[next(iter(ss_instance.dict.get("iLOLDevID")))].get(
+ self.rdmc.app.typepath.defs.hrefstring
+ )
+ if not cert_obtain_path:
+ raise NoContentsFoundForOperationError("Unable to find specified certificate path" " for CSR request")
+ instance = next(iter(self.rdmc.app.select("CertificateService.", path_refresh=True)))
+ body = {"CertificateCollection": {self.rdmc.app.typepath.defs.hrefstring: cert_obtain_path}}
+
+ try:
+ for act in instance.dict.get("Actions"):
+ if "GenerateCSR" in act:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = act.split("#")[-1]
+ else:
+ action = "GenerateCSR"
+ path = instance.dict["Actions"][act]["target"]
+ break
+ except:
+ raise NoContentsFoundForOperationError("Unable to find specified certificate action" "path for CSR request")
+
+ self.rdmc.ui.printer(
+ "iLO is creating a new certificate signing request. " "This request may take up to 10 minutes.\n"
+ )
+ return self.rdmc.app.post_handler(path, body)
+
+ def importtlshelper(self, options):
+ """Helper function for importing TLS certificate
+
+ :param options: list of options
+ :type options: list.
+ """
+ tlsfile = options.certfile
+
+ try:
+ with open(tlsfile) as certfile:
+ certdata = certfile.read()
+ certfile.close()
+ except:
+ raise InvalidFileInputError("Error loading the specified file.")
+
+ select = self.rdmc.app.typepath.defs.hphttpscerttype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+ try:
+ for item in bodydict["Actions"]:
+ if "ImportCertificate" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "ImportCertificate"
+ path = bodydict["Actions"][item]["target"]
+ break
+ except:
+ action = "ImportCertificate"
+
+ body = {"Action": action, "Certificate": certdata}
+
+ try:
+ result = self.rdmc.app.post_handler(path, body)
+ if result.status == 200:
+ self.rdmc.ui.printer("Imported the TLS certificate successfully\n")
+ return ReturnCodes.SUCCESS
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to import TLS certificate\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except ScepenabledError:
+ self.rdmc.ui.printer("SCEP is enabled , operation not allowed \n")
+ return ReturnCodes.SCEP_ENABLED_ERROR
+ except IloLicenseError:
+ self.rdmc.ui.error("Error occurred while importing TLS certificate")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def importcrlhelper(self, options):
+ """Helper function for importing CRL certificate
+
+ :param options: list of options
+ :type options: list.
+ """
+ if not self.rdmc.app.typepath.flagiften:
+ raise IncompatibleiLOVersionError("This certificate is not available on this system.")
+
+ select = "HpeCertAuth."
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ bodydict = results.resp.dict
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ for item in bodydict["Actions"]:
+ if "ImportCRL" in item:
+ action = item.split("#")[-1]
+ path = bodydict["Actions"][item]["target"]
+ break
+
+ body = {"Action": action, "ImportUri": options.certfile}
+ # self.rdmc.app.post_handler(path, body)
+ try:
+ result = self.rdmc.app.post_handler(path, body)
+ if result.status == 200:
+ self.rdmc.ui.printer("Imported the CRL certificate successfully\n")
+ return ReturnCodes.SUCCESS
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to import CRL certificate\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except IloLicenseError:
+ self.rdmc.ui.error("Error Occured while importing CRL certificate")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def importcahelper(self, options):
+ """Helper function for importing CA certificate
+
+ :param options: list of options
+ :type options: list.
+ """
+ if not self.rdmc.app.typepath.flagiften:
+ raise IncompatibleiLOVersionError("This certificate is not available on this system.")
+
+ tlsfile = options.certfile
+
+ try:
+ with open(tlsfile) as certfile:
+ certdata = certfile.read()
+ certfile.close()
+ except:
+ raise InvalidFileInputError("Error loading the specified file.")
+
+ select = "HpeCertAuth."
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ bodydict = results.resp.dict
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ for item in bodydict["Actions"]:
+ if "ImportCACertificate" in item:
+ action = item.split("#")[-1]
+ path = bodydict["Actions"][item]["target"]
+ break
+
+ body = {"Action": action, "Certificate": certdata}
+
+ try:
+ result = self.rdmc.app.post_handler(path, body)
+ if result.status == 200:
+ self.rdmc.ui.printer("Imported the CA certificate successfully\n")
+ return ReturnCodes.SUCCESS
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to import CA certificate\n")
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except IloLicenseError:
+ self.rdmc.ui.error("Error occurred while importing CA certificate")
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def importplatformhelper(self, options):
+ """Helper function for importing a platform certificate
+ :param options: options attributes
+ :type options: attributes
+ """
+
+ # ss_instance = next(
+ # iter(self.rdmc.app.select("SecurityService.", path_refresh=True))
+ # )
+ if getattr(options, "ldevid_cert"):
+ instance_path_uri = "/redfish/v1/Managers/1/Diagnostics/Actions/HpeiLODiagnostics.ImportiLOLDevID"
+ else:
+ ss_instance = self.rdmc.app.get_handler("/redfish/v1/Managers/1/Diagnostics/", service=True, silent=True)
+ instance_path_uri = None
+
+ if getattr(options, "ldevid_cert"):
+ type = "iLOLDevID"
+ elif getattr(options, "idevid_cert"):
+ type = "iLOIDevID"
+ elif getattr(options, "systemiak_cert"):
+ type = "SystemIAK"
+ elif getattr(options, "systemidevid_cert"):
+ type = "SystemIDevID"
+ elif getattr(options, "platform_cert"):
+ type = "PlatformCert"
+ else:
+ raise InvalidCommandLineErrorOPTS(
+ "An invalid set of options were selected...verify " " options were selected correctly and re-try."
+ )
+ type_totarget = "#HpeiLODiagnostics.Import" + type
+
+ if not instance_path_uri:
+ instance_path_uri = ss_instance.dict["Actions"][type_totarget]["target"]
+
+ # instance_path_uri = (
+ # (
+ # ss_instance.dict[type]["Certificates"][
+ # self.rdmc.app.typepath.defs.hrefstring
+ # ]
+ # )
+ # if ss_instance.dict.get("SystemIAK")
+ # else None
+ # )
+ if instance_path_uri:
+ certdata = None
+ try:
+ with open(options.certfile) as cf:
+ certdata = cf.read()
+ except:
+ raise InvalidFileInputError("Error loading the specified file.")
+
+ payload = {"Certificate": certdata}
+
+ if certdata:
+ try:
+ result = self.rdmc.app.post_handler(instance_path_uri, payload)
+ if result.status == 200:
+ self.rdmc.ui.printer("Imported the %s certificate successfully\n" % type)
+ return ReturnCodes.SUCCESS
+ except IdTokenError:
+ self.rdmc.ui.printer("Insufficient Privilege to import %s certificate\n" % type)
+ return ReturnCodes.RIS_MISSING_ID_TOKEN
+ except IloLicenseError:
+ self.rdmc.ui.error("Error Occured while importing %s certificate\n" % type)
+ return ReturnCodes.ILO_LICENSE_ERROR
+ except IncompatibleiLOVersionError:
+ self.rdmc.ui.error("iLO FW version on this server doesnt support this operation")
+ return ReturnCodes.INCOMPATIBLE_ILO_VERSION_ERROR
+
+ def certificatesvalidation(self, options):
+ """certificates validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for certificates command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ subcommand_parser = customparser.add_subparsers(dest="command")
+
+ # gen csr sub-parser
+ gen_csr_help = "Please use the below example command to execute the same function."
+ gen_csr_parser = subcommand_parser.add_parser(
+ "csr",
+ help=gen_csr_help,
+ description=gen_csr_help + "\nexample: certificate gen_csr [ORG_NAME] [ORG_UNIT]"
+ " [COMMON_NAME] [COUNTRY] [STATE] [CITY]\n\nNOTE: please make "
+ "certain the order of arguments is correct.",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ gen_csr_parser.add_argument(
+ "csr_orgname",
+ help="Organization name. i.e. Hewlett Packard Enterprise.",
+ metavar="ORGNAME",
+ )
+ gen_csr_parser.add_argument(
+ "csr_orgunit",
+ help="Organization unit. i.e. Intelligent Provisioning.",
+ metavar="ORGUNIT",
+ )
+ gen_csr_parser.add_argument(
+ "csr_commonname",
+ help="Organization common name. i.e. Common Organization Name.",
+ metavar="ORGCNAME",
+ )
+ gen_csr_parser.add_argument(
+ "csr_country",
+ help="Organization country. i.e. United States.",
+ metavar="ORGCOUNTRY",
+ )
+ gen_csr_parser.add_argument("csr_state", help="Organization state. i.e. Texas.", metavar="ORGSTATE")
+ gen_csr_parser.add_argument("csr_city", help="Organization city. i.e. Houston.", metavar="ORGCITY")
+ self.cmdbase.add_login_arguments_group(gen_csr_parser)
+
+ # get csr
+ get_csr_help = (
+ "Retrieve the generated certificate signing request (CSR) printed to the " "console or to a json file."
+ )
+ get_csr_parser = subcommand_parser.add_parser(
+ "getcsr",
+ help=get_csr_help,
+ description=get_csr_help
+ + "\nexample: certificate getcsr\nexample: certificate getcsr --TLS_CERT/--PLATFORM_CERT"
+ "-f mycsrfile.json",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ get_csr_parser.add_argument(
+ "--TLS_CERT",
+ dest="TLSCERT",
+ help="specify to retrieve a TLS/SSL certificate signing request.",
+ action="store_true",
+ default=None,
+ )
+ get_csr_parser.add_argument(
+ "--PLATFORM_CERT",
+ dest="PLATFORM",
+ help="specify to retrieve a platform certificate signing request.",
+ action="store_true",
+ default=None,
+ )
+ get_csr_parser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag if you wish to use a different"
+ " filename for the certificate signing request. The default"
+ " filename is %s." % __filename__,
+ action="append",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(get_csr_parser)
+
+ # ca certificate
+ ca_help = "This command has been deprecated. Please use the below example command to execute the same function."
+ ca_parser = subcommand_parser.add_parser(
+ "ca",
+ help=ca_help,
+ description=ca_help + "\nexample: certificate import --ca_cert mycertfile.txt\nNote: The "
+ "certificate must be in X.509 format",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ ca_parser.add_argument("certfile", help="X.509 formatted CA certificate", metavar="CACERTFILE")
+ self.cmdbase.add_login_arguments_group(ca_parser)
+
+ # crl certificate
+ crl_help = (
+ "This command has been deprecated. Please use the below example command to execute the same function."
+ )
+ crl_parser = subcommand_parser.add_parser(
+ "crl",
+ help=crl_help,
+ description=crl_help + "\nexample: certificate import --crl_cert https://mycertfileurl/mycertfile.txt"
+ "\nNote: The certificate must be in X.509 format",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ crl_parser.add_argument(
+ "certfile_url",
+ help="URL pointing to the location of the X.509 CA certificate",
+ metavar="CERTFILEURL",
+ )
+ self.cmdbase.add_login_arguments_group(crl_parser)
+
+ # tls certificate
+ tls_help = (
+ "This command has been deprecated. Please use the below example command to execute the same function."
+ )
+ tls_parser = subcommand_parser.add_parser(
+ "tls",
+ help=tls_help,
+ description=tls_help + "\nexample: certificate import --tls_cert mycertfile.txt\nNote: The "
+ "certificate must be in TLS X.509 format",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ tls_parser.add_argument("certfile", help="X.509 formatted TLS certificate", metavar="TLSCERTFILE")
+ self.cmdbase.add_login_arguments_group(tls_parser)
+
+ # view certificate
+ view_help = "View Certificates (https or scep)"
+ view_parser = subcommand_parser.add_parser(
+ "view",
+ help=view_help,
+ description=view_help
+ + "\nexample: certificate view --https_cert \n or \n certificate view --scep_cert \n"
+ + "Webserver certificate whether self-signed or manually imported or issued by SCEP server can be viewed",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ view_parser.add_argument(
+ "--scep_cert",
+ dest="scep_cert",
+ help="Gets the information of SCEP settings for iLO such as SCEP enable status, URL of the SCEP server,"
+ + " ChallengePassword, SCEP CA certificate name, webserver CSR subject contents, SCEP enrollment status",
+ action="store_true",
+ )
+ view_parser.add_argument(
+ "--https_cert",
+ dest="https_cert",
+ help="Gets the https certificate whether self-signed or manually imported or issued by SCEP server",
+ action="store_true",
+ )
+ self.cmdbase.add_login_arguments_group(view_parser)
+
+ # import certificate
+ import_help = "Imports the Certificates."
+ import_parser = subcommand_parser.add_parser(
+ "import",
+ help=import_help,
+ description=import_help
+ + "\nexample: certificate import --scep_cert certificate.txt \n"
+ + " make sure you are providing a .txt file input.\n",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ import_parser.add_argument(
+ "--scep_cert",
+ dest="scep_cert",
+ help="Gets the https certificate whether self-signed or manually imported or issued by SCEP server",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--ca_cert",
+ dest="ca_cert",
+ help="Upload a X.509 formatted CA certificate to iLO.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--crl_cert",
+ dest="crl_cert",
+ help="Provide iLO with a URL to retrieve the X.509 formatted CA certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--tls_cert",
+ dest="tls_cert",
+ help="Upload a X.509 TLS certificate to iLO.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--idevid_cert",
+ dest="idevid_cert",
+ help="Upload an IDEVID certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--ldevid_cert",
+ dest="ldevid_cert",
+ help="Upload an LDEVID certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--systemiak_cert",
+ dest="systemiak_cert",
+ help="Upload an System IAK certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--systemidevid_cert",
+ dest="systemidevid_cert",
+ help="Upload an system IDEVID certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--platform_cert",
+ dest="platform_cert",
+ help="Upload a platform certificate.",
+ action="store_true",
+ )
+ import_parser.add_argument(
+ "--from_url",
+ dest="from_url",
+ help="Use this flag to specify a URL for certificate import",
+ action="append",
+ default=None,
+ )
+ import_parser.add_argument(
+ "certfile",
+ help="Certificate can be imported via POST action",
+ metavar="certfile",
+ )
+ self.cmdbase.add_login_arguments_group(import_parser)
+
+ # export cert
+ export_help = "Pull an X.509 formatted platform certificate from iLO."
+ export_parser = subcommand_parser.add_parser(
+ "export",
+ help=export_help,
+ description=export_help + "\nExample: certificate export --IDEVID -f " "myidevidfile\n",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+
+ export_parser.add_argument(
+ "--idevid_cert",
+ dest="idevid_cert",
+ help="Specify for an IDEVID certificate. ",
+ action="store_true",
+ default=None,
+ )
+ export_parser.add_argument(
+ "--ldevid_cert",
+ dest="ldevid_cert",
+ help="Specify for an LDEVID certificate. ",
+ action="store_true",
+ default=None,
+ )
+ export_parser.add_argument(
+ "--systemiak_cert",
+ dest="systemiak_cert",
+ help="Specify for a system IAK certificate.",
+ action="store_true",
+ default=None,
+ )
+ export_parser.add_argument(
+ "--systemidevid_cert",
+ dest="systemidevid_cert",
+ help="Specify for a system IDEVID certificate.",
+ action="store_true",
+ default=None,
+ )
+ export_parser.add_argument(
+ "--platform_cert",
+ dest="platform_cert",
+ help="Specify for a platform certificate.",
+ action="store_true",
+ default=None,
+ )
+ export_parser.add_argument(
+ "--id",
+ dest="id",
+ help="Optionally specify the certificate instance, if multiples are available. If"
+ "the instance specified is not available, then the next is retrieved. Default is"
+ "the first instance",
+ default=1,
+ )
+ export_parser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag to import a certificate from or export a certificate to a file.",
+ action="append",
+ default=None,
+ )
+
+ self.cmdbase.add_login_arguments_group(export_parser)
+
+ # delete certificate
+ delete_help = "Deletes the https Certificate"
+ delete_parser = subcommand_parser.add_parser(
+ "delete",
+ help=delete_help,
+ description=delete_help + "\nexample: certificate delete \n delete the https_cert certificate ",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+
+ self.cmdbase.add_login_arguments_group(delete_parser)
+
+ # gen csr sub-parser
+ gencsr_help = (
+ "Generate a certificate signing request (CSR) for iLO SSL certificate "
+ "authentication.\nNote: iLO will create a Base64 encoded CSR in PKCS "
+ "#10 Format."
+ )
+ gencsr_parser = subcommand_parser.add_parser(
+ "gen_csr",
+ help=gencsr_help,
+ description=gen_csr_help + "\nexample: certificate gen_csr [ORG_NAME] [ORG_UNIT]"
+ " [COMMON_NAME] [COUNTRY] [STATE] [CITY] [INCLUDEIP] \n\nNOTE: please make "
+ "certain the order of arguments is correct.",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ gencsr_parser.add_argument(
+ "gencsr_orgname",
+ help="Organization name. i.e. Hewlett Packard Enterprise.",
+ metavar="ORGNAME",
+ )
+ gencsr_parser.add_argument(
+ "gencsr_orgunit",
+ help="Organization unit. i.e. Intelligent Provisioning.",
+ metavar="ORGUNIT",
+ )
+ gencsr_parser.add_argument(
+ "gencsr_commonname",
+ help="Organization common name. i.e. Common Organization Name.",
+ metavar="ORGCNAME",
+ )
+ gencsr_parser.add_argument(
+ "gencsr_country",
+ help="Organization country. i.e. United States.",
+ metavar="ORGCOUNTRY",
+ )
+ gencsr_parser.add_argument("gencsr_state", help="Organization state. i.e. Texas.", metavar="ORGSTATE")
+ gencsr_parser.add_argument("gencsr_city", help="Organization city. i.e. Houston.", metavar="ORGCITY")
+ gencsr_parser.add_argument(
+ "gencsr_parser_includeIP",
+ help="Include IP. i.e. True or False.",
+ metavar="INCLUDEIP",
+ )
+ self.cmdbase.add_login_arguments_group(gencsr_parser)
+
+ # automatic enrollment sub-parser
+ autoenroll_help = (
+ "Use this command for invoking the auto enroll the certificate enrollment process"
+ "\nMake sure you have imported the scep CA certificate "
+ "before head using certificate import --scep certifcate.txt"
+ )
+ autoenroll_parser = subcommand_parser.add_parser(
+ "auto_enroll",
+ help=autoenroll_help,
+ description=autoenroll_help + "\nexample: certificate auto_enroll [ORG_NAME] [ORG_UNIT]"
+ " [COMMON_NAME] [COUNTRY] [STATE] [CITY] [SCEP_ADDRESS] [CHALLENGEPASSWORD] "
+ "[SERVICEENABLED] [INCLUDEIP]]\n\nNOTE: please make "
+ "certain the order of arguments is correct.",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_orgname",
+ help="Organization name. i.e. Hewlett Packard Enterprise.",
+ metavar="ORGNAME",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_orgunit",
+ help="Organization unit. i.e. Intelligent Provisioning.",
+ metavar="ORGUNIT",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_commonname",
+ help="Organization common name. i.e. Common Organization Name.",
+ metavar="ORGCNAME",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_country",
+ help="Organization country. i.e. United States.",
+ metavar="ORGCOUNTRY",
+ )
+ autoenroll_parser.add_argument("autoenroll_state", help="Organization state. i.e. Texas.", metavar="ORGSTATE")
+ autoenroll_parser.add_argument("autoenroll_city", help="Organization city. i.e. Houston.", metavar="ORGCITY")
+ autoenroll_parser.add_argument(
+ "autoenroll_scep_enrollAddress",
+ help="Scep-enroll dll address",
+ metavar="AEADDRESS",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_challengepassword",
+ help="challenge password",
+ metavar="AECHALLPASS",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_ScepService",
+ help="Scep service enable or disable",
+ metavar="AESCEPSERVICE",
+ )
+ autoenroll_parser.add_argument(
+ "autoenroll_includeIP",
+ help="Include IP. i.e. True or False.",
+ metavar="INCLUDEIP",
+ )
+ self.cmdbase.add_login_arguments_group(autoenroll_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ChannelInterfaceCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ChannelInterfaceCommand.py
new file mode 100644
index 0000000..7014f53
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ChannelInterfaceCommand.py
@@ -0,0 +1,130 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""iLO CHIF Reset Command for rdmc"""
+import sys
+from argparse import RawDescriptionHelpFormatter
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ )
+
+
+class ChannelInterfaceCommand:
+ """Reset iLO on the server that is currently logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "channelinterface",
+ "usage": None,
+ "description": "Reset CHIF on the current logged in" " server.\n\tExample: channelinterface reset",
+ "summary": "Reset CHIF on the current logged in server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main iLO reset worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.iloresetvalidation(options)
+ if "blobstore" not in self.rdmc.app.current_client.base_url:
+ raise InvalidCommandLineError("CHIF reset is not supported in remote mode.\n")
+
+ if line[0].lower() == "reset":
+
+ ret = self.chif_reset()
+ if ret:
+ sys.stdout.write("Successfully reset the CHIF interface.\n")
+ return ReturnCodes.SUCCESS
+ else:
+ sys.stdout.write("Failed to reset the CHIF interface.\n")
+ return ReturnCodes.RIS_ILO_CHIF_PACKET_EXCHANGE_ERROR
+ else:
+ raise InvalidCommandLineError("Invalid parameter, please provide channelinterface reset command.\n")
+
+ def chif_reset(self):
+ """Function for handling chif packet exchange
+
+ :param data: data to be sent for packet exchange
+ :type data: str
+
+ """
+ try:
+ libhandle = self.rdmc.app.current_client.connection._conn.channel.dll
+ libhandle.ChifTerminate()
+ libhandle.ChifInitialize(None)
+ return True
+ except:
+ return False
+
+ def iloresetvalidation(self, options):
+ """reboot method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+
+ reset_help = "To disconnect from ComputeOpsManagement\n"
+ reset_parser = subcommand_parser.add_parser(
+ "reset",
+ help=reset_help,
+ description=reset_help + "\n\tExample:\n\tchannelinterface reset",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ self.cmdbase.add_login_arguments_group(reset_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ClearRestApiStateCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ClearRestApiStateCommand.py
new file mode 100644
index 0000000..33cd997
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ClearRestApiStateCommand.py
@@ -0,0 +1,130 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Clear Rest API State Command for rdmc"""
+
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class ClearRestApiStateCommand:
+ """Clear the rest api state of the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "clearrestapistate",
+ "usage": None,
+ "description": "Clears the persistent rest api state.\n\texample: "
+ "clearrestapistate\n\n\tNote: Some types such as Bios, "
+ "Iscsi, and SmartStorageConfig will be unavailable until "
+ "a system reboot occurs after running this command.",
+ "summary": "Clears the persistent state of the REST API. Some "
+ "portions of the API may not be available until after the server reboots.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main clearrestapistate function.
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("clearrestapistate command takes no arguments.")
+
+ self.clearrestapistatevalidation(options)
+
+ select = "Manager."
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Manager. not found.")
+
+ bodydict = results.resp.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ try:
+ for item in bodydict["Actions"]:
+ if "ClearRestApiState" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "ClearRestApiState"
+ path = bodydict["Actions"][item]["target"]
+ body = {"Action": action}
+ break
+ except:
+ body = {"Action": "ClearRestApiState", "Target": "/Oem/Hp"}
+ self.rdmc.app.post_handler(path, body)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def clearrestapistatevalidation(self, options):
+ """clearrestapistate method validation function.
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ComputeOpsManagementCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ComputeOpsManagementCommand.py
new file mode 100644
index 0000000..c9d53ad
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ComputeOpsManagementCommand.py
@@ -0,0 +1,3092 @@
+###
+# Copyright 2021-2025 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+# -*- coding: utf-8 -*-
+"""computeopsmanagement Command for rdmc"""
+
+
+import json
+import time
+import os
+from argparse import RawDescriptionHelpFormatter
+from datetime import datetime
+from ipaddress import ip_address
+from typing import Dict, Any
+
+import requests
+import urllib3
+from requests.exceptions import ConnectionError, Timeout, RequestException
+from urllib3.util.retry import Retry
+
+
+try:
+ from rdmc_helper import (
+ CloudConnectFailedError,
+ CloudConnectTimeoutError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoCurrentSessionEstablished,
+ ProxyConfigFailedError,
+ ReturnCodes,
+ LOGGER,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ CloudConnectFailedError,
+ CloudConnectTimeoutError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoCurrentSessionEstablished,
+ ProxyConfigFailedError,
+ ReturnCodes,
+ LOGGER,
+ )
+
+from redfish.ris.ris import SessionExpired
+from .data.InputTemplate import template
+
+ErrorMapping = {
+ "ProxySettingsInvalid_RDA": "Could not connect to HPE using the provided web proxy. "
+ "Ensure that the proxy details are correct. Additionally,"
+ " you can set proxy using the --proxy parameter in the computeopsmanagement command.\n",
+ "ProxyOrFirewallError_RDA": "The proxy or firewall in not configured properly."
+ " Please check the respective values. Additionally, "
+ "you can set proxy using the --proxy parameter in the computeopsmanagement command.\n",
+ "iLOTimeError_RDA": "Could not obtain an identity for this server "
+ "to connect to Compute Ops Management due to a "
+ "incorrect iLO system time. Please update time on RBSU.\n",
+ "iLOTimeError_COM": "Could not verify the identity of Compute Ops Management. "
+ "This could be due to an incorrect iLO system time. "
+ "Contact HPE support if the problem persists.\n",
+ "ProxyOrFirewallError_COM": "Could not connect to Compute Ops Management due to proxy issue.\n",
+ "ProxySettingsInvalid_COM": "Could not connect to HPE using the "
+ "provided web proxy. Ensure that the proxy details are correct.\n",
+ "ActivationKeyRequired": "An activation key is required in order "
+ "to connect to HPE Compute Ops Management. Please enter an activation key.\n",
+ "InvalidActivationKey": "The activation key entered is invalid. "
+ "Please check the value entered. If the problem persists, contact HPE Support.\n",
+ "ExpiredActivationKey": "The activation key entered is expired. "
+ "Please enter a valid activation key. "
+ "If the problem persists, contact HPE Support.\n ",
+ "WrongiLOVersion": (
+ "Unsupported iLO version. Upgrade to the latest iLO version " "to use HPE Compute Ops Management.\n"
+ ),
+ "DeviceAssignFailed": "Device assignment has failed. Visit HPE GreenLake, select Device tab and ensure"
+ " this device is not already added to another Compute Ops Management instance.\n",
+ "DeviceClaimUnauthorized": "Device claim is unauthorized. Contact your HPE GreenLake administrator to "
+ "verify that you have the right permissions to add "
+ "a device to HPE GreenLake device inventory.\n",
+ "DeviceNotFound": "Device not found. Contact HPE Support to resolve this issue.\n",
+ "InternalError_RDA": "Unknown error. Reset iLO and re-try connecting."
+ " Contact HPE support if the problem persists.\n",
+ "InternalError_COM": "Unknown error. Reset iLO and re-try connecting. "
+ "Contact HPE support if the problem persists.\n",
+ "ExternalError_RDA": "External error. Retry after some time. Contact HPE support if the problem persists.\n",
+ "ExternalError_COM": "External error. Retry after some time. Contact HPE support if the problem persists.\n",
+ "DisabledByCOM": "Disabled by Compute Ops Management.\n",
+}
+
+Cloudconnectstatus = {
+ # Network Status Config
+ "Initializing": "Cloud connection setup is in progress. Please wait a moment.",
+ "IPAddressNotConfigured": "iLO doesn't have an IP address configured. Please check network settings.",
+ "DNSResolutionError": "Unable to resolve DNS. Please verify your DNS configuration.",
+ "Configured": "Configured.",
+ "NotTested": "Not tested yet.",
+ "InternalError": "iLO ran into an internal issue while trying "
+ "to connect to the cloud. Please try again or contact support.",
+ # Web Connectivity
+ "iLOTimeError": "The iLO time settings are incorrect. Please synchronize time to proceed.",
+ "ProxyOrFirewallError": "iLO couldn't reach the endpoint. Check your proxy or firewall settings.",
+ "Connected": "Connected.",
+ "ProxySettingsInvalid": "Proxy settings appear to be invalid. Please review and update them.",
+ "ExternalError": "There was an issue with the external cloud service. Please try again later.",
+ # iLO Configuration for Cloud Connect
+ "ActivationKeyRequired": "An activation key is needed to enable cloud connectivity. Please provide a valid key.",
+ "WrongiLOVersion": "This iLO version doesn't support the requested operation. Please update iLO firmware.",
+ "InvalidActivationKey": "The activation key provided is invalid. Please check and enter a valid one.",
+ "DeviceAssignFailed": "iLO was unable to register the device with the cloud. Please try again later.",
+ "DeviceClaimUnauthorized": "Device claim failed due to insufficient authorization. Please check credentials.",
+ "DeviceNotFound": "The device couldn't be found in the cloud service. Ensure the activation key is correct.",
+ "DisabledByCOM": "Cloud connectivity has been disabled by COM settings.",
+}
+
+DEFAULT_TEMPLATE_FILE_NAME = "multiconnect_input_template.json"
+DNS_SERVER_EXCLUSION_LIST = ["0.0.0.0", "::"]
+
+
+class ComputeOpsManagementCommand:
+ """Main new command template class"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "computeopsmanagement",
+ "usage": "computeopsmanagement\n\n",
+ "description": (
+ "Run to enable your servers to be discovered, monitored and managed through ComputeOpsManagement\n\t"
+ "Example:\n\t"
+ "computeopsmanagement connect\n\t"
+ "computeopsmanagement connect --activationkey \n\t"
+ "computeopsmanagement connect --activationkey --proxy http://proxy.abc.com:8080\n\t"
+ "computeopsmanagement disconnect\n\t"
+ "computeopsmanagement multiconnect --input_file_json_template\n\t"
+ "computeopsmanagement multiconnect --input_file servers.json\n\t"
+ "computeopsmanagement multiconnect --input_file servers.json --allow_ilo_reset\n\t"
+ "computeopsmanagement multiconnect --input_file servers.json --output report.json\n\t"
+ "computeopsmanagement multiconnect --input_file servers.json --output report.json --allow_ilo_reset\n\t"
+ "computeopsmanagement status\n\t"
+ "computeopsmanagement status -j\n\n"
+ ),
+ "summary": (
+ "Enables the server to be discovered, monitored and managed "
+ "through ComputeOpsManagement, Also gives bulk onboarding of iLOs to COM."
+ ),
+ "aliases": [],
+ "auxcommands": ["EthernetCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def resolve_ilo_config(self, config_json_path: str) -> Dict[str, Dict[str, Any]]:
+ """Load config from a JSON file and resolve iLO configuration as a dict keyed by IP."""
+ with open(config_json_path, "r") as f:
+ config_json = json.load(f)
+
+ common = config_json.get("commonSettings", {})
+ common_dns = common.get("network", {}).get("dns", [])
+ common_ntp = common.get("network", {}).get("ntp", [])
+ common_proxy = common.get("proxy", {})
+ common_auth = {
+ "username": common.get("iloAuthentication", {}).get("iloUser"),
+ "password": common.get("iloAuthentication", {}).get("iloPassword"),
+ }
+
+ resolved = {}
+
+ def build_config(
+ ip_str: str,
+ entry: Dict[str, Any],
+ default_dns,
+ default_ntp,
+ skip_dns=False,
+ skip_ntp=False,
+ skip_proxy=False,
+ ):
+ network = entry.get("network", {})
+ dns = [] if skip_dns else network.get("dns", default_dns)
+ ntp = [] if skip_ntp else network.get("ntp", default_ntp)
+ return {
+ "ip": ip_str,
+ "dns": dns,
+ "ntp": ntp,
+ "skipDns": skip_dns,
+ "skipNtp": skip_ntp,
+ "skipProxy": skip_proxy, # Consistent naming without underscore
+ "proxy": common_proxy,
+ "auth": common_auth,
+ }
+
+ individual_entries = config_json.get("targets", {}).get("ilos", {}).get("individual", [])
+ for entry in individual_entries:
+ ip = entry["ip"]
+ skip_dns = entry.get("skipDns", False)
+ skip_ntp = entry.get("skipNtp", False)
+ skip_proxy = entry.get("skipProxy", False) # Read from input as "skipProxy" (user-facing)
+ config = build_config(ip, entry, common_dns, common_ntp, skip_dns, skip_ntp, skip_proxy)
+ resolved[ip] = config
+
+ range_entries = config_json.get("targets", {}).get("ilos", {}).get("ranges", [])
+ for range_entry in range_entries:
+ start_ip = ip_address(range_entry["start"])
+ end_ip = ip_address(range_entry["end"])
+ skip_dns = range_entry.get("skipDns", False)
+ skip_ntp = range_entry.get("skipNtp", False)
+ skip_proxy = range_entry.get("skipProxy", False) # Read from input as "skipProxy" (user-facing)
+ network = range_entry.get("network", {})
+ range_dns = [] if skip_dns else network.get("dns", common_dns)
+ range_ntp = [] if skip_ntp else network.get("ntp", common_ntp)
+
+ # Optimize: Convert to int once, iterate directly, convert back to string once per IP
+ start_int = int(start_ip)
+ end_int = int(end_ip)
+ for ip_int in range(start_int, end_int + 1):
+ ip_str = str(ip_address(ip_int))
+ config = build_config(ip_str, {}, range_dns, range_ntp, skip_dns, skip_ntp, skip_proxy)
+ resolved[ip_str] = config
+
+ return resolved
+
+ def _get_progress_bar_implementation(self):
+ """Get tqdm progress bar implementation or fallback to simple progress bar
+
+ :returns: tqdm function or SimplePbar fallback
+ :rtype: function
+ """
+ # Try to import tqdm, fall back to simple progress if not available
+ try:
+ from tqdm import tqdm
+ return tqdm
+ except ImportError:
+ # Create a simple progress bar fallback that displays to stdout
+ class SimplePbar:
+ def __init__(self, total, desc="", unit=""):
+ self.total = total
+ self.desc = desc
+ self.unit = unit
+ self.current = 0
+ # Display initial progress bar to stdout
+ initial_bar = self._get_progress_bar()
+ print(f"{desc}: 0/{total} {initial_bar}", end="", flush=True)
+
+ def _get_progress_bar(self, width=40):
+ """Generate visual progress bar with # symbols"""
+ progress_pct = (self.current / self.total * 100) if self.total > 0 else 0
+ filled_length = int(width * self.current // self.total) if self.total > 0 else 0
+ bar = "#" * filled_length + "-" * (width - filled_length)
+ return f"[{bar}] {progress_pct:.1f}%"
+
+ def set_description(self, desc):
+ self.desc = desc
+
+ def set_postfix(self, **kwargs):
+ postfix_str = ", ".join([f"{k}={v}" for k, v in kwargs.items()])
+ # Update stdout display with visual progress bar
+ progress_bar = self._get_progress_bar()
+ status_info = f" [{postfix_str}]" if postfix_str else ""
+ print(
+ f"\r{self.desc}: {self.current}/{self.total} " f"{progress_bar}{status_info}",
+ end="",
+ flush=True,
+ )
+
+ def update(self, n):
+ self.current += n
+ # Display visual progress bar
+ progress_bar = self._get_progress_bar()
+ print(f"\r{self.desc}: {self.current}/{self.total} {progress_bar}", end="", flush=True)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ print() # New line after progress bar completion
+
+ def tqdm(total, desc="", unit="", file=None):
+ return SimplePbar(total, desc, unit)
+
+ return tqdm
+
+ def proxy_config(self, proxy_server):
+ """Main cloudconnect worker function
+
+ :param proxy_server: proxy
+ :type proxy_server: str.
+ """
+ if proxy_server != "None":
+ try:
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ body["Oem"]["Hpe"]["WebProxyConfiguration"] = {}
+ proxy_body = body["Oem"]["Hpe"]["WebProxyConfiguration"]
+ proxy_body["ProxyServer"] = None
+ proxy_body["ProxyUserName"] = None
+ proxy_body["ProxyPassword"] = None
+ if "https" in proxy_server:
+ proxy_body["ProxyPort"] = 443
+ else:
+ proxy_body["ProxyPort"] = 80
+ if "@" in proxy_server:
+ proxy = proxy_server.split("@")
+ proxy_usr_pass = proxy[0]
+ proxy_srv_port = proxy[1]
+ if "//" in proxy_usr_pass:
+ proxy_usr_pass = proxy_usr_pass.split("//")[1]
+ if ":" in proxy_srv_port:
+ proxy = proxy_srv_port.split(":")
+ proxy_body["ProxyServer"] = proxy[0]
+ proxy_body["ProxyPort"] = int(proxy[1])
+ else:
+ proxy_body["ProxyServer"] = proxy_srv_port
+ if ":" in proxy_usr_pass:
+ proxy = proxy_usr_pass.split(":")
+ proxy_body["ProxyPassword"] = proxy[1]
+ proxy_body["ProxyUserName"] = proxy[0]
+ else:
+ proxy_body["ProxyUserName"] = proxy_usr_pass
+ else:
+ proxy_srv_port = proxy_server
+ if "//" in proxy_srv_port:
+ proxy_srv_port = proxy_srv_port.split("//")[1]
+ if ":" in proxy_srv_port:
+ proxy = proxy_srv_port.split(":")
+ proxy_body["ProxyServer"] = proxy[0]
+ proxy_body["ProxyPort"] = int(proxy[1])
+ else:
+ proxy_body["ProxyServer"] = proxy_srv_port
+
+ path = self.rdmc.app.getidbytype("NetworkProtocol.")
+
+ if path and body:
+ self.rdmc.ui.printer("Setting Proxy configuration...\n", verbose_override=True)
+ self.rdmc.app.patch_handler(path[0], body, service=False, silent=True)
+ except Exception as e:
+ raise ProxyConfigFailedError(f"Setting Proxy Server Configuration Failed: {str(e)}\n")
+ else:
+ try:
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ body["Oem"]["Hpe"]["WebProxyConfiguration"] = {}
+ proxy_body = body["Oem"]["Hpe"]["WebProxyConfiguration"]
+ proxy_body["ProxyServer"] = ""
+ proxy_body["ProxyPort"] = None
+ proxy_body["ProxyUserName"] = ""
+ proxy_body["ProxyPassword"] = None
+ path = self.rdmc.app.getidbytype("NetworkProtocol.")
+
+ if path and body:
+ self.rdmc.ui.printer("Clearing Proxy configuration...\n", verbose_override=True)
+ self.rdmc.app.patch_handler(path[0], body, service=False, silent=True)
+ except Exception as e:
+ raise ProxyConfigFailedError(f"Clearing Proxy Server Configuration Failed: {str(e)}\n")
+
+ def get_cloud_status(self, need_reason=None):
+ path = self.rdmc.app.typepath.defs.managerpath
+ resp = self.rdmc.app.get_handler(path, service=False, silent=True)
+ if resp.status != 200:
+ raise SessionExpired("Invalid session. Please logout and log back in or include credentials.")
+ status = resp.dict["Oem"]["Hpe"]["CloudConnect"]["CloudConnectStatus"]
+ if need_reason:
+ reason = resp.dict["Oem"]["Hpe"]["CloudConnect"].get("FailReason", "")
+ return status, reason
+ return status
+
+ def connect_cloud(self, activationkey=None):
+ """cloud connect function
+
+ :param activationkey: activation key
+ :type activationkey: str.
+ """
+ status = self.get_cloud_status()
+ if status == "Connected":
+ self.rdmc.ui.printer("Warning: ComputeOpsManagement is already connected.\n")
+ return ReturnCodes.SUCCESS
+ body = dict()
+
+ if activationkey:
+ body["ActivationKey"] = activationkey
+ else:
+ body = {}
+ path = self.rdmc.app.typepath.defs.managerpath + "Actions" + self.rdmc.app.typepath.defs.oempath
+ path = path + "/HpeiLO.EnableCloudConnect"
+ try:
+ if path:
+ self.rdmc.ui.printer("Connecting to ComputeOpsManagement...", verbose_override=True)
+ self.rdmc.app.post_handler(path, body, service=False, silent=True)
+ except Exception as e:
+ raise CloudConnectFailedError(f"ComputeOpsManagement connection Failed: {str(e)}\n")
+ start_time = time.time()
+ allowed_seconds = 120
+ time_increment = 5
+ i = 1
+ while True:
+ # time.sleep(time_increment * i)
+ time.sleep(time_increment)
+ current_time = time.time()
+ elapsed_time = current_time - start_time
+ if elapsed_time > allowed_seconds:
+ self.rdmc.ui.printer("\n")
+ status, reason = self.get_cloud_status(need_reason=True)
+ if status == "NotEnabled":
+ raise CloudConnectFailedError(ErrorMapping[reason])
+ raise CloudConnectTimeoutError(
+ "ComputeOpsManagement connection timed out, Please check the "
+ "activation key and network or proxy settings and try again.\n"
+ )
+ else:
+ status, reason = self.get_cloud_status(need_reason=True)
+ # self.rdmc.ui.printer("ComputeOpsManagement connection status is %s.\n" % status)
+ if status == "Connected":
+ # Check again after 10 seconds before breaking the loop
+ time.sleep(10)
+ status, reason = self.get_cloud_status(need_reason=True)
+ if status == "Connected":
+ self.rdmc.ui.printer("\n")
+ self.rdmc.ui.printer("ComputeOpsManagement connection is successful.\n")
+ break
+ # If connection has failed while checking waiting for success message
+ if status == "ConnectionFailed" or status == "NotConnected":
+ self.rdmc.ui.printer("\n")
+ if reason:
+ raise CloudConnectFailedError(ErrorMapping[reason])
+ raise CloudConnectFailedError(
+ "ComputeOpsManagement connection Failed. Please check the "
+ "activation key and network or proxy settings and try again.\n"
+ )
+ else:
+ self.rdmc.ui.printer("..")
+ i = i + 1
+
+ def disconnect_cloud(self):
+ """cloud disconnect function"""
+ cloud_status = self.get_cloud_status()
+ if cloud_status == "Connected" or cloud_status == "ConnectionFailed":
+ path = self.rdmc.app.typepath.defs.managerpath + "Actions" + self.rdmc.app.typepath.defs.oempath
+ path = path + "/HpeiLO.DisableCloudConnect"
+ body = dict()
+ try:
+ if path:
+ self.rdmc.ui.printer("Disconnecting ComputeOpsManagement...\n", verbose_override=True)
+ self.rdmc.app.post_handler(path, body)
+ time.sleep(10)
+ cloud_status = self.get_cloud_status()
+ if cloud_status == "NotEnabled":
+ self.rdmc.ui.printer("The operation completed successfully.\n")
+ except Exception as e:
+ raise CloudConnectFailedError(f"ComputeOpsManagement is not disconnected: {str(e)}\n")
+ else:
+ self.rdmc.ui.printer(
+ "Warning: ComputeOpsManagement is not at all connected.\n",
+ verbose_override=True,
+ )
+
+ def cloud_status(self, json=False):
+ """cloud connect function
+
+ :param json: json
+ :type json: bool
+ """
+ path = self.rdmc.app.typepath.defs.managerpath
+ resp = self.rdmc.app.get_handler(path, service=False, silent=True)
+ if resp.status != 200:
+ raise SessionExpired("Invalid session. Please logout and log back in or include credentials.")
+ cloud_info = resp.dict["Oem"]["Hpe"]["CloudConnect"]
+ output = "------------------------------------------------\n"
+ output += "ComputeOpsManagement connection status\n"
+ output += "------------------------------------------------\n"
+ output += "ComputeOpsManagement Status : %s\n" % (cloud_info["CloudConnectStatus"])
+ if cloud_info["CloudConnectStatus"] != "NotEnabled":
+ if "CloudActivateURL" in cloud_info:
+ output += "CloudActivateURL : %s\n" % (cloud_info["CloudActivateURL"])
+ if "ActivationKey" in cloud_info:
+ output += "ActivationKey : %s\n" % (cloud_info["ActivationKey"])
+ if "ExtendedStatusInfo" in cloud_info:
+ output += "\n"
+ output += "Extended Cloud connect status is as follows:\n\n"
+ output += "Network Configuration : %s\n" % (
+ Cloudconnectstatus[cloud_info["ExtendedStatusInfo"]["NetworkConfig"]]
+ )
+ output += "Web Connectivity : %s\n" % (
+ Cloudconnectstatus[cloud_info["ExtendedStatusInfo"]["WebConnectivity"]]
+ )
+ output += "iLO Configuration for Cloud Connect : %s\n" % (
+ Cloudconnectstatus[cloud_info["ExtendedStatusInfo"]["iLOConfigForCloudConnect"]]
+ )
+ if not json:
+ self.rdmc.ui.printer(output, verbose_override=True)
+ else:
+ self.rdmc.ui.print_out_json(cloud_info)
+
+ def generate_template_file(self, template_input_file=DEFAULT_TEMPLATE_FILE_NAME):
+ """Generate template JSON input file for multiconnect"""
+
+ template_str = json.dumps(template, indent=4)
+
+ try:
+ with open(template_input_file, "w") as outfile:
+ outfile.write(template_str)
+ self.rdmc.ui.printer(f"Template written to: {template_input_file}\n", verbose_override=True)
+ except Exception as e:
+ LOGGER.error(f"Exception writing template file: {e}")
+ raise InvalidCommandLineError(f"Failed to write template JSON file: {e}\n")
+
+ def bulk_connect_precheck(self, config_file, output_file=None):
+ """Perform precheck validation for bulk connect operations
+
+ :param config_file: path to JSON configuration file
+ :type config_file: str
+ :param output_file: optional output file for report
+ :type output_file: str or None
+ :param allow_ilo_reset: allow iLO reset during operations
+ :type allow_ilo_reset: bool
+ """
+ # Suppress urllib3 warnings (connection retry warnings, InsecureRequestWarning, etc.)
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+ urllib3.disable_warnings(urllib3.exceptions.ConnectionError)
+ urllib3.disable_warnings()
+
+ current_time = datetime.now()
+ LOGGER.info(f"Starting bulk_connect precheck: config={config_file}, output={output_file}")
+
+ # Load and validate configuration (do once, not in loop)
+ try:
+ config = self._validate_input_file(config_file)
+ except (Exception) as e:
+ raise e
+
+ # Generate default output filename if not provided
+ if not output_file:
+ output_file = f"onboard_precheck_{current_time.strftime('%m%d%Y_%H%M%S')}.json"
+
+ LOGGER.info(f"Report will be saved to: {output_file}")
+
+ # Initialize report with new format
+ report = {
+ "iloPrecheck": [],
+ "resultCount": {"ilos": "0", "preCheckPassed": "0", "preCheckFailed": "0"}
+ }
+
+ # Counters for result tracking
+ counters = {"total": 0, "preCheckPassed": 0, "preCheckFailed": 0}
+
+ # Progressive update for pbar implementation
+ # This gets updated in every iteration of the for loop below
+ pbar_postfix = {}
+
+ # Extract common settings
+ common_settings = config.get("commonSettings", {})
+ ilo_auth = (
+ common_settings.get("iloAuthentication", {})
+ if isinstance(common_settings.get("iloAuthentication"), dict)
+ else {}
+ )
+ com_settings = (
+ common_settings.get("computeOpsManagement", {})
+ if isinstance(common_settings.get("computeOpsManagement"), dict)
+ else {}
+ )
+ network_settings = (
+ common_settings.get("network", {}) if isinstance(common_settings.get("network"), dict) else {}
+ )
+ proxy_settings = common_settings.get("proxy", {}) if isinstance(common_settings.get("proxy"), dict) else {}
+
+ # Get target IPs
+ try:
+ target_ips = self._get_all_target_ips(config.get("targets", {}))
+ counters["total_targets"] = len(target_ips)
+ LOGGER.info(f"Precheck for {len(target_ips)} target iLO(s)")
+ except Exception as e:
+ error_msg = f"Error processing target IPs: {str(e)}"
+ LOGGER.error(error_msg)
+ raise InvalidCommandLineError(error_msg)
+
+ # Get progress bar implementation
+ tqdm = self._get_progress_bar_implementation()
+
+ # Process each target IP with progress bar for precheck validation
+ with tqdm(total=len(target_ips), desc="Validating iLOs", unit="iLO") as pbar:
+ for ip_info in target_ips:
+ ip = ip_info["ip"]
+ individual_settings = ip_info.get("settings", {}) if isinstance(ip_info.get("settings"), dict) else {}
+
+ pbar.set_description(f"Validating {ip}")
+ LOGGER.info(f"Starting precheck validation for iLO: {ip}")
+
+ # Initialize precheck result
+ precheck_result = {
+ "ip": ip,
+ "managementProcessorModel": "",
+ "iloVersion": "",
+ "preCheckResult": "FAILED",
+ "details": "",
+ "recommendation": "",
+ }
+
+ # Initialize status tracking variables
+ postfix_type = "FAILED"
+
+ # Validate single iLO
+ validation_result = self._validate_single_ilo_for_precheck(
+ ip, ilo_auth, com_settings, network_settings, individual_settings
+ )
+
+ status = validation_result["status"]
+ error = validation_result["error"]
+ recommendation = validation_result["recommendation"]
+ precheck_result["managementProcessorModel"] = validation_result["managementProcessorModel"]
+ precheck_result["iloVersion"] = validation_result["iloVersion"]
+
+ # Set postfix type for progress bar
+ if error == "Authentication or connectivity failed with iLO":
+ postfix_type = "ERROR"
+
+ # Update precheck result with final values
+ precheck_result["preCheckResult"] = status
+ precheck_result["details"] = error
+ precheck_result["recommendation"] = recommendation
+
+ # Update counters and progress bar
+ if status == "PASSED":
+ counters["preCheckPassed"] += 1
+ pbar_postfix["status"] = "PASSED"
+ pbar_postfix["preCheckPassed"] = counters["preCheckPassed"]
+ pbar.set_postfix(**pbar_postfix)
+ else:
+ counters["preCheckFailed"] += 1
+ pbar_postfix["status"] = postfix_type
+ pbar_postfix["preCheckFailed"] = counters["preCheckFailed"]
+ pbar.set_postfix(**pbar_postfix)
+
+ # Add to report
+ report["iloPrecheck"].append(precheck_result)
+ counters["total"] += 1
+ pbar.update(1)
+
+ # Finalize report
+ report["resultCount"] = {
+ "ilos": str(counters["total"]),
+ "preCheckPassed": str(counters["preCheckPassed"]),
+ "preCheckFailed": str(counters["preCheckFailed"]),
+ }
+
+ # Write report to JSON file
+ try:
+ self._save_json_report(report, output_file)
+ LOGGER.info(f"Precheck report saved to: {output_file}")
+ self.rdmc.ui.printer(f"Precheck completed. Report saved to: {output_file}\n", verbose_override=True)
+ self.rdmc.ui.printer(f"Precheck passed for {counters['preCheckPassed']} iLO(s).\n", verbose_override=True)
+ self.rdmc.ui.printer(f"Precheck failed for {counters['preCheckFailed']} iLO(s).\n", verbose_override=True)
+ except Exception as e:
+ LOGGER.error(f"Failed to save precheck report: {str(e)}")
+ raise InvalidCommandLineError(f"Failed to save precheck report: {str(e)}")
+
+ return ReturnCodes.SUCCESS
+
+ def _get_ilo_credentials(self, ilo_auth, individual_settings):
+ """Extract iLO credentials, prioritizing individual settings over common settings
+
+ :param ilo_auth: Common iLO authentication settings
+ :type ilo_auth: dict
+ :param individual_settings: Individual iLO settings that may contain authentication
+ :type individual_settings: dict
+ :returns: tuple of (username, password)
+ :rtype: tuple
+ """
+ # Check if individual settings contain iLO authentication
+ individual_auth = individual_settings.get("iloAuthentication", {})
+ if isinstance(individual_auth, dict) and individual_auth.get("iloUser") and individual_auth.get("iloPassword"):
+ username = individual_auth.get("iloUser")
+ password = individual_auth.get("iloPassword")
+ return username, password
+
+ # Fall back to common authentication settings
+ username = ilo_auth.get("iloUser", "administrator")
+ password = ilo_auth.get("iloPassword", "")
+ return username, password
+
+ def _validate_single_ilo_for_precheck(self, ip, ilo_auth, com_settings, network_settings, individual_settings):
+ """Validate a single iLO for precheck operations
+
+ :param ip: iLO IP address
+ :type ip: str
+ :param ilo_auth: iLO authentication settings
+ :type ilo_auth: dict
+ :param com_settings: COM settings
+ :type com_settings: dict
+ :param network_settings: network settings
+ :type network_settings: dict
+ :param proxy_settings: proxy settings
+ :type proxy_settings: dict
+ :param individual_settings: individual iLO settings
+ :type individual_settings: dict
+ :returns: dict with validation results
+ :rtype: dict
+ """
+ # Initialize results
+ validation_data = {
+ "status": "FAILED",
+ "error": "",
+ "recommendation": "",
+ "managementProcessorModel": "",
+ "iloVersion": "",
+ }
+
+ try:
+ # Perform basic connectivity and version checks
+ base_url = f"https://{ip}"
+ username, password = self._get_ilo_credentials(ilo_auth, individual_settings)
+
+ if not password:
+ validation_data["error"] = "No password provided"
+ validation_data["recommendation"] = "Provide valid iLO credentials"
+ return validation_data
+ # Basic connectivity test
+ from requests.auth import HTTPBasicAuth
+
+ response = requests.get(
+ f"{base_url}/redfish/v1/",
+ auth=HTTPBasicAuth(username, password),
+ verify=False,
+ timeout=30
+ )
+
+ if response.status_code == 200:
+ # Get iLO version information
+ model, ilo_version = self._get_ilo_version_from_response(response.json(), base_url, username, password)
+
+ # Set model and version info
+ validation_data["managementProcessorModel"] = f"iLO{model}" if model else ""
+ validation_data["iloVersion"] = str(ilo_version) if ilo_version else ""
+
+ # Check version compatibility
+ is_compatible, err_msg, rec_msg = self._check_ilo_version_compatibility(model, ilo_version, ip)
+ if is_compatible:
+ # Check if iLO is already connected to COM
+ # Create HTTP session
+ session = requests.Session()
+ session.auth = HTTPBasicAuth(username, password)
+ session.verify = False
+
+ is_connected = self._check_com_connection_status(session, base_url)
+ # if iLO is connected, set validation_data results as PASSED
+ if is_connected:
+ validation_data["status"] = "PASSED"
+ else:
+ # Check activationKey/workspace_id based on iLO version capabilities
+ is_pin_supported = self._is_pin_onboarding_supported(ilo_version, model)
+
+ if not is_pin_supported:
+ workspace_id = com_settings.get("workspace_id", "")
+ if not workspace_id:
+ validation_data["error"] = "The version of iLO requires a valid workspace_id"
+ validation_data["recommendation"] = (
+ "Provide a valid workspace_id in the configuration for onboarding"
+ )
+ return validation_data
+ else:
+ activation_key = com_settings.get("activationKey", "")
+ if not activation_key:
+ validation_data["error"] = "The version of iLO requires a valid activation Key"
+ validation_data["recommendation"] = (
+ "Provide a valid activation Key in the configuration for onboarding"
+ )
+ return validation_data
+
+ merged_network = network_settings.copy() if isinstance(network_settings, dict) else {}
+ individual_network = individual_settings.get("network", {})
+ if individual_network and isinstance(individual_network, dict):
+ merged_network.update(individual_network)
+
+ session = self._create_session_with_retries(username, password)
+
+ dns_precheck_result, validation_data = self._precheck_validate_dns_settings(
+ session,
+ base_url,
+ merged_network,
+ individual_settings,
+ validation_data,
+ )
+ if not dns_precheck_result:
+ return validation_data
+
+ ntp_precheck_result, validation_data = self._precheck_validate_ntp_settings(
+ session,
+ base_url,
+ merged_network,
+ individual_settings,
+ validation_data,
+ )
+ if not ntp_precheck_result:
+ return validation_data
+
+ # All checks have passed, mark as ready for onboarding
+ validation_data["status"] = "PASSED"
+ else:
+ validation_data["error"] = err_msg
+ validation_data["recommendation"] = rec_msg
+ else:
+ validation_data["error"] = f"HTTP {response.status_code} - Authentication or connectivity failed"
+ validation_data["recommendation"] = "Verify iLO credentials and network connectivity"
+
+ except Exception as e:
+ validation_data["error"] = "Authentication or connectivity failed with iLO"
+ validation_data["recommendation"] = "Review the input file for credentials and re-run the command"
+
+ return validation_data
+
+ def _precheck_validate_dns_settings(self, session, base_url, merged_network, individual_settings, validation_data):
+ """Validate a DNS setting in iLO for precheck
+
+ :param session: session object to iLO
+ :type session: object
+ :param base_url: base URL for iLO REST API
+ :type base_url: str
+ :param merged_network: merged network settings
+ :type merged_network: dict
+ :param individual_settings: individual settings
+ :type individual_settings: dict
+ :param validation_data: validation data dictionary
+ :type validation_data: dict
+ :returns: tuple of (DNS precheck status, updated validation data)
+ :rtype: tuple
+ """
+ dns_precheck_status = False
+ current_dns_servers, dhcp_dns_enabled, dhcp_check_attempted = self._get_current_dns_servers(session, base_url)
+ if not dhcp_check_attempted:
+ LOGGER.error("Unable to retrieve DNS settings from iLO during precheck")
+ validation_data["error"] = "Unable to retrieve DNS settings from iLO"
+ validation_data["recommendation"] = "Check iLO connectivity and retry the operation"
+ return dns_precheck_status, validation_data
+
+ if individual_settings.get("skipDns", False):
+ if not current_dns_servers and not dhcp_dns_enabled:
+ if merged_network.get("dns", []):
+ LOGGER.info("skipDNS flag is set, no DNS configured in iLO")
+ validation_data["recommendation"] = (
+ "Remove skipDns or Configure DNS in iLO before attempting"
+ " to Onboard to HPE Compute Ops Management"
+ )
+ else:
+ LOGGER.info("skipDNS flag is set, no DNS settings in input. Also no DNS configured in iLO")
+ validation_data["recommendation"] = (
+ "Configure DNS in iLO before attempting to Onboard to HPE Compute Ops Management"
+ )
+ validation_data["error"] = "No DNS configuration found in iLO"
+ return dns_precheck_status, validation_data
+ elif not merged_network.get("dns", []):
+ if not current_dns_servers and not dhcp_dns_enabled:
+ LOGGER.info("No DNS settings in input and no DNS configured in iLO")
+ validation_data["error"] = "No DNS configuration found in iLO"
+ validation_data["recommendation"] = (
+ "Configure DNS in iLO before attempting to Onboard to HPE Compute Ops Management"
+ )
+ return dns_precheck_status, validation_data
+
+ dns_precheck_status = True
+ LOGGER.info("DNS precheck passed")
+ return dns_precheck_status, validation_data
+
+ def _precheck_validate_ntp_settings(self, session, base_url, merged_network, individual_settings, validation_data):
+ """Validate NTP settings in iLO for precheck
+
+ :param session: session object to iLO
+ :type session: object
+ :param base_url: base URL for iLO REST API
+ :type base_url: str
+ :param merged_network: merged network settings
+ :type merged_network: dict
+ :param individual_settings: individual settings
+ :type individual_settings: dict
+ :param validation_data: validation data dictionary
+ :type validation_data: dict
+ :returns: tuple of (NTP precheck status, updated validation data)
+ :rtype: tuple
+ """
+ ntp_precheck_status = True
+ eth_interfaces_url = f"{base_url}/redfish/v1/Managers/1/EthernetInterfaces"
+ dhcp_ntp_is_enabled, _, dhcp_check_attempted = self._check_dhcp_ntp_config_on_interfaces(
+ session,
+ base_url,
+ eth_interfaces_url,
+ )
+ ntp_servers = self._get_current_ntp_servers(session, base_url)
+ if not dhcp_check_attempted or ntp_servers is None:
+ LOGGER.error("Unable to retrieve NTP settings from iLO during precheck")
+ ntp_precheck_status = False
+ validation_data["error"] = "Unable to retrieve NTP settings from iLO"
+ validation_data["recommendation"] = "Check iLO connectivity and retry the operation"
+ return ntp_precheck_status, validation_data
+
+ if individual_settings.get("skipNtp", False):
+ if not dhcp_ntp_is_enabled and not ntp_servers:
+ if merged_network.get("ntp", []):
+ LOGGER.info("skipNTP flag is set and no NTP configured in iLO")
+ validation_data["recommendation"] = (
+ "Remove skipNtp or Configure NTP in iLO before attempting"
+ " to Onboard to HPE Compute Ops Management"
+ )
+ else:
+ LOGGER.info("skipNTP flag is set, no NTP settings in input. Also no NTP configured in iLO")
+ validation_data["recommendation"] = (
+ "Configure NTP in iLO before attempting to Onboard to HPE Compute Ops Management"
+ )
+ validation_data["error"] = "No NTP configuration found in iLO"
+ return ntp_precheck_status, validation_data
+ elif not merged_network.get("ntp", []):
+ if not dhcp_ntp_is_enabled and not ntp_servers:
+ LOGGER.info("No NTP settings in input and no NTP configured in iLO")
+ validation_data["error"] = "No NTP configuration found in iLO"
+ validation_data["recommendation"] = (
+ "Configure NTP in iLO before attempting to Onboard to HPE Compute Ops Management"
+ )
+ return ntp_precheck_status, validation_data
+
+ LOGGER.info("NTP precheck passed")
+ return ntp_precheck_status, validation_data
+
+ def _validate_bulk_connect_input(self, config):
+ """
+ Validate the input JSON from file for below:
+ - key named "commonSettings" must be present
+ - commonSettings must have computeOpsManagement key with atleast either of activationKey or workspace_id
+ - commonSettings must have iloAuthentication key that contains iloUser and iloPassword attributes.
+ - key named "targets" must be present
+ - key named "ilos" must be present
+ - atleast one iLO ip should be present either in individual or ranges section.
+ """
+ if not isinstance(config, dict):
+ raise InvalidCommandLineError("Invalid configuration format: expected a JSON object at the top level")
+
+ if "commonSettings" not in config:
+ raise InvalidCommandLineError("Missing 'commonSettings' section in configuration")
+
+ common_settings = config["commonSettings"]
+ if not isinstance(common_settings, dict):
+ raise InvalidCommandLineError("'commonSettings' must be a JSON object")
+
+ com_settings = common_settings.get("computeOpsManagement", {})
+ if not isinstance(com_settings, dict):
+ raise InvalidCommandLineError("'computeOpsManagement' in 'commonSettings' must be a JSON object")
+
+ activation_key = com_settings.get("activationKey")
+ workspace_id = com_settings.get("workspace_id")
+ if not activation_key and not workspace_id:
+ raise InvalidCommandLineError(
+ "In 'commonSettings.computeOpsManagement', either 'activationKey' or 'workspace_id' must be provided"
+ )
+
+ if "targets" not in config:
+ raise InvalidCommandLineError("Missing 'targets' section in configuration")
+
+ targets = config["targets"]
+ if not isinstance(targets, dict):
+ raise InvalidCommandLineError("'targets' must be a JSON object")
+
+ if "ilos" not in targets:
+ raise InvalidCommandLineError("Missing 'ilos' section in 'targets'")
+
+ ilos = targets["ilos"]
+ if not isinstance(ilos, dict):
+ raise InvalidCommandLineError("'ilos' in 'targets' must be a JSON object")
+
+ individual_ilos = ilos.get("individual", [])
+ ranges = ilos.get("ranges", [])
+
+ if not isinstance(individual_ilos, list):
+ raise InvalidCommandLineError("'individual' in 'ilos' must be an array")
+
+ # Check if common iLO authentication is provided
+ common_auth_provided = False
+ if "iloAuthentication" in common_settings:
+ ilo_auth = common_settings["iloAuthentication"]
+ if isinstance(ilo_auth, dict) and ilo_auth.get("iloUser") and ilo_auth.get("iloPassword"):
+ common_auth_provided = True
+
+ # If common authentication is not provided, verify all targets have individual authentication
+ if not common_auth_provided:
+ # Check that all individual iLOs have authentication
+ for ilo in individual_ilos:
+ individual_auth = ilo.get("iloAuthentication", {})
+ if not (isinstance(individual_auth, dict) and
+ individual_auth.get("iloUser") and
+ individual_auth.get("iloPassword")):
+ raise InvalidCommandLineError(
+ "The operation failed as iLO credential is not provided in the input json"
+ )
+
+ # Check that all ranges have authentication
+ for range_config in ranges:
+ range_auth = range_config.get("iloAuthentication", {})
+ if not (isinstance(range_auth, dict) and
+ range_auth.get("iloUser") and
+ range_auth.get("iloPassword")):
+ raise InvalidCommandLineError(
+ "The operation failed as iLO credential is not provided in the input json"
+ )
+ else:
+ # Common authentication is provided, ensure it's properly formatted
+ ilo_auth = common_settings["iloAuthentication"]
+ if not isinstance(ilo_auth, dict):
+ raise InvalidCommandLineError("'iloAuthentication' in 'commonSettings' must be a JSON object")
+
+ if "iloUser" not in ilo_auth or "iloPassword" not in ilo_auth:
+ raise InvalidCommandLineError("Missing 'iloUser' or 'iloPassword' in 'iloAuthentication'")
+
+ if not isinstance(ranges, list):
+ raise InvalidCommandLineError("'ranges' in 'ilos' must be an array")
+
+ total_individual_ilos = len(individual_ilos)
+ total_range_objects = len(ranges)
+ for ilo in individual_ilos:
+ if not isinstance(ilo, dict) or "ip" not in ilo:
+ raise InvalidCommandLineError("Each entry in 'individual' must be an object with an 'ip' field")
+
+ for range_config in ranges:
+ if not isinstance(range_config, dict) or "start" not in range_config or "end" not in range_config:
+ raise InvalidCommandLineError("Each entry in 'ranges' must be an object with 'start' and 'end' fields")
+
+ if total_individual_ilos == 0 and total_range_objects == 0:
+ raise InvalidCommandLineError("At least one iLO IP must be specified in 'individual' or 'ranges'")
+
+ def _validate_input_file(self, config_file) -> Dict[str, Any]:
+ # Load and validate configuration (do once, not in loop)
+ try:
+ if not os.path.exists(config_file):
+ error_msg = f"Configuration file not found: {config_file}"
+ LOGGER.error(error_msg)
+ raise InvalidCommandLineError(error_msg)
+
+ with open(config_file, "r") as f:
+ config = json.load(f)
+ LOGGER.info(f"Loaded configuration from: {config_file}")
+
+ # Validate configuration
+ self._validate_bulk_connect_input(config)
+ LOGGER.info("Configuration validation passed")
+ return config
+ except (json.JSONDecodeError, InvalidCommandLineError) as e:
+ error_msg = f"Error in configuration file '{config_file}': {str(e)}"
+ LOGGER.error(error_msg)
+ raise InvalidCommandLineError(error_msg)
+
+ def bulk_connect(self, config_file, output_file=None, allow_ilo_reset=False):
+ """Perform bulk connect operations for multiple iLOs
+
+ :param config_file: path to JSON configuration file
+ :type config_file: str
+ :param output_file: optional output file for report
+ :type output_file: str or None
+ :param allow_ilo_reset: allow iLO reset during operations
+ :type allow_ilo_reset: bool
+ """
+
+ # Suppress urllib3 warnings (connection retry warnings, InsecureRequestWarning, etc.)
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+ urllib3.disable_warnings(urllib3.exceptions.ConnectionError)
+ urllib3.disable_warnings()
+
+ # Get progress bar implementation
+ tqdm = self._get_progress_bar_implementation()
+
+ # Setup logging
+ current_time = datetime.now()
+ LOGGER.info(f"Starting bulk_connect: config={config_file}, output={output_file}, allow_reset={allow_ilo_reset}")
+
+ # Load and validate configuration (do once, not in loop)
+ try:
+ config = self._validate_input_file(config_file)
+ except (Exception) as e:
+ raise e
+
+ # Generate default output filename if not provided
+ if not output_file:
+ output_file = f"onboard_{current_time.strftime('%m%d%Y_%H%M%S')}.json"
+
+ LOGGER.info(f"Report will be saved to: {output_file}")
+
+ # Initialize report with new format
+ report = {
+ "iloOnboard": [],
+ "resultCount": {"ilos": "0", "connected": "0", "failed": "0", "notAttempted": "0", "pendingiloReset": "0"},
+ }
+
+ # Counters for result tracking
+ counters = {"total": 0, "connected": 0, "failed": 0, "notAttempted": 0, "pendingiloReset": 0}
+
+ # Progressive update for pbar implementation
+ # This gets updated in every iteration of the for loop below
+ pbar_postfix = {}
+
+ # Extract common settings once (not per iLO)
+ common_settings = config.get("commonSettings", {})
+ ilo_auth = (
+ common_settings.get("iloAuthentication", {})
+ if isinstance(common_settings.get("iloAuthentication"), dict)
+ else {}
+ )
+ com_settings = (
+ common_settings.get("computeOpsManagement", {})
+ if isinstance(common_settings.get("computeOpsManagement"), dict)
+ else {}
+ )
+ network_settings = (
+ common_settings.get("network", {}) if isinstance(common_settings.get("network"), dict) else {}
+ )
+ proxy_settings = common_settings.get("proxy", {}) if isinstance(common_settings.get("proxy"), dict) else {}
+
+ # Get target IPs
+ try:
+ target_ips = self._get_all_target_ips(config.get("targets", {}))
+ counters["total_targets"] = len(target_ips)
+ LOGGER.info(f"Processing {len(target_ips)} target iLO(s)")
+ except Exception as e:
+ error_msg = f"Error processing target IPs: {str(e)}"
+ LOGGER.error(error_msg)
+ raise InvalidCommandLineError(error_msg)
+
+ # Process each target IP sequentially with progress bar
+ with tqdm(total=len(target_ips), desc="Connecting iLOs", unit="iLO") as pbar:
+ for ip_info in target_ips:
+ ip = ip_info["ip"]
+ individual_settings = ip_info.get("settings", {}) if isinstance(ip_info.get("settings"), dict) else {}
+
+ pbar.set_description(f"Processing {ip}")
+ LOGGER.info(f"Starting connection for iLO: {ip}")
+
+ result = {
+ "ip": ip,
+ "status": "pending",
+ "message": "",
+ "timestamp": datetime.now().isoformat(),
+ "details": {},
+ }
+
+ # add precheck validation here
+ validation_result = self._validate_single_ilo_for_precheck(
+ ip, ilo_auth, com_settings, network_settings, individual_settings
+ )
+
+ if validation_result["status"] == "PASSED":
+ try:
+ # Connect to individual iLO and perform COM connection
+ success, ilo_was_reset, needs_manual_reset, ilo_result = self._connect_single_ilo_to_com(
+ ip=ip,
+ ilo_auth=ilo_auth,
+ com_settings=com_settings,
+ network_settings=network_settings,
+ proxy_settings=proxy_settings,
+ individual_settings=individual_settings,
+ allow_ilo_reset=allow_ilo_reset,
+ validation_result=validation_result,
+ )
+ except Exception as e:
+ # Handle unexpected exceptions
+ report["iloOnboard"].append(
+ {
+ "ip": ip,
+ "managementProcessorModel": "",
+ "iloVersion": "",
+ "onboardStatus": "not attempted",
+ "onboardError": f"{str(e)}",
+ "recommendation": "Review error details and verify iLO configuration",
+ }
+ )
+ counters["total"] += 1
+ counters["failed"] += 1
+ LOGGER.error(f"ERROR {ip}: Error - {str(e)}")
+ pbar_postfix["status"] = "ERROR"
+ pbar_postfix["failed"] = counters["failed"]
+ pbar.set_postfix(**pbar_postfix)
+ else:
+ # Add to report
+ report["iloOnboard"].append(
+ {
+ "ip": ilo_result["ip"],
+ "managementProcessorModel": ilo_result["managementProcessorModel"],
+ "iloVersion": ilo_result["iloVersion"],
+ "onboardStatus": ilo_result["onboardStatus"],
+ "onboardError": ilo_result["onboardError"],
+ "recommendation": ilo_result["recommendation"],
+ "preCheckResult": validation_result["status"],
+ "preCheckError": validation_result["error"],
+ }
+ )
+
+ # Update counters
+ counters["total"] += 1
+ if ilo_result["onboardStatus"] == "connected":
+ counters["connected"] += 1
+
+ if success:
+ result["status"] = "success"
+ result["message"] = "Successfully connected to COM"
+ result["details"] = {
+ "authentication": "successful",
+ "com_connection": "established",
+ "activation_method": "pin" if com_settings.get("activationKey") else "workspace_id",
+ "ilo_was_reset": ilo_was_reset,
+ }
+ LOGGER.info(f"SUCCESS {ip}: Connected successfully")
+ pbar_postfix["status"] = "SUCCESS"
+ pbar_postfix["connected"] = counters["connected"]
+ pbar.set_postfix(**pbar_postfix)
+ elif ilo_result["onboardStatus"] == "not attempted":
+ counters["notAttempted"] += 1
+ LOGGER.warning(f"NOT ATTEMPTED {ip}: {ilo_result['onboardError']}")
+ pbar_postfix["status"] = "NOT ATTEMPTED"
+ pbar_postfix["notAttempted"] = counters["notAttempted"]
+ pbar.set_postfix(**pbar_postfix)
+ elif (
+ "reset" in ilo_result.get("onboardError", "").lower()
+ or "waiting" in ilo_result.get("onboardError", "").lower()
+ ):
+ counters["pendingiloReset"] += 1
+ LOGGER.warning(f"PENDING RESET {ip}")
+ pbar_postfix["status"] = "PENDING RESET"
+ pbar_postfix["pendingiloReset"] = counters["pendingiloReset"]
+ pbar.set_postfix(**pbar_postfix)
+ else:
+ counters["failed"] += 1
+ LOGGER.warning(f"FAILED {ip}: {ilo_result['onboardError']}")
+ pbar_postfix["status"] = "FAILED"
+ pbar_postfix["failed"] = counters["failed"]
+ pbar.set_postfix(**pbar_postfix)
+ result["status"] = "failed"
+ if needs_manual_reset:
+ result["message"] = "NTP configured but requires manual iLO reset"
+ result["details"] = {
+ "error": "NTP configured but manual reset required",
+ "ilo_was_reset": ilo_was_reset,
+ "needs_manual_reset": True,
+ }
+ else:
+ result["message"] = "Failed to connect to COM"
+ result["details"] = {
+ "error": "Connection failed during COM setup",
+ "ilo_was_reset": ilo_was_reset,
+ }
+ else:
+ # Precheck failed, do not attempt connection
+ report["iloOnboard"].append(
+ {
+ "ip": ip,
+ "managementProcessorModel": validation_result["managementProcessorModel"],
+ "iloVersion": validation_result["iloVersion"],
+ "onboardStatus": "not attempted",
+ "onboardError": "",
+ "recommendation": validation_result["recommendation"],
+ "preCheckResult": validation_result["status"],
+ "preCheckError": validation_result["error"],
+ }
+ )
+ counters["total"] += 1
+ counters["notAttempted"] += 1
+ LOGGER.warning(f"NOT ATTEMPTED {ip}: Precheck failed - {validation_result['error']}")
+ pbar_postfix["status"] = "NOT ATTEMPTED"
+ pbar_postfix["notAttempted"] = counters["notAttempted"]
+ pbar.set_postfix(**pbar_postfix)
+ pbar.update(1)
+
+ # Finalize report
+ report["resultCount"] = {
+ "ilos": str(counters["total"]),
+ "connected": str(counters["connected"]),
+ "failed": str(counters["failed"]),
+ "notAttempted": str(counters["notAttempted"]),
+ "pendingiloReset": str(counters["pendingiloReset"]),
+ }
+
+ end_time = datetime.now()
+ duration_seconds = (end_time - current_time).total_seconds()
+ LOGGER.info(
+ f"Bulk connect completed in {duration_seconds:.2f}s: "
+ f"{counters['connected']} connected, {counters['failed']} failed, "
+ f"{counters['notAttempted']} not attempted, {counters['pendingiloReset']} pending reset"
+ )
+
+ # Write report to JSON file
+ try:
+ self._save_json_report(report, output_file)
+ except Exception as e:
+ LOGGER.error(f"Failed to save report: {str(e)}")
+ print(f"ERROR: Could not save report to {output_file}: {str(e)}")
+
+ # Display summary both to stdout and log file
+ print(f"ComputeOpsManagement connection successful for {counters['connected']} server(s).")
+ print(f"ComputeOpsManagement connection failed for {counters['failed']} server(s).")
+ print(f"The operation completed. Details available in the output {output_file} file")
+
+ LOGGER.info(f"Report saved to: {output_file}")
+ return ReturnCodes.SUCCESS
+
+ def _get_all_target_ips(self, targets):
+ """Extract all target IPs from the configuration
+
+ :param targets: targets section from config
+ :type targets: dict
+ :returns: list of IP information dictionaries
+ :rtype: list
+ """
+ import ipaddress
+
+ target_ips = []
+ ilos = targets.get("ilos", {})
+
+ # Process individual IPs
+ individual_ilos = ilos.get("individual", [])
+ for ilo in individual_ilos:
+ ip_info = {"ip": ilo["ip"], "settings": ilo}
+ target_ips.append(ip_info)
+
+ # Process IP ranges
+ ranges = ilos.get("ranges", [])
+ for range_config in ranges:
+ start_ip = ipaddress.IPv4Address(range_config["start"])
+ end_ip = ipaddress.IPv4Address(range_config["end"])
+
+ # Validate range size to prevent memory issues
+ range_size = int(end_ip) - int(start_ip) + 1
+ if range_size > 1000:
+ raise InvalidCommandLineError(
+ f"IP range {range_config['start']} to {range_config['end']} contains {range_size} IPs. "
+ f"Maximum allowed range size is 1000 IPs to prevent memory issues."
+ )
+
+ current_ip = start_ip
+ while current_ip <= end_ip:
+ ip_info = {"ip": str(current_ip), "settings": range_config}
+ target_ips.append(ip_info)
+ current_ip += 1
+
+ return target_ips
+
+ def _get_all_target_ips_optimized(self, targets):
+ """Optimized generator to extract all target IPs from the configuration
+
+ :param targets: targets section from config
+ :type targets: dict
+ :yields: IP information dictionaries
+ :rtype: iterator
+ """
+ import ipaddress
+
+ ilos = targets.get("ilos", {})
+
+ # Process individual IPs
+ individual_ilos = ilos.get("individual", [])
+ for ilo in individual_ilos:
+ yield {"ip": ilo["ip"], "settings": ilo}
+
+ # Process IP ranges
+ ranges = ilos.get("ranges", [])
+ for range_config in ranges:
+ start_ip = ipaddress.IPv4Address(range_config["start"])
+ end_ip = ipaddress.IPv4Address(range_config["end"])
+
+ # Validate range size to prevent memory issues
+ range_size = int(end_ip) - int(start_ip) + 1
+ if range_size > 1000:
+ raise InvalidCommandLineError(
+ f"IP range {range_config['start']} to {range_config['end']} contains {range_size} IPs. "
+ f"Maximum allowed range size is 1000 IPs to prevent memory issues."
+ )
+
+ current_ip = start_ip
+ while current_ip <= end_ip:
+ yield {"ip": str(current_ip), "settings": range_config}
+ current_ip += 1
+
+ def _connect_single_ilo_to_com(
+ self,
+ ip,
+ ilo_auth,
+ com_settings,
+ network_settings,
+ proxy_settings,
+ individual_settings,
+ allow_ilo_reset=False,
+ validation_result=None,
+ ):
+ """Connect a single iLO to COM using HTTP-only connection
+
+ :returns: tuple (success, ilo_was_reset, needs_manual_reset, result_dict)
+ :rtype: tuple
+ """
+ import requests
+ from requests.auth import HTTPBasicAuth
+
+ # Initialize return structure
+ result = {
+ "ip": ip,
+ "managementProcessorModel": "",
+ "iloVersion": "",
+ "onboardStatus": "not attempted",
+ "onboardError": "",
+ "recommendation": "",
+ }
+
+ ilo_was_reset = False
+
+ try:
+ # Create base URL and validate credentials
+ base_url = f"https://{ip}"
+ username, password = self._get_ilo_credentials(ilo_auth, individual_settings)
+
+ if not password:
+ result["onboardError"] = "No password provided"
+ result["recommendation"] = "Provide valid iLO credentials"
+ LOGGER.warning(f"{ip}: No password provided")
+ return False, ilo_was_reset, False, result
+
+ # Test connection with simple HTTP GET using requests
+ response = requests.get(
+ f"{base_url}/redfish/v1/", auth=HTTPBasicAuth(username, password), verify=False, timeout=30
+ )
+
+ if response.status_code != 200:
+ result["onboardError"] = f"HTTP {response.status_code} - Authentication failed"
+ result["recommendation"] = "Verify iLO credentials and network connectivity"
+ LOGGER.error(f"{ip}: Authentication failed - HTTP {response.status_code}")
+ return False, ilo_was_reset, False, result
+
+ LOGGER.info(f"{ip}: Authenticated successfully")
+
+ # Get iLO version information
+ model = ""
+ model_version = ""
+ ilo_version = ""
+ if validation_result:
+ model = validation_result.get("managementProcessorModel", "iLO0")
+ model_version = int((model.strip()).split("iLO")[-1])
+ ilo_version = float(validation_result.get("iloVersion", "0.0"))
+ else:
+ model_version, ilo_version = self._get_ilo_version_from_response(
+ response.json(),
+ base_url,
+ username,
+ password
+ )
+ model = f"iLO{str(model_version)}" if model_version else ""
+
+ result["managementProcessorModel"] = model
+ result["iloVersion"] = ilo_version
+
+ session = self._create_session_with_retries(username, password)
+
+ # Configure proxy if needed - stop and move to next iLO on failure
+ try:
+ self._configure_proxy_for_ilo_requests(session, base_url, proxy_settings, individual_settings)
+ except Exception as proxy_err:
+ result["onboardStatus"] = "not attempted"
+ result["onboardError"] = f"Proxy configuration failed: {str(proxy_err)}"
+ result["recommendation"] = "Verify proxy settings and network connectivity, then retry"
+ LOGGER.error(f"{ip}: Proxy configuration failed - {str(proxy_err)}")
+ return False, ilo_was_reset, False, result
+
+ # Configure network settings if needed and capture reset status - stop and move to next iLO on failure
+ try:
+ ilo_was_reset, ntp_configured_needs_reset = self._configure_network_settings_requests(
+ session, base_url, network_settings, individual_settings, allow_ilo_reset
+ )
+ except Exception as network_err:
+ result["onboardStatus"] = "not attempted"
+ result["onboardError"] = f"Network configuration failed: {str(network_err)}"
+ result["recommendation"] = "Verify DNS/NTP settings and network connectivity, then retry"
+ LOGGER.error(f"{ip}: Network configuration failed - {str(network_err)}")
+ return False, ilo_was_reset, False, result
+
+ # If NTP was configured but reset is not allowed, stop onboarding
+ if ntp_configured_needs_reset:
+ result["onboardStatus"] = "pending reset"
+ result["onboardError"] = "NTP configuration requires manual iLO reset"
+ result["recommendation"] = (
+ "Manually reset the iLO for NTP settings to take effect, "
+ "then re-run the onboarding command. "
+ "Alternatively, use --allow_ilo_reset flag to automatically reset iLO."
+ )
+ LOGGER.warning(f"{ip}: NTP configured but requires manual iLO reset")
+ return False, ilo_was_reset, True, result
+
+ # If iLO was reset and reset is allowed, wait for it to come back online
+ if ilo_was_reset and allow_ilo_reset:
+ LOGGER.info(f"{ip}: iLO was reset, waiting for it to come back online...")
+ if self._wait_for_ilo_after_reset(ip, username, password):
+ LOGGER.info(f"{ip}: iLO is back online")
+ # Create new session after reset
+ session = requests.Session()
+ session.auth = HTTPBasicAuth(username, password)
+ session.verify = False
+ else:
+ LOGGER.error(f"{ip}: iLO did not come back online within timeout")
+ return False, ilo_was_reset, False, result
+ elif ilo_was_reset and not allow_ilo_reset:
+ LOGGER.warning(f"{ip}: iLO was reset but --allow_ilo_reset not specified")
+ return False, ilo_was_reset, True, result
+
+ # Determine if PIN-based onboarding is supported
+ workspace_id = com_settings.get("workspace_id")
+ activation_key = com_settings.get("activationKey")
+ pin_supported = self._is_pin_onboarding_supported(ilo_version, model_version)
+
+ credential = activation_key if pin_supported and activation_key else workspace_id
+ if credential:
+ success, fail_reason = self._enable_com_connection_requests(session, base_url, credential)
+ if success:
+ result["onboardStatus"] = "connected"
+ LOGGER.info(f"{ip}: Successfully connected to COM")
+ if not ilo_was_reset and "Configure NTP in iLO" in validation_result["recommendation"]:
+ result["recommendation"] = (
+ "Though iLO is onboarded to HPE Compute Ops Management, "
+ "it is recommended to configure NTP in iLO"
+ )
+ return True, ilo_was_reset, False, result
+ else:
+ result["onboardStatus"] = "not connected"
+ if fail_reason == "InvalidActivationKey":
+ result["onboardError"] = "The activation key entered is invalid"
+ result["recommendation"] = (
+ "Please check the activation key value. " "If the problem persists, contact HPE Support"
+ )
+ elif fail_reason == "ExpiredActivationKey":
+ result["onboardError"] = "The activation key entered is expired"
+ result["recommendation"] = (
+ "Please enter a valid activation key. " "If the problem persists, contact HPE Support"
+ )
+ elif fail_reason == "ActivationKeyRequired":
+ result["onboardError"] = "An activation key is required"
+ result["recommendation"] = "Please provide a valid activation key"
+ else:
+ result["onboardError"] = (
+ f"Failed to enable COM connection: {fail_reason}"
+ if fail_reason
+ else "Failed to enable COM connection"
+ )
+ result["recommendation"] = "Verify activation key/workspace_id and network settings, then retry"
+ LOGGER.warning(f"{ip}: Failed to enable COM - {result['onboardError']}")
+ return False, ilo_was_reset, False, result
+
+ except requests.exceptions.ConnectionError:
+ result["onboardError"] = "iLO is not reachable"
+ result["recommendation"] = "Verify network connectivity to iLO"
+ LOGGER.error(f"{ip}: Connection error - iLO not reachable")
+ return False, ilo_was_reset, False, result
+ except requests.exceptions.Timeout:
+ result["onboardError"] = "Connection timeout"
+ result["recommendation"] = "Verify network connectivity and iLO responsiveness"
+ LOGGER.error(f"{ip}: Connection timeout")
+ return False, ilo_was_reset, False, result
+ except Exception as e:
+ result["onboardError"] = str(e)
+ result["recommendation"] = "Review error details and verify iLO configuration"
+ LOGGER.error(f"{ip}: Unexpected error - {str(e)}")
+ return False, ilo_was_reset, False, result
+
+ def _create_session_with_retries(self, username, password):
+ """Create a requests session with retry strategy
+
+ :param username: iLO username
+ :type username: str
+ :param password: iLO password
+ :type password: str
+ :returns: configured requests session
+ :rtype: requests.Session
+ """
+ from requests.auth import HTTPBasicAuth
+ from requests.adapters import HTTPAdapter
+
+ # Create a session object with proper connection settings
+ session = requests.Session()
+ session.auth = HTTPBasicAuth(username, password)
+ session.verify = False
+
+ # Define retry strategy for connection errors
+ retry_strategy = Retry(
+ total=3, # Total number of retries
+ backoff_factor=1, # Wait 1, 2, 4 seconds between retries
+ status_forcelist=[500, 502, 503, 504], # Retry on these HTTP status codes
+ allowed_methods=["GET", "POST", "PATCH"], # Retry these methods
+ raise_on_status=False, # Don't raise exceptions on retry exhaustion
+ )
+
+ # Create adapter with retry strategy
+ adapter = HTTPAdapter(
+ max_retries=retry_strategy,
+ pool_connections=1, # Single connection pool
+ pool_maxsize=1, # Single connection in pool to avoid conflicts
+ )
+
+ # Mount adapter for both http and https
+ session.mount("http://", adapter)
+ session.mount("https://", adapter)
+
+ return session
+
+ def _wait_for_ilo_after_reset(self, ip, username, password, timeout_minutes=3):
+ """Wait for iLO to come back online after reset
+
+ :param ip: iLO IP address
+ :type ip: str
+ :param username: iLO username
+ :type username: str
+ :param password: iLO password
+ :type password: str
+ :param timeout_minutes: timeout in minutes to wait for iLO
+ :type timeout_minutes: int
+ :returns: True if iLO is back online, False if timeout
+ :rtype: bool
+ """
+ import requests
+ from requests.auth import HTTPBasicAuth
+ import time
+
+ base_url = f"https://{ip}"
+ timeout_seconds = timeout_minutes * 60
+ check_interval = 10 # Check every 10 seconds as requested
+ max_attempts = int(timeout_seconds / check_interval)
+ start_time = time.time()
+
+ LOGGER.info("Continuing to wait ...")
+
+ for attempt in range(max_attempts):
+ try:
+ response = requests.get(
+ f"{base_url}/redfish/v1/", auth=HTTPBasicAuth(username, password), verify=False, timeout=30
+ )
+
+ if response.status_code == 200:
+ elapsed_time = time.time() - start_time
+ LOGGER.info(f"{ip}: iLO back online after {elapsed_time:.1f}s")
+ return True
+
+ except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
+ # Expected during reset - iLO is still rebooting
+ pass
+ except Exception as e:
+ LOGGER.warning(f"{ip}: Unexpected error while waiting - {str(e)}")
+
+ # Wait before next check (skip on last attempt)
+ if attempt < max_attempts - 1:
+ time.sleep(check_interval)
+
+ elapsed_time = time.time() - start_time
+ LOGGER.error(f"{ip}: Timeout - iLO did not come back online within {timeout_minutes}m ({elapsed_time:.1f}s)")
+ return False
+
+ def _get_ilo_version_from_response(self, redfish_data, base_url=None, username=None, password=None):
+ """Get iLO version from Redfish response data."""
+ model_version, ilo_version = 0, 0.0
+
+ try:
+ if all([redfish_data.get("Managers"), base_url, username, password]):
+ model, firmware_version = self._get_ilo_model_and_version(base_url, username, password)
+
+ if model and "iLO" in model:
+ model_version = int(model.replace("iLO", ""))
+
+ if firmware_version:
+ parts = firmware_version.split()
+ if len(parts) >= 3:
+ # Handle old format: "iLO 6 v1.66" -> parts[2] = "v1.66"
+ if parts[0].lower() == "ilo" and parts[2].startswith('v'):
+ version_str = parts[2].lstrip('v') # Remove 'v' prefix from old format
+ # Handle new format: "iLO 7 1.19.00 Nov 24 2025" -> parts[2] = "1.19.00"
+ elif parts[0].lower() == "ilo" and not parts[2].startswith('v'):
+ version_str = parts[2] # Use version number as-is from new format
+ # Handle direct version format: "1.19.00 Nov 24 2025" -> parts[0] = "1.19.00"
+ else:
+ version_str = parts[0] # Use first part for direct version format
+
+ version_parts = version_str.split(".")
+ ilo_version = float(".".join(version_parts[:2])) if len(version_parts) >= 2 else 0.0
+ else:
+ LOGGER.warning("Managers section not found in Redfish response or invalid credentials")
+ except Exception as e:
+ LOGGER.warning(f"Could not determine iLO version from response: {str(e)}")
+ raise e
+
+ return model_version, ilo_version
+
+ def _is_pin_onboarding_supported(self, ilo_version, major_version):
+ """Determine if PIN-based onboarding is supported
+
+ :param ilo_version: iLO firmware version
+ :type ilo_version: float
+ :param major_version: iLO major version
+ :type major_version: int
+ :returns: True if PIN onboarding is supported
+ :rtype: bool
+ """
+ # PIN-based onboarding support:
+ # - iLO 6: available from version 1.64 onwards
+ # - iLO 5: available from version 3.09 onwards
+ # - iLO 7: available from version 1.12 onwards
+ if major_version == 6 and ilo_version >= 1.64:
+ return True
+ elif major_version == 5 and ilo_version >= 3.09:
+ return True
+ elif major_version == 7 and ilo_version >= 1.12:
+ return True
+ return False
+
+ def _check_ilo_version_compatibility(self, model, ilo_version, ip):
+ """Check if iLO version is compatible with COM requirements
+
+ :param model: iLO major version number
+ :type model: int
+ :param ilo_version: iLO firmware version
+ :type ilo_version: float
+ :param ip: iLO IP address for logging
+ :type ip: str
+ :returns: tuple of (is_compatible, error_message, recommendation)
+ :rtype: tuple
+ """
+ # Define minimum supported versions
+ ILO5_MIN_SUPPORTED = 2.47
+ ILO6_MIN_SUPPORTED = 1.64
+ ILO7_MIN_SUPPORTED = 1.12
+
+ if model == 5 and ilo_version < ILO5_MIN_SUPPORTED:
+ error_msg = f"iLO version should be greater than {ILO5_MIN_SUPPORTED}"
+ recommendation = "Upgrade to the latest iLO version to use HPE Compute Ops Management"
+ LOGGER.warning(f"{ip}: iLO version {ilo_version} incompatible - requires 2.47 or higher")
+ return False, error_msg, recommendation
+ elif model == 6 and ilo_version < ILO6_MIN_SUPPORTED:
+ error_msg = f"iLO version should be greater than {ILO6_MIN_SUPPORTED}"
+ recommendation = "Upgrade to the latest iLO version to use HPE Compute Ops Management"
+ LOGGER.warning(f"{ip}: iLO version {ilo_version} incompatible - requires 1.64 or higher")
+ return False, error_msg, recommendation
+ elif model == 7 and ilo_version < ILO7_MIN_SUPPORTED:
+ error_msg = f"iLO version should be greater than {ILO7_MIN_SUPPORTED}"
+ recommendation = "Upgrade to the latest iLO version to use HPE Compute Ops Management"
+ LOGGER.warning(f"{ip}: iLO version {ilo_version} incompatible - requires 1.12 or higher")
+ return False, error_msg, recommendation
+
+ # Compatible version
+ return True, "", ""
+
+ def _get_ilo_info(self, base_url, username, password):
+ """Get iLO model and version information"""
+ import requests
+ from requests.auth import HTTPBasicAuth
+
+ try:
+ response = requests.get(
+ f"{base_url}/redfish/v1/Managers/1/", auth=HTTPBasicAuth(username, password), verify=False, timeout=30
+ )
+
+ if response.status_code == 200:
+ data = response.json()
+ model = data.get("Model", "iLO5")
+ firmware_version = data.get("FirmwareVersion", "3.09")
+
+ try:
+ version_parts = firmware_version.split(".")
+ major_version = int(model.replace("iLO", "")) if "iLO" in model else 5
+
+ if len(version_parts) >= 2:
+ version_float = float(f"{version_parts[0]}.{version_parts[1]}")
+ else:
+ version_float = float(version_parts[0])
+ except:
+ version_float = 3.09
+ major_version = 5
+
+ return model, firmware_version, version_float, major_version
+ except Exception as e:
+ LOGGER.warning(f"Failed to get iLO info: {str(e)}")
+
+ return "iLO5", "3.09", 3.09, 5
+
+ def _get_ilo_model_and_version(self, base_url, username, password):
+ """Get iLO model and version information from the Redfish API
+
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param username: iLO username
+ :type username: str
+ :param password: iLO password
+ :type password: str
+ :returns: tuple of (model, firmware_version)
+ :rtype: tuple
+ """
+ from requests.auth import HTTPBasicAuth
+
+ try:
+ mgr_response = requests.get(
+ f"{base_url}/redfish/v1/Managers/1/",
+ auth=HTTPBasicAuth(username, password),
+ verify=False,
+ timeout=30,
+ )
+ if mgr_response.status_code == 200:
+ mgr_data = mgr_response.json()
+ model = mgr_data.get("Model", "")
+ firmware_version = mgr_data.get("FirmwareVersion", "")
+ return model, firmware_version
+ elif mgr_response.status_code == 401:
+ LOGGER.warning("Authentication failed while retrieving iLO model and version")
+ raise Exception("Authentication failed")
+ else:
+ return "", ""
+ except Exception as e:
+ LOGGER.warning(f"Failed to get iLO model and version: {str(e)}")
+ raise e
+
+ def _check_dns_configuration(self, session, base_url, network_settings, individual_settings):
+ """Check if DNS is configured in iLO"""
+ try:
+ if individual_settings.get("skipDns", False):
+ return True
+
+ response = session.get(f"{base_url}/redfish/v1/Managers/1/EthernetInterfaces/1", timeout=30)
+ if response.status_code == 200:
+ data = response.json()
+ oem_hpe = data.get("Oem", {}).get("Hpe", {})
+
+ ipv4_dns = oem_hpe.get("IPv4", {}).get("DNSServers", [])
+ ipv6_dns = oem_hpe.get("IPv6", {}).get("DNSServers", [])
+
+ dns_servers = [dns for dns in (ipv4_dns + ipv6_dns) if dns and dns.strip() and dns != "0.0.0.0"]
+
+ return len(dns_servers) > 0
+ except Exception as e:
+ LOGGER.warning(f"Failed to check DNS configuration: {str(e)}")
+
+ return False
+
+ def _check_ntp_configuration(self, session, base_url, network_settings, individual_settings):
+ """Check if NTP is configured in iLO"""
+ try:
+ if individual_settings.get("skipNtp", False):
+ return True
+
+ response = session.get(f"{base_url}/redfish/v1/Managers/1/DateTime/", timeout=30)
+ if response.status_code == 200:
+ data = response.json()
+ ntp_servers = data.get("NTPServers", [])
+ static_ntp = data.get("StaticNTPServers", [])
+
+ all_ntp = [ntp for ntp in (ntp_servers + static_ntp) if ntp and ntp.strip()]
+
+ return len(all_ntp) > 0
+ except Exception as e:
+ LOGGER.warning(f"Failed to check NTP configuration: {str(e)}")
+
+ return False
+
+ def _check_com_connection_status(self, session, base_url):
+ """Check if COM is already connected
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :returns: True if COM is connected or connection is unstable, False if not connected,
+ None if status check failed for other reasons
+ :rtype: bool or None
+ """
+ try:
+ response = session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if response.status_code != 200:
+ LOGGER.warning(f"Failed to get manager resource, status: {response.status_code}")
+ return False
+
+ manager_data = response.json()
+ cloud_connect = manager_data.get("Oem", {}).get("Hpe", {}).get("CloudConnect", {})
+ current_status = cloud_connect.get("CloudConnectStatus", "")
+
+ if current_status == "Connected":
+ LOGGER.info(f"COM is already connected for {base_url.split('//')[1]}")
+ return True
+ return False
+ except (ConnectionError, Timeout, RequestException) as e:
+ # Connection errors indicate unstable connection - treat as "already connected" to be safe
+ LOGGER.warning(f"Connection error while checking COM status: {str(e)}")
+ LOGGER.info(
+ "Treating as COM connected due to connection instability. Skipping configuration to avoid disruption."
+ )
+ return True # Assume connected to avoid disruption during unstable connection
+ except Exception as e:
+ LOGGER.warning(f"Error checking COM connection status: {str(e)}")
+ return None # Cannot determine status
+
+ def _configure_network_settings_requests(
+ self, session, base_url, network_settings, individual_settings, allow_ilo_reset
+ ):
+ """Configure network settings using requests session
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param network_settings: common network settings
+ :type network_settings: dict
+ :param individual_settings: individual iLO settings
+ :type individual_settings: dict
+ :param allow_ilo_reset: allow iLO reset if needed
+ :type allow_ilo_reset: bool
+ :returns: tuple of (ilo_was_reset flag, ntp_configured_needs_reset flag)
+ :rtype: tuple(bool, bool)
+ """
+ ilo_was_reset = False
+ ntp_configured_needs_reset = False
+ all_configs_successful = True # Track if all configurations succeed
+
+ try:
+ # Skip if both DNS and NTP are explicitly skipped
+ if individual_settings.get("skipDns", False) and individual_settings.get("skipNtp", False):
+ return ilo_was_reset, ntp_configured_needs_reset
+
+ # Check COM connection status before doing any patch operation
+ if self._check_com_connection_status(session, base_url):
+ LOGGER.info("COM is already connected. Skipping network configuration to avoid disruption.")
+ return ilo_was_reset, ntp_configured_needs_reset
+
+ # Merge network settings (individual overrides common)
+ merged_network = network_settings.copy() if isinstance(network_settings, dict) else {}
+ individual_network = individual_settings.get("network", {})
+ if individual_network and isinstance(individual_network, dict):
+ merged_network.update(individual_network)
+
+ # Configure DNS - if this fails, mark configs as unsuccessful
+ if not individual_settings.get("skipDns", False) and merged_network.get("dns"):
+ dns_servers = merged_network["dns"]
+ try:
+ self._configure_dns(session, base_url, dns_servers)
+ except Exception as dns_err:
+ all_configs_successful = False
+ LOGGER.error(f"DNS configuration failed, will not reset iLO: {str(dns_err)}")
+ raise # Re-raise to stop processing
+
+ # Configure NTP and capture if iLO was reset
+ # Only allow iLO reset if all previous configurations succeeded
+ if not individual_settings.get("skipNtp", False) and merged_network.get("ntp"):
+ ntp_servers = merged_network["ntp"]
+ # Pass allow_ilo_reset only if all configs successful so far
+ effective_allow_reset = allow_ilo_reset and all_configs_successful
+ if not effective_allow_reset and allow_ilo_reset:
+ LOGGER.warning("iLO reset disabled due to configuration errors, even though allow_ilo_reset=True")
+
+ try:
+ ilo_was_reset, is_ntp_config_matching = self._configure_ntp(
+ session, base_url, ntp_servers, effective_allow_reset
+ )
+ except Exception as ntp_err:
+ all_configs_successful = False
+ LOGGER.error(f"NTP configuration failed, will not reset iLO: {str(ntp_err)}")
+ raise # Re-raise to stop processing
+
+ LOGGER.info(f"is_ntp_config_matching {is_ntp_config_matching}")
+ if is_ntp_config_matching:
+ return ilo_was_reset, ntp_configured_needs_reset
+
+ # If allow_ilo_reset is False and NTP was configured, user must manually reset iLO
+ if not allow_ilo_reset and not ilo_was_reset:
+ ntp_configured_needs_reset = True
+ LOGGER.warning("=" * 80)
+ LOGGER.warning("NTP CONFIGURATION REQUIRES MANUAL iLO RESET")
+ LOGGER.warning("=" * 80)
+ LOGGER.warning("NTP configuration has been applied, but --allow_ilo_reset was not specified.")
+ LOGGER.warning("Please manually reset the iLO for NTP changes to take effect,")
+ LOGGER.warning("then retry the onboarding command.")
+ LOGGER.warning("=" * 80)
+
+ except Exception as e:
+ error_msg = f"Failed to configure network settings: {str(e)}"
+ LOGGER.error(error_msg)
+ raise
+
+ return ilo_was_reset, ntp_configured_needs_reset
+
+ def _configure_dns(self, session, base_url, dns_servers):
+ """Configure DNS settings for the iLO with IPv4 and IPv6 support.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param dns_servers: list of DNS servers (IPv4 and/or IPv6)
+ :type dns_servers: list
+ """
+ try:
+ # First, check if DNS servers are already configured as desired
+ current_dns_servers, _, _ = self._get_current_dns_servers(session, base_url)
+
+ if current_dns_servers is not None:
+ # Normalize both lists for comparison (remove empty strings, sort)
+ desired_dns = sorted([srv.strip() for srv in dns_servers if srv and srv.strip()])
+ current_dns = sorted([srv.strip() for srv in current_dns_servers if srv and srv.strip()])
+
+ if desired_dns == current_dns:
+ LOGGER.info(f"DNS already configured: {desired_dns}")
+ return
+ else:
+ LOGGER.info(f"Updating DNS from {current_dns} to {desired_dns}")
+ else:
+ LOGGER.info("Unable to retrieve current DNS configuration. Proceeding with DNS configuration.")
+
+ # Separate IPv4 and IPv6 DNS servers
+ ipv4_dns_servers = []
+ ipv6_dns_servers = []
+
+ # Optimize: Strip once and reuse the cleaned value
+ for dns_server in dns_servers:
+ if dns_server:
+ dns_clean = dns_server.strip()
+ if dns_clean:
+ if self._is_ipv6_address(dns_clean):
+ ipv6_dns_servers.append(dns_clean)
+ else:
+ ipv4_dns_servers.append(dns_clean)
+
+ # Use EthernetInterfaces endpoint to configure DNS properly
+ dns_config_url = f"{base_url}/redfish/v1/Managers/1/EthernetInterfaces/1"
+
+ # Build payload to disable DHCP for DNS servers and set static DNS for both IPv4 and IPv6
+ payload = {
+ "DHCPv4": {"UseDNSServers": False},
+ "DHCPv6": {"UseDNSServers": False},
+ "Oem": {"Hpe": {"DHCPv4": {"UseDNSServers": False}, "DHCPv6": {"UseDNSServers": False}}},
+ }
+
+ # Add IPv4 DNS servers if present
+ if ipv4_dns_servers:
+ payload["Oem"]["Hpe"]["IPv4"] = {"DNSServers": ipv4_dns_servers}
+
+ # Add IPv6 DNS servers if present
+ if ipv6_dns_servers:
+ payload["Oem"]["Hpe"]["IPv6"] = {"DNSServers": ipv6_dns_servers}
+
+ # Create fresh session for DNS configuration to avoid connection reuse issues
+ dns_session = requests.Session()
+ dns_session.auth = session.auth
+ dns_session.verify = False
+
+ LOGGER.info(f"Configuring DNS - IPv4: {ipv4_dns_servers}, IPv6: {ipv6_dns_servers}")
+
+ response = dns_session.patch(dns_config_url, json=payload, timeout=30)
+
+ if response.status_code not in [200, 204]:
+ raise Exception(f"Failed to configure DNS. HTTP {response.status_code}: {response.text}")
+
+ LOGGER.info("DNS configuration successful")
+ except (ConnectionError, Timeout, RequestException) as e:
+ error_msg = f"Connection error while configuring DNS: {str(e)}"
+ LOGGER.error(error_msg)
+ raise Exception(error_msg)
+ except Exception as e:
+ error_msg = f"Error configuring DNS: {str(e)}"
+ LOGGER.error(error_msg)
+ raise
+
+ def _is_ipv6_address(self, address):
+ """Check if the given address is an IPv6 address.
+
+ :param address: IP address string
+ :type address: str
+ :returns: True if IPv6, False otherwise
+ :rtype: bool
+ """
+ try:
+ from ipaddress import IPv6Address
+
+ IPv6Address(address)
+ return True
+ except (ValueError, Exception):
+ return False
+
+ def _get_current_dns_servers(self, session, base_url):
+ """Get current DNS server configuration from iLO (both IPv4 and IPv6).
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :returns: list of currently configured DNS servers (IPv4 and IPv6), or None if unable to retrieve
+ :rtype: list or None
+ """
+ dhcp_dns_enabled = False
+ dhcp_check_attempted = False
+ try:
+ dns_config_url = f"{base_url}/redfish/v1/Managers/1/EthernetInterfaces/1"
+
+ # Reuse existing session instead of creating a new one
+ response = session.get(dns_config_url, timeout=30)
+ if response.status_code == 200:
+ data = response.json()
+
+ dhcp_check_attempted = True
+ dhcpv4_dns_enabled = data.get("DHCPv4", {}).get("UseDNSServers", False)
+ dhcpv6_dns_enabled = data.get("DHCPv6", {}).get("UseDNSServers", False)
+
+ oem_hpe = data.get("Oem", {}).get("Hpe", {})
+
+ oem_dhcpv4_dns_enabled = oem_hpe.get("DHCPv4", {}).get("UseDNSServers", False)
+ oem_dhcpv6_dns_enabled = oem_hpe.get("DHCPv6", {}).get("UseDNSServers", False)
+ if any([dhcpv4_dns_enabled, dhcpv6_dns_enabled, oem_dhcpv4_dns_enabled, oem_dhcpv6_dns_enabled]):
+ LOGGER.info("DHCP DNS is enabled; static DNS servers may not be in use.")
+ dhcp_dns_enabled = True
+
+ # Get both IPv4 and IPv6 DNS servers
+ ipv4_dns_servers = oem_hpe.get("IPv4", {}).get("DNSServers", [])
+ ipv6_dns_servers = oem_hpe.get("IPv6", {}).get("DNSServers", [])
+
+ # Combine and filter using list comprehension (more efficient)
+ current_dns_servers = [srv for srv in ipv4_dns_servers + ipv6_dns_servers if srv]
+
+ formatted_dns_servers = [
+ srv.strip() for srv in current_dns_servers if srv and srv.strip() not in DNS_SERVER_EXCLUSION_LIST
+ ]
+
+ return formatted_dns_servers, dhcp_dns_enabled, dhcp_check_attempted
+ else:
+ LOGGER.warning(f"Failed to get current DNS configuration. HTTP {response.status_code}")
+ return None, dhcp_dns_enabled, dhcp_check_attempted
+ except (ConnectionError, Timeout, RequestException) as e:
+ LOGGER.warning(f"Connection error while reading DNS configuration: {str(e)}")
+ return None, dhcp_dns_enabled, dhcp_check_attempted
+ except Exception as e:
+ LOGGER.warning(f"Error reading DNS configuration: {str(e)}")
+ return None, dhcp_dns_enabled, dhcp_check_attempted
+
+ def _configure_ntp(self, session, base_url, ntp_servers, allow_ilo_reset):
+ """Configure NTP settings for the iLO.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param ntp_servers: list of NTP servers
+ :type ntp_servers: list
+ :param allow_ilo_reset: allow iLO reset if needed
+ :type allow_ilo_reset: bool
+ :returns: tuple of (ilo_was_reset flag, is_config_matching flag)
+ :rtype: tuple(bool, bool)
+ """
+ ilo_was_reset = False
+ is_ntp_config_matching = False
+ dhcp_disabled_successfully = False
+
+ LOGGER.info(f"Starting NTP configuration for {base_url.split('//')[1]}")
+
+ try:
+ # First, check if DHCP NTP is enabled - if it is, static NTP list is irrelevant
+ dhcp_ntp_is_enabled = False
+ dhcp_check_attempted = False
+
+ eth_interfaces_url = f"{base_url}/redfish/v1/Managers/1/EthernetInterfaces"
+
+ dhcp_ntp_is_enabled, dhcp_disabled_successfully, dhcp_check_attempted = self._check_dhcp_ntp_config_on_interfaces(
+ session,
+ base_url,
+ eth_interfaces_url,
+ )
+
+ if dhcp_check_attempted and not dhcp_ntp_is_enabled:
+ LOGGER.info("DHCP NTP is already disabled on all ethernet interfaces")
+ current_ntp_servers = self._get_current_ntp_servers(session, base_url)
+ ntp_ips = [ntp_ip.strip() for ntp_ip in ntp_servers if ntp_ip and ntp_ip.strip()]
+ if sorted(current_ntp_servers) == sorted(ntp_ips):
+ LOGGER.info("NTP servers are already configured as desired")
+ is_ntp_config_matching = True
+ return ilo_was_reset, is_ntp_config_matching
+
+ # If we couldn't check any interface due to errors
+ if not dhcp_check_attempted:
+ LOGGER.warning("Could not check DHCP NTP status - will attempt to disable")
+
+ # Always apply NTP configuration regardless of current state
+ is_ntp_config_matching = False
+
+ # Disable DHCP NTP if it's enabled
+ if dhcp_ntp_is_enabled:
+ LOGGER.info("Disabling DHCP NTP on ethernet interfaces")
+
+ # Try to disable DHCP on ethernet interfaces
+ try:
+ eth_response = session.get(eth_interfaces_url, timeout=30)
+ if eth_response.status_code == 200:
+ eth_data = eth_response.json()
+ members = eth_data.get("Members", [])
+
+ for member in members:
+ interface_url = base_url + member.get("@odata.id", "")
+ try:
+ interface_response = session.get(interface_url, timeout=30)
+ if interface_response.status_code == 200:
+ interface_data = interface_response.json()
+ if interface_data.get("Status", {}).get("State") == "Enabled":
+ dhcp_ntp_payload = {
+ "DHCPv4": {"UseNTPServers": False},
+ "DHCPv6": {"UseNTPServers": False},
+ "Oem": {
+ "Hpe": {
+ "DHCPv4": {"UseNTPServers": False},
+ "DHCPv6": {"UseNTPServers": False},
+ }
+ },
+ }
+
+ dhcp_response = session.patch(interface_url, json=dhcp_ntp_payload, timeout=30)
+ if dhcp_response.status_code in [200, 204]:
+ LOGGER.info("Successfully disabled DHCP NTP")
+ dhcp_disabled_successfully = True
+ time.sleep(3)
+ break
+ except Exception:
+ continue
+
+ if not dhcp_disabled_successfully:
+ LOGGER.warning("Could not disable DHCP NTP via ethernet interfaces")
+ except Exception:
+ pass
+ else:
+ dhcp_disabled_successfully = True
+
+ # Configure static NTP servers
+ if not is_ntp_config_matching:
+ # If DHCP wasn't disabled via ethernet interfaces, try NetworkProtocol
+ if not dhcp_disabled_successfully:
+ try:
+ network_protocol_url = f"{base_url}/redfish/v1/Managers/1/NetworkProtocol"
+ network_payload = {
+ "NTP": {"ProtocolEnabled": True},
+ "Oem": {"Hpe": {"ConfigurationSettings": "Current"}},
+ }
+
+ network_response = session.patch(network_protocol_url, json=network_payload, timeout=30)
+
+ if network_response.status_code in [200, 204]:
+ LOGGER.info("Successfully configured NetworkProtocol for static NTP")
+ dhcp_disabled_successfully = True
+ time.sleep(3)
+ except Exception:
+ pass
+
+ # If still not successful after all attempts, raise error and stop
+ if not dhcp_disabled_successfully:
+ error_msg = (
+ "Failed to disable DHCP NTP. Cannot apply static NTP configuration while DHCP NTP is enabled."
+ )
+ LOGGER.error(error_msg)
+ raise Exception(error_msg)
+
+ # Apply static NTP servers
+ ntp_datetime_url = f"{base_url}/redfish/v1/Managers/1/DateTime"
+ ntp_server_list = list(ntp_servers)
+
+ if len(ntp_server_list) == 1:
+ ntp_server_list.append("")
+
+ payload = {"StaticNTPServers": ntp_server_list}
+ LOGGER.info(f"Configuring static NTP servers: {ntp_servers}")
+
+ try:
+ response = session.patch(ntp_datetime_url, json=payload, timeout=30)
+
+ if response.status_code not in [200, 204]:
+ error_msg = f"Failed to configure NTP. HTTP {response.status_code}: {response.text}"
+ LOGGER.error(error_msg)
+ raise Exception(error_msg)
+ except (ConnectionError, Timeout, RequestException) as conn_err:
+ LOGGER.warning(f"Connection error during NTP configuration: {str(conn_err)}")
+ LOGGER.info("NTP configuration may have been applied despite connection error")
+ is_ntp_config_matching = False
+
+ # NTP configuration may require iLO reset to take effect
+ # Only reset if allow_ilo_reset is True
+ if allow_ilo_reset:
+ LOGGER.info("Initiating iLO reset for NTP configuration")
+ reset_url = f"{base_url}/redfish/v1/Managers/1/Actions/Manager.Reset"
+ reset_payload = {"ResetType": "GracefulRestart"}
+
+ try:
+ reset_response = session.post(reset_url, json=reset_payload, timeout=30)
+
+ if reset_response.status_code in [200, 202, 204]:
+ LOGGER.info("iLO reset initiated successfully")
+ ilo_was_reset = True
+ time.sleep(30)
+ else:
+ LOGGER.warning(f"Failed to reset iLO - HTTP {reset_response.status_code}")
+ except (ConnectionError, Timeout, RequestException):
+ LOGGER.info("iLO appears to be resetting (connection closed)")
+ ilo_was_reset = True
+ time.sleep(30)
+ else:
+ LOGGER.info("NTP configured but iLO reset not allowed - manual reset may be required")
+
+ except (ConnectionError, Timeout, RequestException) as e:
+ LOGGER.error(f"Connection error during NTP configuration: {str(e)}")
+ except Exception as e:
+ LOGGER.error(f"Error configuring NTP: {str(e)}")
+
+ LOGGER.info(f"NTP configuration completed - reset={ilo_was_reset}, matching={is_ntp_config_matching}")
+ return ilo_was_reset, is_ntp_config_matching
+
+ def _check_dhcp_ntp_config_on_interfaces(self, session, base_url, eth_interfaces_url):
+ """Check NTP configuration status on ethernet interfaces.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param members: list of ethernet interface members
+ :type members: list
+ :returns: tuple of (dhcp_ntp_is_enabled, dhcp_disabled_successfully, dhcp_check_attempted)
+ :rtype: tuple(bool, bool, bool)
+ """
+ dhcp_ntp_is_enabled = False
+ dhcp_disabled_successfully = False
+ dhcp_check_attempted = False
+
+ members = []
+ try:
+ eth_response = session.get(eth_interfaces_url, timeout=30)
+
+ if eth_response.status_code == 200:
+ eth_data = eth_response.json()
+ members = eth_data.get("Members", [])
+ except (ConnectionError, Timeout, RequestException) as eth_err:
+ LOGGER.warning(f"Error accessing ethernet interfaces: {str(eth_err)}")
+ return dhcp_ntp_is_enabled, dhcp_disabled_successfully, dhcp_check_attempted
+
+ # Find the first enabled interface to check/disable DHCP NTP
+ for idx, member in enumerate(members):
+ interface_url = base_url + member.get("@odata.id", "")
+
+ try:
+ interface_response = session.get(interface_url, timeout=30)
+
+ if interface_response.status_code == 200:
+ interface_data = interface_response.json()
+ interface_state = interface_data.get("Status", {}).get("State")
+
+ if interface_state == "Enabled":
+ dhcp_check_attempted = True
+
+ dhcpv4_ntp_enabled = interface_data.get("DHCPv4", {}).get("UseNTPServers", False)
+ dhcpv6_ntp_enabled = interface_data.get("DHCPv6", {}).get("UseNTPServers", False)
+ oem_dhcpv4_ntp = (
+ interface_data.get("Oem", {})
+ .get("Hpe", {})
+ .get("DHCPv4", {})
+ .get("UseNTPServers", False)
+ )
+ oem_dhcpv6_ntp = (
+ interface_data.get("Oem", {})
+ .get("Hpe", {})
+ .get("DHCPv6", {})
+ .get("UseNTPServers", False)
+ )
+
+ if any([dhcpv4_ntp_enabled, dhcpv6_ntp_enabled, oem_dhcpv4_ntp, oem_dhcpv6_ntp]):
+ # DHCP NTP is enabled
+ dhcp_ntp_is_enabled = True
+ LOGGER.info("DHCP NTP is enabled, will disable and configure static NTP")
+ else:
+ LOGGER.info("DHCP NTP already disabled")
+ dhcp_disabled_successfully = True
+ break
+ except (ConnectionError, Timeout, RequestException) as iface_err:
+ # Connection errors during interface access - implement retry with new session
+ LOGGER.warning(f"Connection error accessing interface: {type(iface_err).__name__}")
+
+ # For RemoteDisconnected errors, retry once with a fresh session
+ if "RemoteDisconnected" in str(type(iface_err)) or "Connection aborted" in str(iface_err):
+ retry_session = None
+ try:
+ time.sleep(2)
+ retry_session = requests.Session()
+ retry_session.auth = session.auth
+ retry_session.verify = False
+ retry_response = retry_session.get(interface_url, timeout=30)
+ if retry_response.status_code == 200:
+ interface_data = retry_response.json()
+ interface_state = interface_data.get("Status", {}).get("State")
+
+ if interface_state == "Enabled":
+ dhcp_check_attempted = True
+ dhcpv4_ntp_enabled = interface_data.get("DHCPv4", {}).get(
+ "UseNTPServers", False
+ )
+ dhcpv6_ntp_enabled = interface_data.get("DHCPv6", {}).get(
+ "UseNTPServers", False
+ )
+ oem_dhcpv4_ntp = (
+ interface_data.get("Oem", {})
+ .get("Hpe", {})
+ .get("DHCPv4", {})
+ .get("UseNTPServers", False)
+ )
+ oem_dhcpv6_ntp = (
+ interface_data.get("Oem", {})
+ .get("Hpe", {})
+ .get("DHCPv6", {})
+ .get("UseNTPServers", False)
+ )
+
+ if any(
+ [dhcpv4_ntp_enabled, dhcpv6_ntp_enabled, oem_dhcpv4_ntp, oem_dhcpv6_ntp]
+ ):
+ dhcp_ntp_is_enabled = True
+ LOGGER.info("DHCP NTP enabled (retry check)")
+ else:
+ dhcp_disabled_successfully = True
+ break
+ except Exception:
+ pass
+ finally:
+ if retry_session:
+ retry_session.close()
+ continue
+
+ return dhcp_ntp_is_enabled, dhcp_disabled_successfully, dhcp_check_attempted
+
+ def _get_current_ntp_servers(self, session, base_url):
+ """Get current NTP server configuration from iLO.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :returns: list of currently configured NTP servers, or None if unable to retrieve
+ :rtype: list or None
+ """
+ try:
+ ntp_datetime_url = f"{base_url}/redfish/v1/Managers/1/DateTime"
+ response = session.get(ntp_datetime_url, timeout=30)
+
+ if response.status_code == 200:
+ data = response.json()
+ current_ntp_servers = data.get("StaticNTPServers", [])
+ # Filter out empty strings and None values using list comprehension
+ current_ntp_servers = [srv for srv in current_ntp_servers if srv]
+ return current_ntp_servers
+ else:
+ LOGGER.warning(f"Failed to get NTP configuration - HTTP {response.status_code}")
+ return None
+ except (ConnectionError, Timeout, RequestException) as e:
+ LOGGER.warning(f"Connection error reading NTP configuration: {str(e)}")
+ return None
+ except Exception as e:
+ LOGGER.warning(f"Error reading NTP configuration: {str(e)}")
+ return None
+
+ def _get_current_proxy_settings(self, session, base_url):
+ """Get current proxy configuration from iLO.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :returns: dict with current proxy settings, or None if unable to retrieve
+ :rtype: dict or None
+ """
+ try:
+ proxy_config_url = f"{base_url}/redfish/v1/Managers/1/NetworkProtocol/"
+
+ # Reuse existing session instead of creating a new one
+ response = session.get(proxy_config_url, timeout=30)
+ if response.status_code == 200:
+ data = response.json()
+ web_proxy = data.get("Oem", {}).get("Hpe", {}).get("WebProxyConfiguration", {})
+
+ # Get raw values
+ raw_server = web_proxy.get("ProxyServer", "")
+ raw_port = web_proxy.get("ProxyPort")
+ raw_username = web_proxy.get("ProxyUserName", "")
+
+ # Normalize all values: treat empty strings and None as "no proxy"
+ # Use 'is not None' check for port to correctly handle port 0
+ current_proxy = {
+ "server": raw_server.strip() if raw_server else "",
+ "port": raw_port if raw_port is not None else None,
+ "username": raw_username.strip() if raw_username else "",
+ }
+
+ return current_proxy
+ else:
+ LOGGER.warning(f"Failed to get current proxy configuration. HTTP {response.status_code}")
+ return None
+ except (ConnectionError, Timeout, RequestException) as e:
+ LOGGER.warning(f"Connection error while reading proxy configuration: {str(e)}")
+ return None
+ except Exception as e:
+ LOGGER.warning(f"Error reading proxy configuration: {str(e)}")
+ return None
+
+ def _configure_proxy_for_ilo_requests(self, session, base_url, proxy_settings, individual_settings=None):
+ """Configure proxy settings using requests session
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param proxy_settings: proxy settings
+ :type proxy_settings: dict
+ :param individual_settings: individual iLO settings
+ :type individual_settings: dict or None
+ """
+ try:
+ # Check if proxy configuration should be skipped
+ if individual_settings and individual_settings.get("skipProxy", False):
+ LOGGER.info("Proxy configuration skipped per individual settings.")
+ return
+
+ # Check COM connection status before doing any patch operation
+ if self._check_com_connection_status(session, base_url):
+ LOGGER.info("COM is already connected. Skipping proxy configuration to avoid disruption.")
+ return
+
+ individual_proxy = individual_settings.get("proxy", {})
+ # Only configure if proxy_settings exists and has meaningful content
+ # Check if there's actually a proxy server to configure
+ if not proxy_settings and not individual_proxy:
+ LOGGER.info("No proxy settings provided. Skipping proxy configuration.")
+ return
+
+ # Apply proxy from individual or range in if present over common
+ # Else proxy from common settings
+ proxy_to_configure = individual_proxy if individual_proxy else proxy_settings
+
+ # Check if server is provided and not empty
+ proxy_server = proxy_to_configure.get("server", "").strip() if proxy_to_configure.get("server") else ""
+ if not proxy_server:
+ LOGGER.info("No proxy server specified. Skipping proxy configuration.")
+ return
+
+ # At this point, we have valid proxy settings to configure
+ self._configure_proxy(session, base_url, proxy_to_configure)
+ # Give iLO time to process proxy changes
+ time.sleep(2)
+ except Exception as e:
+ error_msg = f"Failed to configure proxy settings: {str(e)}"
+ LOGGER.error(error_msg)
+ raise
+
+ def _configure_proxy(self, session, base_url, proxy_settings):
+ """Configure proxy settings for the iLO.
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param proxy_settings: proxy settings
+ :type proxy_settings: dict
+ """
+ try:
+ # First, check if proxy settings are already configured as desired
+ current_proxy = self._get_current_proxy_settings(session, base_url)
+
+ if current_proxy is not None:
+ # Normalize desired proxy settings for comparison
+ # Treat empty strings and None as "no proxy configured"
+ desired_server = proxy_settings.get("server", "").strip() if proxy_settings.get("server") else ""
+ desired_port = proxy_settings.get("port") if proxy_settings.get("port") else None
+ desired_credentials = proxy_settings.get("credentials", {})
+ # Normalize port to int for comparison if not None
+ if desired_port is not None:
+ try:
+ desired_port = int(desired_port)
+ except (ValueError, TypeError):
+ desired_port = None
+ desired_username = desired_credentials.get("username", "").strip() if desired_credentials else ""
+
+ # Normalize current proxy settings (already normalized in _get_current_proxy_settings)
+ current_server = current_proxy.get("server", "")
+ current_port = current_proxy.get("port")
+ # Ensure current port is also int or None for consistent comparison
+ if current_port is not None:
+ try:
+ current_port = int(current_port)
+ except (ValueError, TypeError):
+ current_port = None
+ current_username = current_proxy.get("username", "")
+
+ # Detect "no proxy" scenarios:
+ # iLO has no proxy if: server is empty string AND port is None
+ current_has_no_proxy = not current_server and current_port is None
+ desired_has_no_proxy = not desired_server and desired_port is None
+
+ if current_has_no_proxy and desired_has_no_proxy:
+ LOGGER.info("Proxy not configured - skipping")
+ return
+ elif (
+ desired_server == current_server
+ and desired_port == current_port
+ and desired_username == current_username
+ ):
+ LOGGER.info(f"Proxy already configured: {desired_server}:{desired_port}")
+ return
+ else:
+ LOGGER.info(f"Updating proxy configuration to {desired_server}:{desired_port}")
+ else:
+ LOGGER.info("Unable to retrieve current proxy configuration. Proceeding with proxy configuration.")
+
+ proxy_config_url = f"{base_url}/redfish/v1/Managers/1/NetworkProtocol/"
+
+ # Build proxy configuration payload
+ web_proxy_config = {
+ "ProxyServer": proxy_settings.get("server", ""),
+ "ProxyPort": proxy_settings.get("port"),
+ "ProxyUserName": "",
+ "ProxyPassword": "",
+ }
+
+ # Only add username and password if they are provided (not None and not empty)
+ username = desired_credentials.get("username", "").strip() if desired_credentials else ""
+ password = desired_credentials.get("password", "").strip() if desired_credentials else ""
+
+ if username:
+ web_proxy_config["ProxyUserName"] = username
+ if password:
+ web_proxy_config["ProxyPassword"] = password
+
+ payload = {"Oem": {"Hpe": {"WebProxyConfiguration": web_proxy_config}}}
+
+ response = session.patch(proxy_config_url, json=payload, timeout=30)
+
+ if response.status_code not in [200, 204]:
+ raise Exception(f"Failed to configure proxy. HTTP {response.status_code}: {response.text}")
+
+ LOGGER.info("Proxy configured successfully")
+ except Exception as e:
+ error_msg = f"Error configuring proxy: {str(e)}"
+ LOGGER.error(error_msg)
+ raise
+
+ def _enable_com_connection_requests(self, session, base_url, activation_key):
+ """Enable COM connection using requests session
+
+ :param session: requests Session object
+ :type session: requests.Session
+ :param base_url: base URL for iLO
+ :type base_url: str
+ :param activation_key: COM activation key or PIN
+ :type activation_key: str
+ :returns: tuple of (success, fail_reason) where success is True/False and fail_reason is string or None
+ :rtype: tuple
+ """
+
+ try:
+ # Get manager resource to check COM capabilities with timeout
+ response = session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if response.status_code != 200:
+ LOGGER.warning(f"Failed to get manager resource - HTTP {response.status_code}")
+ return False, f"Failed to get manager resource: HTTP {response.status_code}"
+
+ manager_data = response.json()
+
+ # Check if COM is already connected
+ cloud_connect = manager_data.get("Oem", {}).get("Hpe", {}).get("CloudConnect", {})
+ current_status = cloud_connect.get("CloudConnectStatus", "")
+
+ if current_status == "Connected":
+ LOGGER.info("iLO is already connected")
+ return True, None
+
+ # Check if COM connect actions are available
+ oem_actions = manager_data.get("Oem", {}).get("Hpe", {}).get("Actions", {})
+ if "#HpeiLO.EnableCloudConnect" not in oem_actions:
+ LOGGER.warning("COM feature not available on this iLO")
+ return False, "COM feature not available"
+
+ # Enable cloud connect with proper error handling and retry logic
+ action_uri = f"{base_url}/redfish/v1/Managers/1/Actions/Oem/Hpe/HpeiLO.EnableCloudConnect/"
+ body = {"ActivationKey": activation_key} if activation_key else {}
+
+ LOGGER.info("Enabling COM connection")
+
+ # Create a new session for the POST request to avoid connection reuse issues
+ post_session = requests.Session()
+ post_session.auth = session.auth
+ post_session.verify = False
+
+ success = False
+ retry_count = 0
+ max_retries = 3
+
+ while retry_count <= max_retries and not success:
+ try:
+ timeout_value = 90 + (retry_count * 30) # Increase timeout with retries
+ response = post_session.post(action_uri, json=body, timeout=timeout_value)
+
+ if response.status_code in [200, 202]:
+ success = True
+ LOGGER.info("COM enable request successful")
+ else:
+ LOGGER.warning(f"COM enable failed - HTTP {response.status_code}")
+ if retry_count < max_retries:
+ retry_count += 1
+ time.sleep(5)
+ else:
+ return False, "COM enable request failed"
+
+ except (ConnectionError, Timeout, RequestException) as conn_err:
+ LOGGER.warning(f"Connection issue during COM enable: {str(conn_err)}")
+
+ # Give iLO time to process the request
+ time.sleep(10)
+
+ # Try to check if the request was actually accepted by querying status
+ try:
+ status_response = session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if status_response.status_code == 200:
+ mgr_data = status_response.json()
+ cc_status = (
+ mgr_data.get("Oem", {})
+ .get("Hpe", {})
+ .get("CloudConnect", {})
+ .get("CloudConnectStatus", "")
+ )
+ if cc_status in ["Initializing", "ConnectionInProgress", "Connected"]:
+ LOGGER.info(f"COM enable appears successful (status: {cc_status})")
+ success = True
+ break
+ except Exception:
+ pass
+
+ if retry_count < max_retries:
+ retry_count += 1
+ time.sleep(15)
+ else:
+ LOGGER.warning("Connection errors occurred, will check status to confirm")
+ success = True # Proceed to status checking
+ break
+
+ if not success:
+ return False, "COM enable request failed"
+
+ # Wait for connection to complete with improved status checking
+ max_wait = 480 # Increased from 180s (3 min) to 480s (8 min)
+ wait_interval = 12
+ waited = 0
+
+ while waited < max_wait:
+ time.sleep(wait_interval)
+ waited += wait_interval
+
+ try:
+ status_response = session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if status_response.status_code == 200:
+ manager_data = status_response.json()
+ cloud_connect = manager_data.get("Oem", {}).get("Hpe", {}).get("CloudConnect", {})
+ current_status = cloud_connect.get("CloudConnectStatus", "")
+
+ if current_status == "Connected":
+ LOGGER.info("COM connection successful")
+ return True, None
+ elif current_status == "Initializing":
+ LOGGER.info("COM connection is initializing, waiting...")
+ continue # Keep waiting
+ elif current_status == "ConnectionFailed":
+ LOGGER.warning(f"COM connect terminal Status: {current_status}")
+ break
+ elif current_status in ["NotConnected", "NotEnabled"]:
+ fail_reason = cloud_connect.get("FailReason", "Unknown error")
+
+ LOGGER.warning(f"COM Current Status: {current_status}")
+ LOGGER.warning("Continuing to wait ...")
+ continue # Keep waiting
+ # Add other terminal states if needed
+ else:
+ LOGGER.warning(
+ f"Failed to get manager resource during status check - HTTP {status_response.status_code}"
+ )
+ except (ConnectionError, Timeout, RequestException):
+ LOGGER.warning("Connection error during COM status polling, will retry...")
+ continue
+ except Exception as check_err:
+ LOGGER.warning(f"Error during COM status polling: {str(check_err)}")
+ continue
+
+ status_response = session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if status_response.status_code == 200:
+ manager_data = status_response.json()
+ cloud_connect = manager_data.get("Oem", {}).get("Hpe", {}).get("CloudConnect", {})
+ current_status = cloud_connect.get("CloudConnectStatus", "")
+ if current_status == "Connected":
+ LOGGER.info("COM connection successful")
+ return True, None
+ else:
+ fail_reason = cloud_connect.get("FailReason", "Unknown error")
+ LOGGER.warning(f"COM connection failed - final status: {current_status}")
+ return False, f"COM connection failed: {fail_reason}"
+
+ except (ConnectionError, Timeout, RequestException) as e:
+ LOGGER.warning(f"Connection error during COM enable: {str(e)}")
+ # Try to verify if the connection actually succeeded
+ try:
+ verify_session = requests.Session()
+ verify_session.auth = session.auth
+ verify_session.verify = False
+ time.sleep(5)
+
+ response = verify_session.get(f"{base_url}/redfish/v1/Managers/1/", timeout=30)
+ if response.status_code == 200:
+ mgr_data = response.json()
+ cc_status = (
+ mgr_data.get("Oem", {}).get("Hpe", {}).get("CloudConnect", {}).get("CloudConnectStatus", "")
+ )
+ if cc_status in ["Initializing", "ConnectionInProgress", "Connected"]:
+ LOGGER.info(f"COM enable appears successful despite connection error (status: {cc_status})")
+ success = True
+ else:
+ return False, "Connection error"
+ else:
+ return False, "Connection error"
+ except Exception:
+ return False, "Connection error"
+
+ if not success:
+ return False, "Connection error"
+
+ except Exception as e:
+ LOGGER.error(f"Unexpected error enabling COM: {str(e)}")
+ return False, str(e)
+
+ def _save_json_report(self, report, output_file):
+ """Save report to JSON file with proper formatting
+
+ :param report: report data to save
+ :type report: dict
+ :param output_file: output filename
+ :type output_file: str
+ :returns: True if successful, False otherwise
+ :rtype: bool
+ """
+ try:
+ with open(output_file, "w") as f:
+ json.dump(report, f, indent=2, sort_keys=True)
+
+ LOGGER.info(f"Report successfully saved to {output_file}")
+
+ except Exception as e:
+ error_msg = f"Failed to save report to {output_file}: {str(e)}"
+ LOGGER.error(error_msg)
+ raise Exception(error_msg)
+
+ def run(self, line, help_disp=False):
+ """Wrapper function for cloudconnect main function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except Exception:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ # Skip validations for multiconnect as it handles multiple connections differently
+ if not (options.command and options.command.lower() == "multiconnect"):
+ self.cmdbase.login_select_validation(self, options)
+ if not self.rdmc.app.redfishinst:
+ raise NoCurrentSessionEstablished("Please login to iLO and retry the command")
+
+ ilo_ver = self.rdmc.app.getiloversion()
+ if ilo_ver < 5.247:
+ raise IncompatibleiLOVersionError(
+ "ComputeOpsManagement Feature is only available with iLO 5 version 2.47 or higher.\n"
+ )
+
+ # validation checks
+ self.cloudconnectvalidation(options)
+ if options.command:
+ if options.command.lower() == "connect":
+ if options.proxy:
+ self.proxy_config(options.proxy)
+ if options.activationkey:
+ self.connect_cloud(activationkey=options.activationkey)
+ elif not options.activationkey:
+ self.connect_cloud()
+ else:
+ raise InvalidCommandLineError(
+ "Activation Key %s is not alphanumeric or not of length 32." % str(options.activationkey)
+ )
+ elif options.command.lower() == "disconnect":
+ self.disconnect_cloud()
+ elif options.command.lower() == "status":
+ if options.json:
+ self.cloud_status(json=True)
+ else:
+ self.cloud_status()
+ elif options.command.lower() == "multiconnect":
+ # Handle template generation
+ if options.template_input_file is not None:
+ self.generate_template_file(options.template_input_file)
+ # Handle bulk operations
+ elif options.file:
+ if getattr(options, "precheck", False):
+ result = self.bulk_connect_precheck(options.file, options.output_file)
+ if result != ReturnCodes.SUCCESS:
+ return result
+ else:
+ result = self.bulk_connect(options.file, options.output_file, options.allow_ilo_reset)
+ if result != ReturnCodes.SUCCESS:
+ return result
+ else:
+ raise InvalidCommandLineError(
+ "For multiconnect command, either --input_file_json_template"
+ " or --input_file must be specified. "
+ "Use -h for help."
+ )
+ else:
+ raise InvalidCommandLineError("%s is not a valid option for this command." % str(options.command))
+ else:
+ raise InvalidCommandLineError(
+ "Please provide either connect, disconnect, status, or multiconnect as additional subcommand."
+ " For help or usage related information, use -h or --help"
+ )
+ # logout routine
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def cloudconnectvalidation(self, options):
+ """new command method validation function"""
+ # Check if Cloud Connect feature is enabled in iLO.
+ path = self.rdmc.app.typepath.defs.managerpath
+ resp = self.rdmc.app.get_handler(path, service=True, silent=True)
+ if resp.status == 200:
+ oem_actions = resp.dict["Oem"]["Hpe"]["Actions"]
+ # print(oem_actions)
+ if "#HpeiLO.EnableCloudConnect" not in oem_actions or "#HpeiLO.DisableCloudConnect" not in oem_actions:
+ raise CloudConnectFailedError("ComputeOpsManagement is disabled in this iLO.\n")
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ connect_help = "To connect to ComputeOpsManagement\n"
+ # connect sub-parser
+ connect_parser = subcommand_parser.add_parser(
+ "connect",
+ help=connect_help,
+ description=connect_help + "\n\tExample:\n\tcomputeopsmanagement connect or"
+ "\n\tcomputeopsmanagement connect --proxy http://proxy.abc.com:8080 or "
+ "\n\tcomputeopsmanagement connect --proxy None or "
+ "\n\tcomputeopsmanagement connect --activationkey 123456789EFGA or "
+ "\n\tcomputeopsmanagement connect --activationkey 123456789EFGA --proxy http://proxy.abc.com:8080 or "
+ "\n\tcomputeopsmanagement connect --activationkey 123456789EFGA --proxy None",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ connect_parser.add_argument(
+ "--activationkey",
+ dest="activationkey",
+ help="activation key is optional for connecting",
+ required=False,
+ type=str,
+ default=None,
+ )
+ connect_parser.add_argument(
+ "--proxy",
+ dest="proxy",
+ help="to set or clear proxy while connecting",
+ type=str,
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(connect_parser)
+ status_help = "To check the ComputeOpsManagement connection status\n"
+ status_parser = subcommand_parser.add_parser(
+ "status",
+ help=status_help,
+ description=status_help + "\n\tExample:\n\tcomputeopsmanagement status or "
+ "\n\tcomputeopsmanagement status -j",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ status_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ help="to print in json format",
+ action="store_true",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(status_parser)
+ disconnect_help = "To disconnect from ComputeOpsManagement\n"
+ disconnect_parser = subcommand_parser.add_parser(
+ "disconnect",
+ help=disconnect_help,
+ description=disconnect_help + "\n\tExample:\n\tcomputeopsmanagement disconnect",
+ )
+ self.cmdbase.add_login_arguments_group(disconnect_parser)
+
+ # Multiconnect subcommand
+ multiconnect_help = "Bulk connect multiple iLOs using JSON configuration"
+ multiconnect_parser = subcommand_parser.add_parser(
+ "multiconnect",
+ help=multiconnect_help,
+ description=multiconnect_help
+ + """
+ EXAMPLES:
+ # Generate template JSON file
+ computeopsmanagement multiconnect --input_file_json_template
+ # Generate precheck report without making applying changes
+ computeopsmanagement multiconnect --input_file servers.json --precheck
+ computeopsmanagement multiconnect --input_file servers.json --precheck --output report.json
+ # Onboard multiple servers from JSON file to COM
+ computeopsmanagement multiconnect --input_file servers.json
+ computeopsmanagement multiconnect --input_file servers.json --allow_ilo_reset
+ computeopsmanagement multiconnect --input_file servers.json --allow_ilo_reset --output report.json
+ """,
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+
+ multiconnect_parser.add_argument(
+ "--input_file",
+ "-i",
+ dest="file",
+ help="Path to JSON file containing bulk server configuration",
+ required=False,
+ type=str,
+ default=None,
+ )
+
+ multiconnect_parser.add_argument(
+ "--input_file_json_template",
+ "-t",
+ dest="template_input_file",
+ help=f"Generate template JSON input file (writes {DEFAULT_TEMPLATE_FILE_NAME})",
+ action="store_const",
+ const=DEFAULT_TEMPLATE_FILE_NAME,
+ default=None,
+ )
+
+ # Create mutually exclusive group for precheck and allow_ilo_reset
+ exclusive_group = multiconnect_parser.add_mutually_exclusive_group()
+
+ exclusive_group.add_argument(
+ "--allow_ilo_reset",
+ "-r",
+ dest="allow_ilo_reset",
+ help="If needed, allow iLO reset during bulk operations",
+ required=False,
+ action="store_true",
+ default=False,
+ )
+
+ multiconnect_parser.add_argument(
+ "--output",
+ "-of",
+ dest="output_file",
+ help="Output file for reports",
+ required=False,
+ type=str,
+ default=None,
+ )
+
+ exclusive_group.add_argument(
+ "--precheck",
+ "-c",
+ dest="precheck",
+ help="Perform precheck validation only (no onboarding or configuration)",
+ required=False,
+ action="store_true",
+ default=False,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DirectoryCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DirectoryCommand.py
new file mode 100644
index 0000000..a11018a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DirectoryCommand.py
@@ -0,0 +1,876 @@
+###
+# Copyright 2017 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Directory Command for rdmc"""
+
+import re
+import sys
+from argparse import Action, RawDescriptionHelpFormatter
+
+from redfish.ris.rmc_helper import IdTokenError, IloResponseError
+
+try:
+ from rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ )
+
+__subparsers__ = ["ldap", "kerberos", "test"]
+
+PRIVKEY = {
+ 1: ("Login", "AssignedPrivileges"),
+ 2: ("RemoteConsolePriv", "OemPrivileges"),
+ 3: ("ConfigureUsers", "AssignedPrivileges"),
+ 4: ("ConfigureManager", "AssignedPrivileges"),
+ 5: ("VirtualMediaPriv", "OemPrivileges"),
+ 6: ("VirtualPowerAndResetPriv", "OemPrivileges"),
+ 7: ("HostNICConfigPriv", "OemPrivileges"),
+ 8: ("HostBIOSConfigPriv", "OemPrivileges"),
+ 9: ("HostStorageConfigPriv", "OemPrivileges"),
+ 10: ("SystemRecoveryConfigPriv", "OemPrivileges"),
+ 11: ("ConfigureSelf", "AssignedPrivileges"),
+ 12: ("ConfigureComponents", "AssignedPrivileges"),
+}
+
+
+class _DirectoryParse(Action):
+ def __init__(self, option_strings, dest, nargs, **kwargs):
+ super(_DirectoryParse, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_strings):
+ """Helper for parsing options"""
+ if option_strings.endswith("disable"):
+ setattr(namespace, self.dest, False)
+ elif option_strings.endswith("enable"):
+ setattr(namespace, self.dest, True)
+ elif option_strings.endswith("enablelocalauth"):
+ setattr(namespace, self.dest, False)
+ elif option_strings.endswith("disablelocalauth"):
+ setattr(namespace, self.dest, True)
+ elif option_strings == "--removerolemap":
+ setattr(namespace, self.dest, {"remove": []})
+ for role in next(iter(values)).split("#"):
+ role = role.replace('"', "")
+ if role:
+ namespace.roles["remove"].append(role)
+ elif option_strings == "--addrolemap":
+ setattr(namespace, self.dest, {"add": []})
+ for role in next(iter(values)).split("#"):
+ role = role.replace('"', "")
+ if role and re.match(".*:.*", role):
+ privs = role.split(":")[0].split(";")
+ if len(privs) > 1:
+ for priv in privs:
+ try:
+ if priv and int(priv) > 12:
+ try:
+ parser.error("Invalid privilege number added %s." % priv)
+ except SystemExit:
+ raise InvalidCommandLineErrorOPTS("")
+ except ValueError:
+ try:
+ parser.error("Privileges must be added as numbers.")
+ except SystemExit:
+ raise InvalidCommandLineErrorOPTS("")
+ namespace.roles["add"].append(role)
+ else:
+ try:
+ parser.error("Supply roles to add in form :")
+ except SystemExit:
+ raise InvalidCommandLineErrorOPTS("")
+ elif option_strings == "--addsearch":
+ setattr(namespace, self.dest, {"add": []})
+ for search in next(iter(values)).split(";"):
+ if search:
+ namespace.search["add"].append(search)
+
+ elif option_strings == "--removesearch":
+ setattr(namespace, self.dest, {"remove": []})
+ for search in next(iter(values)).split(";"):
+ if search:
+ namespace.search["remove"].append(search)
+
+
+class DirectoryCommand:
+ """Update directory settings on the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "directory",
+ "usage": None,
+ "description": "\tAdd credentials, service address, two search strings, and enable"
+ "\n\tLDAP directory service, remote role groups (mapping), local custom role\n\t"
+ "IDs with privileges.\n\n\tTo view help on specific sub-commands"
+ " run: directory -h\n\n\tExample: directory ldap -h\n",
+ "summary": "Update directory settings, add/delete directory roles, and test directory "
+ "settings on the currently logged in server.",
+ "aliases": ["ad", "activedirectory"],
+ "auxcommands": ["IloAccountsCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main directory Function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ ident_subparser = False
+ for cmnd in __subparsers__:
+ if cmnd in line:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ ident_subparser = True
+ break
+ if not ident_subparser:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line, default=True)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.directoryvalidation(options)
+
+ if self.rdmc.app.getiloversion() < 5.140:
+ raise IncompatibleiLOVersionError("Directory settings are only available on " "iLO 5 1.40 or greater.")
+ results = None
+ if options.command.lower() == "ldap" or (
+ (True if options.ldap_kerberos == "ldap" else False) if hasattr(options, "ldap_kerberos") else False
+ ):
+ try:
+ results = self.rdmc.app.select(selector="AccountService.", path_refresh=True)[0].dict
+ path = results[self.rdmc.app.typepath.defs.hrefstring]
+ oem = results["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ local_auth = results["LocalAccountAuth"]
+ results = results["LDAP"]
+ if "RemoteRoleMapping" not in results:
+ rolemap = {"RemoteRoleMapping": {}}
+ results.update(rolemap)
+ name = "LDAP"
+ except (KeyError, IndexError):
+ raise NoContentsFoundForOperationError("Unable to gather LDAP settings.")
+
+ elif options.command.lower() == "kerberos" or (
+ (True if options.ldap_kerberos == "kerberos" else False) if hasattr(options, "ldap_kerberos") else False
+ ):
+ try:
+ results = self.rdmc.app.select(selector="AccountService.", path_refresh=True)[0].dict
+ path = results[self.rdmc.app.typepath.defs.hrefstring]
+ oem = results["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ local_auth = results["LocalAccountAuth"]
+ results = results["ActiveDirectory"]
+ if "RemoteRoleMapping" not in results:
+ rolemap = {"RemoteRoleMapping": {}}
+ results.update(rolemap)
+ name = "ActiveDirectory"
+ except (KeyError, IndexError):
+ raise NoContentsFoundForOperationError("Unable to gather Kerberos settings.")
+
+ if results:
+ keytab = None
+ payload = {}
+ if hasattr(options, "keytab"):
+ keytab = options.keytab
+ try:
+ directory_settings = self.directory_helper(results, options)
+ if "ldap" in line and options.roles and "add" in options.roles:
+ role_val = options.roles["add"]
+ for r in role_val:
+ if ";" in r.split(":")[0]:
+ name = "ActiveDirectory"
+ break
+ except IndexError:
+ directory_settings = self.directory_helper(results, options)
+
+ if directory_settings:
+ payload[name] = directory_settings
+
+ if hasattr(options, "authmode"):
+ if options.authmode:
+ payload.update(
+ {"Oem": {"Hpe": {"DirectorySettings": {"LdapAuthenticationMode": options.authmode}}}}
+ )
+
+ if not payload and not keytab:
+ if getattr(options, "json", False):
+ # self.rdmc.ui.print_out_json({name: results, 'LocalAccountAuth': local_auth,
+ # "Oem": {"Hpe": oem}})
+ text_content = self.print_s(results, oem, local_auth, name)
+ self.rdmc.ui.print_out_json(text_content)
+ else:
+ self.print_settings(results, oem, local_auth, name)
+
+ if payload:
+ priv_patches = {}
+ try:
+ if hasattr(options, "localauth"):
+ if options.localauth:
+ payload["LocalAccountAuth"] = "Enabled" if options.localauth else "Disabled"
+ elif local_auth:
+ payload["LocalAccountAuth"] = "Enabled" if local_auth else "Disabled"
+ except (NameError, AttributeError):
+ payload["LocalAccountAuth"] = "Disabled"
+ try:
+ maps = {}
+ if payload.get("LDAP"):
+ maps = payload["LDAP"].get("RemoteRoleMapping", {})
+ elif payload.get("ActiveDirectory"):
+ maps = payload["ActiveDirectory"].get("RemoteRoleMapping", {})
+ # Check if we need to modify roles after creating
+ for mapping in maps:
+ privs = mapping["LocalRole"].split(";")
+ if len(privs) > 1:
+ privs = [int(priv) for priv in privs if priv]
+
+ if 10 in privs:
+ user_privs = self.auxcommands["iloaccounts"].getsesprivs()
+ if "SystemRecoveryConfigPriv" not in list(user_privs.keys()):
+ raise IdTokenError(
+ "The currently logged in account "
+ "must have the System Recovery Config privilege to "
+ "add the System Recovery Config privilege to a local "
+ "role group."
+ )
+
+ priv_patches[mapping["RemoteGroup"]] = privs
+ mapping["LocalRole"] = "ReadOnly"
+ except Exception as excp:
+ self.rdmc.ui.error(excp)
+ self.rdmc.ui.printer("Changing settings...\n")
+ try:
+ self.rdmc.app.patch_handler(path, payload)
+ except IloResponseError as excp:
+ if not results["ServiceEnabled"]:
+ self.rdmc.ui.error(
+ "You must enable this directory service before or "
+ "during assignment of username and password. Try adding the flag "
+ "--enable.\n",
+ excp,
+ )
+ else:
+ raise IloResponseError
+ if priv_patches:
+ self.update_mapping_privs(priv_patches)
+ if keytab:
+ path = oem["Actions"][next(iter(oem["Actions"]))]["target"]
+ self.rdmc.ui.printer("Adding keytab...\n")
+ self.rdmc.app.post_handler(path, {"ImportUri": keytab})
+ elif options.command.lower() == "test":
+ self.test_directory(options, json=getattr(options, "json", False))
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def update_mapping_privs(self, roles_to_update):
+ """Helper function to update created role mappings to match user privileges.
+
+ :param roles_to_update: Dictionary of privileges to update.
+ :type roles_to_update: dict
+ """
+ self.rdmc.ui.printer("Updating privileges of created role maps...\n")
+ try:
+ results = self.rdmc.app.select(selector="AccountService.", path_refresh=True)[0].dict
+ roles = self.rdmc.app.getcollectionmembers(self.rdmc.app.getidbytype("RoleCollection.")[0])
+ except (KeyError, IndexError):
+ raise NoContentsFoundForOperationError(
+ "Unable to gather Role settings. Roles may not " "be updated to match privileges requested."
+ )
+ for rolemap in results["LDAP"]["RemoteRoleMapping"]:
+ for role in roles:
+ if role["RoleId"] == rolemap["LocalRole"]:
+ role["RemoteGroup"] = rolemap["RemoteGroup"]
+ break
+ for role in roles:
+ privs = {"AssignedPrivileges": [], "OemPrivileges": []}
+
+ for update_role in list(roles_to_update.keys()):
+ if role.get("RemoteGroup", None) == update_role:
+ for priv in roles_to_update[update_role]:
+ privs[PRIVKEY[priv][1]].append(PRIVKEY[priv][0])
+ try:
+ self.rdmc.app.patch_handler(role["@odata.id"], privs)
+ self.rdmc.ui.printer("Updated privileges for %s\n" % update_role)
+ except IloResponseError as excp:
+ self.rdmc.ui.error("Unable to update privileges for %s\n" % update_role, excp)
+ break
+
+ def directory_helper(self, settings, options):
+ """Helper function to set the payload based on options and arguments
+
+ :param settings: dictionary to change
+ :type settings: dict.
+ :param options: list of options
+ :type options: list.
+ """
+
+ payload = {}
+ serviceaddress = None
+
+ if hasattr(options, "serviceaddress"):
+ if isinstance(options.serviceaddress, str):
+ serviceaddress = options.serviceaddress
+ if serviceaddress == '""' or serviceaddress == "''":
+ serviceaddress = ""
+ if hasattr(options, "port"):
+ if isinstance(options.port, str):
+ if serviceaddress is None:
+ serviceaddress = settings["ServiceAddresses"][0]
+ serviceaddress = serviceaddress + ":" + options.port
+ if hasattr(options, "realm"):
+ if isinstance(options.realm, str):
+ if serviceaddress is None:
+ serviceaddress = settings["ServiceAddresses"][0]
+ if options.realm == '""' or options.realm == "''":
+ options.realm = ""
+ serviceaddress = serviceaddress + "@" + options.realm
+ if serviceaddress is not None:
+ payload["ServiceAddresses"] = [serviceaddress]
+
+ if hasattr(options, "enable"):
+ if options.enable is not None:
+ payload["ServiceEnabled"] = options.enable
+
+ if hasattr(options, "ldap_username") and hasattr(options, "ldap_password"):
+ if options.ldap_username and options.ldap_password:
+ payload.update(
+ {
+ "Authentication": {
+ "Username": options.ldap_username,
+ "Password": options.ldap_password,
+ }
+ }
+ )
+
+ if hasattr(options, "roles"):
+ if options.roles:
+ payload["RemoteRoleMapping"] = self.role_helper(options.roles, settings["RemoteRoleMapping"])
+
+ if hasattr(options, "search"):
+ if options.search:
+ payload.update(
+ {
+ "LDAPService": {
+ "SearchSettings": self.search_helper(
+ options.search,
+ settings["LDAPService"]["SearchSettings"],
+ )
+ }
+ }
+ )
+ return payload
+
+ def test_directory(self, options, json=False):
+ """Function to perform directory testing
+
+ :param options: namespace of custom parser attributes which contain the original command
+ arguments for 'start/stop/viewresults'
+ :type options: namespace
+ :param json: Bool to print in json format or not.
+ :type json: bool.
+ """
+ results = self.rdmc.app.select(selector="HpeDirectoryTest.", path_refresh=True)[0].dict
+ if options.start_stop_view.lower() == "start":
+ path = None
+ for item in results["Actions"]:
+ if "StartTest" in item:
+ path = results["Actions"][item]["target"]
+ break
+ if not path:
+ raise NoContentsFoundForOperationError("Unable to start directory test.")
+ self.rdmc.ui.printer(
+ "Starting the directory test. Monitor results with " 'command: "directory test viewresults".\n'
+ )
+ self.rdmc.app.post_handler(path, {})
+ elif options.start_stop_view.lower() == "stop":
+ path = None
+ for item in results["Actions"]:
+ if "StopTest" in item:
+ path = results["Actions"][item]["target"]
+ break
+ if not path:
+ raise NoContentsFoundForOperationError("Unable to stop directory test.")
+ self.rdmc.ui.printer("Stopping the directory test.\n")
+ self.rdmc.app.post_handler(path, {})
+ elif options.start_stop_view.lower() == "viewresults":
+ if getattr(options, "json", False):
+ self.rdmc.ui.print_out_json(results["TestResults"])
+ else:
+ for test in results["TestResults"]:
+ self.rdmc.ui.printer("Test: %s\n" % test["TestName"])
+ self.rdmc.ui.printer("------------------------\n")
+ self.rdmc.ui.printer("Status: %s\n" % test["Status"])
+ self.rdmc.ui.printer("Notes: %s\n\n" % test["Notes"])
+
+ def print_s(self, settings, oem_settings, local_auth_setting, name):
+ setting = "Kerberos" if name == "ActiveDirectory" else name
+ enable = str(settings["ServiceEnabled"])
+ serviceaddress = settings["ServiceAddresses"][0]
+ address = serviceaddress if serviceaddress else "Not Set"
+ username = settings["Authentication"]["Username"]
+ dis_name = username if username else "Not Set"
+ auth = local_auth_setting
+ content = {
+ "Settings": setting,
+ "Enabled": enable,
+ "Service Address": address,
+ "Distinguished Name": dis_name,
+ "Local Account Authorization": auth,
+ }
+ if name.lower() == "activedirectory":
+ address_settings = oem_settings["KerberosSettings"]
+ port = address_settings["KDCServerPort"]
+ realm = address_settings["KerberosRealm"] if address_settings["KerberosRealm"] else "Not Set"
+ content.update({"Port": port, "Realm": realm})
+
+ else:
+ address_settings = oem_settings["DirectorySettings"]
+ port = address_settings["LdapServerPort"]
+ authmode = address_settings["LdapAuthenticationMode"]
+ content.update({"Port": port, "Authentication Mode": authmode})
+ addsearch = []
+ try:
+ count = 1
+ for search in settings["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]:
+ # search = (count, search)
+ search = "Search %s: %s" % (count, search)
+ addsearch.append(search)
+ count += 1
+ content.update({"Search Settings": addsearch})
+ except KeyError:
+ search = "No Search Settings"
+ content.update({"Search Settings": {search}})
+ addval = []
+ if "RemoteRoleMapping" in settings:
+ if len(settings["RemoteRoleMapping"]) > 0:
+ for role in settings["RemoteRoleMapping"]:
+ roleadd = {
+ "Local Role": role["LocalRole"],
+ "Remote Group": role["RemoteGroup"],
+ }
+ addval.append(roleadd)
+ content.update({"Remote Role Mapping(s)": addval})
+ else:
+ addval = {"No Remote Role Mappings"}
+ content.update({"Remote Role Mapping(s)": addval})
+ else:
+ content.update({"No Remote Role Mappings"})
+ return content
+
+ def print_settings(self, settings, oem_settings, local_auth_setting, name):
+ """Pretty print settings of LDAP or Kerberos
+
+ :param settings: settings to print
+ :type settings: dict.
+ :param oem_settings: oem_settings to print
+ :type oem_settings: dict.
+ :param local_auth_settings: local authorization setting
+ :type local_auth_settings: str.
+ :param name: type of setting (activedirectory or ldap)
+ :type name: str.
+ """
+
+ self.rdmc.ui.printer("%s settings:\n" % ("Kerberos" if name == "ActiveDirectory" else name))
+ self.rdmc.ui.printer("--------------------------------\n")
+ self.rdmc.ui.printer("Enabled: %s\n" % str(settings["ServiceEnabled"]))
+
+ serviceaddress = settings["ServiceAddresses"][0]
+
+ self.rdmc.ui.printer("Service Address: %s\n" % (serviceaddress if serviceaddress else "Not Set"))
+ username = settings["Authentication"]["Username"]
+ self.rdmc.ui.printer("Distinguished Name: %s\n" % (username if username else "Not Set"))
+
+ self.rdmc.ui.printer("Local Account Authorization: %s\n" % local_auth_setting)
+
+ if name.lower() == "activedirectory":
+ address_settings = oem_settings["KerberosSettings"]
+ self.rdmc.ui.printer("Port: %s\n" % address_settings["KDCServerPort"])
+
+ self.rdmc.ui.printer(
+ "Realm: %s\n" % (address_settings["KerberosRealm"] if address_settings["KerberosRealm"] else "Not Set")
+ )
+ else:
+ address_settings = oem_settings["DirectorySettings"]
+ self.rdmc.ui.printer("Port: %s\n" % address_settings["LdapServerPort"])
+ self.rdmc.ui.printer("Authentication Mode: %s\n" % address_settings["LdapAuthenticationMode"])
+
+ try:
+ self.rdmc.ui.printer("Search Settings:\n")
+ count = 1
+ for search in settings["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"]:
+ self.rdmc.ui.printer("\tSearch %s: %s\n" % (count, search))
+ count += 1
+ except KeyError:
+ self.rdmc.ui.printer("\tNo Search Settings\n")
+
+ if "RemoteRoleMapping" in settings:
+ self.rdmc.ui.printer("Remote Role Mapping(s):\n")
+ if len(settings["RemoteRoleMapping"]) > 0:
+ for role in settings["RemoteRoleMapping"]:
+ self.rdmc.ui.printer("\tLocal Role: %s\n" % role["LocalRole"])
+ self.rdmc.ui.printer("\tRemote Group: %s\n" % role["RemoteGroup"])
+ else:
+ self.rdmc.ui.printer("\tNo Remote Role Mappings.\n")
+ else:
+ self.rdmc.ui.printer("No Remote Role Mappings.\n")
+
+ def role_helper(self, new_roles, curr_roles):
+ """Helper to prepare adding and removing roles for patching
+
+ :param new_roles: dictionary of new roles to add or remove
+ :type new_roles: dict.
+ :param curr_roles: list of current roles on the system
+ :type curr_roles: list.
+ """
+ final_roles = curr_roles
+ if "add" in new_roles:
+ for role in new_roles["add"]:
+ role = role.split(":", 1)
+ if not self.duplicate_group(role[1], curr_roles):
+ if len(curr_roles) > 0:
+ final_roles.append({"LocalRole": role[0], "RemoteGroup": role[1]})
+ else:
+ final_roles = [{"LocalRole": role[0], "RemoteGroup": role[1]}]
+ curr_roles = final_roles
+
+ else:
+ raise ResourceExists('Group DN "%s" already exists.' % role[1].split(":")[0])
+ if "remove" in new_roles:
+ removed = False
+ for role in new_roles["remove"]:
+ removed = False
+ for item in reversed(final_roles):
+ if item["LocalRole"] == role:
+ if len(curr_roles) > 1:
+ del final_roles[final_roles.index(item)]
+ removed = True
+ break
+ elif len(curr_roles) == 1:
+ del final_roles[final_roles.index(item)]
+ final_roles = [{}]
+ removed = True
+ break
+ if not removed:
+ raise InvalidCommandLineError("Unable to find local role %s to delete" % role)
+ return final_roles
+
+ def duplicate_group(self, group_dn, curr_roles):
+ """Checks if new role is a duplicate
+
+ :param group_dn: group domain name from user
+ :type group_dn: str.
+ :param curr_roles: list of current roles
+ :type curr_roles: list.
+ """
+ group_dn = group_dn.split(":")[0]
+ for item in curr_roles:
+ comp_dn = item["RemoteGroup"].split(":")[0]
+ if comp_dn == group_dn:
+ return True
+ return False
+
+ def search_helper(self, new_searches, curr_searches):
+ """Helper to prepare search strings for patching
+
+ :param new_serches: dictionary of new searches to add
+ :type new_searches: dict.
+ :param curr_searches: list of current searches
+ :type curr_searches: dict.
+ """
+ final_searches = curr_searches
+
+ if "add" in new_searches:
+ if "BaseDistinguishedNames" in final_searches:
+ for search in new_searches["add"]:
+ for key, value in final_searches.items():
+ for v in value:
+ if search.lower() == v.lower():
+ raise ResourceExists('Search Setting "%s" already exists.\n' % search)
+
+ final_searches["BaseDistinguishedNames"].append(search)
+ else:
+ final_searches["BaseDistinguishedNames"] = new_searches["add"]
+ elif "remove" in new_searches:
+ to_remove = []
+
+ if "BaseDistinguishedNames" not in curr_searches:
+ raise NoContentsFoundForOperationError("No search strings to remove")
+
+ for search in new_searches["remove"]:
+ if search in curr_searches["BaseDistinguishedNames"]:
+ to_remove.append(search)
+ else:
+ raise InvalidCommandLineError("Unable to find search %s to delete" % search)
+ for item in to_remove:
+ final_searches["BaseDistinguishedNames"].remove(item)
+
+ if not final_searches["BaseDistinguishedNames"]:
+ sys.stdout.write("Attempting to delete all searches.\n")
+ final_searches["BaseDistinguishedNames"].append("")
+
+ return final_searches
+
+ def directoryvalidation(self, options):
+ """directory validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def options_argument_group(self, parser):
+ """Additional argument
+
+ :param parser: The parser to add the removeprivs option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+
+ parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ default_parser = subcommand_parser.add_parser("default")
+
+ default_parser.add_argument(
+ "ldap_kerberos",
+ help="Specify LDAP or Kerberos configuration settings",
+ metavar="LDAP_KERBEROS",
+ nargs="?",
+ type=str,
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(default_parser)
+
+ privilege_help = (
+ "\n\nPRIVILEGES:\n\t1: Login\n\t2: Remote Console\n\t"
+ "3: User Config\n\t4: iLO (Manager) Config\n\t5: Virtual Media\n\t"
+ "6: Virtual Power and Reset\n\t7: Host NIC Config\n\t8: Host Bios Config\n\t9: "
+ "Host Storage Config\n\t10: System Recovery Config\n\t11: Self Password Change\n\t"
+ "12: Configure Components\n\n\tLOCAL ROLES:\n\tReadOnly\n\tOperator\n\tAdministrator"
+ "\n\n\tNOTE: The Self Password Change privilege is automatically added to roles with "
+ "the Login privilege."
+ )
+ ldap_help = "Show, add or modify properties pertaining to iLO LDAP Configuration."
+ ldap_parser = subcommand_parser.add_parser(
+ __subparsers__[0],
+ help=ldap_help,
+ description=ldap_help + "\n\n\tSimply show LDAP configuration:\n\t\tdirectory ldap\n\n"
+ "To modify the LDAP username, password, service address, search strings or "
+ "enable/disable LDAP.\n\t\tdirectory ldap "
+ '--serviceaddress x.x.y.z --addsearch "string1;string2" --enable.\n\n\tTo add role '
+ 'mapping.\n\t\tdirectory ldap --addrolemap "LocalRole1:'
+ 'RemoteGroup3#LocalRole2:RemoteGroup4:SID".\n\n\tTo remove role mapping.\n\t\t'
+ "directory ldap --removerolemap LocalRole1#LocalRole2." + privilege_help,
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ ldap_parser.add_argument(
+ "ldap_username",
+ help="The LDAP username used in verifying AD (optional outside of '--enable' and" "'--disable')",
+ metavar="USERNAME",
+ nargs="?",
+ type=str,
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "ldap_password",
+ help="The LDAP password used in verifying AD (optional outside of '--enable' and" "'--disable')",
+ metavar="PASSWORD",
+ nargs="?",
+ type=str,
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "--enable",
+ "--disable",
+ dest="enable",
+ type=str,
+ nargs="*",
+ action=_DirectoryParse,
+ help="Optionally add this flag to enable LDAP services.",
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "--addsearch",
+ "--removesearch",
+ dest="search",
+ nargs="*",
+ action=_DirectoryParse,
+ help="Optionally add this flag to add or remove search strings for " "generic LDAP services.",
+ type=str,
+ default={},
+ )
+ ldap_parser.add_argument(
+ "--serviceaddress",
+ dest="serviceaddress",
+ help="Optionally include this flag to set the service address of the LDAP Services.",
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "--port",
+ dest="port",
+ help="Optionally include this flag to set the port of the LDAP services.",
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "--addrolemap",
+ "--removerolemap",
+ dest="roles",
+ nargs="*",
+ action=_DirectoryParse,
+ help="Optionally add this flag to add or remove Role Mapping(s) for the LDAP."
+ " Remove EX: --removerolemap LocalRole1#LocalRole2 "
+ 'Add EX: --addrolemap "LocalRole1:RemoteGroup3#LocalRole2:RemoteGroup4\n\n"'
+ 'SID EX: --addrolemap "LocalRole1:RemoteGroup2:SID#LocalRole2:RemoteGroup5:SID"'
+ "\n\nNOTE 1: Create a custom local role group (and subsequently assign to a role map)"
+ "by adding the numbers associated with privilege(s) desired separated by a semicolon"
+ "(;)\n\nNOTE 2: SID is optional",
+ type=str,
+ default={},
+ )
+ ldap_parser.add_argument(
+ "--enablelocalauth",
+ "--disablelocalauth",
+ dest="localauth",
+ nargs="*",
+ type=str,
+ action=_DirectoryParse,
+ help="Optionally include this flag if you wish to enable or disable authentication " "for local accounts.",
+ default=None,
+ )
+ ldap_parser.add_argument(
+ "--authentication",
+ dest="authmode",
+ choices=["DefaultSchema", "ExtendedSchema"],
+ help="Optionally include this flag if you would like to choose a LDAP authentication "
+ "mode Valid choices are: DefaultSchema (Directory Default Schema or Schema-free) or "
+ "ExtendedSchema (HPE Extended Schema).",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(ldap_parser)
+
+ self.options_argument_group(ldap_parser)
+
+ kerberos_help = "Show, add or modify properties pertaining to AD Kerberos Configuration."
+ kerberos_parser = subcommand_parser.add_parser(
+ __subparsers__[1],
+ help=kerberos_help,
+ description=kerberos_help + "\n\nExamples:\n\nShow Kerberos specific AD/LDAP configuration "
+ "settings.\n\tdirectory kerberos\n\nShow current AD Kerberos configuration."
+ "\n\tdirectory kerberos\n\nAlter kerberos service address, AD realm and Port.\n\t"
+ "directory kerberos --serviceaddress x.x.y.z --port 8888 --realm adrealm1",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ kerberos_parser.add_argument(
+ "--serviceaddress",
+ dest="serviceaddress",
+ help="Optionally include this flag to set the Kerberos serviceaddress.",
+ default=None,
+ )
+ kerberos_parser.add_argument(
+ "--port",
+ dest="port",
+ help="Optionally include this flag to set the Kerberos port.",
+ default=None,
+ )
+ kerberos_parser.add_argument(
+ "--realm",
+ dest="realm",
+ help="Optionally include this flag to set the Kerberos realm.",
+ default=None,
+ )
+ kerberos_parser.add_argument(
+ "--keytab",
+ dest="keytab",
+ help="Optionally include this flag to import a Kerberos Keytab by it's URI location.",
+ default="",
+ )
+ kerberos_parser.add_argument(
+ "--enable",
+ "--disable",
+ dest="enable",
+ type=str,
+ nargs="*",
+ action=_DirectoryParse,
+ help="Optionally add this flag to enable or disable Kerberos services.",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(kerberos_parser)
+
+ self.options_argument_group(kerberos_parser)
+
+ directory_test_help = (
+ "Start, stop or view results of an AD/LDAP test which include: ICMP, "
+ "Domain Resolution, Connectivity, Authentication, Bindings, LOM Object and User "
+ "Context tests."
+ )
+ directory_test_parser = subcommand_parser.add_parser(
+ __subparsers__[2],
+ help=directory_test_help,
+ description=directory_test_help + "\n\nExamples:\n\nStart a directory test:\n\tdirectory test "
+ "start\n\nStop a directory test:\n\tdirectory test stop\n\nView results of the last "
+ "directory test:\n\tdirectory test viewresults",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ directory_test_parser.add_argument(
+ "start_stop_view",
+ help="Start, stop, or view results on an AD/LDAP test.",
+ metavar="START, STOP, VIEWRESULTS",
+ default="viewresults",
+ )
+ self.cmdbase.add_login_arguments_group(directory_test_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DisableIloFunctionalityCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DisableIloFunctionalityCommand.py
new file mode 100644
index 0000000..ace8fbb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/DisableIloFunctionalityCommand.py
@@ -0,0 +1,204 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""iLO Functionality Command for rdmc"""
+
+import json
+
+try:
+ from rdmc_helper import (
+ IncompatableServerTypeError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IncompatableServerTypeError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class DisableIloFunctionalityCommand:
+ """Disables iLO functionality to the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "disableilofunctionality",
+ "usage": None,
+ "description": "Disable iLO functionality on the current logged in server."
+ "\n\texample: disableilofunctionality\n\n\tWARNING: this will"
+ " render iLO unable to respond to network operations.\n\n\t"
+ "Add the --force flag to ignore critical task checking.",
+ "summary": "disables iLO's accessibility via the network and resets "
+ "iLO. WARNING: This should be used with caution as it will "
+ "render iLO unable to respond to further network operations "
+ "(including REST operations) until iLO is re-enabled using the"
+ " RBSU menu.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main DisableIloFunctionalityCommand function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("disableilofunctionality command takes no arguments.")
+
+ self.ilofunctionalityvalidation(options)
+
+ select = "Manager."
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Manager. not found.")
+
+ bodydict = results.resp.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ if bodydict["iLOFunctionalityRequired"]:
+ raise IncompatableServerTypeError(
+ "disableilofunctionality"
+ " command is not available. iLO functionality is required"
+ " and can not be disabled on this platform."
+ )
+
+ try:
+ for item in bodydict["Actions"]:
+ if "iLOFunctionality" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "iLOFunctionality"
+
+ path = bodydict["Actions"][item]["target"]
+ body = {"Action": action}
+ break
+ except:
+ body = {"Action": "iLOFunctionality", "Target": "/Oem/Hp"}
+
+ if self.ilodisablechecks(options):
+ self.rdmc.ui.warn(
+ "Disabling iLO functionality. iLO will be unavailable on the logged "
+ " in server until it is re-enabled manually.\n"
+ )
+
+ results = self.rdmc.app.post_handler(path, body, silent=True, service=True)
+
+ if results.status == 200:
+ self.rdmc.ui.printer("[%d] The operation completed successfully.\n" % results.status)
+ else:
+ self.rdmc.ui.printer("[%d] iLO responded with the following info: \n" % results.status)
+ json_payload = json.loads(results._http_response.data)
+ try:
+ self.rdmc.ui.error("%s" % json_payload["error"]["@Message.ExtendedInfo"][0]["MessageId"])
+ except:
+ self.rdmc.ui.error("An invalid or incomplete response was received: %s\n" % json_payload)
+
+ else:
+ self.rdmc.ui.error(
+ "iLO is currently performing a critical task and "
+ "can not be safely disabled at this time. Please try again later.\n"
+ )
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def ilodisablechecks(self, options):
+ """Verify it is safe to actually disable iLO
+
+ :param options: command line options
+ :type options: values, attributes of class obj
+ """
+
+ if options.force:
+ self.rdmc.ui.warn("Force Enabled: Ignoring critical operation/mode checking.\n")
+ return True
+
+ else:
+ keyword_list = "idle", "complete"
+
+ try:
+ results = self.rdmc.app.select(selector="UpdateService.")[0]
+ except:
+ raise NoContentsFoundForOperationError("UpdateService. not found.")
+
+ try:
+ state = results.resp.dict["Oem"]["Hpe"]["State"].lower()
+ for val in keyword_list:
+ if val in state:
+ return True
+ return False
+
+ except:
+ raise NoContentsFoundForOperationError("iLO state not identified")
+
+ def ilofunctionalityvalidation(self, options):
+ """ilofunctionalityvalidation method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--force",
+ dest="force",
+ help="Ignore any critical task checking and force disable iLO.",
+ action="store_true",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ESKMCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ESKMCommand.py
new file mode 100644
index 0000000..70e5d09
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/ESKMCommand.py
@@ -0,0 +1,134 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""ESKM Command for rdmc"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class ESKMCommand:
+ """Commands ESKM available actions"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "eskm",
+ "usage": None,
+ "description": "Clear the ESKM logs.\n\texample: eskm"
+ " clearlog\n\n\tTest the ESKM connections.\n\texample: eskm testconnections",
+ "summary": "Command for all ESKM available actions.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main ESKMCommand function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if not len(args) == 1:
+ raise InvalidCommandLineError("eskm command only takes one parameter.")
+
+ self.eskmvalidation(options)
+
+ select = self.rdmc.app.typepath.defs.hpeskmtype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("ESKM not found.")
+
+ if args[0].lower() == "clearlog":
+ actionitem = "ClearESKMLog"
+ elif args[0].lower() == "testconnections":
+ actionitem = "TestESKMConnections"
+ else:
+ raise InvalidCommandLineError("Invalid command.")
+
+ bodydict = results.resp.dict
+ try:
+ for item in bodydict["Actions"]:
+ if actionitem in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ actionitem = item.split("#")[-1]
+
+ path = bodydict["Actions"][item]["target"]
+ break
+ except:
+ pass
+
+ body = {"Action": actionitem}
+ self.rdmc.app.post_handler(path, body)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def eskmvalidation(self, options):
+ """eskmvalidation method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/EthernetCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/EthernetCommand.py
new file mode 100644
index 0000000..3faec34
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/EthernetCommand.py
@@ -0,0 +1,1242 @@
+###
+# Copyright 2019 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Certificates Command for rdmc"""
+
+import copy
+import json
+import re
+from argparse import RawDescriptionHelpFormatter
+from collections import OrderedDict
+
+import redfish.ris
+from redfish.ris.ris import RisInstanceNotFoundError
+from redfish.ris.rmc_helper import (
+ IloResponseError,
+ IncompatibleiLOVersionError,
+ InvalidPathError,
+)
+from redfish.ris.utils import diffdict, json_traversal, json_traversal_delete_empty
+
+try:
+ from rdmc_helper import (
+ UI,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ InvalidPropertyError,
+ NoDifferencesFoundError,
+ RdmcError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ UI,
+ Encryption,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ InvalidPropertyError,
+ NoDifferencesFoundError,
+ RdmcError,
+ ReturnCodes,
+ )
+
+__eth_file__ = "eth.json"
+__subparsers__ = ["save", "load"]
+
+
+class EthernetCommand:
+ """ Commands for Ethernet Management Controller and RDE supported NIC card \
+ configuration on server """
+
+ def __init__(self):
+ self.ident = {
+ "name": "ethernet",
+ "usage": None,
+ "description": "Save or load from a JSON formatted file containing "
+ "properties pertaining to a system's iLO ethernet management controller. "
+ "Additionally enable/disable individual management controller NICs (including VNIC), "
+ "IPv4 and IPv6 addressing configuration and domain name servers."
+ "\n\tBy default the JSON file will be named 'eth.json'.\n\t"
+ "\n\t***Credentials for an iLO administrator level account must be provided as"
+ " well as the iLO base URL.***"
+ "\n\n\tSave ethernet management controller data.\n\texample: ethernet save "
+ "\n\n\tLoad ethernet management controller data.\n\texample: ethernet load"
+ "\n\n\tSave ethernet management controller data to a different file, silently\n\t"
+ "ethernet save -f --silent"
+ "\n\n\tLoad etherent management controller data from a different file, silently\n\t"
+ "ethernet load -f --silent"
+ "\n\n\tEnable network interfaces by listing each interface to be enabled. **Note**: "
+ "Non-existent interfaces will be omitted from configuration.\n\t "
+ "ethernet --enable_nic 1,2,3"
+ "\n\n\tDisable network interfaces by listing each interface to be disabled. **Note**: "
+ "Non-existent interfaces will be omitted from configuration.\n\t "
+ "ethernet --disable_nic 1,2,3"
+ "\n\n\tEnable virtual network interface of management network.\n\t"
+ "ethernet --enable_vnic"
+ "\n\n\tDisable virtual network interface of management network.\n\t "
+ "ethernet --disable_vnic"
+ "\n\n\tEnable Enhanced Download Performance.\n\t"
+ "ethernet --enable_enhanced_downloads"
+ "\n\n\tDisable Enhanced Download Performance.\n\t "
+ "ethernet --disable_enhanced_downloads"
+ "\n\n\tConfigure Domain Name Servers (DNS) in a list: \n\t"
+ "ethernet --nameservers 8.8.8.8,1.1.1.1,2.2.2.2 OR ethernet --nameservers "
+ "dns_resolver1.aws.com,dns_resolver2.aws.com"
+ "\n\n\tConfigure Static IPv4 Settings. Provide a list of network settings\n\t"
+ "ethernet --network_ipv4 ,,"
+ "\n\n\tConfigure Proxy Settings. Provide a proxy server and port\n\t"
+ "ethernet --proxy http://proxy.company.net:8080"
+ "\n\n\tClear Proxy Settings. \n\t"
+ "ethernet --proxy None"
+ "\n\n\tConfigure Static IPv6 Settings. Provide a list of network settings\n\t"
+ "ethernet --network_ipv6 ,,",
+ "summary": "Command for configuring Ethernet Management Controller Interfaces and " "associated properties",
+ "aliases": [],
+ "auxcommands": ["IloResetCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+ self.save = None
+ self.load = None
+ self.eth_file = None # set in validation
+ self.eth_args = None
+ self.saved_inputline = None # set in validation
+
+ def run(self, line, help_disp=False):
+ """Main Ethernet Management Controller Interfaces Run
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ ident_subparser = False
+ for cmnd in __subparsers__:
+ if cmnd in line:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ ident_subparser = True
+ break
+ if not ident_subparser:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line, default=True)
+ except:
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.ethernetvalidation(options)
+
+ if "default" in options.command.lower():
+ # flags are used to showing data with respect to enhance download
+ flag = True
+ return_flag = True
+ if options.enable_enhanced_downloads or options.disable_enhanced_downloads:
+ ilo_ver = self.rdmc.app.getiloversion()
+ self.rdmc.ui.printer("iLO Version is " + str(ilo_ver) + "\n")
+ if ilo_ver < 5.263:
+ raise IncompatibleiLOVersionError("Enhance download required minimum " "iLO Version iLO5.263")
+ # net_path = self.rdmc.app.getidbytype('NetworkProtocol.')
+ net_path = self.rdmc.app.typepath.defs.managerpath + "NetworkProtocol"
+ results = self.rdmc.app.get_handler(net_path, silent=True, service=True).dict
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ if options.enable_enhanced_downloads:
+ if results["Oem"]["Hpe"]["EnhancedDownloadPerformanceEnabled"]:
+ self.rdmc.ui.printer(
+ "Enhanced Download Performance " "already enabled!! \n",
+ verbose_override=True,
+ )
+ return_flag = False
+ else:
+ self.rdmc.ui.printer("Enabling enhanced " "downloads...\n", verbose_override=True)
+ body["Oem"]["Hpe"]["EnhancedDownloadPerformanceEnabled"] = True
+ flag = False
+ else:
+ if not results["Oem"]["Hpe"]["EnhancedDownloadPerformanceEnabled"]:
+ self.rdmc.ui.printer(
+ "Enhanced Download Performance " "already disabled!!\n\n",
+ verbose_override=True,
+ )
+ return_flag = False
+ else:
+ self.rdmc.ui.printer("Disabling enhanced " "downloads...\n", verbose_override=True)
+ body["Oem"]["Hpe"]["EnhancedDownloadPerformanceEnabled"] = False
+ flag = False
+ if options.enable_vnic or options.disable_vnic:
+ self.rdmc.app.patch_handler(net_path, body, service=False, silent=True)
+ elif return_flag:
+ self.rdmc.app.patch_handler(net_path, body, service=False, silent=False)
+ if options.enable_vnic or options.disable_vnic:
+ results = self.rdmc.app.get_handler(
+ self.rdmc.app.typepath.defs.managerpath, service=True, silent=True
+ ).dict
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ if options.enable_vnic:
+ if results["Oem"]["Hpe"]["VirtualNICEnabled"]:
+ self.rdmc.ui.printer("Virtual NIC already enabled!!\n", verbose_override=True)
+ return ReturnCodes.SUCCESS
+ self.rdmc.ui.printer("Enabling Virtual NIC...\n", verbose_override=True)
+ body["Oem"]["Hpe"]["VirtualNICEnabled"] = True
+ else:
+ if not results["Oem"]["Hpe"]["VirtualNICEnabled"]:
+ self.rdmc.ui.printer("Virtual NIC already disabled!!\n", verbose_override=True)
+ return ReturnCodes.SUCCESS
+ self.rdmc.ui.printer("Disabling Virtual NIC...\n", verbose_override=True)
+ body["Oem"]["Hpe"]["VirtualNICEnabled"] = False
+ self.rdmc.app.patch_handler(
+ self.rdmc.app.typepath.defs.managerpath,
+ body,
+ service=False,
+ silent=False,
+ )
+ self.rdmc.ui.printer("Warning: Resetting iLO...\n")
+ self.auxcommands["iloreset"].run("")
+ self.rdmc.ui.printer("You will need to re-login to access this system...\n")
+ elif flag and return_flag:
+ data = self.get_data(options)
+ get_data = True
+ for inst in data:
+ if "ethernetinterface" in inst.lower() or "ethernetnetworkinterface" in inst.lower():
+ for path in data[inst]:
+ if (
+ "managers/1/ethernetinterfaces/1" in path.lower()
+ or "managers/1/ethernetnetworkinterfaces/1" in path.lower()
+ ): # only process managers interfaces
+ if options.enable_nic or options.disable_nic:
+ get_data = False
+ self.enable_disable_nics(data[inst][path], options)
+
+ if (
+ options.network_ipv4 or options.network_ipv6 or options.nameservers or options.proxy
+ ) and "virtual" not in data[inst][path]["Name"].lower():
+ get_data = False
+ self.configure_static_settings(data[inst][path], options)
+ if options.proxy:
+ return ReturnCodes.SUCCESS
+ if "networkprotocol" in inst.lower():
+ for path in data[inst]:
+ if "managers/1/networkprotocol" in path.lower():
+ if options.enable_enhanced_downloads or options.disable_enhanced_downloads:
+ get_data = False
+ if get_data:
+ self.output_data(data, options, get_data)
+ else:
+ self.load_main_function(options, data)
+
+ if "save" in options.command.lower():
+ self.save = True
+ self.load = False
+ if "save" in options.command.lower() and not options.ethfilename:
+ self.rdmc.ui.printer("Saving configurations to default file '%s'.\n" % self.eth_file)
+ self.output_data(self.get_data(options), options, False)
+
+ elif "load" in options.command.lower():
+ self.save = False
+ self.load = True
+ if options.force_network_config:
+ self.load_main_function(options)
+ else:
+ self.rdmc.ui.printer("Skipping network configurations as " "--force_network_config is not included.\n")
+
+ return ReturnCodes.SUCCESS
+
+ def get_data(self, options):
+ """Obtain data for Ethernet Management Interfaces, Ethernet System Interfaces, \
+ Manager Network Services and iLO Date Time Types and subsequently save to a dictionary \
+ :param options: command line options
+ :type options: attribute
+ """
+
+ network_data_collection = eth_iface = manager_network_services = ilo_date_time = OrderedDict()
+
+ if self.rdmc.app.redfishinst.is_redfish:
+ try:
+ iface_results = self.rdmc.app.select(selector="ethernetinterface.")
+ except RisInstanceNotFoundError:
+ self.rdmc.ui.printer("Type 'EthernetInterface.' not available.\n")
+ else:
+ try:
+ iface_results = self.rdmc.app.select(selector="ethernetnetworkinterface.")
+ except RisInstanceNotFoundError:
+ self.rdmc.ui.printer("Type 'EthernetNetworkInterface.' not available.\n")
+
+ for inst in iface_results:
+ try:
+ eth_iface[inst.type].update({inst.path: inst.dict})
+ except KeyError:
+ eth_iface[inst.type] = {inst.path: inst.dict}
+ continue
+ except AttributeError:
+ self.rdmc.ui.printer("Missing instance data for type '%s' : '%s'" % (inst.type, inst.path))
+ if self.rdmc.app.typepath.defs.isgen9:
+ selector_content = self.rdmc.app.select(selector="ManagerNetworkService.0.11.1.networkprotocol")
+ else:
+ selector_content = self.rdmc.app.select(selector="networkprotocol.")
+ for inst in selector_content:
+ try:
+ manager_network_services[inst.type].update({inst.path: inst.dict})
+ except KeyError:
+ manager_network_services[inst.type] = {inst.path: inst.dict}
+ continue
+ except AttributeError:
+ self.rdmc.ui.printer("Missing instance data for type '%s' : '%s'" % (inst.type, inst.path))
+
+ for inst in self.rdmc.app.select(selector="datetime."):
+ try:
+ ilo_date_time[inst.type].update({inst.path: inst.dict})
+ except KeyError:
+ ilo_date_time[inst.type] = {inst.path: inst.dict}
+ continue
+ except AttributeError:
+ self.rdmc.ui.printer("Missing instance data for type '%s' : '%s'" % (inst.type, inst.path))
+
+ network_data_collection.update(eth_iface)
+ network_data_collection.update(manager_network_services)
+ network_data_collection.update(ilo_date_time)
+
+ return network_data_collection
+
+ def output_data(self, data, options, get_only=False):
+ """ Output data in requested json file or on console
+ :param data: dictionary containing ethernet configuration data:
+ :type data: dictionary
+ :param options: command line options
+ :type options: attribute
+ :param get_only: parameter to only output to console (do not save data to file). \
+ Essentially a targeted \'get\' command.
+ :type: boolean
+ """
+
+ outdata = list()
+
+ outdata.append(self.rdmc.app.create_save_header())
+
+ for _type in data:
+ for path in data.get(_type):
+ temp = dict()
+ try:
+ temp[_type.split("#")[-1]].update({path: self.rdmc.app.removereadonlyprops(data[_type][path])})
+ outdata.append(temp)
+ except KeyError:
+ temp[_type.split("#")[-1]] = {path: self.rdmc.app.removereadonlyprops(data[_type][path])}
+ outdata.append(temp)
+ except Exception:
+ pass
+
+ if self.eth_file and not get_only:
+ if options.encryption:
+ with open(self.eth_file, "wb") as outfile:
+ outfile.write(
+ Encryption().encrypt_file(
+ json.dumps(outdata, indent=2, cls=redfish.ris.JSONEncoder),
+ options.encryption,
+ )
+ )
+ else:
+ with open(self.eth_file, "w") as outfile:
+ outfile.write(json.dumps(outdata, indent=2, cls=redfish.ris.JSONEncoder))
+ else:
+ if options.json:
+ self.rdmc.ui.print_out_json_ordered(outdata)
+ else:
+ UI().print_out_human_readable(outdata)
+
+ def enable_disable_nics(self, data, options):
+ if options.disable_nic:
+ for ident in re.split("[, ]", options.disable_nic):
+ try:
+ if int(data.get("Id")) == int(ident):
+ data.update({"InterfaceEnabled": False})
+ break
+ except ValueError:
+ if data.get("Name") == ident:
+ data.update({"InterfaceEnabled": False})
+ break
+
+ if options.enable_nic:
+ for ident in re.split("[, ]", options.enable_nic):
+ try:
+ if int(data.get("Id")) == int(ident):
+ data.update({"InterfaceEnabled": True})
+ break
+ except ValueError:
+ if data.get("Name") == ident:
+ data.update({"InterfaceEnabled": True})
+ break
+
+ def configure_static_settings(self, data, options):
+ if options.network_ipv4:
+ usr_data = re.split("[, ]", options.network_ipv4)
+ if len(usr_data) > 2:
+ data["IPv4Addresses"][0] = {
+ "Address": usr_data[0],
+ "Gateway": usr_data[1],
+ "SubnetMask": usr_data[2],
+ }
+ data["DHCPv4"].update({"DHCPEnabled": False})
+ data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"].update({"Enabled": False})
+ else:
+ raise InvalidCommandLineErrorOPTS(
+ "An invalid number of arguments provided to "
+ " quick networking configuration. Check '--network_ipv4' entry."
+ )
+ else:
+ del data["IPv4Addresses"]
+
+ if options.network_ipv6:
+ usr_data = re.split("[, ]", options.network_ipv6)
+ if len(usr_data) > 2:
+ next(iter(data["IPv6Addresses"])).update({"Address": usr_data[0], "PrefixLength": usr_data[2]})
+ data["IPv6DefaultGateway"] = options.network_ipv6[1]
+ data["DHCPv6"].update({"DHCPEnabled": False})
+ data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv6"].update({"Enabled": False})
+ else:
+ raise InvalidCommandLineErrorOPTS(
+ "An invalid number of arguments provided to "
+ " quick networking configuration. Check '--network_ipv6' entry."
+ )
+ else:
+ del data["IPv6Addresses"]
+
+ if options.nameservers:
+ usr_data = re.split("[, ]", options.nameservers)
+ if len(usr_data) <= 3:
+ ipv6_list = list()
+ ipv4_list = list()
+ static_list = list()
+ for elem in usr_data:
+ oem_ipv4 = data["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv4"]
+ oem_ipv6 = data["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv6"]
+ if "::" in elem:
+ ipv6_list.append(elem)
+ elif "." in elem:
+ ipv4_list.append(elem)
+ static_list.append(elem)
+ oem_ipv6["DNSServers"] = ipv6_list
+ oem_ipv4["DNSServers"] = ipv4_list
+ data["StaticNameServers"] = static_list
+ else:
+ raise InvalidCommandLineErrorOPTS("Name Servers argument has to be less than or equal to 2")
+ else:
+ del data["StaticNameServers"]
+ del data["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv4"]["DNSServers"]
+ del data["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv6"]["DNSServers"]
+
+ if options.proxy and options.proxy != "None":
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ body["Oem"]["Hpe"]["WebProxyConfiguration"] = {}
+ proxy_body = body["Oem"]["Hpe"]["WebProxyConfiguration"]
+ proxy_body["ProxyServer"] = None
+ proxy_body["ProxyUserName"] = None
+ proxy_body["ProxyPassword"] = None
+ if "https" in options.proxy:
+ proxy_body["ProxyPort"] = 443
+ else:
+ proxy_body["ProxyPort"] = 80
+ if "@" in options.proxy:
+ proxy = options.proxy.split("@")
+ proxy_usr_pass = proxy[0]
+ proxy_srv_port = proxy[1]
+ if "//" in proxy_usr_pass:
+ proxy_usr_pass = proxy_usr_pass.split("//")[1]
+ if ":" in proxy_srv_port:
+ proxy = proxy_srv_port.split(":")
+ proxy_body["ProxyServer"] = proxy[0]
+ proxy_body["ProxyPort"] = int(proxy[1])
+ else:
+ proxy_body["ProxyServer"] = proxy_srv_port
+ if ":" in proxy_usr_pass:
+ proxy = proxy_usr_pass.split(":")
+ proxy_body["ProxyPassword"] = proxy[1]
+ proxy_body["ProxyUserName"] = proxy[0]
+ else:
+ proxy_body["ProxyUserName"] = proxy_usr_pass
+ else:
+ proxy_srv_port = options.proxy
+ if "//" in proxy_srv_port:
+ proxy_srv_port = proxy_srv_port.split("//")[1]
+ if ":" in proxy_srv_port:
+ proxy = proxy_srv_port.split(":")
+ proxy_body["ProxyServer"] = proxy[0]
+ proxy_body["ProxyPort"] = int(proxy[1])
+ else:
+ proxy_body["ProxyServer"] = proxy_srv_port
+ path = self.rdmc.app.getidbytype("NetworkProtocol.")
+
+ if path and body:
+ self.rdmc.ui.printer("Enabling Proxy configuration...\n", verbose_override=True)
+ self.rdmc.app.patch_handler(path[0], body, service=False, silent=False)
+ elif options.proxy and options.proxy == "None":
+ body = dict()
+ body["Oem"] = {}
+ body["Oem"]["Hpe"] = {}
+ body["Oem"]["Hpe"]["WebProxyConfiguration"] = {}
+ proxy_body = body["Oem"]["Hpe"]["WebProxyConfiguration"]
+ proxy_body["ProxyServer"] = ""
+ proxy_body["ProxyPort"] = None
+ proxy_body["ProxyUserName"] = ""
+ proxy_body["ProxyPassword"] = None
+ path = self.rdmc.app.getidbytype("NetworkProtocol.")
+
+ if path and body:
+ self.rdmc.ui.printer("Clearing Proxy configuration...\n", verbose_override=True)
+ self.rdmc.app.patch_handler(path[0], body, service=False, silent=False)
+
+ json_traversal_delete_empty(data, None, None)
+
+ def load_main_function(self, options, data=None):
+ """
+ Load main function. Handles SSO and SSL/TLS Certificates, kickoff load
+ helper, load of patch file, get server status and issues system reboot
+ and iLO reset after completion of all post and patch commands.
+ :param options: command line options
+ :type options: attribute
+ :param data: data
+ :type data: optional
+ """
+
+ if self.rdmc.app.redfishinst.is_redfish:
+ _ = "ethernetinterface."
+ else:
+ _ = "ethernetnetworkinterface."
+
+ if not data:
+ data = {}
+ try:
+ if options.encryption:
+ with open(self.eth_file, "rb") as file_handle:
+ data = json.loads(Encryption().decrypt_file(file_handle.read(), options.encryption))
+ else:
+ with open(self.eth_file, "rb") as file_handle:
+ data = json.loads(file_handle.read())
+ except:
+ raise InvalidFileInputError(
+ "Invalid file formatting found. Verify the file has a " "valid JSON format."
+ )
+ if "load" in options.command.lower():
+ for d in data:
+ for ilotype, subsect in d.items():
+ _type = ilotype.split(".")[0]
+ for _path in subsect:
+ if not subsect[_path]:
+ continue
+ elif "ethernetinterface" in _type.lower() or "ethernetnetworkinterface" in _type.lower():
+ if "managers" in _path.lower():
+ self.load_ethernet_aux(_type, _path, d[ilotype][_path])
+ elif "systems" in _path.lower():
+ self.rdmc.ui.warn("Systems Ethernet Interfaces '%s' " "cannot be modified." % _path)
+ continue
+ elif "datetime" in _type.lower():
+ if "StaticNTPServers" in list(subsect.get(_path).keys()):
+ # must set NTP Servers to static in OEM then reset iLO for StaticNTPServers
+ # property to appear in iLODateTime
+ if self.rdmc.app.redfishinst.is_redfish:
+ eth_config_type = "ethernetinterface"
+ else:
+ eth_config_type = "ethernetnetworkinterface"
+ for key in list(data.keys()):
+ if key.split(".")[0].lower() == eth_config_type:
+ eth_config_type = key
+ for _path in data[eth_config_type]:
+ if "managers" in _path.lower():
+ try:
+ oemhp = self.rdmc.app.typepath.defs.oemhp
+ data[eth_config_type][_path]["DHCPv4"]["UseNTPServers"] = True
+ data[eth_config_type][_path]["DHCPv6"]["UseNTPServers"] = True
+ data[eth_config_type][_path]["Oem"][oemhp]["DHCPv4"][
+ "UseNTPServers"
+ ] = True
+ data[eth_config_type][_path]["Oem"][oemhp]["DHCPv6"][
+ "UseNTPServers"
+ ] = True
+ self.load_ethernet_aux(
+ eth_config_type,
+ _path,
+ data[eth_config_type][_path],
+ )
+ except KeyError:
+ self.rdmc.ui.printer(
+ "Unable to configure " "'UseNTPServers' for '%s'.\n" % _path
+ )
+ self.rdmc.ui.printer(
+ "iLO must be reset in order for "
+ "changes to static network time protocol servers to "
+ "take effect.\n"
+ )
+ elif "default" in options.command.lower():
+ for ilotype, subsect in data.items():
+ _type = ilotype.split(".")[0]
+ for _path in subsect:
+ if not subsect[_path]:
+ continue
+ elif "ethernetinterface" in _type.lower() or "ethernetnetworkinterface" in _type.lower():
+ if "managers" in _path.lower():
+ self.load_ethernet_aux(_type, _path, data[ilotype][_path])
+ elif "systems" in _path.lower():
+ self.rdmc.ui.warn("Systems Ethernet Interfaces '%s' " "cannot be modified." % _path)
+ continue
+ elif "datetime" in _type.lower():
+ if "StaticNTPServers" in list(subsect.get(_path).keys()):
+ # must set NTP Servers to static in OEM then reset iLO for StaticNTPServers
+ # property to appear in iLODateTime
+ if self.rdmc.app.redfishinst.is_redfish:
+ eth_config_type = "ethernetinterface"
+ else:
+ eth_config_type = "ethernetnetworkinterface"
+ for key in list(data.keys()):
+ if key.split(".")[0].lower() == eth_config_type:
+ eth_config_type = key
+ for _path in data[eth_config_type]:
+ if "managers" in _path.lower():
+ try:
+ oemhp = self.rdmc.app.typepath.defs.oemhp
+ data[eth_config_type][_path]["DHCPv4"]["UseNTPServers"] = True
+ data[eth_config_type][_path]["DHCPv6"]["UseNTPServers"] = True
+ data[eth_config_type][_path]["Oem"][oemhp]["DHCPv4"][
+ "UseNTPServers"
+ ] = True
+ data[eth_config_type][_path]["Oem"][oemhp]["DHCPv6"][
+ "UseNTPServers"
+ ] = True
+ self.load_ethernet_aux(
+ eth_config_type,
+ _path,
+ data[eth_config_type][_path],
+ )
+ except KeyError:
+ self.rdmc.ui.printer(
+ "Unable to configure " "'UseNTPServers' for '%s'.\n" % _path
+ )
+ self.rdmc.ui.printer(
+ "iLO must be reset in order for "
+ "changes to static network time protocol servers to "
+ "take effect.\n"
+ )
+
+ def load_ethernet_aux(self, _type, _path, ethernet_data):
+ """helper function for parsing and bucketting ethernet properties.
+ :param _type: Redfish type used for querying the current server data
+ :type _type: string
+ :param _path: URI path to be patched.
+ :type _path: string
+ :param ethernet_data: JSON containing the ethernet instance (and valid, associated
+ properties) to be patched.
+ :type ethernet_data: JSON
+
+ """
+
+ support_ipv6 = True
+ dhcpv4curr = dhcpv4conf = oem_dhcpv4conf = dict()
+ _ = dhcpv6conf = oem_dhcpv6conf = dict()
+ errors = []
+
+ ident_eth = False
+ if "EthernetInterface" in _type:
+ for curr_sel in self.rdmc.app.select(
+ _type.split(".")[0] + ".",
+ (
+ self.rdmc.app.typepath.defs.hrefstring,
+ self.rdmc.app.typepath.defs.managerpath + "*",
+ ),
+ path_refresh=True,
+ ):
+ if curr_sel.path == _path:
+ ident_eth = True
+ break
+ # 'links/self/href' required when using iLO 4 (rest).
+ elif "EthernetNetworkInterface" in _type:
+ for curr_sel in self.rdmc.app.select(
+ _type.split(".")[0] + ".",
+ (
+ "links/self/" + self.rdmc.app.typepath.defs.hrefstring,
+ self.rdmc.app.typepath.defs.managerpath + "*",
+ ),
+ path_refresh=True,
+ ):
+ if curr_sel.path == _path:
+ ident_eth = True
+ break
+ else:
+ raise Exception("Invalid type in management NIC load operation: '%s'" % _type)
+
+ if not ident_eth:
+ raise InvalidPathError("Path: '%s' is invalid/not identified on this server.\n" % _path)
+
+ ident_name = curr_sel.dict.get("Name")
+ ident_id = curr_sel.dict.get("Id")
+ # ENABLING ETHERNET INTERFACE SECTION
+ try:
+ # Enable the Interface if called for and not already enabled
+ if ethernet_data.get("InterfaceEnabled") and not curr_sel.dict.get("InterfaceEnabled"):
+ self.rdmc.app.patch_handler(_path, {"InterfaceEnabled": True}, silent=True)
+ self.rdmc.ui.printer("NIC Interface Enabled.\n")
+ # Disable the Interface if called for and not disabled already
+ # No need to do anything else, just return
+ elif not ethernet_data.get("InterfaceEnabled") and not curr_sel.dict.get("InterfaceEnabled"):
+ self.rdmc.app.patch_handler(_path, {"InterfaceEnabled": False}, silent=True)
+ self.rdmc.ui.warn("NIC Interface Disabled. All additional configurations " "omitted.")
+ return
+ except (KeyError, NameError, TypeError, AttributeError):
+ # check OEM for NICEnabled instead
+ if (
+ not curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["NICEnabled"]
+ and ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["NICEnabled"]
+ ):
+ self.rdmc.app.patch_handler(
+ _path,
+ {"Oem": {self.rdmc.app.typepath.defs.oemhp: {"NICEnabled": True}}},
+ silent=True,
+ )
+ self.rdmc.ui.printer("NIC Interface Enabled.\n")
+ elif (
+ not curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["NICEnabled"]
+ and not ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["NICEnabled"]
+ ):
+ self.rdmc.app.patch_handler(
+ _path,
+ {"Oem": {self.rdmc.app.typepath.defs.oemhp: {"NICEnabled": False}}},
+ silent=True,
+ )
+ self.rdmc.ui.printer("NIC Interface Disabled.\n")
+ return
+ # except IloResponseError should just be raised and captured by decorator. No point in
+ # performing any other operations if the interface can not be set.
+
+ # END ENABLING ETHERNET INTEFACE SECTION
+ # ---------------------------------------
+ # DETERMINE DHCPv4 and DHCPv6 States and associated flags
+
+ if "NICSupportsIPv6" in list(curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp].keys()):
+ support_ipv6 = curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["NICSupportsIPv6"]
+
+ # obtain DHCPv4 Config and OEM
+ try:
+ if "DHCPv4" in list(curr_sel.dict.keys()) and "DHCPv4" in list(ethernet_data.keys()):
+ dhcpv4curr = copy.deepcopy(curr_sel.dict["DHCPv4"])
+ dhcpv4conf = copy.deepcopy(ethernet_data["DHCPv4"])
+ except (KeyError, NameError, TypeError, AttributeError):
+ errors.append("Unable to find Redfish DHCPv4 Settings.\n")
+ finally:
+ try:
+ oem_dhcpv4conf = copy.deepcopy(ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"])
+
+ except (KeyError, NameError):
+ errors.append("Unable to find OEM Keys for DHCPv4 or IPv4")
+
+ try:
+ if support_ipv6:
+ if "DHCPv6" in list(curr_sel.dict.keys()) and "DHCPv6" in list(ethernet_data.keys()):
+ dhcpv6conf = copy.deepcopy(ethernet_data["DHCPv6"])
+ else:
+ self.rdmc.ui.warn("NIC Does not support IPv6.")
+ except (KeyError, NameError, TypeError, AttributeError):
+ errors.append("Unable to find Redfish DHCPv6 Settings.\n")
+ finally:
+ try:
+ oem_dhcpv6conf = copy.deepcopy(ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv6"])
+ except (KeyError, NameError):
+ errors.append("Unable to find OEM Keys for DHCPv6 or IPv6")
+
+ try:
+ # if DHCP Enable request but not currently enabled
+ if dhcpv4conf.get("DHCPEnabled") and not curr_sel.dict["DHCPv4"]["DHCPEnabled"]:
+ self.rdmc.app.patch_handler(_path, {"DHCPv4": {"DHCPEnabled": True}}, silent=True)
+ self.rdmc.ui.printer("DHCP Enabled.\n")
+ # if DHCP Disable request but currently enabled
+ elif not dhcpv4conf["DHCPEnabled"] and curr_sel.dict["DHCPv4"]["DHCPEnabled"]:
+ self.rdmc.app.patch_handler(_path, {"DHCPv4": {"DHCPEnabled": False}}, silent=True)
+ dhcpv4conf["UseDNSServers"] = False
+ dhcpv4conf["UseNTPServers"] = False
+ dhcpv4conf["UseGateway"] = False
+ dhcpv4conf["UseDomainName"] = False
+ self.rdmc.ui.printer("DHCP Disabled.\n")
+ except (KeyError, NameError, TypeError, AttributeError):
+ # try with OEM
+ try:
+ if (
+ oem_dhcpv4conf.get("Enabled")
+ and not curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"]["Enabled"]
+ ):
+ self.rdmc.app.patch_handler(
+ _path,
+ {"Oem": {self.rdmc.app.typepath.defs.oemhp: {"DHCPv4": {"DHCPEnabled": True}}}},
+ silent=True,
+ )
+ self.rdmc.ui.printer("DHCP Enabled.\n")
+ if "IPv4Addresses" in ethernet_data:
+ del ethernet_data["IPv4Addresses"]
+ elif (
+ not oem_dhcpv4conf.get("Enabled")
+ and curr_sel.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"]["Enabled"]
+ ):
+ oem_dhcpv4conf["UseDNSServers"] = False
+ oem_dhcpv4conf["UseNTPServers"] = False
+ oem_dhcpv4conf["UseGateway"] = False
+ oem_dhcpv4conf["UseDomainName"] = False
+ self.rdmc.ui.printer("DHCP Disabled.\n")
+ except (KeyError, NameError) as exp:
+ errors.append("Failure in parsing or removing data in OEM DHCPv4: %s.\n" % exp)
+
+ try:
+ # if the ClientIDType is custom and we are missing the ClientID then this property can
+ # not be set.
+ if "ClientIdType" in list(dhcpv4conf.keys()):
+ if dhcpv4conf["ClientIdType"] == "Custom" and "ClientID" not in list(dhcpv4conf.keys()):
+ del ethernet_data["DHCPv4"]["ClientIdType"]
+ elif "ClientIdType" in list(oem_dhcpv4conf.keys()):
+ if oem_dhcpv4conf["ClientIdType"] == "Custom" and "ClientID" not in list(oem_dhcpv4conf.keys()):
+ del ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"]["ClientIdType"]
+ except (KeyError, NameError, TypeError, AttributeError):
+ try:
+ if "ClientIdType" in list(oem_dhcpv4conf.keys()):
+ if oem_dhcpv4conf["ClientIdType"] == "Custom" and "ClientID" not in list(oem_dhcpv4conf.keys()):
+ del ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DHCPv4"]["ClientIdType"]
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ # special considerations go here for things that need to stay despite diffdict
+ # EX: IPv4 addresses (aka bug). Changing only one property within the
+ # IPv4StaticAddresses or IPv4Addresses causes an issue during load. Must include IP,
+ # subnet mask and gateway (they can not be patched individually).
+ # spec_dict = {'Oem': {self.rdmc.app.typepath.defs.oemhp: {}}}
+ spec_dict = dict()
+ if "IPv4Addresses" in ethernet_data:
+ spec_dict["IPv4Addresses"] = copy.deepcopy(ethernet_data["IPv4Addresses"])
+ try:
+ if "IPv4Addresses" in ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]:
+ spec_dict["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv4Addresses"] = copy.deepcopy(
+ ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["IPv4StaticAddresses"]
+ )
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+
+ # diff and overwrite the original payload
+ ethernet_data = diffdict(ethernet_data, curr_sel.dict)
+ ethernet_data.update(spec_dict)
+
+ # verify dependencies on those flags which are to be applied are eliminated
+ try:
+ # delete Domain name and FQDN if UseDomainName for DHCPv4 or DHCPv6
+ # is present. can wait to apply at the end
+ if dhcpv4conf.get("UseDomainName"): # or dhcpv6conf['UseDomainName']:
+ if "DomainName" in ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]:
+ del ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DomainName"]
+ if "FQDN" in ethernet_data:
+ del ethernet_data["FQDN"]
+ except (KeyError, NameError, TypeError, AttributeError):
+ # try again with OEM
+ try:
+ if oem_dhcpv4conf.get("UseDomainName") or oem_dhcpv6conf.get("UseDomainName"):
+ if (
+ "Oem" in ethernet_data
+ and "DomainName" in ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ ):
+ del ethernet_data["Oem"][self.rdmc.app.typepath.defs.oemhp]["DomainName"]
+ if "FQDN" in ethernet_data:
+ del ethernet_data["FQDN"]
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ try:
+ # delete DHCP4 DNSServers from IPV4 dict if UseDNSServers Enabled
+ # can wait to apply at the end
+ if dhcpv4conf.get("UseDNSServers"): # and ethernet_data.get('NameServers'):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["NameServers"])
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+ finally:
+ try:
+ if oem_dhcpv4conf.get("UseDNSServers"):
+ # del_sections('DNSServers', ethernet_data)
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["DNSServers"])
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+ try:
+ if dhcpv4conf.get("UseWINSServers"):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["WINServers"])
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+ finally:
+ try:
+ if oem_dhcpv4conf.get("UseWINSServers"):
+ json_traversal_delete_empty(
+ data=ethernet_data,
+ remove_list=["WINServers", "WINSRegistration"],
+ )
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ try:
+ if dhcpv4conf.get("UseStaticRoutes"):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["StaticRoutes"])
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+ finally:
+ try:
+ if oem_dhcpv4conf.get("UseStaticRoutes"):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["StaticRoutes"])
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ try:
+ # if using DHCPv4, remove static addresses
+ if dhcpv4conf.get("DHCPEnabled"):
+ json_traversal_delete_empty(
+ data=ethernet_data,
+ remove_list=["IPv4Addresses", "IPv4StaticAddresses"],
+ )
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+ finally:
+ try:
+ if oem_dhcpv4conf.get("Enabled"):
+ json_traversal_delete_empty(
+ data=ethernet_data,
+ remove_list=["IPv4Addresses", "IPv4StaticAddresses"],
+ )
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ try:
+ # if not using DHCPv6, remove static addresses from payload
+ if dhcpv6conf.get("OperatingMode") == "Disabled":
+ json_traversal_delete_empty(
+ data=ethernet_data,
+ remove_list=["IPv6Addresses", "IPv6StaticAddresses"],
+ )
+ except (KeyError, NameError, TypeError, AttributeError):
+ pass
+ finally:
+ try:
+ if not oem_dhcpv6conf.get("StatefulModeEnabled"):
+ json_traversal_delete_empty(
+ data=ethernet_data,
+ remove_list=["IPv6Addresses", "IPv6StaticAddresses"],
+ )
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ flags = ethernet_data
+ if "StaticNameServers" in flags:
+ if dhcpv4curr["UseDNSServers"]:
+ flags["DHCPv4"] = {"UseDNSServers": False}
+
+ # verify dependencies on those flags which are to be applied are eliminated
+ if (
+ "IPv4Addresses" in flags
+ and "Address" in flags["IPv4Addresses"][0]
+ and "Address" in curr_sel.dict["IPv4Addresses"][0]
+ and flags["IPv4Addresses"][0]["Address"] == curr_sel.dict["IPv4Addresses"][0]["Address"]
+ and "Gateway" in flags["IPv4Addresses"][0]
+ and "Gateway" in curr_sel.dict["IPv4Addresses"][0]
+ and flags["IPv4Addresses"][0]["Gateway"] == curr_sel.dict["IPv4Addresses"][0]["Gateway"]
+ and "SubnetMask" in flags["IPv4Addresses"][0]
+ and "SubnetMask" in curr_sel.dict["IPv4Addresses"][0]
+ and flags["IPv4Addresses"][0]["SubnetMask"] == curr_sel.dict["IPv4Addresses"][0]["SubnetMask"]
+ ):
+ del flags["IPv4Addresses"]
+ try:
+ if not flags:
+ self.rdmc.ui.warn("No change in configurations in " + _path)
+ else:
+ self.rdmc.app.patch_handler(_path, flags, silent=True)
+ except IloResponseError as excp:
+ errors.append("iLO Responded with the following errors setting DHCP: %s.\n" % excp)
+
+ try:
+ if "AutoNeg" not in list(ethernet_data.keys()):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["FullDuplex", "SpeedMbps"])
+
+ # if Full Duplex exists, check if FullDuplexing enabled. If so,
+ # remove Speed setting.
+ elif "FullDuplex" in list(ethernet_data.keys()):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["FullDuplex", "SpeedMbps"])
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ try:
+ if "FrameSize" in list(ethernet_data.keys()):
+ json_traversal_delete_empty(data=ethernet_data, remove_list=["FrameSize"])
+ except (KeyError, NameError) as exp:
+ errors.append("Unable to remove property %s.\n" % exp)
+
+ if ethernet_data:
+ self.patch_eth(_path, ethernet_data, errors)
+
+ if errors and "Virtual" not in ident_name:
+ raise RdmcError(
+ "Ethernet configuration errors were found collectively on adapter: "
+ "'%s, %s'\ntype: %s\nerrors: %s" % (ident_name, ident_id, _type, errors)
+ )
+
+ def patch_eth(self, _path, eth_data, errors=[]):
+ """helper function for patching ethernet properties. Retry functionality with the ability
+ to remove the offending property.
+ :param _path: URI path to be patched.
+ :type _path: string
+ :param eth_data: JSON containing the ethernet instance (and valid, associated
+ properties) to be patched.
+ :type eth_data: JSON
+ :param errors: list of errors catalogued between attempts
+ :type errors: list
+
+ """
+
+ try:
+ if eth_data:
+ # eth_data = json.dumps(eth_data)
+ # import ast
+ # eth_data = ast.literal_eval(eth_data)
+ tmp = self.rdmc.app.patch_handler(_path, eth_data, silent=False, service=False)
+ if tmp.status == 400:
+ raise InvalidPropertyError(tmp.dict["error"][next(iter(tmp.dict["error"]))])
+ else:
+ raise NoDifferencesFoundError(
+ "No differences between existing iLO ethernet "
+ "configuration and new ethernet configuration.\nPath: %s\n" % _path
+ )
+
+ except InvalidPropertyError as excp:
+ errors.append("iLO Responded with the following error: %s.\n" % excp)
+
+ def drill_to_data(data, list_o_keys):
+ if len(list_o_keys) > 1:
+ k = list_o_keys.pop(0)
+ else:
+ del data[k]
+ if isinstance(data, dict):
+ drill_to_data(data[k], list_o_keys)
+
+ if hasattr(excp, "message"):
+ for key in excp.message[0]["MessageArgs"]:
+ try:
+ eth_data.pop(key)
+ except (AttributeError, KeyError, StopIteration):
+ try:
+ drill_to_data(
+ eth_data,
+ list_o_keys=json_traversal(eth_data, key, ret_key_path=True),
+ )
+ except:
+ errors.append("Unable to find '%s'" % key)
+ return
+ self.patch_eth(_path, eth_data)
+
+ except NoDifferencesFoundError as excp:
+ errors.append("%s" % excp)
+
+ def ethernetvalidation(self, options):
+ """ethernet validation function
+ :param options: command line options
+ :type options: list.
+ """
+
+ self.cmdbase.login_select_validation(self, options)
+
+ if options.ethfilename:
+ if len(options.ethfilename) < 2:
+ self.eth_file = options.ethfilename[0]
+ else:
+ raise InvalidCommandLineError("Only a single ethernet file may be specified.")
+ else:
+ self.eth_file = __eth_file__
+
+ @staticmethod
+ def options_argument_group(parser):
+ """Define option arguments group
+ :param parser: The parser to add the login option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+
+ parser.add_argument(
+ "--encryption",
+ dest="encryption",
+ help="Optionally include this flag to encrypt/decrypt a file" " using the key provided.",
+ default=None,
+ )
+ parser.add_argument(
+ "-f",
+ "--ethfile",
+ dest="ethfilename",
+ help="""Optionally specify a JSON file to store or load ethernet configuration data.""",
+ action="append",
+ default=None,
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for certificates command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ self.options_argument_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ default_parser = subcommand_parser.add_parser(
+ "default",
+ help="Obtain iLO management networking interface details and configure basic "
+ "properties such as enablement/disablement, domain name servers, ipv4 and ipv6 "
+ "networking configuration.",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ default_parser.add_argument(
+ "--enable_nic",
+ dest="enable_nic",
+ help="Enable network interfaces. List each interface to be enabled by ID: "
+ "Ex: ethernet --enable_nic 1,2,3. **Note**: Non-existent interfaces will be omitted from "
+ "configuration.",
+ type=str,
+ default=None,
+ )
+ default_parser.add_argument(
+ "--disable_nic",
+ dest="disable_nic",
+ help="Disable network interfaces. List each interface to be disabled by ID: "
+ "Ex: ethernet --disable_nic 1,2,3. **Note**: Non-existent interfaces will be omitted from "
+ "configuration.",
+ type=str,
+ default=None,
+ )
+ default_parser.add_argument(
+ "--enable_vnic",
+ dest="enable_vnic",
+ help="""Enable virtual network interfaces of management network.
+ Ex: ethernet --enable_vnic""",
+ action="store_true",
+ default=False,
+ )
+ default_parser.add_argument(
+ "--disable_vnic",
+ dest="disable_vnic",
+ help="""Disable virtual network interfaces of management network.
+ Ex: ethernet --disable_vnic""",
+ action="store_true",
+ default=False,
+ )
+ default_parser.add_argument(
+ "--disable_enhanced_downloads",
+ dest="disable_enhanced_downloads",
+ help="""Disable enhanced download for virtual media and firmware update.
+ Ex: ethernet --disable_enhanced_downloads""",
+ action="store_true",
+ default=False,
+ )
+ default_parser.add_argument(
+ "--enable_enhanced_downloads",
+ dest="enable_enhanced_downloads",
+ help="""Enable enhanced download for virtual media and firmware update.
+ Ex: ethernet --enable_enhanced_downloads""",
+ action="store_true",
+ default=False,
+ )
+ default_parser.add_argument(
+ "--nameservers",
+ dest="nameservers",
+ help="Configure physical and shared management network interface domain name "
+ "servers (DNS) in a list as follows: "
+ "Ex: ethernet --nameservers 8.8.8.8,1.1.1.1,2.2.2.2 ethernet --nameservers dns_resolver1.aws.com, "
+ "dns_resolver2.aws.com",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--proxy",
+ dest="proxy",
+ type=str,
+ help="Configure or clear proxy server for the network "
+ "Ex:\nTo set proxy\nethernet --proxy http://proxy.abc.net:8080 or \n"
+ "To clear proxy settings\n"
+ "ethernet --proxy None",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--network_ipv4",
+ dest="network_ipv4",
+ help="Configure physical and shared management network interface static IPv4 "
+ "settings. Settings provided in a list as follows: "
+ ", , . Ex: ethernet --network_ipv4 "
+ "192.168.1.10, 192.168.1.1, 255.255.0.0",
+ default=None,
+ )
+ default_parser.add_argument(
+ "--network_ipv6",
+ dest="network_ipv6",
+ help="Configure physical and shared management network interface static IPv6 "
+ "settings. Settings provided in a list as follows: "
+ ", , . Ex: ethernet --network_ipv6 "
+ "0:0:0:0:ffff:c0a8:10e, 0:0:0:0:0:ffff:c0a8:101, 64. **Note**: IPv6 network mask"
+ "is restricted to '64' bits.",
+ default=None,
+ )
+ default_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the " "displayed output to JSON format.",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(default_parser)
+ save_help = "Save a Network Configuration."
+ save_parser = subcommand_parser.add_parser(
+ __subparsers__[0],
+ help=save_help,
+ description="{0}\n\texample: ethernet save\n\n\tSave iLO ethernet network management interface "
+ "settings to a non-default file name.\n\tEx: ethernet save -f networking.json\n\n\tSave "
+ "an encrypted iLO networking configuration file\n\texample: ethernet save --encryption "
+ "".format(save_help),
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ self.cmdbase.add_login_arguments_group(save_parser)
+ self.options_argument_group(save_parser)
+ load_help = "Load a Network Configuration."
+ load_parser = subcommand_parser.add_parser(
+ __subparsers__[1],
+ help=load_help,
+ description="{0}\n\texample: ethernet load\n\n\tLoad iLO ethernet networking management interface "
+ "settings from a non-default file name.\n\tEx: ethernet load -f "
+ "networking.json\n\n\tLoad an encrypted iLO networking configuration file\n\texample: "
+ "ethernet load --encryption ".format(load_help),
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ load_parser.add_argument(
+ "--force_network_config",
+ dest="force_network_config",
+ help="Use this flag to force set network configuration."
+ "Network settings will be skipped if the flag is not included.",
+ action="store_true",
+ default=None,
+ )
+ self.cmdbase.add_login_arguments_group(load_parser)
+ self.options_argument_group(load_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FactoryDefaultsCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FactoryDefaultsCommand.py
new file mode 100644
index 0000000..ff98397
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FactoryDefaultsCommand.py
@@ -0,0 +1,135 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Factory Defaults Command for rdmc"""
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class FactoryDefaultsCommand:
+ """Reset server to factory default settings"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "factorydefaults",
+ "usage": None,
+ "description": "Reset iLO to factory defaults in the current logged in "
+ "server.\n\texample: factorydefaults",
+ "summary": "Resets iLO to factory defaults. WARNING: user data will " "be removed use with caution.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main factorydefaults function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("factorydefaults command takes no arguments.")
+
+ self.factorydefaultsvalidation(options)
+
+ self.rdmc.ui.warn("Resetting iLO to factory default settings.\n" "Current session will be terminated.\n")
+
+ select = "Manager."
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Manager. not found.")
+
+ bodydict = results.resp.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]
+
+ try:
+ for item in bodydict["Actions"]:
+ if "ResetToFactoryDefaults" in item:
+ if self.rdmc.app.typepath.defs.isgen10:
+ action = item.split("#")[-1]
+ else:
+ action = "ResetToFactoryDefaults"
+
+ path = bodydict["Actions"][item]["target"]
+ body = {"Action": action, "ResetType": "Default"}
+ break
+ except:
+ body = {
+ "Action": "ResetToFactoryDefaults",
+ "Target": "/Oem/Hp",
+ "ResetType": "Default",
+ }
+
+ self.rdmc.app.post_handler(path, body, service=True)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def factorydefaultsvalidation(self, options):
+ """factory defaults validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareIntegrityCheckCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareIntegrityCheckCommand.py
new file mode 100644
index 0000000..0148ca5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareIntegrityCheckCommand.py
@@ -0,0 +1,170 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Firmware Update Command for rdmc"""
+
+import time
+
+try:
+ from rdmc_helper import (
+ IloLicenseError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ TimeOutError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IloLicenseError,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ ReturnCodes,
+ TimeOutError,
+ )
+
+
+class FirmwareIntegrityCheckCommand:
+ """Reboot server that is currently logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "fwintegritycheck",
+ "usage": None,
+ "description": "Perform a firmware "
+ "integrity check on the current logged in server.\n\t"
+ "example: fwintegritycheck\n\n\tPerform a firmware integrity check and "
+ "return results of the check.\n\texmaple: fwintegritycheck --results",
+ "summary": "Perform a firmware integrity check on the currently logged in server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main firmware update worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ if args:
+ raise InvalidCommandLineError("fwintegritycheck command takes no arguments")
+
+ self.firmwareintegritycheckvalidation(options)
+ if self.rdmc.app.typepath.defs.isgen9:
+ raise IncompatibleiLOVersionError("fwintegritycheck command is " "only available on iLO 5.")
+
+ licenseres = self.rdmc.app.select(selector="HpeiLOLicense.")
+ try:
+ licenseres = licenseres[0]
+ except:
+ pass
+ if not licenseres.dict["LicenseFeatures"]["FWScan"]:
+ raise IloLicenseError("This command is not available with this iLO license.\n")
+
+ select = self.rdmc.app.typepath.defs.hpilofirmwareupdatetype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ bodydict = results.resp.dict
+ if "#HpeiLOUpdateServiceExt.StartFirmwareIntegrityCheck" in bodydict["Oem"]["Hpe"]["Actions"]:
+ path = bodydict["Oem"]["Hpe"]["Actions"]["#HpeiLOUpdateServiceExt.StartFirmwareIntegrityCheck"]["target"]
+ else:
+ raise InvalidCommandLineError("Firmware integrity check action not found.")
+
+ self.rdmc.app.post_handler(path, {})
+
+ if options.results:
+ results_string = "Awaiting results of firmware integrity check..."
+ self.rdmc.ui.printer(results_string)
+ polling = 50
+ found = False
+ while polling > 0:
+ if not polling % 5:
+ self.rdmc.ui.printer(".")
+ get_results = self.rdmc.app.get_handler(bodydict["@odata.id"], service=True, silent=True)
+ if get_results:
+ curr_time = time.strptime(bodydict["Oem"]["Hpe"]["CurrentTime"], "%Y-%m-%dT%H:%M:%SZ")
+ scan_time = time.strptime(
+ get_results.dict["Oem"]["Hpe"]["FirmwareIntegrity"]["LastScanTime"],
+ "%Y-%m-%dT%H:%M:%SZ",
+ )
+
+ if scan_time > curr_time:
+ self.rdmc.ui.printer(
+ "\nScan Result: %s\n"
+ % get_results.dict["Oem"]["Hpe"]["FirmwareIntegrity"]["LastScanResult"]
+ )
+ found = True
+ break
+
+ polling -= 1
+ time.sleep(1)
+ if not found:
+ self.rdmc.ui.error("\nPolling timed out before scan completed.\n")
+ TimeOutError("")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def firmwareintegritycheckvalidation(self, options):
+ """Firmware update method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--results",
+ dest="results",
+ help="Optionally include this flag to show results of firmware integrity check.",
+ default=False,
+ action="store_true",
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareUpdateCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareUpdateCommand.py
new file mode 100644
index 0000000..f363acd
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/FirmwareUpdateCommand.py
@@ -0,0 +1,325 @@
+###
+# Copyright 2016-2023 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Firmware Update Command for rdmc"""
+
+import time
+
+from redfish.ris.resp_handler import ResponseHandler
+
+try:
+ from rdmc_helper import (
+ FirmwareUpdateError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ FirmwareUpdateError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ )
+
+
+class FirmwareUpdateCommand:
+ """Reboot server that is currently logged in"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "firmwareupdate",
+ "usage": None,
+ "description": "Apply a firmware "
+ "update to the current logged in server.\n\texample: "
+ "firmwareupdate /images/image.bin\n\t"
+ "update based on targets GPU .\n\texample: "
+ "firmwareupdate --targets ",
+ "summary": "Perform a firmware update on the currently logged in server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main firmware update worker function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+ # Third Party FW
+
+ file_name = args[0].lower()
+ if args and (".pup" in file_name or ".fup" in file_name or ".hpb" in file_name or ".HPb" in file_name):
+ raise InvalidCommandLineErrorOPTS("For flashing 3rd party firmwares, please use flashfwpkg command")
+
+ # Supports only .bin/fwpkg/full/vme Firmware files.
+ if (
+ args
+ and (".bin" in file_name or ".fwpkg" in file_name or ".full" in file_name or ".vme" in file_name)
+ or ".lpk" in file_name
+ ):
+ if args[0].startswith('"') and args[0].endswith('"'):
+ args[0] = args[0][1:-1]
+ else:
+ raise InvalidCommandLineErrorOPTS("Please input URL pointing to a .bin or .fwpkg or .full or .vme firmware")
+
+ self.firmwareupdatevalidation(options)
+
+ action = None
+ uri = "FirmwareURI"
+
+ select = self.rdmc.app.typepath.defs.hpilofirmwareupdatetype
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ update_path = results.resp.request.path
+ else:
+ raise NoContentsFoundForOperationError("Unable to find %s" % select)
+
+ bodydict = results.resp.dict
+
+ try:
+ if options.targets:
+ for item in bodydict["Oem"]["Hpe"]["Actions"]:
+ if "AddFromUri" in item:
+ action = item.split("#")[-1]
+ uri = "ImageURI"
+ if action:
+ put_path = bodydict["Oem"]["Hpe"]["Actions"][item]["target"]
+ break
+ else:
+ for item in bodydict["Actions"]:
+ if self.rdmc.app.typepath.defs.isgen10:
+ if "SimpleUpdate" in item:
+ action = item.split("#")[-1]
+
+ uri = "ImageURI"
+ # options.tpmenabled = False
+ elif "InstallFromURI" in item:
+ action = "InstallFromURI"
+
+ if action:
+ put_path = bodydict["Actions"][item]["target"]
+ break
+ except:
+ put_path = update_path
+ action = "Reset"
+
+ if "AddFromUri" in action:
+ body = {
+ "Action": action,
+ uri: args[0],
+ "TPMOverrideFlag": options.tpmenabled,
+ }
+ elif "SimpleUpdate" in action:
+ body = {"Action": action, uri: args[0]}
+ if options.targets:
+ targets = []
+ target_list = options.targets.split(",")
+ for target in target_list:
+ target_url = "/redfish/v1/UpdateService/FirmwareInventory/" + str(target) + "/"
+ targets.append(target_url)
+ verified = self.verify_targets(options.targets)
+ if not verified:
+ self.rdmc.ui.error("Provided target was not available, Please provide valid target id\n")
+ return ReturnCodes.INVALID_TARGET_ERROR
+ body["UpdateTarget"] = options.updatetarget
+ body["UpdateRepository"] = options.updaterepository
+ body["Targets"] = targets
+ self.rdmc.ui.printer('payload: "%s"\n' % body)
+ self.rdmc.app.post_handler(put_path, body, silent=True, service=True)
+
+ self.rdmc.ui.printer("\nStarting upgrade process...\n\n")
+
+ self.showupdateprogress(update_path)
+ # logoutobj.run("")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def verify_targets(self, target):
+ target_list = target.split(",")
+ for target in target_list:
+ try:
+ target_url = "/redfish/v1/UpdateService/FirmwareInventory/" + target
+ dd = self.rdmc.app.get_handler(target_url, service=True, silent=True)
+ if dd.status == 404:
+ return False
+ except:
+ return False
+ return True
+
+ def showupdateprogress(self, path):
+ """handler function for updating the progress
+
+ :param path: path to update service.
+ :tyep path: str
+ """
+ counter = 0
+ written = False
+ uploadingpost = False
+ spinner = ["|", "/", "-", "\\"]
+ position = 0
+
+ while True:
+ if counter == 100:
+ raise FirmwareUpdateError(
+ "Error occurred while updating the firmware. Check if TPM is enabled. "
+ "And if TPM enabled provide --tpmenabled flag"
+ )
+ else:
+ counter += 1
+
+ results = self.rdmc.app.get_handler(path, silent=True)
+ results = results.dict
+ try:
+ results = results["Oem"]["Hpe"]
+ except:
+ pass
+
+ if not results:
+ raise FirmwareUpdateError("Unable to contact Update Service. " "Please re-login and try again.")
+
+ if results["State"].lower().startswith("idle"):
+ time.sleep(2)
+ elif results["State"].lower().startswith("uploading"):
+ counter = 0
+
+ if not uploadingpost:
+ uploadingpost = True
+ else:
+ if not written:
+ written = True
+ self.rdmc.ui.printer("\n iLO is uploading the necessary files. Please wait...\n")
+
+ time.sleep(0.5)
+ elif results["State"].lower().startswith(("progressing", "updating", "verifying", "writing", "backingup")):
+ counter = 0
+
+ for _ in range(2):
+ if position < 4:
+ self.rdmc.ui.printer("Updating: " + spinner[position] + "\r")
+ position += 1
+ time.sleep(0.1)
+ else:
+ position = 0
+ self.rdmc.ui.printer("Updating: " + spinner[position] + "\r")
+ position += 1
+ time.sleep(0.1)
+ elif results["State"].lower().startswith("complete"):
+ self.rdmc.ui.printer(
+ "\n\nFirmware update has completed and iLO"
+ " may reset. \nIf iLO resets the"
+ " session will be terminated.\nPlease wait"
+ " for iLO to initialize completely before"
+ " logging in again.\nA reboot may be required"
+ " for firmware changes to take effect.\n"
+ )
+ break
+ elif results["State"].lower().startswith("error"):
+ error = self.rdmc.app.get_handler(path, silent=True)
+ self.printerrmsg(error)
+
+ def printerrmsg(self, error):
+ """raises and prints the detailed error message if possible"""
+ output = "Error occurred while updating the firmware."
+
+ try:
+ error = error.dict["Oem"]["Hpe"]["Result"]["MessageId"].split(".")
+ # TODO: Update to new ResponseHandler Method 'return_reg'
+ errmessages = ResponseHandler(
+ self.rdmc.app.validation_manager,
+ self.rdmc.app.typepath.defs.messageregistrytype,
+ ).get_error_messages()
+ for messagetype in list(errmessages.keys()):
+ if error[0] == messagetype:
+ if errmessages[messagetype][error[-1]]["NumberOfArgs"] == 0:
+ output = "Firmware update error. %s" % errmessages[messagetype][error[-1]]["Message"]
+ else:
+ output = "Firmware update error. %s" % errmessages[messagetype][error[-1]]["Description"]
+ break
+ except:
+ pass
+
+ raise FirmwareUpdateError(output)
+
+ def firmwareupdatevalidation(self, options):
+ """Firmware update method validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "--tpmenabled",
+ dest="tpmenabled",
+ action="store_true",
+ help="Use this flag if the server you are currently logged into" " has a TPM chip installed.",
+ default=False,
+ )
+ customparser.add_argument(
+ "--updatetarget",
+ dest="updatetarget",
+ action="store_true",
+ help="UpdateTarget set as true/false" "if true firmware updates",
+ default=True,
+ )
+ customparser.add_argument(
+ "--updaterepository",
+ dest="updaterepository",
+ action="store_false",
+ help="UpdateRepository set as true or false" " if true it will add repository.",
+ default=True,
+ )
+ customparser.add_argument(
+ "--targets",
+ help="If targets value specify a comma separated\t" "firmwareinventory id only",
+ metavar="targets_indices",
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IPProfilesCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IPProfilesCommand.py
new file mode 100644
index 0000000..eb7a482
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IPProfilesCommand.py
@@ -0,0 +1,701 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""RawGet Command for rdmc"""
+
+import base64
+import gzip
+import json
+import os
+import re
+import time
+from ctypes import create_string_buffer
+from datetime import datetime
+import six
+from six import BytesIO
+
+if six.PY3:
+ from datetime import timezone
+
+
+import redfish
+from redfish.hpilo.risblobstore2 import BlobStore2
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ PathUnavailableError,
+ ReturnCodes,
+ UnableToDecodeError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ InvalidCommandLineErrorOPTS,
+ InvalidFileFormattingError,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ PathUnavailableError,
+ ReturnCodes,
+ UnableToDecodeError,
+ )
+
+
+class IPProfilesCommand:
+ """Raw form of the get command"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "ipprofiles",
+ "usage": None,
+ "description": "Decodes and lists "
+ "ipprofiles. This is default option. No argument required"
+ "\n\tExample: ipprofiles"
+ "\n\n\tAdds a new ipprofile from the provided json file."
+ "\n\tNOTE: Path can be absolute or from the "
+ "same path you launch iLOrest."
+ "\n\tipprofiles "
+ "\n\n\tDelete an ipprofile or list of profiles.\n\t"
+ "Provide the unique key that corresponds to the ipprofile"
+ " data you want to delete.\n\tSeveral IDs can be comma-separated"
+ " with no space in between to delete more than one profile. "
+ "\n\tipprofiles --delete ID1,ID2,ID3..."
+ "\n\n\tCopies ip profile with the specified ID into the ip job queue."
+ "and starts it.\n\texample: ipprofiles --start=",
+ "summary": "This is used to manage hpeipprofile data store.",
+ "aliases": [],
+ "auxcommands": ["BootOrderCommand", "RebootCommand"],
+ }
+ self.path = ""
+ self.ipjobs = ""
+ self.running_jobs = ""
+ self.hvt_output = ""
+ self.ipjobtype = ["langsel", "hvt", "ssa", "install", "rbsu"]
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main raw get worker function
+
+ :param line: command line input
+ :type line: string.
+ :param help_disp: display help flag
+ :type line: bool.
+
+ """
+ if help_disp:
+ self.parser.print_help()
+ return ReturnCodes.SUCCESS
+ try:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.getpaths()
+
+ self.validation(options)
+
+ self.ipprofileworkerfunction(options, args)
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def ipprofileworkerfunction(self, options, args):
+ """
+ Ipprofile manager worker function. It calls appropriate function.
+ :param options: command line options
+ :type options: list.
+ :param args: command line args
+ :type args: string.
+ """
+
+ if options.running_jobs:
+ self.get_running_job()
+ return ReturnCodes.SUCCESS
+ if options.get_hvt:
+ if options.filename:
+ self.rdmc.ui.warn("iprprofiles -D option does not support -f, please remove -f and rerun\n")
+ return ReturnCodes.SUCCESS
+ self.get_hvt_output()
+ return ReturnCodes.SUCCESS
+ if options.del_key:
+ self.deletekeyfromipprofiledata(options)
+ return ReturnCodes.SUCCESS
+
+ if options.start_ip:
+ self.addprofileandstartjob(options)
+ return ReturnCodes.SUCCESS
+
+ if len(args) == 1:
+ self.encodeandpatchipprofiledata(args)
+ return ReturnCodes.SUCCESS
+
+ self.getipprofiledataanddecode(options)
+
+ def getipprofiledataanddecode(self, options):
+ """
+ Retrieves and decodes, if encoded, data from hpeprofile data store
+ :param options: command line options
+ :type options: list.
+ :return returncode: int
+ """
+
+ results = self.rdmc.app.get_handler(self.path, silent=True)
+ if results.status == 404:
+ raise PathUnavailableError(
+ "The Intelligent Provisioning resource "
+ "is not available on this system. You may need"
+ " to run IP at least once to add the resource."
+ )
+
+ if results and results.status == 200:
+ j2python = json.loads(results.read)
+ for _, val in enumerate(j2python.keys()):
+ if isinstance(val, six.string_types):
+ result = self.decode_base64_string(str(j2python[val]))
+ if result is not None:
+ j2python[val] = result
+
+ results.read = json.dumps(j2python, ensure_ascii=False, sort_keys=True)
+ if results.dict:
+ if options.filename:
+ output = json.dumps(
+ results.dict,
+ indent=2,
+ cls=redfish.ris.JSONEncoder,
+ sort_keys=True,
+ )
+
+ filehndl = open(options.filename[0], "w")
+ filehndl.write(output)
+ filehndl.close()
+
+ self.rdmc.ui.printer("Results written out to '%s'.\n" % options.filename[0])
+ else:
+ self.rdmc.ui.print_out_json(results.dict)
+ else:
+ self.rdmc.ui.warn("No IP profiles found\n")
+
+ def get_running_job(self):
+ """
+ Retrieves and decodes, running job
+ :return returncode: int
+ """
+
+ results = self.rdmc.app.get_handler(self.running_jobs, silent=True)
+ if results.status == 404:
+ raise PathUnavailableError(
+ "The Intelligent Provisioning resource "
+ "is not available on this system. You may need"
+ " to run IP at least once to add the resource."
+ )
+
+ if results and results.status == 200:
+ j2python = json.loads(results.read)
+ for _, val in enumerate(list(j2python.keys())):
+ if isinstance(val, six.string_types):
+ result = self.decode_base64_string(str(j2python[val]))
+ if result is not None:
+ j2python[val] = result
+
+ results.read = json.dumps(j2python, ensure_ascii=False)
+ if results.dict:
+ self.rdmc.ui.print_out_json(results.dict)
+ else:
+ self.rdmc.ui.warn("No IP profiles found\n")
+
+ def get_hvt_output(self):
+ """
+ Retrieves and decodes, running job
+ :return returncode: int
+ """
+ return_value = {}
+ results = self.rdmc.app.get_handler(self.hvt_output, silent=True)
+ if results.status == 404:
+ raise PathUnavailableError(
+ "The Intelligent Provisioning resource "
+ "is not available on this system. You may need"
+ " to run IP at least once to add the resource."
+ )
+
+ if results and results.status == 200:
+ j2python = json.loads(results.read)
+ for _, val in enumerate(list(j2python.keys())):
+ if isinstance(val, six.string_types) and "@" not in val:
+ return_value = json.loads(self.decode_base64_string(str(j2python[val])))
+ self.rdmc.ui.print_out_json(return_value)
+ else:
+ self.rdmc.ui.error("No IP profiles found\n")
+
+ def encodeandpatchipprofiledata(self, args):
+ """
+ Reads file in the given path, encode it,
+ and apply it on iLO hpeipprofiles data store.
+ :param args: command line args
+ :type args: string.
+ :retirn returncode: int
+ """
+
+ contentsholder = self.encode_base64_string(args)
+
+ if "path" in contentsholder and "body" in contentsholder:
+ self.rdmc.app.patch_handler(contentsholder["path"], contentsholder["body"])
+
+ return ReturnCodes.SUCCESS
+
+ def deletekeyfromipprofiledata(self, options):
+ """
+ Provide a string which represents a valid key in
+ hpeipprofiles data store.
+ :param options: command line options
+ :type options: list.
+ :return returncode: int
+ """
+
+ get_results = self.rdmc.app.get_handler(self.path, silent=True)
+
+ j2python = json.loads(get_results.read)
+ all_keys = options.del_key[0].split(",")
+ for key in all_keys:
+ if isinstance(key, six.string_types) and j2python.get(key.strip(), False):
+ del j2python[key.strip()]
+ else:
+ raise InvalidFileFormattingError("%s was not found .\n" % key)
+
+ payload = {"path": self.path, "body": j2python}
+
+ self.rdmc.app.put_handler(payload["path"], payload["body"])
+
+ return ReturnCodes.SUCCESS
+
+ def addprofileandstartjob(self, options):
+ """
+ Adds ip profile into the job queue and start it.
+ :return returncode: int
+ """
+
+ ipprovider = self.hasipprovider()
+ if ipprovider is None:
+ raise PathUnavailableError("System does not support this feature of IP.\n")
+
+ ipjob = self.hasipjobs()
+ if not ipjob:
+ raise InvalidFileFormattingError("System does not have any IP" " profile to copy to the job queue.\n")
+
+ current_state = self.inipstate(ipprovider)
+ if current_state is None:
+ raise PathUnavailableError("System does not support this feature of IP.\n")
+
+ later_state = False
+ ipstate = current_state["InIP"]
+ if isinstance(ipstate, bool) and ipstate:
+ # make sure we are in IP state. Reset and monitor
+ self.resetinipstate(ipprovider, current_state)
+ # if we are in ip, monitor should be fast, use 15 seconds
+ later_status = self.monitorinipstate(ipprovider, 3)
+ if later_status:
+ self.rdmc.ui.printer("Server is in IP state. Powering on...\n")
+ self.copyjobtoipqueue(ipjob, options.start_ip)
+ self.rdmc.ui.printer("Copy operation was successful...\n")
+ return ReturnCodes.SUCCESS
+
+ if not isinstance(ipstate, bool):
+ # inip is in an unknown state, so ...
+ # patch to false, reboot, then monitor...if it turns true later...
+ # then we are in IP state otherwise, manually check system...
+ self.resetinipstate(ipprovider, current_state)
+ later_status = self.monitorinipstate(ipprovider, 3)
+ if later_status:
+ self.copyjobtoipqueue(ipjob, options.start_ip)
+ self.rdmc.ui.printer("Copy operation was successful...\n")
+ return ReturnCodes.SUCCESS
+
+ # Check if the server is On or Off, If it is Off, make it on
+ path = self.rdmc.app.typepath.defs.systempath
+ get_results = self.rdmc.app.get_handler(path, silent=True)
+ result = json.loads(get_results.read)
+ if result and result.get("PowerState") == "Off":
+ self.rdmc.ui.printer("Server is in OFF state. Powering on...\n")
+ self.auxcommands["bootorder"].run("--onetimeboot=Utilities " "--reboot=On --commit")
+ elif result and result.get("Oem").get("Hpe").get("PostState") != "FinishedPost":
+ self.rdmc.ui.printer("Server is in POST state. Restarting...\n")
+ # Forcefully switch off as it is stuck in POST
+ self.auxcommands["reboot"].run("ForceOff")
+ time.sleep(5)
+ # Switch on with bootorder set
+ self.auxcommands["bootorder"].run("--onetimeboot=Utilities " "--reboot=On --commit")
+ # Check to find out if server in POST
+ # count = 0
+ # in_post = True
+ # path = self.rdmc.app.typepath.defs.systempath
+ # while (count < 5):
+ # count = count + 1
+ # get_results = self.rdmc.app.get_handler(path, silent=True)
+ # result = json.loads(get_results.read)
+ # if result and result.get("Oem").get("Hpe").get("PostState") == "FinishedPost":
+ # in_post = False
+ # break
+ # else:
+ # # Sleep for 1 minutes for 5 times.
+ # self.rdmc.ui.printer("System in POST...waiting for 1 min...\n")
+ # time.sleep(60)
+
+ # if in_post:
+ # raise NoContentsFoundForOperationError(
+ # "Server still in POST even after 5 minutes - Please check"
+ # )
+ else:
+ self.rdmc.ui.printer("Server is in OS state. ColdBooting...\n")
+ try:
+ self.auxcommands["bootorder"].run("--onetimeboot=Utilities " "--reboot=ColdBoot --commit")
+ except:
+ raise InvalidFileFormattingError("System failed to reboot")
+
+ # After reboot, login again
+ time.sleep(options.sleep_time) # Sleep until reboot
+ self.validation(options)
+
+ later_state = self.monitorinipstate(ipprovider)
+ if later_state:
+ self.copyjobtoipqueue(ipjob, options.start_ip)
+ self.rdmc.ui.printer("Copy operation was successful...\n")
+ else:
+ raise InvalidFileFormattingError(
+ "\nSystem reboot took longer than 4 minutes."
+ "something is wrong. You need to physically check this system.\n"
+ )
+
+ return ReturnCodes.SUCCESS
+
+ def resetinipstate(self, ipprovider, current_state):
+ """
+ Regardless of previous value, sets InIP value to False
+ :param ipprovider: url path of heip.
+ :type ipprovider: string.
+ :param current_state: the value of InIP.
+ :type current_state: dict
+ """
+
+ current_state["InIP"] = False
+
+ payload = {"path": ipprovider, "body": current_state}
+
+ self.rdmc.app.put_handler(payload["path"], payload["body"], silent=True)
+
+ def monitorinipstate(self, ipprovider, timer=48):
+ """
+ Monitor InIP value every 5 seconds until it turns true or time expires.
+ :param ipprovider: url path of heip.
+ :type ipprovider: string.
+ :param timer: time it takes iLO to boot into F10 assuming we are in boot state.
+ :type timer: int
+ :return ipstate: boolean
+ """
+
+ retry = timer # 48 * 5 = 4 minutes
+ ipstate = False
+ progress = self.progressbar()
+ self.rdmc.ui.printer("\n")
+ while retry > 0:
+ time.sleep(5)
+ next(progress)
+ status = self.inipstate(ipprovider)
+ if isinstance(status["InIP"], bool) and status["InIP"]:
+ ipstate = True
+ break
+ retry = retry - 1
+ self.rdmc.ui.printer("\n")
+
+ return ipstate
+
+ def progressbar(self):
+ """
+ An on demand function use to output the progress while iLO is booting into F10.
+ """
+ while True:
+ yield self.rdmc.ui.printer(">>>")
+
+ def copyjobtoipqueue(self, ipjobs, jobkey):
+ """
+ Copies HpeIpJob to Job queue. Function assumes there is a job to copy.
+ A check was already done to make sure we have a job to copy in hasipjobs()
+ :param ipjobs: url path of heip.
+ :type ipjobs: list of dictionary.
+ :param jobkey: key of job to copy.
+ :type jokbey: str.
+ """
+
+ get_results = self.rdmc.app.get_handler(self.path, silent=True)
+
+ j2python = json.loads(get_results.read)
+ copy_job = {}
+ for ipj in j2python:
+ if jobkey == ipj:
+ _decode = self.decode_base64_string(j2python[ipj])
+ if _decode is not None:
+ _critical_props = {
+ "log": '[{"msg": "WAITINGTOBEPROCESSED", "percent": 0}]',
+ "status": "waiting",
+ }
+ copy_job.update(
+ {
+ k: v.update(_critical_props) or v
+ for k, v in json.loads(_decode).items()
+ if k in self.ipjobtype
+ }
+ )
+ else:
+ raise NoContentsFoundForOperationError("Not supported profile content")
+ break
+ if not copy_job:
+ raise NoContentsFoundForOperationError("The ID %s does not match any ipprofile" % jobkey)
+ payload = {"path": self.ipjobs, "body": copy_job}
+
+ self.rdmc.app.put_handler(payload["path"], payload["body"])
+
+ def inipstate(self, ipprovider):
+ """
+ A check is done to determine if this version of iLO has InIP profile.
+ :param ipprovider: url path of heip.
+ :type ipprovider: string.
+ :return is_inip: None or dict
+ """
+
+ if ipprovider.startswith("/redfish/"):
+ get_results = self.rdmc.app.get_handler(ipprovider, silent=True)
+ result = json.loads(get_results.read)
+
+ is_inip = None
+ try:
+ if "InIP" in list(result.keys()):
+ is_inip = result
+ except KeyError:
+ pass
+
+ return is_inip
+
+ def hasipjobs(self):
+ """
+ A check is done to determine if there is a job in HpeIpJobs we can
+ copy to IP job queue
+ :param options: command line options
+ :type options: list.
+ :return list_dict: list of dicts
+ """
+
+ results = self.rdmc.app.get_handler(self.path, silent=True)
+
+ j2python = json.loads(results.read)
+ for _, val in enumerate(j2python.keys()):
+ if isinstance(val, six.string_types):
+ result = self.decode_base64_string(str(j2python[val]))
+ if result is not None:
+ j2python[val] = result
+
+ list_dict = []
+
+ for key, value in j2python.items():
+ if not re.match("@odata", key):
+ if len(key) >= 13 and key.isdigit():
+ list_dict.append({key: value}) # list of dict with valid key/value
+
+ return list_dict
+
+ def hasipprovider(self):
+ """
+ A check is done here to determine if this version of iLO has IP provider
+ profile path using the "Oem.Hpe.Links.HpeIpProvider"
+
+ :return is_provider: None or string.
+ """
+ path = self.rdmc.app.typepath.defs.systempath
+ get_results = self.rdmc.app.get_handler(path, silent=True)
+
+ result = json.loads(get_results.read)
+
+ is_ipprovider = None
+ try:
+ is_ipprovider = list(result["Oem"]["Hpe"]["Links"]["HpeIpProvider"].values())[0]
+ except KeyError:
+ pass
+
+ return is_ipprovider
+
+ def validation(self, options):
+ """IPProfiles validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def decode_base64_string(self, str_b64):
+ """
+ Decodes a given string that was encoded with base64 and gzipped.
+ :param str_b64: a string that was base64 encoded and the gzipped
+ :type str_b64: string.
+ """
+
+ read_data = None
+ if isinstance(str_b64, six.string_types) and str_b64:
+ try:
+ decoded_str = base64.decodebytes(str_b64.encode("utf-8"))
+ inbuffer = BytesIO(decoded_str)
+ gzffile = gzip.GzipFile(mode="rb", fileobj=inbuffer)
+ read_data = ""
+ for line in gzffile.readlines():
+ read_data = read_data + line.decode("utf-8")
+ except:
+ pass
+
+ return read_data
+
+ def encode_base64_string(self, args):
+ """
+ Encode a given string with base64 and gzip it.
+ :param args: command line args
+ :type args: string.
+ """
+
+ payload = {}
+ filename = args[0]
+ if filename:
+ if not os.path.isfile(filename):
+ raise InvalidFileInputError(
+ "File '%s' doesn't exist. " "Please create file by running 'save' command." % filename
+ )
+
+ try:
+ with open(filename, "r") as fh:
+ contentsholder = json.loads(fh.read())
+ except:
+ raise InvalidFileFormattingError("Input file '%s' was not " "format properly." % filename)
+
+ try:
+ text = json.dumps(contentsholder).encode("utf-8")
+ buf = BytesIO()
+ gzfile = gzip.GzipFile(mode="wb", fileobj=buf)
+ gzfile.write(text)
+ gzfile.close()
+
+ en_text = base64.encodebytes(buf.getvalue()).decode("utf-8")
+
+ if six.PY3:
+ epoch = datetime.fromtimestamp(0, tz=timezone.utc)
+ now = datetime.now(tz=timezone.utc)
+ elif six.PY2:
+ epoch = datetime.utcfromtimestamp(0)
+ now = datetime.utcnow()
+ delta = now - epoch
+ time_stamp = delta.total_seconds() * 1000
+ time_stamp = repr(time_stamp).split(".")[0]
+
+ body_text = {time_stamp: en_text.strip()}
+
+ payload["body"] = body_text
+ if isinstance(self.path, bytes):
+ self.path = self.path.decode("utf-8")
+ payload["path"] = self.path
+ except Exception as excp:
+ raise UnableToDecodeError("Error while encoding string %s." % excp)
+
+ return payload
+
+ def getpaths(self):
+ """Get paths for ipprofiles command"""
+ if not all(iter([self.path, self.ipjobs, self.running_jobs, self.hvt_output])):
+ dll = BlobStore2.gethprestchifhandle()
+
+ profiles_path = create_string_buffer(50)
+ jobs_path = create_string_buffer(50)
+ running_jobs_path = create_string_buffer(50)
+ hvt_output_path = create_string_buffer(50)
+
+ dll.get_ip_profiles(profiles_path)
+ dll.get_ip_jobs(jobs_path)
+ dll.get_running_jobs(running_jobs_path)
+ dll.get_hvt_output(hvt_output_path)
+
+ self.path = profiles_path.value
+ self.ipjobs = jobs_path.value
+ self.running_jobs = running_jobs_path.value
+ self.hvt_output = hvt_output_path.value
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ customparser.add_argument(
+ "-r",
+ "--running",
+ dest="running_jobs",
+ default=False,
+ action="store_true",
+ help="""Show status of the currently running or last job executed""",
+ )
+ customparser.add_argument(
+ "-D",
+ "--diags",
+ help="""Get result of last HVT (diagnostics) run as part of an ipprofile job""",
+ default=False,
+ action="store_true",
+ dest="get_hvt",
+ )
+ customparser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="""Write results to the specified file.""",
+ action="append",
+ default=None,
+ )
+ customparser.add_argument(
+ "--delete",
+ dest="del_key",
+ action="append",
+ help="Look for the key or keys in the ipprofile manager and delete",
+ default=None,
+ )
+ customparser.add_argument(
+ "-s",
+ "--start",
+ dest="start_ip",
+ help="Copies the specified ip profile into the job queue and starts it",
+ default=None,
+ )
+ customparser.add_argument(
+ "-t",
+ "--sleeptime",
+ dest="sleep_time",
+ type=int,
+ help="Sleep time in seconds when server in OS mode and rebooted",
+ default=320,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloAccountsCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloAccountsCommand.py
new file mode 100644
index 0000000..27e1f4c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloAccountsCommand.py
@@ -0,0 +1,780 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Add Account Command for rdmc"""
+
+import getpass
+import os
+from argparse import Action, RawDescriptionHelpFormatter
+
+from redfish.ris.ris import SessionExpired
+from redfish.ris.rmc_helper import IdTokenError
+
+try:
+ from rdmc_helper import (
+ Encryption,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ Encryption,
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ )
+
+__subparsers__ = ["add", "modify", "changepass", "delete", "addcert", "deletecert"]
+
+
+class _AccountParse(Action):
+ def __init__(self, option_strings, dest, nargs, **kwargs):
+ super(_AccountParse, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_strings):
+ """Account privileges option helper"""
+
+ privkey = {
+ 1: "LoginPriv",
+ 2: "RemoteConsolePriv",
+ 3: "UserConfigPriv",
+ 4: "iLOConfigPriv",
+ 5: "VirtualMediaPriv",
+ 6: "VirtualPowerAndResetPriv",
+ 7: "HostNICConfigPriv",
+ 8: "HostBIOSConfigPriv",
+ 9: "HostStorageConfigPriv",
+ 10: "SystemRecoveryConfigPriv",
+ }
+
+ for priv in next(iter(values)).split(","):
+ try:
+ priv = int(priv)
+ except ValueError:
+ try:
+ parser.error("Invalid privilege entered: %s. Privileges must " "be numbers." % priv)
+ except:
+ raise InvalidCommandLineErrorOPTS("")
+ try:
+ if not isinstance(namespace.optprivs, list):
+ namespace.optprivs = list()
+ if option_strings.startswith("--add"):
+ namespace.optprivs.append({privkey[priv]: True})
+ elif option_strings.startswith("--remove"):
+ namespace.optprivs.append({privkey[priv]: False})
+ except KeyError:
+ try:
+ parser.error(
+ "Invalid privilege entered: %s. Number does not " "match an available privilege." % priv
+ )
+ except:
+ raise InvalidCommandLineErrorOPTS("")
+
+ # account_parse.counter = 0
+
+
+class IloAccountsCommand:
+ """command to manipulate/add ilo user accounts"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "iloaccounts",
+ "usage": None,
+ "description": "\tView, Add, Remove, and Modify iLO accounts based on the "
+ "sub-command used.\n\n\tTo view help on specific sub-commands run: "
+ "iloaccounts -h\n\t"
+ "Example: iloaccounts add -h\n\tNote: \n\t\t1. UserName and LoginName are reversed "
+ "in the iLO GUI for Redfish compatibility.\n\t\t"
+ "2. While executing the command: iloaccounts add in a Linux machine, "
+ "an escape character needs to be added before a special character in the password.\n\t\t\t"
+ "Example: iloaccount add rest rest 12iso*help\n",
+ "summary": "Views/Adds/deletes/modifies an iLO account on the currently logged in server.",
+ "aliases": ["iloaccount"],
+ "auxcommands": [],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main iloaccounts function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ acct = mod_acct = None
+ try:
+ ident_subparser = False
+ for cmnd in __subparsers__:
+ if cmnd in line:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ ident_subparser = True
+ break
+ if not ident_subparser:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line, default=True)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.iloaccountsvalidation(options)
+
+ redfish = self.rdmc.app.monolith.is_redfish
+ path = self.rdmc.app.typepath.defs.accountspath
+ results = self.rdmc.app.get_handler(path, service=True, silent=True).dict
+
+ if redfish:
+ results = results["Members"]
+ else:
+ if "Member" in results["links"]:
+ results = results["links"]["Member"]
+ else:
+ results = list()
+
+ for indx, acct in enumerate(results):
+ acct = self.rdmc.app.get_handler(
+ acct[self.rdmc.app.typepath.defs.hrefstring], service=True, silent=True
+ ).dict
+ try:
+ if hasattr(options, "identifier"):
+ if acct["Id"] == options.identifier or acct["UserName"] == options.identifier:
+ if redfish:
+ path = acct["@odata.id"]
+ else:
+ path = acct["links"]["self"]["href"]
+ mod_acct = acct
+ elif options.command == "default":
+ results[indx] = acct
+ else:
+ raise KeyError
+ except KeyError:
+ continue
+ else:
+ if mod_acct:
+ acct = mod_acct
+ break
+ else:
+ acct = None
+
+ if not results:
+ raise NoContentsFoundForOperationError("No iLO Management Accounts were found.")
+
+ outdict = dict()
+ if options.command.lower() == "default":
+ if not options.json:
+ self.rdmc.ui.printer(
+ "\niLO Account info:\n\n[Id] UserName (LoginName): " "\nPrivileges\n-----------------\n\n",
+ verbose_override=True,
+ )
+ for acct in sorted(results, key=lambda k: int(k["Id"])):
+ privstr = ""
+ privs = acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["Privileges"]
+
+ if (
+ "ServiceAccount" in list(acct["Oem"][self.rdmc.app.typepath.defs.oemhp].keys())
+ and acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["ServiceAccount"]
+ ):
+ service = "ServiceAccount=True"
+ else:
+ service = "ServiceAccount=False"
+ if not options.json:
+ for priv in privs:
+ privstr += priv + "=" + str(privs[priv]) + "\n"
+ self.rdmc.ui.printer(
+ "[%s] %s (%s):\n%s\n%s\n"
+ % (
+ acct["Id"],
+ acct["UserName"],
+ acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["LoginName"],
+ service,
+ privstr,
+ ),
+ verbose_override=True,
+ )
+ keyval = "[" + str(acct["Id"]) + "] " + acct["UserName"]
+ outdict[keyval] = privs
+ outdict[keyval]["ServiceAccount"] = service.split("=")[-1].lower()
+ if options.json:
+ self.rdmc.ui.print_out_json_ordered(outdict)
+ elif options.command.lower() == "changepass":
+ if not acct:
+ raise InvalidCommandLineError("Unable to find the specified account.")
+ if not options.acct_password:
+ self.rdmc.ui.printer("Please input the new password.\n", verbose_override=True)
+ tempinput = getpass.getpass()
+ self.credentialsvalidation("", "", tempinput, "", True, options)
+ options.acct_password = tempinput
+
+ self.credentialsvalidation("", "", options.acct_password.split("\r")[0], "", True)
+ body = {"Password": options.acct_password.split("\r")[0]}
+
+ if path and body:
+ self.rdmc.app.patch_handler(path, body)
+ else:
+ raise NoContentsFoundForOperationError("Unable to find the specified account.")
+
+ elif options.command.lower() == "add":
+ if options.encode:
+ args[2] = Encryption.decode_credentials(args[2])
+ if isinstance(args[2], bytes):
+ args[2] = args[2].decode("utf-8")
+
+ privs = self.getprivs(options)
+ path = self.rdmc.app.typepath.defs.accountspath
+
+ body = {
+ "UserName": options.identifier,
+ "Password": options.acct_password,
+ "Oem": {self.rdmc.app.typepath.defs.oemhp: {"LoginName": options.loginname}},
+ }
+ if privs:
+ body["Oem"][self.rdmc.app.typepath.defs.oemhp].update({"Privileges": privs})
+ self.credentialsvalidation(options.identifier, options.loginname, options.acct_password, acct, True)
+ if options.serviceacc:
+ body["Oem"][self.rdmc.app.typepath.defs.oemhp].update({"ServiceAccount": True})
+ if options.role:
+ if self.rdmc.app.getiloversion() >= 5.140:
+ body["RoleId"] = options.role
+ else:
+ raise IncompatibleiLOVersionError("Roles can only be set in iLO 5" " 1.40 or greater.")
+ if path and body:
+ self.rdmc.app.post_handler(path, body)
+ self.rdmc.ui.printer("New iLO account %s is added successfully\n" % options.identifier)
+ elif options.command.lower() == "modify":
+ if not mod_acct:
+ raise InvalidCommandLineError("Unable to find the specified account.")
+ body = {}
+
+ if options.optprivs:
+ body.update({"Oem": {self.rdmc.app.typepath.defs.oemhp: {"Privileges": {}}}})
+ if any(
+ priv for priv in options.optprivs if "SystemRecoveryConfigPriv" in priv
+ ) and "SystemRecoveryConfigPriv" not in list(self.getsesprivs().keys()):
+ excp = IdTokenError()
+ note_msg = ""
+ if self.rdmc.app.getiloversion(skipschemas=True) >= 7:
+ note_msg = (
+ "Note: If you have logged into iLO using appaccount, "
+ "this is an expected behaviour. Please log in through "
+ "--no_app_account with a privileged user credentials to proceed.\n"
+ )
+ error_msg = (
+ "The currently logged in account must have The System "
+ "Recovery Config privilege to add the System Recovery "
+ "Config privilege.\n" + note_msg
+ )
+ excp.message = error_msg
+ raise excp
+ privs = self.getprivs(options)
+ body["Oem"][self.rdmc.app.typepath.defs.oemhp]["Privileges"] = privs
+
+ if options.role and self.rdmc.app.getiloversion() >= 5.140:
+ body["RoleId"] = options.role
+
+ if not body:
+ raise InvalidCommandLineError(
+ "Valid Privileges/RoleID have not been provided; "
+ " no changes have been made to this account: %s\n" % options.identifier
+ )
+ self.rdmc.app.patch_handler(path, body)
+
+ elif options.command.lower() == "delete":
+ if not acct:
+ raise InvalidCommandLineError("Unable to find the specified account.")
+ self.rdmc.app.delete_handler(path)
+
+ elif "cert" in options.command.lower():
+ certpath = "/redfish/v1/AccountService/UserCertificateMapping/"
+ privs = self.getsesprivs()
+ if self.rdmc.app.typepath.defs.isgen9:
+ IncompatibleiLOVersionError("This operation is only available on gen 10 " "and newer machines.")
+ elif not privs["UserConfigPriv"]:
+ excp = IdTokenError()
+ note_msg = ""
+ if self.rdmc.app.getiloversion(skipschemas=True) >= 7:
+ note_msg = (
+ "Note: If you have logged into iLO using appaccount, "
+ "this is an expected behaviour. Please log in through "
+ "--no_app_account with a privileged user credentials to proceed.\n"
+ )
+ error_msg = (
+ "The currently logged in account must have The User "
+ "Config privilege to manage certificates for users.\n" + note_msg
+ )
+ excp.message = error_msg
+ raise excp
+ else:
+ if options.command.lower() == "addcert":
+ if not acct:
+ raise InvalidCommandLineError("Unable to find the specified account.")
+ body = {}
+ username = acct["UserName"]
+
+ fingerprintfile = options.certificate
+ if os.path.exists(fingerprintfile):
+ with open(fingerprintfile, "r") as fingerfile:
+ fingerprint = fingerfile.read()
+ else:
+ raise InvalidFileInputError("%s cannot be read." % fingerprintfile)
+ body = {"Fingerprint": fingerprint, "UserName": username}
+ self.rdmc.app.post_handler(certpath, body)
+
+ elif options.command.lower() == "deletecert":
+ if not acct:
+ raise InvalidCommandLineError("Unable to find the specified account.")
+ certpath += acct["Id"]
+ self.rdmc.app.delete_handler(certpath)
+
+ else:
+ raise InvalidCommandLineError("Invalid command.")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def getprivs(self, options):
+ """find and return the privileges to set
+
+ :param options: command line options
+ :type options: list.
+ """
+ sesprivs = self.getsesprivs()
+ setprivs = {}
+ availableprivs = self.getsesprivs(availableprivsopts=True)
+
+ if "UserConfigPriv" not in list(sesprivs.keys()):
+ excp = IdTokenError()
+ note_msg = ""
+ if self.rdmc.app.getiloversion(skipschemas=True) >= 7:
+ note_msg = (
+ "Note: If you have logged into iLO using appaccount, "
+ "this is an expected behaviour. Please log in through "
+ "--no_app_account with a privileged user credentials to proceed.\n"
+ )
+ error_msg = (
+ "The currently logged in account does not have the User Config "
+ "privilege and cannot add or modify user accounts.\n" + note_msg
+ )
+ excp.message = error_msg
+ raise excp
+
+ if options.optprivs:
+ for priv in options.optprivs:
+ priv = next(iter(list(priv.keys())))
+ if priv not in availableprivs:
+ raise IncompatibleiLOVersionError("Privilege %s is not available on this " "iLO version." % priv)
+
+ if all(priv.values() for priv in options.optprivs):
+ if (
+ any(priv for priv in options.optprivs if "SystemRecoveryConfigPriv" in priv)
+ and "SystemRecoveryConfigPriv" not in sesprivs.keys()
+ ):
+ excp = IdTokenError()
+ note_msg = ""
+ if self.rdmc.app.getiloversion(skipschemas=True) >= 7:
+ note_msg = (
+ "Note: If you have logged into iLO using appaccount, "
+ "this is an expected behaviour. Please log in through "
+ "--no_app_account with a privileged user credentials to proceed.\n"
+ )
+ error_msg = (
+ "The currently logged in account must have The System "
+ "Recovery Config privilege to add the System Recovery "
+ "Config privilege.\n" + note_msg
+ )
+ excp.message = error_msg
+ raise excp
+ else:
+ setprivs = {}
+ for priv in options.optprivs:
+ setprivs.update(priv)
+
+ return setprivs
+
+ def getsesprivs(self, availableprivsopts=False):
+ """Finds and returns the curent session's privileges
+
+ :param availableprivsopts: return available privileges
+ :type availableprivsopts: boolean.
+ """
+ if self.rdmc.app.current_client:
+ sespath = self.rdmc.app.current_client.session_location
+ sespath = (
+ self.rdmc.app.current_client.default_prefix
+ + sespath.split(self.rdmc.app.current_client.default_prefix)[-1]
+ )
+
+ ses = self.rdmc.app.get_handler(sespath, service=False, silent=True)
+
+ if not ses:
+ raise SessionExpired("Invalid session. Please logout and " "log back in or include credentials.")
+
+ sesprivs = {
+ "HostBIOSConfigPriv": True,
+ "HostNICConfigPriv": True,
+ "HostStorageConfigPriv": True,
+ "LoginPriv": True,
+ "RemoteConsolePriv": True,
+ "SystemRecoveryConfigPriv": True,
+ "UserConfigPriv": True,
+ "VirtualMediaPriv": True,
+ "VirtualPowerAndResetPriv": True,
+ "iLOConfigPriv": True,
+ }
+ if "Oem" in ses.dict:
+ sesoemhp = ses.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ if "Privileges" in list(sesoemhp.keys()):
+ sesprivs = sesoemhp["Privileges"]
+ availableprivs = list(sesprivs.keys())
+ updated_privs = dict()
+ for priv, val in sesprivs.items():
+ if val:
+ updated_privs[priv] = sesprivs[priv]
+ sesprivs = updated_privs
+ del updated_privs
+ else:
+ sesprivs = None
+ if availableprivsopts:
+ return availableprivs
+ else:
+ return sesprivs
+
+ def credentialsvalidation(
+ self,
+ username="",
+ loginname="",
+ password="",
+ acct=None,
+ check_password=False,
+ options=None,
+ ):
+ """sanity validation of credentials
+ :param username: username to be added
+ :type username: str.
+ :param loginname: loginname to be added
+ :type loginname: str.
+ :param password: password to be added
+ :type password: str.
+ :param accounts: target federation account
+ :type accounts: dict.
+ :param check_password: flag to check password
+ :type check_password: bool.
+ """
+ username_max_chars = 39 # 60
+ loginname_max_chars = 39 # 60
+ password_max_chars = 39 # PASSWORD MAX CHARS
+ password_min_chars = 8 # PASSWORD MIN CHARS
+
+ password_min_chars = next(iter(self.rdmc.app.select("AccountService."))).dict["Oem"][
+ self.rdmc.app.typepath.defs.oemhp
+ ]["MinPasswordLength"]
+
+ if username != "" and loginname != "":
+ if len(username) > username_max_chars:
+ raise InvalidCommandLineError(
+ "Username exceeds maximum length" ". Use at most %s characters." % username_max_chars
+ )
+
+ if len(loginname) > loginname_max_chars:
+ raise InvalidCommandLineError(
+ "Login name exceeds maximum " "length. Use at most %s characters." % loginname_max_chars
+ )
+
+ try:
+ if acct:
+ if (
+ acct["UserName"] == username
+ or acct["Oem"][self.rdmc.app.typepath.defs.oemhp]["LoginName"] == loginname
+ ):
+ raise ResourceExists("Username or login name is already in use.")
+ except KeyError:
+ pass
+
+ if check_password:
+ if password == "" or password == "/r":
+ raise InvalidCommandLineError("An invalid password was entered.")
+ else:
+ if len(password) > password_max_chars:
+ raise InvalidCommandLineError(
+ "Password length is invalid." " Use at most %s characters." % password_max_chars
+ )
+ if len(password) < password_min_chars:
+ raise InvalidCommandLineError(
+ "Password length is invalid." " Use at least %s characters." % password_min_chars
+ )
+
+ def iloaccountsvalidation(self, options):
+ """add account validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ @staticmethod
+ def options_argument_group(parser):
+ """Define optional arguments group
+
+ :param parser: The parser to add the --addprivs option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+ group = parser.add_argument_group(
+ "GLOBAL OPTIONS",
+ "Options are available for all " "arguments within the scope of this command.",
+ )
+
+ group.add_argument(
+ "--addprivs",
+ dest="optprivs",
+ nargs="*",
+ action=_AccountParse,
+ type=str,
+ help="Optionally include this flag if you wish to specify "
+ "which privileges you want added to the iLO account. Pick "
+ "privileges from the privilege list in above help text. EX: --addprivs=1,2,4",
+ default=None,
+ metavar="Priv,",
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ privilege_help = (
+ "\n\n\tPRIVILEGES:\n\t1: Login\n\t2: Remote Console\n\t3: User Config\n\t4:"
+ " iLO Config\n\t5: Virtual Media\n\t6: Virtual Power and Reset\n\n\tiLO 5 added "
+ "privileges:\n\t7: Host NIC Config\n\t8: Host Bios Config\n\t9: Host Storage Config"
+ "\n\t10: System Recovery Config"
+ )
+ # default sub-parser
+ default_parser = subcommand_parser.add_parser(
+ "default",
+ help="Running without any sub-command will return all account information on the\n"
+ "currently logged in server.",
+ )
+ default_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(default_parser)
+ # add sub-parser
+ add_help = (
+ "Adds an iLO user account to the currently logged in server with privileges\n" "specified in --addprivs."
+ )
+ add_parser = subcommand_parser.add_parser(
+ __subparsers__[0],
+ help=add_help,
+ description=add_help + "\n\t*Note*:By default only the login privilege is added to the"
+ ' newly created account\n\twith role "ReadOnly"in iLO 5 and no privileges in iLO 4.'
+ + privilege_help
+ + "\n\n\tExamples:\n\n\tAdd an account with specific privileges:\n\t\tiloaccounts add "
+ "username accountname password --addprivs 1,2,4\n\n\tAdd an account and specify "
+ "privileges by role:\n\t\tiloaccounts add username accountname password --role "
+ "ReadOnly",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ # addprivs
+ add_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to modify.",
+ metavar="USERNAMEorID#",
+ )
+ add_parser.add_argument(
+ "loginname",
+ help="The loginname of the iLO account to add. This is NOT used to login to the newly " "created account.",
+ metavar="LOGINNAME",
+ )
+ add_parser.add_argument(
+ "acct_password",
+ help="The password of the iLO account to add. If you do not include a password, you "
+ "will be prompted to enter one before an account is created. This is used to login to "
+ "the newly created account.",
+ metavar="PASSWORD",
+ nargs="?",
+ default="",
+ )
+ add_parser.add_argument(
+ "--role",
+ dest="role",
+ choices=["Administrator", "ReadOnly", "Operator"],
+ help="Optionally include this option if you would like to specify Privileges by role."
+ " Roles are a set of privileges created based on the role of the account.",
+ default=None,
+ )
+ add_parser.add_argument(
+ "--serviceaccount",
+ dest="serviceacc",
+ action="store_true",
+ help="Optionally include this flag if you wish to created account " "to be a service account.",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(add_parser)
+
+ self.options_argument_group(add_parser)
+ # modify sub-parser
+ modify_help = (
+ "Modifies the provided iLO user account on the currently logged in server"
+ '\nadding privileges using "--addprivs" to include privileges and using\n'
+ '"--removeprivs" for removing privileges.'
+ )
+ modify_parser = subcommand_parser.add_parser(
+ __subparsers__[1],
+ help=modify_help,
+ description=modify_help + privilege_help + "\n\n\tExamples:\n\n\tModify an iLO account's "
+ "privileges by adding:\n\tiloaccounts modify username --addprivs 3,5\n\n\t"
+ "Modify an iLO account's privileges by removal:\n\tiloaccounts modify username "
+ "--removeprivs 10\n\n\tOr modify an iLO account's privileges by both simultaneously "
+ "adding and removing privleges:\n\n\tiloaccounts modify username --addprivs 3,7 "
+ "--removeprivs 9,10",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ modify_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to modify.",
+ metavar="USERNAMEorID#",
+ )
+ self.cmdbase.add_login_arguments_group(modify_parser)
+
+ self.options_argument_group(modify_parser) # addprivs
+ modify_parser.add_argument(
+ "--role",
+ dest="role",
+ choices=["Administrator", "ReadOnly", "Operator"],
+ help="Optionally include this option if you would like to specify Privileges by role."
+ " Roles are a set of privileges created based on the role of the account.",
+ default=None,
+ )
+ modify_parser.add_argument(
+ "--removeprivs",
+ dest="optprivs",
+ nargs="*",
+ action=_AccountParse,
+ type=str,
+ help="Include this flag if you wish to specify "
+ "which privileges you want removed from the iLO account. Pick "
+ "privileges from the privilege list in the above help text. EX: --removeprivs=1,2,4",
+ default=None,
+ metavar="PRIV,",
+ )
+ # changepass sub-parser
+ changepass_help = "Changes the password of the provided iLO user account on the currently " "logged in server."
+ changepass_parser = subcommand_parser.add_parser(
+ __subparsers__[2],
+ help=changepass_help,
+ description=changepass_help + "\n\nExamples:\n\nChange the password of an account:\n\t"
+ "iloaccounts changepass 2 newpassword",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ changepass_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to change the password for.",
+ metavar="USERNAMEorID#",
+ )
+ changepass_parser.add_argument(
+ "acct_password",
+ help="The password to change the selected iLO account to. If you do not include a "
+ "password, you will be prompted to enter one before an account is created. This is "
+ "used to login to the newly created account.",
+ metavar="PASSWORD",
+ nargs="?",
+ default="",
+ )
+ self.cmdbase.add_login_arguments_group(changepass_parser)
+
+ # delete sub-parser
+ delete_help = "Deletes the provided iLO user account on the currently logged in server."
+ delete_parser = subcommand_parser.add_parser(
+ __subparsers__[3],
+ help=delete_help,
+ description=delete_help + "\n\nExamples:\n\nDelete an iLO account:\n\t" "iloaccounts delete username",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ delete_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to delete.",
+ metavar="USERNAMEorID#",
+ )
+ self.cmdbase.add_login_arguments_group(delete_parser)
+
+ # addcert sub-parser
+ addcert_help = "Adds a certificate to the provided iLO user account on the currently logged" " in server."
+ addcert_parser = subcommand_parser.add_parser(
+ __subparsers__[4],
+ help=addcert_help,
+ description=addcert_help + r"\n\nExamples:\n\nAdd a user certificate to the provided "
+ r"iLO account.\n\tiloaccounts addcert accountUserName C:\Users\user\cert.txt",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ addcert_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to add a certificate to.",
+ metavar="USERNAMEorID#",
+ )
+ addcert_parser.add_argument(
+ "certificate",
+ help="The certificate to add to the provided iLO account.",
+ metavar="X.509CERTIFICATE",
+ )
+ self.cmdbase.add_login_arguments_group(addcert_parser)
+
+ # deletecert sub-parser
+ deletecert_help = "Deletes a certificate to the provided iLO user account on the currently " "logged in server."
+ deletecert_parser = subcommand_parser.add_parser(
+ __subparsers__[5],
+ help=deletecert_help,
+ description=deletecert_help + "\n\nExamples:\n\nDelete a user certificate from the "
+ "provided iLO account.\n\tiloaccounts deletecert username",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ deletecert_parser.add_argument(
+ "identifier",
+ help="The username or ID of the iLO account to delete the certificate from.",
+ metavar="USERNAMEorID#",
+ )
+ self.cmdbase.add_login_arguments_group(deletecert_parser)
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloBackupRestoreCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloBackupRestoreCommand.py
new file mode 100644
index 0000000..badf63c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloBackupRestoreCommand.py
@@ -0,0 +1,310 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Factory Defaults Command for rdmc"""
+import os
+from argparse import RawDescriptionHelpFormatter
+
+try:
+ from rdmc_helper import (
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ InvalidFileInputError,
+ InvalidPasswordLengthError,
+ NoContentsFoundForOperationError,
+ ReturnCodes,
+ UploadError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ ReturnCodes,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ InvalidFileInputError,
+ InvalidPasswordLengthError,
+ UploadError,
+ )
+
+from redfish.ris.ris import SessionExpired
+
+
+class IloBackupRestoreCommand:
+ """Backup and restore server using iLO's .bak file"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "backuprestore",
+ "usage": None,
+ "description": "Create a .bak file. \n\tExample: backuprestore backup\n\n\t"
+ "Restore a server using a .bak file. \n\texample: backuprestore "
+ "restore\n\n\tNOTE: This command is designed to only restore\n\tthe "
+ "machine from which the backup file was created against.\n\tIf you would like to "
+ "take one configuration and apply it\n\tto multiple systems see the "
+ "serverclone command.\n\tThis command is only available in remote mode.",
+ "summary": "Backup and restore iLO to a server using a .bak file.",
+ "aliases": ["br"],
+ "auxcommands": ["LogoutCommand"],
+ }
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main factorydefaults function
+
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ try:
+ (options, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ # if not len(args) == 1:
+ # raise InvalidCommandLineError("backuprestore command takes one argument.")
+
+ self.ilobackuprestorevalidation(options)
+
+ if "blobstore" in self.rdmc.app.current_client.base_url:
+ raise InvalidCommandLineError("This command is only available remotely.")
+
+ sessionkey = self.rdmc.app.current_client.session_key
+ # sessionkey = (sessionkey).encode('ascii', 'ignore')
+
+ if options.command == "backup":
+ self.backupserver(options, sessionkey)
+ elif options.command == "restore":
+ self.restoreserver(options, sessionkey)
+ else:
+ raise InvalidCommandLineError("Only options are backup or restore\n")
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def backupserver(self, options, skey):
+ """Create .bak file for a server
+
+ :param options: command options
+ :type options: list.
+ :param skey: sessionkey of the currently logged in server
+ :type skey: str.
+ """
+ select = "HpeiLOBackupRestoreService."
+ backupfile = None
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ service = results.resp.dict
+ else:
+ raise NoContentsFoundForOperationError("%s not found.It may not " "be available on this system.\n" % select)
+
+ backuplocation = service["BackupFileLocation"]
+ backupname = backuplocation.split("/")[-1]
+
+ postdata = []
+ postdata.append(("sessionKey", skey))
+
+ if options.fpass:
+ if len(options.fpass) > 32:
+ raise InvalidPasswordLengthError("Length of password cannot be greater than 32 characters.")
+ postdata.append(("password", options.fpass))
+ self.rdmc.ui.printer("Downloading backup file %s...\n" % backupname)
+ backupfile = self.rdmc.app.post_handler(backuplocation, postdata, service=True, silent=True)
+
+ if backupfile:
+ if (backupfile.status != 200) and backupfile.read != "":
+ if "Invalid Session" in backupfile.read:
+ raise SessionExpired("Invalid session. Please logout and log back in or include credentials.")
+ self.rdmc.ui.printer("Download complete.\n")
+ outfile = open(backupname, "wb")
+ outfile.write(backupfile.ori)
+ outfile.close()
+ else:
+ raise NoContentsFoundForOperationError("Unable to download file.\n")
+
+ def restoreserver(self, options, skey):
+ """Use a .bak file to restore a server
+
+ :param options: command options
+ :type options: list.
+ :param skey: sessionkey of the currently logged in server
+ :type skey: str.
+ """
+
+ select = "HpeiLOBackupRestoreService."
+
+ if options.filename:
+ filename = options.filename[0]
+ else:
+ files = []
+ files = [f for f in os.listdir(".") if os.path.isfile(f) and f.endswith(".bak")]
+ if files and len(files) > 1:
+ raise InvalidFileInputError(
+ "More than one .bak file found in "
+ "the current directory. Please specify "
+ "a file using the -f option."
+ )
+ elif not files:
+ raise InvalidFileInputError(
+ "No .bak file found in current " "directory. Please specify a file using the -f option."
+ )
+ else:
+ filename = files[0]
+
+ results = self.rdmc.app.select(selector=select)
+
+ try:
+ results = results[0]
+ except:
+ pass
+
+ if results:
+ service = results.resp.dict
+ else:
+ raise NoContentsFoundForOperationError("%s not found.It may not " "be available on this system." % select)
+ restorelocation = service["HttpPushUri"]
+ postdata = []
+
+ with open(filename, "rb") as fle:
+ bakfile = fle.read()
+ postdata.append(("sessionKey", skey))
+ if options.fpass:
+ postdata.append(("password", options.fpass))
+ postdata.append(("file", (filename, bakfile, "application/octet-stream")))
+ if isinstance(skey, bytes):
+ skey = skey.decode("utf-8")
+ resp = self.rdmc.app.post_handler(
+ restorelocation,
+ postdata,
+ service=False,
+ silent=True,
+ headers={"Cookie": "sessionKey=" + skey},
+ )
+
+ if not resp.status == 200:
+ resp_ori = resp.ori
+ if isinstance(resp_ori, bytes):
+ resp_ori = resp_ori.decode("utf-8")
+ if resp_ori == "invalid_restore_password":
+ raise UploadError(
+ "Invalid or no password supplied during restore. Please "
+ "supply the password used during creation of the backup file."
+ )
+ else:
+ raise UploadError("Error while uploading the backup file.")
+ else:
+ self.rdmc.ui.printer(
+ "Restore in progress. iLO will be unresponsive while the "
+ "restore completes.\nYour session will be terminated.\n"
+ )
+ self.auxcommands["logout"].run("")
+
+ def ilobackuprestorevalidation(self, options):
+ """factory defaults validation function
+
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ subcommand_parser.required = True
+ backup_help = "Create a backup of a server. This option is iLO5 Onwards"
+ # backup sub-parser
+ backup_parser = subcommand_parser.add_parser(
+ "backup",
+ help=backup_help,
+ description=backup_help + "\n\texample: backuprestore backup " "--f --filepass ",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ self.cmdbase.add_login_arguments_group(backup_parser)
+
+ backup_parser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag to specify which backup file to restore. By "
+ "default the commmand will try to find a .bak file in the current "
+ "working directory.",
+ action="append",
+ default=None,
+ )
+ backup_parser.add_argument(
+ "--filepass",
+ dest="fpass",
+ help="Optionally use the provided password when creating the "
+ "backup file. The same password must be used for restoring.",
+ default=None,
+ )
+
+ restore_help = "Restore a server with backup file. This option is for iLO5 Onwards"
+ # backup sub-parser
+ restore_parser = subcommand_parser.add_parser(
+ "restore",
+ help=restore_help,
+ description=restore_help + "\n\texample: backuprestore restore " "--f --filepass ",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ self.cmdbase.add_login_arguments_group(restore_parser)
+
+ restore_parser.add_argument(
+ "-f",
+ "--filename",
+ dest="filename",
+ help="Use this flag to specify which backup file to restore. By "
+ "default the commmand will try to find a .bak file in the current "
+ "working directory.",
+ action="append",
+ default=None,
+ )
+ restore_parser.add_argument(
+ "--filepass",
+ dest="fpass",
+ help="Optionally use the provided password when creating the "
+ "backup file. The same password must be used for restoring.",
+ default=None,
+ )
diff --git a/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloFederationCommand.py b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloFederationCommand.py
new file mode 100644
index 0000000..b545c50
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/ilorest/extensions/iLO_COMMANDS/IloFederationCommand.py
@@ -0,0 +1,537 @@
+###
+# Copyright 2016-2021 Hewlett Packard Enterprise, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###
+
+# -*- coding: utf-8 -*-
+"""Add Federation Command for rdmc"""
+
+
+from argparse import Action, RawDescriptionHelpFormatter
+
+from redfish.ris.ris import SessionExpired
+from redfish.ris.rmc_helper import IdTokenError
+
+try:
+ from rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ UsernamePasswordRequiredError,
+ )
+except ImportError:
+ from ilorest.rdmc_helper import (
+ IncompatibleiLOVersionError,
+ InvalidCommandLineError,
+ InvalidCommandLineErrorOPTS,
+ NoContentsFoundForOperationError,
+ ResourceExists,
+ ReturnCodes,
+ UsernamePasswordRequiredError,
+ )
+
+__subparsers__ = ["add", "modify", "changekey", "delete"]
+
+
+class _FederationParse(Action):
+ def __init__(self, option_strings, dest, nargs, **kwargs):
+ super(_FederationParse, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_strings):
+ """Federation privileges option helper"""
+
+ privkey = {
+ 1: "LoginPriv",
+ 2: "RemoteConsolePriv",
+ 3: "UserConfigPriv",
+ 4: "iLOConfigPriv",
+ 5: "VirtualMediaPriv",
+ 6: "VirtualPowerAndResetPriv",
+ 7: "HostNICConfigPriv",
+ 8: "HostBIOSConfigPriv",
+ 9: "HostStorageConfigPriv",
+ 10: "SystemRecoveryConfigPriv",
+ }
+
+ for priv in next(iter(values)).split(","):
+ try:
+ priv = int(priv)
+ except ValueError:
+ try:
+ parser.error("Invalid privilege entered: %s. Privileges must " "be numbers." % priv)
+ except:
+ raise InvalidCommandLineErrorOPTS("")
+
+ try:
+ if not isinstance(namespace.optprivs, list):
+ namespace.optprivs = list()
+ if option_strings.startswith("--add"):
+ namespace.optprivs.append({privkey[priv]: True})
+ elif option_strings.startswith("--remove"):
+ namespace.optprivs.append({privkey[priv]: False})
+ except KeyError:
+ try:
+ parser.error(
+ "Invalid privilege entered: %s. Number does not " "match an available privlege." % priv
+ )
+ except:
+ raise InvalidCommandLineErrorOPTS("")
+
+
+class IloFederationCommand:
+ """Add a new ilo federation to the server"""
+
+ def __init__(self):
+ self.ident = {
+ "name": "ilofederation",
+ "usage": None,
+ "description": "View, Add, Remove and Modify iLO Federation accoutns based on the "
+ "sub-command used.\nTo view help on specific sub-commands run: ilofederation "
+ " -h\n\nExample: ilofederation add -h\n"
+ "NOTE 1: By default only the login privilege is added to the newly created\n\t\t"
+ 'federation group with role "ReadOnly" in iLO 5 and no privileges in iLO 4.\n\t'
+ "NOTE 2: Federation credentials are case-sensitive.",
+ "summary": "Adds / deletes an iLO federation group on the currently logged in server.",
+ "aliases": [],
+ "auxcommands": [],
+ }
+
+ self.cmdbase = None
+ self.rdmc = None
+ self.auxcommands = dict()
+
+ def run(self, line, help_disp=False):
+ """Main addfederation function
+ :param line: string of arguments passed in
+ :type line: str.
+ :param help_disp: display help flag
+ :type line: bool.
+ """
+ if help_disp:
+ line.append("-h")
+ try:
+ (_, _) = self.rdmc.rdmc_parse_arglist(self, line)
+ except:
+ return ReturnCodes.SUCCESS
+ return ReturnCodes.SUCCESS
+ body = dict()
+ try:
+ ident_subparser = False
+ for cmnd in __subparsers__:
+ if cmnd in line:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line)
+ ident_subparser = True
+ break
+ if not ident_subparser:
+ (options, args) = self.rdmc.rdmc_parse_arglist(self, line, default=True)
+ except (InvalidCommandLineErrorOPTS, SystemExit):
+ if ("-h" in line) or ("--help" in line):
+ return ReturnCodes.SUCCESS
+ else:
+ raise InvalidCommandLineErrorOPTS("")
+
+ self.ilofederationvalidation(options)
+
+ redfish = self.rdmc.app.monolith.is_redfish
+ path = self.rdmc.app.typepath.defs.federationpath
+ results = self.rdmc.app.get_handler(path, service=True, silent=True).dict
+
+ if redfish:
+ if "Members" in results:
+ results = results["Members"]
+ else:
+ results = list()
+ else:
+ if "Member" in results["links"]:
+ results = results["links"]["Member"]
+ else:
+ results = list()
+
+ mod_fed = None
+ path = None
+ for indx, acct in enumerate(results):
+ fed = self.rdmc.app.get_handler(
+ acct[self.rdmc.app.typepath.defs.hrefstring], service=True, silent=True
+ ).dict
+ try:
+ if hasattr(options, "fedname"):
+ if fed["Name"].lower() == options.fedname.lower():
+ if redfish:
+ path = acct["@odata.id"]
+ else:
+ path = acct["href"]
+ mod_fed = fed
+ elif options.command == "default":
+ results[indx] = fed
+ else:
+ raise KeyError
+ except KeyError:
+ continue
+ else:
+ if mod_fed:
+ break
+
+ if mod_fed:
+ if options.command.lower() == "add":
+ raise ResourceExists("Federation name %s is already in use." % options.fedname)
+ else:
+ if options.command.lower() != "add" and options.command.lower() != "default":
+ raise InvalidCommandLineError("Unable to find the specified federation %s." % options.fedname)
+
+ if options.command.lower() == "add":
+ privs = self.getprivs(options)
+ path = self.rdmc.app.typepath.defs.federationpath
+
+ body = {"Name": options.fedname, "Key": options.fedkey}
+ if privs:
+ body.update({"Privileges": privs})
+ self.addvalidation(options.fedname, options.fedkey, results)
+
+ if path and body:
+ resp = self.rdmc.app.post_handler(path, body, silent=False, service=False)
+
+ if resp and resp.dict:
+ if "resourcealreadyexist" in str(resp.dict).lower():
+ raise ResourceExists("")
+
+ elif options.command.lower() == "changekey":
+ try:
+ newkey = options.fedkey
+ except:
+ raise InvalidCommandLineError("Invalid number of parameters.")
+
+ body = {"Key": newkey}
+
+ if path and body:
+ self.rdmc.app.patch_handler(path, body, service=True)
+ else:
+ raise NoContentsFoundForOperationError("Unable to find " "the specified federation.")
+ elif options.command.lower() == "modify":
+ if options.optprivs:
+ body.update({"Privileges": {}})
+ if any(
+ priv for priv in options.optprivs if "SystemRecoveryConfigPriv" in priv
+ ) and "SystemRecoveryConfigPriv" not in list(self.getsesprivs().keys()):
+ raise IdTokenError(
+ "The currently logged in federation must have The System "
+ "Recovery Config privilege to add the System Recovery "
+ "Config privilege."
+ )
+ privs = self.getprivs(options)
+ body["Privileges"] = privs
+
+ self.rdmc.app.patch_handler(path, body)
+
+ elif options.command.lower() == "delete":
+ if path:
+ self.rdmc.app.delete_handler(path)
+ else:
+ if len(results) == 0:
+ self.rdmc.ui.printer("No iLO Federation accounts found.\n")
+ else:
+ self.rdmc.ui.printer("iLO Federation Id list with Privileges:\n")
+ if options.json:
+ outdict = dict()
+ for fed in sorted(results, key=lambda k: k["Name"]):
+ outdict[fed["Name"]] = fed["Privileges"]
+ self.rdmc.ui.print_out_json_ordered(outdict)
+ else:
+ for fed in sorted(results, key=lambda k: k["Name"]):
+ privstr = ""
+ privs = fed["Privileges"]
+ for priv in privs:
+ privstr += priv + "=" + str(privs[priv]) + "\n"
+ self.rdmc.ui.printer("\nName=%s:\n%s" % (fed["Name"], privstr))
+
+ self.cmdbase.logout_routine(self, options)
+ # Return code
+ return ReturnCodes.SUCCESS
+
+ def getprivs(self, options):
+ """find and return the privileges to set
+ :param options: command line options
+ :type options: list.
+ """
+ sesprivs = self.getsesprivs()
+ setprivs = {}
+ availableprivs = self.getsesprivs(availableprivsopts=True)
+
+ if "UserConfigPriv" not in list(sesprivs.keys()):
+ raise IdTokenError(
+ "The currently logged in federation does not have the Config"
+ "Privilege and cannot add or modify federations."
+ )
+
+ if options.optprivs:
+ for priv in options.optprivs:
+ priv = next(iter(list(priv.keys())))
+ if not options.user or not options.password:
+ if priv == "SystemRecoveryConfigPriv" and self.rdmc.app.current_client.base_url == "blobstore://.":
+ raise UsernamePasswordRequiredError(
+ "Privilege %s need username and password to be specified." % priv
+ )
+ if priv not in availableprivs:
+ raise IncompatibleiLOVersionError("Privilege %s is not available on this " "iLO version." % priv)
+
+ if all(priv.values() for priv in options.optprivs):
+ if (
+ any(priv for priv in options.optprivs if "SystemRecoveryConfigPriv" in priv)
+ and "SystemRecoveryConfigPriv" not in sesprivs.keys()
+ ):
+ raise IdTokenError(
+ "The currently logged in account must have The System "
+ "Recovery Config privilege to add the System Recovery "
+ "Config privilege."
+ )
+ else:
+ setprivs = {}
+ for priv in options.optprivs:
+ setprivs.update(priv)
+
+ return setprivs
+
+ def getsesprivs(self, availableprivsopts=False):
+ """Finds and returns the current session's privileges
+ :param availableprivsopts: return available privileges
+ :type availableprivsopts: boolean.
+ """
+ if self.rdmc.app.current_client:
+ sespath = self.rdmc.app.current_client.session_location
+ sespath = (
+ self.rdmc.app.current_client.default_prefix
+ + sespath.split(self.rdmc.app.current_client.default_prefix)[-1]
+ )
+
+ ses = self.rdmc.app.get_handler(sespath, service=False, silent=True)
+
+ if ses.status != 200:
+ raise SessionExpired("Invalid session. Please logout and " "log back in or include credentials.")
+
+ sesprivs = {
+ "HostBIOSConfigPriv": True,
+ "HostNICConfigPriv": True,
+ "HostStorageConfigPriv": True,
+ "LoginPriv": True,
+ "RemoteConsolePriv": True,
+ "SystemRecoveryConfigPriv": True,
+ "UserConfigPriv": True,
+ "VirtualMediaPriv": True,
+ "VirtualPowerAndResetPriv": True,
+ "iLOConfigPriv": True,
+ }
+ if "Oem" in ses.dict:
+ sesoemhp = ses.dict["Oem"][self.rdmc.app.typepath.defs.oemhp]
+ if "Privileges" in list(sesoemhp.keys()):
+ sesprivs = sesoemhp["Privileges"]
+ availableprivs = list(sesprivs.keys())
+ keepprivs = dict()
+ for priv, val in sesprivs.items():
+ if val:
+ keepprivs[priv] = sesprivs[priv]
+ sesprivs = keepprivs
+ else:
+ sesprivs = None
+
+ if availableprivsopts:
+ return availableprivs
+ else:
+ return sesprivs
+
+ def addvalidation(self, username, key, feds):
+ """add validation function
+ :param username: username to be added
+ :type username: str.
+ :param key: key to be added
+ :type key: str.
+ :param feds: list of federation accounts
+ :type feds: list.
+ """
+
+ if len(username) >= 32:
+ raise InvalidCommandLineError("User name exceeds maximum length.")
+ elif len(key) >= 32 or len(key) <= 7:
+ raise InvalidCommandLineError("Password is invalid length.")
+
+ def ilofederationvalidation(self, options):
+ """addfederation validation function
+ :param options: command line options
+ :type options: list.
+ """
+ self.cmdbase.login_select_validation(self, options)
+
+ @staticmethod
+ def options_addprivs_argument_group(parser):
+ """Define optional arguments group
+ :param parser: The parser to add the addprivs option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+ parser.add_argument(
+ "--addprivs",
+ dest="optprivs",
+ nargs="*",
+ action=_FederationParse,
+ type=str,
+ help="Optionally include this flag if you wish to specify "
+ "which privileges you want added to the iLO federation. This overrides the default of "
+ "duplicating privileges of the currently logged in federation on the new federation. "
+ "Pick privileges from the privilege list in the above help text. EX: --addprivs=1,2,4",
+ default=None,
+ )
+
+ @staticmethod
+ def options_removeprivs_argument_group(parser):
+ """Additional argument
+ :param parser: The parser to add the removeprivs option group to
+ :type parser: ArgumentParser/OptionParser
+ """
+
+ parser.add_argument(
+ "--removeprivs",
+ dest="optprivs",
+ nargs="*",
+ action=_FederationParse,
+ type=str,
+ help="Optionally include this flag if you wish to specify "
+ "which privileges you want removed from the iLO federation. This overrides the default"
+ " of duplicating privileges of the currently logged in federation on the new "
+ "federation. Pick privileges from the privilege list in the above help text. "
+ "EX: --removeprivs=1,2,4",
+ default=None,
+ )
+
+ def definearguments(self, customparser):
+ """Wrapper function for new command main function
+ :param customparser: command line input
+ :type customparser: parser.
+ """
+ if not customparser:
+ return
+
+ self.cmdbase.add_login_arguments_group(customparser)
+ subcommand_parser = customparser.add_subparsers(dest="command")
+ privilege_help = (
+ "\n\nPRIVILEGES:\n\t1: Login\n\t2: Remote Console\n\t3: User Config\n\t4:"
+ " iLO Config\n\t5: Virtual Media\n\t6: Virtual Power and Reset\n\n\tiLO 5 added "
+ "privileges:\n\t7: Host NIC Config\n\t8: Host Bios Config\n\t9: Host Storage Config"
+ "\n\t10: System Recovery Config"
+ )
+ # default sub-parser
+ default_parser = subcommand_parser.add_parser(
+ "default",
+ help="Running without any sub-command will return all federation group information "
+ " on the currently logged in server.",
+ )
+ default_parser.add_argument(
+ "-j",
+ "--json",
+ dest="json",
+ action="store_true",
+ help="Optionally include this flag if you wish to change the"
+ " displayed output to JSON format. Preserving the JSON data"
+ " structure makes the information easier to parse.",
+ default=False,
+ )
+ self.cmdbase.add_login_arguments_group(default_parser) # add sub-parser
+ add_help = (
+ "Adds an iLO federation group to the currently logged in server. Federation "
+ 'group privileges may be specified with\n"--addprivs". If a federation key '
+ "is not "
+ "provided, the user will be prompted to provide one prior to account creation."
+ )
+ add_parser = subcommand_parser.add_parser(
+ __subparsers__[0],
+ help=add_help,
+ description=add_help
+ + "\n\tilofederation add [FEDERATIONNAME] [FEDERATIONKEY] "
+ + privilege_help
+ + "\n\tilofederation add newilofedname thisfedkey --addprivs 1,3,4",
+ formatter_class=RawDescriptionHelpFormatter,
+ )
+ add_parser.add_argument(
+ "fedname",
+ help="Federation name of the federation group to add.",
+ type=str,
+ metavar="FEDERATION NAME",
+ )
+ add_parser.add_argument(
+ "fedkey",
+ help="Federation key of the federation group to add.",
+ type=str,
+ metavar="FEDERATION KEY",
+ )
+ self.options_addprivs_argument_group(add_parser)
+ self.cmdbase.add_login_arguments_group(add_parser)
+
+ modify_help = "Modify the privileges on an existing federation group."
+ modify_parser = subcommand_parser.add_parser(
+ __subparsers__[1],
+ help=modify_help,
+ description=modify_help + "\n\nTo add privileges:\n\tilofederation modify "
+ "[FEDNAME] --addprivs