Exemple #1
0
    def post(self, namespace=None, key_name=None, key=None):
        if not namespace:
            return api_base.error(400, 'no namespace specified')

        with db.get_lock('namespace', None, 'all', op='Namespace update'):
            rec = db.get_namespace(namespace)
            if not rec:
                rec = {'name': namespace, 'keys': {}}

            # Allow shortcut of creating key at same time as the namespace
            if key_name:
                if not key:
                    return api_base.error(400, 'no key specified')
                if not isinstance(key, str):
                    # Must be a string to encode()
                    return api_base.error(400, 'key is not a string')
                if key_name == 'service_key':
                    return api_base.error(403, 'illegal key name')

                encoded = str(
                    base64.b64encode(
                        bcrypt.hashpw(key.encode('utf-8'), bcrypt.gensalt())),
                    'utf-8')
                rec['keys'][key_name] = encoded

            # Initialise metadata
            db.persist_metadata('namespace', namespace, {})
            db.persist_namespace(namespace, rec)

        return namespace
Exemple #2
0
    def post(self, namespace=None, key=None):
        if not namespace:
            return api_base.error(400, 'missing namespace in request')
        if not key:
            return api_base.error(400, 'missing key in request')
        if not isinstance(key, str):
            # Must be a string to encode()
            return api_base.error(400, 'key is not a string')

        ns = db.get_namespace(namespace)
        if not ns:
            return api_base.error(401, 'unauthorized')
        service_key = ns.get('service_key')
        if service_key and key == service_key:
            return {
                'access_token':
                create_access_token(identity=[namespace, '_service_key'])
            }

        for key_name in ns.get('keys', {}):
            possible_key = base64.b64decode(ns['keys'][key_name])
            if bcrypt.checkpw(key.encode('utf-8'), possible_key):
                return {
                    'access_token':
                    create_access_token(identity=[namespace, key_name])
                }

        return api_base.error(401, 'unauthorized')
Exemple #3
0
    def get(self, blob_uuid=None, offset=0):
        # Ensure the blob exists
        b = Blob.from_db(blob_uuid)
        if not b:
            return api_base.error(404, 'blob not found')

        # Fast path if we have the blob locally
        os.makedirs(os.path.join(config.STORAGE_PATH, 'blobs'), exist_ok=True)
        blob_path = os.path.join(config.STORAGE_PATH, 'blobs', blob_uuid)
        if os.path.exists(blob_path):
            return flask.Response(flask.stream_with_context(
                _read_file(blob_path, offset)),
                                  mimetype='text/plain',
                                  status=200)

        # Otherwise find a node which has the blob and proxy.
        locations = b.locations
        if not locations:
            return api_base.error(404, 'blob missing')

        random.shuffle(locations)
        return flask.Response(flask.stream_with_context(
            _read_remote(locations[0], blob_uuid, offset=offset)),
                              mimetype='text/plain',
                              status=200)
Exemple #4
0
 def get(self, label_name=None):
     artifacts = list(
         Artifacts(filters=[
             partial(type_filter, Artifact.TYPE_LABEL),
             partial(url_filter, _label_url(label_name)),
             active_states_filter
         ]))
     if len(artifacts) == 0:
         api_base.error(404, 'label %s not found' % label_name)
     return artifacts[0].external_view()
Exemple #5
0
    def delete(self, namespace=None, key=None, value=None):
        if not key:
            return api_base.error(400, 'no key specified')

        with db.get_lock('metadata',
                         'namespace',
                         namespace,
                         op='Metadata delete'):
            md = db.get_metadata('namespace', namespace)
            if md is None or key not in md:
                return api_base.error(404, 'key not found')
            del md[key]
            db.persist_metadata('namespace', namespace, md)
Exemple #6
0
    def post(self, label_name=None, blob_uuid=None, max_versions=0):
        b = Blob.from_db(blob_uuid)
        if not b:
            return api_base.error(404, 'blob not found')
        try:
            b.ref_count_inc()
        except BlobDeleted:
            return api_base.error(400, 'blob has been deleted')

        a = Artifact.from_url(Artifact.TYPE_LABEL, _label_url(label_name),
                              max_versions)
        a.add_index(blob_uuid)
        a.state = dbo.STATE_CREATED
        return a.external_view()
Exemple #7
0
def metadata_putpost(meta_type, owner, key, value):
    if meta_type not in ['namespace', 'instance', 'network']:
        return api_base.error(500, 'invalid meta_type %s' % meta_type)
    if not key:
        return api_base.error(400, 'no key specified')
    if not value:
        return api_base.error(400, 'no value specified')

    with db.get_lock('metadata', meta_type, owner,
                     op='Metadata update'):
        md = db.get_metadata(meta_type, owner)
        if md is None:
            md = {}
        md[key] = value
        db.persist_metadata(meta_type, owner, md)
Exemple #8
0
    def delete(self, label_name=None):
        artifacts = list(
            Artifacts(filters=[
                partial(type_filter, Artifact.TYPE_LABEL),
                partial(url_filter, _label_url(label_name)),
                active_states_filter
            ]))
        if len(artifacts) == 0:
            api_base.error(404, 'label %s not found' % label_name)

        for a in artifacts:
            a.state = dbo.STATE_DELETED
            for blob_index in a.get_all_indexes():
                b = Blob.from_db(blob_index['blob_uuid'])
                b.ref_count_dec()
Exemple #9
0
    def delete(self, artifact_uuid=None, artifact_from_db=None, version_id=0):
        try:
            ver_index = int(version_id)
        except ValueError:
            return api_base.error(400, 'version index is not an integer')

        indexes = list(artifact_from_db.get_all_indexes())
        for idx in indexes:
            if idx['index'] == ver_index:
                artifact_from_db.del_index(idx['index'])
                if len(indexes) == 1:
                    artifact_from_db.state = Artifact.STATE_DELETED
                return

        return api_base.error(404, 'artifact index not found')
Exemple #10
0
    def delete(self, namespace, key_name):
        if not namespace:
            return api_base.error(400, 'no namespace specified')
        if not key_name:
            return api_base.error(400, 'no key name specified')

        with db.get_lock('namespace',
                         None,
                         namespace,
                         op='Namespace key delete'):
            ns = db.get_namespace(namespace)
            if ns.get('keys') and key_name in ns['keys']:
                del ns['keys'][key_name]
            else:
                return api_base.error(404, 'key name not found in namespace')
            db.persist_namespace(namespace, ns)
Exemple #11
0
    def post(self, artifact_name=None, upload_uuid=None, source_url=None):
        u = Upload.from_db(upload_uuid)
        if not u:
            return api_base.error(404, 'upload not found')

        if u.node != config.NODE_NAME:
            url = 'http://%s:%d%s' % (u.node, config.API_PORT,
                                      flask.request.environ['PATH_INFO'])
            api_token = util_general.get_api_token(
                'http://%s:%d' % (u.node, config.API_PORT),
                namespace=get_jwt_identity()[0])
            r = requests.request(
                flask.request.environ['REQUEST_METHOD'],
                url,
                data=json.dumps(api_base.flask_get_post_body()),
                headers={
                    'Authorization': api_token,
                    'User-Agent': util_general.get_user_agent()
                })

            LOG.info('Proxied %s %s returns: %d, %s' %
                     (flask.request.environ['REQUEST_METHOD'], url,
                      r.status_code, r.text))
            resp = flask.Response(r.text, mimetype='application/json')
            resp.status_code = r.status_code
            return resp

        if not source_url:
            source_url = ('%s%s/%s' %
                          (UPLOAD_URL, get_jwt_identity()[0], artifact_name))
        a = Artifact.from_url(Artifact.TYPE_IMAGE, source_url)

        with a.get_lock(ttl=(12 * constants.LOCK_REFRESH_SECONDS),
                        timeout=config.MAX_IMAGE_TRANSFER_SECONDS):
            blob_uuid = str(uuid.uuid4())
            blob_dir = os.path.join(config.STORAGE_PATH, 'blobs')
            blob_path = os.path.join(blob_dir, blob_uuid)

            upload_dir = os.path.join(config.STORAGE_PATH, 'uploads')
            upload_path = os.path.join(upload_dir, u.uuid)

            # NOTE(mikal): we can't use os.rename() here because these paths
            # might be on different filesystems.
            shutil.move(upload_path, blob_path)
            st = os.stat(blob_path)
            b = Blob.new(
                blob_uuid, st.st_size,
                time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()),
                time.time())
            b.state = Blob.STATE_CREATED
            b.ref_count_inc()
            b.observe()
            b.request_replication()

            a.state = Artifact.STATE_CREATED
            a.add_event('upload', None, None, 'success')
            a.add_index(b.uuid)
            a.state = Artifact.STATE_CREATED
            return a.external_view()
Exemple #12
0
 def post(self,
          artifact_uuid=None,
          artifact_from_db=None,
          max_versions=config.ARTIFACT_MAX_VERSIONS_DEFAULT):
     try:
         mv = int(max_versions)
     except ValueError:
         return api_base.error(400, 'max version is not an integer')
     artifact_from_db.max_versions = mv
Exemple #13
0
    def wrapper(*args, **kwargs):
        if 'artifact_uuid' in kwargs:
            kwargs['artifact_from_db'] = Artifact.from_db(
                kwargs['artifact_uuid'])
        if not kwargs.get('artifact_from_db'):
            LOG.with_field('artifact',
                           kwargs['artifact_uuid']).info('Artifact not found')
            return api_base.error(404, 'artifact not found')

        return func(*args, **kwargs)
Exemple #14
0
    def post(self, interface_uuid=None):
        ni, n, err = api_util.safe_get_network_interface(interface_uuid)
        if err:
            return err

        float_net = net.Network.from_db('floating')
        if not float_net:
            return api_base.error(404, 'floating network not found')

        # Address is freed as part of the job, so code is "unbalanced" compared
        # to above for reasons.
        etcd.enqueue('networknode',
                     DefloatNetworkInterfaceTask(n.uuid, interface_uuid))
Exemple #15
0
def assign_floating_ip(ni):
    float_net = net.Network.from_db('floating')
    if not float_net:
        return api_base.error(404, 'floating network not found')

    # Address is allocated and added to the record here, so the job has it later.
    db.add_event('interface', ni.uuid, 'api', 'float', None, None)
    with db.get_lock('ipmanager', None, 'floating', ttl=120, op='Interface float'):
        ipm = IPManager.from_db('floating')
        addr = ipm.get_random_free_address(ni.unique_label())
        ipm.persist()

    ni.floating = addr
Exemple #16
0
def safe_get_network_interface(interface_uuid):
    ni = NetworkInterface.from_db(interface_uuid)
    if not ni:
        return None, None, api_base.error(404, 'interface not found')

    log = LOG.with_fields({'network': ni.network_uuid,
                           'networkinterface': ni.uuid})

    n = net.Network.from_db(ni.network_uuid)
    if not n:
        log.info('Network not found or deleted')
        return None, None, api_base.error(404, 'interface network not found')

    if get_jwt_identity()[0] not in [n.namespace, 'system']:
        log.info('Interface not found, failed ownership test')
        return None, None, api_base.error(404, 'interface not found')

    i = Instance.from_db(ni.instance_uuid)
    if get_jwt_identity()[0] not in [i.namespace, 'system']:
        log.with_object(i).info('Instance not found, failed ownership test')
        return None, None, api_base.error(404, 'interface not found')

    return ni, n, None
Exemple #17
0
def _namespace_keys_putpost(namespace=None, key_name=None, key=None):
    if not namespace:
        return api_base.error(400, 'no namespace specified')
    if not key_name:
        return api_base.error(400, 'no key name specified')
    if not key:
        return api_base.error(400, 'no key specified')
    if key_name == 'service_key':
        return api_base.error(403, 'illegal key name')

    with db.get_lock('namespace', None, 'all', op='Namespace key update'):
        rec = db.get_namespace(namespace)
        if not rec:
            return api_base.error(404, 'namespace does not exist')

        encoded = str(
            base64.b64encode(
                bcrypt.hashpw(key.encode('utf-8'), bcrypt.gensalt())), 'utf-8')
        rec['keys'][key_name] = encoded

        db.persist_namespace(namespace, rec)

    return key_name
Exemple #18
0
    def delete(self, namespace):
        if not namespace:
            return api_base.error(400, 'no namespace specified')
        if namespace == 'system':
            return api_base.error(403,
                                  'you cannot delete the system namespace')

        # The namespace must be empty
        instances = []
        deleted_instances = []
        for i in instance.instances_in_namespace(namespace):
            if i.state.value in [dbo.STATE_DELETED, dbo.STATE_ERROR]:
                deleted_instances.append(i.uuid)
            else:
                LOG.withFields({
                    'instance': i.uuid,
                    'state': i.state
                }).info('Blocks namespace delete')
                instances.append(i.uuid)
        if len(instances) > 0:
            return api_base.error(
                400, 'you cannot delete a namespace with instances')

        networks = []
        for n in net.networks_in_namespace(namespace):
            if not n.is_dead():
                LOG.withFields({
                    'network': n.uuid,
                    'state': n.state
                }).info('Blocks namespace delete')
                networks.append(n.uuid)
        if len(networks) > 0:
            return api_base.error(
                400, 'you cannot delete a namespace with networks')

        db.delete_namespace(namespace)
        db.delete_metadata('namespace', namespace)
Exemple #19
0
    def delete(self, artifact_uuid=None, artifact_from_db=None):
        """Delete an artifact from the cluster

        Artifacts can only be deleted from the system if they are not in use.
        The actual deletion of the on-disk files is left to the cleaner daemon.

        It is acknowledged that there is a potential race condition between the
        check that an artifact is not in use and the marking of the artifact as
        deleted. This is only caused by a user simultaneously deleting an
        artifact and attempting to start a VM using it. It is recommended that
        the user does not do that.
        """
        # TODO(andy): Enforce namespace permissions when snapshots have namespaces
        # TODO(mikal): this should all be refactored to be in the object

        if artifact_from_db.state.value == Artifact.STATE_DELETED:
            # Already deleted, nothing to do.
            return

        # Check for instances using a blob referenced by the artifact.
        blobs = []
        sole_ref_in_use = []
        for blob_index in artifact_from_db.get_all_indexes():
            b = Blob.from_db(blob_index['blob_uuid'])
            if b:
                blobs.append(b)
                if b.ref_count == 1:
                    sole_ref_in_use += b.instances
        if sole_ref_in_use:
            return api_base.error(
                400,
                'Cannot delete last reference to blob in use by instance (%s)'
                % (', '.join(sole_ref_in_use), ))

        artifact_from_db.delete()
        for b in blobs:
            b.ref_count_dec()
Exemple #20
0
    def put(self, namespace=None, key_name=None, key=None):
        rec = db.get_namespace(namespace)
        if key_name not in rec['keys']:
            return api_base.error(404, 'key does not exist')

        return _namespace_keys_putpost(namespace, key_name, key)