Initial Python client bindings and CLI
Change-Id: Iac4a73acfb515c1e213a1dd0865a62bc39e3ed0f
This commit is contained in:
parent
fd06cdfad0
commit
6f280a8693
4
doc/requirements.txt
Normal file
4
doc/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
cliff
|
||||||
|
jsonschema>=0.7
|
||||||
|
requests
|
||||||
|
python-keystoneclient>=0.2.0
|
243
doc/source/conf.py
Normal file
243
doc/source/conf.py
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# monikerclient documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Wed Oct 31 18:58:17 2012.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'monikerclient'
|
||||||
|
copyright = u'2012, Managed I.T.'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
from monikerclient.version import version_info as monikerclient_version
|
||||||
|
version = monikerclient_version.canonical_version_string()
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = monikerclient_version.version_string_with_vcs()
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
html_theme = 'default'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_domain_indices = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_sphinx = True
|
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_copyright = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = None
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'monikerclientdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'monikerclient.tex', u'Moniker Client Documentation',
|
||||||
|
u'Managed I.T.', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# If true, show page references after internal links.
|
||||||
|
#latex_show_pagerefs = False
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#latex_show_urls = False
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
('index', 'monikerclient', u'Moniker Client Documentation',
|
||||||
|
[u'Managed I.T.'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'monikerclient', u'Moniker Client Documentation',
|
||||||
|
u'Managed I.T.', 'monikerclient', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#texinfo_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#texinfo_domain_indices = True
|
||||||
|
|
||||||
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
|
#texinfo_show_urls = 'footnote'
|
21
doc/source/index.rst
Normal file
21
doc/source/index.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.. moniker documentation master file, created by
|
||||||
|
sphinx-quickstart on Wed Oct 31 18:58:17 2012.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to monikerclients's documentation!
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
|
|
94
monikerclient/auth.py
Normal file
94
monikerclient/auth.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Copyright 2012 Managed I.T.
|
||||||
|
#
|
||||||
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
from urlparse import urlparse
|
||||||
|
from requests.auth import AuthBase
|
||||||
|
|
||||||
|
from keystoneclient.v2_0.client import Client
|
||||||
|
|
||||||
|
|
||||||
|
class KeystoneAuth(AuthBase):
|
||||||
|
def __init__(self, auth_url, username=None, password=None, tenant_id=None,
|
||||||
|
tenant_name=None, token=None, service_type=None,
|
||||||
|
endpoint_type=None):
|
||||||
|
self.auth_url = str(auth_url).rstrip('/')
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.tenant_id = tenant_id
|
||||||
|
self.tenant_name = tenant_name
|
||||||
|
self.token = token
|
||||||
|
|
||||||
|
if (not username and not password) and not token:
|
||||||
|
raise ValueError('A username and password, or token is required')
|
||||||
|
|
||||||
|
if not service_type or not endpoint_type:
|
||||||
|
raise ValueError("Need service_type and/or endpoint_type")
|
||||||
|
|
||||||
|
self.service_type = service_type
|
||||||
|
self.endpoint_type = endpoint_type
|
||||||
|
|
||||||
|
self.refresh_auth()
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
if not self.token:
|
||||||
|
self.refresh_auth()
|
||||||
|
|
||||||
|
request.headers['X-Auth-Token'] = self.token
|
||||||
|
|
||||||
|
return request
|
||||||
|
|
||||||
|
def get_ksclient(self):
|
||||||
|
insecure = urlparse(self.auth_url).scheme != 'https'
|
||||||
|
|
||||||
|
return Client(username=self.username,
|
||||||
|
password=self.password,
|
||||||
|
tenant_id=self.tenant_id,
|
||||||
|
tenant_name=self.tenant_name,
|
||||||
|
auth_url=self.auth_url,
|
||||||
|
insecure=insecure)
|
||||||
|
|
||||||
|
def get_endpoints(self, service_type=None, endpoint_type=None):
|
||||||
|
return self.service_catalog.get_endpoints(
|
||||||
|
service_type=service_type,
|
||||||
|
endpoint_type=endpoint_type)
|
||||||
|
|
||||||
|
def get_url(self, service_type=None, endpoint_type=None):
|
||||||
|
service_type = service_type or self.service_type
|
||||||
|
endpoint_type = endpoint_type or self.endpoint_type
|
||||||
|
endpoints = self.get_endpoints(service_type, endpoint_type)
|
||||||
|
|
||||||
|
return endpoints[service_type][0][endpoint_type].rstrip('/')
|
||||||
|
|
||||||
|
def refresh_auth(self):
|
||||||
|
ks = self.get_ksclient()
|
||||||
|
self.token = ks.auth_token
|
||||||
|
self.service_catalog = ks.service_catalog
|
||||||
|
|
||||||
|
def args_hook(self, args):
|
||||||
|
url = urlparse(args['url'])
|
||||||
|
|
||||||
|
if str(url.scheme) == '':
|
||||||
|
if not self.token:
|
||||||
|
self.refresh_token()
|
||||||
|
|
||||||
|
endpoints = self.get_endpoints()
|
||||||
|
|
||||||
|
if url.netloc in endpoints.keys():
|
||||||
|
|
||||||
|
args['url'] = '%s/%s?%s' % (
|
||||||
|
self.get_url(),
|
||||||
|
url.path.lstrip('/'),
|
||||||
|
url.query
|
||||||
|
)
|
94
monikerclient/cli/base.py
Normal file
94
monikerclient/cli/base.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Copyright 2012 Managed I.T.
|
||||||
|
#
|
||||||
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||||
|
#
|
||||||
|
# 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 abc
|
||||||
|
from cliff.command import Command as CliffCommand
|
||||||
|
from cliff.lister import Lister
|
||||||
|
from cliff.show import ShowOne
|
||||||
|
from monikerclient.v1 import Client
|
||||||
|
|
||||||
|
|
||||||
|
class Command(CliffCommand):
|
||||||
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
|
def run(self, parsed_args):
|
||||||
|
client_args = {
|
||||||
|
'endpoint': self.app.options.os_endpoint,
|
||||||
|
'auth_url': self.app.options.os_auth_url,
|
||||||
|
'username': self.app.options.os_username,
|
||||||
|
'password': self.app.options.os_password,
|
||||||
|
'tenant_id': self.app.options.os_tenant_id,
|
||||||
|
'tenant_name': self.app.options.os_tenant_name,
|
||||||
|
'token': self.app.options.os_token,
|
||||||
|
'region_name': self.app.options.os_region_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client = Client(**client_args)
|
||||||
|
|
||||||
|
return super(Command, self).run(parsed_args)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
"""
|
||||||
|
Execute something, this is since we overload self.take_action()
|
||||||
|
in order to format the data
|
||||||
|
|
||||||
|
This method __NEEDS__ to be overloaded!
|
||||||
|
|
||||||
|
:param parsed_args: The parsed args that are given by take_action()
|
||||||
|
"""
|
||||||
|
|
||||||
|
def post_execute(self, data):
|
||||||
|
"""
|
||||||
|
Format the results locally if needed, by default we just return data
|
||||||
|
|
||||||
|
:param data: Whatever is returned by self.execute()
|
||||||
|
"""
|
||||||
|
return data
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
# TODO: Common Exception Handling Here
|
||||||
|
results = self.execute(parsed_args)
|
||||||
|
return self.post_execute(results)
|
||||||
|
|
||||||
|
|
||||||
|
class ListCommand(Command, Lister):
|
||||||
|
def post_execute(self, results):
|
||||||
|
if len(results) > 0:
|
||||||
|
column_names = results[0].keys()
|
||||||
|
data = [r.values() for r in results]
|
||||||
|
|
||||||
|
return column_names, data
|
||||||
|
else:
|
||||||
|
return [], ()
|
||||||
|
|
||||||
|
|
||||||
|
class GetCommand(Command, ShowOne):
|
||||||
|
def post_execute(self, results):
|
||||||
|
return results.keys(), results.values()
|
||||||
|
|
||||||
|
|
||||||
|
class CreateCommand(Command, ShowOne):
|
||||||
|
def post_execute(self, results):
|
||||||
|
return results.keys(), results.values()
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateCommand(Command, ShowOne):
|
||||||
|
def post_execute(self, results):
|
||||||
|
return results.keys(), results.values()
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteCommand(Command):
|
||||||
|
pass
|
98
monikerclient/cli/domains.py
Normal file
98
monikerclient/cli/domains.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Copyright 2012 Managed I.T.
|
||||||
|
#
|
||||||
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||||
|
#
|
||||||
|
# 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 logging
|
||||||
|
from monikerclient.cli import base
|
||||||
|
from monikerclient.v1.domains import Domain
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ListDomainsCommand(base.ListCommand):
|
||||||
|
""" List Domains """
|
||||||
|
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
return self.client.domains.list()
|
||||||
|
|
||||||
|
|
||||||
|
class GetDomainCommand(base.GetCommand):
|
||||||
|
""" Get Domain """
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(GetDomainCommand, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument('--domain-id', help="Domain ID", required=True)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
return self.client.domains.get(parsed_args.domain_id)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateDomainCommand(base.CreateCommand):
|
||||||
|
""" Create Domain """
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(CreateDomainCommand, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument('--domain-name', help="Domain Name", required=True)
|
||||||
|
parser.add_argument('--domain-email', help="Domain Email",
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
domain = Domain(
|
||||||
|
name=parsed_args.domain_name,
|
||||||
|
email=parsed_args.domain_email
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.client.domains.create(domain)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateDomainCommand(base.UpdateCommand):
|
||||||
|
""" Update Domain """
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(UpdateDomainCommand, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument('--domain-id', help="Domain ID", required=True)
|
||||||
|
parser.add_argument('--domain-name', help="Domain Name")
|
||||||
|
parser.add_argument('--domain-email', help="Domain Email")
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
# TODO: API needs updating.. this get is silly
|
||||||
|
domain = self.client.domains.get(parsed_args.domain_id)
|
||||||
|
|
||||||
|
# TODO: How do we tell if an arg was supplied or intentionally set to
|
||||||
|
# None?
|
||||||
|
|
||||||
|
return self.client.domains.update(domain)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteDomainCommand(base.DeleteCommand):
|
||||||
|
""" Delete Domain """
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(DeleteDomainCommand, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument('--domain-id', help="Domain ID")
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def execute(self, parsed_args):
|
||||||
|
return self.client.domains.delete(parsed_args.domain_id)
|
@ -21,3 +21,23 @@ class Base(Exception):
|
|||||||
|
|
||||||
class ResourceNotFound(Base):
|
class ResourceNotFound(Base):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteError(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Unknown(RemoteError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Forbidden(RemoteError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Conflict(RemoteError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotFound(RemoteError):
|
||||||
|
pass
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
# 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 os
|
||||||
from cliff.app import App
|
from cliff.app import App
|
||||||
from cliff.commandmanager import CommandManager
|
from cliff.commandmanager import CommandManager
|
||||||
|
|
||||||
@ -25,3 +25,41 @@ class MonikerShell(App):
|
|||||||
version='0.1',
|
version='0.1',
|
||||||
command_manager=CommandManager('moniker.cli'),
|
command_manager=CommandManager('moniker.cli'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def build_option_parser(self, description, version, argparse_kwargs=None):
|
||||||
|
parser = super(MonikerShell, self).build_option_parser(
|
||||||
|
description, version, argparse_kwargs)
|
||||||
|
|
||||||
|
parser.add_argument('--os-endpoint',
|
||||||
|
default=os.environ.get('OS_SERVICE_ENDPOINT'),
|
||||||
|
help="Defaults to env[OS_SERVICE_ENDPOINT]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-auth-url',
|
||||||
|
default=os.environ.get('OS_AUTH_URL'),
|
||||||
|
help="Defaults to env[OS_AUTH_URL]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-username',
|
||||||
|
default=os.environ.get('OS_USERNAME'),
|
||||||
|
help="Defaults to env[OS_USERNAME]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-password',
|
||||||
|
default=os.environ.get('OS_PASSWORD'),
|
||||||
|
help="Defaults to env[OS_PASSWORD]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-tenant-id',
|
||||||
|
default=os.environ.get('OS_TENANT_ID'),
|
||||||
|
help="Defaults to env[OS_TENANT_ID]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-tenant-name',
|
||||||
|
default=os.environ.get('OS_TENANT_NAME'),
|
||||||
|
help="Defaults to env[OS_TENANT_NAME]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-token',
|
||||||
|
default=os.environ.get('OS_SERVICE_TOKEN'),
|
||||||
|
help="Defaults to env[OS_SERVICE_TOKEN]")
|
||||||
|
|
||||||
|
parser.add_argument('--os-region-name',
|
||||||
|
default=os.environ.get('OS_REGION_NAME'),
|
||||||
|
help="Defaults to env[OS_REGION_NAME]")
|
||||||
|
|
||||||
|
return parser
|
||||||
|
0
monikerclient/tests/__init__.py
Normal file
0
monikerclient/tests/__init__.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Copyright 2012 Managed I.T.
|
||||||
|
#
|
||||||
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||||
|
#
|
||||||
|
# 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 requests
|
||||||
|
from urlparse import urlparse
|
||||||
|
from monikerclient import exceptions
|
||||||
|
from monikerclient.auth import KeystoneAuth
|
||||||
|
from monikerclient.v1 import domains
|
||||||
|
from monikerclient.v1 import records
|
||||||
|
from monikerclient.v1 import servers
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
""" Client for the Moniker v1 API """
|
||||||
|
|
||||||
|
def __init__(self, endpoint=None, auth_url=None, username=None,
|
||||||
|
password=None, tenant_id=None, tenant_name=None, token=None,
|
||||||
|
region_name=None, endpoint_type='publicURL'):
|
||||||
|
"""
|
||||||
|
:param endpoint: Endpoint URL
|
||||||
|
:param auth_url: Keystone auth_url
|
||||||
|
:param username: The username to auth with
|
||||||
|
:param password: The password to auth with
|
||||||
|
:param tenant_id: The tenant ID
|
||||||
|
:param tenant_name: The tenant name
|
||||||
|
:param token: A token instead of username / password
|
||||||
|
:param region_name: The region name
|
||||||
|
:param endpoint_type: The endpoint type (publicURL for example)
|
||||||
|
"""
|
||||||
|
if auth_url:
|
||||||
|
auth = KeystoneAuth(auth_url, username, password, tenant_id,
|
||||||
|
tenant_name, token, 'dns', endpoint_type)
|
||||||
|
endpoint = auth.get_url()
|
||||||
|
elif endpoint:
|
||||||
|
auth = None
|
||||||
|
else:
|
||||||
|
raise ValueError('Either an auth_url or endpoint must be supplied')
|
||||||
|
|
||||||
|
headers = {'Content-Type': 'application/json'}
|
||||||
|
|
||||||
|
def _ensure_url_hook(args):
|
||||||
|
url_ = urlparse(args['url'])
|
||||||
|
if not url_.scheme:
|
||||||
|
args['url'] = endpoint + url_.path
|
||||||
|
|
||||||
|
hooks = {'args': _ensure_url_hook}
|
||||||
|
|
||||||
|
self.requests = requests.session(
|
||||||
|
auth=auth,
|
||||||
|
headers=headers,
|
||||||
|
hooks=hooks)
|
||||||
|
|
||||||
|
self.domains = domains.DomainsController(client=self)
|
||||||
|
self.records = records.RecordsController(client=self)
|
||||||
|
self.servers = servers.ServersController(client=self)
|
||||||
|
|
||||||
|
def wrap_api_call(self, func, *args, **kw):
|
||||||
|
"""
|
||||||
|
Wrap a self.<rest function> with exception handling
|
||||||
|
|
||||||
|
:param func: The function to wrap
|
||||||
|
"""
|
||||||
|
response = func(*args, **kw)
|
||||||
|
|
||||||
|
if response.status_code in (401, 403):
|
||||||
|
raise exceptions.Forbidden()
|
||||||
|
elif response.status_code == 404:
|
||||||
|
raise exceptions.NotFound()
|
||||||
|
elif response.status_code == 409:
|
||||||
|
raise exceptions.Conflict()
|
||||||
|
elif response.status_code == 500:
|
||||||
|
raise exceptions.Unknown()
|
||||||
|
else:
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get(self, path, **kw):
|
||||||
|
return self.wrap_api_call(self.requests.get, path, **kw)
|
||||||
|
|
||||||
|
def post(self, path, **kw):
|
||||||
|
return self.wrap_api_call(self.requests.post, path, **kw)
|
||||||
|
|
||||||
|
def put(self, path, **kw):
|
||||||
|
return self.wrap_api_call(self.requests.put, path, **kw)
|
||||||
|
|
||||||
|
def delete(self, path, **kw):
|
||||||
|
return self.wrap_api_call(self.requests.delete, path, **kw)
|
53
monikerclient/v1/base.py
Normal file
53
monikerclient/v1/base.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Copyright 2012 Managed I.T.
|
||||||
|
#
|
||||||
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||||
|
#
|
||||||
|
# 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 abc
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(object):
|
||||||
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def list(self, *args, **kw):
|
||||||
|
"""
|
||||||
|
List something
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get(self, *args, **kw):
|
||||||
|
"""
|
||||||
|
Get something
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def create(self, *args, **kw):
|
||||||
|
"""
|
||||||
|
Create something
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def update(self, *args, **kw):
|
||||||
|
"""
|
||||||
|
Update something
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def delete(self, *args, **kw):
|
||||||
|
"""
|
||||||
|
Delete something
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
@ -1,27 +0,0 @@
|
|||||||
# Copyright 2012 Managed I.T.
|
|
||||||
#
|
|
||||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
from monikerclient.v1 import domains
|
|
||||||
from monikerclient.v1 import records
|
|
||||||
from monikerclient.v1 import servers
|
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
|
||||||
""" Client for the Moniker v1 API """
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.domains = domains.Controller()
|
|
||||||
self.records = records.Controller()
|
|
||||||
self.servers = servers.Controller()
|
|
@ -13,20 +13,25 @@
|
|||||||
# 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 json
|
||||||
from monikerclient import warlock
|
from monikerclient import warlock
|
||||||
from monikerclient import utils
|
from monikerclient import utils
|
||||||
|
from monikerclient.v1.base import Controller
|
||||||
|
|
||||||
|
|
||||||
Domain = warlock.model_factory(utils.load_schema('v1', 'domain'))
|
Domain = warlock.model_factory(utils.load_schema('v1', 'domain'))
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class DomainsController(Controller):
|
||||||
def list(self):
|
def list(self):
|
||||||
"""
|
"""
|
||||||
Retrieve a list of domains
|
Retrieve a list of domains
|
||||||
|
|
||||||
:returns: A list of :class:`Domain`s
|
:returns: A list of :class:`Domain`s
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/domains')
|
||||||
|
|
||||||
|
return [Domain(i) for i in response.json['domains']]
|
||||||
|
|
||||||
def get(self, domain_id):
|
def get(self, domain_id):
|
||||||
"""
|
"""
|
||||||
@ -35,6 +40,9 @@ class Controller(object):
|
|||||||
:param domain_id: Domain Identifier
|
:param domain_id: Domain Identifier
|
||||||
:returns: :class:`Domain`
|
:returns: :class:`Domain`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/domains/%s' % domain_id)
|
||||||
|
|
||||||
|
return Domain(response.json)
|
||||||
|
|
||||||
def create(self, domain):
|
def create(self, domain):
|
||||||
"""
|
"""
|
||||||
@ -43,6 +51,9 @@ class Controller(object):
|
|||||||
:param domain: A :class:`Domain` to create
|
:param domain: A :class:`Domain` to create
|
||||||
:returns: :class:`Domain`
|
:returns: :class:`Domain`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.post('/domains', data=json.dumps(domain))
|
||||||
|
|
||||||
|
return Domain(response.json)
|
||||||
|
|
||||||
def update(self, domain):
|
def update(self, domain):
|
||||||
"""
|
"""
|
||||||
@ -51,6 +62,10 @@ class Controller(object):
|
|||||||
:param domain: A :class:`Domain` to update
|
:param domain: A :class:`Domain` to update
|
||||||
:returns: :class:`Domain`
|
:returns: :class:`Domain`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.put('/domains/%s' % domain.id,
|
||||||
|
data=json.dumps(domain.changes))
|
||||||
|
|
||||||
|
return Domain(response.json)
|
||||||
|
|
||||||
def delete(self, domain):
|
def delete(self, domain):
|
||||||
"""
|
"""
|
||||||
@ -58,3 +73,7 @@ class Controller(object):
|
|||||||
|
|
||||||
:param domain: A :class:`Domain`, or Domain Identifier to delete
|
:param domain: A :class:`Domain`, or Domain Identifier to delete
|
||||||
"""
|
"""
|
||||||
|
if isinstance(domain, Domain):
|
||||||
|
self.client.delete('/domains/%s' % domain.id)
|
||||||
|
else:
|
||||||
|
self.client.delete('/domains/%s' % domain)
|
||||||
|
@ -13,20 +13,25 @@
|
|||||||
# 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 json
|
||||||
from monikerclient import warlock
|
from monikerclient import warlock
|
||||||
from monikerclient import utils
|
from monikerclient import utils
|
||||||
|
from monikerclient.v1.base import Controller
|
||||||
|
|
||||||
|
|
||||||
Record = warlock.model_factory(utils.load_schema('v1', 'record'))
|
Record = warlock.model_factory(utils.load_schema('v1', 'record'))
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class RecordsController(Controller):
|
||||||
def list(self):
|
def list(self):
|
||||||
"""
|
"""
|
||||||
Retrieve a list of records
|
Retrieve a list of records
|
||||||
|
|
||||||
:returns: A list of :class:`Record`s
|
:returns: A list of :class:`Record`s
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/records')
|
||||||
|
|
||||||
|
return [Record(i) for i in response.json['records']]
|
||||||
|
|
||||||
def get(self, record_id):
|
def get(self, record_id):
|
||||||
"""
|
"""
|
||||||
@ -35,6 +40,9 @@ class Controller(object):
|
|||||||
:param record_id: Record Identifier
|
:param record_id: Record Identifier
|
||||||
:returns: :class:`Record`
|
:returns: :class:`Record`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/records/%s' % record_id)
|
||||||
|
|
||||||
|
return Record(response.json)
|
||||||
|
|
||||||
def create(self, record):
|
def create(self, record):
|
||||||
"""
|
"""
|
||||||
@ -43,6 +51,9 @@ class Controller(object):
|
|||||||
:param record: A :class:`Record` to create
|
:param record: A :class:`Record` to create
|
||||||
:returns: :class:`Record`
|
:returns: :class:`Record`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.post('/records', data=json.dumps(record))
|
||||||
|
|
||||||
|
return record.update(response.json)
|
||||||
|
|
||||||
def update(self, record):
|
def update(self, record):
|
||||||
"""
|
"""
|
||||||
@ -51,6 +62,10 @@ class Controller(object):
|
|||||||
:param record: A :class:`Record` to update
|
:param record: A :class:`Record` to update
|
||||||
:returns: :class:`Record`
|
:returns: :class:`Record`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.put('/records/%s' % record.id,
|
||||||
|
data=json.dumps(record))
|
||||||
|
|
||||||
|
return record.update(response.json)
|
||||||
|
|
||||||
def delete(self, record):
|
def delete(self, record):
|
||||||
"""
|
"""
|
||||||
@ -58,3 +73,7 @@ class Controller(object):
|
|||||||
|
|
||||||
:param record: A :class:`Record`, or Record Identifier to delete
|
:param record: A :class:`Record`, or Record Identifier to delete
|
||||||
"""
|
"""
|
||||||
|
if isinstance(record, Record):
|
||||||
|
self.client.delete('/records/%s' % record.id)
|
||||||
|
else:
|
||||||
|
self.client.delete('/records/%s' % record)
|
||||||
|
@ -13,20 +13,25 @@
|
|||||||
# 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 json
|
||||||
from monikerclient import warlock
|
from monikerclient import warlock
|
||||||
from monikerclient import utils
|
from monikerclient import utils
|
||||||
|
from monikerclient.v1.base import Controller
|
||||||
|
|
||||||
|
|
||||||
Server = warlock.model_factory(utils.load_schema('v1', 'server'))
|
Server = warlock.model_factory(utils.load_schema('v1', 'server'))
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class ServersController(Controller):
|
||||||
def list(self):
|
def list(self):
|
||||||
"""
|
"""
|
||||||
Retrieve a list of servers
|
Retrieve a list of servers
|
||||||
|
|
||||||
:returns: A list of :class:`Server`s
|
:returns: A list of :class:`Server`s
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/servers')
|
||||||
|
|
||||||
|
return [Server(i) for i in response.json['servers']]
|
||||||
|
|
||||||
def get(self, server_id):
|
def get(self, server_id):
|
||||||
"""
|
"""
|
||||||
@ -35,6 +40,9 @@ class Controller(object):
|
|||||||
:param server_id: Server Identifier
|
:param server_id: Server Identifier
|
||||||
:returns: :class:`Server`
|
:returns: :class:`Server`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.get('/servers/%s' % server_id)
|
||||||
|
|
||||||
|
return Server(response.json)
|
||||||
|
|
||||||
def create(self, server):
|
def create(self, server):
|
||||||
"""
|
"""
|
||||||
@ -43,6 +51,9 @@ class Controller(object):
|
|||||||
:param server: A :class:`Server` to create
|
:param server: A :class:`Server` to create
|
||||||
:returns: :class:`Server`
|
:returns: :class:`Server`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.post('/servers', data=json.dumps(server))
|
||||||
|
|
||||||
|
return server.update(response.json)
|
||||||
|
|
||||||
def update(self, server):
|
def update(self, server):
|
||||||
"""
|
"""
|
||||||
@ -51,6 +62,10 @@ class Controller(object):
|
|||||||
:param server: A :class:`Server` to update
|
:param server: A :class:`Server` to update
|
||||||
:returns: :class:`Server`
|
:returns: :class:`Server`
|
||||||
"""
|
"""
|
||||||
|
response = self.client.put('/servers/%s' % server.id,
|
||||||
|
data=json.dumps(server))
|
||||||
|
|
||||||
|
return server.update(response.json)
|
||||||
|
|
||||||
def delete(self, server):
|
def delete(self, server):
|
||||||
"""
|
"""
|
||||||
@ -58,3 +73,7 @@ class Controller(object):
|
|||||||
|
|
||||||
:param server: A :class:`Server`, or Server Identifier to delete
|
:param server: A :class:`Server`, or Server Identifier to delete
|
||||||
"""
|
"""
|
||||||
|
if isinstance(server, Server):
|
||||||
|
self.client.delete('/servers/%s' % server.id)
|
||||||
|
else:
|
||||||
|
self.client.delete('/servers/%s' % server)
|
||||||
|
@ -113,6 +113,9 @@ def model_factory(schema):
|
|||||||
def itervalues(self):
|
def itervalues(self):
|
||||||
return copy.deepcopy(dict(self)).itervalues()
|
return copy.deepcopy(dict(self)).itervalues()
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return copy.deepcopy(dict(self)).keys()
|
||||||
|
|
||||||
def values(self):
|
def values(self):
|
||||||
return copy.deepcopy(dict(self)).values()
|
return copy.deepcopy(dict(self)).values()
|
||||||
|
|
||||||
|
@ -6,3 +6,11 @@ cover-inclusive=true
|
|||||||
verbosity=2
|
verbosity=2
|
||||||
detailed-errors=1
|
detailed-errors=1
|
||||||
where=monikerclient/tests
|
where=monikerclient/tests
|
||||||
|
|
||||||
|
[build_sphinx]
|
||||||
|
source-dir = doc/source
|
||||||
|
build-dir = doc/build
|
||||||
|
all_files = 1
|
||||||
|
|
||||||
|
[upload_docs]
|
||||||
|
upload-dir = doc/build/html
|
||||||
|
9
setup.py
9
setup.py
@ -14,6 +14,7 @@
|
|||||||
# 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 textwrap
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
from monikerclient.openstack.common import setup as common_setup
|
from monikerclient.openstack.common import setup as common_setup
|
||||||
from monikerclient.version import version_info as version
|
from monikerclient.version import version_info as version
|
||||||
@ -46,6 +47,14 @@ setup(
|
|||||||
'bin/moniker',
|
'bin/moniker',
|
||||||
],
|
],
|
||||||
cmdclass=common_setup.get_cmdclass(),
|
cmdclass=common_setup.get_cmdclass(),
|
||||||
|
entry_points=textwrap.dedent("""
|
||||||
|
[moniker.cli]
|
||||||
|
domain-list = monikerclient.cli.domains:ListDomainsCommand
|
||||||
|
domain-get = monikerclient.cli.domains:GetDomainCommand
|
||||||
|
domain-create = monikerclient.cli.domains:CreateDomainCommand
|
||||||
|
domain-update = monikerclient.cli.domains:UpdateDomainCommand
|
||||||
|
domain-delete = monikerclient.cli.domains:DeleteDomainCommand
|
||||||
|
"""),
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
'Topic :: Internet :: Name Service (DNS)',
|
'Topic :: Internet :: Name Service (DNS)',
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
cliff
|
cliff
|
||||||
jsonschema>=0.7
|
jsonschema>=0.7
|
||||||
|
requests
|
||||||
|
python-keystoneclient>=0.2.0
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
nose
|
nose
|
||||||
mox
|
mox
|
||||||
openstack.nose_plugin
|
openstack.nose_plugin
|
||||||
|
sphinx
|
||||||
|
Loading…
x
Reference in New Issue
Block a user