Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
snf-ganeti
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
itminedu
snf-ganeti
Commits
10b207d4
Commit
10b207d4
authored
Jul 22, 2008
by
Oleksiy Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Split RAPI resources to pieces
Reviewed-by: iustinp
parent
53b1d12b
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
346 additions
and
276 deletions
+346
-276
Makefile.am
Makefile.am
+5
-2
doc/build-rapi-resources-doc
doc/build-rapi-resources-doc
+5
-3
lib/rapi/RESTHTTPServer.py
lib/rapi/RESTHTTPServer.py
+5
-3
lib/rapi/baseresources.py
lib/rapi/baseresources.py
+0
-0
lib/rapi/connector.py
lib/rapi/connector.py
+141
-0
lib/rapi/rlib1.py
lib/rapi/rlib1.py
+26
-260
lib/rapi/rlib2.py
lib/rapi/rlib2.py
+154
-0
test/ganeti.rapi.resources_unittest.py
test/ganeti.rapi.resources_unittest.py
+10
-8
No files found.
Makefile.am
View file @
10b207d4
...
...
@@ -94,7 +94,10 @@ rapi_PYTHON = \
lib/rapi/__init__.py
\
lib/rapi/RESTHTTPServer.py
\
lib/rapi/httperror.py
\
lib/rapi/resources.py
lib/rapi/baserlib.py
\
lib/rapi/connector.py
\
lib/rapi/rlib1.py
\
lib/rapi/rlib2.py
docsgml
=
\
...
...
@@ -226,7 +229,7 @@ doc/%.html: doc/%.in $(DOCBOOK_WRAPPER)
doc/rapi.pdf doc/rapi.html
:
doc/rapi-resources.sgml
doc/rapi-resources.sgml
:
$(BUILD_RAPI_RESOURCE_DOC) lib/rapi/
resources
.py
doc/rapi-resources.sgml
:
$(BUILD_RAPI_RESOURCE_DOC) lib/rapi/
connector
.py
PYTHONPATH
=
.:
$(top_builddir)
$(BUILD_RAPI_RESOURCE_DOC)
>
$@
||
rm
-f
$@
man/%.7
:
man/%.in man/footer.sgml $(DOCBOOK_WRAPPER)
...
...
doc/build-rapi-resources-doc
View file @
10b207d4
...
...
@@ -26,7 +26,9 @@ import re
import
cgi
import
inspect
from
ganeti.rapi
import
resources
from
ganeti.rapi
import
rlib1
from
ganeti.rapi
import
rlib2
from
ganeti.rapi
import
connector
CHECKED_COMMANDS
=
[
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
]
...
...
@@ -34,9 +36,9 @@ CHECKED_COMMANDS = ["GET", "POST", "PUT", "DELETE"]
def
main
():
# Get list of all resources
all
=
list
(
resources
.
_
CONNECTOR
.
itervalues
())
all
=
list
(
connector
.
CONNECTOR
.
itervalues
())
# Sort r
esources
by URI
# Sort r
lib1
by URI
all
.
sort
(
cmp
=
lambda
a
,
b
:
cmp
(
a
.
DOC_URI
,
b
.
DOC_URI
))
print
"<!-- Automatically generated, do not edit -->"
...
...
lib/rapi/RESTHTTPServer.py
View file @
10b207d4
...
...
@@ -20,9 +20,10 @@
"""
import
socket
import
BaseHTTPServer
import
OpenSSL
import
re
import
socket
import
time
from
ganeti
import
constants
...
...
@@ -30,7 +31,8 @@ from ganeti import errors
from
ganeti
import
logger
from
ganeti
import
rpc
from
ganeti
import
serializer
from
ganeti.rapi
import
resources
from
ganeti.rapi
import
connector
from
ganeti.rapi
import
httperror
...
...
@@ -158,7 +160,7 @@ class RESTRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self
.
connection
=
self
.
request
self
.
rfile
=
socket
.
_fileobject
(
self
.
request
,
"rb"
,
self
.
rbufsize
)
self
.
wfile
=
socket
.
_fileobject
(
self
.
request
,
"wb"
,
self
.
wbufsize
)
self
.
_resmap
=
resources
.
Mapper
()
self
.
_resmap
=
connector
.
Mapper
()
def
handle_one_request
(
self
):
"""Handle a single REST request.
...
...
lib/rapi/baseresources.py
0 → 100644
View file @
10b207d4
lib/rapi/connector.py
0 → 100644
View file @
10b207d4
#
#
# Copyright (C) 2006, 2007, 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Remote API connection map.
"""
import
cgi
import
re
from
ganeti
import
constants
from
ganeti.rapi
import
baserlib
from
ganeti.rapi
import
httperror
from
ganeti.rapi
import
rlib1
from
ganeti.rapi
import
rlib2
# the connection map created at the end of this file
CONNECTOR
=
{}
class
Mapper
:
"""Map resource to method.
"""
def
__init__
(
self
,
connector
=
CONNECTOR
):
"""Resource mapper constructor.
Args:
con: a dictionary, mapping method name with URL path regexp
"""
self
.
_connector
=
connector
def
getController
(
self
,
uri
):
"""Find method for a given URI.
Args:
uri: string with URI
Returns:
None if no method is found or a tuple containing the following fields:
methd: name of method mapped to URI
items: a list of variable intems in the path
args: a dictionary with additional parameters from URL
"""
if
'?'
in
uri
:
(
path
,
query
)
=
uri
.
split
(
'?'
,
1
)
args
=
cgi
.
parse_qs
(
query
)
else
:
path
=
uri
query
=
None
args
=
{}
result
=
None
for
key
,
handler
in
self
.
_connector
.
iteritems
():
# Regex objects
if
hasattr
(
key
,
"match"
):
m
=
key
.
match
(
path
)
if
m
:
result
=
(
handler
,
list
(
m
.
groups
()),
args
)
break
# String objects
elif
key
==
path
:
result
=
(
handler
,
[],
args
)
break
if
result
is
not
None
:
return
result
else
:
raise
httperror
.
HTTPNotFound
()
class
R_root
(
baserlib
.
R_Generic
):
"""/ resource.
"""
DOC_URI
=
"/"
def
GET
(
self
):
"""Show the list of mapped resources.
Returns:
A dictionary with 'name' and 'uri' keys for each of them.
"""
root_pattern
=
re
.
compile
(
'^R_([a-zA-Z0-9]+)$'
)
rootlist
=
[]
for
handler
in
CONNECTOR
.
values
():
m
=
root_pattern
.
match
(
handler
.
__name__
)
if
m
:
name
=
m
.
group
(
1
)
if
name
!=
'root'
:
rootlist
.
append
(
name
)
return
baserlib
.
BuildUriList
(
rootlist
,
"/%s"
)
CONNECTOR
.
update
({
"/"
:
R_root
,
"/version"
:
rlib1
.
R_version
,
"/tags"
:
rlib1
.
R_tags
,
"/info"
:
rlib1
.
R_info
,
"/nodes"
:
rlib1
.
R_nodes
,
re
.
compile
(
r
'^/nodes/([\w\._-]+)$'
):
rlib1
.
R_nodes_name
,
re
.
compile
(
r
'^/nodes/([\w\._-]+)/tags$'
):
rlib1
.
R_nodes_name_tags
,
"/instances"
:
rlib1
.
R_instances
,
re
.
compile
(
r
'^/instances/([\w\._-]+)$'
):
rlib1
.
R_instances_name
,
re
.
compile
(
r
'^/instances/([\w\._-]+)/tags$'
):
rlib1
.
R_instances_name_tags
,
"/os"
:
rlib1
.
R_os
,
"/2/jobs"
:
rlib2
.
R_2_jobs
,
"/2/nodes"
:
rlib2
.
R_2_nodes
,
re
.
compile
(
r
'/2/jobs/(%s)$'
%
constants
.
JOB_ID_TEMPLATE
):
rlib2
.
R_2_jobs_id
,
})
lib/rapi/r
esources
.py
→
lib/rapi/r
lib1
.py
View file @
10b207d4
...
...
@@ -19,189 +19,24 @@
# 02110-1301, USA.
"""Remote API
resources
.
"""Remote API
version 1 resources library
.
"""
import
cgi
import
re
import
ganeti.opcodes
import
ganeti.errors
import
ganeti.cli
import
ganeti.errors
import
ganeti.opcodes
from
ganeti
import
constants
from
ganeti
import
luxi
from
ganeti
import
utils
from
ganeti.rapi
import
httperror
# Initialized at the end of this file.
_CONNECTOR
=
{}
def
BuildUriList
(
ids
,
uri_format
,
uri_fields
=
(
"name"
,
"uri"
)):
"""Builds a URI list as used by index resources.
Args:
- ids: List of ids as strings
- uri_format: Format to be applied for URI
- uri_fields: Optional parameter for field ids
"""
(
field_id
,
field_uri
)
=
uri_fields
def
_MapId
(
m_id
):
return
{
field_id
:
m_id
,
field_uri
:
uri_format
%
m_id
,
}
# Make sure the result is sorted, makes it nicer to look at and simplifies
# unittests.
ids
.
sort
()
return
map
(
_MapId
,
ids
)
def
ExtractField
(
sequence
,
index
):
"""Creates a list containing one column out of a list of lists.
Args:
- sequence: Sequence of lists
- index: Index of field
"""
return
map
(
lambda
item
:
item
[
index
],
sequence
)
def
MapFields
(
names
,
data
):
"""Maps two lists into one dictionary.
Args:
- names: Field names (list of strings)
- data: Field data (list)
Example:
>>> MapFields(["a", "b"], ["foo", 123])
{'a': 'foo', 'b': 123}
"""
if
len
(
names
)
!=
len
(
data
):
raise
AttributeError
(
"Names and data must have the same length"
)
return
dict
([(
names
[
i
],
data
[
i
])
for
i
in
range
(
len
(
names
))])
def
_Tags_GET
(
kind
,
name
=
None
):
"""Helper function to retrieve tags.
"""
if
name
is
None
:
# Do not cause "missing parameter" error, which happens if a parameter
# is None.
name
=
""
op
=
ganeti
.
opcodes
.
OpGetTags
(
kind
=
kind
,
name
=
name
)
tags
=
ganeti
.
cli
.
SubmitOpCode
(
op
)
return
list
(
tags
)
class
Mapper
:
"""Map resource to method.
"""
def
__init__
(
self
,
connector
=
_CONNECTOR
):
"""Resource mapper constructor.
Args:
con: a dictionary, mapping method name with URL path regexp
"""
self
.
_connector
=
connector
from
ganeti.rapi
import
baserlib
from
ganeti.rapi
import
httperror
def
getController
(
self
,
uri
):
"""Find method for a given URI.
Args:
uri: string with URI
Returns:
None if no method is found or a tuple containing the following fields:
methd: name of method mapped to URI
items: a list of variable intems in the path
args: a dictionary with additional parameters from URL
"""
if
'?'
in
uri
:
(
path
,
query
)
=
uri
.
split
(
'?'
,
1
)
args
=
cgi
.
parse_qs
(
query
)
else
:
path
=
uri
query
=
None
args
=
{}
result
=
None
for
key
,
handler
in
self
.
_connector
.
iteritems
():
# Regex objects
if
hasattr
(
key
,
"match"
):
m
=
key
.
match
(
path
)
if
m
:
result
=
(
handler
,
list
(
m
.
groups
()),
args
)
break
# String objects
elif
key
==
path
:
result
=
(
handler
,
[],
args
)
break
if
result
is
not
None
:
return
result
else
:
raise
httperror
.
HTTPNotFound
()
class
R_Generic
(
object
):
"""Generic class for resources.
"""
def
__init__
(
self
,
request
,
items
,
queryargs
):
"""Generic resource constructor.
Args:
request: HTTPRequestHandler object
items: a list with variables encoded in the URL
queryargs: a dictionary with additional options from URL
"""
self
.
request
=
request
self
.
items
=
items
self
.
queryargs
=
queryargs
class
R_root
(
R_Generic
):
"""/ resource.
"""
DOC_URI
=
"/"
def
GET
(
self
):
"""Show the list of mapped resources.
Returns:
A dictionary with 'name' and 'uri' keys for each of them.
"""
root_pattern
=
re
.
compile
(
'^R_([a-zA-Z0-9]+)$'
)
rootlist
=
[]
for
handler
in
_CONNECTOR
.
values
():
m
=
root_pattern
.
match
(
handler
.
__name__
)
if
m
:
name
=
m
.
group
(
1
)
if
name
!=
'root'
:
rootlist
.
append
(
name
)
return
BuildUriList
(
rootlist
,
"/%s"
)
class
R_version
(
R_Generic
):
class
R_version
(
baserlib
.
R_Generic
):
"""/version resource.
This resource should be used to determine the remote API version and to adapt
...
...
@@ -217,7 +52,7 @@ class R_version(R_Generic):
return
constants
.
RAPI_VERSION
class
R_tags
(
R_Generic
):
class
R_tags
(
baserlib
.
R_Generic
):
"""/tags resource.
Manages cluster tags.
...
...
@@ -231,10 +66,10 @@ class R_tags(R_Generic):
Example: ["tag1", "tag2", "tag3"]
"""
return
_Tags_GET
(
constants
.
TAG_CLUSTER
)
return
baserlib
.
_Tags_GET
(
constants
.
TAG_CLUSTER
)
class
R_info
(
R_Generic
):
class
R_info
(
baserlib
.
R_Generic
):
"""Cluster info.
"""
...
...
@@ -263,7 +98,7 @@ class R_info(R_Generic):
return
ganeti
.
cli
.
SubmitOpCode
(
op
)
class
R_nodes
(
R_Generic
):
class
R_nodes
(
baserlib
.
R_Generic
):
"""/nodes resource.
"""
...
...
@@ -289,7 +124,7 @@ class R_nodes(R_Generic):
nodes_details
=
[]
for
node
in
result
:
mapped
=
MapFields
(
fields
,
node
)
mapped
=
baserlib
.
MapFields
(
fields
,
node
)
nodes_details
.
append
(
mapped
)
return
nodes_details
...
...
@@ -330,15 +165,15 @@ class R_nodes(R_Generic):
"""
op
=
ganeti
.
opcodes
.
OpQueryNodes
(
output_fields
=
[
"name"
],
names
=
[])
nodeslist
=
ExtractField
(
ganeti
.
cli
.
SubmitOpCode
(
op
),
0
)
nodeslist
=
baserlib
.
ExtractField
(
ganeti
.
cli
.
SubmitOpCode
(
op
),
0
)
if
'bulk'
in
self
.
queryargs
:
return
self
.
_GetDetails
(
nodeslist
)
return
BuildUriList
(
nodeslist
,
"/nodes/%s"
)
return
baserlib
.
BuildUriList
(
nodeslist
,
"/nodes/%s"
)
class
R_nodes_name
(
R_Generic
):
class
R_nodes_name
(
baserlib
.
R_Generic
):
"""/nodes/[node_name] resources.
"""
...
...
@@ -357,10 +192,10 @@ class R_nodes_name(R_Generic):
names
=
[
node_name
])
result
=
ganeti
.
cli
.
SubmitOpCode
(
op
)
return
MapFields
(
fields
,
result
[
0
])
return
baserlib
.
MapFields
(
fields
,
result
[
0
])
class
R_nodes_name_tags
(
R_Generic
):
class
R_nodes_name_tags
(
baserlib
.
R_Generic
):
"""/nodes/[node_name]/tags resource.
Manages per-node tags.
...
...
@@ -374,10 +209,10 @@ class R_nodes_name_tags(R_Generic):
Example: ["tag1", "tag2", "tag3"]
"""
return
_Tags_GET
(
constants
.
TAG_NODE
,
name
=
self
.
items
[
0
])
return
baserlib
.
_Tags_GET
(
constants
.
TAG_NODE
,
name
=
self
.
items
[
0
])
class
R_instances
(
R_Generic
):
class
R_instances
(
baserlib
.
R_Generic
):
"""/instances resource.
"""
...
...
@@ -405,7 +240,7 @@ class R_instances(R_Generic):
instances_details
=
[]
for
instance
in
result
:
mapped
=
MapFields
(
fields
,
instance
)
mapped
=
baserlib
.
MapFields
(
fields
,
instance
)
instances_details
.
append
(
mapped
)
return
instances_details
...
...
@@ -453,16 +288,16 @@ class R_instances(R_Generic):
"""
op
=
ganeti
.
opcodes
.
OpQueryInstances
(
output_fields
=
[
"name"
],
names
=
[])
instanceslist
=
ExtractField
(
ganeti
.
cli
.
SubmitOpCode
(
op
),
0
)
instanceslist
=
baserlib
.
ExtractField
(
ganeti
.
cli
.
SubmitOpCode
(
op
),
0
)
if
'bulk'
in
self
.
queryargs
:
return
self
.
_GetDetails
(
instanceslist
)
else
:
return
BuildUriList
(
instanceslist
,
"/instances/%s"
)
return
baserlib
.
BuildUriList
(
instanceslist
,
"/instances/%s"
)
class
R_instances_name
(
R_Generic
):
class
R_instances_name
(
baserlib
.
R_Generic
):
"""/instances/[instance_name] resources.
"""
...
...
@@ -483,10 +318,10 @@ class R_instances_name(R_Generic):
names
=
[
instance_name
])
result
=
ganeti
.
cli
.
SubmitOpCode
(
op
)
return
MapFields
(
fields
,
result
[
0
])
return
baserlib
.
MapFields
(
fields
,
result
[
0
])
class
R_instances_name_tags
(
R_Generic
):
class
R_instances_name_tags
(
baserlib
.
R_Generic
):
"""/instances/[instance_name]/tags resource.
Manages per-instance tags.
...
...
@@ -500,10 +335,10 @@ class R_instances_name_tags(R_Generic):
Example: ["tag1", "tag2", "tag3"]
"""
return
_Tags_GET
(
constants
.
TAG_INSTANCE
,
name
=
self
.
items
[
0
])
return
baserlib
.
_Tags_GET
(
constants
.
TAG_INSTANCE
,
name
=
self
.
items
[
0
])
class
R_os
(
R_Generic
):
class
R_os
(
baserlib
.
R_Generic
):
"""/os resource.
"""
...
...
@@ -525,72 +360,3 @@ class R_os(R_Generic):
raise
httperror
.
HTTPInternalError
(
message
=
"Can't get OS list"
)
return
[
row
[
0
]
for
row
in
diagnose_data
if
row
[
1
]]
class
R_2_jobs
(
R_Generic
):
"""/2/jobs resource.
"""
DOC_URI
=
"/2/jobs"
def
GET
(
self
):
"""Returns a dictionary of jobs.
Returns:
A dictionary with jobs id and uri.
"""
fields
=
[
"id"
]
# Convert the list of lists to the list of ids
result
=
[
job_id
for
[
job_id
]
in
luxi
.
Client
().
QueryJobs
(
None
,
fields
)]
return
BuildUriList
(
result
,
"/2/jobs/%s"
,
uri_fields
=
(
"id"
,
"uri"
))
class
R_2_jobs_id
(
R_Generic
):
"""/2/jobs/[job_id] resource.
"""
DOC_URI
=
"/2/jobs/[job_id]"
def
GET
(
self
):
"""Returns a job status.
Returns:
A dictionary with job parameters.
The result includes:
id - job ID as a number
status - current job status as a string
ops - involved OpCodes as a list of dictionaries for each opcodes in
the job
opstatus - OpCodes status as a list
opresult - OpCodes results as a list of lists
"""
fields
=
[
"id"
,
"ops"
,
"status"
,
"opstatus"
,
"opresult"
]
job_id
=
self
.
items
[
0
]
result
=
luxi
.
Client
().
QueryJobs
([
job_id
,],
fields
)[
0
]
return
MapFields
(
fields
,
result
)
_CONNECTOR
.
update
({
"/"
:
R_root
,
"/version"
:
R_version
,
"/tags"
:
R_tags
,
"/info"
:
R_info
,
"/nodes"
:
R_nodes
,
re
.
compile
(
r
'^/nodes/([\w\._-]+)$'
):
R_nodes_name
,
re
.
compile
(
r
'^/nodes/([\w\._-]+)/tags$'
):
R_nodes_name_tags
,
"/instances"
:
R_instances
,
re
.
compile
(
r
'^/instances/([\w\._-]+)$'
):
R_instances_name
,
re
.
compile
(
r
'^/instances/([\w\._-]+)/tags$'
):
R_instances_name_tags
,
"/os"
:
R_os
,
"/2/jobs"
:
R_2_jobs
,
re
.
compile
(
r
'/2/jobs/(%s)$'
%
constants
.
JOB_ID_TEMPLATE
):
R_2_jobs_id
,
})
lib/rapi/rlib2.py
0 → 100644
View file @
10b207d4
#
#
# Copyright (C) 2006, 2007, 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Remote API version 2 baserlib.library.
"""
import
re
import
ganeti.opcodes
from
ganeti
import
constants
from
ganeti
import
luxi
from
ganeti.rapi
import
baserlib
class
R_2_jobs
(
baserlib
.
R_Generic
):
"""/2/jobs resource.
"""
DOC_URI
=
"/2/jobs"