示例#1
0
def list_machines(request):
    """Gets machines and their metadata for a backend.

    Because each provider stores metadata in different places several checks
    are needed.

    The folowing are considered:::

        * For tags, Rackspace stores them in extra.metadata.tags while EC2 in
          extra.tags.tags.
        * For images, both EC2 and Rackpace have an image and an etra.imageId
          attribute
        * For flavors, EC2 has an extra.instancetype attribute while Rackspace
          an extra.flavorId. however we also expect to get size attribute.
    """
    try:
        conn = connect(request)
    except RuntimeError as e:
        log.error(e)
        return Response('Internal server error: %s' % e, 503)
    except:
        return Response('Backend not found', 404)

    try:
        machines = conn.list_nodes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for m in machines:
        tags = m.extra.get('tags', None) or m.extra.get('metadata', None)
        tags = tags or {}
        tags = [value for key, value in tags.iteritems() if key != 'Name']

        if m.extra.get('availability', None):
            # for EC2
            tags.append(m.extra['availability'])
        elif m.extra.get('DATACENTERID', None):
            # for Linode
            tags.append(LINODE_DATACENTERS[m.extra['DATACENTERID']])

        image_id = m.image or m.extra.get('imageId', None)

        size = m.size or m.extra.get('flavorId', None)
        size = size or m.extra.get('instancetype', None)

        machine = {'id'            : m.id,
                   'uuid'          : m.get_uuid(),
                   'name'          : m.name,
                   'imageId'       : image_id,
                   'size'          : size,
                   'state'         : STATES[m.state],
                   'private_ips'   : m.private_ips,
                   'public_ips'    : m.public_ips,
                   'tags'          : tags,
                   'extra'         : m.extra,
                  }
        machine.update(get_machine_actions(m, conn))
        ret.append(machine)
    return ret
示例#2
0
def stop_machine(request):
    """Stops a machine on backends that support it.

    Currently only EC2 supports that.

    .. note:: Normally try won't get an AttributeError exception because this
              action is not allowed for machines that don't support it. Check
              helpers.get_machine_actions.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    machine_id = request.matchdict["machine"]
    machine = Node(machine_id, name=machine_id, state=0, public_ips=[], private_ips=[], driver=conn)

    try:
        # In libcloud it is not possible to call this with machine.stop()
        conn.ex_stop_node(machine)
        return Response("Success", 200)
    except AttributeError:
        return Response("Action not supported for this machine", 404)
    except:
        return []
示例#3
0
def destroy_machine(request):
    """Destroys a machine on a certain backend.

    After destroying a machine it also deletes all key associations. However,
    it doesn't undeploy the keypair. There is no need to do it because the
    machine will be destroyed.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    machine_id = request.matchdict["machine"]
    machine = Node(machine_id, name=machine_id, state=0, public_ips=[], private_ips=[], driver=conn)

    machine.destroy()

    backend_id = request.matchdict["backend"]
    pair = [backend_id, machine_id]

    try:
        keypairs = request.environ["beaker.session"]["keypairs"]
    except:
        keypairs = request.registry.settings.get("keypairs", {})

    for key in keypairs:
        machines = keypairs[key].get("machines", None)
        if pair in machines:
            disassociate_key(request, key, backend_id, machine_id, undeploy=False)

    return Response("Success", 200)
示例#4
0
def list_sizes(request):
    """List sizes (aka flavors) from each backend."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        sizes = conn.list_sizes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for size in sizes:
        ret.append({
            'id': size.id,
            'bandwidth': size.bandwidth,
            'disk': size.disk,
            'driver': size.driver.name,
            'name': size.name,
            'price': size.price,
            'ram': size.ram,
        })

    return ret
示例#5
0
def list_locations(request):
    """List locations from each backend.

    Locations mean different things in each backend. e.g. EC2 uses it as a
    datacenter in a given availability zone, whereas Linode lists availability
    zones. However all responses share id, name and country eventhough in some
    cases might be empty, e.g. Openstack.

    In EC2 all locations by a provider have the same name, so the availability
    zones are listed instead of name.
    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        locations = conn.list_locations()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for location in locations:
        if conn.type in EC2_PROVIDERS:
            name = location.availability_zone.name
        else:
            name = location.name

        ret.append({
            'id': location.id,
            'name': name,
            'country': location.country,
        })

    return ret
示例#6
0
def shell_command(request):
    """Send a shell command to a machine over ssh, using fabric."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    backend_id = request.matchdict['backend']
    host = request.params.get('host', None)
    ssh_user = request.params.get('ssh_user', None)
    command = request.params.get('command', None)

    if not ssh_user or ssh_user == 'undefined':
        ssh_user = '******'

    try:
        keypairs = request.environ['beaker.session']['keypairs']
    except:
        keypairs = request.registry.settings.get('keypairs', {})

    keypair = get_keypair(keypairs, backend_id, machine_id)

    if keypair:
        private_key = keypair['private']
        public_key = keypair['public']
    else:
        private_key = public_key = None

    return run_command(conn, machine_id, host, ssh_user, private_key, command)
示例#7
0
def list_images(request):
    """List images from each backend."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        if conn.type in EC2_PROVIDERS:
            images = conn.list_images(None, EC2_IMAGES[conn.type].keys())
            for image in images:
                image.name = EC2_IMAGES[conn.type][image.id]
        else:
            images = conn.list_images()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for image in images:
        ret.append({
            'id': image.id,
            'extra': image.extra,
            'name': image.name,
        })
    return ret
示例#8
0
def list_sizes(request):
    """List sizes (aka flavors) from each backend."""
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    try:
        sizes = conn.list_sizes()
    except:
        return Response("Backend unavailable", 503)

    ret = []
    for size in sizes:
        ret.append(
            {
                "id": size.id,
                "bandwidth": size.bandwidth,
                "disk": size.disk,
                "driver": size.driver.name,
                "name": size.name,
                "price": size.price,
                "ram": size.ram,
            }
        )

    return ret
示例#9
0
def shell_command(request):
    """Sends a shell command to a machine over ssh, using fabric.

    .. note:: Used for uptime only.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    machine_id = request.matchdict["machine"]
    backend_id = request.matchdict["backend"]
    host = request.params.get("host", None)
    ssh_user = request.params.get("ssh_user", None)
    command = request.params.get("command", None)

    if not ssh_user or ssh_user == "undefined":
        ssh_user = "******"

    try:
        keypairs = request.environ["beaker.session"]["keypairs"]
    except:
        keypairs = request.registry.settings.get("keypairs", {})

    keypair = get_keypair(keypairs, backend_id, machine_id)

    if keypair:
        private_key = keypair["private"]
        public_key = keypair["public"]
    else:
        private_key = public_key = None

    ret = run_command(conn, machine_id, host, ssh_user, private_key, command)
    return ret
示例#10
0
def stop_machine(request):
    """Stops a machine on backends that support it.

    Currently only EC2 supports that.

    .. note:: Normally try won't get an AttributeError exception because this
              action is not allowed for machines that don't support it. Check
              helpers.get_machine_actions.
    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)

    try:
        # In libcloud it is not possible to call this with machine.stop()
        conn.ex_stop_node(machine)
        Response('Success', 200)
    except AttributeError:
        return Response('Action not supported for this machine', 404)
    except:
        return []
示例#11
0
def list_locations(request):
    """List locations from each backend.

    Locations mean different things in each backend. e.g. EC2 uses it as a
    datacenter in a given availability zone, whereas Linode lists availability
    zones. However all responses share id, name and country eventhough in some
    cases might be empty, e.g. Openstack.

    In EC2 all locations by a provider have the same name, so the availability
    zones are listed instead of name.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    try:
        locations = conn.list_locations()
    except:
        return Response("Backend unavailable", 503)

    ret = []
    for location in locations:
        if conn.type in EC2_PROVIDERS:
            name = location.availability_zone.name
        else:
            name = location.name

        ret.append({"id": location.id, "name": name, "country": location.country})

    return ret
示例#12
0
文件: views.py 项目: michailb/mist.io
def shell_command(request):
    """Sends a shell command to a machine over ssh, using fabric.

    .. note:: Used for uptime only.

    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    backend_id = request.matchdict['backend']
    host = request.params.get('host', None)
    ssh_user = request.params.get('ssh_user', None)
    command = request.params.get('command', None)

    if not ssh_user or ssh_user == 'undefined':
        ssh_user = '******'

    try:
        keypairs = request.environ['beaker.session']['keypairs']
    except:
        keypairs = request.registry.settings.get('keypairs', {})

    keypair = get_keypair(keypairs, backend_id, machine_id)

    if keypair:
        private_key = keypair['private']
        public_key = keypair['public']
    else:
        private_key = public_key = None

    ret = run_command(conn, machine_id, host, ssh_user, private_key, command)
    return ret
示例#13
0
文件: views.py 项目: michailb/mist.io
def set_machine_metadata(request):
    """Sets metadata for a machine, given the backend and machine id.

    Libcloud handles this differently for each provider. Linode and Rackspace,
    at least the old Rackspace providers, don't support metadata adding.

    machine_id comes as u'...' but the rest are plain strings so use == when
    comparing in ifs. u'f' is 'f' returns false and 'in' is too broad.

    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    if conn.type in [Provider.LINODE, Provider.RACKSPACE_FIRST_GEN]:
        return Response('Adding metadata is not supported in this provider',
                        501)

    machine_id = request.matchdict['machine']

    try:
        tag = request.json_body['tag']
        unique_key = 'mist.io_tag-' + datetime.now().isoformat()
        pair = {unique_key: tag}
    except:
        return Response('Malformed metadata format', 400)

    if conn.type in EC2_PROVIDERS:
        try:
            machine = Node(machine_id,
                           name='',
                           state=0,
                           public_ips=[],
                           private_ips=[],
                           driver=conn)
            conn.ex_create_tags(machine, pair)
        except:
            return Response('Error while creating tag in EC2', 503)
    else:
        try:
            nodes = conn.list_nodes()
            for node in nodes:
                if node.id == machine_id:
                    machine = node
                    break
        except:
            return Response('Machine not found', 404)

        try:
            machine.extra['metadata'].update(pair)
            conn.ex_set_metadata(machine, pair)
        except:
            return Response('Error while creating tag', 503)

    return Response('Success', 200)
示例#14
0
def set_machine_metadata(request):
    """Sets metadata for a machine, given the backend and machine id.

    Libcloud handles this differently for each provider. Linode and Rackspace,
    at least the old Rackspace providers, don't support metadata adding.

    machine_id comes as u'...' but the rest are plain strings so use == when
    comparing in ifs. u'f' is 'f' returns false and 'in' is too broad.
    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    if conn.type in [Provider.LINODE, Provider.RACKSPACE_FIRST_GEN]:
        return Response('Adding metadata is not supported in this provider',
                        501)

    machine_id = request.matchdict['machine']

    try:
        tag = request.json_body['tag']
        unique_key = 'mist.io_tag-' + datetime.now().isoformat()
        pair = {unique_key: tag}
    except:
        return Response('Malformed metadata format', 400)

    if conn.type in EC2_PROVIDERS:
        try:
            machine = Node(machine_id,
                           name='',
                           state=0,
                           public_ips=[],
                           private_ips=[],
                           driver=conn)
            conn.ex_create_tags(machine, pair)
        except:
            return Response('Error while creating tag in EC2', 503)
    else:
        try:
            nodes = conn.list_nodes()
            for node in nodes:
                if node.id == machine_id:
                    machine = node
                    break
        except:
            return Response('Machine not found', 404)

        try:
            machine.extra['metadata'].update(pair)
            conn.ex_set_metadata(machine, pair)
        except:
            return Response('Error while creating tag', 503)

    return Response('Success', 200)
示例#15
0
def reboot_machine(request):
    """Reboots a machine on a certain backend."""
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    machine_id = request.matchdict["machine"]
    machine = Node(machine_id, name=machine_id, state=0, public_ips=[], private_ips=[], driver=conn)

    machine.reboot()

    return Response("Success", 200)
示例#16
0
    def __call__(self, environ, start_response):
        request = Request(environ)
        if request.path.endswith('shell') and request.method == 'GET':
            try:
                backend = self.app.routes_mapper(request)['match']['backend']
                machine = self.app.routes_mapper(request)['match']['machine']
                host = request.params.get('host', None)
                ssh_user = request.params.get('ssh_user', None)
                command = request.params.get('command', None)
                request.registry = self.app.registry

                if not ssh_user or ssh_user == 'undefined':
                    log.debug("Will select root as the ssh-user as we don't know who we are")
                    ssh_user = '******'

                try:
                    keypairs = environ['beaker.session']['keypairs']
                except:
                    keypairs = request.registry.settings.get('keypairs', {})

                preferred_keypairs = get_preferred_keypairs(keypairs, backend, machine)
                log.debug("preferred keypairs = %s" % preferred_keypairs)
              
                if preferred_keypairs:
                    keypair = keypairs[preferred_keypairs[0]]
                    private_key = keypair['private']
                    s_user = get_ssh_user_from_keypair(keypair, backend, machine)
                    log.debug("get user from keypair returned: %s" % s_user)
                    if s_user:
                        ssh_user = s_user
                        log.debug("Will select %s as the ssh-user" % ssh_user)
                else:
                    private_key = None

                conn = connect(request, backend)
                if conn:
                    return self.stream_command(conn, machine, host, ssh_user, 
                                               private_key, command, 
                                               start_response)
                else:
                    raise
            except:
                # leave error handling up to the app
                return self.app(environ, start_response)
        else:
            return self.app(environ, start_response)
示例#17
0
def destroy_machine(request):
    """Destroys a machine on a certain backend."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)

    machine.destroy()

    return Response('Success', 200)
示例#18
0
def destroy_machine(request):
    """Destroys a machine on a certain backend."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    machine_id = request.matchdict['machine']
    machine = Node(machine_id,
                   name=machine_id,
                   state=0,
                   public_ips=[],
                   private_ips=[],
                   driver=conn)

    machine.destroy()

    return Response('Success', 200)
示例#19
0
def list_images(request):
    """List images from each backend."""
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    try:
        if conn.type in EC2_PROVIDERS:
            images = conn.list_images(None, EC2_IMAGES[conn.type].keys())
            for image in images:
                image.name = EC2_IMAGES[conn.type][image.id]
        else:
            images = conn.list_images()
    except:
        return Response("Backend unavailable", 503)

    ret = []
    for image in images:
        ret.append({"id": image.id, "extra": image.extra, "name": image.name})
    return ret
示例#20
0
    def __call__(self, environ, start_response):
        request = Request(environ)
                                    
        if request.path.endswith('shell') and request.method == 'GET':
            try:
                backend = self.app.routes_mapper(request)['match']['backend']
                machine = self.app.routes_mapper(request)['match']['machine']
                host = request.params.get('host', None)
                ssh_user = request.params.get('ssh_user', None)
                command = request.params.get('command', None)
                request.registry = self.app.registry

                if not ssh_user or ssh_user == 'undefined':
                    ssh_user = '******'

                try:
                    keypairs = environ['beaker.session']['keypairs']
                except:
                    keypairs = request.registry.settings.get('keypairs', {})

                keypair = get_keypair(keypairs, backend, machine)
              
                if keypair:
                    private_key = keypair['private']
                else:
                    private_key = None

                conn = connect(request, backend)
                if conn:
                    return self.stream_command(conn, machine, host, ssh_user, 
                                               private_key, command, 
                                               start_response)
                else:
                    raise
            except:
                # leave error handling up to the app
                return self.app(environ, start_response)
        else:
            return self.app(environ, start_response)
示例#21
0
文件: views.py 项目: michailb/mist.io
def list_sizes(request):
    """List sizes (aka flavors) from each backend."""
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    try:
        sizes = conn.list_sizes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for size in sizes:
        ret.append({'id'        : size.id,
                    'bandwidth' : size.bandwidth,
                    'disk'      : size.disk,
                    'driver'    : size.driver.name,
                    'name'      : size.name,
                    'price'     : size.price,
                    'ram'       : size.ram,
                    })

    return ret
示例#22
0
def create_machine(request):
    """Creates a new virtual machine on the specified backend.

    If the backend is Rackspace it attempts to deploy the node with an ssh key
    provided in config. the method used is the only one working in the old
    Rackspace backend. create_node(), from libcloud.compute.base, with 'auth'
    kwarg doesn't do the trick. Didn't test if you can upload some ssh related
    files using the 'ex_files' kwarg from openstack 1.0 driver.

    In Linode creation is a bit different. There you can pass the key file
    directly during creation. The Linode API also requires to set a disk size
    and doesn't get it from size.id. So, send size.disk from the client and
    use it in all cases just to avoid provider checking. Finally, Linode API
    does not support association between a machine and the image it came from.
    We could set this, at least for machines created through mist.io in
    ex_comment, lroot or lconfig. lroot seems more appropriate. However,
    liblcoud doesn't support linode.config.list at the moment, so no way to
    get them. Also, it will create inconsistencies for machines created
    through mist.io and those from the Linode interface.
    """

    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    backend_id = request.matchdict['backend']

    try:
        key_name = request.json_body['key']
    except:
        key_name = None

    try:
        keypairs = request.environ['beaker.session']['keypairs']
    except:
        keypairs = request.registry.settings.get('keypairs', {})

    if key_name:
        keypair = get_keypair_by_name(keypairs, key_name)
    else:
        keypair = get_keypair(keypairs)

    if keypair:
        private_key = keypair['private']
        public_key = keypair['public']
    else:
        private_key = public_key = None

    try:
        machine_name = request.json_body['name']
        location_id = request.json_body['location']
        image_id = request.json_body['image']
        size_id = request.json_body['size']
        #deploy_script received as unicode, but ScriptDeployment wants str
        script = str(request.json_body.get('script', ''))
        # these are required only for Linode, passing them anyway
        image_extra = request.json_body['image_extra']
        disk = request.json_body['disk']
    except Exception as e:
        return Response('Invalid payload', 400)

    size = NodeSize(size_id,
                    name='',
                    ram='',
                    disk=disk,
                    bandwidth='',
                    price='',
                    driver=conn)
    image = NodeImage(image_id, name='', extra=image_extra, driver=conn)

    if conn.type in EC2_PROVIDERS:
        locations = conn.list_locations()
        for loc in locations:
            if loc.id == location_id:
                location = loc
                break
    else:
        location = NodeLocation(location_id, name='', country='', driver=conn)

    if conn.type in [Provider.RACKSPACE_FIRST_GEN, Provider.RACKSPACE] and\
    public_key:
        key = SSHKeyDeployment(str(public_key))
        deploy_script = ScriptDeployment(script)
        msd = MultiStepDeployment([key, deploy_script])
        try:
            node = conn.deploy_node(name=machine_name,
                                    image=image,
                                    size=size,
                                    location=location,
                                    deploy=msd)
            if keypair:
                machines = keypair.get('machines', None)
                if machines and len(machines):
                    keypair['machines'].append([backend_id, node.id])
                else:
                    keypair['machines'] = [
                        [backend_id, node.id],
                    ]
                save_keypairs(request, keypair)
        except Exception as e:
            return Response(
                'Something went wrong with node creation in RackSpace: %s' % e,
                500)
    elif conn.type in EC2_PROVIDERS and public_key:
        imported_key = import_key(conn, public_key, key_name)
        created_security_group = create_security_group(conn, EC2_SECURITYGROUP)
        deploy_script = ScriptDeployment(script)

        (tmp_key, tmp_key_path) = tempfile.mkstemp()
        key_fd = os.fdopen(tmp_key, 'w+b')
        key_fd.write(private_key)
        key_fd.close()
        #deploy_node wants path for ssh private key
        if imported_key and created_security_group:
            try:
                node = conn.deploy_node(
                    name=machine_name,
                    image=image,
                    size=size,
                    deploy=deploy_script,
                    location=location,
                    ssh_key=tmp_key_path,
                    ex_keyname=key_name,
                    ex_securitygroup=EC2_SECURITYGROUP['name'])

                if keypair:
                    machines = keypair.get('machines', None)
                    if machines and len(machines):
                        keypair['machines'].append([backend_id, node.id])
                    else:
                        keypair['machines'] = [
                            [backend_id, node.id],
                        ]
                    save_keypairs(request, keypair)
            except Exception as e:
                return Response(
                    'Something went wrong with node creation in EC2: %s' % e,
                    500)
        #remove temp file with private key
        try:
            os.remove(tmp_key_path)
        except:
            pass
    elif conn.type is Provider.LINODE and public_key:
        auth = NodeAuthSSHKey(public_key)
        deploy_script = ScriptDeployment(script)
        try:
            node = conn.create_node(name=machine_name,
                                    image=image,
                                    size=size,
                                    deploy=deploy_script,
                                    location=location,
                                    auth=auth)
            if keypair:
                machines = keypair.get('machines', None)
                if machines and len(machines):
                    keypair['machines'].append([backend_id, node.id])
                else:
                    keypair['machines'] = [
                        [backend_id, node.id],
                    ]
                save_keypairs(request, keypair)
        except:
            return Response('Something went wrong with Linode creation', 500)

    else:
        try:
            node = conn.create_node(name=machine_name,
                                    image=image,
                                    size=size,
                                    location=location)
        except Exception as e:
            return Response(
                'Something went wrong with generic node creation: %s' % e, 500)

    return {
        'id': node.id,
        'name': node.name,
        'extra': node.extra,
        'public_ips': node.public_ips,
        'private_ips': node.private_ips,
    }
示例#23
0
def delete_machine_metadata(request):
    """Delete metadata for a machine, given the machine id and the tag to be
    deleted.

    Libcloud handles this differently for each provider. Linode and Rackspace,
    at least the old Rackspace providers, don't support metadata updating. In
    EC2 you can delete just the tag you like. In Openstack you can only set a
    new list and not delete from the existing.

    Mist.io client knows only the value of the tag and not it's key so it
    has to loop through the machine list in order to find it.

    Don't forget to check string encoding before using them in ifs.
    u'f' is 'f' returns false.
    """
    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    if conn.type in [Provider.LINODE, Provider.RACKSPACE_FIRST_GEN]:
        return Response('Updating metadata is not supported in this provider',
                        501)

    try:
        tag = request.json_body['tag']
    except:
        return Response('Malformed metadata format', 400)

    machine_id = request.matchdict['machine']

    try:
        nodes = conn.list_nodes()
        for node in nodes:
            if node.id == machine_id:
                machine = node
                break
    except:
        return Response('Machine not found', 404)

    if conn.type in EC2_PROVIDERS:
        tags = machine.extra.get('tags', None)
        try:
            for mkey, mdata in tags.iteritems():
                if tag == mdata:
                    pair = {mkey: tag}
                    break
        except:
            return Response('Tag not found', 404)

        try:
            conn.ex_delete_tags(machine, pair)
        except:
            return Response('Error while deleting metadata in EC2', 503)
    else:
        tags = machine.extra.get('metadata', None)
        try:
            for mkey, mdata in tags.iteritems():
                if tag == mdata:
                    tags.pop(mkey)
                    break
        except:
            return Response('Tag not found', 404)

        try:
            conn.ex_set_metadata(machine, tags)
        except:
            return Response('Error while updating metadata', 503)

    return Response('Success', 200)
示例#24
0
def delete_machine_metadata(request):
    """Deletes metadata for a machine, given the machine id and the tag to be
    deleted.

    Libcloud handles this differently for each provider. Linode and Rackspace,
    at least the old Rackspace providers, don't support metadata updating. In
    EC2 you can delete just the tag you like. In Openstack you can only set a
    new list and not delete from the existing.

    Mist.io client knows only the value of the tag and not it's key so it
    has to loop through the machine list in order to find it.

    Don't forget to check string encoding before using them in ifs.
    u'f' is 'f' returns false.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    if conn.type in [Provider.LINODE, Provider.RACKSPACE_FIRST_GEN]:
        return Response("Updating metadata is not supported in this provider", 501)

    try:
        tag = request.json_body["tag"]
    except:
        return Response("Malformed metadata format", 400)

    machine_id = request.matchdict["machine"]

    try:
        nodes = conn.list_nodes()
        for node in nodes:
            if node.id == machine_id:
                machine = node
                break
    except:
        return Response("Machine not found", 404)

    if conn.type in EC2_PROVIDERS:
        tags = machine.extra.get("tags", None)
        try:
            for mkey, mdata in tags.iteritems():
                if tag == mdata:
                    pair = {mkey: tag}
                    break
        except:
            return Response("Tag not found", 404)

        try:
            conn.ex_delete_tags(machine, pair)
        except:
            return Response("Error while deleting metadata in EC2", 503)
    else:
        tags = machine.extra.get("metadata", None)
        try:
            for mkey, mdata in tags.iteritems():
                if tag == mdata:
                    tags.pop(mkey)
                    break
        except:
            return Response("Tag not found", 404)

        try:
            conn.ex_set_metadata(machine, tags)
        except:
            return Response("Error while updating metadata", 503)

    return Response("Success", 200)
示例#25
0
def create_machine(request):
    """Creates a new virtual machine on the specified backend.

    If the backend is Rackspace it attempts to deploy the node with an ssh key
    provided in config. the method used is the only one working in the old
    Rackspace backend. create_node(), from libcloud.compute.base, with 'auth'
    kwarg doesn't do the trick. Didn't test if you can upload some ssh related
    files using the 'ex_files' kwarg from openstack 1.0 driver.

    In Linode creation is a bit different. There you can pass the key file
    directly during creation. The Linode API also requires to set a disk size
    and doesn't get it from size.id. So, send size.disk from the client and
    use it in all cases just to avoid provider checking. Finally, Linode API
    does not support association between a machine and the image it came from.
    We could set this, at least for machines created through mist.io in
    ex_comment, lroot or lconfig. lroot seems more appropriate. However,
    liblcoud doesn't support linode.config.list at the moment, so no way to
    get them. Also, it will create inconsistencies for machines created
    through mist.io and those from the Linode interface.
    """

    try:
        conn = connect(request)
    except:
        return Response('Backend not found', 404)

    backend_id = request.matchdict['backend']

    try:
        key_name = request.json_body['key']
    except:
        key_name = None
    
    try:
        keypairs = request.environ['beaker.session']['keypairs']
    except:
        keypairs = request.registry.settings.get('keypairs', {})

    if key_name:
        keypair = get_keypair_by_name(keypairs, key_name)
    else:
        keypair = get_keypair(keypairs)      

    if keypair:
        private_key = keypair['private']
        public_key = keypair['public']
    else:
        private_key = public_key = None

    try:
        machine_name = request.json_body['name']
        location_id = request.json_body['location']
        image_id = request.json_body['image']
        size_id = request.json_body['size']
        #deploy_script received as unicode, but ScriptDeployment wants str
        script = str(request.json_body.get('script', ''))
        # these are required only for Linode, passing them anyway
        image_extra = request.json_body['image_extra']
        disk = request.json_body['disk']
    except Exception as e:
        return Response('Invalid payload', 400)

    size = NodeSize(size_id, name='', ram='', disk=disk, bandwidth='',
                    price='', driver=conn)
    image = NodeImage(image_id, name='', extra=image_extra, driver=conn)

    if conn.type in EC2_PROVIDERS:
        locations = conn.list_locations()
        for loc in locations:
            if loc.id == location_id:
                location = loc
                break
    else:
        location = NodeLocation(location_id, name='', country='', driver=conn)
    
    if conn.type in [Provider.RACKSPACE_FIRST_GEN, Provider.RACKSPACE] and\
    public_key:
        key = SSHKeyDeployment(str(public_key))
        deploy_script = ScriptDeployment(script)
        msd = MultiStepDeployment([key, deploy_script])        
        try:
            node = conn.deploy_node(name=machine_name,
                             image=image,
                             size=size,
                             location=location,
                             deploy=msd)
            if keypair:
                machines = keypair.get('machines', None)
                if machines and len(machines):
                    keypair['machines'].append([backend_id, node.id])
                else:
                    keypair['machines'] = [[backend_id, node.id],]
                save_keypairs(request, keypair)
        except Exception as e:
            return Response('Something went wrong with node creation in RackSpace: %s' % e, 500)
    elif conn.type in EC2_PROVIDERS and public_key:
        imported_key = import_key(conn, public_key, key_name)
        created_security_group = create_security_group(conn, EC2_SECURITYGROUP)
        deploy_script = ScriptDeployment(script)

        (tmp_key, tmp_key_path) = tempfile.mkstemp()
        key_fd = os.fdopen(tmp_key, 'w+b')
        key_fd.write(private_key)
        key_fd.close()
        #deploy_node wants path for ssh private key
        if imported_key and created_security_group:
            try:
                node = conn.deploy_node(name=machine_name,
                                 image=image,
                                 size=size,
                                 deploy=deploy_script,
                                 location=location,
                                 ssh_key=tmp_key_path,
                                 ex_keyname=key_name,
                                 ex_securitygroup=EC2_SECURITYGROUP['name'])

                if keypair:
                    machines = keypair.get('machines', None)
                    if machines and len(machines):
                        keypair['machines'].append([backend_id, node.id])
                    else:
                        keypair['machines'] = [[backend_id, node.id],]
                    save_keypairs(request, keypair)
            except Exception as e:
                return Response('Something went wrong with node creation in EC2: %s' % e, 500)
        #remove temp file with private key
        try:
            os.remove(tmp_key_path)
        except:
            pass
    elif conn.type is Provider.LINODE and public_key:
        auth = NodeAuthSSHKey(public_key)
        deploy_script = ScriptDeployment(script)
        try:
            node = conn.create_node(name=machine_name,
                             image=image,
                             size=size,
                             deploy=deploy_script,
                             location=location,
                             auth=auth)
            if keypair:
                machines = keypair.get('machines', None)
                if machines and len(machines):
                    keypair['machines'].append([backend_id, node.id])
                else:
                    keypair['machines'] = [[backend_id, node.id],]
                save_keypairs(request, keypair)
        except:
            return Response('Something went wrong with Linode creation', 500)

    else:
        try:
            node = conn.create_node(name=machine_name,
                             image=image,
                             size=size,
                             location=location)
        except Exception as e:
            return Response('Something went wrong with generic node creation: %s' % e, 500)

    return {'id': node.id,
            'name': node.name,
            'extra': node.extra,
            'public_ips': node.public_ips,
            'private_ips': node.private_ips,
            }
示例#26
0
def list_machines(request):
    """Gets machines and their metadata for a backend.

    Because each provider stores metadata in different places several checks
    are needed.

    The folowing are considered:::

        * For tags, Rackspace stores them in extra.metadata.tags while EC2 in
          extra.tags.tags.
        * For images, both EC2 and Rackpace have an image and an etra.imageId
          attribute
        * For flavors, EC2 has an extra.instancetype attribute while Rackspace
          an extra.flavorId. however we also expect to get size attribute.
    """
    try:
        conn = connect(request)
    except RuntimeError as e:
        log.error(e)
        return Response('Internal server error: %s' % e, 503)
    except:
        return Response('Backend not found', 404)

    try:
        machines = conn.list_nodes()
    except:
        return Response('Backend unavailable', 503)

    ret = []
    for m in machines:
        tags = m.extra.get('tags', None) or m.extra.get('metadata', None)
        tags = tags or {}
        tags = [value for key, value in tags.iteritems() if key != 'Name']

        if m.extra.get('availability', None):
            # for EC2
            tags.append(m.extra['availability'])
        elif m.extra.get('DATACENTERID', None):
            # for Linode
            tags.append(LINODE_DATACENTERS[m.extra['DATACENTERID']])

        image_id = m.image or m.extra.get('imageId', None)

        size = m.size or m.extra.get('flavorId', None)
        size = size or m.extra.get('instancetype', None)

        machine = {
            'id': m.id,
            'uuid': m.get_uuid(),
            'name': m.name,
            'imageId': image_id,
            'size': size,
            'state': STATES[m.state],
            'private_ips': m.private_ips,
            'public_ips': m.public_ips,
            'tags': tags,
            'extra': m.extra,
        }
        machine.update(get_machine_actions(m, conn))
        ret.append(machine)
    return ret
示例#27
0
def create_machine(request):
    """Creates a new virtual machine on the specified backend.

    If the backend is Rackspace it attempts to deploy the node with an ssh key
    provided in config. the method used is the only one working in the old
    Rackspace backend. create_node(), from libcloud.compute.base, with 'auth'
    kwarg doesn't do the trick. Didn't test if you can upload some ssh related
    files using the 'ex_files' kwarg from openstack 1.0 driver.

    In Linode creation is a bit different. There you can pass the key file
    directly during creation. The Linode API also requires to set a disk size
    and doesn't get it from size.id. So, send size.disk from the client and
    use it in all cases just to avoid provider checking. Finally, Linode API
    does not support association between a machine and the image it came from.
    We could set this, at least for machines created through mist.io in
    ex_comment, lroot or lconfig. lroot seems more appropriate. However,
    liblcoud doesn't support linode.config.list at the moment, so no way to
    get them. Also, it will create inconsistencies for machines created
    through mist.io and those from the Linode interface.

    """
    try:
        conn = connect(request)
    except:
        return Response("Backend not found", 404)

    backend_id = request.matchdict["backend"]

    try:
        key_id = request.json_body["key"]
    except:
        key_id = None

    try:
        keypairs = request.environ["beaker.session"]["keypairs"]
    except:
        keypairs = request.registry.settings.get("keypairs", {})

    if key_id:
        keypair = get_keypair_by_name(keypairs, key_id)
    else:
        keypair = get_keypair(keypairs)

    if keypair:
        private_key = keypair["private"]
        public_key = keypair["public"]
    else:
        private_key = public_key = None

    try:
        machine_name = request.json_body["name"]
        location_id = request.json_body["location"]
        image_id = request.json_body["image"]
        size_id = request.json_body["size"]
        # deploy_script received as unicode, but ScriptDeployment wants str
        script = str(request.json_body.get("script", ""))
        # these are required only for Linode, passing them anyway
        image_extra = request.json_body["image_extra"]
        disk = request.json_body["disk"]
    except Exception as e:
        return Response("Invalid payload", 400)

    size = NodeSize(size_id, name="", ram="", disk=disk, bandwidth="", price="", driver=conn)
    image = NodeImage(image_id, name="", extra=image_extra, driver=conn)

    if conn.type in EC2_PROVIDERS:
        locations = conn.list_locations()
        for loc in locations:
            if loc.id == location_id:
                location = loc
                break
    else:
        location = NodeLocation(location_id, name="", country="", driver=conn)

    if conn.type in [Provider.RACKSPACE_FIRST_GEN, Provider.RACKSPACE] and public_key:
        key = SSHKeyDeployment(str(public_key))
        deploy_script = ScriptDeployment(script)
        msd = MultiStepDeployment([key, deploy_script])
        try:
            node = conn.deploy_node(name=machine_name, image=image, size=size, location=location, deploy=msd)
            associate_key(request, key_id, backend_id, node.id, deploy=False)
        except Exception as e:
            return Response("Failed to create machine in Rackspace: %s" % e, 500)
    elif conn.type in EC2_PROVIDERS and public_key and private_key:
        imported_key = import_key(conn, public_key, key_id)
        created_security_group = create_security_group(conn, EC2_SECURITYGROUP)
        deploy_script = ScriptDeployment(script)

        (tmp_key, tmp_key_path) = tempfile.mkstemp()
        key_fd = os.fdopen(tmp_key, "w+b")
        key_fd.write(private_key)
        key_fd.close()
        # deploy_node wants path for ssh private key
        if imported_key and created_security_group:
            try:
                node = conn.deploy_node(
                    name=machine_name,
                    image=image,
                    size=size,
                    deploy=deploy_script,
                    location=location,
                    ssh_key=tmp_key_path,
                    ssh_alternate_usernames=["ec2-user", "ubuntu"],
                    max_tries=1,
                    ex_keyname=key_id,
                    ex_securitygroup=EC2_SECURITYGROUP["name"],
                )
                associate_key(request, key_id, backend_id, node.id, deploy=False)
            except Exception as e:
                return Response("Failed to create machine in EC2: %s" % e, 500)
        # remove temp file with private key
        try:
            os.remove(tmp_key_path)
        except:
            pass
    elif conn.type is Provider.LINODE and public_key and private_key:
        auth = NodeAuthSSHKey(public_key)

        (tmp_key, tmp_key_path) = tempfile.mkstemp()
        key_fd = os.fdopen(tmp_key, "w+b")
        key_fd.write(private_key)
        key_fd.close()

        deploy_script = ScriptDeployment(script)
        try:
            node = conn.deploy_node(
                name=machine_name,
                image=image,
                size=size,
                deploy=deploy_script,
                location=location,
                auth=auth,
                ssh_key=tmp_key_path,
            )
            associate_key(request, key_id, backend_id, node.id, deploy=False)
        except Exception as e:
            return Response("Failed to create machine in Linode: %s" % e, 500)
        # remove temp file with private key
        try:
            os.remove(tmp_key_path)
        except:
            pass
    else:
        return Response("Cannot create a machine without a keypair", 400)

    return {
        "id": node.id,
        "name": node.name,
        "extra": node.extra,
        "public_ips": node.public_ips,
        "private_ips": node.private_ips,
    }
示例#28
0
def list_machines(request):
    """Gets machines and their metadata from a backend.

    Several checks are needed, because each backend stores metadata
    differently.

    The folowing are considered:::

        * For tags, Rackspace stores them in extra.metadata.tags while EC2 in
          extra.tags.tags.
        * For images, both EC2 and Rackpace have an image and an etra.imageId
          attribute
        * For flavors, EC2 has an extra.instancetype attribute while Rackspace
          an extra.flavorId. however we also expect to get size attribute.

    """
    try:
        conn = connect(request)
    except RuntimeError as e:
        log.error(e)
        return Response("Internal server error: %s" % e, 503)
    except:
        return Response("Backend not found", 404)

    try:
        machines = conn.list_nodes()
    except InvalidCredsError:
        return Response("Invalid credentials", 401)
    except:
        return Response("Backend unavailable", 503)

    ret = []
    for m in machines:
        tags = m.extra.get("tags", None) or m.extra.get("metadata", None)
        tags = tags or {}
        tags = [value for key, value in tags.iteritems() if key != "Name"]

        if m.extra.get("availability", None):
            # for EC2
            tags.append(m.extra["availability"])
        elif m.extra.get("DATACENTERID", None):
            # for Linode
            tags.append(LINODE_DATACENTERS[m.extra["DATACENTERID"]])

        image_id = m.image or m.extra.get("imageId", None)

        size = m.size or m.extra.get("flavorId", None)
        size = size or m.extra.get("instancetype", None)

        machine = {
            "id": m.id,
            "uuid": m.get_uuid(),
            "name": m.name,
            "imageId": image_id,
            "size": size,
            "state": STATES[m.state],
            "private_ips": m.private_ips,
            "public_ips": m.public_ips,
            "tags": tags,
            "extra": m.extra,
        }
        machine.update(get_machine_actions(m, conn))
        ret.append(machine)
    return ret