parent
db1d1959af
commit
4e90f08824
@ -0,0 +1,30 @@
|
||||
# Compiled files
|
||||
*.py[co]
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Sphinx
|
||||
_build
|
||||
|
||||
# Packages/installer info
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Other
|
||||
.testrepository
|
||||
.tox
|
||||
.*.swp
|
||||
.coverage
|
||||
cover
|
||||
AUTHORS
|
||||
ChangeLog
|
@ -0,0 +1,4 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack-dev/hacking.git
|
@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
@ -0,0 +1,17 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps in the "If you're a developer, start here"
|
||||
section of this page:
|
||||
|
||||
http://wiki.openstack.org/HowToContribute
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
http://wiki.openstack.org/GerritWorkflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/hacking
|
@ -0,0 +1,176 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
@ -0,0 +1,7 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include README.rst
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
@ -0,0 +1,304 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
hacking is a set of flake8 plugins that test and enforce:
|
||||
|
||||
OpenStack Style Commandments
|
||||
============================
|
||||
|
||||
- Step 1: Read http://www.python.org/dev/peps/pep-0008/
|
||||
- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
|
||||
- Step 3: Read on
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
- Put two newlines between top-level code (funcs, classes, etc)
|
||||
- Use only UNIX style newlines ("\n"), not Windows style ("\r\n")
|
||||
- Put one newline between methods in classes and anywhere else
|
||||
- Long lines should be wrapped in parentheses
|
||||
in preference to using a backslash for line continuation.
|
||||
- Do not write "except:", use "except Exception:" at the very least
|
||||
- Include your name with TODOs as in "#TODO(termie)"
|
||||
- Do not shadow a built-in or reserved word. Example::
|
||||
|
||||
def list():
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = list() # BAD, shadows `list` built-in
|
||||
|
||||
class Foo(object):
|
||||
def list(self):
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = Foo().list() # OKAY, does not shadow built-in
|
||||
|
||||
- Use the "is not" operator when testing for unequal identities. Example::
|
||||
|
||||
if not X is Y: # BAD, intended behavior is ambiguous
|
||||
pass
|
||||
|
||||
if X is not Y: # OKAY, intuitive
|
||||
pass
|
||||
|
||||
- Use the "not in" operator for evaluating membership in a collection. Example::
|
||||
|
||||
if not X in Y: # BAD, intended behavior is ambiguous
|
||||
pass
|
||||
|
||||
if X not in Y: # OKAY, intuitive
|
||||
pass
|
||||
|
||||
if not (X in Y or X in Z): # OKAY, still better than all those 'not's
|
||||
pass
|
||||
|
||||
|
||||
Imports
|
||||
-------
|
||||
- Do not import objects, only modules (*)
|
||||
- Do not import more than one module per line (*)
|
||||
- Do not use wildcard ``*`` import (*)
|
||||
- Do not make relative imports
|
||||
- Do not make new nova.db imports in nova/virt/*
|
||||
- Order your imports by the full module path
|
||||
- Organize your imports according to the following template
|
||||
|
||||
(*) exceptions are:
|
||||
|
||||
- imports from ``migrate`` package
|
||||
- imports from ``sqlalchemy`` package
|
||||
- imports from ``nova.db.sqlalchemy.session`` module
|
||||
- imports from ``nova.db.sqlalchemy.migration.versioning_api`` package
|
||||
|
||||
Example::
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
{{stdlib imports in human alphabetical order}}
|
||||
\n
|
||||
{{third-party lib imports in human alphabetical order}}
|
||||
\n
|
||||
{{nova imports in human alphabetical order}}
|
||||
\n
|
||||
\n
|
||||
{{begin your code}}
|
||||
|
||||
|
||||
Human Alphabetical Order Examples
|
||||
---------------------------------
|
||||
Example::
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import random
|
||||
import StringIO
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import eventlet
|
||||
import webob.exc
|
||||
|
||||
import nova.api.ec2
|
||||
from nova.api import openstack
|
||||
from nova.auth import users
|
||||
from nova.endpoint import cloud
|
||||
import nova.flags
|
||||
from nova import test
|
||||
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
Example::
|
||||
|
||||
"""A one line docstring looks like this and ends in a period."""
|
||||
|
||||
|
||||
"""A multi line docstring has a one-line summary, less than 80 characters.
|
||||
|
||||
Then a new paragraph after a newline that explains in more detail any
|
||||
general information about the function, class or method. Example usages
|
||||
are also great to have here if it is a complex class for function.
|
||||
|
||||
When writing the docstring for a class, an extra line should be placed
|
||||
after the closing quotations. For more in-depth explanations for these
|
||||
decisions see http://www.python.org/dev/peps/pep-0257/
|
||||
|
||||
If you are going to describe parameters and return values, use Sphinx, the
|
||||
appropriate syntax is as follows.
|
||||
|
||||
:param foo: the foo parameter
|
||||
:param bar: the bar parameter
|
||||
:returns: return_type -- description of the return value
|
||||
:returns: description of the return value
|
||||
:raises: AttributeError, KeyError
|
||||
"""
|
||||
|
||||
|
||||
Dictionaries/Lists
|
||||
------------------
|
||||
If a dictionary (dict) or list object is longer than 80 characters, its items
|
||||
should be split with newlines. Embedded iterables should have their items
|
||||
indented. Additionally, the last item in the dictionary should have a trailing
|
||||
comma. This increases readability and simplifies future diffs.
|
||||
|
||||
Example::
|
||||
|
||||
my_dictionary = {
|
||||
"image": {
|
||||
"name": "Just a Snapshot",
|
||||
"size": 2749573,
|
||||
"properties": {
|
||||
"user_id": 12,
|
||||
"arch": "x86_64",
|
||||
},
|
||||
"things": [
|
||||
"thing_one",
|
||||
"thing_two",
|
||||
],
|
||||
"status": "ACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Calling Methods
|
||||
---------------
|
||||
Calls to methods 80 characters or longer should format each argument with
|
||||
newlines. This is not a requirement, but a guideline::
|
||||
|
||||
unnecessarily_long_function_name('string one',
|
||||
'string two',
|
||||
kwarg1=constants.ACTIVE,
|
||||
kwarg2=['a', 'b', 'c'])
|
||||
|
||||
|
||||
Rather than constructing parameters inline, it is better to break things up::
|
||||
|
||||
list_of_strings = [
|
||||
'what_a_long_string',
|
||||
'not as long',
|
||||
]
|
||||
|
||||
dict_of_numbers = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'twenty four': 24,
|
||||
}
|
||||
|
||||
object_one.call_a_method('string three',
|
||||
'string four',
|
||||
kwarg1=list_of_strings,
|
||||
kwarg2=dict_of_numbers)
|
||||
|
||||
|
||||
Internationalization (i18n) Strings
|
||||
-----------------------------------
|
||||
In order to support multiple languages, we have a mechanism to support
|
||||
automatic translations of exception and log strings.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("An error occurred")
|
||||
raise HTTPBadRequest(explanation=msg)
|
||||
|
||||
If you have a variable to place within the string, first internationalize the
|
||||
template string then do the replacement.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("Missing parameter: %s") % ("flavor",)
|
||||
LOG.error(msg)
|
||||
|
||||
If you have multiple variables to place in the string, use keyword parameters.
|
||||
This helps our translators reorder parameters when needed.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("The server with id %(s_id)s has no key %(m_key)s")
|
||||
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
|
||||
|
||||
|
||||
Creating Unit Tests
|
||||
-------------------
|
||||
For every new feature, unit tests should be created that both test and
|
||||
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||
bug that had no unit test, a new passing unit test should be added. If a
|
||||
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||
without the patch and passes with the patch.
|
||||
|
||||
For more information on creating unit tests and utilizing the testing
|
||||
infrastructure in OpenStack Nova, please read nova/tests/README.rst.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
The testing system is based on a combination of tox and testr. The canonical
|
||||
approach to running tests is to simply run the command `tox`. This will
|
||||
create virtual environments, populate them with depenedencies and run all of
|
||||
the tests that OpenStack CI systems run. Behind the scenes, tox is running
|
||||
`testr run --parallel`, but is set up such that you can supply any additional
|
||||
testr arguments that are needed to tox. For example, you can run:
|
||||
`tox -- --analyze-isolation` to cause tox to tell testr to add
|
||||
--analyze-isolation to its argument list.
|
||||
|
||||
It is also possible to run the tests inside of a virtual environment
|
||||
you have created, or it is possible that you have all of the dependencies
|
||||
installed locally already. In this case, you can interact with the testr
|
||||
command directly. Running `testr run` will run the entire test suite. `testr
|
||||
run --parallel` will run it in parallel (this is the default incantation tox
|
||||
uses.) More information about testr can be found at:
|
||||
http://wiki.openstack.org/testr
|
||||
|
||||
|
||||
openstack-common
|
||||
----------------
|
||||
|
||||
A number of modules from openstack-common are imported into the project.
|
||||
|
||||
These modules are "incubating" in openstack-common and are kept in sync
|
||||
with the help of openstack-common's update.py script. See:
|
||||
|
||||
http://wiki.openstack.org/CommonLibrary#Incubation
|
||||
|
||||
The copy of the code should never be directly modified here. Please
|
||||
always update openstack-common first and then run the script to copy
|
||||
the changes across.
|
||||
|
||||
OpenStack Trademark
|
||||
-------------------
|
||||
|
||||
OpenStack is a registered trademark of the OpenStack Foundation, and uses the
|
||||
following capitalization:
|
||||
|
||||
OpenStack
|
||||
|
||||
|
||||
Commit Messages
|
||||
---------------
|
||||
Using a common format for commit messages will help keep our git history
|
||||
readable. Follow these guidelines:
|
||||
|
||||
First, provide a brief summary of 50 characters or less. Summaries
|
||||
of greater then 72 characters will be rejected by the gate.
|
||||
|
||||
The first line of the commit message should provide an accurate
|
||||
description of the change, not just a reference to a bug or
|
||||
blueprint. It must be followed by a single blank line.
|
||||
|
||||
If the change relates to a specific driver (libvirt, xenapi, qpid, etc...),
|
||||
begin the first line of the commit message with the driver name, lowercased,
|
||||
followed by a colon.
|
||||
|
||||
Following your brief summary, provide a more detailed description of
|
||||
the patch, manually wrapping the text at 72 characters. This
|
||||
description should provide enough detail that one does not have to
|
||||
refer to external resources to determine its high-level functionality.
|
||||
|
||||
Once you use 'git review', two lines will be appended to the commit
|
||||
message: a blank line followed by a 'Change-Id'. This is important
|
||||
to correlate this commit with a specific review in Gerrit, and it
|
||||
should not be modified.
|
||||
|
||||
For further information on constructing high quality commit messages,
|
||||
and how to split up commits into a series of changes, consult the
|
||||
project wiki:
|
||||
|
||||
http://wiki.openstack.org/GitCommitMessages
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
sudo ifconfig -a | grep br | grep -v bridge | cut -f1 -d" " | xargs -n1 -ifoo ifconfig foo down
|
||||
sudo ifconfig -a | grep br | grep -v bridge | cut -f1 -d" " | xargs -n1 -ifoo brctl delbr foo
|
||||
sudo ifconfig -a | grep vlan | cut -f1 -d" " | xargs -n1 -ifoo ifconfig foo down
|
||||
sudo ifconfig -a | grep vlan | cut -f1 -d" " | xargs -n1 -ifoo ip link del foo
|
@ -1,20 +0,0 @@
|
||||
This generate_sample.sh tool is used to generate etc/nova/nova.conf.sample
|
||||
|
||||
Run it from the top-level working directory i.e.
|
||||
|
||||
$> ./tools/conf/generate_sample.sh
|
||||
|
||||
Watch out for warnings about modules like libvirt, qpid and zmq not
|
||||
being found - these warnings are significant because they result
|
||||
in options not appearing in the generated config file.
|
||||
|
||||
|
||||
The analyze_opts.py tool is used to find options which appear in
|
||||
/etc/nova/nova.conf but not in etc/nova/nova.conf.sample
|
||||
This helps identify options in the nova.conf file which are not used by nova.
|
||||
The tool also identifies any options which are set to the default value.
|
||||
|
||||
Run it from the top-level working directory i.e.
|
||||
|
||||
$> ./tools/conf/analyze_opts.py
|
||||
|
@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012, Cloudscaling
|
||||
# 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.
|
||||
'''
|
||||
find_unused_options.py
|
||||
|
||||
Compare the nova.conf file with the nova.conf.sample file to find any unused
|
||||
options or default values in nova.conf
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.getcwd())
|
||||
from oslo.config import iniparser
|
||||
|
||||
|
||||
class PropertyCollecter(iniparser.BaseParser):
|
||||
def __init__(self):
|
||||
super(PropertyCollecter, self).__init__()
|
||||
self.key_value_pairs = {}
|
||||
|
||||
def assignment(self, key, value):
|
||||
self.key_value_pairs[key] = value
|
||||
|
||||
def new_section(self, section):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def collect_properties(cls, lineiter, sample_format=False):
|
||||
def clean_sample(f):
|
||||
for line in f:
|
||||
if line.startswith("# ") and line != '# nova.conf sample #\n':
|
||||
line = line[2:]
|
||||
yield line
|
||||
pc = cls()
|
||||
if sample_format:
|
||||
lineiter = clean_sample(lineiter)
|
||||
pc.parse(lineiter)
|
||||
return pc.key_value_pairs
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='''Compare the nova.conf
|
||||
file with the nova.conf.sample file to find any unused options or
|
||||
default values in nova.conf''')
|
||||
|
||||
parser.add_argument('-c', action='store',
|
||||
default='/etc/nova/nova.conf',
|
||||
help='path to nova.conf\
|
||||
(defaults to /etc/nova/nova.conf)')
|
||||
parser.add_argument('-s', default='./etc/nova/nova.conf.sample',
|
||||
help='path to nova.conf.sample\
|
||||
(defaults to ./etc/nova/nova.conf.sample')
|
||||
options = parser.parse_args()
|
||||
|
||||
conf_file_options = PropertyCollecter.collect_properties(open(options.c))
|
||||
sample_conf_file_options = PropertyCollecter.collect_properties(
|
||||
open(options.s), sample_format=True)
|
||||
|
||||
for k, v in sorted(conf_file_options.items()):
|
||||
if k not in sample_conf_file_options:
|
||||
print "Unused:", k
|
||||
for k, v in sorted(conf_file_options.items()):
|
||||
if k in sample_conf_file_options and v == sample_conf_file_options[k]:
|
||||
print "Default valued:", k
|
@ -1,269 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 SINA Corporation
|
||||
# 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.
|
||||
#
|
||||
# @author: Zhongyue Luo, SINA Corporation.
|
||||
#
|
||||
|
||||
"""Extracts OpenStack config option info from module(s)."""
|
||||
|
||||
import imp
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova.openstack.common import importutils
|
||||
|
||||
|
||||
STROPT = "StrOpt"
|
||||
BOOLOPT = "BoolOpt"
|
||||
INTOPT = "IntOpt"
|
||||
FLOATOPT = "FloatOpt"
|
||||
LISTOPT = "ListOpt"
|
||||
MULTISTROPT = "MultiStrOpt"
|
||||
|
||||
OPT_TYPES = {
|
||||
STROPT: 'string value',
|
||||
BOOLOPT: 'boolean value',
|
||||
INTOPT: 'integer value',
|
||||
FLOATOPT: 'floating point value',
|
||||
LISTOPT: 'list value',
|
||||
MULTISTROPT: 'multi valued',
|
||||
}
|
||||
|
||||
OPTION_COUNT = 0
|
||||
OPTION_REGEX = re.compile(r"(%s)" % "|".join([STROPT, BOOLOPT, INTOPT,
|
||||
FLOATOPT, LISTOPT,
|
||||
MULTISTROPT]))
|
||||
|
||||
PY_EXT = ".py"
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
|
||||
WORDWRAP_WIDTH = 60
|
||||
|
||||
|
||||
def main(srcfiles):
|
||||
mods_by_pkg = dict()
|
||||
for filepath in srcfiles:
|
||||
pkg_name = filepath.split(os.sep)[1]
|
||||
mod_str = '.'.join(['.'.join(filepath.split(os.sep)[:-1]),
|
||||
os.path.basename(filepath).split('.')[0]])
|
||||
mods_by_pkg.setdefault(pkg_name, list()).append(mod_str)
|
||||
# NOTE(lzyeval): place top level modules before packages
|
||||
pkg_names = filter(lambda x: x.endswith(PY_EXT), mods_by_pkg.keys())
|
||||
pkg_names.sort()
|
||||
ext_names = filter(lambda x: x not in pkg_names, mods_by_pkg.keys())
|
||||
ext_names.sort()
|
||||
pkg_names.extend(ext_names)
|
||||
|
||||
# opts_by_group is a mapping of group name to an options list
|
||||
# The options list is a list of (module, options) tuples
|
||||
opts_by_group = {'DEFAULT': []}
|
||||
|
||||
for pkg_name in pkg_names:
|
||||
mods = mods_by_pkg.get(pkg_name)
|
||||
mods.sort()
|
||||
for mod_str in mods:
|
||||
if mod_str.endswith('.__init__'):
|
||||
mod_str = mod_str[:mod_str.rfind(".")]
|
||||
|
||||
mod_obj = _import_module(mod_str)
|
||||
if not mod_obj:
|
||||
continue
|
||||
|
||||
for group, opts in _list_opts(mod_obj):
|
||||
opts_by_group.setdefault(group, []).append((mod_str, opts))
|
||||
|
||||
print_group_opts('DEFAULT', opts_by_group.pop('DEFAULT', []))
|
||||
for group, opts in opts_by_group.items():
|
||||
print_group_opts(group, opts)
|
||||
|
||||
print "# Total option count: %d" % OPTION_COUNT
|
||||
|
||||
|
||||
def _import_module(mod_str):
|
||||
try:
|
||||
if mod_str.startswith('bin.'):
|
||||
imp.load_source(mod_str[4:], os.path.join('bin', mod_str[4:]))
|
||||
return sys.modules[mod_str[4:]]
|
||||
else:
|
||||
return importutils.import_module(mod_str)
|
||||
except (ValueError, AttributeError), err:
|
||||
return None
|
||||
except ImportError, ie:
|
||||
sys.stderr.write("%s\n" % str(ie))
|
||||
return None
|
||||
except Exception, e:
|
||||
return None
|
||||
|
||||
|
||||
def _guess_groups(opt, mod_obj):
|
||||
groups = []
|
||||
|
||||
# is it in the DEFAULT group?
|
||||
if (opt.dest in cfg.CONF and
|
||||
not isinstance(cfg.CONF[opt.dest], cfg.CONF.GroupAttr)):
|
||||
groups.append('DEFAULT')
|
||||
|
||||
# what other groups is it in?
|
||||
for key, value in cfg.CONF.items():
|
||||
if not isinstance(value, cfg.CONF.GroupAttr):
|
||||
continue
|
||||
if opt.dest not in value:
|
||||
continue
|
||||
groups.append(key)
|
||||
|
||||
if len(groups) == 1:
|
||||
return groups[0]
|
||||
|
||||
group = None
|
||||
for g in groups:
|
||||
if g in mod_obj.__name__:
|
||||
group = g
|
||||
break
|
||||
|
||||
if group is None and 'DEFAULT' in groups:
|
||||
sys.stderr.write("Guessing that " + opt.dest +
|
||||
" in " + mod_obj.__name__ +
|
||||
" is in DEFAULT group out of " +
|
||||
','.join(groups) + "\n")
|
||||
return 'DEFAULT'
|
||||
|
||||
if group is None:
|
||||
sys.stderr.write("Unable to guess what group " + opt.dest +
|
||||
" in " + mod_obj.__name__ +
|
||||
" is in out of " + ','.join(groups) + "\n")
|
||||
sys.exit(1)
|
||||
|
||||
sys.stderr.write("Guessing that " + opt.dest +
|
||||
" in " + mod_obj.__name__ +
|
||||
" is in the " + group +
|
||||
" group out of " + ','.join(groups) + "\n")
|
||||
return group
|
||||
|
||||
|
||||
def _list_opts(obj):
|
||||
def is_opt(o):
|
||||
return (isinstance(o, cfg.Opt) and
|
||||
not isinstance(o, cfg.SubCommandOpt))
|
||||
|
||||
opts = list()
|
||||
for attr_str in dir(obj):
|
||||
attr_obj = getattr(obj, attr_str)
|
||||
if is_opt(attr_obj):
|
||||
opts.append(attr_obj)
|
||||
elif (isinstance(attr_obj, list) and
|
||||
all(map(lambda x: is_opt(x), attr_obj))):
|
||||
opts.extend(attr_obj)
|
||||
|
||||
ret = {}
|
||||
for opt in opts:
|
||||
ret.setdefault(_guess_groups(opt, obj), []).append(opt)
|
||||
return ret.items()
|
||||
|
||||
|
||||
def print_group_opts(group, opts_by_module):
|
||||
print "[%s]" % group
|
||||
print
|
||||
global OPTION_COUNT
|
||||
for mod, opts in opts_by_module:
|
||||
OPTION_COUNT += len(opts)
|
||||
print '#'
|
||||
print '# Options defined in %s' % mod
|
||||
print '#'
|
||||
print
|
||||
for opt in opts:
|
||||
_print_opt(opt)
|
||||
print
|
||||
|
||||
|
||||
def _get_my_ip():
|
||||
try:
|
||||
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
csock.connect(('8.8.8.8', 80))
|
||||
(addr, port) = csock.getsockname()
|
||||
csock.close()
|
||||
return addr
|
||||
except socket.error:
|
||||
return None
|
||||
|
||||
|
||||
def _sanitize_default(s):
|
||||
"""Set up a reasonably sensible default for pybasedir, my_ip and host."""
|
||||
if s.startswith(BASEDIR):
|
||||
return s.replace(BASEDIR, '/usr/lib/python/site-packages')
|
||||
elif BASEDIR in s:
|
||||
return s.replace(BASEDIR, '')
|
||||
elif s == _get_my_ip():
|
||||
return '10.0.0.1'
|
||||
elif s == socket.getfqdn():
|
||||
return 'nova'
|
||||
elif s.strip() != s:
|
||||
return '"%s"' % s
|
||||
return s
|
||||
|
||||
|
||||
def _print_opt(opt):
|
||||
opt_name, opt_default, opt_help = opt.dest, opt.default, opt.help
|
||||
if not opt_help:
|
||||
sys.stderr.write('WARNING: "%s" is missing help string.\n' % opt_name)
|
||||
opt_type = None
|
||||
try:
|
||||
opt_type = OPTION_REGEX.search(str(type(opt))).group(0)
|
||||
except (ValueError, AttributeError), err:
|
||||
sys.stderr.write("%s\n" % str(err))
|
||||
sys.exit(1)
|
||||
opt_help += ' (' + OPT_TYPES[opt_type] + ')'
|
||||
print '#', "\n# ".join(textwrap.wrap(opt_help, WORDWRAP_WIDTH))
|
||||
try:
|
||||
if opt_default is None:
|
||||
print '#%s=<None>' % opt_name
|
||||
elif opt_type == STROPT:
|
||||
assert(isinstance(opt_default, basestring))
|
||||
print '#%s=%s' % (opt_name, _sanitize_default(opt_default))
|
||||
elif opt_type == BOOLOPT:
|
||||
assert(isinstance(opt_default, bool))
|
||||
print '#%s=%s' % (opt_name, str(opt_default).lower())
|
||||
elif opt_type == INTOPT:
|
||||
assert(isinstance(opt_default, int) and
|
||||
not isinstance(opt_default, bool))
|
||||
print '#%s=%s' % (opt_name, opt_default)
|
||||
elif opt_type == FLOATOPT:
|
||||
assert(isinstance(opt_default, float))
|
||||
print '#%s=%s' % (opt_name, opt_default)
|
||||
elif opt_type == LISTOPT:
|
||||
assert(isinstance(opt_default, list))
|
||||
print '#%s=%s' % (opt_name, ','.join(opt_default))
|
||||
elif opt_type == MULTISTROPT:
|
||||
assert(isinstance(opt_default, list))
|
||||
if not opt_default:
|
||||
opt_default = ['']
|
||||
for default in opt_default:
|
||||
print '#%s=%s' % (opt_name, default)
|
||||
print
|
||||
except Exception:
|
||||
sys.stderr.write('Error in option "%s"\n' % opt_name)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print "usage: python %s [srcfile]...\n" % sys.argv[0]
|
||||
sys.exit(0)
|
||||
main(sys.argv[1:])
|
@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 SINA Corporation
|
||||
# All Rights Reserved.
|
||||
# Author: Zhongyue Luo <lzyeval@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
FILES=$(find nova -type f -name "*.py" ! -path "nova/tests/*" -exec \
|
||||
grep -l "Opt(" {} \; | sort -u)
|
||||
BINS=$(echo bin/nova-*)
|
||||
|
||||
PYTHONPATH=./:${PYTHONPATH} \
|
||||
python $(dirname "$0")/extract_opts.py ${FILES} ${BINS} > \
|
||||
etc/nova/nova.conf.sample
|
||||
|
||||
# Remove compiled files created by imp.import_source()
|
||||
for bin in ${BINS}; do
|
||||
[ -f ${bin}c ] && rm ${bin}c
|
||||
done
|
@ -1,270 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Utility for diff'ing two versions of the DB schema.
|
||||
|
||||
Each release cycle the plan is to compact all of the migrations from that
|
||||
release into a single file. This is a manual and, unfortunately, error-prone
|
||||
process. To ensure that the schema doesn't change, this tool can be used to
|
||||
diff the compacted DB schema to the original, uncompacted form.
|
||||
|
||||
|
||||
The schema versions are specified by providing a git ref (a branch name or
|
||||
commit hash) and a SQLAlchemy-Migrate version number:
|
||||
Run like:
|
||||
|
||||
./tools/db/schema_diff.py mysql master:latest my_branch:82
|
||||
"""
|
||||
import datetime
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
### Dump
|
||||
|
||||
|
||||
def dump_db(db_driver, db_name, migration_version, dump_filename):
|
||||
db_driver.create(db_name)
|
||||
try:
|
||||
migrate(db_driver, db_name, migration_version)
|
||||
db_driver.dump(db_name, dump_filename)
|
||||
finally:
|
||||
db_driver.drop(db_name)
|
||||
|
||||
|
||||
### Diff
|
||||
|
||||
|
||||
def diff_files(filename1, filename2):
|
||||
pipeline = ['diff -U 3 %(filename1)s %(filename2)s' % locals()]
|
||||
|
||||
# Use colordiff if available
|
||||
if subprocess.call(['which', 'colordiff']) == 0:
|
||||
pipeline.append('colordiff')
|
||||
|
||||
pipeline.append('less -R')
|
||||
|
||||
cmd = ' | '.join(pipeline)
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
|
||||
|
||||
### Database
|
||||
|
||||
|
||||
class MySQL(object):
|
||||
def create(self, name):
|
||||
subprocess.check_call(['mysqladmin', '-u', 'root', 'create', name])
|
||||
|
||||
def drop(self, name):
|
||||
subprocess.check_call(['mysqladmin', '-f', '-u', 'root', 'drop', name])
|
||||
|
||||
def dump(self, name, dump_filename):
|
||||
subprocess.check_call(
|
||||
'mysqldump -u root %(name)s > %(dump_filename)s' % locals(),
|
||||
shell=True)
|
||||
|
||||
def url(self, name):
|
||||
return 'mysql://root@localhost/%s' % name
|
||||
|
||||
|
||||
class Postgres(object):
|
||||
def create(self, name):
|
||||
subprocess.check_call(['createdb', name])
|
||||
|
||||
def drop(self, name):
|
||||
subprocess.check_call(['dropdb', name])
|
||||
|
||||
def dump(self, name, dump_filename):
|
||||
subprocess.check_call(
|
||||
'pg_dump %(name)s > %(dump_filename)s' % locals(),
|
||||
shell=True)
|
||||
|
||||
def url(self, name):
|
||||
return 'postgres://localhost/%s' % name
|
||||
|
||||
|
||||
def _get_db_driver_class(db_type):
|
||||
if db_type == "mysql":
|
||||
return MySQL
|
||||
elif db_type == "postgres":
|
||||
return Postgres
|
||||
else:
|
||||
raise Exception(_("database %s not supported") % db_type)
|
||||
|
||||
|
||||
### Migrate
|
||||
|
||||
|
||||
MIGRATE_REPO = os.path.join(os.getcwd(), "nova/db/sqlalchemy/migrate_repo")
|
||||
|
||||
|
||||
def migrate(db_driver, db_name, migration_version):
|
||||
earliest_version = _migrate_get_earliest_version()
|
||||
|
||||
# NOTE(sirp): sqlalchemy-migrate currently cannot handle the skipping of
|
||||
# migration numbers.
|
||||
_migrate_cmd(
|
||||
db_driver, db_name, 'version_control', str(earliest_version - 1))
|
||||
|
||||
upgrade_cmd = ['upgrade']
|
||||
if migration_version != 'latest':
|
||||
upgrade_cmd.append(str(migration_version))
|
||||
|
||||
_migrate_cmd(db_driver, db_name, *upgrade_cmd)
|
||||
|
||||
|
||||
def _migrate_cmd(db_driver, db_name, *cmd):
|
||||
manage_py = os.path.join(MIGRATE_REPO, 'manage.py')
|
||||
|
||||
args = ['python', manage_py]
|
||||
args += cmd
|
||||
args += ['--repository=%s' % MIGRATE_REPO,
|
||||
'--url=%s' % db_driver.url(db_name)]
|
||||
|
||||
subprocess.check_call(args)
|
||||
|
||||
|
||||
def _migrate_get_earliest_version():
|
||||
versions_glob = os.path.join(MIGRATE_REPO, 'versions', '???_*.py')
|
||||
|
||||
versions = []
|
||||
for path in glob.iglob(versions_glob):
|
||||
filename = os.path.basename(path)
|
||||
prefix = filename.split('_', 1)[0]
|
||||
try:
|
||||
version = int(prefix)
|
||||
except ValueError:
|
||||
pass
|
||||
versions.append(version)
|
||||
|
||||
versions.sort()
|
||||
return versions[0]
|
||||
|
||||
|
||||
### Git
|
||||
|
||||
|
||||
def git_current_branch_name():
|
||||
ref_name = git_symbolic_ref('HEAD', quiet=True)
|
||||
current_branch_name = ref_name.replace('refs/heads/', '')
|
||||
return current_branch_name
|
||||
|
||||
|
||||
def git_symbolic_ref(ref, quiet=False):
|
||||
args = ['git', 'symbolic-ref', ref]
|
||||
if quiet:
|
||||
args.append('-q')
|
||||
proc = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
return stdout.strip()
|
||||
|
||||
|
||||
def git_checkout(branch_name):
|
||||
subprocess.check_call(['git', 'checkout', branch_name])
|
||||
|
||||
|
||||
def git_has_uncommited_changes():
|
||||
return subprocess.call(['git', 'diff', '--quiet', '--exit-code']) == 1
|
||||
|
||||
|
||||
### Command
|
||||
|
||||
|
||||
def die(msg):
|
||||
print >> sys.stderr, "ERROR: %s" % msg
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def usage(msg=None):
|
||||
if msg:
|
||||
print >> sys.stderr, "ERROR: %s" % msg
|
||||
|
||||
prog = "schema_diff.py"
|
||||
args = ["<mysql|postgres>", "<orig-branch:orig-version>",
|
||||
"<new-branch:new-version>"]
|
||||
|
||||
print >> sys.stderr, "usage: %s %s" % (prog, ' '.join(args))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parse_options():
|
||||
try:
|
||||
db_type = sys.argv[1]
|
||||
except IndexError:
|
||||
usage("must specify DB type")
|
||||
|
||||
try:
|
||||
orig_branch, orig_version = sys.argv[2].split(':')
|
||||
except IndexError:
|
||||
usage('original branch and version required (e.g. master:82)')
|
||||
|
||||
try:
|
||||
new_branch, new_version = sys.argv[3].split(':')
|
||||
except IndexError:
|
||||
usage('new branch and version required (e.g. master:82)')
|
||||
|
||||
return db_type, orig_branch, orig_version, new_branch, new_version
|
||||
|
||||
|
||||
def main():
|
||||
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
ORIG_DB = 'orig_db_%s' % timestamp
|
||||
NEW_DB = 'new_db_%s' % timestamp
|
||||
|
||||
ORIG_DUMP = ORIG_DB + ".dump"
|
||||
NEW_DUMP = NEW_DB + ".dump"
|
||||
|
||||
options = parse_options()
|
||||
db_type, orig_branch, orig_version, new_branch, new_version = options
|
||||
|
||||
# Since we're going to be switching branches, ensure user doesn't have any
|
||||
# uncommited changes
|
||||
if git_has_uncommited_changes():
|
||||
die("You have uncommited changes. Please commit them before running "
|
||||
"this command.")
|
||||
|
||||
db_driver = _get_db_driver_class(db_type)()
|
||||
|
||||
users_branch = git_current_branch_name()
|
||||
git_checkout(orig_branch)
|
||||
|
||||
try:
|
||||
# Dump Original Schema
|
||||
dump_db(db_driver, ORIG_DB, orig_version, ORIG_DUMP)
|
||||
|
||||
# Dump New Schema
|
||||
git_checkout(new_branch)
|
||||
dump_db(db_driver, NEW_DB, new_version, NEW_DUMP)
|
||||
|
||||
diff_files(ORIG_DUMP, NEW_DUMP)
|
||||
finally:
|
||||
git_checkout(users_branch)
|
||||
|
||||
if os.path.exists(ORIG_DUMP):
|
||||
os.unlink(ORIG_DUMP)
|
||||
|
||||
if os.path.exists(NEW_DUMP):
|
||||
os.unlink(NEW_DUMP)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -0,0 +1,83 @@
|
||||
{% extends "basic/layout.html" %}
|
||||
{% set css_files = css_files + ['_static/tweaks.css'] %}
|
||||
{% set script_files = script_files + ['_static/jquery.tweet.js'] %}
|
||||
|
||||
{%- macro sidebar() %}
|
||||
{%- if not embedded %}{% if not theme_nosidebar|tobool %}
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
{%- block sidebarlogo %}
|
||||
{%- if logo %}
|
||||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
|
||||
</a></p>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block sidebartoc %}
|
||||
{%- if display_toc %}
|
||||
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
|
||||
{{ toc }}
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block sidebarrel %}
|
||||
{%- if prev %}
|
||||
<h4>{{ _('Previous topic') }}</h4>
|
||||
<p class="topless"><a href="{{ prev.link|e }}"
|
||||
title="{{ _('previous chapter') }}">{{ prev.title }}</a></p>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<h4>{{ _('Next topic') }}</h4>
|
||||
<p class="topless"><a href="{{ next.link|e }}"
|
||||
title="{{ _('next chapter') }}">{{ next.title }}</a></p>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block sidebarsourcelink %}
|
||||
{%- if show_source and has_source and sourcename %}
|
||||
<h3>{{ _('This Page') }}</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="{{ pathto('_sources/' + sourcename, true)|e }}"
|
||||
rel="nofollow">{{ _('Show Source') }}</a></li>
|
||||
</ul>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- if customsidebar %}
|
||||
{% include customsidebar %}
|
||||
{%- endif %}
|
||||
{%- block sidebarsearch %}
|
||||
{%- if pagename != "search" %}
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>{{ _('Quick search') }}</h3>
|
||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" size="18" />
|
||||
<input type="submit" value="{{ _('Go') }}" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
{{ _('Enter search terms or a module, class or function name.') }}
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif %}{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% block relbar1 %}{% endblock relbar1 %}
|
||||
|
||||
{% block header %}
|
||||
<div id="header">
|
||||
<h1 id="logo"><a href="http://www.openstack.org/">OpenStack</a></h1>
|
||||
<ul id="navigation">
|
||||
<li><a href="http://www.openstack.org/" title="Go to the Home page" class="link">Home</a></li>
|
||||
<li><a href="http://www.openstack.org/projects/" title="Go to the OpenStack Projects page">Projects</a></li>
|
||||
<li><a href="http://www.openstack.org/user-stories/" title="Go to the User Stories page" class="link">User Stories</a></li>
|
||||
<li><a href="http://www.openstack.org/community/" title="Go to the Community page" class="link">Community</a></li>
|
||||
<li><a href="http://www.openstack.org/blog/" title="Go to the OpenStack Blog">Blog</a></li>
|
||||
<li><a href="http://wiki.openstack.org/" title="Go to the OpenStack Wiki">Wiki</a></li>
|
||||
<li><a href="http://docs.openstack.org/" title="Go to OpenStack Documentation" class="current">Documentation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,4 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = nature.css
|
||||
pygments_style = tango
|
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# 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.intersphinx']
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'hacking'
|
||||
copyright = u'2013, OpenStack Foundation'
|
||||
|
||||
# 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
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme_path = ["."]
|
||||
html_theme = '_theme'
|
||||
html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack LLC', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
@ -0,0 +1,21 @@
|
||||
hacking
|
||||
=======
|
||||
|
||||
hacking is a set of flake8 plugins to test or enforce the more stringent
|
||||
style guidelines that the OpenStack project operates under.
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api/autoindex
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -0,0 +1,416 @@
|
||||
/**
|
||||
* Sphinx stylesheet -- basic theme
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||