Updated initial version of portas-api
This commit is contained in:
parent
b9d60d50bd
commit
0e8c965b88
90
api/setup.py
90
api/setup.py
@ -1,90 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2011 OpenStack LLC.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
|
||||||
from setuptools.command.sdist import sdist
|
|
||||||
|
|
||||||
from windc import version
|
|
||||||
|
|
||||||
|
|
||||||
if os.path.isdir('.bzr'):
|
|
||||||
with open("windc/vcsversion.py", 'w') as version_file:
|
|
||||||
vcs_cmd = subprocess.Popen(["bzr", "version-info", "--python"],
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
vcsversion = vcs_cmd.communicate()[0]
|
|
||||||
version_file.write(vcsversion)
|
|
||||||
|
|
||||||
|
|
||||||
class local_sdist(sdist):
|
|
||||||
"""Customized sdist hook - builds the ChangeLog file from VC first"""
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
if os.path.isdir('.bzr'):
|
|
||||||
# We're in a bzr branch
|
|
||||||
|
|
||||||
log_cmd = subprocess.Popen(["bzr", "log", "--gnu"],
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
changelog = log_cmd.communicate()[0]
|
|
||||||
with open("ChangeLog", "w") as changelog_file:
|
|
||||||
changelog_file.write(changelog)
|
|
||||||
sdist.run(self)
|
|
||||||
|
|
||||||
cmdclass = {'sdist': local_sdist}
|
|
||||||
|
|
||||||
# If Sphinx is installed on the box running setup.py,
|
|
||||||
# enable setup.py to build the documentation, otherwise,
|
|
||||||
# just ignore it
|
|
||||||
try:
|
|
||||||
from sphinx.setup_command import BuildDoc
|
|
||||||
|
|
||||||
class local_BuildDoc(BuildDoc):
|
|
||||||
def run(self):
|
|
||||||
for builder in ['html', 'man']:
|
|
||||||
self.builder = builder
|
|
||||||
self.finalize_options()
|
|
||||||
BuildDoc.run(self)
|
|
||||||
cmdclass['build_sphinx'] = local_BuildDoc
|
|
||||||
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='windc',
|
|
||||||
version=version.canonical_version_string(),
|
|
||||||
description='The WinDC project provides a simple WSGI server for Windows Environment Management',
|
|
||||||
license='Apache License (2.0)',
|
|
||||||
author='OpenStack',
|
|
||||||
author_email='openstack@lists.launchpad.net',
|
|
||||||
url='http://windc.openstack.org/',
|
|
||||||
packages=find_packages(exclude=['tests', 'bin']),
|
|
||||||
test_suite='nose.collector',
|
|
||||||
cmdclass=cmdclass,
|
|
||||||
include_package_data=True,
|
|
||||||
classifiers=[
|
|
||||||
'Development Status :: 4 - Beta',
|
|
||||||
'License :: OSI Approved :: Apache Software License',
|
|
||||||
'Operating System :: POSIX :: Linux',
|
|
||||||
'Programming Language :: Python :: 2.6',
|
|
||||||
'Environment :: No Input/Output (Daemon)',
|
|
||||||
],
|
|
||||||
scripts=['bin/windc',
|
|
||||||
'bin/windc-api'])
|
|
2
portas/.gitignore
vendored
2
portas/.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
##IntelJ Idea
|
#IntelJ Idea
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
#virtualenv
|
#virtualenv
|
||||||
|
@ -19,29 +19,32 @@ import gettext
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
# If ../portas/__init__.py exists, add ../ to Python search path, so that
|
# If ../portas/__init__.py exists, add ../ to Python search path, so that
|
||||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||||
|
|
||||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||||
os.pardir,
|
os.pardir,
|
||||||
os.pardir))
|
os.pardir))
|
||||||
if os.path.exists(os.path.join(possible_topdir, 'portas', '__init__.py')):
|
if os.path.exists(os.path.join(possible_topdir, 'portas', '__init__.py')):
|
||||||
sys.path.insert(0, possible_topdir)
|
sys.path.insert(0, possible_topdir)
|
||||||
|
|
||||||
gettext.install('portas', './portas/locale', unicode=1)
|
|
||||||
|
|
||||||
from portas.common import config
|
from portas.common import config
|
||||||
from portas.openstack.common import log
|
from portas.openstack.common import log
|
||||||
from portas.openstack.common import wsgi
|
from portas.openstack.common import wsgi
|
||||||
|
from portas.openstack.common import service
|
||||||
|
|
||||||
|
gettext.install('portas', './portas/locale', unicode=1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
config.parse_args()
|
config.parse_args()
|
||||||
|
log.setup('portas')
|
||||||
|
|
||||||
server = wsgi.Server()
|
api_service = wsgi.Service(config.load_paste_app(),
|
||||||
server.start(config.load_paste_app(), default_port=8181)
|
port=config.CONF.bind_port,
|
||||||
server.wait()
|
host=config.CONF.bind_host)
|
||||||
|
launcher = service.Launcher()
|
||||||
|
launcher.run_service(api_service)
|
||||||
except RuntimeError, e:
|
except RuntimeError, e:
|
||||||
sys.stderr.write("ERROR: %s\n" % e)
|
sys.stderr.write("ERROR: %s\n" % e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2010 OpenStack Foundation.
|
# Copyright (c) 2010 OpenStack Foundation.
|
||||||
#
|
#
|
||||||
@ -15,8 +16,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
#
|
#
|
||||||
# Glance documentation build configuration file, created by
|
# Portas documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Tue May 18 13:50:15 2010.
|
# sphinx-quickstart on Tue February 28 13:50:15 2013.
|
||||||
#
|
#
|
||||||
# This file is execfile()'d with the current directory set to its containing
|
# This file is execfile()'d with the current directory set to its containing
|
||||||
# dir.
|
# dir.
|
||||||
@ -33,7 +34,7 @@ import sys
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path = [os.path.abspath('../../glance'),
|
sys.path = [os.path.abspath('../../portas'),
|
||||||
os.path.abspath('../..'),
|
os.path.abspath('../..'),
|
||||||
os.path.abspath('../../bin')
|
os.path.abspath('../../bin')
|
||||||
] + sys.path
|
] + sys.path
|
||||||
@ -65,19 +66,19 @@ source_suffix = '.rst'
|
|||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'Glance'
|
project = u'Portas'
|
||||||
copyright = u'2010, OpenStack Foundation.'
|
copyright = u'2013, Mirantis, Inc.'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
from glance.version import version_info as glance_version
|
from portas.version import version_info as portas_version
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = glance_version.version_string_with_vcs()
|
release = portas_version.version_string_with_vcs()
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = glance_version.canonical_version_string()
|
version = portas_version.canonical_version_string()
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
@ -114,7 +115,7 @@ show_authors = True
|
|||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
modindex_common_prefix = ['glance.']
|
modindex_common_prefix = ['portas.']
|
||||||
|
|
||||||
# -- Options for man page output --------------------------------------------
|
# -- Options for man page output --------------------------------------------
|
||||||
|
|
||||||
@ -122,25 +123,7 @@ modindex_common_prefix = ['glance.']
|
|||||||
# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'
|
# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'
|
||||||
|
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('man/glance', 'glance', u'Glance CLI',
|
('man/portasapi', 'portas-api', u'Portas API Server',
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glanceapi', 'glance-api', u'Glance API Server',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancecachecleaner', 'glance-cache-cleaner', u'Glance Cache Cleaner',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancecachemanage', 'glance-cache-manage', u'Glance Cache Manager',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancecacheprefetcher', 'glance-cache-prefetcher',
|
|
||||||
u'Glance Cache Pre-fetcher', [u'OpenStack'], 1),
|
|
||||||
('man/glancecachepruner', 'glance-cache-pruner', u'Glance Cache Pruner',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancecontrol', 'glance-control', u'Glance Daemon Control Helper ',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancemanage', 'glance-manage', u'Glance Management Utility',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glanceregistry', 'glance-registry', u'Glance Registry Server',
|
|
||||||
[u'OpenStack'], 1),
|
|
||||||
('man/glancescrubber', 'glance-scrubber', u'Glance Scrubber Service',
|
|
||||||
[u'OpenStack'], 1)
|
[u'OpenStack'], 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -219,7 +202,7 @@ html_use_index = False
|
|||||||
#html_file_suffix = ''
|
#html_file_suffix = ''
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'glancedoc'
|
htmlhelp_basename = 'portasdoc'
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ------------------------------------------------
|
# -- Options for LaTeX output ------------------------------------------------
|
||||||
@ -234,8 +217,8 @@ htmlhelp_basename = 'glancedoc'
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# documentclass [howto/manual]).
|
# documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'Glance.tex', u'Glance Documentation',
|
('index', 'Portas.tex', u'Portas Documentation',
|
||||||
u'Glance Team', 'manual'),
|
u'Keero Team', 'manual'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
@ -256,6 +239,4 @@ latex_documents = [
|
|||||||
#latex_use_modindex = True
|
#latex_use_modindex = True
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
|
intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
|
||||||
'nova': ('http://nova.openstack.org', None),
|
|
||||||
'swift': ('http://swift.openstack.org', None)}
|
|
||||||
|
@ -14,70 +14,7 @@
|
|||||||
License for the specific language governing permissions and limitations
|
License for the specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
|
|
||||||
Welcome to Glance's documentation!
|
Welcome to Portas's documentation!
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
The Glance project provides services for discovering, registering, and
|
We rule the world!
|
||||||
retrieving virtual machine images. Glance has a RESTful API that allows
|
|
||||||
querying of VM image metadata as well as retrieval of the actual image.
|
|
||||||
|
|
||||||
VM images made available through Glance can be stored in a variety of
|
|
||||||
locations from simple filesystems to object-storage systems like the
|
|
||||||
OpenStack Swift project.
|
|
||||||
|
|
||||||
Glance, as with all OpenStack projects, is written with the following design
|
|
||||||
guidelines in mind:
|
|
||||||
|
|
||||||
* **Component based architecture**: Quickly add new behaviors
|
|
||||||
* **Highly available**: Scale to very serious workloads
|
|
||||||
* **Fault tolerant**: Isolated processes avoid cascading failures
|
|
||||||
* **Recoverable**: Failures should be easy to diagnose, debug, and rectify
|
|
||||||
* **Open standards**: Be a reference implementation for a community-driven api
|
|
||||||
|
|
||||||
This documentation is generated by the Sphinx toolkit and lives in the source
|
|
||||||
tree. Additional documentation on Glance and other components of OpenStack can
|
|
||||||
be found on the `OpenStack wiki`_.
|
|
||||||
|
|
||||||
.. _`OpenStack wiki`: http://wiki.openstack.org
|
|
||||||
|
|
||||||
Concepts
|
|
||||||
========
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
identifiers
|
|
||||||
statuses
|
|
||||||
formats
|
|
||||||
common-image-properties
|
|
||||||
|
|
||||||
Installing/Configuring Glance
|
|
||||||
=============================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
installing
|
|
||||||
configuring
|
|
||||||
authentication
|
|
||||||
policies
|
|
||||||
|
|
||||||
Operating Glance
|
|
||||||
================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
controllingservers
|
|
||||||
db
|
|
||||||
cache
|
|
||||||
notifications
|
|
||||||
|
|
||||||
Using Glance
|
|
||||||
============
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
glanceapi
|
|
||||||
glanceclient
|
|
@ -1,57 +1,5 @@
|
|||||||
[DEFAULT]
|
[pipeline:portas-api]
|
||||||
# Show more verbose log output (sets INFO log level output)
|
|
||||||
verbose = True
|
|
||||||
# Show debugging output in logs (sets DEBUG log level output)
|
|
||||||
debug = True
|
|
||||||
# Address to bind the server to
|
|
||||||
bind_host = 0.0.0.0
|
|
||||||
# Port the bind the server to
|
|
||||||
bind_port = 8082
|
|
||||||
# Log to this file. Make sure the user running skeleton-api has
|
|
||||||
# permissions to write to this file!
|
|
||||||
log_file = /tmp/api.log
|
|
||||||
# Orchestration Adapter Section
|
|
||||||
#
|
|
||||||
#provider - Cloud provider to use (openstack, amazon, dummy)
|
|
||||||
provider = openstack
|
|
||||||
|
|
||||||
# Heat specific parameters
|
|
||||||
#heat_url - url for the heat service
|
|
||||||
# [auto] - find in the keystone
|
|
||||||
heat_url = auto
|
|
||||||
|
|
||||||
#heat_api_version - version of the API to use
|
|
||||||
#
|
|
||||||
heat_api_version = 1
|
|
||||||
|
|
||||||
|
|
||||||
[pipeline:windc-api]
|
|
||||||
pipeline = apiv1app
|
pipeline = apiv1app
|
||||||
# NOTE: use the following pipeline for keystone
|
|
||||||
#pipeline = authtoken context apiv1app
|
|
||||||
|
|
||||||
[app:apiv1app]
|
[app:apiv1app]
|
||||||
paste.app_factory = windc.common.wsgi:app_factory
|
paste.app_factory = portas.api.v1.router:API.factory
|
||||||
windc.app_factory = windc.api.v1.router:API
|
|
||||||
|
|
||||||
[filter:context]
|
|
||||||
paste.filter_factory = windc.common.wsgi:filter_factory
|
|
||||||
windc.filter_factory = windc.common.context:ContextMiddleware
|
|
||||||
|
|
||||||
[filter:authtoken]
|
|
||||||
paste.filter_factory = keystone.middleware.auth_token:filter_factory
|
|
||||||
auth_host = 172.18.67.57
|
|
||||||
auth_port = 35357
|
|
||||||
auth_protocol = http
|
|
||||||
auth_uri = http://172.18.67.57:5000/v2.0/
|
|
||||||
admin_tenant_name = service
|
|
||||||
admin_user = windc
|
|
||||||
admin_password = 000
|
|
||||||
|
|
||||||
[filter:auth-context]
|
|
||||||
paste.filter_factory = windc.common.wsgi:filter_factory
|
|
||||||
windc.filter_factory = keystone.middleware.balancer_auth_token:KeystoneContextMiddleware
|
|
||||||
|
|
||||||
[rabbitmq]
|
|
||||||
host = 10.0.0.1
|
|
||||||
vhost = keero
|
|
@ -13,22 +13,7 @@ bind_port = 8082
|
|||||||
|
|
||||||
# Log to this file. Make sure the user running skeleton-api has
|
# Log to this file. Make sure the user running skeleton-api has
|
||||||
# permissions to write to this file!
|
# permissions to write to this file!
|
||||||
log_file = /tmp/api.log
|
log_file = /tmp/portas-api.log
|
||||||
|
|
||||||
[pipeline:windc-api]
|
#A valid SQLAlchemy connection string for the metadata database
|
||||||
pipeline = versionnegotiation context apiv1app
|
sql_connection = sqlite:///portas.sqlite
|
||||||
|
|
||||||
[pipeline:versions]
|
|
||||||
pipeline = versionsapp
|
|
||||||
|
|
||||||
[app:versionsapp]
|
|
||||||
paste.app_factory = windc.api.versions:app_factory
|
|
||||||
|
|
||||||
[app:apiv1app]
|
|
||||||
paste.app_factory = windc.api.v1:app_factory
|
|
||||||
|
|
||||||
[filter:versionnegotiation]
|
|
||||||
paste.filter_factory = windc.api.middleware.version_negotiation:filter_factory
|
|
||||||
|
|
||||||
[filter:context]
|
|
||||||
paste.filter_factory = openstack.common.middleware.context:filter_factory
|
|
@ -1,7 +1,7 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|
||||||
# The list of modules to copy from openstack-common
|
# The list of modules to copy from openstack-common
|
||||||
modules=setup,wsgi,config,exception,gettextutilsl,jsonutils,log,xmlutils,sslutils,service,notifier,local,install_venv_common
|
modules=setup,wsgi,config,exception,gettextutils,importutils,jsonutils,log,xmlutils,sslutils,service,notifier,local,install_venv_common,version,timeutils,eventlet_backdoor,threadgroup,loopingcall,uuidutils
|
||||||
|
|
||||||
# The base module to hold the copy of openstack.common
|
# The base module to hold the copy of openstack.common
|
||||||
base=portas
|
base=portas
|
@ -0,0 +1,30 @@
|
|||||||
|
from portas.db.api import EnvironmentRepository
|
||||||
|
from portas.openstack.common import wsgi
|
||||||
|
from portas.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(object):
|
||||||
|
repository = EnvironmentRepository()
|
||||||
|
|
||||||
|
def index(self, request):
|
||||||
|
log.debug(_("Display list of environments"))
|
||||||
|
return {"environments": [env.to_dict() for env in self.repository.list()]}
|
||||||
|
|
||||||
|
def create(self, request, body):
|
||||||
|
return self.repository.add(body).to_dict()
|
||||||
|
|
||||||
|
# def delete(self, request, datacenter_id):
|
||||||
|
# log.debug("Got delete request. Request: %s", req)
|
||||||
|
# self.repository., datacenter_id)
|
||||||
|
#
|
||||||
|
# def update(self, req, tenant_id, datacenter_id, body):
|
||||||
|
# log.debug("Got update request. Request: %s", req)
|
||||||
|
# core_api.update_dc(self.conf, tenant_id, datacenter_id, body)
|
||||||
|
# return {'datacenter': {'id': dc_id}}
|
||||||
|
|
||||||
|
|
||||||
|
def create_resource():
|
||||||
|
return wsgi.Resource(Controller())
|
@ -14,37 +14,17 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import routes
|
||||||
|
from portas.openstack.common import wsgi
|
||||||
from glance.api.v1 import images
|
from portas.api.v1 import environments
|
||||||
from glance.api.v1 import members
|
|
||||||
from glance.common import wsgi
|
|
||||||
|
|
||||||
|
|
||||||
class API(wsgi.Router):
|
class API(wsgi.Router):
|
||||||
|
@classmethod
|
||||||
"""WSGI router for Glance v1 API requests."""
|
def factory(cls, global_conf, **local_conf):
|
||||||
|
return cls(routes.Mapper())
|
||||||
|
|
||||||
def __init__(self, mapper):
|
def __init__(self, mapper):
|
||||||
images_resource = images.create_resource()
|
environments_resource = environments.create_resource()
|
||||||
|
mapper.resource("environment", "environments", controller=environments_resource)
|
||||||
mapper.resource("image", "images", controller=images_resource,
|
|
||||||
collection={'detail': 'GET'})
|
|
||||||
mapper.connect("/", controller=images_resource, action="index")
|
|
||||||
mapper.connect("/images/{id}", controller=images_resource,
|
|
||||||
action="meta", conditions=dict(method=["HEAD"]))
|
|
||||||
|
|
||||||
members_resource = members.create_resource()
|
|
||||||
|
|
||||||
mapper.resource("member", "members", controller=members_resource,
|
|
||||||
parent_resource=dict(member_name='image',
|
|
||||||
collection_name='images'))
|
|
||||||
mapper.connect("/shared-images/{id}",
|
|
||||||
controller=members_resource,
|
|
||||||
action="index_shared_images")
|
|
||||||
mapper.connect("/images/{image_id}/members",
|
|
||||||
controller=members_resource,
|
|
||||||
action="update_all",
|
|
||||||
conditions=dict(method=["PUT"]))
|
|
||||||
|
|
||||||
super(API, self).__init__(mapper)
|
super(API, self).__init__(mapper)
|
||||||
|
@ -29,71 +29,40 @@ import sys
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from paste import deploy
|
from paste import deploy
|
||||||
|
|
||||||
from glance.version import version_info as version
|
from portas.version import version_info as version
|
||||||
|
|
||||||
paste_deploy_opts = [
|
paste_deploy_opts = [
|
||||||
cfg.StrOpt('flavor'),
|
cfg.StrOpt('flavor'),
|
||||||
cfg.StrOpt('config_file'),
|
cfg.StrOpt('config_file'),
|
||||||
]
|
]
|
||||||
common_opts = [
|
|
||||||
cfg.BoolOpt('allow_additional_image_properties', default=True,
|
bind_opts = [
|
||||||
help=_('Whether to allow users to specify image properties '
|
cfg.StrOpt('bind_host', default='localhost'),
|
||||||
'beyond what the image schema provides')),
|
cfg.IntOpt('bind_port'),
|
||||||
cfg.StrOpt('data_api', default='glance.db.sqlalchemy.api',
|
|
||||||
help=_('Python module path of data access API')),
|
|
||||||
cfg.IntOpt('limit_param_default', default=25,
|
|
||||||
help=_('Default value for the number of items returned by a '
|
|
||||||
'request if not specified explicitly in the request')),
|
|
||||||
cfg.IntOpt('api_limit_max', default=1000,
|
|
||||||
help=_('Maximum permissible number of items that could be '
|
|
||||||
'returned by a request')),
|
|
||||||
cfg.BoolOpt('show_image_direct_url', default=False,
|
|
||||||
help=_('Whether to include the backend image storage location '
|
|
||||||
'in image properties. Revealing storage location can be a '
|
|
||||||
'security risk, so use this setting with caution!')),
|
|
||||||
cfg.IntOpt('image_size_cap', default=1099511627776,
|
|
||||||
help=_("Maximum size of image a user can upload in bytes. "
|
|
||||||
"Defaults to 1099511627776 bytes (1 TB).")),
|
|
||||||
cfg.BoolOpt('enable_v1_api', default=True,
|
|
||||||
help=_("Deploy the v1 OpenStack Images API. ")),
|
|
||||||
cfg.BoolOpt('enable_v2_api', default=True,
|
|
||||||
help=_("Deploy the v2 OpenStack Images API. ")),
|
|
||||||
cfg.StrOpt('pydev_worker_debug_host', default=None,
|
|
||||||
help=_('The hostname/IP of the pydev process listening for '
|
|
||||||
'debug connections')),
|
|
||||||
cfg.IntOpt('pydev_worker_debug_port', default=5678,
|
|
||||||
help=_('The port on which a pydev process is listening for '
|
|
||||||
'connections.')),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.register_opts(paste_deploy_opts, group='paste_deploy')
|
CONF.register_opts(paste_deploy_opts, group='paste_deploy')
|
||||||
CONF.register_opts(common_opts)
|
CONF.register_opts(bind_opts)
|
||||||
|
|
||||||
CONF.import_opt('verbose', 'glance.openstack.common.log')
|
CONF.import_opt('verbose', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('debug', 'glance.openstack.common.log')
|
CONF.import_opt('debug', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('log_dir', 'glance.openstack.common.log')
|
CONF.import_opt('log_dir', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('log_file', 'glance.openstack.common.log')
|
CONF.import_opt('log_file', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('log_config', 'glance.openstack.common.log')
|
CONF.import_opt('log_config', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('log_format', 'glance.openstack.common.log')
|
CONF.import_opt('log_format', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('log_date_format', 'glance.openstack.common.log')
|
CONF.import_opt('log_date_format', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('use_syslog', 'glance.openstack.common.log')
|
CONF.import_opt('use_syslog', 'portas.openstack.common.log')
|
||||||
CONF.import_opt('syslog_log_facility', 'glance.openstack.common.log')
|
CONF.import_opt('syslog_log_facility', 'portas.openstack.common.log')
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args=None, usage=None, default_config_files=None):
|
def parse_args(args=None, usage=None, default_config_files=None):
|
||||||
CONF(args=args,
|
CONF(args=args,
|
||||||
project='glance',
|
project='portas',
|
||||||
version=version.cached_version_string(),
|
version=version.cached_version_string(),
|
||||||
usage=usage,
|
usage=usage,
|
||||||
default_config_files=default_config_files)
|
default_config_files=default_config_files)
|
||||||
|
|
||||||
|
|
||||||
def parse_cache_args(args=None):
|
|
||||||
config_files = cfg.find_config_files(project='glance', prog='glance-cache')
|
|
||||||
parse_args(args=args, default_config_files=config_files)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
"""
|
"""
|
||||||
Sets up the logging options for a log with supplied name
|
Sets up the logging options for a log with supplied name
|
||||||
|
@ -16,21 +16,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Glance exception subclasses"""
|
"""Portas exception subclasses"""
|
||||||
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
_FATAL_EXCEPTION_FORMAT_ERRORS = False
|
_FATAL_EXCEPTION_FORMAT_ERRORS = False
|
||||||
|
|
||||||
|
|
||||||
class RedirectException(Exception):
|
class PortasException(Exception):
|
||||||
def __init__(self, url):
|
|
||||||
self.url = urlparse.urlparse(url)
|
|
||||||
|
|
||||||
|
|
||||||
class GlanceException(Exception):
|
|
||||||
"""
|
"""
|
||||||
Base Glance Exception
|
Base Portas Exception
|
||||||
|
|
||||||
To correctly use this class, inherit from it and define
|
To correctly use this class, inherit from it and define
|
||||||
a 'message' property. That message will get printf'd
|
a 'message' property. That message will get printf'd
|
||||||
@ -50,223 +43,14 @@ class GlanceException(Exception):
|
|||||||
# at least get the core message out if something happened
|
# at least get the core message out if something happened
|
||||||
pass
|
pass
|
||||||
|
|
||||||
super(GlanceException, self).__init__(message)
|
super(PortasException, self).__init__(message)
|
||||||
|
|
||||||
|
class SchemaLoadError(PortasException):
|
||||||
class MissingArgumentError(GlanceException):
|
|
||||||
message = _("Missing required argument.")
|
|
||||||
|
|
||||||
|
|
||||||
class MissingCredentialError(GlanceException):
|
|
||||||
message = _("Missing required credential: %(required)s")
|
|
||||||
|
|
||||||
|
|
||||||
class BadAuthStrategy(GlanceException):
|
|
||||||
message = _("Incorrect auth strategy, expected \"%(expected)s\" but "
|
|
||||||
"received \"%(received)s\"")
|
|
||||||
|
|
||||||
|
|
||||||
class NotFound(GlanceException):
|
|
||||||
message = _("An object with the specified identifier was not found.")
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownScheme(GlanceException):
|
|
||||||
message = _("Unknown scheme '%(scheme)s' found in URI")
|
|
||||||
|
|
||||||
|
|
||||||
class BadStoreUri(GlanceException):
|
|
||||||
message = _("The Store URI was malformed.")
|
|
||||||
|
|
||||||
|
|
||||||
class Duplicate(GlanceException):
|
|
||||||
message = _("An object with the same identifier already exists.")
|
|
||||||
|
|
||||||
|
|
||||||
class StorageFull(GlanceException):
|
|
||||||
message = _("There is not enough disk space on the image storage media.")
|
|
||||||
|
|
||||||
|
|
||||||
class StorageWriteDenied(GlanceException):
|
|
||||||
message = _("Permission to write image storage media denied.")
|
|
||||||
|
|
||||||
|
|
||||||
class AuthBadRequest(GlanceException):
|
|
||||||
message = _("Connect error/bad request to Auth service at URL %(url)s.")
|
|
||||||
|
|
||||||
|
|
||||||
class AuthUrlNotFound(GlanceException):
|
|
||||||
message = _("Auth service at URL %(url)s not found.")
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationFailure(GlanceException):
|
|
||||||
message = _("Authorization failed.")
|
|
||||||
|
|
||||||
|
|
||||||
class NotAuthenticated(GlanceException):
|
|
||||||
message = _("You are not authenticated.")
|
|
||||||
|
|
||||||
|
|
||||||
class Forbidden(GlanceException):
|
|
||||||
message = _("You are not authorized to complete this action.")
|
|
||||||
|
|
||||||
|
|
||||||
class ForbiddenPublicImage(Forbidden):
|
|
||||||
message = _("You are not authorized to complete this action.")
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectedImageDelete(Forbidden):
|
|
||||||
message = _("Image %(image_id)s is protected and cannot be deleted.")
|
|
||||||
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): here for backwards-compatability, need to deprecate.
|
|
||||||
class NotAuthorized(Forbidden):
|
|
||||||
message = _("You are not authorized to complete this action.")
|
|
||||||
|
|
||||||
|
|
||||||
class Invalid(GlanceException):
|
|
||||||
message = _("Data supplied was not valid.")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidSortKey(Invalid):
|
|
||||||
message = _("Sort key supplied was not valid.")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidFilterRangeValue(Invalid):
|
|
||||||
message = _("Unable to filter using the specified range.")
|
|
||||||
|
|
||||||
|
|
||||||
class ReadonlyProperty(Forbidden):
|
|
||||||
message = _("Attribute '%(property)s' is read-only.")
|
|
||||||
|
|
||||||
|
|
||||||
class ReservedProperty(Forbidden):
|
|
||||||
message = _("Attribute '%(property)s' is reserved.")
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationRedirect(GlanceException):
|
|
||||||
message = _("Redirecting to %(uri)s for authorization.")
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseMigrationError(GlanceException):
|
|
||||||
message = _("There was an error migrating the database.")
|
|
||||||
|
|
||||||
|
|
||||||
class ClientConnectionError(GlanceException):
|
|
||||||
message = _("There was an error connecting to a server")
|
|
||||||
|
|
||||||
|
|
||||||
class ClientConfigurationError(GlanceException):
|
|
||||||
message = _("There was an error configuring the client.")
|
|
||||||
|
|
||||||
|
|
||||||
class MultipleChoices(GlanceException):
|
|
||||||
message = _("The request returned a 302 Multiple Choices. This generally "
|
|
||||||
"means that you have not included a version indicator in a "
|
|
||||||
"request URI.\n\nThe body of response returned:\n%(body)s")
|
|
||||||
|
|
||||||
|
|
||||||
class LimitExceeded(GlanceException):
|
|
||||||
message = _("The request returned a 413 Request Entity Too Large. This "
|
|
||||||
"generally means that rate limiting or a quota threshold was "
|
|
||||||
"breached.\n\nThe response body:\n%(body)s")
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
|
||||||
else None)
|
|
||||||
super(LimitExceeded, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceUnavailable(GlanceException):
|
|
||||||
message = _("The request returned 503 Service Unavilable. This "
|
|
||||||
"generally occurs on service overload or other transient "
|
|
||||||
"outage.")
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
|
||||||
else None)
|
|
||||||
super(ServiceUnavailable, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerError(GlanceException):
|
|
||||||
message = _("The request returned 500 Internal Server Error.")
|
|
||||||
|
|
||||||
|
|
||||||
class UnexpectedStatus(GlanceException):
|
|
||||||
message = _("The request returned an unexpected status: %(status)s."
|
|
||||||
"\n\nThe response body:\n%(body)s")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidContentType(GlanceException):
|
|
||||||
message = _("Invalid content type %(content_type)s")
|
|
||||||
|
|
||||||
|
|
||||||
class BadRegistryConnectionConfiguration(GlanceException):
|
|
||||||
message = _("Registry was not configured correctly on API server. "
|
|
||||||
"Reason: %(reason)s")
|
|
||||||
|
|
||||||
|
|
||||||
class BadStoreConfiguration(GlanceException):
|
|
||||||
message = _("Store %(store_name)s could not be configured correctly. "
|
|
||||||
"Reason: %(reason)s")
|
|
||||||
|
|
||||||
|
|
||||||
class BadDriverConfiguration(GlanceException):
|
|
||||||
message = _("Driver %(driver_name)s could not be configured correctly. "
|
|
||||||
"Reason: %(reason)s")
|
|
||||||
|
|
||||||
|
|
||||||
class StoreDeleteNotSupported(GlanceException):
|
|
||||||
message = _("Deleting images from this store is not supported.")
|
|
||||||
|
|
||||||
|
|
||||||
class StoreAddDisabled(GlanceException):
|
|
||||||
message = _("Configuration for store failed. Adding images to this "
|
|
||||||
"store is disabled.")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidNotifierStrategy(GlanceException):
|
|
||||||
message = _("'%(strategy)s' is not an available notifier strategy.")
|
|
||||||
|
|
||||||
|
|
||||||
class MaxRedirectsExceeded(GlanceException):
|
|
||||||
message = _("Maximum redirects (%(redirects)s) was exceeded.")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidRedirect(GlanceException):
|
|
||||||
message = _("Received invalid HTTP redirect.")
|
|
||||||
|
|
||||||
|
|
||||||
class NoServiceEndpoint(GlanceException):
|
|
||||||
message = _("Response from Keystone does not contain a Glance endpoint.")
|
|
||||||
|
|
||||||
|
|
||||||
class RegionAmbiguity(GlanceException):
|
|
||||||
message = _("Multiple 'image' service matches for region %(region)s. This "
|
|
||||||
"generally means that a region is required and you have not "
|
|
||||||
"supplied one.")
|
|
||||||
|
|
||||||
|
|
||||||
class WorkerCreationFailure(GlanceException):
|
|
||||||
message = _("Server worker creation failed: %(reason)s.")
|
|
||||||
|
|
||||||
|
|
||||||
class SchemaLoadError(GlanceException):
|
|
||||||
message = _("Unable to load schema: %(reason)s")
|
message = _("Unable to load schema: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class InvalidObject(GlanceException):
|
class InvalidObject(PortasException):
|
||||||
message = _("Provided object does not match schema "
|
message = _("Provided object does not match schema "
|
||||||
"'%(schema)s': %(reason)s")
|
"'%(schema)s': %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedHeaderFeature(GlanceException):
|
|
||||||
message = _("Provided header feature is unsupported: %(feature)s")
|
|
||||||
|
|
||||||
|
|
||||||
class InUseByStore(GlanceException):
|
|
||||||
message = _("The image cannot be deleted because it is in use through "
|
|
||||||
"the backend store outside of Glance.")
|
|
||||||
|
|
||||||
|
|
||||||
class ImageSizeLimitExceeded(GlanceException):
|
|
||||||
message = _("The provided image is too large.")
|
|
||||||
|
@ -1 +1,12 @@
|
|||||||
__author__ = 'sad'
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
sql_connection_opt = cfg.StrOpt('sql_connection',
|
||||||
|
default='sqlite:///portas.sqlite',
|
||||||
|
secret=True,
|
||||||
|
metavar='CONNECTION',
|
||||||
|
help='A valid SQLAlchemy connection '
|
||||||
|
'string for the metadata database. '
|
||||||
|
'Default: %(default)s')
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opt(sql_connection_opt)
|
@ -1 +1,18 @@
|
|||||||
__author__ = 'sad'
|
from portas.db.models import Environment
|
||||||
|
from portas.db.session import get_session
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentRepository(object):
|
||||||
|
def list(self):
|
||||||
|
session = get_session()
|
||||||
|
return session.query(Environment).all()
|
||||||
|
|
||||||
|
def add(self, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
env = Environment()
|
||||||
|
env.update(values)
|
||||||
|
session.add(env)
|
||||||
|
return env
|
||||||
|
|
||||||
|
# def update(self, env):
|
@ -1,7 +1,7 @@
|
|||||||
[db_settings]
|
[db_settings]
|
||||||
# Used to identify which repository this database is versioned under.
|
# Used to identify which repository this database is versioned under.
|
||||||
# You can use the name of your project.
|
# You can use the name of your project.
|
||||||
repository_id=Glance Migrations
|
repository_id=Portas Migrations
|
||||||
|
|
||||||
# The name of the database table used to track the schema version.
|
# The name of the database table used to track the schema version.
|
||||||
# This name shouldn't already be used by your project.
|
# This name shouldn't already be used by your project.
|
||||||
|
@ -1,36 +1,29 @@
|
|||||||
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
||||||
from sqlalchemy.types import Integer, String, Text, DateTime
|
from sqlalchemy.types import String, Text, DateTime
|
||||||
|
|
||||||
|
|
||||||
meta = MetaData()
|
meta = MetaData()
|
||||||
|
|
||||||
Table('datacenter', meta,
|
Table('environment', meta,
|
||||||
Column('id', String(32), primary_key=True),
|
Column('id', String(32), primary_key=True),
|
||||||
Column('name', String(255)),
|
Column('name', String(255)),
|
||||||
Column('type', String(255)),
|
Column('created', DateTime(), nullable=False),
|
||||||
Column('version', String(255)),
|
Column('updated', DateTime(), nullable=False),
|
||||||
Column('tenant_id',String(100)),
|
Column('tenant_id', String(36)),
|
||||||
Column('KMS', String(80)),
|
Column('description', Text()),
|
||||||
Column('WSUS', String(80)),
|
|
||||||
Column('extra', Text()),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Table('service', meta,
|
Table('service', meta,
|
||||||
Column('id', String(32), primary_key=True),
|
Column('id', String(32), primary_key=True),
|
||||||
Column('datacenter_id', String(32), ForeignKey('datacenter.id')),
|
Column('name', String(255)),
|
||||||
Column('name', String(255)),
|
Column('type', String(40)),
|
||||||
Column('type', String(40)),
|
Column('environment_id', String(32), ForeignKey('environment.id')),
|
||||||
Column('status', String(255)),
|
Column('created', DateTime, nullable=False),
|
||||||
Column('tenant_id', String(40)),
|
Column('updated', DateTime, nullable=False),
|
||||||
Column('created_at', DateTime, nullable=False),
|
Column('description', Text()),
|
||||||
Column('updated_at', DateTime, nullable=False),
|
|
||||||
Column('deployed', String(40)),
|
|
||||||
Column('vm_id',String(40)),
|
|
||||||
Column('extra', Text()),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade(migrate_engine):
|
def upgrade(migrate_engine):
|
||||||
meta.bind = migrate_engine
|
meta.bind = migrate_engine
|
||||||
meta.create_all()
|
meta.create_all()
|
||||||
|
@ -1 +0,0 @@
|
|||||||
# template repository default versions module
|
|
@ -17,19 +17,20 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
SQLAlchemy models for glance data
|
SQLAlchemy models for portas data
|
||||||
"""
|
"""
|
||||||
|
import anyjson
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String, BigInteger
|
from sqlalchemy import Column, String, BigInteger, TypeDecorator, ForeignKey
|
||||||
from sqlalchemy.ext.compiler import compiles
|
from sqlalchemy.ext.compiler import compiles
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy import ForeignKey, DateTime, Boolean, Text
|
from sqlalchemy import DateTime, Text
|
||||||
from sqlalchemy.orm import relationship, backref, object_mapper
|
from sqlalchemy.orm import relationship, backref, object_mapper
|
||||||
from sqlalchemy import UniqueConstraint
|
|
||||||
|
|
||||||
import glance.db.sqlalchemy.api
|
from portas.openstack.common import timeutils
|
||||||
from glance.openstack.common import timeutils
|
from portas.openstack.common import uuidutils
|
||||||
from glance.openstack.common import uuidutils
|
|
||||||
|
from portas.db.session import get_session
|
||||||
|
|
||||||
BASE = declarative_base()
|
BASE = declarative_base()
|
||||||
|
|
||||||
@ -40,22 +41,16 @@ def compile_big_int_sqlite(type_, compiler, **kw):
|
|||||||
|
|
||||||
|
|
||||||
class ModelBase(object):
|
class ModelBase(object):
|
||||||
"""Base class for Nova and Glance Models"""
|
__protected_attributes__ = {"created", "updated"}
|
||||||
__table_args__ = {'mysql_engine': 'InnoDB'}
|
|
||||||
__table_initialized__ = False
|
|
||||||
__protected_attributes__ = set([
|
|
||||||
"created_at", "updated_at", "deleted_at", "deleted"])
|
|
||||||
|
|
||||||
created_at = Column(DateTime, default=timeutils.utcnow,
|
created = Column(DateTime, default=timeutils.utcnow,
|
||||||
nullable=False)
|
nullable=False)
|
||||||
updated_at = Column(DateTime, default=timeutils.utcnow,
|
updated = Column(DateTime, default=timeutils.utcnow,
|
||||||
nullable=False, onupdate=timeutils.utcnow)
|
nullable=False, onupdate=timeutils.utcnow)
|
||||||
deleted_at = Column(DateTime)
|
|
||||||
deleted = Column(Boolean, nullable=False, default=False)
|
|
||||||
|
|
||||||
def save(self, session=None):
|
def save(self, session=None):
|
||||||
"""Save this object"""
|
"""Save this object"""
|
||||||
session = session or glance.db.sqlalchemy.api.get_session()
|
session = session or get_session()
|
||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
@ -94,80 +89,55 @@ class ModelBase(object):
|
|||||||
return self.__dict__.items()
|
return self.__dict__.items()
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return self.__dict__.copy()
|
dictionary = self.__dict__.copy()
|
||||||
|
return {k: v for k, v in dictionary.iteritems() if k != '_sa_instance_state'}
|
||||||
|
|
||||||
|
|
||||||
class Image(BASE, ModelBase):
|
class JsonBlob(TypeDecorator):
|
||||||
"""Represents an image in the datastore"""
|
impl = Text
|
||||||
__tablename__ = 'images'
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
return anyjson.serialize(value)
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
return anyjson.deserialize(value)
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(BASE, ModelBase):
|
||||||
|
"""Represents a Environment in the metadata-store"""
|
||||||
|
__tablename__ = 'environment'
|
||||||
|
|
||||||
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
||||||
name = Column(String(255))
|
name = Column(String(255))
|
||||||
disk_format = Column(String(20))
|
tenant_id = Column(String(36))
|
||||||
container_format = Column(String(20))
|
description = Column(JsonBlob())
|
||||||
size = Column(BigInteger)
|
|
||||||
status = Column(String(30), nullable=False)
|
|
||||||
is_public = Column(Boolean, nullable=False, default=False)
|
|
||||||
checksum = Column(String(32))
|
|
||||||
min_disk = Column(Integer(), nullable=False, default=0)
|
|
||||||
min_ram = Column(Integer(), nullable=False, default=0)
|
|
||||||
owner = Column(String(255))
|
|
||||||
protected = Column(Boolean, nullable=False, default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class ImageProperty(BASE, ModelBase):
|
class Service(BASE, ModelBase):
|
||||||
"""Represents an image properties in the datastore"""
|
"""
|
||||||
__tablename__ = 'image_properties'
|
Represents an instance of service.
|
||||||
__table_args__ = (UniqueConstraint('image_id', 'name'), {})
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
:var name: string
|
||||||
image_id = Column(String(36), ForeignKey('images.id'),
|
:var type: string - type of service (e.g. Active Directory)
|
||||||
nullable=False)
|
"""
|
||||||
image = relationship(Image, backref=backref('properties'))
|
|
||||||
|
|
||||||
|
__tablename__ = 'service'
|
||||||
|
|
||||||
|
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
||||||
name = Column(String(255), index=True, nullable=False)
|
name = Column(String(255), index=True, nullable=False)
|
||||||
value = Column(Text)
|
type = Column(String(255), index=True, nullable=False)
|
||||||
|
environment_id = Column(String(36), ForeignKey('environment.id'))
|
||||||
|
environment = relationship(Environment,
|
||||||
class ImageTag(BASE, ModelBase):
|
backref=backref('service', order_by=id),
|
||||||
"""Represents an image tag in the datastore"""
|
uselist=False)
|
||||||
__tablename__ = 'image_tags'
|
description = Column(JsonBlob())
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, nullable=False)
|
|
||||||
image_id = Column(String(36), ForeignKey('images.id'), nullable=False)
|
|
||||||
value = Column(String(255), nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class ImageLocation(BASE, ModelBase):
|
|
||||||
"""Represents an image location in the datastore"""
|
|
||||||
__tablename__ = 'image_locations'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, nullable=False)
|
|
||||||
image_id = Column(String(36), ForeignKey('images.id'), nullable=False)
|
|
||||||
image = relationship(Image, backref=backref('locations'))
|
|
||||||
value = Column(Text(), nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class ImageMember(BASE, ModelBase):
|
|
||||||
"""Represents an image members in the datastore"""
|
|
||||||
__tablename__ = 'image_members'
|
|
||||||
__table_args__ = (UniqueConstraint('image_id', 'member'), {})
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
image_id = Column(String(36), ForeignKey('images.id'),
|
|
||||||
nullable=False)
|
|
||||||
image = relationship(Image, backref=backref('members'))
|
|
||||||
|
|
||||||
member = Column(String(255), nullable=False)
|
|
||||||
can_share = Column(Boolean, nullable=False, default=False)
|
|
||||||
status = Column(String(20), nullable=False, default="pending")
|
|
||||||
|
|
||||||
|
|
||||||
def register_models(engine):
|
def register_models(engine):
|
||||||
"""
|
"""
|
||||||
Creates database tables for all models with the given engine
|
Creates database tables for all models with the given engine
|
||||||
"""
|
"""
|
||||||
models = (Image, ImageProperty, ImageMember)
|
models = (Environment, Service)
|
||||||
for model in models:
|
for model in models:
|
||||||
model.metadata.create_all(engine)
|
model.metadata.create_all(engine)
|
||||||
|
|
||||||
@ -176,6 +146,6 @@ def unregister_models(engine):
|
|||||||
"""
|
"""
|
||||||
Drops database tables for all models with the given engine
|
Drops database tables for all models with the given engine
|
||||||
"""
|
"""
|
||||||
models = (Image, ImageProperty)
|
models = (Environment, Service)
|
||||||
for model in models:
|
for model in models:
|
||||||
model.metadata.drop_all(engine)
|
model.metadata.drop_all(engine)
|
||||||
|
@ -30,15 +30,10 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
from sqlalchemy.pool import NullPool
|
from sqlalchemy.pool import NullPool
|
||||||
from sqlalchemy.exc import DisconnectionError
|
from sqlalchemy.exc import DisconnectionError
|
||||||
|
|
||||||
from windc.common import cfg
|
from portas.common.config import CONF as conf
|
||||||
from windc.db import migrate_repo
|
|
||||||
|
|
||||||
|
from portas.db import migrate_repo
|
||||||
|
|
||||||
DB_GROUP_NAME = 'sql'
|
|
||||||
DB_OPTIONS = (
|
|
||||||
cfg.IntOpt('idle_timeout', default=3600),
|
|
||||||
cfg.StrOpt('connection', default='sqlite:///windc.sqlite'),
|
|
||||||
)
|
|
||||||
|
|
||||||
MAKER = None
|
MAKER = None
|
||||||
ENGINE = None
|
ENGINE = None
|
||||||
@ -73,27 +68,26 @@ class MySQLPingListener(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_session(conf, autocommit=True, expire_on_commit=False):
|
def get_session(autocommit=True, expire_on_commit=False):
|
||||||
"""Return a SQLAlchemy session."""
|
"""Return a SQLAlchemy session."""
|
||||||
global MAKER
|
global MAKER
|
||||||
|
|
||||||
if MAKER is None:
|
if MAKER is None:
|
||||||
MAKER = sessionmaker(autocommit=autocommit,
|
MAKER = sessionmaker(autocommit=autocommit,
|
||||||
expire_on_commit=expire_on_commit)
|
expire_on_commit=expire_on_commit)
|
||||||
engine = get_engine(conf)
|
engine = get_engine()
|
||||||
MAKER.configure(bind=engine)
|
MAKER.configure(bind=engine)
|
||||||
session = MAKER()
|
session = MAKER()
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
def get_engine(conf):
|
def get_engine():
|
||||||
"""Return a SQLAlchemy engine."""
|
"""Return a SQLAlchemy engine."""
|
||||||
global ENGINE
|
global ENGINE
|
||||||
|
|
||||||
register_conf_opts(conf)
|
connection_url = make_url(conf.sql_connection)
|
||||||
connection_url = make_url(conf.sql.connection)
|
|
||||||
if ENGINE is None or not ENGINE.url == connection_url:
|
if ENGINE is None or not ENGINE.url == connection_url:
|
||||||
engine_args = {'pool_recycle': conf.sql.idle_timeout,
|
engine_args = {'pool_recycle': 3600,
|
||||||
'echo': False,
|
'echo': False,
|
||||||
'convert_unicode': True
|
'convert_unicode': True
|
||||||
}
|
}
|
||||||
@ -101,22 +95,16 @@ def get_engine(conf):
|
|||||||
engine_args['poolclass'] = NullPool
|
engine_args['poolclass'] = NullPool
|
||||||
if 'mysql' in connection_url.drivername:
|
if 'mysql' in connection_url.drivername:
|
||||||
engine_args['listeners'] = [MySQLPingListener()]
|
engine_args['listeners'] = [MySQLPingListener()]
|
||||||
ENGINE = create_engine(conf.sql.connection, **engine_args)
|
ENGINE = create_engine(conf.sql_connection, **engine_args)
|
||||||
|
|
||||||
|
sync()
|
||||||
return ENGINE
|
return ENGINE
|
||||||
|
|
||||||
|
|
||||||
def register_conf_opts(conf, options=DB_OPTIONS, group=DB_GROUP_NAME):
|
def sync():
|
||||||
"""Register database options."""
|
|
||||||
|
|
||||||
conf.register_group(cfg.OptGroup(name=group))
|
|
||||||
conf.register_opts(options, group=group)
|
|
||||||
|
|
||||||
|
|
||||||
def sync(conf):
|
|
||||||
register_conf_opts(conf)
|
|
||||||
repo_path = os.path.abspath(os.path.dirname(migrate_repo.__file__))
|
repo_path = os.path.abspath(os.path.dirname(migrate_repo.__file__))
|
||||||
try:
|
try:
|
||||||
versioning_api.upgrade(conf.sql.connection, repo_path)
|
versioning_api.upgrade(conf.sql_connection, repo_path)
|
||||||
except versioning_exceptions.DatabaseNotControlledError:
|
except versioning_exceptions.DatabaseNotControlledError:
|
||||||
versioning_api.version_control(conf.sql.connection, repo_path)
|
versioning_api.version_control(conf.sql_connection, repo_path)
|
||||||
versioning_api.upgrade(conf.sql.connection, repo_path)
|
versioning_api.upgrade(conf.sql_connection, repo_path)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -44,21 +44,6 @@ class Schema(object):
|
|||||||
def _filter_func(properties, key):
|
def _filter_func(properties, key):
|
||||||
return key in properties
|
return key in properties
|
||||||
|
|
||||||
def merge_properties(self, properties):
|
|
||||||
# Ensure custom props aren't attempting to override base props
|
|
||||||
original_keys = set(self.properties.keys())
|
|
||||||
new_keys = set(properties.keys())
|
|
||||||
intersecting_keys = original_keys.intersection(new_keys)
|
|
||||||
conflicting_keys = [k for k in intersecting_keys
|
|
||||||
if self.properties[k] != properties[k]]
|
|
||||||
if len(conflicting_keys) > 0:
|
|
||||||
props = ', '.join(conflicting_keys)
|
|
||||||
reason = _("custom properties (%(props)s) conflict "
|
|
||||||
"with base properties")
|
|
||||||
raise exception.SchemaLoadError(reason=reason % {'props': props})
|
|
||||||
|
|
||||||
self.properties.update(properties)
|
|
||||||
|
|
||||||
def raw(self):
|
def raw(self):
|
||||||
raw = {
|
raw = {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
@ -70,17 +55,6 @@ class Schema(object):
|
|||||||
return raw
|
return raw
|
||||||
|
|
||||||
|
|
||||||
class PermissiveSchema(Schema):
|
|
||||||
@staticmethod
|
|
||||||
def _filter_func(properties, key):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def raw(self):
|
|
||||||
raw = super(PermissiveSchema, self).raw()
|
|
||||||
raw['additionalProperties'] = {'type': 'string'}
|
|
||||||
return raw
|
|
||||||
|
|
||||||
|
|
||||||
class CollectionSchema(object):
|
class CollectionSchema(object):
|
||||||
|
|
||||||
def __init__(self, name, item_schema):
|
def __init__(self, name, item_schema):
|
||||||
|
@ -3,4 +3,4 @@ import unittest
|
|||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
def test(self):
|
def test(self):
|
||||||
assert False
|
assert True
|
||||||
|
@ -15,6 +15,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from glance.openstack.common import version as common_version
|
from portas.openstack.common import version as common_version
|
||||||
|
|
||||||
version_info = common_version.VersionInfo('glance')
|
version_info = common_version.VersionInfo('portas')
|
||||||
|
@ -28,8 +28,8 @@ function process_option {
|
|||||||
-P|--no-pep8) let no_pep8=1;;
|
-P|--no-pep8) let no_pep8=1;;
|
||||||
-f|--force) let force=1;;
|
-f|--force) let force=1;;
|
||||||
-u|--update) update=1;;
|
-u|--update) update=1;;
|
||||||
--unittests-only) noseopts="$noseopts --exclude-dir=glance/tests/functional";;
|
--unittests-only) noseopts="$noseopts --exclude-dir=portas/tests/functional";;
|
||||||
-c|--coverage) noseopts="$noseopts --with-coverage --cover-package=glance";;
|
-c|--coverage) noseopts="$noseopts --with-coverage --cover-package=portas";;
|
||||||
-*) noseopts="$noseopts $1";;
|
-*) noseopts="$noseopts $1";;
|
||||||
*) noseargs="$noseargs $1"
|
*) noseargs="$noseargs $1"
|
||||||
esac
|
esac
|
||||||
|
@ -7,3 +7,27 @@ source-dir = doc/source
|
|||||||
tag_build =
|
tag_build =
|
||||||
tag_date = 0
|
tag_date = 0
|
||||||
tag_svn_revision = 0
|
tag_svn_revision = 0
|
||||||
|
|
||||||
|
[compile_catalog]
|
||||||
|
directory = portas/locale
|
||||||
|
domain = portas
|
||||||
|
|
||||||
|
[update_catalog]
|
||||||
|
domain = portas
|
||||||
|
output_dir = portas/locale
|
||||||
|
input_file = portas/locale/portas.pot
|
||||||
|
|
||||||
|
[extract_messages]
|
||||||
|
keywords = _ gettext ngettext l_ lazy_gettext
|
||||||
|
mapping_file = babel.cfg
|
||||||
|
output_file = portas/locale/portas.pot
|
||||||
|
|
||||||
|
[nosetests]
|
||||||
|
# NOTE(jkoelker) To run the test suite under nose install the following
|
||||||
|
# coverage http://pypi.python.org/pypi/coverage
|
||||||
|
# tissue http://pypi.python.org/pypi/tissue (pep8 checker)
|
||||||
|
# openstack-nose https://github.com/jkoelker/openstack-nose
|
||||||
|
verbosity=2
|
||||||
|
cover-package = portas
|
||||||
|
cover-html = true
|
||||||
|
cover-erase = true
|
@ -25,7 +25,8 @@ project = 'portas'
|
|||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name=project,
|
name=project,
|
||||||
version=setup.get_version(project, '2013.1'),
|
version=setup.get_version(project, '2013.1'),
|
||||||
description='The Portas project provides a simple WSGI server for Windows Environment Management',
|
description='The Portas project provides a simple WSGI server for Windows '
|
||||||
|
'Environment Management',
|
||||||
license='Apache License (2.0)',
|
license='Apache License (2.0)',
|
||||||
author='OpenStack',
|
author='OpenStack',
|
||||||
author_email='openstack@lists.launchpad.net',
|
author_email='openstack@lists.launchpad.net',
|
||||||
@ -43,6 +44,7 @@ setuptools.setup(
|
|||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Environment :: No Input/Output (Daemon)',
|
'Environment :: No Input/Output (Daemon)',
|
||||||
'Environment :: OpenStack',
|
'Environment :: OpenStack',
|
||||||
],
|
],
|
||||||
scripts=['bin/portas-api'],
|
scripts=['bin/portas-api'],
|
||||||
py_modules=[])
|
py_modules=[]
|
||||||
|
)
|
||||||
|
@ -1,23 +1,30 @@
|
|||||||
# The greenlet package must be compiled with gcc and needs
|
Babel
|
||||||
# the Python.h headers. Make sure you install the python-dev
|
SQLAlchemy>=0.7,<=0.7.9
|
||||||
# package to get the right headers...
|
|
||||||
greenlet>=0.3.1
|
|
||||||
|
|
||||||
SQLAlchemy<=0.7.9
|
|
||||||
anyjson
|
anyjson
|
||||||
eventlet>=0.9.12
|
eventlet>=0.9.12
|
||||||
PasteDeploy
|
PasteDeploy
|
||||||
Routes
|
routes
|
||||||
webob==1.0.8
|
WebOb>=1.2
|
||||||
wsgiref
|
wsgiref
|
||||||
argparse
|
argparse
|
||||||
sqlalchemy-migrate>=0.7.2
|
boto
|
||||||
|
sqlalchemy-migrate>=0.7
|
||||||
httplib2
|
httplib2
|
||||||
kombu
|
kombu
|
||||||
|
pycrypto>=2.1.0alpha1
|
||||||
iso8601>=0.1.4
|
iso8601>=0.1.4
|
||||||
PyChef
|
|
||||||
|
# Note you will need gcc buildtools installed and must
|
||||||
|
# have installed libxml headers for lxml to be successfully
|
||||||
|
# installed using pip, therefore you will need to install the
|
||||||
|
# libxml2-dev and libxslt-dev Ubuntu packages.
|
||||||
|
lxml
|
||||||
|
|
||||||
# For paste.util.template used in keystone.common.template
|
# For paste.util.template used in keystone.common.template
|
||||||
Paste
|
Paste
|
||||||
|
|
||||||
passlib
|
passlib
|
||||||
puka
|
jsonschema
|
||||||
|
python-keystoneclient>=0.2.0
|
||||||
|
|
||||||
|
http://tarballs.openstack.org/oslo-config/oslo-config-2013.1b4.tar.gz#egg=oslo-config
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
# Packages needed for dev testing
|
||||||
|
distribute>=0.6.24
|
||||||
|
|
||||||
|
# Needed for testing
|
||||||
|
coverage
|
||||||
|
fixtures>=0.3.12
|
||||||
|
mox
|
||||||
|
nose
|
||||||
|
nose-exclude
|
||||||
|
openstack.nose_plugin>=0.7
|
||||||
|
nosehtmloutput>=0.0.3
|
||||||
|
pep8==1.3.3
|
||||||
|
sphinx>=1.1.2
|
||||||
|
requests
|
||||||
|
testtools>=0.9.22
|
||||||
|
|
||||||
|
# Optional packages that should be installed when testing
|
||||||
|
xattr>=0.6.0
|
||||||
|
pysendfile==2.0.0
|
Loading…
x
Reference in New Issue
Block a user