async def POST_Datatype(request): """HTTP method to create new committed datatype object""" log.request(request) app = request.app username, pswd = getUserPasswordFromRequest(request) # write actions need auth await validateUserPassword(app, username, pswd) if not request.has_body: msg = "POST Datatype with no body" log.warn(msg) raise HTTPBadRequest(reason=msg) body = await request.json() if "type" not in body: msg = "POST Datatype has no type key in body" log.warn(msg) raise HTTPBadRequest(reason=msg) datatype = body["type"] if isinstance(datatype, str): try: # convert predefined type string (e.g. "H5T_STD_I32LE") to # corresponding json representation datatype = getBaseTypeJson(datatype) log.debug(f"got datatype: {datatype}") except TypeError: msg = "POST Dataset with invalid predefined type" log.warn(msg) raise HTTPBadRequest(reason=msg) validateTypeItem(datatype) domain = getDomainFromRequest(request) if not isValidDomain(domain): msg = f"Invalid domain: {domain}" log.warn(msg) raise HTTPBadRequest(reason=msg) bucket = getBucketForDomain(domain) domain_json = await getDomainJson(app, domain, reload=True) aclCheck(domain_json, "create", username) # throws exception if not allowed if "root" not in domain_json: msg = f"Expected root key for domain: {domain}" log.warn(msg) raise HTTPBadRequest(reason=msg) link_id = None link_title = None if "link" in body: link_body = body["link"] if "id" in link_body: link_id = link_body["id"] if "name" in link_body: link_title = link_body["name"] if link_id and link_title: log.debug(f"link id: {link_id}") # verify that the referenced id exists and is in this domain # and that the requestor has permissions to create a link await validateAction(app, domain, link_id, username, "create") root_id = domain_json["root"] ctype_id = createObjId("datatypes", rootid=root_id) log.debug(f"new type id: {ctype_id}") ctype_json = {"id": ctype_id, "root": root_id, "type": datatype} log.debug("create named type, body: " + json.dumps(ctype_json)) req = getDataNodeUrl(app, ctype_id) + "/datatypes" params = {} if bucket: params["bucket"] = bucket type_json = await http_post(app, req, data=ctype_json, params=params) # create link if requested if link_id and link_title: link_json = {} link_json["id"] = ctype_id link_json["class"] = "H5L_TYPE_HARD" link_req = getDataNodeUrl(app, link_id) link_req += "/groups/" + link_id + "/links/" + link_title log.debug("PUT link - : " + link_req) put_rsp = await http_put(app, link_req, data=link_json, params=params) log.debug(f"PUT Link resp: {put_rsp}") # datatype creation successful resp = await jsonResponse(request, type_json, status=201) log.response(request, resp=resp) return resp
async def POST_Dataset(request): """HTTP method to create a new dataset object""" log.request(request) app = request.app username, pswd = getUserPasswordFromRequest(request) # write actions need auth await validateUserPassword(app, username, pswd) if not request.has_body: msg = "POST Datasets with no body" log.warn(msg) raise HTTPBadRequest(reason=msg) body = await request.json() # get domain, check authorization domain = getDomainFromRequest(request) if not isValidDomain(domain): msg = "Invalid host value: {}".format(domain) log.warn(msg) raise HTTPBadRequest(reason=msg) domain_json = await getDomainJson(app, domain, reload=True) root_id = domain_json["root"] aclCheck(domain_json, "create", username) # throws exception if not allowed if "root" not in domain_json: msg = "Expected root key for domain: {}".format(domain) log.warn(msg) raise HTTPBadRequest(reason=msg) # # validate type input # if "type" not in body: msg = "POST Dataset has no type key in body" log.warn(msg) raise HTTPBadRequest(reason=msg) datatype = body["type"] if isinstance(datatype, str) and datatype.startswith("t-"): # Committed type - fetch type json from DN ctype_id = datatype log.debug("got ctypeid: {}".format(ctype_id)) ctype_json = await getObjectJson(app, ctype_id) log.debug("ctype: {}".format(ctype_json)) if ctype_json["root"] != root_id: msg = "Referenced committed datatype must belong in same domain" log.warn(msg) raise HTTPBadRequest(reason=msg) datatype = ctype_json["type"] # add the ctype_id to type type datatype["id"] = ctype_id elif isinstance(datatype, str): try: # convert predefined type string (e.g. "H5T_STD_I32LE") to # corresponding json representation datatype = getBaseTypeJson(datatype) log.debug("got datatype: {}".format(datatype)) except TypeError: msg = "POST Dataset with invalid predefined type" log.warn(msg) raise HTTPBadRequest(reason=msg) validateTypeItem(datatype) item_size = getItemSize(datatype) # # Validate shape input # dims = None shape_json = {} if "shape" not in body: shape_json["class"] = "H5S_SCALAR" else: shape = body["shape"] if isinstance(shape, int): shape_json["class"] = "H5S_SIMPLE" dims = [ shape, ] shape_json["dims"] = dims elif isinstance(shape, str): # only valid string value is H5S_NULL if shape != "H5S_NULL": msg = "POST Datset with invalid shape value" log.warn(msg) raise HTTPBadRequest(reason=msg) shape_json["class"] = "H5S_NULL" elif isinstance(shape, list): if len(shape) == 0: shape_json["class"] = "H5S_SCALAR" else: shape_json["class"] = "H5S_SIMPLE" shape_json["dims"] = shape dims = shape else: msg = "Bad Request: shape is invalid" log.warn(msg) raise HTTPBadRequest(reason=msg) if dims is not None: for i in range(len(dims)): extent = dims[i] if not isinstance(extent, int): msg = "Invalid shape type" log.warn(msg) raise HTTPBadRequest(reason=msg) if extent < 0: msg = "shape dimension is negative" log.warn(msg) raise HTTPBadRequest(reason=msg) maxdims = None if "maxdims" in body: if dims is None: msg = "Maxdims cannot be supplied if space is NULL" log.warn(msg) raise HTTPBadRequest(reason=msg) maxdims = body["maxdims"] if isinstance(maxdims, int): dim1 = maxdims maxdims = [dim1] elif isinstance(maxdims, list): pass # can use as is else: msg = "Bad Request: maxdims is invalid" log.warn(msg) raise HTTPBadRequest(reason=msg) if len(dims) != len(maxdims): msg = "Maxdims rank doesn't match Shape" log.warn(msg) raise HTTPBadRequest(reason=msg) if maxdims is not None: for extent in maxdims: if not isinstance(extent, int): msg = "Invalid maxdims type" log.warn(msg) raise HTTPBadRequest(reason=msg) if extent < 0: msg = "maxdims dimension is negative" log.warn(msg) raise HTTPBadRequest(reason=msg) if len(maxdims) != len(dims): msg = "Bad Request: maxdims array length must equal shape array length" log.warn(msg) raise HTTPBadRequest(reason=msg) shape_json["maxdims"] = [] for i in range(len(dims)): maxextent = maxdims[i] if not isinstance(maxextent, int): msg = "Bad Request: maxdims must be integer type" log.warn(msg) raise HTTPBadRequest(reason=msg) elif maxextent == 0: # unlimited dimension shape_json["maxdims"].append(0) elif maxextent < dims[i]: msg = "Bad Request: maxdims extent can't be smaller than shape extent" log.warn(msg) raise HTTPBadRequest(reason=msg) else: shape_json["maxdims"].append(maxextent) layout = None min_chunk_size = int(config.get("min_chunk_size")) max_chunk_size = int(config.get("max_chunk_size")) if 'creationProperties' in body: creationProperties = body["creationProperties"] if 'layout' in creationProperties: layout = creationProperties["layout"] validateChunkLayout(shape_json, item_size, layout) if layout is None and shape_json["class"] != "H5S_NULL": # default to chunked layout layout = {"class": "H5D_CHUNKED"} if layout and layout["class"] == 'H5D_CONTIGUOUS_REF': chunk_dims = getContiguousLayout(shape_json, item_size, chunk_min=min_chunk_size, chunk_max=max_chunk_size) layout["dims"] = chunk_dims log.debug(f"autoContiguous layout: {layout}") if layout and layout["class"] == 'H5D_CHUNKED' and "dims" not in layout: # do autochunking chunk_dims = guessChunk(shape_json, item_size) layout["dims"] = chunk_dims log.debug(f"initial autochunk layout: {layout}") if layout and layout["class"] == 'H5D_CHUNKED': chunk_dims = layout["dims"] chunk_size = getChunkSize(chunk_dims, item_size) log.debug("chunk_size: {}, min: {}, max: {}".format( chunk_size, min_chunk_size, max_chunk_size)) # adjust the chunk shape if chunk size is too small or too big adjusted_chunk_dims = None if chunk_size < min_chunk_size: log.debug( "chunk size: {} less than min size: {}, expanding".format( chunk_size, min_chunk_size)) adjusted_chunk_dims = expandChunk(chunk_dims, item_size, shape_json, chunk_min=min_chunk_size, layout_class=layout["class"]) elif chunk_size > max_chunk_size: log.debug( "chunk size: {} greater than max size: {}, expanding".format( chunk_size, max_chunk_size, layout_class=layout["class"])) adjusted_chunk_dims = shrinkChunk(chunk_dims, item_size, chunk_max=max_chunk_size) if adjusted_chunk_dims: log.debug( f"requested chunk_dimensions: {chunk_dims} modified dimensions: {adjusted_chunk_dims}" ) layout["dims"] = adjusted_chunk_dims if layout and layout["class"] in ('H5D_CHUNKED_REF', 'H5D_CHUNKED_REF_INDIRECT'): chunk_dims = layout["dims"] chunk_size = getChunkSize(chunk_dims, item_size) log.debug("chunk_size: {}, min: {}, max: {}".format( chunk_size, min_chunk_size, max_chunk_size)) # adjust the chunk shape if chunk size is too small or too big if chunk_size < min_chunk_size: log.warn( "chunk size: {} less than min size: {} for H5D_CHUNKED_REF dataset" .format(chunk_size, min_chunk_size)) elif chunk_size > max_chunk_size: log.warn( "chunk size: {} greater than max size: {}, for H5D_CHUNKED_REF dataset" .format(chunk_size, max_chunk_size, layout_class=layout["class"])) link_id = None link_title = None if "link" in body: link_body = body["link"] if "id" in link_body: link_id = link_body["id"] if "name" in link_body: link_title = link_body["name"] if link_id and link_title: log.info("link id: {}".format(link_id)) # verify that the referenced id exists and is in this domain # and that the requestor has permissions to create a link await validateAction(app, domain, link_id, username, "create") dset_id = createObjId("datasets", rootid=root_id) log.info("new dataset id: {}".format(dset_id)) dataset_json = { "id": dset_id, "root": root_id, "type": datatype, "shape": shape_json } if "creationProperties" in body: # TBD - validate all creationProperties creationProperties = body["creationProperties"] if "fillValue" in creationProperties: # validate fill value compatible with type dt = createDataType(datatype) fill_value = creationProperties["fillValue"] if isinstance(fill_value, list): fill_value = tuple(fill_value) try: np.asarray(fill_value, dtype=dt) except (TypeError, ValueError): msg = "Fill value {} not compatible with dataset type: {}".format( fill_value, datatype) log.warn(msg) raise HTTPBadRequest(reason=msg) dataset_json["creationProperties"] = creationProperties if layout is not None: dataset_json["layout"] = layout log.debug("create dataset: " + json.dumps(dataset_json)) req = getDataNodeUrl(app, dset_id) + "/datasets" post_json = await http_post(app, req, data=dataset_json) # create link if requested if link_id and link_title: link_json = {} link_json["id"] = dset_id link_json["class"] = "H5L_TYPE_HARD" link_req = getDataNodeUrl(app, link_id) link_req += "/groups/" + link_id + "/links/" + link_title log.info("PUT link - : " + link_req) put_rsp = await http_put(app, link_req, data=link_json) log.debug("PUT Link resp: {}".format(put_rsp)) # dataset creation successful resp = await jsonResponse(request, post_json, status=201) log.response(request, resp=resp) return resp
async def PUT_Attribute(request): """HTTP method to create a new attribute""" log.request(request) app = request.app collection = getRequestCollectionName(request) # returns datasets|groups|datatypes obj_id = request.match_info.get('id') if not obj_id: msg = "Missing object id" log.warn(msg) raise HTTPBadRequest(reason=msg) if not isValidUuid(obj_id, obj_class=collection): msg = f"Invalid object id: {obj_id}" log.warn(msg) raise HTTPBadRequest(reason=msg) attr_name = request.match_info.get('name') log.debug(f"Attribute name: [{attr_name}]") validateAttributeName(attr_name) log.info(f"PUT Attribute id: {obj_id} name: {attr_name}") username, pswd = getUserPasswordFromRequest(request) # write actions need auth await validateUserPassword(app, username, pswd) if not request.has_body: msg = "PUT Attribute with no body" log.warn(msg) raise HTTPBadRequest(reason=msg) body = await request.json() domain = getDomainFromRequest(request) if not isValidDomain(domain): msg = f"Invalid domain: {domain}" log.warn(msg) raise HTTPBadRequest(reason=msg) bucket = getBucketForDomain(domain) # get domain JSON domain_json = await getDomainJson(app, domain) if "root" not in domain_json: log.error(f"Expected root key for domain: {domain}") raise HTTPBadRequest(reason="Unexpected Error") root_id = domain_json["root"] # TBD - verify that the obj_id belongs to the given domain await validateAction(app, domain, obj_id, username, "create") if "type" not in body: msg = "PUT attribute with no type in body" log.warn(msg) raise HTTPBadRequest(reason=msg) datatype = body["type"] if isinstance(datatype, str) and datatype.startswith("t-"): # Committed type - fetch type json from DN ctype_id = datatype log.debug(f"got ctypeid: {ctype_id}") ctype_json = await getObjectJson(app, ctype_id, bucket=bucket) log.debug(f"ctype {ctype_id}: {ctype_json}") if ctype_json["root"] != root_id: msg = "Referenced committed datatype must belong in same domain" log.warn(msg) raise HTTPBadRequest(reason=msg) datatype = ctype_json["type"] # add the ctype_id to type type datatype["id"] = ctype_id elif isinstance(datatype, str): try: # convert predefined type string (e.g. "H5T_STD_I32LE") to # corresponding json representation datatype = getBaseTypeJson(datatype) log.debug(f"got datatype: {datatype}") except TypeError: msg = "PUT attribute with invalid predefined type" log.warn(msg) raise HTTPBadRequest(reason=msg) validateTypeItem(datatype) dims = None shape_json = {} if "shape" in body: shape_body = body["shape"] shape_class = None if isinstance(shape_body, dict) and "class" in shape_body: shape_class = shape_body["class"] elif isinstance(shape_body, str): shape_class = shape_body if shape_class: if shape_class == "H5S_NULL": shape_json["class"] = "H5S_NULL" if isinstance(shape_body, dict) and "dims" in shape_body: msg = "can't include dims with null shape" log.warn(msg) raise HTTPBadRequest(reason=msg) if isinstance(shape_body, dict) and "value" in body: msg = "can't have H5S_NULL shape with value" log.warn(msg) raise HTTPBadRequest(reason=msg) elif shape_class == "H5S_SCALAR": shape_json["class"] = "H5S_SCALAR" dims = getShapeDims(shape_body) if len(dims) != 1 or dims[0] != 1: msg = "dimensions aren't valid for scalar attribute" log.warn(msg) raise HTTPBadRequest(reason=msg) elif shape_class == "H5S_SIMPLE": shape_json["class"] = "H5S_SIMPLE" dims = getShapeDims(shape_body) shape_json["dims"] = dims else: msg = f"Unknown shape class: {shape_class}" log.warn(msg) raise HTTPBadRequest(reason=msg) else: # no class, interpet shape value as dimensions and # use H5S_SIMPLE as class if isinstance(shape_body, list) and len(shape_body) == 0: shape_json["class"] = "H5S_SCALAR" dims = [1,] else: shape_json["class"] = "H5S_SIMPLE" dims = getShapeDims(shape_body) shape_json["dims"] = dims else: shape_json["class"] = "H5S_SCALAR" dims = [1,] if "value" in body: if dims is None: msg = "Bad Request: data can not be included with H5S_NULL space" log.warn(msg) raise HTTPBadRequest(reason=msg) value = body["value"] # validate that the value agrees with type/shape arr_dtype = createDataType(datatype) # np datatype if len(dims) == 0: np_dims = [1,] else: np_dims = dims log.debug(f"attribute dims: {np_dims}") log.debug(f"attribute value: {value}") try: arr = jsonToArray(np_dims, arr_dtype, value) except ValueError: msg = "Bad Request: input data doesn't match selection" log.warn(msg) raise HTTPBadRequest(reason=msg) log.info(f"Got: {arr.size} array elements") else: value = None # ready to add attribute now req = getDataNodeUrl(app, obj_id) req += '/' + collection + '/' + obj_id + "/attributes/" + attr_name log.info("PUT Attribute: " + req) attr_json = {} attr_json["type"] = datatype attr_json["shape"] = shape_json if value is not None: attr_json["value"] = value params = {} if bucket: params["bucket"] = bucket put_rsp = await http_put(app, req, params=params, data=attr_json) log.info(f"PUT Attribute resp: {put_rsp}") hrefs = [] # TBD req_rsp = { "hrefs": hrefs } # attribute creation successful resp = await jsonResponse(request, req_rsp, status=201) log.response(request, resp=resp) return resp