Exemplo n.º 1
0
def save(extract, filename, **kwargs):
    """
    save lab to a local yaml file
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            if extract:
                click.secho("Extracting configurations...")
                extract_configurations(lab)

            lab_export = lab.download()

            click.secho("Writing {}".format(filename))
            with open(filename, "w") as fd:
                fd.write(lab_export)
        else:
            click.secho("Failed to find running lab {}".format(current_lab),
                        fg="red")
            exit(1)
    else:
        click.secho("Current lab is not set", fg="red")
        exit(1)
Exemplo n.º 2
0
def pyats(**kwargs):
    """
    generates a pyats testbed config for a lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            if kwargs.get("output"):
                # user specified output filename
                file_name = kwargs.get("output")
            else:
                # writes to <lab.id>_testbed.yaml by default
                file_name = "{}_testbed.yaml".format(lab.id)

            testbed = pyats_testbed_generator(lab)

            if testbed:
                click.secho("Writing {}".format(file_name))
                with open(file_name, "w") as fd:
                    fd.write(testbed)
            else:
                click.secho("Failed to get testbed data", fg="red")
                exit(1)
        else:
            click.secho("Failed to find running lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("Current lab is not set", fg="red")
        exit(1)
Exemplo n.º 3
0
def ls(**kwargs):
    """
    list all images or the details of a specific image
    """

    image = kwargs.get("image")
    server = VIRLServer()
    client = get_cml_client(server)
    pl = None

    # Regardless of the argument, we have to get all the flavors
    # In the case of no arg, we print them all.
    # In the case of an arg, we have to go back and get details.
    defs = client.definitions.image_definitions()

    try:
        pl = ViewerPlugin(viewer="image_def")
    except NoPluginError:
        pass

    if image:
        for f in list(defs):
            if f["name"] == image:
                if pl:
                    pl.visualize(image_defs=[f])
                else:
                    image_list_table([f])
                break
    else:
        if pl:
            pl.visualize(image_defs=defs)
        else:
            image_list_table(defs)
Exemplo n.º 4
0
def stop(node):
    """
    stop a node
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            try:
                node_obj = lab.get_node_by_label(node)

                if node_obj.is_active():
                    node_obj.stop(wait=True)
                    click.secho("Stopped node {}".format(node_obj.label))
                else:
                    click.secho("Node {} is already stopped".format(
                        node_obj.label),
                                fg="yellow")
            except NodeNotFound:
                click.secho("Node {} was not found in lab {}".format(
                    node, current_lab),
                            fg="red")
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 5
0
def use(lab, id, lab_name):
    """
    use lab launched elsewhere
    """
    server = VIRLServer()
    client = get_cml_client(server)
    lab_id = None

    if not lab and not id and not lab_name:
        exit(call([get_command(), "use", "--help"]))

    if id:
        lab_id = check_lab_cache_server(id, client)

    # Prefer --lab-name over positional argument
    if lab_name:
        lab = lab_name

    if not id and lab:
        lab_obj = safe_join_existing_lab_by_title(lab, client)
        if lab_obj:
            # Make sure this lab is cached.
            lab_id = check_lab_cache_server(lab_obj.id, client)

    if lab_id:
        set_current_lab(lab_id)
    else:
        click.secho("Unable to find lab in the cache or on the server",
                    fg="red")
        exit(1)
Exemplo n.º 6
0
def ls(all, all_users):
    """
    lists running labs and optionally those in the cache
    """
    server = VIRLServer()
    client = get_cml_client(server)
    labs = []
    cached_labs = None

    lab_ids = client.get_lab_list(all_users)
    for id in lab_ids:
        labs.append(client.join_existing_lab(id))

    if all:
        cached_labs = []
        cache_root = get_cache_root()
        if os.path.isdir(cache_root):
            for f in os.listdir(cache_root):
                lab_id = f
                cached_labs.append(CachedLab(lab_id, cache_root + "/" + f))

    try:
        pl = ViewerPlugin(viewer="lab")
        pl.visualize(labs=labs, cached_labs=cached_labs)
    except NoPluginError:
        lab_list_table(labs, cached_labs)
Exemplo n.º 7
0
def nimport(node, filename):
    """
    import a node definition
    """

    server = VIRLServer()
    client = get_cml_client(server)

    if not os.path.isfile(filename):
        click.secho("Node definition file {} does not exist or is not a file",
                    fg="red")
        exit(1)
    else:
        defs = client.definitions
        contents = None

        with open(filename, "r") as fd:
            contents = fd.read()

        try:
            defs.upload_node_definition(node, contents)
        except Exception as e:
            click.secho("Failed to import node definition for {}: {}".format(
                node, e),
                        fg="red")
            exit(1)
Exemplo n.º 8
0
def down(id=None, lab_name=None):
    """
    stop a lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    lab = None

    if id:
        lab = safe_join_existing_lab(id, client)

    if not lab and lab_name:
        lab = safe_join_existing_lab_by_title(lab_name, client)

    if not lab:
        lab_id = get_current_lab()
        if lab_id:
            lab = safe_join_existing_lab(lab_id, client)

    if lab:
        if lab.is_active():
            click.secho("Shutting down lab {} (ID: {}).....".format(
                lab.title, lab.id))
            lab.stop()
            click.echo(click.style("SUCCESS", fg="green"))
        else:
            click.secho(
                "Lab with ID {} and title {} is already stopped".format(
                    lab.id, lab.title))

    else:
        click.secho("Failed to find lab on server", fg="red")
        exit(1)
Exemplo n.º 9
0
def deregister(confirm):
    """
    deregister the Smart License
    """
    server = VIRLServer()
    client = get_cml_client(server)

    # As of 2.0.0b5 of virl2-client, there is no Python API for licensing.  So we use
    # the library as much as we can, and use requests for the rest.

    ret = "y"
    if confirm:
        ret = input("Are you sure you want to deregister [y/N]? ")
        if not ret.lower().startswith("y"):
            click.secho("Not deregistering")
            exit(0)

    try:
        response = client.session.delete(client._base_url +
                                         "licensing/deregistration")
        response.raise_for_status()
    except Exception as e:
        click.secho("Failed to deregister the Smart License: {}".format(e),
                    fg="red")
        exit(1)
Exemplo n.º 10
0
def register(token, **kwargs):
    """
    register with a Smart License account
    """
    ssms = kwargs["smart_license_server"]
    proxy = kwargs["proxy_host"]
    port = None
    cert = kwargs["certificate"]
    reregister = kwargs["reregister"]
    server = VIRLServer()
    client = get_cml_client(server)
    licensing = client.licensing

    if ssms or proxy:
        if not ssms:
            ssms = DEFAULT_SSMS
        if proxy:
            port = kwargs["proxy_port"]

        try:
            licensing.set_transport(ssms, proxy, port)
        except Exception as e:
            click.secho(
                "Failed to configure Smart License server and proxy: {}".
                format(e),
                fg="red")
            exit(1)
    else:
        try:
            licensing.delete_certificate()
        except Exception:
            pass

        licensing.set_default_transport()

    if cert:
        if not os.path.isfile(cert):
            click.secho("Certificate {} is not a valid file!".format(cert),
                        fg="red")
            exit(1)

        with open(cert, "r") as fd:
            contents = fd.read()
            try:
                licensing.upload_certificate(contents)
            except Exception as e:
                click.secho("Failed to upload certificate {}: {}".format(
                    cert, e),
                            fg="red")
                exit(1)

    try:
        licensing.register(token, reregister)
    except Exception as e:
        click.secho("Failed to register with Smart Licensing: {}".format(e),
                    fg="red")
        exit(1)
Exemplo n.º 11
0
def rm(force, confirm, from_cache):
    """
    remove a lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            if lab.is_active() and force:
                lab.stop(wait=True)

            if lab.state() != "DEFINED_ON_CORE" and force:
                lab.wipe(wait=True)

            # Check again just to be sure.
            if lab.state() == "DEFINED_ON_CORE":
                ret = "y"
                if confirm:
                    ret = input(
                        "Are you sure you want to remove lab {} (ID: {}) [y/N]? "
                        .format(lab.title, current_lab))
                if ret.lower().startswith("y"):
                    # We need to save the lab's title before we remove it.
                    title = lab.title
                    lab.remove()
                    click.secho("Lab {} (ID: {}) removed".format(
                        title, current_lab))
                    if from_cache:
                        try:
                            os.remove(check_lab_cache(current_lab))
                        except OSError:
                            # File doesn't exist.
                            pass

                        click.secho(
                            "Removed lab {} from cache".format(current_lab))
                    clear_current_lab()
                else:
                    click.secho("Not removing lab {} (ID: {})".format(
                        lab.title, current_lab))

            else:
                click.secho(
                    "Lab {} (ID: {}) is either active or not wiped; either down and wipe it or use --force"
                    .format(lab.title, current_lab),
                    fg="red",
                )
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("Current lab is not set", fg="red")
        exit(1)
Exemplo n.º 12
0
def command(node, command, config, **kwargs):
    """
    send a command or config to a node (requires pyATS)
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            pylab = None
            try:
                pylab = ClPyats(lab)
            except PyatsNotInstalled:
                click.secho("pyATS is not installed, run 'pip install pyats'",
                            fg="red")
                exit(1)

            pyats_username = server.config.get("CML_DEVICE_USERNAME")
            pyats_password = server.config.get("CML_DEVICE_PASSWORD")
            pyats_auth_password = server.config.get(
                "CML_DEVICE_ENABLE_PASSWORD")

            if pyats_username:
                os.environ["PYATS_USERNAME"] = pyats_username
            if pyats_password:
                os.environ["PYATS_PASSWORD"] = pyats_password
            if pyats_auth_password:
                os.environ["PYATS_AUTH_PASS"] = pyats_auth_password

            pylab.sync_testbed(server.user, server.passwd)

            try:
                result = ""
                if config:
                    result = pylab.run_config_command(node, command)
                else:
                    result = pylab.run_command(node, command)

                click.secho(result)
            except PyatsDeviceNotFound:
                click.secho("Node '{}' is not supported by pyATS".format(node),
                            fg="yellow")
            except Exception as e:
                click.secho("Failed to run '{}' on '{}': {}".format(
                    command, node, e))
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 13
0
def __get_server_ver():
    """
    Taste a VIRL/CML server and try and determine its version.
    This tries the 2.x flow and assumes 1.x if that flow fails in an
    unexpected way.  The reason for this is that compatibility with 1.x
    is stressed, but the code has been factored in a way so that the 1.x
    code can be removed when the time is right.

    Returns:
        string: Either '1' for VIRL/CML 1.x or the empty string for CML 2+
    """
    res = ""
    try:
        server = VIRLServer()
        if "CML2_PLUS" not in server.config:
            # If the user hasn't explicitly said they are on the CML 2+, then
            # attempt to guess the server version.

            # We don't care about cert validation here.  If this is a CML server,
            # we'll fail validation later anyway.
            #
            # Because of that, pass obviously bogus credentials.  The login will fail
            # in a predictable way if this is a CML server.
            requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
            r = requests.get("https://{}/".format(server.host), verify=False)
            warnings.simplefilter("default", InsecureRequestWarning)
            r.raise_for_status()
            # While one could have a user called virutils-test, the user must have a password.
            # So if we send an empty password, that will fail with a known error.
            server.user = "******"
            server.passwd = ""
            get_cml_client(server, ignore=True)
    except virl2_client.InitializationError:
        # The client library will raise this error if it encounters an authorization failure.
        pass
    except Exception:
        # Any other error likely means a VIRL/CML 1.x host.
        res = "1"

    return res
Exemplo n.º 14
0
def registration():
    """
    renew Smart License registration
    """
    server = VIRLServer()
    client = get_cml_client(server)
    licensing = client.licensing

    try:
        licensing.register_renew()
    except Exception as e:
        click.secho("Failed to renew registration: {}".format(e), fg="red")
        exit(1)
Exemplo n.º 15
0
def ui():
    """
    opens the Workbench for the current lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            url = "https://{}/lab/{}".format(server.host, current_lab)
            subprocess.Popen(["open", url])
Exemplo n.º 16
0
def update(id, value):
    """
    update the number of feature instances
    """
    server = VIRLServer()
    client = get_cml_client(server)
    licensing = client.licensing

    try:
        licensing.update_features({id: value})
    except Exception as e:
        click.secho("Failed to update features: {}".format(e), fg="red")
        exit(1)
Exemplo n.º 17
0
def ssh(node):
    """
    ssh to a node
    """
    server = VIRLServer()
    client = get_cml_client(server)
    username = server.config.get("VIRL_SSH_USERNAME", "cisco")

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            node_obj = lab.get_node_by_label(node)

            if node_obj:
                if node_obj.is_active():
                    mgmtip = get_node_mgmt_ip(node_obj)
                    if mgmtip:
                        if "VIRL_SSH_COMMAND" in server.config:
                            cmd = server.config["VIRL_SSH_COMMAND"]
                            cmd = cmd.format(host=mgmtip, username=username)
                            print("Calling user specified command: {}".format(
                                cmd))
                            exit(call(cmd.split()))
                        else:
                            click.secho(
                                "Attemping ssh connection to {} at {}".format(
                                    node_obj.label, mgmtip))

                            exit(
                                call(["ssh", "{}@{}".format(username,
                                                            mgmtip)]))
                    else:
                        click.secho(
                            "Node {} does not have an external management IP".
                            format(node_obj.label))
                else:
                    click.secho("Node {} is not active".format(node_obj.label),
                                fg="yellow")
            else:
                click.secho("Node {} was not found in lab {}".format(
                    node, current_lab),
                            fg="red")
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 18
0
def show():
    """
    display license details
    """
    server = VIRLServer()
    client = get_cml_client(server)
    licensing = client.licensing

    try:
        license = licensing.features()
    except Exception as e:
        click.secho("Failed to get license features: {}".format(e), fg="red")
        exit(1)
    else:
        license_features_table(license)
Exemplo n.º 19
0
def node(node, force, confirm):
    """
    wipe a node
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            node_obj = lab.get_node_by_label(node)

            if node_obj:
                if node_obj.is_active() and force:
                    node_obj.stop()
                    while node_obj.is_active():
                        time.sleep(1)

                if not node_obj.is_active():
                    ret = "y"
                    if confirm:
                        ret = input(
                            "Are you sure you want to wipe node {} [y/N]? ".
                            format(node_obj.label))
                    if ret.lower().startswith("y"):
                        node_obj.wipe(wait=True)
                        click.secho("Node {} wiped".format(node_obj.label))
                    else:
                        click.secho("Not wiping node {}".format(
                            node_obj.label))
                else:
                    click.secho(
                        "Node {} is active; either stop it or use --force".
                        format(node_obj.label),
                        fg="red")
                    exit(1)
            else:
                click.secho("Node {} was not found in lab {}".format(
                    node, current_lab),
                            fg="red")
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 20
0
def version():
    """
    version information
    """
    server = VIRLServer()
    client = get_cml_client(server)
    server_version = "Unknown"
    try:
        response = client.session.get(client._base_url + "system_information")
        response.raise_for_status()
        server_version = response.json()["version"]
    except Exception:
        pass
    virlutils_version = __version__
    click.secho("virlutils Version: {}".format(virlutils_version))
    click.secho("CML Controller Version: {}".format(server_version))
Exemplo n.º 21
0
def nso(syncfrom, **kwargs):
    """
    generate nso inventory
    """

    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:

            if kwargs.get("output"):
                file_name = kwargs.get("output")
            else:
                file_name = None

            inv = nso_payload_generator(lab, server)

            if inv:
                if file_name:
                    click.secho("Writing {}".format(file_name))
                    with open(file_name, "w") as fd:
                        fd.write(inv)
                else:
                    click.secho("Updating NSO....")
                    nso_obj = NSO()
                    nso_response = nso_obj.update_devices(inv)
                    if nso_response.ok:
                        click.secho("Successfully added CML devices to NSO")
                    else:
                        click.secho("Error updating NSO: ", fg="red")
                        click.secho(nso_response.text)
                    if syncfrom:
                        resp = nso_obj.perform_sync_from()
                        sync_table(resp.json())
            else:
                click.secho("Failed to get inventory data", fg="red")
                exit(1)
        else:
            click.secho("Failed to find running lab {}".format(current_lab),
                        fg="red")
            exit(1)
    else:
        click.secho("Current lab is not set", fg="red")
        exit(1)
Exemplo n.º 22
0
def telnet(node):
    """
    telnet to a node
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            try:
                node_obj = lab.get_node_by_label(node)
            except NodeNotFound:
                click.secho("Node {} was not found in lab {}".format(
                    node, current_lab),
                            fg="red")
                exit(1)

            if node_obj.is_active():
                mgmtip = get_node_mgmt_ip(node_obj)
                if mgmtip:
                    if "VIRL_TELNET_COMMAND" in server.config:
                        cmd = server.config["VIRL_TELNET_COMMAND"]
                        cmd = cmd.format(host=mgmtip)
                        print("Calling user specified command: {}".format(cmd))
                        exit(call(cmd.split()))
                    else:
                        click.secho(
                            "Attemping telnet connection to {} at {}".format(
                                node_obj.label, mgmtip))

                        exit(call(["telnet", mgmtip]))
                else:
                    click.secho(
                        "Node {} does not have an external management IP".
                        format(node_obj.label))
            else:
                click.secho("Node {} is not active".format(node_obj.label),
                            fg="yellow")
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 23
0
def registration():
    """
    renew Smart License registration
    """
    server = VIRLServer()
    client = get_cml_client(server)

    # As of 2.0.0b5 of virl2-client, there is no Python API for licensing.  So we use
    # the library as much as we can, and use requests for the rest.

    try:
        response = client.session.put(client._base_url +
                                      "licensing/registration/renew")
        response.raise_for_status()
    except Exception as e:
        click.secho("Failed to renew registration: {}".format(e), fg="red")
        exit(1)
Exemplo n.º 24
0
def show():
    """
    display license details
    """
    server = VIRLServer()
    client = get_cml_client(server)

    # As of 2.0.0b5 of virl2-client, there is no Python API for licensing.  So we use
    # the library as much as we can, and use requests for the rest.
    try:
        response = client.session.get(client._base_url + "licensing")
        response.raise_for_status()
        license = response.json()
    except Exception as e:
        click.secho("Failed to get license details: {}".format(e), fg="red")
        exit(1)
    else:
        license_details_table(license)
Exemplo n.º 25
0
def nodes():
    """
    get node list for the current lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            node_list_table(lab.nodes())
        else:
            click.secho("Lab {} is not running".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab selected", fg="red")
        exit(1)
Exemplo n.º 26
0
def show():
    """
    display license details
    """
    server = VIRLServer()
    client = get_cml_client(server)
    licensing = client.licensing

    try:
        license = licensing.status()
    except Exception as e:
        click.secho("Failed to get license details: {}".format(e), fg="red")
        exit(1)
    else:
        try:
            pl = ViewerPlugin(viewer="license")
            pl.visualize(license=license)
        except NoPluginError:
            license_details_table(license)
Exemplo n.º 27
0
    def generate(delimiter, output):
        """
        generate generic CSV inventory
        """
        server = VIRLServer()
        client = get_cml_client(server)

        current_lab = get_current_lab()
        if not current_lab:
            click.secho("Current lab is not set", fg="red")
            exit(1)

        lab = safe_join_existing_lab(current_lab, client)
        if not lab:
            click.secho("Failed to find running lab {}".format(current_lab),
                        fg="red")
            exit(1)

        exit(CSVInventory.write_inventory(lab.nodes(), delimiter, output))
Exemplo n.º 28
0
def lab(force, confirm):
    """
    wipe a lab
    """
    server = VIRLServer()
    client = get_cml_client(server)

    current_lab = get_current_lab()
    if current_lab:
        lab = safe_join_existing_lab(current_lab, client)
        if lab:
            active = check_lab_active(lab)
            if active and force:
                lab.stop(wait=True)

            # Check again just to be sure.
            if not check_lab_active(lab):
                ret = "y"
                if confirm:
                    ret = input(
                        "Are you sure you want to wipe lab {} (ID: {}) [y/N]? "
                        .format(lab.title, current_lab))
                if ret.lower().startswith("y"):
                    lab.wipe(wait=True)
                    click.secho("Lab {} (ID: {}) wiped".format(
                        lab.title, current_lab))
                else:
                    click.secho("Not wiping lab {} (ID: {})".format(
                        lab.title, current_lab))

            else:
                click.secho(
                    "Lab {} (ID: {}) is active; either down it or use --force".
                    format(lab.title, current_lab),
                    fg="red")
                exit(1)
        else:
            click.secho("Unable to find lab {}".format(current_lab), fg="red")
            exit(1)
    else:
        click.secho("No current lab set", fg="red")
        exit(1)
Exemplo n.º 29
0
def image_file(filename, rename):
    """
    import an image file
    """

    server = VIRLServer()
    client = get_cml_client(server)

    if not os.path.isfile(filename):
        click.secho("Image file {} does not exist or is not a file", fg="red")
        exit(1)
    else:
        defs = client.definitions
        try:
            defs.upload_image_file(filename, rename)
        except Exception as e:
            click.secho("Failed to import image file {}: {}".format(
                filename, e),
                        fg="red")
            exit(1)
Exemplo n.º 30
0
def ls(**kwargs):
    """
    list all images or the details of a specific image
    """

    image = kwargs.get("image")
    server = VIRLServer()
    client = get_cml_client(server)

    # Regardless of the argument, we have to get all the flavors
    # In the case of no arg, we print them all.
    # In the case of an arg, we have to go back and get details.
    defs = client.definitions.image_definitions()

    if image:
        for f in list(defs):
            if f["name"] == image:
                image_list_table([f])
                break
    else:
        image_list_table(defs)