Skip to content

Commit

Permalink
Added cdmi Request parser object and created tests for it.
Browse files Browse the repository at this point in the history
Also added the CDMI protocol specfile.
  • Loading branch information
koenbollen committed May 30, 2010
1 parent 58ff3d0 commit ff71b0c
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
.pyc
Binary file added docs/CDMI_SNIA_Architecture_v1.0.pdf
Binary file not shown.
146 changes: 146 additions & 0 deletions src/cdmi.py
@@ -0,0 +1,146 @@
#!/usr/bin/env python
# Koen Bollen <meneer@koenbollen.nl>
# 2010 GPL

SPECIFICATION_VERSION = 1.0

httpstatuscodes = [
( 200, "OK",
"Resource retrieved successfully" ),
( 201, "Created",
"Resource created successfully" ),
( 202, "Accepted",
"Long running operation accepted for processing" ),
( 204, "No Content",
"Operation successful, no data" ),
( 400, "Bad Request",
"Missing or invalid request contents" ),
( 401, "Unauthorized",
"Invalid authentication/authorization credentials" ),
( 403, "Forbidden",
"This user is not allowed to perform this request" ),
( 404, "Not Found",
"Requested resource not found" ),
( 405, "Method Not Allowed",
"Requested HTTP verb not allowed on this resource" ),
( 406, "Not Acceptable",
"No content type can be produced at this URI that matches the request" ),
( 409, "Conflict",
"The operation conflicts with a non-CDMI access protocol lock, "
+ "or could cause a state transition error on the server." ),
( 500, "Internal Server Error",
"An unexpected vendor specific error" ),
( 501, "Not Implemented",
"A CDMI operation or metadata value was attempted that is not implemented.")
]

mimetypes = {
'object': "application/vnd.org.snia.cdmi.object+json",
'container': "application/vnd.org.snia.cdmi.container+json",
'dataobject': "application/vnd.org.snia.cdmi.dataobject+json",
'domain': "application/vnd.org.snia.cdmi.domain+json",
'queue': "application/vnd.org.snia.cdmi.queue+json",
'capabilities': "application/vnd.org.snia.cdmi.capabilities+json",
}

class CDMIError( Exception ):
pass

class CDMIProtocolError( CDMIError ):
def __init__(self, message, cause ):
self.message = message
self.cause = cause

def __str__(self ):
return "%s: %r" % (self.message, self.cause)

def __repr__(self ):
return "<CDMIProtocolError %s>" % (str(self))

class Request( object ):

def __init__(self, method, path, headers ):

self.method = method.lower()
self.path = path.strip()
self.headers = headers
self.cdmi = None
self.objecttype = "unknown"
self.source = None
self.json = None

self.__validate()
self.__parse()

def __validate(self ):
if self.method not in ("get", "post", "put", "delete"):
raise CDMIProtocolError( "invalid method:", self.method )
if self.path[0] != '/':
raise CDMIProtocolError( "invalid path", self.path )

def __parse(self ):
h = self.headers
spec = h.get('X-CDMI-Specification-Version')
self.cdmi = spec is not None
if self.cdmi:

try:
if float(spec) < SPECIFICATION_VERSION:
raise ValueError
except ValueError:
raise CDMIProtocolError( "incorrect version", spec )

try:
accept = h['Accept']
contenttype = h['Content-Type']
except KeyError, e:
# One exception, a GET request without a type:
if not (e.args[0].lower() == "accept" and self.method=="get"):
raise CDMIProtocolError( "missing header", str(e) )
accept = None

for objecttype, mime in mimetypes.items():
if accept == contenttype == mime:
self.objecttype = objecttype
break
if self.method == "get" and accept == mime:
self.objecttype = objecttype
break
if self.objecttype == "object":
self.objecttype = "unknown"

self.accept = accept
self.contenttype = contenttype

else:
self.accept = None
self.contenttype = h.get('Content-Type')
if self.method in ("put","post"):
if self.contenttype:
self.objecttype = "dataobject"
else:
self.objecttype = "container"
else:
self.objecttype = "unknown"

p = self.path
self.fields = {}
if "?" in self.path:
self.path, fstr = self.path.split( "?", 1 )
fields = [(f.split(":", 1)+[None])[:2] for f in fstr.split(";")]
for k,v in fields:
if v:
v = v.strip()
self.fields[k.strip().lower()] = v

def __repr__(self ):
tmpl = "<cdmi request: {cdmi} {method} a {objecttype}: '{path}'>"
values = vars(self)
values['cdmi'] = self.cdmi and "cdmi" or "non-cdmi"
return tmpl.format( **values )

def read(self, fp ):
pass

# vim: expandtab shiftwidth=4 softtabstop=4 textwidth=79:

26 changes: 26 additions & 0 deletions tests/requests.py
@@ -0,0 +1,26 @@
#!/usr/bin/env python
# Koen Bollen <meneer koenbollen nl>
# 2010 GPL

import os
import yaml

import cdmi

def main():
path = os.path.join( os.path.dirname(__file__), "requests.yaml" )
requests = yaml.load( open( path ) )
for req in requests:
print req['description']

headers = req.get( "headers", {} )
headers['Host'] = "localhost"
res = cdmi.Request( req['method'], req['path'], headers )
print res, res.fields
print

if __name__ == "__main__":
main()

# vim: expandtab shiftwidth=4 softtabstop=4 textwidth=79:

182 changes: 182 additions & 0 deletions tests/requests.yaml
@@ -0,0 +1,182 @@
---
- description: Create a Container (CDMI Content Type)
method: PUT
path: /mydata
headers:
Accept: application/vnd.org.snia.cdmi.container+json
Content-Type: application/vnd.org.snia.cdmi.container+json
X-CDMI-Specification-Version: 1.0
data: |
{
"metadata": {
}
}
response: 201

- description: Create a Container (Non-CDMI Content Type)
method: PUT
path: /mydata
response: 201

- description: Read a Container Object (CDMI Content Type)
method: GET
path: /mydata
headers:
Accept: application/vnd.org.snia.cdmi.container+json
Content-Type: application/vnd.org.snia.cdmi.object+json
X-CDMI-Specification-Version: 1.0
response: 200

- description: Read a Container Object (CDMI Content Type)
method: GET
path: /mydata?children:0-42
headers:
Accept: application/vnd.org.snia.cdmi.container+json
Content-Type: application/vnd.org.snia.cdmi.object+json
X-CDMI-Specification-Version: 1.0
response: 200

- description: Read a Container Object (Non-CDMI Content Type)
method: GET
path: /mydata?parentURI
response: 200

- description: Update a Container (CDMI Content Type)
method: PUT
path: /mydata
headers:
Accept: application/vnd.org.snia.cdmi.container+json
Content-Type: application/vnd.org.snia.cdmi.container+json
X-CDMI-Specification-Version: 1.0
data: |
{
"metadata": {
'creator': "Koen Bollen",
'tags': "test"
}
}
response: 200

- description: Delete a Container Object (CDMI Content Type)
method: DELETE
path: /mydata
headers:
Accept: application/vnd.org.snia.cdmi.container+json
Content-Type: application/vnd.org.snia.cdmi.container+json
X-CDMI-Specification-Version: 1.0
response: 200

- description: Delete a Container Object (Non-CDMI Content Type)
method: DELETE
path: /mydata
response: 200

- description: Create a New Data Object (CDMI Content Type)
method: POST
path: /mydata/
headers:
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
data: |
{
"mimetype" : "text/plain",
"metadata" : {
},
"value" : "The cake is a lie!"
}
response: 201

- description: Create a New Data Object (Non-CDMI Content Type)
method: POST
path: /mydata/
headers:
Content-Type: text/plain
Content-Length: 15
data: Koen was hier!!
response: 201


- description: Create a Data Object (CDMI Content Type)
method: PUT
path: /mydata/hello.txt
headers:
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
data: |
{
"mimetype" : "text/plain",
"metadata" : {
},
"value" : "Hello, world!!\n"
}
response: 201

- description: Create a Data Object (Non-CDMI Content Type)
method: PUT
path: /mydata/hello.txt
headers:
Content-Type: text/plain
Content-Length: 15
data: Hello, world!!\n
response: 201

- description: Read a Data Object (CDMI Content Type)
method: GET
path: /mydata/hello.txt
headers:
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.object+json
X-CDMI-Specification-Version: 1.0
response: 200

- description: Read a Data Object (Non-CDMI Content Type)
method: GET
path: /mydata/hello.txt
response: 200

- description: Read a Data Object Fields (Non-CDMI Content Type)
method: GET
path: /mydata/hello.txt?objectID;metadata
response: 200

- description: Update a Data Object (CDMI Content Type)
method: PUT
path: /mydata/hello.txt
headers:
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
data: |
{
"mimetype" : "text/plain",
"metadata" : {
},
"value" : "new value.."
}
response: 201

- description: Update a Data Object (Non-CDMI Content Type)
method: PUT
path: /mydata/hello.txt
headers:
Content-Type: text/plain
Content-Length: 5
data: sup?\n
response: 201

- description: Delete a Data Object (CDMI Content Type)
method: DELETE
path: /mydata/hello.txt
headers:
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
response: 200

- description: Delete a Data Object (Non-CDMI Content Type)
method: DELETE
path: /mydata/hello.txt
response: 200

0 comments on commit ff71b0c

Please sign in to comment.