Exemplo n.º 1
0
def show_instance_details(instance_id):
    """
    Shows the details of a Packet Tracer instance.
    ---
    tags:
      - instance
    parameters:
      - name: instance_id
        in: path
        type: integer
        description: instance identifier
        required: true
    responses:
      200:
        description: Details of the instance
        schema:
            $ref: '#/definitions/assign_instance_post_Instance'
      404:
        description: There is not an instance for the given instance_id.
        schema:
            $ref: '#/definitions/allocate_instance_post_Error'
    """
    instance = Instance.get(instance_id)
    if instance is None:
        return not_found(error="The instance does not exist.")
    return jsonify(instance.serialize(request.base_url, get_host()))
Exemplo n.º 2
0
def try_restart_on_exited_containers():
    docker = get_docker_client()
    restarted_instances = []
    # 'exited': 0 throws exception, 'exited': '0' does not work.
    # Because of this I have felt forced to use regular expressions :-(
    pattern = re.compile(r"Exited [(](\d+)[)]")
    for container in docker.containers(filters={'status': 'exited'}):
        # Ignore containers not created from image 'packettracer'
        if container.get('Image')=='packettracer':
            match = pattern.match(container.get('Status'))
            if match:  # Stopped containers only
                container_id = container.get('Id')
                instance = Instance.get_by_docker_id(container_id)
                if instance:
                    if match.group(1)=='0':
                        # Restart stopped containers (which exited successfully)
                        instance.mark_starting()
                        try:
                            logger.info('Restarting %s.' % instance)
                            docker.start(container=container_id)
                            wait_for_ready_container.s(instance.id).delay()
                            restarted_instances.append(instance.id)
                        except APIError as ae:
                            logger.error('Error restarting container.')
                            logger.error('Docker API exception. %s.' % ae)
                            instance.mark_error()
                    else:
                        # TODO Check more thoroughly if containers with other
                        # type of exit errors could be restarted too.
                        instance.mark_error()
    return restarted_instances
Exemplo n.º 3
0
def delete_instance(instance_id):
    """
    Stops a running Packet Tracer instance.
    ---
    tags:
      - instance
    parameters:
      - name: instance_id
        in: path
        description: instance identifier
        required: true
        type: integer
    responses:
      200:
          description: Instance stopped
          schema:
              $ref: '#/definitions/assign_instance_post_Instance'
      404:
          description: There is not an instance for the given instance_id.
          schema:
              $ref: '#/definitions/allocate_instance_post_Error'
      503:
          description: The instance has not been deleted in the given time.
          schema:
              $ref: '#/definitions/allocate_instance_post_Error'
    """
    instance = Instance.get(instance_id)
    if not instance or not instance.is_active():
        return not_found(error="The instance does not exist.")

    result = tasks.remove_container.delay(instance.docker_id)
    result.get()
    instance.delete()
    # TODO update instance object as status has changed
    return jsonify(instance.serialize(request.base_url, get_host()))
Exemplo n.º 4
0
def assign_instance():
    """
    Creates a new Packet Tracer instance.
    ---
    tags:
        - instance
    responses:
        201:
            description: Packet Tracer instance created
            schema:
                id: Instance
                properties:
                    id:
                        type: integer
                        description: Identifier of the instance
                    dockerId:
                        type: string
                        description: Identifier of the docker container which serves the instance
                    url:
                        type: string
                        description: URL to handle the instance
                    packetTracer:
                        type: string
                        description: Host and port where the Packet Tracer instance can be contacted (through IPC)
                    vnc:
                        type: string
                        description: VNC URL to access the Packet Tracer instance
                    createdAt:
                        type: string
                        format: date-time
                        description: When was the instance created?
                    deletedAt:
                        type: string
                        format: date-time
                        description: When was the instance removed/stopped?
                    status:
                        type: string
                        enum: [all, starting, deallocated, allocated, running, finished, error]
                        description: Show status of the given instance
        500:
            description: The container could not be created, there was an error.
            schema:
                $ref: '#/definitions/allocate_instance_post_Error'
        503:
            description: At the moment the server cannot create more instances.
            schema:
                $ref: '#/definitions/allocate_instance_post_Error'
    """
    try:
        result = tasks.create_instance.delay()
        instance_id = result.get()
        if instance_id:
            instance = Instance.get(instance_id)
            return jsonify(
                instance.serialize("%s/%d" % (request.base_url, instance.id),
                                   get_host()))
        return unavailable()
    except DockerContainerError as e:
        return internal_error(e.args[0])
Exemplo n.º 5
0
def assign_instance():
    """
    Creates a new Packet Tracer instance.
    ---
    tags:
        - instance
    responses:
        201:
            description: Packet Tracer instance created
            schema:
                id: Instance
                properties:
                    id:
                        type: integer
                        description: Identifier of the instance
                    dockerId:
                        type: string
                        description: Identifier of the docker container which serves the instance
                    url:
                        type: string
                        description: URL to handle the instance
                    packetTracer:
                        type: string
                        description: Host and port where the Packet Tracer instance can be contacted (through IPC)
                    vnc:
                        type: string
                        description: VNC URL to access the Packet Tracer instance
                    createdAt:
                        type: string
                        format: date-time
                        description: When was the instance created?
                    deletedAt:
                        type: string
                        format: date-time
                        description: When was the instance removed/stopped?
                    status:
                        type: string
                        enum: [all, starting, deallocated, allocated, running, finished, error]
                        description: Show status of the given instance
        500:
            description: The container could not be created, there was an error.
            schema:
                $ref: '#/definitions/allocate_instance_post_Error'
        503:
            description: At the moment the server cannot create more instances.
            schema:
                $ref: '#/definitions/allocate_instance_post_Error'
    """
    try:
        result = tasks.create_instance.delay()
        instance_id = result.get()
        if instance_id:
            instance = Instance.get(instance_id)
            return jsonify(instance.serialize("%s/%d" % (request.base_url, instance.id), get_host()))
        return unavailable()
    except DockerContainerError as e:
        return internal_error(e.args[0])
Exemplo n.º 6
0
def delete_erroneous(not_delete=[]):
    deleted_instances = []
    for erroneous_instance in Instance.get_erroneous():
        if erroneous_instance.id not in not_delete:
            logger.info('Deleting erroneous %s.' % erroneous_instance)
            erroneous_instance.delete()
            deleted_instances.append(erroneous_instance.id)
            # Very conservative approach:
            #   we remove it even if it might still be usable.
            remove_container.s(erroneous_instance.docker_id).delay()
    return deleted_instances
Exemplo n.º 7
0
def delete_erroneous(not_delete=[]):
    deleted_instances = []
    for erroneous_instance in Instance.get_erroneous():
        if erroneous_instance.id not in not_delete:
            logger.info('Deleting erroneous %s.' % erroneous_instance)
            erroneous_instance.delete()
            deleted_instances.append(erroneous_instance.id)
            # Very conservative approach:
            #   we remove it even if it might still be usable.
            remove_container.s(erroneous_instance.docker_id).delay()
    return deleted_instances
Exemplo n.º 8
0
def deallocate_instance(instance_id):
    """Marks instance as deallocated and pauses the associated container."""
    logger.info('Deallocating instance %s.' % instance_id)
    instance = Instance.get(instance_id)
    try:
        docker = get_docker_client()
        docker.pause(instance.docker_id)
        instance.deallocate()
    except APIError as ae:
        logger.error('Error deallocating instance %s.' % instance_id)
        logger.error('Docker API exception. %s.' % ae)
        # e.g., if it was already paused
        instance.mark_error()
Exemplo n.º 9
0
def deallocate_instance(instance_id):
    """Marks instance as deallocated and pauses the associated container."""
    logger.info('Deallocating instance %s.' % instance_id)
    instance = Instance.get(instance_id)
    try:
        docker = get_docker_client()
        docker.pause(instance.docker_id)
        instance.deallocate()
    except APIError as ae:
        logger.error('Error deallocating instance %s.' % instance_id)
        logger.error('Docker API exception. %s.' % ae)
        # e.g., if it was already paused
        instance.mark_error()
Exemplo n.º 10
0
def allocate_instance():
    """Unpauses available container and marks associated instance as allocated."""
    logger.info('Allocating instance.')
    docker = get_docker_client()

    error_discovered = False
    allocation_id = None
    for instance in Instance.get_deallocated():
        try:
            docker.unpause(instance.docker_id)
            allocation_id = instance.allocate().id
            break
        except APIError as ae:
            logger.error('Error allocating instance %s.' % instance.id)
            logger.error('Docker API exception. %s.' % ae)
            # e.g., if it was already unpaused or it has been stopped
            instance.mark_error()

    if not allocation_id:
        # If there were no instances available, consider the creation of a new one
        instance_id = create_instance.s()()  # Execute task inline
        allocation_id = Instance.get(instance_id).allocate().id

    return allocation_id
Exemplo n.º 11
0
def allocate_instance():
    """Unpauses available container and marks associated instance as allocated."""
    logger.info('Allocating instance.')
    docker = get_docker_client()

    error_discovered = False
    allocation_id = None
    for instance in Instance.get_deallocated():
        try:
            docker.unpause(instance.docker_id)
            allocation_id = instance.allocate().id
            break
        except APIError as ae:
            logger.error('Error allocating instance %s.' % instance.id)
            logger.error('Docker API exception. %s.' % ae)
            # e.g., if it was already unpaused or it has been stopped
            instance.mark_error()

    if not allocation_id:
        # If there were no instances available, consider the creation of a new one
        instance_id = create_instance.s()()  # Execute task inline
        allocation_id = Instance.get(instance_id).allocate().id

    return allocation_id
Exemplo n.º 12
0
def list_instances():
    """
    Lists instances.
    ---
    tags:
      - instance
    parameters:
      - name: show
        in: query
        type: string
        description: Show different types of instances
        default: running
        enum: [all, starting, deallocated, allocated, running, finished, error]
    responses:
      200:
        description: Packet Tracer instances
        schema:
            properties:
                instances:
                    type: array
                    items:
                      $ref: '#/definitions/assign_instance_post_Instance'
    """
    show_param = request.args.get("show")
    if show_param is None or show_param == "running":  # default option
        return get_json_instances(Instance.get_running())
    else:
        states = ("all", "starting", "deallocated", "allocated", "running",
                  "finished", "error")
        if show_param not in states:
            state_enum = "["
            for s in states:
                state_enum += "%s, " % s
            state_enum = state_enum[:-2] + "]"
            return BadRequest(
                "The 'show' parameter must contain one of the following values: %s."
                % state_enum)

        if show_param == "all":
            return get_json_instances(Instance.get_all())  # .limit(10)
        elif show_param == "starting":
            return get_json_instances(Instance.get_starting())
        elif show_param == "deallocated":
            return get_json_instances(Instance.get_deallocated())
        elif show_param == "allocated":
            return get_json_instances(Instance.get_allocated())
        elif show_param == "error":
            return get_json_instances(Instance.get_erroneous())
        else:  # show_param is "finished":
            return get_json_instances(Instance.get_finished())
Exemplo n.º 13
0
def list_instances():
    """
    Lists instances.
    ---
    tags:
      - instance
    parameters:
      - name: show
        in: query
        type: string
        description: Show different types of instances
        default: running
        enum: [all, starting, deallocated, allocated, running, finished, error]
    responses:
      200:
        description: Packet Tracer instances
        schema:
            properties:
                instances:
                    type: array
                    items:
                      $ref: '#/definitions/assign_instance_post_Instance'
    """
    show_param = request.args.get("show")
    if show_param is None or show_param == "running":  # default option
        return get_json_instances(Instance.get_running())
    else:
        states = ("all", "starting", "deallocated", "allocated", "running", "finished", "error")
        if show_param not in states:
            state_enum = "["
            for s in states:
                state_enum += "%s, " % s
            state_enum = state_enum[:-2] + "]"
            return BadRequest("The 'show' parameter must contain one of the following values: %s." % state_enum)

        if show_param == "all":
            return get_json_instances(Instance.get_all())  # .limit(10)
        elif show_param == "starting":
            return get_json_instances(Instance.get_starting())
        elif show_param == "deallocated":
            return get_json_instances(Instance.get_deallocated())
        elif show_param == "allocated":
            return get_json_instances(Instance.get_allocated())
        elif show_param == "error":
            return get_json_instances(Instance.get_erroneous())
        else:  # show_param is "finished":
            return get_json_instances(Instance.get_finished())
Exemplo n.º 14
0
def create_instance():
    """Runs a new packettracer container in the specified port and
        create associated instance."""
    logger.info('Creating new container.')
    pt_port = allocate_port()
    vnc_port_number = pt_port.number + 10000
    try:
        container_id = start_container(pt_port.number, vnc_port_number)
        logger.info('Container started: %s' % container_id)

        # If success...
        instance = Instance.create(container_id, pt_port.number, vnc_port_number)
        pt_port.assign(instance.id)

        wait_for_ready_container.s(instance.id).delay()
        return instance.id
    except DockerContainerError as e:
        pt_port.release()
        raise e
Exemplo n.º 15
0
def create_instance():
    """Runs a new packettracer container in the specified port and
        create associated instance."""
    logger.info('Creating new container.')
    pt_port = allocate_port()
    vnc_port_number = pt_port.number + 10000
    try:
        container_id = start_container(pt_port.number, vnc_port_number)
        logger.info('Container started: %s' % container_id)

        # If success...
        instance = Instance.create(container_id, pt_port.number,
                                   vnc_port_number)
        pt_port.assign(instance.id)

        wait_for_ready_container.s(instance.id).delay()
        return instance.id
    except DockerContainerError as e:
        pt_port.release()
        raise e
Exemplo n.º 16
0
def deallocate_instance(allocation_id):
    """
    Stops a running Packet Tracer instance.
    ---
    tags:
      - allocation
    parameters:
      - name: allocation_id
        in: path
        type: integer
        description: allocation identifier
        required: true
    responses:
      200:
          description: Allocation removed
          schema:
              $ref: '#/definitions/allocate_instance_post_Allocation'
      404:
          description: There is not an allocation for the given allocation_id.
          schema:
              $ref: '#/definitions/allocate_instance_post_Error'
    """
    instance = Instance.get_by_allocation_id(allocation_id)
    if not instance:
        return not_found(error="The allocation does not exist.")

    try:
        allocation_id = instance.allocated_by
        result = tasks.deallocate_instance.apply_async(args=(instance.id, ))
        result.get()
        allocation = Allocation.get(allocation_id)
        if allocation:
            # TODO update instance object as status has changed
            return jsonify(allocation.serialize(request.base_url, get_host()))
        # else
        return not_found(error="The allocation does not exist.")
    except Exception as e:
        return internal_error(e.args[0])
Exemplo n.º 17
0
def deallocate_instance(allocation_id):
    """
    Stops a running Packet Tracer instance.
    ---
    tags:
      - allocation
    parameters:
      - name: allocation_id
        in: path
        type: integer
        description: allocation identifier
        required: true
    responses:
      200:
          description: Allocation removed
          schema:
              $ref: '#/definitions/allocate_instance_post_Allocation'
      404:
          description: There is not an allocation for the given allocation_id.
          schema:
              $ref: '#/definitions/allocate_instance_post_Error'
    """
    instance = Instance.get_by_allocation_id(allocation_id)
    if not instance:
        return not_found(error="The allocation does not exist.")

    try:
        allocation_id = instance.allocated_by
        result = tasks.deallocate_instance.apply_async(args=(instance.id,))
        result.get()
        allocation = Allocation.get(allocation_id)
        if allocation:
            # TODO update instance object as status has changed
            return jsonify(allocation.serialize(request.base_url, get_host()))
        # else
        return not_found(error="The allocation does not exist.")
    except Exception as e:
        return internal_error(e.args[0])
Exemplo n.º 18
0
def wait_for_ready_container(instance_id, timeout=2):
    """Waits for an instance to be ready (e.g., answer).
        Otherwise, marks it as erroneous ."""
    logger.info('Waiting for container to be ready.')
    instance = Instance.get(instance_id)
    container_running = is_container_running(instance.docker_id)
    if container_running:
        is_running = ptchecker.is_running(app.config['PT_CHECKER'], 'localhost', instance.pt_port, float(timeout))
        if is_running:
            instance.mark_ready()
            if not instance.is_allocated():
                # TODO rename the following task as it sounds confusing.
                # We call it here to pause the instance, not to "deallocate it".
                deallocate_instance.s(instance_id).delay()
        else:
            try:
                raise wait_for_ready_container.retry()
            except MaxRetriesExceededError:
                instance.mark_error()
    else:
        # If the container is not even running, PT won't answer no matter
        # how many times we try...
        instance.mark_error()
    return instance_id