Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
itminedu
synnefo
Commits
9de489dd
Commit
9de489dd
authored
Aug 07, 2013
by
Giorgos Korfiatis
Browse files
Merge branch 'feature-projects-improved' into develop
parents
5089361f
5c8747a6
Changes
50
Expand all
Hide whitespace changes
Inline
Side-by-side
Changelog
View file @
9de489dd
...
...
@@ -16,6 +16,20 @@ Synnefo-wide
* Integrate Pithos tests in continuous integration.
Astakos
-------
* Changes in project schema:
* A Project entry is created when submitting an application for a new
project, rather than on approval. Its state is dependent on the state
of its `reference' application (current definition). Lock Project rather
than Chain (the latter is semantically obsolete).
* Improve recording of project, application, and membership actions.
* Implement API calls for projects.
Cyclades
--------
...
...
astakosclient/astakosclient/__init__.py
View file @
9de489dd
...
...
@@ -69,6 +69,9 @@ API_SERVICE_QUOTAS = join_urls(ACCOUNTS_PREFIX, "service_quotas")
API_COMMISSIONS
=
join_urls
(
ACCOUNTS_PREFIX
,
"commissions"
)
API_COMMISSIONS_ACTION
=
join_urls
(
API_COMMISSIONS
,
"action"
)
API_FEEDBACK
=
join_urls
(
ACCOUNTS_PREFIX
,
"feedback"
)
API_PROJECTS
=
join_urls
(
ACCOUNTS_PREFIX
,
"projects"
)
API_APPLICATIONS
=
join_urls
(
API_PROJECTS
,
"apps"
)
API_MEMBERSHIPS
=
join_urls
(
API_PROJECTS
,
"memberships"
)
# --------------------------------------------------------------------
# Astakos Keystone API urls
...
...
@@ -575,6 +578,235 @@ class AstakosClient():
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# ----------------------------
# do a GET to ``API_PROJECTS``
def
get_projects
(
self
,
token
,
name
=
None
,
state
=
None
,
owner
=
None
):
"""Retrieve all accessible projects
Arguments:
token -- user's token (string)
name -- filter by name (optional)
state -- filter by state (optional)
owner -- filter by owner (optional)
In case of success, return a list of project descriptions.
"""
path
=
API_PROJECTS
filters
=
{}
if
name
is
not
None
:
filters
[
"name"
]
=
name
if
state
is
not
None
:
filters
[
"state"
]
=
state
if
owner
is
not
None
:
filters
[
"owner"
]
=
owner
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
(
parse_request
({
"filter"
:
filters
},
self
.
logger
)
if
filters
else
None
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
)
# -----------------------------------------
# do a GET to ``API_PROJECTS``/<project_id>
def
get_project
(
self
,
token
,
project_id
):
"""Retrieve project description, if accessible
Arguments:
token -- user's token (string)
project_id -- project identifier
In case of success, return project description.
"""
path
=
join_urls
(
API_PROJECTS
,
str
(
project_id
))
return
self
.
_call_astakos
(
token
,
path
)
# -----------------------------
# do a POST to ``API_PROJECTS``
def
create_project
(
self
,
token
,
specs
):
"""Submit application to create a new project
Arguments:
token -- user's token (string)
specs -- dict describing a project
In case of success, return project and application identifiers.
"""
path
=
API_PROJECTS
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
parse_request
(
specs
,
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# ------------------------------------------
# do a POST to ``API_PROJECTS``/<project_id>
def
modify_project
(
self
,
token
,
project_id
,
specs
):
"""Submit application to modify an existing project
Arguments:
token -- user's token (string)
project_id -- project identifier
specs -- dict describing a project
In case of success, return project and application identifiers.
"""
path
=
join_urls
(
API_PROJECTS
,
str
(
project_id
))
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
parse_request
(
specs
,
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# -------------------------------------------------
# do a POST to ``API_PROJECTS``/<project_id>/action
def
project_action
(
self
,
token
,
project_id
,
action
,
reason
=
""
):
"""Perform action on a project
Arguments:
token -- user's token (string)
project_id -- project identifier
action -- action to perform, one of "suspend", "unsuspend",
"terminate", "reinstate"
reason -- reason of performing the action
In case of success, return nothing.
"""
path
=
join_urls
(
API_PROJECTS
,
str
(
project_id
))
path
=
join_urls
(
path
,
"action"
)
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
parse_request
({
action
:
reason
},
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# --------------------------------
# do a GET to ``API_APPLICATIONS``
def
get_applications
(
self
,
token
,
project
=
None
):
"""Retrieve all accessible applications
Arguments:
token -- user's token (string)
project -- filter by project (optional)
In case of success, return a list of application descriptions.
"""
path
=
API_APPLICATIONS
req_headers
=
{
'content-type'
:
'application/json'
}
body
=
{
"project"
:
project
}
if
project
is
not
None
else
None
req_body
=
parse_request
(
body
,
self
.
logger
)
if
body
else
None
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
)
# -----------------------------------------
# do a GET to ``API_APPLICATIONS``/<app_id>
def
get_application
(
self
,
token
,
app_id
):
"""Retrieve application description, if accessible
Arguments:
token -- user's token (string)
app_id -- application identifier
In case of success, return application description.
"""
path
=
join_urls
(
API_APPLICATIONS
,
str
(
app_id
))
return
self
.
_call_astakos
(
token
,
path
)
# -------------------------------------------------
# do a POST to ``API_APPLICATIONS``/<app_id>/action
def
application_action
(
self
,
token
,
app_id
,
action
,
reason
=
""
):
"""Perform action on an application
Arguments:
token -- user's token (string)
app_id -- application identifier
action -- action to perform, one of "approve", "deny",
"dismiss", "cancel"
reason -- reason of performing the action
In case of success, return nothing.
"""
path
=
join_urls
(
API_APPLICATIONS
,
str
(
app_id
))
path
=
join_urls
(
path
,
"action"
)
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
parse_request
({
action
:
reason
},
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# -------------------------------
# do a GET to ``API_MEMBERSHIPS``
def
get_memberships
(
self
,
token
,
project
=
None
):
"""Retrieve all accessible memberships
Arguments:
token -- user's token (string)
project -- filter by project (optional)
In case of success, return a list of membership descriptions.
"""
path
=
API_MEMBERSHIPS
req_headers
=
{
'content-type'
:
'application/json'
}
body
=
{
"project"
:
project
}
if
project
is
not
None
else
None
req_body
=
parse_request
(
body
,
self
.
logger
)
if
body
else
None
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
)
# -----------------------------------------
# do a GET to ``API_MEMBERSHIPS``/<memb_id>
def
get_membership
(
self
,
token
,
memb_id
):
"""Retrieve membership description, if accessible
Arguments:
token -- user's token (string)
memb_id -- membership identifier
In case of success, return membership description.
"""
path
=
join_urls
(
API_MEMBERSHIPS
,
str
(
memb_id
))
return
self
.
_call_astakos
(
token
,
path
)
# -------------------------------------------------
# do a POST to ``API_MEMBERSHIPS``/<memb_id>/action
def
membership_action
(
self
,
token
,
memb_id
,
action
,
reason
=
""
):
"""Perform action on a membership
Arguments:
token -- user's token (string)
memb_id -- membership identifier
action -- action to perform, one of "leave", "cancel", "accept",
"reject", "remove"
reason -- reason of performing the action
In case of success, return nothing.
"""
path
=
join_urls
(
API_MEMBERSHIPS
,
str
(
memb_id
))
path
=
join_urls
(
path
,
"action"
)
req_headers
=
{
'content-type'
:
'application/json'
}
req_body
=
parse_request
({
action
:
reason
},
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# --------------------------------
# do a POST to ``API_MEMBERSHIPS``
def
join_project
(
self
,
token
,
project_id
):
"""Join a project
Arguments:
token -- user's token (string)
project_id -- project identifier
In case of success, return membership identifier.
"""
path
=
API_MEMBERSHIPS
req_headers
=
{
'content-type'
:
'application/json'
}
body
=
{
"join"
:
{
"project"
:
project_id
}}
req_body
=
parse_request
(
body
,
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# --------------------------------
# do a POST to ``API_MEMBERSHIPS``
def
enroll_member
(
self
,
token
,
project_id
,
email
):
"""Enroll a user in a project
Arguments:
token -- user's token (string)
project_id -- project identifier
email -- user identified by email
In case of success, return membership identifier.
"""
path
=
API_MEMBERSHIPS
req_headers
=
{
'content-type'
:
'application/json'
}
body
=
{
"enroll"
:
{
"project"
:
project_id
,
"user"
:
email
}}
req_body
=
parse_request
(
body
,
self
.
logger
)
return
self
.
_call_astakos
(
token
,
path
,
req_headers
,
req_body
,
"POST"
)
# --------------------------------------------------------------------
# Private functions
...
...
astakosclient/docs/index.rst
View file @
9de489dd
...
...
@@ -192,6 +192,44 @@ retry=0, use_pool=False, pool_size=8, logger=None\ **)**
resolved.
Otherwise raise an AstakosClientException exception.
**get_projects(**\ token, name=None, state=None, owner=None\ **)**
Retrieve all accessible projects
**get_project(**\ token, project_id\ **)**
Retrieve project description, if accessible
**create_project(**\ token, specs\ **)**
Submit application to create a new project
**modify_project(**\ token, project_id, specs\ **)**
Submit application to modify an existing project
**project_action(**\ token, project_id, action, reason=""\ **)**
Perform action on a project
**get_applications(**\ token, project=None\ **)**
Retrieve all accessible applications
**get_application(**\ token, app_id\ **)**
Retrieve application description, if accessible
**application_action(**\ token, app_id, action, reason=""\ **)**
Perform action on an application
**get_memberships(**\ token, project=None\ **)**
Retrieve all accessible memberships
**get_membership(**\ token, memb_id\ **)**
Retrieve membership description, if accessible
**membership_action(**\ token, memb_id, action, reason=""\ **)**
Perform action on a membership
**join_project(**\ token, project_id\ **)**
Join a project
**enroll_member(**\ token, project_id, email\ **)**
Enroll a user in a project
Public Functions
----------------
...
...
docs/dev-guide.rst
View file @
9de489dd
...
...
@@ -46,6 +46,13 @@ Resource and Quota Service API (Astakos)
Resource and Quota API <quota-api-guide.rst>
Project API
===========
.. toctree::
:maxdepth: 2
Project API <project-api-guide.rst>
Compute Service API (Cyclades)
==============================
...
...
docs/project-api-guide.rst
0 → 100644
View file @
9de489dd
Projects
--------
Astakos allows users to create *projects*. Through a project, one can ask for
additional resources on the virtual infrastructure for a certain amount of
time. All users admitted to the project gain access to these resources.
Retrieve List of Projects
.........................
**GET** /account/v1.0/projects
Returns all accessible projects. See below.
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
Request can specify a filter.
**Example Request**:
.. code-block:: javascript
{
"filter": {
"state": ["active", "suspended"],
"owner": [uuid]
}
}
**Response Codes**:
====== =====================
Status Description
====== =====================
200 Success
400 Bad Request
401 Unauthorized (Missing token)
500 Internal Server Error
====== =====================
**Example Successful Response**:
List of project details. See below.
Retrieve a Project
..................
**GET** /account/v1.0/projects/<proj_id>
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
A project is accessible when the request user is admin, project owner,
applicant or member, or the project is active.
**Response Codes**:
====== ============================
Status Description
====== ============================
200 Success
401 Unauthorized (Missing token)
403 Forbidden
404 Not Found
500 Internal Server Error
====== ============================
**Example Successful Response**:
.. code-block:: javascript
{
"id": proj_id,
"application": app_id,
"state": "pending" | "active" | "denied" | "dismissed" | "cancelled" | "suspended" | "terminated",
"creation_date": "2013-06-26T11:48:06.579100+00:00",
"name": "name",
"owner": uuid,
"homepage": homepage or null,
"description": description or null,
"start_date": date,
"end_date": date,
"join_policy": "auto" | "moderated" | "closed",
"leave_policy": "auto" | "moderated" | "closed",
"max_members": int or null
"resources": {"cyclades.vm": {"project_capacity": int or null,
"member_capacity": int
}
}
# only if request user is admin or project owner:
"comments": comments,
"pending_application": last pending app id or null,
"deactivation_date": date # if applicable
}
Create a Project
................
**POST** /account/v1.0/projects
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
**Example Request**:
.. code-block:: javascript
{
"name": name,
"owner": uuid, # if omitted, request user assumed
"homepage": homepage, # optional
"description": description, # optional
"comments": comments, # optional
"start_date": date, # optional
"end_date": date,
"join_policy": "auto" | "moderated" | "closed", # default: "moderated"
"leave_policy": "auto" | "moderated" | "closed", # default: "auto"
"resources": {"cyclades.vm": {"project_capacity": int or null,
"member_capacity": int
}
}
}
**Response Codes**:
====== ============================
Status Description
====== ============================
201 Created
400 Bad Request
401 Unauthorized (Missing token)
403 Forbidden
409 Conflict
500 Internal Server Error
====== ============================
**Example Successful Response**:
.. code-block:: javascript
{
"id": project_id,
"application": application_id
}
Modify a Project
................
**POST** /account/v1.0/projects/<proj_id>
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
**Example Request**:
As above.
**Response Codes**:
====== ============================
Status Description
====== ============================
201 Created
400 Bad Request
401 Unauthorized (Missing token)
403 Forbidden
404 Not Found
409 Conflict
500 Internal Server Error
====== ============================
**Example Successful Response**:
As above.
Take Action on a Project
........................
**POST** /account/v1.0/projects/<proj_id>/action
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
**Example Request**:
.. code-block:: javascript
{
<action>: "reason"
}
<action> can be: "suspend", "unsuspend", "terminate", "reinstate"
**Response Codes**:
====== ============================
Status Description
====== ============================
200 Success
400 Bad Request
401 Unauthorized (Missing token)
403 Forbidden
404 Not Found
409 Conflict
500 Internal Server Error
====== ============================
Retrieve List of Applications
.............................
**GET** /account/v1.0/projects/apps
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
Get all accessible applications. See below.
**Example optional request**
.. code-block:: javascript
{
"project": <project_id>
}
**Response Codes**:
====== ============================
Status Description
====== ============================
200 Success
400 Bad Request
401 Unauthorized (Missing token)
500 Internal Server Error
====== ============================
**Example Successful Response**:
List of application details. See below.
Retrieve an Application
.......................
**GET** /account/v1.0/projects/apps/<app_id>
==================== =========================
Request Header Name Value
==================== =========================
X-Auth-Token User authentication token
==================== =========================
An application is accessible when the request user is admin or the
application owner/applicant.
**Response Codes**:
====== ============================
Status Description
====== ============================
200 Success
401 Unauthorized (Missing token)
403 Forbidden
404 Not Found
500 Internal Server Error
====== ============================
**Example Successful Response**
.. code-block:: javascript
{
"id": app_id,
"project": project_id,
"state": "pending" | "approved" | "replaced" | "denied" | "dismissed" | "cancelled",
"name": "name",
"owner": uuid,
"applicant": uuid,
"homepage": homepage or null,
"description": description or null,
"start_date": date,
"end_date": date,