Esempio n. 1
0
 def __init__(self, server="127.0.0.1"):
     self.datamodel = DataModel()
     self.rest = StcRest(server, self.datamodel.session())
     self.xpath = Linker(self.datamodel, self.rest)
     self.templater = Templater(self.datamodel)
     self.tagMgr = TagManager(self.rest)
Esempio n. 2
0
class MetaModel:
    def __init__(self, server="127.0.0.1"):
        self.datamodel = DataModel()
        self.rest = StcRest(server, self.datamodel.session())
        self.xpath = Linker(self.datamodel, self.rest)
        self.templater = Templater(self.datamodel)
        self.tagMgr = TagManager(self.rest)

    def action(self, params):

        action = params["action"]
        count = params["count"] if "count" in params else 1

        objects = params["objects"] if "objects" in params else None
        if objects == None and "object" in params:
            objects = params["object"]

        log.info("Action: %s" % json.dumps(params, indent=4))

        if (action == "session") or (action == "create_session"):

            chassis = params["chassis"] if "chassis" in params else None
            if chassis != None and chassis != "":
                chassis = chassis.split(" ")
            else:
                chassis = []

            ports = params["ports"] if "ports" in params else None
            if ports != None and ports != "":
                try:
                    ports = resolvePorts(ports)
                    log.info("Ports: %s" % str(ports))
                except Exception as err:
                    return Result.error("Ports handling Exception: %s" %
                                        str(err))

            else:
                ports = []

            portNames = params["names"] if "names" in params else None
            if portNames != None and portNames != "":
                portNames = resolveNames(portNames)
                log.info("PortNames: %s" % str(portNames))
            else:
                portNames = []

            if len(ports) != 0 and len(portNames) != 0 and len(ports) != len(
                    portNames):
                return Result.error(
                    "The number of ports and names does not match, please check"
                )

            # print(">>> new session <<< user:%s name:%s chassis:%s" %
            #       (Color.blue(params["user"]), Color.blue(params["name"]), Color.green(str(chassis))))
            propsDict = {"ports": ports, "names": portNames}

            reset_existing = (not ("reset_existing" in params)) or (
                params["reset_existing"] == True)
            kill_existing = "kill_existing" in params and params[
                "kill_existing"]
            result = self.new_session(params["user"], params["name"], chassis,
                                      propsDict, reset_existing, kill_existing)

        elif action == "attach_session":
            userName = params["user"] if "user" in params else None
            sessionName = params["name"]
            result = self.attach_session(sessionName, userName)

        elif action == "delete_session":
            user_name = params["user"] if "user" in params else None
            sessionName = params["name"]
            sessions = []
            existingSessions = self.rest._getSessions()

            if re.search(',', params["name"]) != None:
                sessionNames = params["name"].split(", ")
                for sessionName in sessionNames:
                    if user_name != None:
                        sessionName = sessionName + " - " + user_name
                    if sessionName in existingSessions:
                        sessions.append(sessionName)
                    else:
                        return Result.error("Session \"%s\" is not exist." %
                                            sessionName)
            else:
                if user_name != None:
                    sessionName = sessionName + " - " + user_name
                if sessionName in existingSessions:
                    sessions.append(sessionName)
                else:
                    return Result.error("Session \"%s\" is not exist." %
                                        sessionName)
            result = self.delete_session(sessions)

        elif action == "delete_all_sessions":
            result = self.delete_all_sessions()

        elif action == "create":

            under = params["under"] if "under" in params else None
            result = self.create(objects, under, count=count)

        elif action == "config":

            if type(objects) is list:
                if len(objects) != 1:
                    return Result.error(
                        "There should be only one object to configure, but there are %d: %s"
                        % (len(objects), objects))
                objects = objects[0]
            result = self.config(params["properties"], objects, count=count)

        elif action == "perform":

            properties = params[
                "properties"] if "properties" in params and params[
                    "properties"] != None else {}
            result = self.perform(params["command"], properties, count=count)

        elif action == "wait":

            if objects == None:
                log.error("No object specified tor get actions: %s" % params)
                return Result.error(
                    "No object specified for the wait actions: %s" % params)

            timeout = params["timeout"] if "timeout" in params and params[
                "timeout"] != None else 60
            result = self.wait(objects,
                               params["until"],
                               timeout=int(timeout),
                               count=count)

        elif action == "get":

            if objects == None:
                log.error("No object specified tor get actions: %s" % params)
                return Result.error(
                    "No object specified for the get actions: %s" % params)

            result = self.get(objects, count=count)

        elif action == "delete":

            if objects == None:
                log.error("No object specified tor get actions: %s" % params)
                return Result.error(
                    "No object specified for the get actions: %s" % params)

            result = self.delete(objects, count=count)

        elif action == "load":

            result = self.load_datamodel(params["datamodel"])

        elif action == "files":

            files = self.rest.files()
            if files == None:
                return Result.error(self.rest.errorInfo)
            return Result.value(files)

        elif action == "download":

            dest = params["dest"] if "dest" in params else "/tmp"
            files = self.rest.download(params["file"], dest)
            if files == None:
                return Result.error(self.rest.errorInfo)

            return Result.value(files)

        elif action[0:4] == "drv.":

            if objects != None:
                if type(objects) is list:
                    if len(objects) != 1:
                        return Result.error(
                            "There should be only one object, but there are %d: %s"
                            % (len(objects), objects))
                    objects = objects[0]

                objects = self.xpath.resolveObjects(objects)

            if objects == None:
                return Result.error(
                    "Can not fetch DRV: no valid objects selected")

            drv = DRV(objects, self.rest)
            if action[4:] == "fetch":
                result = Result.value(drv.fetch())

            elif action[4:] == "subscribe":
                result = Result.value(drv.subscribe())

            else:
                result = Result.error("Unknown DRV action %s" % action[4:])

            return result

        else:

            log.error("Unknown action: %s" % action)
            result = Result.error("Unknown action %s" % action)

        # log.info("action %s result: %s" % (action, json.dumps(result.val, indent=4)))

        self.serialize()
        return result

    def dump(self):
        self.datamodel.dump()

    def serialize(self):
        self.datamodel.serialize()

    # --------------------------------------------------------------------
    # --------------------------------------------------------------------
    # --------------------------------------------------------------------

    def new_session(self,
                    user_name,
                    session_name,
                    chassis=[],
                    props={
                        "ports": [],
                        "names": []
                    },
                    reset_existing=True,
                    kill_existing=False):

        self.datamodel.new(session_name + " - " + user_name, chassis, props),
        if not self.rest.new_session(user_name, session_name, reset_existing,
                                     kill_existing):
            return Result.error("Failed to create a session: %s" %
                                self.rest.errorInfo)

        if len(chassis) > 0 and not self.rest.connect(chassis):
            return Result.error("Failed to connect to the chassis: %s" %
                                self.rest.errorInfo)

        return Result.value(1)

    def attach_session(self,
                       session_name,
                       user_name,
                       chassis=[],
                       props={
                           "ports": [],
                           "names": []
                       },
                       reset_existing=True,
                       kill_existing=False):
        self.datamodel.new(session_name + " - " + user_name, chassis, props)
        if not self.rest.attach_session(session_name, user_name):
            return Result.error("Failed to attach session: %s" %
                                self.rest.errorInfo)

        return Result.value(1)

    def delete_session(self, session_name):
        self.datamodel.deleteSession(session_name)
        if not self.rest.delete_session(session_name):
            return Result.error("Failed to delete session: %s" %
                                self.rest.errorInfo)

        return Result.value(1)

    def delete_all_sessions(self):
        self.datamodel.session()
        if not self.rest.delete_all_sessions():
            return Result.error("Failed to delete all sessions: %s" %
                                self.rest.errorInfo)

        return Result.value(1)

    def load_datamodel(self, filename):

        with open(filename) as dmfile:

            res = self.rest.perform("LoadFromXml", {
                "InputConfigString": dmfile.read(),
                "Filename": "",
            })
            if res != None:
                res["InputConfigString"] = "..."
            else:
                return Result.error(self.rest.errorInfo)

            #Then reset the data-model
            self.datamodel.reset()

            return Result.value(res)
        return "Ooops"

    def config(self, properties, objref=None, count=1):

        handles = {}
        for i in range(0, count):

            ref = self.templater.get(objref, i)
            obj = self.xpath.resolveSingleObject(ref)
            if ref[:7] == "ref:? /" and obj == None:
                log.warning("config: Can not find parent object %s" % ref[7:])
                continue
            elif obj == None:
                return Result.error("config: Can not find parent object %s" %
                                    ref)

            r = self.configObject(obj, self.templater.get(properties, i))
            if r.isError():
                return r

            handles[i] = r.val

        if count == 1:
            handles = handles[0]
        return Result.value(handles)

    def create(self, objects, under=None, count=1):

        handles = {}
        for i in range(0, count):

            parent = None
            if under != None:
                ref = self.templater.get(under, i)
                parent = self.xpath.resolveSingleObject(ref)
                if parent == None:
                    return Result.error(
                        "create: Can not find parent object %s" % ref)

            r = self.createObject(self.templater.get(objects, i), parent)
            if r.isError():
                return r

            handles[i] = r.val

        if count == 1:
            handles = handles[0]
        return Result.value(handles)

    def perform(self, command, properties, count=1):

        handles = {}
        name = properties["name"] if "name" in properties else ""
        userTags = properties["tag"] if "tag" in properties else ""
        for i in range(0, count):
            props = self.templater.get(properties, i)
            if command == "DeviceCreate":
                props["name"] = name
                if userTags != "":
                    props["tag"] = userTags

            r = self.performConfig(command, props)
            if r.isError():
                return r

            handles[i] = r.val

        if count == 1:
            handles = handles[0]
        return Result.value(handles)

    def delete(self, objects, count=1):

        nodes = self._getnodes(objects, count)
        if nodes.isError():
            return nodes

        handles = []
        for node in nodes.val:
            if not self.rest.delete(node.handle):
                return Result.error(self.rest.errorInfo)

            self.datamodel.deleteNode(node)
            handles.append(node.handle)

        return Result.value(handles)

    def wait(self, objects, until, timeout=60, count=1):

        nodes = self._getnodes(objects, count)
        if nodes.isError():
            return nodes

        start = time.time()
        while time.time() - start < timeout:
            failed = 0
            for node in nodes.val:
                # Evaluate the condition
                if not self.evaluateCondition(node.handle, until):
                    failed += 1
                    pass
            if failed == 0:
                break
            time.sleep(1)

        if failed > 0:
            return Result.error("[wait] failed on condition %s" % until)

        return Result.value([n.handle for n in nodes.val])

    def get(self, objects, count=1):

        nodes = self._getnodes(objects, count)
        if nodes.isError():
            return nodes

        result = {}
        isSingleton = len(nodes.val) == 1
        for node in nodes.val:
            obj = self.rest.get(node.handle)
            if isSingleton:
                result = obj
            else:
                result[node.handle] = obj

        return Result.value(result)

    def _getnodes(self, objects, count=1):
        if not type(objects) is list:
            objects = [objects]
        log.debug("Get all handles %s/%d" % (objects, count))

        nodes = []
        for i in range(0, count):
            for obj in objects:
                ref = self.templater.get(obj, i)
                selection = self.xpath.resolveObjects(ref)
                log.debug("Get all nodes [%d/%d] -> %s -> %s" %
                          (i, count, ref, selection))
                if selection != None:
                    nodes += selection.nodes

        if len(nodes) == 0:
            return Result.error(
                "Can not find any object matching %s (count=%d)" %
                (obj, count))

        return Result.value(nodes)

    # --------------------------------------------------------------------
    # --------------------------------------------------------------------
    # --------------------------------------------------------------------

    def evaluateCondition(self, handle, condition):

        match = re.findall(r"^\s*(\S+)\s*=\s*(\S+)\s*$", condition)
        if len(match) != 1:
            raise Exception("Failed to parse condition: %s" % condition)
        key = match[0][0]
        val = match[0][1]

        obj = self.rest.get(handle)

        if not (key in obj):
            raise Exception("Object %s does not have any property %s" %
                            (obj, key))

        name = (" (" + obj["name"] + ")") if "name" in obj else ""
        log.info("[evaluate] handle %s%s key %s val %s expected %s" %
                 (handle, name, key, obj[key], val))
        return obj[key] == val

    # --------------------------------------------------------------------
    # --------------------------------------------------------------------
    # --------------------------------------------------------------------

    def performConfig(self, command, props):

        params = {}
        for key in props.keys():

            val = props[key]
            if type(val) is str and val[0:4] == "ref:":
                objects = self.xpath.resolveObjects(val)
                if objects == None:
                    return Result.error("Failed to resolve: %s" % val)
                val = " ".join(objects.handles())
            params[key] = val

        # The tags are configured with device config
        userTags = {}
        if command == "DeviceCreate":
            userTags = self.tagMgr.getPoppedTags(params)
            log.info("Pop user tags(%s) for later configuration" %
                     str(userTags))

        result = self.rest.perform(command, params)
        if result == None:
            return Result.error(self.rest.errorInfo)

        if command != "DeviceCreate" or not ("name" in props):
            return Result.value(result)

        if not "ReturnList" in result or result["ReturnList"] == None:
            return Result.value("No handles returned!")

        handles = result["ReturnList"].split(" ")
        log.info("There are %d handles to configure" % len(handles))

        for i in range(len(handles)):
            handle = handles[i]
            newTag = self.templater.get(userTags, i)
            self.tagMgr.handleTags(newTag)

            attributes = {
                "name": self.templater.get(props["name"], i),
                "usertag-targets": newTag.get("usertag-targets", "")
            }
            if not self.rest.config(handle, attributes):
                return Result.error(self.rest.errorInfo)

            # Add the new object to the internal data model
            attributes["object_type"] = "EmulatedDevice"
            self.datamodel.insert(handle, attributes,
                                  self.datamodel.root["project1"])

        return Result.value(handles)

        # self.datamodel.dump()

    # --------------------------------------------------------------------
    # --------------------------------------------------------------------
    # --------------------------------------------------------------------

    def configObject(self, root, properties):

        objects = [{root.objectType(): properties}]
        return self.createOrConfigObject(objects, root, True)

    def createObject(self, objects, parent):

        return self.createOrConfigObject(objects, parent, False)

    # --------------------------------------------------------------------

    def createOrConfigObject(self, objects, parent, isConfig):

        tree = ObjectTree(objects)

        # ---- Step 1: create all objects

        newObjects = {}
        for obj in tree.objects:
            props = obj["props"]
            references = {}

            params = {}
            if not isConfig:
                under = parent
                if "under" in obj:
                    under = newObjects[obj["under"]]
                if under != None:
                    params["under"] = under.handle

            for key in props.keys():

                val = props[key]
                if type(val) is str and val[0:4] == "ref:":
                    objects = self.xpath.resolveObjects(val)
                    if objects == None:
                        log.info(
                            "reference \033[92m%s\033[0m is not resolved yet" %
                            (val))
                        references[key] = val
                        continue
                    log.info("reference \033[92m%s\033[0m resolved to %s" %
                             (val, objects))
                    val = " ".join(objects.handles())

                params[key] = val

            obj["references"] = references

            if not isConfig:

                # print("Creating",json.dumps(params,indent=4))
                fparams = {
                    key: value
                    for (key, value) in params.items()
                    if key.find(".object_type") < 0
                }
                # print("Creating",json.dumps(fparams,indent=4))
                if under != None:
                    # When project1 is created, if project1's children is not got,
                    # getting children in tag1 will fail.
                    self.rest.children("project1")
                    self.tagMgr.handleTags(fparams)

                ## DynamicResultView with the same name will be created multiple times, which cause
                ## subscription failed
                if obj["type"] == 'DynamicResultView':
                    drvName = fparams.get('name')
                    if drvName != None:
                        self.delete(
                            'ref:/project/DynamicResultView[name="%s"]' %
                            drvName)

                handle = self.rest.create(obj["type"], fparams)
                if handle == None:
                    return Result.error(self.rest.errorInfo)

                params["object_type"] = obj["type"]
                newObject = self.datamodel.insert(handle, params, under)

                log.info("New object created. Handle %s props %s" %
                         (handle, json.dumps(params, indent=4)))

                newObjects[obj["type"]] = newObject
                obj["object"] = newObject
                obj["under"] = under

                if obj["type"].lower(
                ) == "port" and "name" in params and "location" in params:
                    log.info(
                        "Port %s created with location %s -> handle %s" %
                        (Color.green(params["name"]),
                         Color.green(params["location"]), Color.blue(handle)))

            else:

                # Remove the "object_type" property has it can fail
                fparams = {
                    key: value
                    for (key, value) in params.items()
                    if key.find("object_type") < 0
                }
                if not self.rest.config(parent.handle, fparams):
                    return Result.error(self.rest.errorInfo)
                obj["object"] = parent

        # print("children:", json.dumps(obj["children"], indent=4))

        # ---- Step 2: learn the children handles

        for obj in tree.objects:
            children = self.rest.children(obj["object"].handle)
            # print("Learning children for %s: %s" %
            #       (obj["object"].handle, children))
            for key in obj["children"]:

                if key.find(".") >= 0:
                    continue
                childid = key.lower()
                found = False
                for handle in children:
                    if handle[0:len(childid)] == childid:
                        found = True
                        break
                if not found:
                    # print("Failed to find handle for child", key, "from",
                    #       children)
                    continue

                params = obj["children"][key]
                params["object_type"] = key
                newObject = self.datamodel.insert(handle, params,
                                                  obj["object"])

                log.info("New child object added. Handle %s props %s" %
                         (handle, json.dumps(params, indent=4)))

            #Check for any new children which was added with beeing queried
            for handle in children:
                if not handle in obj["object"].children:
                    otype = re.sub(r'^(.*?)([0-9]*)$', r'\1', handle)
                    params = {"object_type": otype, "!discovered": True}
                    newObject = self.datamodel.insert(handle, params,
                                                      obj["object"])

        # ---- Step 3: resolve the references

        for obj in tree.objects:

            config = {}
            refs = obj["references"]
            for key in refs.keys():

                val = refs[key]
                objects = self.xpath.resolveObjects(val, obj["object"])
                if objects == None:
                    log.error(
                        "Failed to resolve '\033[91m%s\033[0m' for property %s in %s"
                        % (val, key, obj["object"]))
                    return Result.error(
                        "Invalid reference '\033[91m%s\033[0m' for property %s in %s"
                        % (val, key, obj["object"]))

                config[key] = " ".join(objects.handles())
                log.info("Reference '\033[92m%s\033[0m' resolved to %s" %
                         (val, objects))

            if config != {}:
                if not self.rest.config(obj["object"].handle, config):
                    return Result.error(self.rest.errorInfo)

        handles = []
        for obj in tree.objects:
            handles.append(obj["object"].handle)

        return Result.value(handles)