Merge "Revert "Remove NoVNC from the default enabled services""
diff --git a/FUTURE.rst b/FUTURE.rst
new file mode 100644
index 0000000..11bea30
--- /dev/null
+++ b/FUTURE.rst
@@ -0,0 +1,113 @@
+=============
+ Quo Vadimus
+=============
+
+Where are we going?
+
+This is a document in Devstack to outline where we are headed in the
+future. The future might be near or far, but this is where we'd like
+to be.
+
+This is intended to help people contribute, because it will be a
+little clearer if a contribution takes us closer to or further away to
+our end game.
+
+==================
+ Default Services
+==================
+
+Devstack is designed as a development environment first. There are a
+lot of ways to compose the OpenStack services, but we do need one
+default.
+
+That should be the Compute Layer (currently Glance + Nova + Cinder +
+Neutron Core (not advanced services) + Keystone). It should be the
+base building block going forward, and the introduction point of
+people to OpenStack via Devstack.
+
+================
+ Service Howtos
+================
+
+Starting from the base building block all services included in
+OpenStack should have an overview page in the Devstack
+documentation. That should include the following:
+
+- A helpful high level overview of that service
+- What it depends on (both other OpenStack services and other system
+ components)
+- What new daemons are needed to be started, including where they
+ should live
+
+This provides a map for people doing multinode testing to understand
+what portions are control plane, which should live on worker nodes.
+
+Service how to pages will start with an ugly "This team has provided
+no information about this service" until someone does.
+
+===================
+ Included Services
+===================
+
+Devstack doesn't need to eat the world. Given the existence of the
+external devstack plugin architecture, the future direction is to move
+the bulk of the support code out of devstack itself and into external
+plugins.
+
+This will also promote a more clean separation between services.
+
+=============================
+ Included Backends / Drivers
+=============================
+
+Upstream Devstack should only include Open Source backends / drivers,
+it's intent is for Open Source development of OpenStack. Proprietary
+drivers should be supported via external plugins.
+
+Just being Open Source doesn't mean it should be in upstream Devstack
+if it's not required for base development of OpenStack
+components. When in doubt, external plugins should be used.
+
+========================================
+ OpenStack Services vs. System Services
+========================================
+
+ENABLED_SERVICES is currently entirely too overloaded. We should have
+a separation of actual OpenStack services that you have to run (n-cpu,
+g-api) and required backends like mysql and rabbitmq.
+
+===========================
+ Splitting up of Functions
+===========================
+
+The functions-common file has grown over time, and needs to be split
+up into smaller libraries that handle specific domains.
+
+======================
+ Testing of Functions
+======================
+
+Every function in a functions file should get tests. The devstack
+testing framework is young, but we do have some unit tests for the
+tree, and those should be enhanced.
+
+==============================
+ Not Co-Gating with the World
+==============================
+
+As projects spin up functional test jobs, Devstack should not be
+co-gated with every single one of those. The Devstack team has one of
+the fastest turn arounds for blocking bugs of any Open Stack
+project.
+
+Basic service validation should be included as part of Devstack
+installation to mitigate this.
+
+============================
+ Documenting all the things
+============================
+
+Devstack started off as an explanation as much as an install
+script. We would love contributions to that further enhance the
+comments and explanations about what is happening, even if it seems a
+little pedantic at times.
diff --git a/HACKING.rst b/HACKING.rst
index 3ffe1e2..b3c82a3 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -6,7 +6,7 @@
-------
DevStack is written in UNIX shell script. It uses a number of bash-isms
-and so is limited to Bash (version 3 and up) and compatible shells.
+and so is limited to Bash (version 4 and up) and compatible shells.
Shell script was chosen because it best illustrates the steps used to
set up and interact with OpenStack components.
@@ -20,7 +20,7 @@
contains the usual links for blueprints, bugs, etc.
__ contribute_
-.. _contribute: http://wiki.openstack.org/HowToContribute
+.. _contribute: http://docs.openstack.org/infra/manual/developers.html
__ lp_
.. _lp: https://launchpad.net/~devstack
@@ -110,8 +110,8 @@
* Global configuration that may be referenced in ``local.conf``, i.e. ``DEST``, ``DATA_DIR``
* Global service configuration like ``ENABLED_SERVICES``
* Variables used by multiple services that do not have a clear owner, i.e.
- ``VOLUME_BACKING_FILE_SIZE`` (nova-volumes and cinder) or ``PUBLIC_NETWORK_NAME``
- (nova-network and neutron)
+ ``VOLUME_BACKING_FILE_SIZE`` (nova-compute, nova-volumes and cinder) or
+ ``PUBLIC_NETWORK_NAME`` (nova-network and neutron)
* Variables that can not be cleanly declared in a project file due to
dependency ordering, i.e. the order of sourcing the project files can
not be changed for other reasons but the earlier file needs to dereference a
diff --git a/MAINTAINERS.rst b/MAINTAINERS.rst
index d55135d..a376eb0 100644
--- a/MAINTAINERS.rst
+++ b/MAINTAINERS.rst
@@ -56,12 +56,6 @@
* YAMAMOTO Takashi <yamamoto@valinux.co.jp>
* Fumihiko Kakuma <kakuma@valinux.co.jp>
-Ryu
-~~~
-
-* YAMAMOTO Takashi <yamamoto@valinux.co.jp>
-* Fumihiko Kakuma <kakuma@valinux.co.jp>
-
Sahara
~~~~~~
diff --git a/README.md b/README.md
index 7eacebd..40060a7 100644
--- a/README.md
+++ b/README.md
@@ -215,21 +215,6 @@
[[post-config|/$Q_PLUGIN_CONF_FILE]]
[linuxbridge] # or [ovs]
-* ``Q_AGENT_EXTRA_AGENT_OPTS``:
-
- [[post-config|/$Q_PLUGIN_CONF_FILE]]
- [agent]
-
-* ``Q_AGENT_EXTRA_SRV_OPTS``:
-
- [[post-config|/$Q_PLUGIN_CONF_FILE]]
- [linuxbridge] # or [ovs]
-
-* ``Q_SRV_EXTRA_DEFAULT_OPTS``:
-
- [[post-config|$NEUTRON_CONF]]
- [DEFAULT]
-
Example extra config in `local.conf`:
[[post-config|/$Q_PLUGIN_CONF_FILE]]
diff --git a/clean.sh b/clean.sh
index db1a1e4..50d414c 100755
--- a/clean.sh
+++ b/clean.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/bash
# **clean.sh**
@@ -18,7 +18,7 @@
FILES=$TOP_DIR/files
# Load local configuration
-source $TOP_DIR/stackrc
+source $TOP_DIR/openrc
# Get the variables that are set in stack.sh
if [[ -r $TOP_DIR/.stackenv ]]; then
@@ -50,7 +50,6 @@
source $TOP_DIR/lib/ceilometer
source $TOP_DIR/lib/heat
source $TOP_DIR/lib/neutron
-source $TOP_DIR/lib/baremetal
source $TOP_DIR/lib/ironic
source $TOP_DIR/lib/trove
@@ -84,7 +83,10 @@
fi
# Clean projects
-cleanup_cinder
+
+# BUG: cinder tgt doesn't exit cleanly if it's not running.
+cleanup_cinder || /bin/true
+
cleanup_glance
cleanup_keystone
cleanup_nova
diff --git a/doc/source/assets/css/bootstrap.css b/doc/source/assets/css/bootstrap.css
deleted file mode 100644
index 5fd10bc..0000000
--- a/doc/source/assets/css/bootstrap.css
+++ /dev/null
@@ -1,616 +0,0 @@
-article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
-audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
-audio:not([controls]){display:none;}
-html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
-a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-a:hover,a:active{outline:0;}
-sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
-sup{top:-0.5em;}
-sub{bottom:-0.25em;}
-img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;}
-button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
-button,input{*overflow:visible;line-height:normal;}
-button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
-button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
-input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
-input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
-textarea{overflow:auto;vertical-align:top;}
-body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
-a{color:#0088cc;text-decoration:none;}
-a:hover{color:#005580;text-decoration:underline;}
-.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
-.row:after{clear:both;}
-[class*="span"]{float:left;margin-left:20px;}
-.span1{width:60px;}
-.span2{width:140px;}
-.span3{width:220px;}
-.span4{width:300px;}
-.span5{width:380px;}
-.span6{width:460px;}
-.span7{width:540px;}
-.span8{width:620px;}
-.span9{width:700px;}
-.span10{width:780px;}
-.span11{width:860px;}
-.span12,.container{width:940px;}
-.offset1{margin-left:100px;}
-.offset2{margin-left:180px;}
-.offset3{margin-left:260px;}
-.offset4{margin-left:340px;}
-.offset5{margin-left:420px;}
-.offset6{margin-left:500px;}
-.offset7{margin-left:580px;}
-.offset8{margin-left:660px;}
-.offset9{margin-left:740px;}
-.offset10{margin-left:820px;}
-.offset11{margin-left:900px;}
-.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
-.row-fluid:after{clear:both;}
-.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
-.row-fluid>[class*="span"]:first-child{margin-left:0;}
-.row-fluid .span1{width:6.382978723%;}
-.row-fluid .span2{width:14.89361702%;}
-.row-fluid .span3{width:23.404255317%;}
-.row-fluid .span4{width:31.914893614%;}
-.row-fluid .span5{width:40.425531911%;}
-.row-fluid .span6{width:48.93617020799999%;}
-.row-fluid .span7{width:57.446808505%;}
-.row-fluid .span8{width:65.95744680199999%;}
-.row-fluid .span9{width:74.468085099%;}
-.row-fluid .span10{width:82.97872339599999%;}
-.row-fluid .span11{width:91.489361693%;}
-.row-fluid .span12{width:99.99999998999999%;}
-.container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
-.container:after{clear:both;}
-.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
-.container-fluid:after{clear:both;}
-p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
-.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
-h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
-h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
-h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
-h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
-h4,h5,h6{line-height:18px;}
-h4{font-size:14px;}h4 small{font-size:12px;}
-h5{font-size:12px;}
-h6{font-size:11px;color:#999999;text-transform:uppercase;}
-.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
-.page-header h1{line-height:1;}
-ul,ol{padding:0;margin:0 0 9px 25px;}
-ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
-ul{list-style:disc;}
-ol{list-style:decimal;}
-li{line-height:18px;}
-ul.unstyled{margin-left:0;list-style:none;}
-dl{margin-bottom:18px;}
-dt,dd{line-height:18px;}
-dt{font-weight:bold;}
-dd{margin-left:9px;}
-hr{margin:18px 0;border:0;border-top:1px solid #e5e5e5;border-bottom:1px solid #ffffff;}
-strong{font-weight:bold;}
-em{font-style:italic;}
-.muted{color:#999999;}
-abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;}
-blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
-blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
-blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
-q:before,q:after,blockquote:before,blockquote:after{content:"";}
-address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
-small{font-size:100%;}
-cite{font-style:normal;}
-code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-code{padding:3px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
-pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;}pre.prettyprint{margin-bottom:18px;}
-pre code{padding:0;background-color:transparent;}
-form{margin:0 0 18px;}
-fieldset{padding:0;margin:0;border:0;}
-legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}
-label,input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;}
-label{display:block;margin-bottom:5px;color:#333333;}
-input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-.uneditable-textarea{width:auto;height:auto;}
-label input,label textarea,label select{display:block;}
-input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:0;cursor:pointer;border-radius:0 \0/;}
-input[type="file"]{padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
-select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
-select{width:220px;background-color:#ffffff;}
-select[multiple],select[size]{height:auto;}
-input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-textarea{height:auto;}
-input[type="hidden"]{display:none;}
-.radio,.checkbox{padding-left:18px;}
-.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
-.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
-.radio.inline,.checkbox.inline{display:inline-block;margin-bottom:0;vertical-align:middle;}
-.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
-.controls>.radio.inline:first-child,.controls>.checkbox.inline:first-child{padding-top:0;}
-input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
-input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
-input[type="file"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-.input-mini{width:60px;}
-.input-small{width:90px;}
-.input-medium{width:150px;}
-.input-large{width:210px;}
-.input-xlarge{width:270px;}
-.input-xxlarge{width:530px;}
-input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
-input.span1,textarea.span1,.uneditable-input.span1{width:50px;}
-input.span2,textarea.span2,.uneditable-input.span2{width:130px;}
-input.span3,textarea.span3,.uneditable-input.span3{width:210px;}
-input.span4,textarea.span4,.uneditable-input.span4{width:290px;}
-input.span5,textarea.span5,.uneditable-input.span5{width:370px;}
-input.span6,textarea.span6,.uneditable-input.span6{width:450px;}
-input.span7,textarea.span7,.uneditable-input.span7{width:530px;}
-input.span8,textarea.span8,.uneditable-input.span8{width:610px;}
-input.span9,textarea.span9,.uneditable-input.span9{width:690px;}
-input.span10,textarea.span10,.uneditable-input.span10{width:770px;}
-input.span11,textarea.span11,.uneditable-input.span11{width:850px;}
-input.span12,textarea.span12,.uneditable-input.span12{width:930px;}
-input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;}
-.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
-.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
-.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
-.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
-.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
-.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
-.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
-.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
-.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
-input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
-.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;}
-.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
-:-moz-placeholder{color:#999999;}
-::-webkit-input-placeholder{color:#999999;}
-.help-block{margin-top:5px;margin-bottom:0;color:#999999;}
-.help-inline{display:inline-block;*display:inline;*zoom:1;margin-bottom:9px;vertical-align:middle;padding-left:5px;}
-.input-prepend,.input-append{margin-bottom:5px;*zoom:1;}.input-prepend:before,.input-append:before,.input-prepend:after,.input-append:after{display:table;content:"";}
-.input-prepend:after,.input-append:after{clear:both;}
-.input-prepend input,.input-append input,.input-prepend .uneditable-input,.input-append .uneditable-input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
-.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
-.input-prepend .add-on,.input-append .add-on{float:left;display:block;width:auto;min-width:16px;height:18px;margin-right:-1px;padding:4px 5px;font-weight:normal;line-height:18px;color:#999999;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#f5f5f5;border:1px solid #ccc;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
-.input-prepend .add-on{*margin-top:1px;}
-.input-append input,.input-append .uneditable-input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-append .uneditable-input{border-right-color:#ccc;}
-.input-append .add-on{margin-right:0;margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.input-append input:first-child{*margin-left:-160px;}.input-append input:first-child+.add-on{*margin-left:-21px;}
-.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
-.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input{display:inline-block;margin-bottom:0;}
-.form-search label,.form-inline label,.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{display:inline-block;}
-.form-search .input-append .add-on,.form-inline .input-prepend .add-on,.form-search .input-append .add-on,.form-inline .input-prepend .add-on{vertical-align:middle;}
-.control-group{margin-bottom:9px;}
-.form-horizontal legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
-.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
-.form-horizontal .control-group:after{clear:both;}
-.form-horizontal .control-group>label{float:left;width:140px;padding-top:5px;text-align:right;}
-.form-horizontal .controls{margin-left:160px;}
-.form-horizontal .form-actions{padding-left:160px;}
-table{max-width:100%;border-collapse:collapse;border-spacing:0;}
-.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;border-top:1px solid #ddd;}
-.table th{font-weight:bold;vertical-align:bottom;}
-.table td{vertical-align:top;}
-.table thead:first-child tr th,.table thead:first-child tr td{border-top:0;}
-.table tbody+tbody{border-top:2px solid #ddd;}
-.table-condensed th,.table-condensed td{padding:4px 5px;}
-.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;}
-.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
-.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
-.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
-.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
-.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
-.table-striped tbody tr:nth-child(even) td,.table-striped tbody tr:nth-child(even) th{background-color:#f1f1f1;}
-table .span1{float:none;width:44px;margin-left:0;}
-table .span2{float:none;width:124px;margin-left:0;}
-table .span3{float:none;width:204px;margin-left:0;}
-table .span4{float:none;width:284px;margin-left:0;}
-table .span5{float:none;width:364px;margin-left:0;}
-table .span6{float:none;width:444px;margin-left:0;}
-table .span7{float:none;width:524px;margin-left:0;}
-table .span8{float:none;width:604px;margin-left:0;}
-table .span9{float:none;width:684px;margin-left:0;}
-table .span10{float:none;width:764px;margin-left:0;}
-table .span11{float:none;width:844px;margin-left:0;}
-table .span12{float:none;width:924px;margin-left:0;}
-[class^="icon-"]{display:inline-block;width:14px;height:14px;vertical-align:text-top;background-image:url(../img/glyphicons-halflings.png);background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child{*margin-left:0;}
-.icon-white{background-image:url(../img/glyphicons-halflings-white.png);}
-.icon-glass{background-position:0 0;}
-.icon-music{background-position:-24px 0;}
-.icon-search{background-position:-48px 0;}
-.icon-envelope{background-position:-72px 0;}
-.icon-heart{background-position:-96px 0;}
-.icon-star{background-position:-120px 0;}
-.icon-star-empty{background-position:-144px 0;}
-.icon-user{background-position:-168px 0;}
-.icon-film{background-position:-192px 0;}
-.icon-th-large{background-position:-216px 0;}
-.icon-th{background-position:-240px 0;}
-.icon-th-list{background-position:-264px 0;}
-.icon-ok{background-position:-288px 0;}
-.icon-remove{background-position:-312px 0;}
-.icon-zoom-in{background-position:-336px 0;}
-.icon-zoom-out{background-position:-360px 0;}
-.icon-off{background-position:-384px 0;}
-.icon-signal{background-position:-408px 0;}
-.icon-cog{background-position:-432px 0;}
-.icon-trash{background-position:-456px 0;}
-.icon-home{background-position:0 -24px;}
-.icon-file{background-position:-24px -24px;}
-.icon-time{background-position:-48px -24px;}
-.icon-road{background-position:-72px -24px;}
-.icon-download-alt{background-position:-96px -24px;}
-.icon-download{background-position:-120px -24px;}
-.icon-upload{background-position:-144px -24px;}
-.icon-inbox{background-position:-168px -24px;}
-.icon-play-circle{background-position:-192px -24px;}
-.icon-repeat{background-position:-216px -24px;}
-.icon-refresh{background-position:-240px -24px;}
-.icon-list-alt{background-position:-264px -24px;}
-.icon-lock{background-position:-287px -24px;}
-.icon-flag{background-position:-312px -24px;}
-.icon-headphones{background-position:-336px -24px;}
-.icon-volume-off{background-position:-360px -24px;}
-.icon-volume-down{background-position:-384px -24px;}
-.icon-volume-up{background-position:-408px -24px;}
-.icon-qrcode{background-position:-432px -24px;}
-.icon-barcode{background-position:-456px -24px;}
-.icon-tag{background-position:0 -48px;}
-.icon-tags{background-position:-25px -48px;}
-.icon-book{background-position:-48px -48px;}
-.icon-bookmark{background-position:-72px -48px;}
-.icon-print{background-position:-96px -48px;}
-.icon-camera{background-position:-120px -48px;}
-.icon-font{background-position:-144px -48px;}
-.icon-bold{background-position:-167px -48px;}
-.icon-italic{background-position:-192px -48px;}
-.icon-text-height{background-position:-216px -48px;}
-.icon-text-width{background-position:-240px -48px;}
-.icon-align-left{background-position:-264px -48px;}
-.icon-align-center{background-position:-288px -48px;}
-.icon-align-right{background-position:-312px -48px;}
-.icon-align-justify{background-position:-336px -48px;}
-.icon-list{background-position:-360px -48px;}
-.icon-indent-left{background-position:-384px -48px;}
-.icon-indent-right{background-position:-408px -48px;}
-.icon-facetime-video{background-position:-432px -48px;}
-.icon-picture{background-position:-456px -48px;}
-.icon-pencil{background-position:0 -72px;}
-.icon-map-marker{background-position:-24px -72px;}
-.icon-adjust{background-position:-48px -72px;}
-.icon-tint{background-position:-72px -72px;}
-.icon-edit{background-position:-96px -72px;}
-.icon-share{background-position:-120px -72px;}
-.icon-check{background-position:-144px -72px;}
-.icon-move{background-position:-168px -72px;}
-.icon-step-backward{background-position:-192px -72px;}
-.icon-fast-backward{background-position:-216px -72px;}
-.icon-backward{background-position:-240px -72px;}
-.icon-play{background-position:-264px -72px;}
-.icon-pause{background-position:-288px -72px;}
-.icon-stop{background-position:-312px -72px;}
-.icon-forward{background-position:-336px -72px;}
-.icon-fast-forward{background-position:-360px -72px;}
-.icon-step-forward{background-position:-384px -72px;}
-.icon-eject{background-position:-408px -72px;}
-.icon-chevron-left{background-position:-432px -72px;}
-.icon-chevron-right{background-position:-456px -72px;}
-.icon-plus-sign{background-position:0 -96px;}
-.icon-minus-sign{background-position:-24px -96px;}
-.icon-remove-sign{background-position:-48px -96px;}
-.icon-ok-sign{background-position:-72px -96px;}
-.icon-question-sign{background-position:-96px -96px;}
-.icon-info-sign{background-position:-120px -96px;}
-.icon-screenshot{background-position:-144px -96px;}
-.icon-remove-circle{background-position:-168px -96px;}
-.icon-ok-circle{background-position:-192px -96px;}
-.icon-ban-circle{background-position:-216px -96px;}
-.icon-arrow-left{background-position:-240px -96px;}
-.icon-arrow-right{background-position:-264px -96px;}
-.icon-arrow-up{background-position:-289px -96px;}
-.icon-arrow-down{background-position:-312px -96px;}
-.icon-share-alt{background-position:-336px -96px;}
-.icon-resize-full{background-position:-360px -96px;}
-.icon-resize-small{background-position:-384px -96px;}
-.icon-plus{background-position:-408px -96px;}
-.icon-minus{background-position:-433px -96px;}
-.icon-asterisk{background-position:-456px -96px;}
-.icon-exclamation-sign{background-position:0 -120px;}
-.icon-gift{background-position:-24px -120px;}
-.icon-leaf{background-position:-48px -120px;}
-.icon-fire{background-position:-72px -120px;}
-.icon-eye-open{background-position:-96px -120px;}
-.icon-eye-close{background-position:-120px -120px;}
-.icon-warning-sign{background-position:-144px -120px;}
-.icon-plane{background-position:-168px -120px;}
-.icon-calendar{background-position:-192px -120px;}
-.icon-random{background-position:-216px -120px;}
-.icon-comment{background-position:-240px -120px;}
-.icon-magnet{background-position:-264px -120px;}
-.icon-chevron-up{background-position:-288px -120px;}
-.icon-chevron-down{background-position:-313px -119px;}
-.icon-retweet{background-position:-336px -120px;}
-.icon-shopping-cart{background-position:-360px -120px;}
-.icon-folder-close{background-position:-384px -120px;}
-.icon-folder-open{background-position:-408px -120px;}
-.icon-resize-vertical{background-position:-432px -119px;}
-.icon-resize-horizontal{background-position:-456px -118px;}
-.dropdown{position:relative;}
-.dropdown-toggle{*margin-bottom:-3px;}
-.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
-.caret{display:inline-block;width:0;height:0;text-indent:-99999px;*text-indent:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"\2193";}
-.dropdown .caret{margin-top:8px;margin-left:2px;}
-.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
-.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;max-width:220px;_width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.bottom-up{top:auto;bottom:100%;margin-bottom:2px;}
-.dropdown-menu .divider{height:1px;margin:5px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
-.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#555555;white-space:nowrap;}
-.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
-.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
-.dropdown.open .dropdown-menu{display:block;}
-.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
-.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
-.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
-.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
-.btn{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:first-child{*margin-left:0;}
-.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
-.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;}
-.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
-.btn-large .icon{margin-top:1px;}
-.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
-.btn-small .icon{margin-top:-1px;}
-.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
-.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active{color:rgba(255, 255, 255, 0.75);}
-.btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;}
-.btn-primary:active,.btn-primary.active{background-color:#003399 \9;}
-.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
-.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
-.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
-.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
-.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
-.btn-success:active,.btn-success.active{background-color:#408140 \9;}
-.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
-.btn-info:active,.btn-info.active{background-color:#24748c \9;}
-button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
-button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;}
-button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;}
-.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
-.btn-group:after{clear:both;}
-.btn-group:first-child{*margin-left:0;}
-.btn-group+.btn-group{margin-left:5px;}
-.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
-.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
-.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
-.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
-.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
-.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
-.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
-.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:5px;*padding-bottom:5px;}
-.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
-.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn .caret{margin-top:7px;margin-left:0;}
-.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
-.btn-primary .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret{border-top-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
-.btn-small .caret{margin-top:4px;}
-.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.alert,.alert-heading{color:#c09853;}
-.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
-.alert-success{background-color:#dff0d8;border-color:#d6e9c6;}
-.alert-success,.alert-success .alert-heading{color:#468847;}
-.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;}
-.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading{color:#b94a48;}
-.alert-info{background-color:#d9edf7;border-color:#bce8f1;}
-.alert-info,.alert-info .alert-heading{color:#3a87ad;}
-.alert-block{padding-top:14px;padding-bottom:14px;}
-.alert-block>p,.alert-block>ul{margin-bottom:0;}
-.alert-block p+p{margin-top:5px;}
-.nav{margin-left:0;margin-bottom:18px;list-style:none;}
-.nav>li>a{display:block;}
-.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
-.nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;}
-.nav-list>li>a,.nav-list .nav-header{display:block;padding:3px 15px;margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
-.nav-list .nav-header{font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-transform:uppercase;}
-.nav-list>li+.nav-header{margin-top:9px;}
-.nav-list .active>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
-.nav-list .icon{margin-right:2px;}
-.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
-.nav-tabs:after,.nav-pills:after{clear:both;}
-.nav-tabs>li,.nav-pills>li{float:left;}
-.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
-.nav-tabs{border-bottom:1px solid #ddd;}
-.nav-tabs>li{margin-bottom:-1px;}
-.nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
-.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
-.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
-.nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;}
-.nav-stacked>li{float:none;}
-.nav-stacked>li>a{margin-right:0;}
-.nav-tabs.nav-stacked{border-bottom:0;}
-.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
-.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
-.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
-.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
-.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
-.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
-.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;}
-.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;}
-.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;}
-.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
-.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
-.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
-.tabs-stacked .open>a:hover{border-color:#999999;}
-.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
-.tabbable:after{clear:both;}
-.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
-.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
-.tab-content>.active,.pill-content>.active{display:block;}
-.tabs-below .nav-tabs{border-top:1px solid #ddd;}
-.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
-.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
-.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
-.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
-.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
-.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
-.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
-.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
-.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
-.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
-.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
-.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
-.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
-.navbar{overflow:visible;margin-bottom:18px;}
-.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
-.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
-.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
-.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
-.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
-.nav-collapse.collapse{height:auto;}
-.navbar .brand:hover{text-decoration:none;}
-.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
-.navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;}
-.navbar .btn,.navbar .btn-group{margin-top:5px;}
-.navbar .btn-group .btn{margin-top:0;}
-.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
-.navbar-form:after{clear:both;}
-.navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;}
-.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
-.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
-.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;}
-.navbar-search .search-query::-webkit-input-placeholder{color:#eeeeee;}
-.navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);}
-.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
-.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;}
-.navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
-.navbar .nav.pull-right{float:right;}
-.navbar .nav>li{display:block;float:left;}
-.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
-.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
-.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;background-color:rgba(0, 0, 0, 0.5);}
-.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
-.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
-.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
-.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
-.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;}
-.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
-.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
-.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
-.navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;}
-.navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;}
-.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;}
-.breadcrumb .divider{padding:0 5px;color:#999999;}
-.breadcrumb .active a{color:#333333;}
-.pagination{height:36px;margin:18px 0;}
-.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
-.pagination li{display:inline;}
-.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
-.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
-.pagination .active a{color:#999999;cursor:default;}
-.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
-.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.pagination-centered{text-align:center;}
-.pagination-right{text-align:right;}
-.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
-.pager:after{clear:both;}
-.pager li{display:inline;}
-.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
-.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
-.pager .next a{float:right;}
-.pager .previous a{float:left;}
-.modal-open .dropdown-menu{z-index:2050;}
-.modal-open .dropdown.open{*z-index:2050;}
-.modal-open .popover{z-index:2060;}
-.modal-open .tooltip{z-index:2070;}
-.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
-.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
-.modal{position:fixed;top:50%;left:50%;z-index:1050;max-height:500px;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
-.modal.fade.in{top:50%;}
-.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
-.modal-body{padding:15px;}
-.modal-footer{padding:14px 15px 15px;margin-bottom:0;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
-.modal-footer:after{clear:both;}
-.modal-footer .btn{float:right;margin-left:5px;margin-bottom:0;}
-.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
-.tooltip.top{margin-top:-2px;}
-.tooltip.right{margin-left:2px;}
-.tooltip.bottom{margin-top:2px;}
-.tooltip.left{margin-left:-2px;}
-.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
-.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
-.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
-.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.tooltip-arrow{position:absolute;width:0;height:0;}
-.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
-.popover.right{margin-left:5px;}
-.popover.bottom{margin-top:5px;}
-.popover.left{margin-left:-5px;}
-.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
-.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
-.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
-.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.popover .arrow{position:absolute;width:0;height:0;}
-.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
-.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
-.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
-.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
-.thumbnails:after{clear:both;}
-.thumbnails>li{float:left;margin:0 0 18px 20px;}
-.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
-a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
-.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
-.thumbnail .caption{padding:9px;}
-.label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-.label-important{background-color:#b94a48;}
-.label-warning{background-color:#f89406;}
-.label-success{background-color:#468847;}
-.label-info{background-color:#3a87ad;}
-@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
-.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
-.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
-.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
-.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
-.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
-.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.accordion{margin-bottom:18px;}
-.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.accordion-heading{border-bottom:0;}
-.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
-.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
-.carousel{position:relative;margin-bottom:18px;line-height:1;}
-.carousel-inner{overflow:hidden;width:100%;position:relative;}
-.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
-.carousel .item>img{display:block;line-height:1;}
-.carousel .active,.carousel .next,.carousel .prev{display:block;}
-.carousel .active{left:0;}
-.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
-.carousel .next{left:100%;}
-.carousel .prev{left:-100%;}
-.carousel .next.left,.carousel .prev.right{left:0;}
-.carousel .active.left{left:-100%;}
-.carousel .active.right{left:100%;}
-.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
-.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
-.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
-.carousel-caption h4,.carousel-caption p{color:#ffffff;}
-.hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;}
-.hero-unit p{font-size:18px;font-weight:200;line-height:27px;}
-.pull-right{float:right;}
-.pull-left{float:left;}
-.hide{display:none;}
-.show{display:block;}
-.invisible{visibility:hidden;}
-
-
-/* Responsive css */
-
-/*.hidden{display:none;visibility:hidden;}*/
-/*@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:768px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:980px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.801104972%;} .row-fluid .span2{width:14.364640883%;} .row-fluid .span3{width:22.928176794%;} .row-fluid .span4{width:31.491712705%;} .row-fluid .span5{width:40.055248616%;} .row-fluid .span6{width:48.618784527%;} .row-fluid .span7{width:57.182320438000005%;} .row-fluid .span8{width:65.74585634900001%;} .row-fluid .span9{width:74.30939226%;} .row-fluid .span10{width:82.87292817100001%;} .row-fluid .span11{width:91.436464082%;} .row-fluid .span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:980px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.982905983%;} .row-fluid .span2{width:14.529914530000001%;} .row-fluid .span3{width:23.076923077%;} .row-fluid .span4{width:31.623931624%;} .row-fluid .span5{width:40.170940171000005%;} .row-fluid .span6{width:48.717948718%;} .row-fluid .span7{width:57.264957265%;} .row-fluid .span8{width:65.81196581200001%;} .row-fluid .span9{width:74.358974359%;} .row-fluid .span10{width:82.905982906%;} .row-fluid .span11{width:91.45299145300001%;} .row-fluid .span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}}*/
diff --git a/doc/source/assets/css/local.css b/doc/source/assets/css/local.css
deleted file mode 100644
index c8667c4..0000000
--- a/doc/source/assets/css/local.css
+++ /dev/null
@@ -1,122 +0,0 @@
-
-a.brand {
- background: url(../images/small_logo.png) no-repeat left 6px;
- width: 166px;
- text-indent: -9999px;
-}
-
-code {
- background-color: #ffffff;
-}
-
-#home .hero-unit {
- background: url(../images/header_bg.png) top left repeat-x;
- color: #fff;
- padding-left: 25px;
- padding-right: 25px;
- height: 161px;
-}
-
-#home .hero-unit {
- margin-right: ;
-}
-
-h1#main_header {
- background: url(../images/logo.png) top left no-repeat;
- text-indent: -9999px;
-}
-
-.sub_header {
- font-size: 12px;
- font-family: "anivers";
- font-weight: normal;
- width: 420px;
-}
-
-#home .hero-unit a {
- color: #fff;
- text-decoration: underline;
-}
-
-.clear {
- clear: both;
-}
-
-ol#getting_started {
- list-style: none;
- width: 396px;
- margin-top: -45px;
- margin-right: 0;
- margin-left: 72px;
-}
-
-ol#getting_started li {
- background: url(../images/quickstart.png) top left no-repeat;
-}
-
-ol#getting_started li pre {
- font-size: 11px;
- padding: 5px;
- background: #4d7ead;
- border-color: #2c5d8d;
- color: #fff;
- overflow: auto;
-}
-
-li#ubuntu {
- height: 46px;
- padding: ;
- padding-left: 82px;
- padding-top: 27px;
- margin-bottom: 14px;
-}
-
-li#git {
- background-position: left -70px !important;
- height: 61px;
- padding: ;
- padding-left: 82px;
- padding-top: 4px;
- margin-bottom: 25px;
-}
-
-
-li#install {
- background-position: left bottom !important;
- height: 61px;
- padding: ;
- padding-left: 82px;
- padding-top: 4px;
- margin-bottom: 25px;
-}
-
-h2 small {
- font-size: 12px;
- font-style: italic;
-}
-
-.container section {
- margin-bottom: 25px;
-}
-
-
-thead, tfoot{
- background: #ededed;
- color: #444444;
-}
-
-table {
- color: #444;
-}
-a.table-action {
- display: block;
- width: 45px;
-}
-
-footer {
- clear: both;
-}
-
-.wat {
- margin-top: 33px;
-}
diff --git a/doc/source/assets/images/header_bg.png b/doc/source/assets/images/header_bg.png
deleted file mode 100644
index 4e6b2b8..0000000
--- a/doc/source/assets/images/header_bg.png
+++ /dev/null
Binary files differ
diff --git a/doc/source/assets/images/logo-blue.png b/doc/source/assets/images/logo-blue.png
new file mode 100644
index 0000000..6b363af
--- /dev/null
+++ b/doc/source/assets/images/logo-blue.png
Binary files differ
diff --git a/doc/source/assets/images/logo-blue.xcf b/doc/source/assets/images/logo-blue.xcf
new file mode 100644
index 0000000..fff75ee
--- /dev/null
+++ b/doc/source/assets/images/logo-blue.xcf
Binary files differ
diff --git a/doc/source/assets/images/quickstart.png b/doc/source/assets/images/quickstart.png
deleted file mode 100644
index 735617b..0000000
--- a/doc/source/assets/images/quickstart.png
+++ /dev/null
Binary files differ
diff --git a/doc/source/assets/js/bootstrap.js b/doc/source/assets/js/bootstrap.js
deleted file mode 100644
index c832ccb..0000000
--- a/doc/source/assets/js/bootstrap.js
+++ /dev/null
@@ -1,1722 +0,0 @@
-/* ===================================================
- * bootstrap-transition.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#transitions
- * ===================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ========================================================== */
-
-!function( $ ) {
-
- $(function () {
-
- "use strict"
-
- /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
- * ======================================================= */
-
- $.support.transition = (function () {
- var thisBody = document.body || document.documentElement
- , thisStyle = thisBody.style
- , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
-
- return support && {
- end: (function () {
- var transitionEnd = "TransitionEnd"
- if ( $.browser.webkit ) {
- transitionEnd = "webkitTransitionEnd"
- } else if ( $.browser.mozilla ) {
- transitionEnd = "transitionend"
- } else if ( $.browser.opera ) {
- transitionEnd = "oTransitionEnd"
- }
- return transitionEnd
- }())
- }
- })()
-
- })
-
-}( window.jQuery )
-/* ==========================================================
- * bootstrap-alert.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#alerts
- * ==========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ========================================================== */
-
-
-!function( $ ){
-
- "use strict"
-
- /* ALERT CLASS DEFINITION
- * ====================== */
-
- var dismiss = '[data-dismiss="alert"]'
- , Alert = function ( el ) {
- $(el).on('click', dismiss, this.close)
- }
-
- Alert.prototype = {
-
- constructor: Alert
-
- , close: function ( e ) {
- var $this = $(this)
- , selector = $this.attr('data-target')
- , $parent
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- $parent = $(selector)
- $parent.trigger('close')
-
- e && e.preventDefault()
-
- $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
-
- $parent.removeClass('in')
-
- function removeElement() {
- $parent.remove()
- $parent.trigger('closed')
- }
-
- $.support.transition && $parent.hasClass('fade') ?
- $parent.on($.support.transition.end, removeElement) :
- removeElement()
- }
-
- }
-
-
- /* ALERT PLUGIN DEFINITION
- * ======================= */
-
- $.fn.alert = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('alert')
- if (!data) $this.data('alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.alert.Constructor = Alert
-
-
- /* ALERT DATA-API
- * ============== */
-
- $(function () {
- $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
- })
-
-}( window.jQuery )
-/* ============================================================
- * bootstrap-button.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#buttons
- * ============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ============================================================ */
-
-!function( $ ){
-
- "use strict"
-
- /* BUTTON PUBLIC CLASS DEFINITION
- * ============================== */
-
- var Button = function ( element, options ) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.button.defaults, options)
- }
-
- Button.prototype = {
-
- constructor: Button
-
- , setState: function ( state ) {
- var d = 'disabled'
- , $el = this.$element
- , data = $el.data()
- , val = $el.is('input') ? 'val' : 'html'
-
- state = state + 'Text'
- data.resetText || $el.data('resetText', $el[val]())
-
- $el[val](data[state] || this.options[state])
-
- // push to event loop to allow forms to submit
- setTimeout(function () {
- state == 'loadingText' ?
- $el.addClass(d).attr(d, d) :
- $el.removeClass(d).removeAttr(d)
- }, 0)
- }
-
- , toggle: function () {
- var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
-
- $parent && $parent
- .find('.active')
- .removeClass('active')
-
- this.$element.toggleClass('active')
- }
-
- }
-
-
- /* BUTTON PLUGIN DEFINITION
- * ======================== */
-
- $.fn.button = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('button')
- , options = typeof option == 'object' && option
- if (!data) $this.data('button', (data = new Button(this, options)))
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
-
- $.fn.button.defaults = {
- loadingText: 'loading...'
- }
-
- $.fn.button.Constructor = Button
-
-
- /* BUTTON DATA-API
- * =============== */
-
- $(function () {
- $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
- $(e.target).button('toggle')
- })
- })
-
-}( window.jQuery )
-/* ==========================================================
- * bootstrap-carousel.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#carousel
- * ==========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ========================================================== */
-
-
-!function( $ ){
-
- "use strict"
-
- /* CAROUSEL CLASS DEFINITION
- * ========================= */
-
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.carousel.defaults, options)
- this.options.slide && this.slide(this.options.slide)
- }
-
- Carousel.prototype = {
-
- cycle: function () {
- this.interval = setInterval($.proxy(this.next, this), this.options.interval)
- return this
- }
-
- , to: function (pos) {
- var $active = this.$element.find('.active')
- , children = $active.parent().children()
- , activePos = children.index($active)
- , that = this
-
- if (pos > (children.length - 1) || pos < 0) return
-
- if (this.sliding) {
- return this.$element.one('slid', function () {
- that.to(pos)
- })
- }
-
- if (activePos == pos) {
- return this.pause().cycle()
- }
-
- return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
- }
-
- , pause: function () {
- clearInterval(this.interval)
- return this
- }
-
- , next: function () {
- if (this.sliding) return
- return this.slide('next')
- }
-
- , prev: function () {
- if (this.sliding) return
- return this.slide('prev')
- }
-
- , slide: function (type, next) {
- var $active = this.$element.find('.active')
- , $next = next || $active[type]()
- , isCycling = this.interval
- , direction = type == 'next' ? 'left' : 'right'
- , fallback = type == 'next' ? 'first' : 'last'
- , that = this
-
- this.sliding = true
-
- isCycling && this.pause()
-
- $next = $next.length ? $next : this.$element.find('.item')[fallback]()
-
- if (!$.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger('slide')
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid')
- } else {
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- this.$element.trigger('slide')
- this.$element.one($.support.transition.end, function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () { that.$element.trigger('slid') }, 0)
- })
- }
-
- isCycling && this.cycle()
-
- return this
- }
-
- }
-
-
- /* CAROUSEL PLUGIN DEFINITION
- * ========================== */
-
- $.fn.carousel = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('carousel')
- , options = typeof option == 'object' && option
- if (!data) $this.data('carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (typeof option == 'string' || (option = options.slide)) data[option]()
- else data.cycle()
- })
- }
-
- $.fn.carousel.defaults = {
- interval: 5000
- }
-
- $.fn.carousel.Constructor = Carousel
-
-
- /* CAROUSEL DATA-API
- * ================= */
-
- $(function () {
- $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
- var $this = $(this), href
- , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
- $target.carousel(options)
- e.preventDefault()
- })
- })
-
-}( window.jQuery )
-/* =============================================================
- * bootstrap-collapse.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#collapse
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ============================================================ */
-
-!function( $ ){
-
- "use strict"
-
- var Collapse = function ( element, options ) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.collapse.defaults, options)
-
- if (this.options["parent"]) {
- this.$parent = $(this.options["parent"])
- }
-
- this.options.toggle && this.toggle()
- }
-
- Collapse.prototype = {
-
- constructor: Collapse
-
- , dimension: function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
- }
-
- , show: function () {
- var dimension = this.dimension()
- , scroll = $.camelCase(['scroll', dimension].join('-'))
- , actives = this.$parent && this.$parent.find('.in')
- , hasData
-
- if (actives && actives.length) {
- hasData = actives.data('collapse')
- actives.collapse('hide')
- hasData || actives.data('collapse', null)
- }
-
- this.$element[dimension](0)
- this.transition('addClass', 'show', 'shown')
- this.$element[dimension](this.$element[0][scroll])
-
- }
-
- , hide: function () {
- var dimension = this.dimension()
- this.reset(this.$element[dimension]())
- this.transition('removeClass', 'hide', 'hidden')
- this.$element[dimension](0)
- }
-
- , reset: function ( size ) {
- var dimension = this.dimension()
-
- this.$element
- .removeClass('collapse')
- [dimension](size || 'auto')
- [0].offsetWidth
-
- this.$element.addClass('collapse')
- }
-
- , transition: function ( method, startEvent, completeEvent ) {
- var that = this
- , complete = function () {
- if (startEvent == 'show') that.reset()
- that.$element.trigger(completeEvent)
- }
-
- this.$element
- .trigger(startEvent)
- [method]('in')
-
- $.support.transition && this.$element.hasClass('collapse') ?
- this.$element.one($.support.transition.end, complete) :
- complete()
- }
-
- , toggle: function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
- }
-
- }
-
- /* COLLAPSIBLE PLUGIN DEFINITION
- * ============================== */
-
- $.fn.collapse = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('collapse')
- , options = typeof option == 'object' && option
- if (!data) $this.data('collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.collapse.defaults = {
- toggle: true
- }
-
- $.fn.collapse.Constructor = Collapse
-
-
- /* COLLAPSIBLE DATA-API
- * ==================== */
-
- $(function () {
- $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
- var $this = $(this), href
- , target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- , option = $(target).data('collapse') ? 'toggle' : $this.data()
- $(target).collapse(option)
- })
- })
-
-}( window.jQuery )
-/* ============================================================
- * bootstrap-dropdown.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#dropdowns
- * ============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ============================================================ */
-
-
-!function( $ ){
-
- "use strict"
-
- /* DROPDOWN CLASS DEFINITION
- * ========================= */
-
- var toggle = '[data-toggle="dropdown"]'
- , Dropdown = function ( element ) {
- var $el = $(element).on('click.dropdown.data-api', this.toggle)
- $('html').on('click.dropdown.data-api', function () {
- $el.parent().removeClass('open')
- })
- }
-
- Dropdown.prototype = {
-
- constructor: Dropdown
-
- , toggle: function ( e ) {
- var $this = $(this)
- , selector = $this.attr('data-target')
- , $parent
- , isActive
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- $parent = $(selector)
- $parent.length || ($parent = $this.parent())
-
- isActive = $parent.hasClass('open')
-
- clearMenus()
- !isActive && $parent.toggleClass('open')
-
- return false
- }
-
- }
-
- function clearMenus() {
- $(toggle).parent().removeClass('open')
- }
-
-
- /* DROPDOWN PLUGIN DEFINITION
- * ========================== */
-
- $.fn.dropdown = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('dropdown')
- if (!data) $this.data('dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.dropdown.Constructor = Dropdown
-
-
- /* APPLY TO STANDARD DROPDOWN ELEMENTS
- * =================================== */
-
- $(function () {
- $('html').on('click.dropdown.data-api', clearMenus)
- $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
- })
-
-}( window.jQuery )
-/* =========================================================
- * bootstrap-modal.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#modals
- * =========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ========================================================= */
-
-
-!function( $ ){
-
- "use strict"
-
- /* MODAL CLASS DEFINITION
- * ====================== */
-
- var Modal = function ( content, options ) {
- this.options = $.extend({}, $.fn.modal.defaults, options)
- this.$element = $(content)
- .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
- }
-
- Modal.prototype = {
-
- constructor: Modal
-
- , toggle: function () {
- return this[!this.isShown ? 'show' : 'hide']()
- }
-
- , show: function () {
- var that = this
-
- if (this.isShown) return
-
- $('body').addClass('modal-open')
-
- this.isShown = true
- this.$element.trigger('show')
-
- escape.call(this)
- backdrop.call(this, function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
-
- !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position
-
- that.$element
- .show()
-
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
-
- that.$element.addClass('in')
-
- transition ?
- that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
- that.$element.trigger('shown')
-
- })
- }
-
- , hide: function ( e ) {
- e && e.preventDefault()
-
- if (!this.isShown) return
-
- var that = this
- this.isShown = false
-
- $('body').removeClass('modal-open')
-
- escape.call(this)
-
- this.$element
- .trigger('hide')
- .removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade') ?
- hideWithTransition.call(this) :
- hideModal.call(this)
- }
-
- }
-
-
- /* MODAL PRIVATE METHODS
- * ===================== */
-
- function hideWithTransition() {
- var that = this
- , timeout = setTimeout(function () {
- that.$element.off($.support.transition.end)
- hideModal.call(that)
- }, 500)
-
- this.$element.one($.support.transition.end, function () {
- clearTimeout(timeout)
- hideModal.call(that)
- })
- }
-
- function hideModal( that ) {
- this.$element
- .hide()
- .trigger('hidden')
-
- backdrop.call(this)
- }
-
- function backdrop( callback ) {
- var that = this
- , animate = this.$element.hasClass('fade') ? 'fade' : ''
-
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
-
- this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
- .appendTo(document.body)
-
- if (this.options.backdrop != 'static') {
- this.$backdrop.click($.proxy(this.hide, this))
- }
-
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
-
- this.$backdrop.addClass('in')
-
- doAnimate ?
- this.$backdrop.one($.support.transition.end, callback) :
- callback()
-
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade')?
- this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
- removeBackdrop.call(this)
-
- } else if (callback) {
- callback()
- }
- }
-
- function removeBackdrop() {
- this.$backdrop.remove()
- this.$backdrop = null
- }
-
- function escape() {
- var that = this
- if (this.isShown && this.options.keyboard) {
- $(document).on('keyup.dismiss.modal', function ( e ) {
- e.which == 27 && that.hide()
- })
- } else if (!this.isShown) {
- $(document).off('keyup.dismiss.modal')
- }
- }
-
-
- /* MODAL PLUGIN DEFINITION
- * ======================= */
-
- $.fn.modal = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('modal')
- , options = typeof option == 'object' && option
- if (!data) $this.data('modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option]()
- else data.show()
- })
- }
-
- $.fn.modal.defaults = {
- backdrop: true
- , keyboard: true
- }
-
- $.fn.modal.Constructor = Modal
-
-
- /* MODAL DATA-API
- * ============== */
-
- $(function () {
- $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
- var $this = $(this), href
- , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
-
- e.preventDefault()
- $target.modal(option)
- })
- })
-
-}( window.jQuery )
-/* ===========================================================
- * bootstrap-tooltip.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#tooltips
- * Inspired by the original jQuery.tipsy by Jason Frame
- * ===========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ========================================================== */
-
-!function( $ ) {
-
- "use strict"
-
- /* TOOLTIP PUBLIC CLASS DEFINITION
- * =============================== */
-
- var Tooltip = function ( element, options ) {
- this.init('tooltip', element, options)
- }
-
- Tooltip.prototype = {
-
- constructor: Tooltip
-
- , init: function ( type, element, options ) {
- var eventIn
- , eventOut
-
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
- this.enabled = true
-
- if (this.options.trigger != 'manual') {
- eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
- eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
- this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
- }
-
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
-
- , getOptions: function ( options ) {
- options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
-
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay
- , hide: options.delay
- }
- }
-
- return options
- }
-
- , enter: function ( e ) {
- var self = $(e.currentTarget)[this.type](this._options).data(this.type)
-
- if (!self.options.delay || !self.options.delay.show) {
- self.show()
- } else {
- self.hoverState = 'in'
- setTimeout(function() {
- if (self.hoverState == 'in') {
- self.show()
- }
- }, self.options.delay.show)
- }
- }
-
- , leave: function ( e ) {
- var self = $(e.currentTarget)[this.type](this._options).data(this.type)
-
- if (!self.options.delay || !self.options.delay.hide) {
- self.hide()
- } else {
- self.hoverState = 'out'
- setTimeout(function() {
- if (self.hoverState == 'out') {
- self.hide()
- }
- }, self.options.delay.hide)
- }
- }
-
- , show: function () {
- var $tip
- , inside
- , pos
- , actualWidth
- , actualHeight
- , placement
- , tp
-
- if (this.hasContent() && this.enabled) {
- $tip = this.tip()
- this.setContent()
-
- if (this.options.animation) {
- $tip.addClass('fade')
- }
-
- placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
-
- inside = /in/.test(placement)
-
- $tip
- .remove()
- .css({ top: 0, left: 0, display: 'block' })
- .appendTo(inside ? this.$element : document.body)
-
- pos = this.getPosition(inside)
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
-
- switch (inside ? placement.split(' ')[1] : placement) {
- case 'bottom':
- tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
- break
- case 'top':
- tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
- break
- case 'left':
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
- break
- case 'right':
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
- break
- }
-
- $tip
- .css(tp)
- .addClass(placement)
- .addClass('in')
- }
- }
-
- , setContent: function () {
- var $tip = this.tip()
- $tip.find('.tooltip-inner').html(this.getTitle())
- $tip.removeClass('fade in top bottom left right')
- }
-
- , hide: function () {
- var that = this
- , $tip = this.tip()
-
- $tip.removeClass('in')
-
- function removeWithAnimation() {
- var timeout = setTimeout(function () {
- $tip.off($.support.transition.end).remove()
- }, 500)
-
- $tip.one($.support.transition.end, function () {
- clearTimeout(timeout)
- $tip.remove()
- })
- }
-
- $.support.transition && this.$tip.hasClass('fade') ?
- removeWithAnimation() :
- $tip.remove()
- }
-
- , fixTitle: function () {
- var $e = this.$element
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
- }
- }
-
- , hasContent: function () {
- return this.getTitle()
- }
-
- , getPosition: function (inside) {
- return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
- width: this.$element[0].offsetWidth
- , height: this.$element[0].offsetHeight
- })
- }
-
- , getTitle: function () {
- var title
- , $e = this.$element
- , o = this.options
-
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-
- title = title.toString().replace(/(^\s*|\s*$)/, "")
-
- return title
- }
-
- , tip: function () {
- return this.$tip = this.$tip || $(this.options.template)
- }
-
- , validate: function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
- , enable: function () {
- this.enabled = true
- }
-
- , disable: function () {
- this.enabled = false
- }
-
- , toggleEnabled: function () {
- this.enabled = !this.enabled
- }
-
- , toggle: function () {
- this[this.tip().hasClass('in') ? 'hide' : 'show']()
- }
-
- }
-
-
- /* TOOLTIP PLUGIN DEFINITION
- * ========================= */
-
- $.fn.tooltip = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('tooltip')
- , options = typeof option == 'object' && option
- if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tooltip.Constructor = Tooltip
-
- $.fn.tooltip.defaults = {
- animation: true
- , delay: 0
- , selector: false
- , placement: 'top'
- , trigger: 'hover'
- , title: ''
- , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- }
-
-}( window.jQuery )
-/* ===========================================================
- * bootstrap-popover.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#popovers
- * ===========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * =========================================================== */
-
-
-!function( $ ) {
-
- "use strict"
-
- var Popover = function ( element, options ) {
- this.init('popover', element, options)
- }
-
- /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
- ========================================== */
-
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
-
- constructor: Popover
-
- , setContent: function () {
- var $tip = this.tip()
- , title = this.getTitle()
- , content = this.getContent()
-
- $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
- $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
-
- $tip.removeClass('fade top bottom left right in')
- }
-
- , hasContent: function () {
- return this.getTitle() || this.getContent()
- }
-
- , getContent: function () {
- var content
- , $e = this.$element
- , o = this.options
-
- content = $e.attr('data-content')
- || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
-
- content = content.toString().replace(/(^\s*|\s*$)/, "")
-
- return content
- }
-
- , tip: function() {
- if (!this.$tip) {
- this.$tip = $(this.options.template)
- }
- return this.$tip
- }
-
- })
-
-
- /* POPOVER PLUGIN DEFINITION
- * ======================= */
-
- $.fn.popover = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('popover')
- , options = typeof option == 'object' && option
- if (!data) $this.data('popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.popover.Constructor = Popover
-
- $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
- placement: 'right'
- , content: ''
- , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
- })
-
-}( window.jQuery )
-/* =============================================================
- * bootstrap-scrollspy.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#scrollspy
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ============================================================== */
-
-!function ( $ ) {
-
- "use strict"
-
- /* SCROLLSPY CLASS DEFINITION
- * ========================== */
-
- function ScrollSpy( element, options) {
- var process = $.proxy(this.process, this)
- , $element = $(element).is('body') ? $(window) : $(element)
- , href
- this.options = $.extend({}, $.fn.scrollspy.defaults, options)
- this.$scrollElement = $element.on('scroll.scroll.data-api', process)
- this.selector = (this.options.target
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- || '') + ' .nav li > a'
- this.$body = $('body').on('click.scroll.data-api', this.selector, process)
- this.refresh()
- this.process()
- }
-
- ScrollSpy.prototype = {
-
- constructor: ScrollSpy
-
- , refresh: function () {
- this.targets = this.$body
- .find(this.selector)
- .map(function () {
- var href = $(this).attr('href')
- return /^#\w/.test(href) && $(href).length ? href : null
- })
-
- this.offsets = $.map(this.targets, function (id) {
- return $(id).position().top
- })
- }
-
- , process: function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- , offsets = this.offsets
- , targets = this.targets
- , activeTarget = this.activeTarget
- , i
-
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate( targets[i] )
- }
- }
-
- , activate: function (target) {
- var active
-
- this.activeTarget = target
-
- this.$body
- .find(this.selector).parent('.active')
- .removeClass('active')
-
- active = this.$body
- .find(this.selector + '[href="' + target + '"]')
- .parent('li')
- .addClass('active')
-
- if ( active.parent('.dropdown-menu') ) {
- active.closest('li.dropdown').addClass('active')
- }
- }
-
- }
-
-
- /* SCROLLSPY PLUGIN DEFINITION
- * =========================== */
-
- $.fn.scrollspy = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('scrollspy')
- , options = typeof option == 'object' && option
- if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.scrollspy.Constructor = ScrollSpy
-
- $.fn.scrollspy.defaults = {
- offset: 10
- }
-
-
- /* SCROLLSPY DATA-API
- * ================== */
-
- $(function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- $spy.scrollspy($spy.data())
- })
- })
-
-}( window.jQuery )
-/* ========================================================
- * bootstrap-tab.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#tabs
- * ========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ======================================================== */
-
-
-!function( $ ){
-
- "use strict"
-
- /* TAB CLASS DEFINITION
- * ==================== */
-
- var Tab = function ( element ) {
- this.element = $(element)
- }
-
- Tab.prototype = {
-
- constructor: Tab
-
- , show: function () {
- var $this = this.element
- , $ul = $this.closest('ul:not(.dropdown-menu)')
- , selector = $this.attr('data-target')
- , previous
- , $target
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- if ( $this.parent('li').hasClass('active') ) return
-
- previous = $ul.find('.active a').last()[0]
-
- $this.trigger({
- type: 'show'
- , relatedTarget: previous
- })
-
- $target = $(selector)
-
- this.activate($this.parent('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $this.trigger({
- type: 'shown'
- , relatedTarget: previous
- })
- })
- }
-
- , activate: function ( element, container, callback) {
- var $active = container.find('> .active')
- , transition = callback
- && $.support.transition
- && $active.hasClass('fade')
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
-
- element.addClass('active')
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if ( element.parent('.dropdown-menu') ) {
- element.closest('li.dropdown').addClass('active')
- }
-
- callback && callback()
- }
-
- transition ?
- $active.one($.support.transition.end, next) :
- next()
-
- $active.removeClass('in')
- }
- }
-
-
- /* TAB PLUGIN DEFINITION
- * ===================== */
-
- $.fn.tab = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('tab')
- if (!data) $this.data('tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tab.Constructor = Tab
-
-
- /* TAB DATA-API
- * ============ */
-
- $(function () {
- $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
- })
-
-}( window.jQuery )
-/* =============================================================
- * bootstrap-typeahead.js v2.0.0
- * http://twitter.github.com/bootstrap/javascript.html#typeahead
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * 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.
- * ============================================================ */
-
-!function( $ ){
-
- "use strict"
-
- var Typeahead = function ( element, options ) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.typeahead.defaults, options)
- this.matcher = this.options.matcher || this.matcher
- this.sorter = this.options.sorter || this.sorter
- this.highlighter = this.options.highlighter || this.highlighter
- this.$menu = $(this.options.menu).appendTo('body')
- this.source = this.options.source
- this.shown = false
- this.listen()
- }
-
- Typeahead.prototype = {
-
- constructor: Typeahead
-
- , select: function () {
- var val = this.$menu.find('.active').attr('data-value')
- this.$element.val(val)
- return this.hide()
- }
-
- , show: function () {
- var pos = $.extend({}, this.$element.offset(), {
- height: this.$element[0].offsetHeight
- })
-
- this.$menu.css({
- top: pos.top + pos.height
- , left: pos.left
- })
-
- this.$menu.show()
- this.shown = true
- return this
- }
-
- , hide: function () {
- this.$menu.hide()
- this.shown = false
- return this
- }
-
- , lookup: function (event) {
- var that = this
- , items
- , q
-
- this.query = this.$element.val()
-
- if (!this.query) {
- return this.shown ? this.hide() : this
- }
-
- items = $.grep(this.source, function (item) {
- if (that.matcher(item)) return item
- })
-
- items = this.sorter(items)
-
- if (!items.length) {
- return this.shown ? this.hide() : this
- }
-
- return this.render(items.slice(0, this.options.items)).show()
- }
-
- , matcher: function (item) {
- return ~item.toLowerCase().indexOf(this.query.toLowerCase())
- }
-
- , sorter: function (items) {
- var beginswith = []
- , caseSensitive = []
- , caseInsensitive = []
- , item
-
- while (item = items.shift()) {
- if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
- else if (~item.indexOf(this.query)) caseSensitive.push(item)
- else caseInsensitive.push(item)
- }
-
- return beginswith.concat(caseSensitive, caseInsensitive)
- }
-
- , highlighter: function (item) {
- return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
- return '<strong>' + match + '</strong>'
- })
- }
-
- , render: function (items) {
- var that = this
-
- items = $(items).map(function (i, item) {
- i = $(that.options.item).attr('data-value', item)
- i.find('a').html(that.highlighter(item))
- return i[0]
- })
-
- items.first().addClass('active')
- this.$menu.html(items)
- return this
- }
-
- , next: function (event) {
- var active = this.$menu.find('.active').removeClass('active')
- , next = active.next()
-
- if (!next.length) {
- next = $(this.$menu.find('li')[0])
- }
-
- next.addClass('active')
- }
-
- , prev: function (event) {
- var active = this.$menu.find('.active').removeClass('active')
- , prev = active.prev()
-
- if (!prev.length) {
- prev = this.$menu.find('li').last()
- }
-
- prev.addClass('active')
- }
-
- , listen: function () {
- this.$element
- .on('blur', $.proxy(this.blur, this))
- .on('keypress', $.proxy(this.keypress, this))
- .on('keyup', $.proxy(this.keyup, this))
-
- if ($.browser.webkit || $.browser.msie) {
- this.$element.on('keydown', $.proxy(this.keypress, this))
- }
-
- this.$menu
- .on('click', $.proxy(this.click, this))
- .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
- }
-
- , keyup: function (e) {
- e.stopPropagation()
- e.preventDefault()
-
- switch(e.keyCode) {
- case 40: // down arrow
- case 38: // up arrow
- break
-
- case 9: // tab
- case 13: // enter
- if (!this.shown) return
- this.select()
- break
-
- case 27: // escape
- this.hide()
- break
-
- default:
- this.lookup()
- }
-
- }
-
- , keypress: function (e) {
- e.stopPropagation()
- if (!this.shown) return
-
- switch(e.keyCode) {
- case 9: // tab
- case 13: // enter
- case 27: // escape
- e.preventDefault()
- break
-
- case 38: // up arrow
- e.preventDefault()
- this.prev()
- break
-
- case 40: // down arrow
- e.preventDefault()
- this.next()
- break
- }
- }
-
- , blur: function (e) {
- var that = this
- e.stopPropagation()
- e.preventDefault()
- setTimeout(function () { that.hide() }, 150)
- }
-
- , click: function (e) {
- e.stopPropagation()
- e.preventDefault()
- this.select()
- }
-
- , mouseenter: function (e) {
- this.$menu.find('.active').removeClass('active')
- $(e.currentTarget).addClass('active')
- }
-
- }
-
-
- /* TYPEAHEAD PLUGIN DEFINITION
- * =========================== */
-
- $.fn.typeahead = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('typeahead')
- , options = typeof option == 'object' && option
- if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.typeahead.defaults = {
- source: []
- , items: 8
- , menu: '<ul class="typeahead dropdown-menu"></ul>'
- , item: '<li><a href="#"></a></li>'
- }
-
- $.fn.typeahead.Constructor = Typeahead
-
-
- /* TYPEAHEAD DATA-API
- * ================== */
-
- $(function () {
- $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
- var $this = $(this)
- if ($this.data('typeahead')) return
- e.preventDefault()
- $this.typeahead($this.data())
- })
- })
-
-}( window.jQuery )
diff --git a/doc/source/assets/js/bootstrap.min.js b/doc/source/assets/js/bootstrap.min.js
deleted file mode 100644
index 1f295a1..0000000
--- a/doc/source/assets/js/bootstrap.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.remove(),e.trigger("closed")}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){a(b.target).button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide)};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;return this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h](),!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element.addClass("collapse")},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=a.extend({},a.fn.modal.defaults,c),this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=typeof c=="object"&&c;e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.show?c.show():(c.hoverState="in",setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show))},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.hide?c.hide():(c.hoverState="out",setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide))},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip();a.find(".tooltip-inner").html(this.getTitle()),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,delay:0,selector:!1,placement:"top",trigger:"hover",title:"",template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var b=this.tip(),c=this.getTitle(),d=this.getContent();b.find(".popover-title")[a.type(c)=="object"?"append":"html"](c),b.find(".popover-content > *")[a.type(d)=="object"?"append":"html"](d),b.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){this.targets=this.$body.find(this.selector).map(function(){var b=a(this).attr("href");return/^#\w/.test(b)&&a(b).length?b:null}),this.offsets=a.map(this.targets,function(b){return a(b).position().top})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.offsets,c=this.targets,d=this.activeTarget,e;for(e=b.length;e--;)d!=c[e]&&a>=b[e]&&(!b[e+1]||a<=b[e+1])&&this.activate(c[e])},activate:function(a){var b;this.activeTarget=a,this.$body.find(this.selector).parent(".active").removeClass("active"),b=this.$body.find(this.selector+'[href="'+a+'"]').parent("li").addClass("active"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],b.trigger({type:"show",relatedTarget:e}),f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(a),this.hide()},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){if(c.matcher(a))return a}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){return a.replace(new RegExp("("+this.query+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){a.stopPropagation(),a.preventDefault();switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:this.hide();break;default:this.lookup()}},keypress:function(a){a.stopPropagation();if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}},blur:function(a){var b=this;a.stopPropagation(),a.preventDefault(),setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
\ No newline at end of file
diff --git a/doc/source/assets/js/jquery-1.7.1.min.js b/doc/source/assets/js/jquery-1.7.1.min.js
deleted file mode 100644
index ee02337..0000000
--- a/doc/source/assets/js/jquery-1.7.1.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.1 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
-f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
-{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
diff --git a/doc/source/changes.rst b/doc/source/changes.rst
index f4a326d..19fce0f 100644
--- a/doc/source/changes.rst
+++ b/doc/source/changes.rst
@@ -3,8 +3,10 @@
=======
Recent Changes What's been happening?
--------------------------------------
+=====================================
These are the commits to DevStack for the last six months. For the
complete list see `the DevStack project in
Gerrit <https://review.openstack.org/#/q/status:merged+project:openstack-dev/devstack,n,z>`__.
+
+%GIT_LOG%
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index eba2956..fe3e2c2 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -22,7 +22,7 @@
- allow settings in arbitrary configuration files to be changed
local.conf
-~~~~~~~~~~
+==========
The new configuration file is ``local.conf`` and resides in the root
DevStack directory like the old ``localrc`` file. It is a modified INI
@@ -74,7 +74,7 @@
``localrc`` file (actually ``.localrc.auto``). This allows all custom
settings for DevStack to be contained in a single file. If ``localrc``
exists it will be used instead to preserve backward-compatibility. More
-details on the `contents of localrc <localrc.html>`__ are available.
+details on the :doc:`contents of local.conf <local.conf>` are available.
::
@@ -92,11 +92,11 @@
[[post-config|/$Q_PLUGIN_CONF_FILE]]
Also note that the ``localrc`` section is sourced as a shell script
-fragment amd MUST conform to the shell requirements, specifically no
+fragment and MUST conform to the shell requirements, specifically no
whitespace around ``=`` (equals).
Minimal Configuration
-~~~~~~~~~~~~~~~~~~~~~
+=====================
While ``stack.sh`` is happy to run without a ``localrc`` section in
``local.conf``, devlife is better when there are a few minimal variables
@@ -131,14 +131,16 @@
``HOST_IP`` is normally detected on the first run of ``stack.sh`` but
often is indeterminate on later runs due to the IP being moved from an
-Ethernet integace to a bridge on the host. Setting it here also makes it
+Ethernet interface to a bridge on the host. Setting it here also makes it
available for ``openrc`` to set ``OS_AUTH_URL``. ``HOST_IP`` is not set
by default.
Common Configuration Variables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==============================
-Set DevStack install directory
+Installation Directory
+----------------------
+
| *Default: ``DEST=/opt/stack``*
| The DevStack install directory is set by the ``DEST`` variable.
| By setting it early in the ``localrc`` section you can reference it
@@ -150,7 +152,27 @@
DEST=/opt/stack
-stack.sh logging
+Libraries from Git
+------------------
+
+ | *Default: ``LIBS_FROM_GIT=""``*
+
+ | By default devstack installs OpenStack server components from
+ git, however it installs client libraries from released versions
+ on pypi. This is appropriate if you are working on server
+ development, but if you want to see how an unreleased version of
+ the client affects the system you can have devstack install it
+ from upstream, or from local git trees.
+ | Multiple libraries can be specified as a comma separated list.
+ |
+
+ ::
+
+ LIBS_FROM_GIT=python-keystoneclient,oslo.config
+
+Enable Logging
+--------------
+
| *Defaults: ``LOGFILE="" LOGDAYS=7 LOG_COLOR=True``*
| By default ``stack.sh`` output is only written to the console
where is runs. It can be sent to a file in addition to the console
@@ -178,25 +200,26 @@
LOG_COLOR=False
-Screen logging
- | *Default: ``SCREEN_LOGDIR=""``*
- | By default DevStack runs the OpenStack services using ``screen``
- which is useful for watching log and debug output. However, in
- automated testing the interactive ``screen`` sessions may not be
- available after the fact; setting ``SCREEN_LOGDIR`` enables logging
- of the ``screen`` sessions in the specified diretory. There will be
- one file per ``screen`` session named for the session name and a
- timestamp.
+Logging the Service Output
+--------------------------
+
+ | *Default: ``LOGDIR=""``*
+ | DevStack will log the stdout output of the services it starts.
+ When using ``screen`` this logs the output in the screen windows
+ to a file. Without ``screen`` this simply redirects stdout of
+ the service process to a file in ``LOGDIR``.
|
::
- SCREEN_LOGDIR=$DEST/logs/screen
+ LOGDIR=$DEST/logs
*Note the use of ``DEST`` to locate the main install directory; this
is why we suggest setting it in ``local.conf``.*
-One syslog to bind them all
+Enabling Syslog
+---------------
+
| *Default: ``SYSLOG=False SYSLOG_HOST=$HOST_IP SYSLOG_PORT=516``*
| Logging all services to a single syslog can be convenient. Enable
syslogging by setting ``SYSLOG`` to ``True``. If the destination log
@@ -211,6 +234,8 @@
SYSLOG_PORT=516
A clean install every time
+--------------------------
+
| *Default: ``RECLONE=""``*
| By default ``stack.sh`` only clones the project repos if they do
not exist in ``$DEST``. ``stack.sh`` will freshen each repo on each
@@ -222,10 +247,18 @@
RECLONE=yes
- Swift
- Default: SWIFT_HASH="" SWIFT_REPLICAS=1 SWIFT_DATA_DIR=$DEST/data/swift
- Swift is now used as the back-end for the S3-like object store. When enabled Nova's objectstore (n-obj in ENABLED_SERVICES) is automatically disabled. Enable Swift by adding it services to ENABLED_SERVICES:
- enable_service s-proxy s-object s-container s-account
+Swift
+-----
+
+ | Default: SWIFT_HASH=""
+ | SWIFT_REPLICAS=1
+ | SWIFT_DATA_DIR=$DEST/data/swift
+
+ | Swift is now used as the back-end for the S3-like object store.
+ When enabled Nova's objectstore (n-obj in ENABLED_SERVICES) is
+ automatically disabled. Enable Swift by adding it services to
+ ENABLED_SERVICES: enable_service s-proxy s-object s-container
+ s-account
Setting Swift's hash value is required and you will be prompted for
it if Swift is enabled so just set it to something already:
@@ -259,6 +292,8 @@
work correctly.*
Service Catalog Backend
+-----------------------
+
| *Default: ``KEYSTONE_CATALOG_BACKEND=sql``*
| DevStack uses Keystone's ``sql`` service catalog backend. An
alternate ``template`` backend is also available. However, it does
@@ -274,6 +309,8 @@
``files/keystone_data.sh``
Cinder
+------
+
| Default:
| VOLUME_GROUP="stack-volumes" VOLUME_NAME_PREFIX="volume-" VOLUME_BACKING_FILE_SIZE=10250M
| The logical volume group used to hold the Cinder-managed volumes
@@ -289,6 +326,8 @@
VOLUME_BACKING_FILE_SIZE=10250M
Multi-host DevStack
+-------------------
+
| *Default: ``MULTI_HOST=False``*
| Running DevStack with multiple hosts requires a custom
``local.conf`` section for each host. The master is the same as a
@@ -310,19 +349,37 @@
GLANCE_HOSTPORT=w.x.y.z:9292
ENABLED_SERVICES=n-vol,n-cpu,n-net,n-api
-API rate limits
- | Default: ``API_RATE_LIMIT=True``
- | Integration tests such as Tempest will likely run afoul of the
- default rate limits configured for Nova. Turn off rate limiting
- during testing by setting ``API_RATE_LIMIT=False``.*
+IP Version
+ | Default: ``IP_VERSION=4``
+ | This setting can be used to configure DevStack to create either an IPv4,
+ IPv6, or dual stack tenant data network by setting ``IP_VERSION`` to
+ either ``IP_VERSION=4``, ``IP_VERSION=6``, or ``IP_VERSION=4+6``
+ respectively. This functionality requires that the Neutron networking
+ service is enabled by setting the following options:
|
::
- API_RATE_LIMIT=False
+ disable_service n-net
+ enable_service q-svc q-agt q-dhcp q-l3
+
+ | The following optional variables can be used to alter the default IPv6
+ behavior:
+ |
+
+ ::
+
+ IPV6_RA_MODE=slaac
+ IPV6_ADDRESS_MODE=slaac
+ FIXED_RANGE_V6=fd$IPV6_GLOBAL_ID::/64
+ IPV6_PRIVATE_NETWORK_GATEWAY=fd$IPV6_GLOBAL_ID::1
+
+ | *Note: ``FIXED_RANGE_V6`` and ``IPV6_PRIVATE_NETWORK_GATEWAY``
+ can be configured with any valid IPv6 prefix. The default values make
+ use of an auto-generated ``IPV6_GLOBAL_ID`` to comply with RFC 4193.*
Examples
-~~~~~~~~
+========
- Eliminate a Cinder pass-through (``CINDER_PERIODIC_INTERVAL``):
@@ -340,8 +397,8 @@
FIXED_RANGE=10.254.1.0/24
NETWORK_GATEWAY=10.254.1.1
LOGDAYS=1
- LOGFILE=$DEST/logs/stack.sh.log
- SCREEN_LOGDIR=$DEST/logs/screen
+ LOGDIR=$DEST/logs
+ LOGFILE=$LOGDIR/stack.sh.log
ADMIN_PASSWORD=quiet
DATABASE_PASSWORD=$ADMIN_PASSWORD
RABBIT_PASSWORD=$ADMIN_PASSWORD
diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst
index b4f9f37..50c0100 100644
--- a/doc/source/contributing.rst
+++ b/doc/source/contributing.rst
@@ -3,16 +3,16 @@
============
DevStack uses the standard OpenStack contribution process as outlined in
-`the OpenStack wiki 'How To
-Contribute' <https://wiki.openstack.org/wiki/How_To_Contribute>`__. This
+`the OpenStack developer
+guide <http://docs.openstack.org/infra/manual/developers.html>`__. This
means that you will need to meet the requirements of the Contribututors
License Agreement (CLA). If you have already done that for another
OpenStack project you are good to go.
Things To Know
-~~~~~~~~~~~~~~
+==============
-|
+|
| **Where Things Are**
The official DevStack repository is located at
@@ -30,7 +30,7 @@
is, however, used for all commits except for the text of this website.
That should also change in the near future.
-|
+|
| **HACKING.rst**
Like most OpenStack projects, DevStack includes a ``HACKING.rst`` file
@@ -38,7 +38,7 @@
``HACKING.rst`` is in the main DevStack repo it is considered
authoritative. Much of the content on this page is taken from there.
-|
+|
| **bashate Formatting**
Around the time of the OpenStack Havana release we added a tool to do
@@ -51,24 +51,25 @@
formatting. Run it on the entire project with ``./run_tests.sh``.
Code
-~~~~
+====
-|
+|
| **Repo Layout**
The DevStack repo generally keeps all of the primary scripts at the root
level.
-``docs`` - Contains the source for this website. It is built using
-``tools/build_docs.sh``.
+``doc`` - Contains the Sphinx source for the documentation.
+``tools/build_docs.sh`` is used to generate the HTML versions of the
+DevStack scripts. A complete doc build can be run with ``tox -edocs``.
-``exercises`` - Contains the test scripts used to validate and
+``exercises`` - Contains the test scripts used to sanity-check and
demonstrate some OpenStack functions. These scripts know how to exit
early or skip services that are not enabled.
``extras.d`` - Contains the dispatch scripts called by the hooks in
-``stack.sh``, ``unstack.sh`` and ``clean.sh``. See `the plugins
-docs <plugins.html>`__ for more information.
+``stack.sh``, ``unstack.sh`` and ``clean.sh``. See :doc:`the plugins
+docs <plugins>` for more information.
``files`` - Contains a variety of otherwise lost files used in
configuring and operating DevStack. This includes templates for
@@ -84,10 +85,10 @@
DevStack repo.
``tests`` - the DevStack test suite is rather sparse, mostly consisting
-of test of specific fragile functions in the ``functions`` file.
+of test of specific fragile functions in the ``functions`` and
+``functions-common`` files.
-``tools`` - Contains a collection of stand-alone scripts, some of which
-have aged a bit (does anyone still do ramdisk installs?). While these
+``tools`` - Contains a collection of stand-alone scripts. While these
may reference the top-level DevStack configuration they can generally be
run alone. There are also some sub-directories to support specific
environments such as XenServer.
diff --git a/doc/source/exerciserc.rst b/doc/source/exerciserc.rst
index f3780c3..dacae2e 100644
--- a/doc/source/exerciserc.rst
+++ b/doc/source/exerciserc.rst
@@ -3,7 +3,7 @@
==============================
``exerciserc`` is used to configure settings for the exercise scripts.
-The values shown below are the default values. Thse can all be
+The values shown below are the default values. These can all be
overridden by setting them in the ``localrc`` section.
ACTIVE\_TIMEOUT
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index 7b33b41..a449f49 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -7,7 +7,7 @@
- `Miscellaneous <#misc>`__
General Questions
-~~~~~~~~~~~~~~~~~
+=================
Q: Can I use DevStack for production?
A: No. We mean it. Really. DevStack makes some implementation
@@ -24,7 +24,7 @@
by packaging in "real" deployments. To remove additional protections
that will be desired/required in production would be a step
backward.
-Q: But selinux is disabled in RHEL 6!
+Q: But selinux is disabled in RHEL!
A: Today it is, yes. That is a specific exception that certain
DevStack contributors fought strongly against. The primary reason it
was allowed was to support using RHEL6 as the Python 2.6 test
@@ -46,12 +46,9 @@
`git.openstack.org <https://git.openstack.org/cgit/openstack-dev/devstack>`__
and bug reports go to
`LaunchPad <http://bugs.launchpad.net/devstack/>`__. Contributions
- follow the usual process as described in the `OpenStack
- wiki <http://wiki.openstack.org/HowToContribute>`__ even though
- DevStack is not an official OpenStack project. This site is housed
- in the CloudBuilder's
- `github <http://github.com/cloudbuilders/devstack>`__ in the
- gh-pages branch.
+ follow the usual process as described in the `developer
+ guide <http://docs.openstack.org/infra/manual/developers.html>`__. This Sphinx
+ documentation is housed in the doc directory.
Q: Why not use packages?
A: Unlike packages, DevStack leaves your cloud ready to develop -
checkouts of the code and services running in screen. However, many
@@ -73,14 +70,21 @@
Q: Are there any differences between Ubuntu and Fedora support?
A: Neutron is not fully supported prior to Fedora 18 due lack of
OpenVSwitch packages.
-Q: How about RHEL 6?
- A: RHEL 6 has Python 2.6 and many old modules packaged and is a
- challenge to support. There are a number of specific RHEL6
- work-arounds in ``stack.sh`` to handle this. But the testing on py26
- is valuable so we do it...
+Q: Why can't I use another shell?
+ A: DevStack now uses some specific bash-ism that require Bash 4, such
+ as associative arrays. Simple compatibility patches have been accepted
+ in the past when they are not complex, at this point no additional
+ compatibility patches will be considered except for shells matching
+ the array functionality as it is very ingrained in the repo and project
+ management.
+Q: But, but, can't I test on OS/X?
+ A: Yes, even you, core developer who complained about this, needs to
+ install bash 4 via homebrew to keep running tests on OS/X. Get a Real
+ Operating System. (For most of you who don't know, I am refering to
+ myself.)
Operation and Configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+===========================
Q: Can DevStack handle a multi-node installation?
A: Indirectly, yes. You run DevStack on each node with the
@@ -126,7 +130,7 @@
[STRIKEOUT:The majority of deployments will use packages to install
OpenStack that will have distro-based packages as dependencies.
DevStack installs as many of these Python packages as possible to
- mimic the expected production environemnt.] Certain Linux
+ mimic the expected production environment.] Certain Linux
distributions have a 'lack of workaround' in their Python
configurations that installs vendor packaged Python modules and
pip-installed modules to the SAME DIRECTORY TREE. This is causing
@@ -160,7 +164,7 @@
``FORCE_PREREQ=1`` and the package checks will never be skipped.
Miscellaneous
-~~~~~~~~~~~~~
+=============
Q: ``tools/fixup_stuff.sh`` is broken and shouldn't 'fix' just one version of packages.
A: [Another not-a-question] No it isn't. Stuff in there is to
diff --git a/doc/source/guides/devstack-with-nested-kvm.rst b/doc/source/guides/devstack-with-nested-kvm.rst
new file mode 100644
index 0000000..58ec3d3
--- /dev/null
+++ b/doc/source/guides/devstack-with-nested-kvm.rst
@@ -0,0 +1,139 @@
+=======================================================
+Configure DevStack with KVM-based Nested Virtualization
+=======================================================
+
+When using virtualization technologies like KVM, one can take advantage
+of "Nested VMX" (i.e. the ability to run KVM on KVM) so that the VMs in
+cloud (Nova guests) can run relatively faster than with plain QEMU
+emulation.
+
+Kernels shipped with Linux distributions doesn't have this enabled by
+default. This guide outlines the configuration details to enable nested
+virtualization in KVM-based environments. And how to setup DevStack
+(that'll run in a VM) to take advantage of this.
+
+
+Nested Virtualization Configuration
+===================================
+
+Configure Nested KVM for Intel-based Machines
+---------------------------------------------
+
+Procedure to enable nested KVM virtualization on Intel-based machines.
+
+Check if the nested KVM Kernel parameter is enabled:
+
+::
+
+ cat /sys/module/kvm_intel/parameters/nested
+ N
+
+Temporarily remove the KVM intel Kernel module, enable nested
+virtualization to be persistent across reboots and add the Kernel
+module back:
+
+::
+
+ sudo rmmod kvm-intel
+ sudo sh -c "echo 'options kvm-intel nested=y' >> /etc/modprobe.d/dist.conf"
+ sudo modprobe kvm-intel
+
+Ensure the Nested KVM Kernel module parameter for Intel is enabled on
+the host:
+
+::
+
+ cat /sys/module/kvm_intel/parameters/nested
+ Y
+
+ modinfo kvm_intel | grep nested
+ parm: nested:bool
+
+Start your VM, now it should have KVM capabilities -- you can verify
+that by ensuring `/dev/kvm` character device is present.
+
+
+Configure Nested KVM for AMD-based Machines
+--------------------------------------------
+
+Procedure to enable nested KVM virtualization on AMD-based machines.
+
+Check if the nested KVM Kernel parameter is enabled:
+
+::
+
+ cat /sys/module/kvm_amd/parameters/nested
+ 0
+
+
+Temporarily remove the KVM AMD Kernel module, enable nested
+virtualization to be persistent across reboots and add the Kernel module
+back:
+
+::
+
+ sudo rmmod kvm-amd
+ sudo sh -c "echo 'options amd nested=1' >> /etc/modprobe.d/dist.conf"
+ sudo modprobe kvm-amd
+
+Ensure the Nested KVM Kernel module parameter for AMD is enabled on the
+host:
+
+::
+
+ cat /sys/module/kvm_amd/parameters/nested
+ 1
+
+ modinfo kvm_amd | grep -i nested
+ parm: nested:int
+
+To make the above value persistent across reboots, add an entry in
+/etc/modprobe.ddist.conf so it looks as below::
+
+ cat /etc/modprobe.d/dist.conf
+ options kvm-amd nested=y
+
+
+Expose Virtualization Extensions to DevStack VM
+-----------------------------------------------
+
+Edit the VM's libvirt XML configuration via `virsh` utility:
+
+::
+
+ sudo virsh edit devstack-vm
+
+Add the below snippet to expose the host CPU features to the VM:
+
+::
+
+ <cpu mode='host-passthrough'>
+ </cpu>
+
+
+Ensure DevStack VM is Using KVM
+-------------------------------
+
+Before invoking ``stack.sh`` in the VM, ensure that KVM is enabled. This
+can be verified by checking for the presence of the file `/dev/kvm` in
+your VM. If it is present, DevStack will default to using the config
+attribute `virt_type = kvm` in `/etc/nova.conf`; otherwise, it'll fall
+back to `virt_type=qemu`, i.e. plain QEMU emulation.
+
+Optionally, to explicitly set the type of virtualization, to KVM, by the
+libvirt driver in Nova, the below config attribute can be used in
+DevStack's ``local.conf``:
+
+::
+
+ LIBVIRT_TYPE=kvm
+
+
+Once DevStack is configured succesfully, verify if the Nova instances
+are using KVM by noticing the QEMU CLI invoked by Nova is using the
+parameter `accel=kvm`, e.g.:
+
+::
+
+ ps -ef | grep -i qemu
+ root 29773 1 0 11:24 ? 00:00:00 /usr/bin/qemu-system-x86_64 -machine accel=kvm [. . .]
diff --git a/doc/source/guides/multinode-lab.rst b/doc/source/guides/multinode-lab.rst
index 1c53227..ff81c93 100644
--- a/doc/source/guides/multinode-lab.rst
+++ b/doc/source/guides/multinode-lab.rst
@@ -6,19 +6,19 @@
physical servers.
Prerequisites Linux & Network
------------------------------
+=============================
Minimal Install
-~~~~~~~~~~~~~~~
+---------------
You need to have a system with a fresh install of Linux. You can
download the `Minimal
CD <https://help.ubuntu.com/community/Installation/MinimalCD>`__ for
Ubuntu releases since DevStack will download & install all the
additional dependencies. The netinstall ISO is available for
-`Fedora <http://mirrors.kernel.org/fedora/releases/18/Fedora/x86_64/iso/Fedora-20-x86_64-netinst.iso>`__
+`Fedora <http://mirrors.kernel.org/fedora/releases/>`__
and
-`CentOS/RHEL <http://mirrors.kernel.org/centos/6.5/isos/x86_64/CentOS-6.5-x86_64-netinstall.iso>`__.
+`CentOS/RHEL <http://mirrors.kernel.org/centos/>`__.
Install a couple of packages to bootstrap configuration:
@@ -27,7 +27,7 @@
apt-get install -y git sudo || yum install -y git sudo
Network Configuration
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
The first iteration of the lab uses OpenStack's FlatDHCP network
controller so only a single network will be required. It should be on
@@ -60,10 +60,10 @@
GATEWAY=192.168.42.1
Installation shake and bake
----------------------------
+===========================
Add the DevStack User
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
OpenStack runs as a non-root user that has sudo access to root. There is
nothing special about the name, we'll use ``stack`` here. Every node
@@ -88,7 +88,7 @@
``stack`` user.
Set Up Ssh
-~~~~~~~~~~
+----------
Set up the stack user on each node with an ssh key for access:
@@ -98,7 +98,7 @@
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyYjfgyPazTvGpd8OaAvtU2utL8W6gWC4JdRS1J95GhNNfQd657yO6s1AH5KYQWktcE6FO/xNUC2reEXSGC7ezy+sGO1kj9Limv5vrvNHvF1+wts0Cmyx61D2nQw35/Qz8BvpdJANL7VwP/cFI/p3yhvx2lsnjFE3hN8xRB2LtLUopUSVdBwACOVUmH2G+2BWMJDjVINd2DPqRIA4Zhy09KJ3O1Joabr0XpQL0yt/I9x8BVHdAx6l9U0tMg9dj5+tAjZvMAFfye3PJcYwwsfJoFxC8w/SLtqlFX7Ehw++8RtvomvuipLdmWCy+T9hIkl+gHYE4cS3OIqXH7f49jdJf jesse@spacey.local" > ~/.ssh/authorized_keys
Download DevStack
-~~~~~~~~~~~~~~~~~
+-----------------
Grab the latest version of DevStack:
@@ -112,7 +112,7 @@
(aka 'head node') and the compute nodes.
Configure Cluster Controller
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+----------------------------
The cluster controller runs all OpenStack services. Configure the
cluster controller's DevStack in ``local.conf``:
@@ -153,7 +153,7 @@
available in ``stack.sh.log``.
Configure Compute Nodes
-~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------
The compute nodes only run the OpenStack worker services. For additional
machines, create a ``local.conf`` with:
@@ -196,7 +196,7 @@
available in ``stack.sh.log``.
Cleaning Up After DevStack
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+--------------------------
Shutting down OpenStack is now as simple as running the included
``unstack.sh`` script:
@@ -223,10 +223,10 @@
sudo virsh list | grep inst | awk '{print $1}' | xargs -n1 virsh destroy
Options pimp your stack
------------------------
+=======================
Additional Users
-~~~~~~~~~~~~~~~~
+----------------
DevStack creates two OpenStack users (``admin`` and ``demo``) and two
tenants (also ``admin`` and ``demo``). ``admin`` is exactly what it
@@ -242,7 +242,7 @@
# Get admin creds
. openrc admin admin
-
+
# List existing tenants
keystone tenant-list
@@ -260,7 +260,7 @@
# keystone role-list
Swift
-~~~~~
+-----
Swift requires a significant amount of resources and is disabled by
default in DevStack. The support in DevStack is geared toward a minimal
@@ -275,16 +275,16 @@
Swift will put its data files in ``SWIFT_DATA_DIR`` (default
``/opt/stack/data/swift``). The size of the data 'partition' created
(really a loop-mounted file) is set by ``SWIFT_LOOPBACK_DISK_SIZE``. The
-Swift config files are located in ``SWIFT_CONFIG_DIR`` (default
+Swift config files are located in ``SWIFT_CONF_DIR`` (default
``/etc/swift``). All of these settings can be overridden in (wait for
it...) ``local.conf``.
Volumes
-~~~~~~~
+-------
DevStack will automatically use an existing LVM volume group named
``stack-volumes`` to store cloud-created volumes. If ``stack-volumes``
-doesn't exist, DevStack will set up a 5Gb loop-mounted file to contain
+doesn't exist, DevStack will set up a 10Gb loop-mounted file to contain
it. This obviously limits the number and size of volumes that can be
created inside OpenStack. The size can be overridden by setting
``VOLUME_BACKING_FILE_SIZE`` in ``local.conf``.
@@ -305,7 +305,7 @@
vgcreate stack-volumes /dev/sdc
Syslog
-~~~~~~
+------
DevStack is capable of using ``rsyslog`` to aggregate logging across the
cluster. It is off by default; to turn it on set ``SYSLOG=True`` in
@@ -319,7 +319,7 @@
SYSLOG_HOST=192.168.42.11
Using Alternate Repositories/Branches
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------------------
The git repositories for all of the OpenStack services are defined in
``stackrc``. Since this file is a part of the DevStack package changes
@@ -349,10 +349,10 @@
GLANCE_REPO=https://github.com/mcuser/glance.git
Notes stuff you might need to know
-----------------------------------
+==================================
Reset the Bridge
-~~~~~~~~~~~~~~~~
+----------------
How to reset the bridge configuration:
@@ -363,7 +363,7 @@
sudo brctl delbr br100
Set MySQL Password
-~~~~~~~~~~~~~~~~~~
+------------------
If you forgot to set the root password you can do this:
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
new file mode 100644
index 0000000..95cde96
--- /dev/null
+++ b/doc/source/guides/neutron.rst
@@ -0,0 +1,229 @@
+======================================
+Using DevStack with Neutron Networking
+======================================
+
+This guide will walk you through using OpenStack Neutron with the ML2
+plugin and the Open vSwitch mechanism driver.
+
+Network Interface Configuration
+===============================
+
+To use Neutron, it is suggested that two network interfaces be present
+in the host operating system.
+
+The first interface, eth0 is used for the OpenStack management (API,
+message bus, etc) as well as for ssh for an administrator to access
+the machine.
+
+::
+
+ stack@compute:~$ ifconfig eth0
+ eth0 Link encap:Ethernet HWaddr bc:16:65:20:af:fc
+ inet addr:192.168.1.18
+
+eth1 is manually configured at boot to not have an IP address.
+Consult your operating system documentation for the appropriate
+technique. For Ubuntu, the contents of `/etc/network/interfaces`
+contains:
+
+::
+
+ auto eth1
+ iface eth1 inet manual
+ up ifconfig $IFACE 0.0.0.0 up
+ down ifconfig $IFACE 0.0.0.0 down
+
+The second physical interface, eth1 is added to a bridge (in this case
+named br-ex), which is used to forward network traffic from guest VMs.
+Network traffic from eth1 on the compute nodes is then NAT'd by the
+controller node that runs Neutron's `neutron-l3-agent` and provides L3
+connectivity.
+
+::
+
+ stack@compute:~$ sudo ovs-vsctl add-br br-ex
+ stack@compute:~$ sudo ovs-vsctl add-port br-ex eth1
+ stack@compute:~$ sudo ovs-vsctl show
+ 9a25c837-32ab-45f6-b9f2-1dd888abcf0f
+ Bridge br-ex
+ Port br-ex
+ Interface br-ex
+ type: internal
+ Port phy-br-ex
+ Interface phy-br-ex
+ type: patch
+ options: {peer=int-br-ex}
+ Port "eth1"
+ Interface "eth1"
+
+
+
+
+Disabling Next Generation Firewall Tools
+========================================
+
+Devstack does not properly operate with modern firewall tools. Specifically
+it will appear as if the guest VM can access the external network via ICMP,
+but UDP and TCP packets will not be delivered to the guest VM. The root cause
+of the issue is that both ufw (Uncomplicated Firewall) and firewalld (Fedora's
+firewall manager) apply firewall rules to all interfaces in the system, rather
+then per-device. One solution to this problem is to revert to iptables
+functionality.
+
+To get a functional firewall configuration for Fedora do the following:
+
+::
+
+ sudo service iptables save
+ sudo systemctl disable firewalld
+ sudo systemctl enable iptables
+ sudo systemctl stop firewalld
+ sudo systemctl start iptables
+
+
+To get a functional firewall configuration for distributions containing ufw,
+disable ufw. Note ufw is generally not enabled by default in Ubuntu. To
+disable ufw if it was enabled, do the following:
+
+::
+
+ sudo service iptables save
+ sudo ufw disable
+
+
+
+
+Neutron Networking with Open vSwitch
+====================================
+
+Configuring Neutron networking in DevStack is very similar to
+configuring `nova-network` - many of the same configuration variables
+(like `FIXED_RANGE` and `FLOATING_RANGE`) used by `nova-network` are
+used by Neutron, which is intentional.
+
+The only difference is the disabling of `nova-network` in your
+local.conf, and the enabling of the Neutron components.
+
+
+Configuration
+-------------
+
+::
+
+ FIXED_RANGE=10.0.0.0/24
+ FLOATING_RANGE=192.168.27.0/24
+ PUBLIC_NETWORK_GATEWAY=192.168.27.2
+
+ disable_service n-net
+ enable_service q-svc
+ enable_service q-agt
+ enable_service q-dhcp
+ enable_service q-meta
+ enable_service q-l3
+
+ Q_USE_SECGROUP=True
+ ENABLE_TENANT_VLANS=True
+ TENANT_VLAN_RANGE=1000:1999
+ PHYSICAL_NETWORK=default
+ OVS_PHYSICAL_BRIDGE=br-ex
+
+In this configuration we are defining FLOATING_RANGE to be a
+subnet that exists in the private RFC1918 address space - however in
+in a real setup FLOATING_RANGE would be a public IP address range.
+
+Neutron Networking with Open vSwitch and Provider Networks
+==========================================================
+
+In some instances, it is desirable to use Neutron's provider
+networking extension, so that networks that are configured on an
+external router can be utilized by Neutron, and instances created via
+Nova can attach to the network managed by the external router.
+
+For example, in some lab environments, a hardware router has been
+pre-configured by another party, and an OpenStack developer has been
+given a VLAN tag and IP address range, so that instances created via
+DevStack will use the external router for L3 connectivity, as opposed
+to the Neutron L3 service.
+
+
+Service Configuration
+---------------------
+
+**Control Node**
+
+In this example, the control node will run the majority of the
+OpenStack API and management services (Keystone, Glance,
+Nova, Neutron, etc..)
+
+
+**Compute Nodes**
+
+In this example, the nodes that will host guest instances will run
+the `neutron-openvswitch-agent` for network connectivity, as well as
+the compute service `nova-compute`.
+
+DevStack Configuration
+----------------------
+
+The following is a snippet of the DevStack configuration on the
+controller node.
+
+::
+
+ PUBLIC_INTERFACE=eth1
+
+ ## Neutron options
+ Q_USE_SECGROUP=True
+ ENABLE_TENANT_VLANS=True
+ TENANT_VLAN_RANGE=3001:4000
+ PHYSICAL_NETWORK=default
+ OVS_PHYSICAL_BRIDGE=br-ex
+
+ Q_USE_PROVIDER_NETWORKING=True
+ Q_L3_ENABLED=False
+
+ # Do not use Nova-Network
+ disable_service n-net
+
+ # Neutron
+ ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt
+
+ ## Neutron Networking options used to create Neutron Subnets
+
+ FIXED_RANGE="10.1.1.0/24"
+ PROVIDER_SUBNET_NAME="provider_net"
+ PROVIDER_NETWORK_TYPE="vlan"
+ SEGMENTATION_ID=2010
+
+In this configuration we are defining FIXED_RANGE to be a
+subnet that exists in the private RFC1918 address space - however
+in a real setup FIXED_RANGE would be a public IP address range, so
+that you could access your instances from the public internet.
+
+The following is a snippet of the DevStack configuration on the
+compute node.
+
+::
+
+ # Services that a compute node runs
+ ENABLED_SERVICES=n-cpu,rabbit,q-agt
+
+ ## Neutron options
+ Q_USE_SECGROUP=True
+ ENABLE_TENANT_VLANS=True
+ TENANT_VLAN_RANGE=3001:4000
+ PHYSICAL_NETWORK=default
+ OVS_PHYSICAL_BRIDGE=br-ex
+ PUBLIC_INTERFACE=eth1
+ Q_USE_PROVIDER_NETWORKING=True
+ Q_L3_ENABLED=False
+
+When DevStack is configured to use provider networking (via
+`Q_USE_PROVIDER_NETWORKING` is True and `Q_L3_ENABLED` is False) -
+DevStack will automatically add the network interface defined in
+`PUBLIC_INTERFACE` to the `OVS_PHYSICAL_BRIDGE`
+
+For example, with the above configuration, a bridge is
+created, named `br-ex` which is managed by Open vSwitch, and the
+second interface on the compute node, `eth1` is attached to the
+bridge, to forward traffic sent by guest vms.
diff --git a/doc/source/guides/single-machine.rst b/doc/source/guides/single-machine.rst
index 6059511..70287a9 100644
--- a/doc/source/guides/single-machine.rst
+++ b/doc/source/guides/single-machine.rst
@@ -1,31 +1,31 @@
-==========
-All-In-One
-==========
+=========================
+All-In-One Single Machine
+=========================
Things are about to get real! Using OpenStack in containers or VMs is
nice for kicking the tires, but doesn't compare to the feeling you get
with hardware.
Prerequisites Linux & Network
------------------------------
+=============================
Minimal Install
-~~~~~~~~~~~~~~~
+---------------
You need to have a system with a fresh install of Linux. You can
download the `Minimal
CD <https://help.ubuntu.com/community/Installation/MinimalCD>`__ for
Ubuntu releases since DevStack will download & install all the
additional dependencies. The netinstall ISO is available for
-`Fedora <http://mirrors.kernel.org/fedora/releases/18/Fedora/x86_64/iso/Fedora-20-x86_64-netinst.iso>`__
+`Fedora <http://mirrors.kernel.org/fedora/releases/>`__
and
-`CentOS/RHEL <http://mirrors.kernel.org/centos/6.5/isos/x86_64/CentOS-6.5-x86_64-netinstall.iso>`__.
+`CentOS/RHEL <http://mirrors.kernel.org/centos/>`__.
You may be tempted to use a desktop distro on a laptop, it will probably
work but you may need to tell Network Manager to keep its fingers off
the interface(s) that OpenStack uses for bridging.
Network Configuration
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
Determine the network configuration on the interface used to integrate
your OpenStack cloud with your existing network. For example, if the IPs
@@ -36,10 +36,10 @@
of DHCP (i.e. 192.168.1.201).
Installation shake and bake
----------------------------
+===========================
Add your user
-~~~~~~~~~~~~~
+-------------
We need to add a user to install DevStack. (if you created a user during
install you can skip this step and just give the user sudo privileges
@@ -61,7 +61,7 @@
**login** as that user.
Download DevStack
-~~~~~~~~~~~~~~~~~
+-----------------
We'll grab the latest version of DevStack via https:
@@ -72,7 +72,7 @@
cd devstack
Run DevStack
-~~~~~~~~~~~~
+------------
Now to configure ``stack.sh``. DevStack includes a sample in
``devstack/samples/local.conf``. Create ``local.conf`` as shown below to
@@ -108,6 +108,7 @@
MYSQL_PASSWORD=iheartdatabases
RABBIT_PASSWORD=flopsymopsy
SERVICE_PASSWORD=iheartksl
+ SERVICE_TOKEN=xyzpdqlazydog
Run DevStack:
@@ -120,7 +121,7 @@
accounts and passwords to poke at your shiny new OpenStack.
Using OpenStack
-~~~~~~~~~~~~~~~
+---------------
At this point you should be able to access the dashboard from other
computers on the local network. In this example that would be
diff --git a/doc/source/guides/single-vm.rst b/doc/source/guides/single-vm.rst
index d296db6..ab46d91 100644
--- a/doc/source/guides/single-vm.rst
+++ b/doc/source/guides/single-vm.rst
@@ -1,6 +1,6 @@
-=============
-Cloud in a VM
-=============
+====================
+All-In-One Single VM
+====================
Use the cloud to build the cloud! Use your cloud to launch new versions
of OpenStack in about 5 minutes. When you break it, start over! The VMs
@@ -9,16 +9,16 @@
operation. Speed not required.
Prerequisites Cloud & Image
----------------------------
+===========================
Virtual Machine
-~~~~~~~~~~~~~~~
+---------------
DevStack should run in any virtual machine running a supported Linux
-release. It will perform best with 2Gb or more of RAM.
+release. It will perform best with 4Gb or more of RAM.
OpenStack Deployment & cloud-init
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+---------------------------------
If the cloud service has an image with ``cloud-init`` pre-installed, use
it. You can get one from `Ubuntu's Daily
@@ -33,10 +33,10 @@
bare-bones server installation.
Installation shake and bake
----------------------------
+===========================
Launching With Cloud-Init
-~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------
This cloud config grabs the latest version of DevStack via git, creates
a minimal ``local.conf`` file and kicks off ``stack.sh``. It should be
@@ -79,13 +79,13 @@
to create a non-root user and run the ``start.sh`` script as that user.
Launching By Hand
-~~~~~~~~~~~~~~~~~
+-----------------
Using a hypervisor directly, launch the VM and either manually perform
the steps in the embedded shell script above or copy it into the VM.
Using OpenStack
-~~~~~~~~~~~~~~~
+---------------
At this point you should be able to access the dashboard. Launch VMs and
if you give them floating IPs access those VMs from other machines on
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 6c38216..855a2d6 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,6 +1,8 @@
DevStack - an OpenStack Community Production
============================================
+.. image:: assets/images/logo-blue.png
+
.. toctree::
:glob:
:maxdepth: 1
@@ -12,15 +14,12 @@
changes
contributing
- guides/*
-
-
-Quick Start This ain't your first rodeo
----------------------------------------
+Quick Start
+-----------
#. Select a Linux Distribution
- Only Ubuntu 14.04 (Trusty), Fedora 20 and CentOS/RHEL 6.5 are
+ Only Ubuntu 14.04 (Trusty), Fedora 20 and CentOS/RHEL 7 are
documented here. OpenStack also runs and is packaged on other flavors
of Linux such as OpenSUSE and Debian.
@@ -42,8 +41,8 @@
#. Configure
- We recommend at least a `minimal
- configuration <configuration.html>`__ be set up.
+ We recommend at least a :doc:`minimal
+ configuration <configuration>` be set up.
#. Start the install
@@ -59,255 +58,179 @@
Walk through various setups used by stackers
-OpenStack on VMs
-----------------
+.. toctree::
+ :glob:
+ :maxdepth: 1
-These guides tell you how to virtualize your OpenStack cloud in virtual
-machines. This means that you can get started without having to purchase
-any hardware.
+ guides/single-vm
+ guides/single-machine
+ guides/multinode-lab
+ guides/neutron
+ guides/devstack-with-nested-kvm
-Virtual Machine
-~~~~~~~~~~~~~~~
+All-In-One Single VM
+--------------------
-`Run OpenStack in a VM <guides/single-vm.html>`__. The VMs launched in your cloud will be slow as
+Run :doc:`OpenStack in a VM <guides/single-vm>`. The VMs launched in your cloud will be slow as
they are running in QEMU (emulation), but it is useful if you don't have
-spare hardware laying around. `[Read] <guides/single-vm.html>`__
+spare hardware laying around. :doc:`[Read] <guides/single-vm>`
-OpenStack on Hardware
----------------------
+All-In-One Single Machine
+-------------------------
-These guides tell you how to deploy a development environment on real
-hardware. Guides range from running OpenStack on a single laptop to
-running a multi-node deployment on datacenter hardware.
+Run :doc:`OpenStack on dedicated hardware <guides/single-machine>` This can include a
+server-class machine or a laptop at home.
+:doc:`[Read] <guides/single-machine>`
-All-In-One
-~~~~~~~~~~
+Multi-Node Lab
+--------------
-`Run OpenStack on dedicated hardware <guides/single-machine.html>`__ to get real performance in your VMs.
-This can include a server-class machine or a laptop at home. `[Read] <guides/single-machine.html>`__
+Setup a :doc:`multi-node cluster <guides/multinode-lab>` with dedicated VLANs for VMs & Management.
+:doc:`[Read] <guides/multinode-lab>`
-Multi-Node + VLANs
-~~~~~~~~~~~~~~~~~~
+DevStack with Neutron Networking
+--------------------------------
-`Setup a multi-node cluster <guides/multinode-lab.html>`__ with dedicated VLANs for VMs & Management. `[Read] <guides/multinode-lab.html>`__
+Building a DevStack cluster with :doc:`Neutron Networking <guides/neutron>`.
+This guide is meant for building lab environments with a dedicated
+control node and multiple compute nodes.
-Documentation
-=============
+DevStack with KVM-based Nested Virtualization
+---------------------------------------------
+
+Procedure to setup :doc:`DevStack with KVM-based Nested Virtualization
+<guides/devstack-with-nested-kvm>`. With this setup, Nova instances
+will be more performant than with plain QEMU emulation.
+
+DevStack Documentation
+======================
Overview
--------
-`An overview of DevStack goals and priorities <overview.html>`__
+:doc:`An overview of DevStack goals and priorities <overview>`
Configuration
-------------
-`Configuring and customizing the stack <configuration.html>`__
+:doc:`Configuring and customizing the stack <configuration>`
Plugins
-------
-`Extending DevStack with new features <plugins.html>`__
+:doc:`Extending DevStack with new features <plugins>`
Recent Changes
--------------
-`An incomplete summary of recent changes <changes.html>`__
+:doc:`An incomplete summary of recent changes <changes>`
FAQ
---
-`The DevStack FAQ <faq.html>`__
+:doc:`The DevStack FAQ <faq>`
Contributing
------------
-`Pitching in to make DevStack a better place <contributing.html>`__
+:doc:`Pitching in to make DevStack a better place <contributing>`
Code
====
-A look at the bits that make it all go
+*A look at the bits that make it all go*
Scripts
-------
-Generated documentation of DevStack scripts.
+* `stack.sh <stack.sh.html>`__ - The main script
+* `functions <functions.html>`__ - DevStack-specific functions
+* `functions-common <functions-common.html>`__ - Functions shared with other projects
+* `lib/apache <lib/apache.html>`__
+* `lib/ceilometer <lib/ceilometer.html>`__
+* `lib/ceph <lib/ceph.html>`__
+* `lib/cinder <lib/cinder.html>`__
+* `lib/config <lib/config.html>`__
+* `lib/database <lib/database.html>`__
+* `lib/dib <lib/dib.html>`__
+* `lib/dstat <lib/dstat.html>`__
+* `lib/glance <lib/glance.html>`__
+* `lib/heat <lib/heat.html>`__
+* `lib/horizon <lib/horizon.html>`__
+* `lib/infra <lib/infra.html>`__
+* `lib/ironic <lib/ironic.html>`__
+* `lib/keystone <lib/keystone.html>`__
+* `lib/ldap <lib/ldap.html>`__
+* `lib/neutron <lib/neutron.html>`__
+* `lib/nova <lib/nova.html>`__
+* `lib/oslo <lib/oslo.html>`__
+* `lib/rpc\_backend <lib/rpc_backend.html>`__
+* `lib/sahara <lib/sahara.html>`__
+* `lib/swift <lib/swift.html>`__
+* `lib/tempest <lib/tempest.html>`__
+* `lib/tls <lib/tls.html>`__
+* `lib/trove <lib/trove.html>`__
+* `lib/zaqar <lib/zaqar.html>`__
+* `unstack.sh <unstack.sh.html>`__
+* `clean.sh <clean.sh.html>`__
+* `run\_tests.sh <run_tests.sh.html>`__
-+-------------------------------+----------------------------------------------+
-| Filename | Link |
-+===============================+==============================================+
-| stack.sh | `Read » <stack.sh.html>`__ |
-+-------------------------------+----------------------------------------------+
-| functions | `Read » <functions.html>`__ |
-+-------------------------------+----------------------------------------------+
-| functions-common | `Read » <functions-common.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/apache | `Read » <lib/apache.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/baremetal | `Read » <lib/baremetal.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/ceilometer | `Read » <lib/ceilometer.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/cinder | `Read » <lib/cinder.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/config | `Read » <lib/config.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/database | `Read » <lib/database.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/glance | `Read » <lib/glance.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/heat | `Read » <lib/heat.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/horizon | `Read » <lib/horizon.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/infra | `Read » <lib/infra.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/ironic | `Read » <lib/ironic.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/keystone | `Read » <lib/keystone.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/ldap | `Read » <lib/ldap.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/zaqar | `Read » <lib/zaqar.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/neutron | `Read » <lib/neutron.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/nova | `Read » <lib/nova.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/oslo | `Read » <lib/oslo.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/rpc\_backend | `Read » <lib/rpc_backend.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/sahara | `Read » <lib/sahara.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/savanna | `Read » <lib/savanna.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/stackforge | `Read » <lib/stackforge.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/swift | `Read » <lib/swift.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/tempest | `Read » <lib/tempest.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/tls | `Read » <lib/tls.html>`__ |
-+-------------------------------+----------------------------------------------+
-| lib/trove | `Read » <lib/trove.html>`__ |
-+-------------------------------+----------------------------------------------+
-| unstack.sh | `Read » <unstack.sh.html>`__ |
-+-------------------------------+----------------------------------------------+
-| clean.sh | `Read » <clean.sh.html>`__ |
-+-------------------------------+----------------------------------------------+
-| run\_tests.sh | `Read » <run_tests.sh.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/50-ironic.sh | `Read » <extras.d/50-ironic.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/70-zaqar.sh | `Read » <extras.d/70-zaqar.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/70-sahara.sh | `Read » <extras.d/70-sahara.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/70-savanna.sh | `Read » <extras.d/70-savanna.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/70-trove.sh | `Read » <extras.d/70-trove.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/80-opendaylight.sh | `Read » <extras.d/80-opendaylight.html>`__ |
-+-------------------------------+----------------------------------------------+
-| extras.d/80-tempest.sh | `Read » <extras.d/80-tempest.html>`__ |
-+-------------------------------+----------------------------------------------+
+* `extras.d/40-dib.sh <extras.d/40-dib.sh.html>`__
+* `extras.d/50-ironic.sh <extras.d/50-ironic.sh.html>`__
+* `extras.d/60-ceph.sh <extras.d/60-ceph.sh.html>`__
+* `extras.d/70-sahara.sh <extras.d/70-sahara.sh.html>`__
+* `extras.d/70-trove.sh <extras.d/70-trove.sh.html>`__
+* `extras.d/70-tuskar.sh <extras.d/70-tuskar.sh.html>`__
+* `extras.d/70-zaqar.sh <extras.d/70-zaqar.sh.html>`__
+* `extras.d/80-tempest.sh <extras.d/80-tempest.sh.html>`__
Configuration
-------------
-+--------------+--------------------------------+
-| Filename | Link |
-+==============+================================+
-| local.conf | `Read » <local.conf.html>`__ |
-+--------------+--------------------------------+
-| stackrc | `Read » <stackrc.html>`__ |
-+--------------+--------------------------------+
-| openrc | `Read » <openrc.html>`__ |
-+--------------+--------------------------------+
-| exerciserc | `Read » <exerciserc.html>`__ |
-+--------------+--------------------------------+
-| eucarc | `Read » <eucarc.html>`__ |
-+--------------+--------------------------------+
-
-Tools
------
-
-+-----------------------------+----------------------------------------------+
-| Filename | Link |
-+=============================+==============================================+
-| tools/info.sh | `Read » <tools/info.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/build\_docs.sh | `Read » <tools/build_docs.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/create\_userrc.sh | `Read » <tools/create_userrc.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/fixup\_stuff.sh | `Read » <tools/fixup_stuff.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/install\_prereqs.sh | `Read » <tools/install_prereqs.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/install\_pip.sh | `Read » <tools/install_pip.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-| tools/upload\_image.sh | `Read » <tools/upload_image.sh.html>`__ |
-+-----------------------------+----------------------------------------------+
-
-Samples
--------
-
-Generated documentation of DevStack sample files.
-
-+------------+--------------------------------------+
-| Filename | Link |
-+============+======================================+
-| local.sh | `Read » <samples/local.sh.html>`__ |
-+------------+--------------------------------------+
-| localrc | `Read » <samples/localrc.html>`__ |
-+------------+--------------------------------------+
-
-Exercises
----------
-
-+---------------------------------+-------------------------------------------------+
-| Filename | Link |
-+=================================+=================================================+
-| exercise.sh | `Read » <exercise.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/aggregates.sh | `Read » <exercises/aggregates.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/boot\_from\_volume.sh | `Read » <exercises/boot_from_volume.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/bundle.sh | `Read » <exercises/bundle.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/client-args.sh | `Read » <exercises/client-args.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/client-env.sh | `Read » <exercises/client-env.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/euca.sh | `Read » <exercises/euca.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/floating\_ips.sh | `Read » <exercises/floating_ips.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/horizon.sh | `Read » <exercises/horizon.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/neutron-adv-test.sh | `Read » <exercises/neutron-adv-test.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/sahara.sh | `Read » <exercises/sahara.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/savanna.sh | `Read » <exercises/savanna.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/sec\_groups.sh | `Read » <exercises/sec_groups.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/swift.sh | `Read » <exercises/swift.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/trove.sh | `Read » <exercises/trove.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/volumes.sh | `Read » <exercises/volumes.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-| exercises/zaqar.sh | `Read » <exercises/zaqar.sh.html>`__ |
-+---------------------------------+-------------------------------------------------+
-
.. toctree::
:glob:
:maxdepth: 1
- *
+ local.conf
+ stackrc
+ openrc
+ exerciserc
+ eucarc
+
+Tools
+-----
+
+* `tools/build\_docs.sh <tools/build_docs.sh.html>`__
+* `tools/create-stack-user.sh <tools/create-stack-user.sh.html>`__
+* `tools/create\_userrc.sh <tools/create_userrc.sh.html>`__
+* `tools/fixup\_stuff.sh <tools/fixup_stuff.sh.html>`__
+* `tools/info.sh <tools/info.sh.html>`__
+* `tools/install\_pip.sh <tools/install_pip.sh.html>`__
+* `tools/install\_prereqs.sh <tools/install_prereqs.sh.html>`__
+* `tools/make\_cert.sh <tools/make_cert.sh.html>`__
+* `tools/upload\_image.sh <tools/upload_image.sh.html>`__
+
+Samples
+-------
+
+* `local.sh <samples/local.sh.html>`__
+
+Exercises
+---------
+
+* `exercise.sh <exercise.sh.html>`__
+* `exercises/aggregates.sh <exercises/aggregates.sh.html>`__
+* `exercises/boot\_from\_volume.sh <exercises/boot_from_volume.sh.html>`__
+* `exercises/bundle.sh <exercises/bundle.sh.html>`__
+* `exercises/client-args.sh <exercises/client-args.sh.html>`__
+* `exercises/client-env.sh <exercises/client-env.sh.html>`__
+* `exercises/euca.sh <exercises/euca.sh.html>`__
+* `exercises/floating\_ips.sh <exercises/floating_ips.sh.html>`__
+* `exercises/horizon.sh <exercises/horizon.sh.html>`__
+* `exercises/neutron-adv-test.sh <exercises/neutron-adv-test.sh.html>`__
+* `exercises/sahara.sh <exercises/sahara.sh.html>`__
+* `exercises/sec\_groups.sh <exercises/sec_groups.sh.html>`__
+* `exercises/swift.sh <exercises/swift.sh.html>`__
+* `exercises/trove.sh <exercises/trove.sh.html>`__
+* `exercises/volumes.sh <exercises/volumes.sh.html>`__
+* `exercises/zaqar.sh <exercises/zaqar.sh.html>`__
diff --git a/doc/source/local.conf.rst b/doc/source/local.conf.rst
index a9dfcb0..a1ca60a 100644
--- a/doc/source/local.conf.rst
+++ b/doc/source/local.conf.rst
@@ -2,8 +2,8 @@
local.conf - User Settings
==========================
-``local.conf`` is a user-maintained setings file that is sourced in
+``local.conf`` is a user-maintained settings file that is sourced in
``stackrc``. It contains a section that replaces the historical
-``localrc`` file. See `the description of
-local.conf <configuration.html>`__ for more details about the mechanics
+``localrc`` file. See the description of
+:doc:`local.conf <configuration>` for more details about the mechanics
of the file.
diff --git a/doc/source/localrc.rst b/doc/source/localrc.rst
deleted file mode 100644
index 98f3083..0000000
--- a/doc/source/localrc.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-=====================
-localrc - The Old Way
-=====================
-
-``localrc`` is the old file used to configure DevStack. It is deprecated
-and has been replaced by ```local.conf`` <local.conf.html>`__. DevStack
-will continue to use ``localrc`` if it is present and ignore the
-``localrc`` section in ``local.conf.``. Remove ``localrc`` to switch to
-using the new file.
diff --git a/doc/source/openrc.rst b/doc/source/openrc.rst
index dc12f76..0b090c7 100644
--- a/doc/source/openrc.rst
+++ b/doc/source/openrc.rst
@@ -4,33 +4,34 @@
``openrc`` configures login credentials suitable for use with the
OpenStack command-line tools. ``openrc`` sources ``stackrc`` at the
-beginning (which in turn sources the ``localrc`` setion of
+beginning (which in turn sources the ``localrc`` section of
``local.conf``) in order to pick up ``HOST_IP`` and/or ``SERVICE_HOST``
to use in the endpoints. The values shown below are the default values.
-OS\_TENANT\_NAME
- The introduction of Keystone to the OpenStack ecosystem has
- standardized the term *tenant* as the entity that owns resources. In
- some places references still exist to the original Nova term
- *project* for this use. Also, *tenant\_name* is preferred to
- *tenant\_id*.
+OS\_PROJECT\_NAME (OS\_TENANT\_NAME)
+ Keystone has
+ standardized the term *project* as the entity that owns resources. In
+ some places references still exist to the previous term
+ *tenant* for this use. Also, *project\_name* is preferred to
+ *project\_id*. OS\_TENANT\_NAME remains supported for compatibility
+ with older tools.
::
- OS_TENANT_NAME=demo
+ OS_PROJECT_NAME=demo
OS\_USERNAME
- In addition to the owning entity (tenant), Nova stores the entity
- performing the action as the *user*.
+ In addition to the owning entity (project), OpenStack calls the entity
+ performing the action *user*.
::
OS_USERNAME=demo
OS\_PASSWORD
- With Keystone you pass the keystone password instead of an api key.
- Recent versions of novaclient use OS\_PASSWORD instead of
- NOVA\_API\_KEYs or NOVA\_PASSWORD.
+ Keystone's default authentication requires a password be provided.
+ The usual cautions about putting passwords in environment variables
+ apply, for most DevStack uses this may be an acceptable tradeoff.
::
@@ -39,7 +40,7 @@
HOST\_IP, SERVICE\_HOST
Set API endpoint host using ``HOST_IP``. ``SERVICE_HOST`` may also
be used to specify the endpoint, which is convenient for some
- ``localrc`` configurations. Typically, ``HOST_IP`` is set in the
+ ``local.conf`` configurations. Typically, ``HOST_IP`` is set in the
``localrc`` section.
::
@@ -57,15 +58,6 @@
OS_AUTH_URL=http://$SERVICE_HOST:5000/v2.0
-GLANCE\_HOST
- Some exercises call Glance directly. On a single-node installation,
- Glance should be listening on ``HOST_IP``. If its running elsewhere
- it can be set here.
-
- ::
-
- GLANCE_HOST=$HOST_IP
-
KEYSTONECLIENT\_DEBUG, NOVACLIENT\_DEBUG
Set command-line client log level to ``DEBUG``. These are commented
out by default.
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
index e3cf75d..23ccf27 100644
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -13,10 +13,10 @@
"tested") going forward.
Supported Components
---------------------
+====================
Base OS
-~~~~~~~
+-------
*The OpenStack Technical Committee (TC) has defined the current CI
strategy to include the latest Ubuntu release and the latest RHEL
@@ -33,7 +33,7 @@
side-effects on other OS platforms.
Databases
-~~~~~~~~~
+---------
*As packaged by the host OS*
@@ -41,7 +41,7 @@
- PostgreSQL
Queues
-~~~~~~
+------
*As packaged by the host OS*
@@ -49,14 +49,14 @@
- Qpid
Web Server
-~~~~~~~~~~
+----------
*As packaged by the host OS*
- Apache
OpenStack Network
-~~~~~~~~~~~~~~~~~
+-----------------
*Default to Nova Network, optionally use Neutron*
@@ -65,7 +65,7 @@
mode using linuxbridge or OpenVSwitch.
Services
-~~~~~~~~
+--------
The default services configured by DevStack are Identity (Keystone),
Object Storage (Swift), Image Storage (Glance), Block Storage (Cinder),
@@ -73,18 +73,18 @@
(Heat)
Additional services not included directly in DevStack can be tied in to
-``stack.sh`` using the `plugin mechanism <plugins.html>`__ to call
+``stack.sh`` using the :doc:`plugin mechanism <plugins>` to call
scripts that perform the configuration and startup of the service.
Node Configurations
-~~~~~~~~~~~~~~~~~~~
+-------------------
- single node
- multi-node is not tested regularly by the core team, and even then
only minimal configurations are reviewed
Exercises
-~~~~~~~~~
+---------
The DevStack exercise scripts are no longer used as integration and gate
testing as that job has transitioned to Tempest. They are still
diff --git a/doc/source/plugins.rst b/doc/source/plugins.rst
index 282c1a4..5d6d3f1 100644
--- a/doc/source/plugins.rst
+++ b/doc/source/plugins.rst
@@ -6,17 +6,17 @@
support for additional projects and features.
Extras.d Hooks
-~~~~~~~~~~~~~~
+==============
-These relatively new hooks are an extension of the existing calls from
-``stack.sh`` at the end of its run, plus ``unstack.sh`` and
+These hooks are an extension of the service calls in
+``stack.sh`` at specific points in its run, plus ``unstack.sh`` and
``clean.sh``. A number of the higher-layer projects are implemented in
DevStack using this mechanism.
The script in ``extras.d`` is expected to be mostly a dispatcher to
functions in a ``lib/*`` script. The scripts are named with a
zero-padded two digits sequence number prefix to control the order that
-the scripts are called, and with a suffix of ``.sh``. DevSack reserves
+the scripts are called, and with a suffix of ``.sh``. DevStack reserves
for itself the sequence numbers 00 through 09 and 90 through 99.
Below is a template that shows handlers for the possible command-line
@@ -92,8 +92,52 @@
- **clean** - Called by ``clean.sh`` before other services are cleaned,
but after ``unstack.sh`` has been called.
+
+Externally Hosted Plugins
+=========================
+
+Based on the extras.d hooks, DevStack supports a standard mechansim
+for including plugins from external repositories. The plugin interface
+assumes the following:
+
+An external git repository that includes a ``devstack/`` top level
+directory. Inside this directory there can be 2 files.
+
+- ``settings`` - a file containing global variables that will be
+ sourced very early in the process. This is helpful if other plugins
+ might depend on this one, and need access to global variables to do
+ their work.
+
+ Your settings should include any ``enable_service`` lines required
+ by your plugin. This is especially important if you are kicking off
+ services using ``run_process`` as it only works with enabled
+ services.
+
+- ``plugin.sh`` - the actual plugin. It will be executed by devstack
+ during it's run. The run order will be done in the registration
+ order for these plugins, and will occur immediately after all in
+ tree extras.d dispatch at the phase in question. The plugin.sh
+ looks like the extras.d dispatcher above.
+
+Plugins are registered by adding the following to the localrc section
+of ``local.conf``.
+
+They are added in the following format::
+
+ [[local|localrc]]
+ enable_plugin <NAME> <GITURL> [GITREF]
+
+- ``name`` - an arbitrary name. (ex: glustfs, docker, zaqar, congress)
+- ``giturl`` - a valid git url that can be cloned
+- ``gitref`` - an optional git ref (branch / ref / tag) that will be
+ cloned. Defaults to master.
+
+An example would be as follows::
+
+ enable_plugin ec2api git://git.openstack.org/stackforge/ec2api
+
Hypervisor
-~~~~~~~~~~
+==========
Hypervisor plugins are fairly new and condense most hypervisor
configuration into one place.
diff --git a/doc/source/stackrc.rst b/doc/source/stackrc.rst
index 0faab45..b21f74f 100644
--- a/doc/source/stackrc.rst
+++ b/doc/source/stackrc.rst
@@ -15,12 +15,12 @@
Specify which services to launch. These generally correspond to
screen tabs. The default includes: Glance (API and Registry),
Keystone, Nova (API, Certificate, Object Store, Compute, Network,
- Scheduler, VNC proxies, Certificate Authentication), Cinder
+ Scheduler, Certificate Authentication), Cinder
(Scheduler, API, Volume), Horizon, MySQL, RabbitMQ, Tempest.
::
- ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,rabbit,tempest,$DATABASE_TYPE
+ ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,c-sch,c-api,c-vol,n-sch,n-cauth,horizon,rabbit,tempest,$DATABASE_TYPE
Other services that are not enabled by default can be enabled in
``localrc``. For example, to add Swift, use the following service
diff --git a/driver_certs/cinder_driver_cert.sh b/driver_certs/cinder_driver_cert.sh
index 7726e7e..d066e06 100755
--- a/driver_certs/cinder_driver_cert.sh
+++ b/driver_certs/cinder_driver_cert.sh
@@ -93,8 +93,8 @@
sleep 5
# run tempest api/volume/test_*
-log_message "Run the actual tempest volume tests (./tools/pretty_tox.sh api.volume)...", True
-./tools/pretty_tox.sh api.volume 2>&1 | tee -a $TEMPFILE
+log_message "Run the actual tempest volume tests (./tools/pretty_tox.sh volume)...", True
+./tools/pretty_tox.sh volume 2>&1 | tee -a $TEMPFILE
if [[ $? = 0 ]]; then
log_message "CONGRATULATIONS!!! Device driver PASSED!", True
log_message "Submit output: ($TEMPFILE)"
diff --git a/exercises/horizon.sh b/exercises/horizon.sh
index d62ad52..ad08221 100755
--- a/exercises/horizon.sh
+++ b/exercises/horizon.sh
@@ -36,7 +36,7 @@
is_service_enabled horizon || exit 55
# can we get the front page
-curl http://$SERVICE_HOST 2>/dev/null | grep -q '<h3>Log In</h3>' || die $LINENO "Horizon front page not functioning!"
+curl http://$SERVICE_HOST 2>/dev/null | grep -q '<h3.*>Log In</h3>' || die $LINENO "Horizon front page not functioning!"
set +o xtrace
echo "*********************************************************************"
diff --git a/exercises/trove.sh b/exercises/trove.sh
deleted file mode 100755
index 053f872..0000000
--- a/exercises/trove.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-
-# **trove.sh**
-
-# Sanity check that trove started if enabled
-
-echo "*********************************************************************"
-echo "Begin DevStack Exercise: $0"
-echo "*********************************************************************"
-
-# This script exits on an error so that errors don't compound and you see
-# only the first error that occurred.
-set -o errexit
-
-# Print the commands being run so that we can see the command that triggers
-# an error. It is also useful for following allowing as the install occurs.
-set -o xtrace
-
-
-# Settings
-# ========
-
-# Keep track of the current directory
-EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
-TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
-
-# Import common functions
-source $TOP_DIR/functions
-
-# Import configuration
-source $TOP_DIR/openrc
-
-# Import exercise configuration
-source $TOP_DIR/exerciserc
-
-is_service_enabled trove || exit 55
-
-# can try to get datastore id
-DSTORE_ID=$(trove datastore-list | tail -n +4 |head -3 | get_field 1)
-die_if_not_set $LINENO DSTORE_ID "Trove API not functioning!"
-
-DV_ID=$(trove datastore-version-list $DSTORE_ID | tail -n +4 | get_field 1)
-die_if_not_set $LINENO DV_ID "Trove API not functioning!"
-
-set +o xtrace
-echo "*********************************************************************"
-echo "SUCCESS: End DevStack Exercise: $0"
-echo "*********************************************************************"
-
diff --git a/extras.d/60-ceph.sh b/extras.d/60-ceph.sh
index 50bdfae..38b901b 100644
--- a/extras.d/60-ceph.sh
+++ b/extras.d/60-ceph.sh
@@ -6,14 +6,19 @@
source $TOP_DIR/lib/ceph
elif [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
echo_summary "Installing Ceph"
- install_ceph
- echo_summary "Configuring Ceph"
- configure_ceph
- # NOTE (leseb): Do everything here because we need to have Ceph started before the main
- # OpenStack components. Ceph OSD must start here otherwise we can't upload any images.
- echo_summary "Initializing Ceph"
- init_ceph
- start_ceph
+ check_os_support_ceph
+ if [ "$REMOTE_CEPH" = "False" ]; then
+ install_ceph
+ echo_summary "Configuring Ceph"
+ configure_ceph
+ # NOTE (leseb): Do everything here because we need to have Ceph started before the main
+ # OpenStack components. Ceph OSD must start here otherwise we can't upload any images.
+ echo_summary "Initializing Ceph"
+ init_ceph
+ start_ceph
+ else
+ install_ceph_remote
+ fi
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
if is_service_enabled glance; then
echo_summary "Configuring Glance for Ceph"
@@ -32,14 +37,39 @@
echo_summary "Configuring libvirt secret"
import_libvirt_secret_ceph
fi
+
+ if [ "$REMOTE_CEPH" = "False" ]; then
+ if is_service_enabled glance; then
+ echo_summary "Configuring Glance for Ceph"
+ configure_ceph_embedded_glance
+ fi
+ if is_service_enabled nova; then
+ echo_summary "Configuring Nova for Ceph"
+ configure_ceph_embedded_nova
+ fi
+ if is_service_enabled cinder; then
+ echo_summary "Configuring Cinder for Ceph"
+ configure_ceph_embedded_cinder
+ fi
+ fi
fi
if [[ "$1" == "unstack" ]]; then
- stop_ceph
- cleanup_ceph
+ if [ "$REMOTE_CEPH" = "True" ]; then
+ cleanup_ceph_remote
+ else
+ cleanup_ceph_embedded
+ stop_ceph
+ fi
+ cleanup_ceph_general
fi
if [[ "$1" == "clean" ]]; then
- cleanup_ceph
+ if [ "$REMOTE_CEPH" = "True" ]; then
+ cleanup_ceph_remote
+ else
+ cleanup_ceph_embedded
+ fi
+ cleanup_ceph_general
fi
fi
diff --git a/extras.d/70-gantt.sh b/extras.d/70-gantt.sh
deleted file mode 100644
index ac1efba..0000000
--- a/extras.d/70-gantt.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-# gantt.sh - Devstack extras script to install Gantt
-
-if is_service_enabled n-sch; then
- disable_service gantt
-fi
-
-if is_service_enabled gantt; then
- if [[ "$1" == "source" ]]; then
- # Initial source
- source $TOP_DIR/lib/gantt
- elif [[ "$1" == "stack" && "$2" == "install" ]]; then
- echo_summary "Installing Gantt"
- install_gantt
- cleanup_gantt
- elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
- echo_summary "Configuring Gantt"
- configure_gantt
-
- elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
- # Initialize gantt
- init_gantt
-
- # Start gantt
- echo_summary "Starting Gantt"
- start_gantt
- fi
-
- if [[ "$1" == "unstack" ]]; then
- stop_gantt
- fi
-fi
diff --git a/extras.d/70-sahara.sh b/extras.d/70-sahara.sh
index 2a34999..f177766 100644
--- a/extras.d/70-sahara.sh
+++ b/extras.d/70-sahara.sh
@@ -15,6 +15,7 @@
create_sahara_accounts
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
echo_summary "Initializing sahara"
+ sahara_register_images
start_sahara
fi
diff --git a/extras.d/70-trove.sh b/extras.d/70-trove.sh
index a4dc7fb..f284354 100644
--- a/extras.d/70-trove.sh
+++ b/extras.d/70-trove.sh
@@ -11,7 +11,6 @@
cleanup_trove
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
echo_summary "Configuring Trove"
- configure_troveclient
configure_trove
if is_service_enabled key; then
diff --git a/extras.d/80-opendaylight.sh b/extras.d/80-opendaylight.sh
deleted file mode 100644
index b673777..0000000
--- a/extras.d/80-opendaylight.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-# opendaylight.sh - DevStack extras script
-
-if is_service_enabled odl-server odl-compute; then
- # Initial source
- [[ "$1" == "source" ]] && source $TOP_DIR/lib/opendaylight
-fi
-
-if is_service_enabled odl-server; then
- if [[ "$1" == "source" ]]; then
- # no-op
- :
- elif [[ "$1" == "stack" && "$2" == "install" ]]; then
- install_opendaylight
- configure_opendaylight
- init_opendaylight
- elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
- configure_ml2_odl
- # This has to start before Neutron
- start_opendaylight
- elif [[ "$1" == "stack" && "$2" == "post-extra" ]]; then
- # no-op
- :
- fi
-
- if [[ "$1" == "unstack" ]]; then
- stop_opendaylight
- cleanup_opendaylight
- fi
-
- if [[ "$1" == "clean" ]]; then
- # no-op
- :
- fi
-fi
-
-if is_service_enabled odl-compute; then
- if [[ "$1" == "source" ]]; then
- # no-op
- :
- elif [[ "$1" == "stack" && "$2" == "install" ]]; then
- install_opendaylight-compute
- elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
- if is_service_enabled nova; then
- create_nova_conf_neutron
- fi
- elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
- echo_summary "Initializing OpenDaylight"
- ODL_LOCAL_IP=${ODL_LOCAL_IP:-$HOST_IP}
- ODL_MGR_PORT=${ODL_MGR_PORT:-6640}
- read ovstbl <<< $(sudo ovs-vsctl get Open_vSwitch . _uuid)
- sudo ovs-vsctl set-manager tcp:$ODL_MGR_IP:$ODL_MGR_PORT
- if [[ -n "$ODL_PROVIDER_MAPPINGS" ]] && [[ "$ENABLE_TENANT_VLANS" == "True" ]]; then
- sudo ovs-vsctl set Open_vSwitch $ovstbl \
- other_config:provider_mappings=$ODL_PROVIDER_MAPPINGS
- fi
- sudo ovs-vsctl set Open_vSwitch $ovstbl other_config:local_ip=$ODL_LOCAL_IP
- elif [[ "$1" == "stack" && "$2" == "post-extra" ]]; then
- # no-op
- :
- fi
-
- if [[ "$1" == "unstack" ]]; then
- sudo ovs-vsctl del-manager
- BRIDGES=$(sudo ovs-vsctl list-br)
- for bridge in $BRIDGES ; do
- sudo ovs-vsctl del-controller $bridge
- done
-
- stop_opendaylight-compute
- fi
-
- if [[ "$1" == "clean" ]]; then
- # no-op
- :
- fi
-fi
diff --git a/files/apache-keystone.template b/files/apache-keystone.template
index 88492d3..504dc01 100644
--- a/files/apache-keystone.template
+++ b/files/apache-keystone.template
@@ -6,6 +6,7 @@
WSGIProcessGroup keystone-public
WSGIScriptAlias / %PUBLICWSGI%
WSGIApplicationGroup %{GLOBAL}
+ WSGIPassAuthorization On
<IfVersion >= 2.4>
ErrorLogFormat "%{cu}t %M"
</IfVersion>
@@ -21,6 +22,7 @@
WSGIProcessGroup keystone-admin
WSGIScriptAlias / %ADMINWSGI%
WSGIApplicationGroup %{GLOBAL}
+ WSGIPassAuthorization On
<IfVersion >= 2.4>
ErrorLogFormat "%{cu}t %M"
</IfVersion>
@@ -30,7 +32,3 @@
%SSLCERTFILE%
%SSLKEYFILE%
</VirtualHost>
-
-# Workaround for missing path on RHEL6, see
-# https://bugzilla.redhat.com/show_bug.cgi?id=1121019
-WSGISocketPrefix /var/run/%APACHE_NAME%
diff --git a/files/apts b/files/apts
new file mode 120000
index 0000000..ef926de
--- /dev/null
+++ b/files/apts
@@ -0,0 +1 @@
+debs/
\ No newline at end of file
diff --git a/files/apts/ryu b/files/apts/ryu
deleted file mode 100644
index 9b85080..0000000
--- a/files/apts/ryu
+++ /dev/null
@@ -1,2 +0,0 @@
-python-eventlet
-python-sphinx
diff --git a/files/apts/baremetal b/files/debs/baremetal
similarity index 100%
rename from files/apts/baremetal
rename to files/debs/baremetal
diff --git a/files/apts/ceilometer-collector b/files/debs/ceilometer-collector
similarity index 100%
rename from files/apts/ceilometer-collector
rename to files/debs/ceilometer-collector
diff --git a/files/apts/ceph b/files/debs/ceph
similarity index 100%
rename from files/apts/ceph
rename to files/debs/ceph
diff --git a/files/apts/cinder b/files/debs/cinder
similarity index 100%
rename from files/apts/cinder
rename to files/debs/cinder
diff --git a/files/apts/dstat b/files/debs/dstat
similarity index 100%
rename from files/apts/dstat
rename to files/debs/dstat
diff --git a/files/apts/general b/files/debs/general
similarity index 92%
rename from files/apts/general
rename to files/debs/general
index 3fe7863..4050191 100644
--- a/files/apts/general
+++ b/files/debs/general
@@ -1,6 +1,5 @@
bridge-utils
pylint
-python-setuptools
screen
unzip
wget
@@ -28,3 +27,4 @@
libffi-dev
libssl-dev # for pyOpenSSL
gettext # used for compiling message catalogs
+openjdk-7-jre-headless # NOPRIME
diff --git a/files/apts/glance b/files/debs/glance
similarity index 94%
rename from files/apts/glance
rename to files/debs/glance
index 15e09aa..8db8145 100644
--- a/files/apts/glance
+++ b/files/debs/glance
@@ -7,7 +7,6 @@
python-routes
python-greenlet
python-sqlalchemy
-python-wsgiref
python-pastedeploy
python-xattr
python-iso8601
diff --git a/files/apts/heat b/files/debs/heat
similarity index 100%
rename from files/apts/heat
rename to files/debs/heat
diff --git a/files/apts/horizon b/files/debs/horizon
similarity index 95%
rename from files/apts/horizon
rename to files/debs/horizon
index 5d06928..f9b7d59 100644
--- a/files/apts/horizon
+++ b/files/debs/horizon
@@ -12,7 +12,6 @@
pylint
python-eventlet
python-nose
-python-sphinx
python-mox
python-coverage
python-cherrypy3 # why?
diff --git a/files/apts/ironic b/files/debs/ironic
similarity index 95%
rename from files/apts/ironic
rename to files/debs/ironic
index 45fdecc..f6c7b74 100644
--- a/files/apts/ironic
+++ b/files/debs/ironic
@@ -12,6 +12,7 @@
qemu
qemu-kvm
qemu-utils
+sgabios
syslinux
tftpd-hpa
xinetd
diff --git a/files/apts/keystone b/files/debs/keystone
similarity index 100%
rename from files/apts/keystone
rename to files/debs/keystone
diff --git a/files/apts/ldap b/files/debs/ldap
similarity index 100%
rename from files/apts/ldap
rename to files/debs/ldap
diff --git a/files/apts/n-api b/files/debs/n-api
similarity index 100%
rename from files/apts/n-api
rename to files/debs/n-api
diff --git a/files/apts/n-cpu b/files/debs/n-cpu
similarity index 100%
rename from files/apts/n-cpu
rename to files/debs/n-cpu
diff --git a/files/apts/n-novnc b/files/debs/n-novnc
similarity index 100%
rename from files/apts/n-novnc
rename to files/debs/n-novnc
diff --git a/files/apts/neutron b/files/debs/neutron
similarity index 85%
rename from files/apts/neutron
rename to files/debs/neutron
index a48a800..3f4b6d2 100644
--- a/files/apts/neutron
+++ b/files/debs/neutron
@@ -1,3 +1,4 @@
+acl # testonly
ebtables
iptables
iputils-ping
@@ -5,6 +6,7 @@
libmysqlclient-dev # testonly
mysql-server #NOPRIME
sudo
+postgresql-server-dev-all # testonly
python-iso8601
python-paste
python-routes
@@ -24,3 +26,4 @@
sqlite3
vlan
radvd # NOPRIME
+uuid-runtime
diff --git a/files/apts/nova b/files/debs/nova
similarity index 100%
rename from files/apts/nova
rename to files/debs/nova
diff --git a/files/apts/openvswitch b/files/debs/openvswitch
similarity index 100%
rename from files/apts/openvswitch
rename to files/debs/openvswitch
diff --git a/files/apts/postgresql b/files/debs/postgresql
similarity index 100%
rename from files/apts/postgresql
rename to files/debs/postgresql
diff --git a/files/apts/q-agt b/files/debs/q-agt
similarity index 100%
rename from files/apts/q-agt
rename to files/debs/q-agt
diff --git a/files/apts/q-l3 b/files/debs/q-l3
similarity index 68%
rename from files/apts/q-l3
rename to files/debs/q-l3
index b98b628..106a6a3 100644
--- a/files/apts/q-l3
+++ b/files/debs/q-l3
@@ -1,2 +1,3 @@
+conntrack
conntrackd
keepalived
diff --git a/files/apts/qpid b/files/debs/qpid
similarity index 100%
rename from files/apts/qpid
rename to files/debs/qpid
diff --git a/files/debs/ryu b/files/debs/ryu
new file mode 100644
index 0000000..354c1b7
--- /dev/null
+++ b/files/debs/ryu
@@ -0,0 +1 @@
+python-eventlet
diff --git a/files/apts/swift b/files/debs/swift
similarity index 100%
rename from files/apts/swift
rename to files/debs/swift
diff --git a/files/apts/tempest b/files/debs/tempest
similarity index 100%
rename from files/apts/tempest
rename to files/debs/tempest
diff --git a/files/apts/tls-proxy b/files/debs/tls-proxy
similarity index 100%
rename from files/apts/tls-proxy
rename to files/debs/tls-proxy
diff --git a/files/apts/trema b/files/debs/trema
similarity index 100%
rename from files/apts/trema
rename to files/debs/trema
diff --git a/files/apts/trove b/files/debs/trove
similarity index 100%
rename from files/apts/trove
rename to files/debs/trove
diff --git a/files/apts/zaqar-server b/files/debs/zaqar-server
similarity index 100%
rename from files/apts/zaqar-server
rename to files/debs/zaqar-server
diff --git a/files/default_catalog.templates b/files/default_catalog.templates
index a18d38f..4aab416 100644
--- a/files/default_catalog.templates
+++ b/files/default_catalog.templates
@@ -30,9 +30,9 @@
catalog.RegionOne.volumev2.name = Volume Service V2
-catalog.RegionOne.ec2.publicURL = http://%SERVICE_HOST%:8773/services/Cloud
-catalog.RegionOne.ec2.adminURL = http://%SERVICE_HOST%:8773/services/Admin
-catalog.RegionOne.ec2.internalURL = http://%SERVICE_HOST%:8773/services/Cloud
+catalog.RegionOne.ec2.publicURL = http://%SERVICE_HOST%:8773/
+catalog.RegionOne.ec2.adminURL = http://%SERVICE_HOST%:8773/
+catalog.RegionOne.ec2.internalURL = http://%SERVICE_HOST%:8773/
catalog.RegionOne.ec2.name = EC2 Service
diff --git a/files/patches/unittest2-discover.patch b/files/patches/unittest2-discover.patch
deleted file mode 100644
index 347300d..0000000
--- a/files/patches/unittest2-discover.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-diff -r b2efb7df637b discover.py
---- a/discover.py Thu Mar 24 00:31:02 2011 -0400
-+++ b/discover.py Thu Nov 28 12:02:19 2013 +0000
-@@ -82,7 +82,11 @@
- """
- testMethodPrefix = 'test'
- sortTestMethodsUsing = cmp
-- suiteClass = unittest.TestSuite
-+ try:
-+ import unittest2
-+ suiteClass = unittest2.TestSuite
-+ except ImportError:
-+ suiteClass = unittest.TestSuite
- _top_level_dir = None
-
- def loadTestsFromTestCase(self, testCaseClass):
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index 0a4746f..63ef705 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -15,10 +15,10 @@
psmisc
python-cmd2 # dist:opensuse-12.3
python-pylint
-python-setuptools # instead of python-distribute; dist:sle11sp2
python-unittest2
screen
tar
tcpdump
unzip
wget
+net-tools
diff --git a/files/rpms-suse/glance b/files/rpms-suse/glance
index edd1564..9b962f9 100644
--- a/files/rpms-suse/glance
+++ b/files/rpms-suse/glance
@@ -8,5 +8,4 @@
python-greenlet
python-iso8601
python-pyOpenSSL
-python-wsgiref
python-xattr
diff --git a/files/rpms-suse/horizon b/files/rpms-suse/horizon
index fa7e439..d1f378a 100644
--- a/files/rpms-suse/horizon
+++ b/files/rpms-suse/horizon
@@ -4,7 +4,6 @@
python-Paste
python-PasteDeploy
python-Routes
-python-Sphinx
python-SQLAlchemy
python-WebOb
python-anyjson
diff --git a/files/rpms-suse/neutron b/files/rpms-suse/neutron
index 8431bd1..66d6e4c 100644
--- a/files/rpms-suse/neutron
+++ b/files/rpms-suse/neutron
@@ -1,9 +1,11 @@
+acl # testonly
dnsmasq
dnsmasq-utils # dist:opensuse-12.3,opensuse-13.1
ebtables
iptables
iputils
mariadb # NOPRIME
+postgresql-devel # testonly
python-eventlet
python-greenlet
python-iso8601
diff --git a/files/rpms-suse/ryu b/files/rpms-suse/ryu
index 6b426fb..354c1b7 100644
--- a/files/rpms-suse/ryu
+++ b/files/rpms-suse/ryu
@@ -1,2 +1 @@
-python-Sphinx
python-eventlet
diff --git a/files/rpms/cinder b/files/rpms/cinder
index ce6181e..082a35a 100644
--- a/files/rpms/cinder
+++ b/files/rpms/cinder
@@ -3,4 +3,4 @@
qemu-img
postgresql-devel
iscsi-initiator-utils
-python-lxml #dist:f19,f20,rhel7
+python-lxml
diff --git a/files/rpms/general b/files/rpms/general
index d4a9fcb..6f22391 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -13,8 +13,6 @@
libxslt-devel
psmisc
pylint
-python-setuptools
-python-prettytable # dist:rhel6 [1]
python-unittest2
python-virtualenv
python-devel
@@ -27,8 +25,5 @@
bc
libyaml-devel
gettext # used for compiling message catalogs
-
-# [1] : some of installed tools have unversioned dependencies on this,
-# but others have versioned (<=0.7). So if a later version (0.7.1)
-# gets installed in response to an unversioned dependency, it breaks.
-# This pre-installs a compatible 0.6(ish) version from RHEL
+net-tools
+java-1.7.0-openjdk-headless # NOPRIME
diff --git a/files/rpms/glance b/files/rpms/glance
index 5a7f073..a09b669 100644
--- a/files/rpms/glance
+++ b/files/rpms/glance
@@ -6,10 +6,9 @@
python-argparse
python-eventlet
python-greenlet
-python-lxml #dist:f19,f20,rhel7
-python-paste-deploy #dist:f19,f20,rhel7
+python-lxml
+python-paste-deploy
python-routes
python-sqlalchemy
-python-wsgiref #dist:f18,f19,f20
pyxattr
zlib-devel # testonly
diff --git a/files/rpms/horizon b/files/rpms/horizon
index 7add23a..585c36c 100644
--- a/files/rpms/horizon
+++ b/files/rpms/horizon
@@ -12,10 +12,9 @@
python-migrate
python-mox
python-nose
-python-paste #dist:f19,f20
-python-paste-deploy #dist:f19,f20
+python-paste
+python-paste-deploy
python-routes
-python-sphinx
python-sqlalchemy
python-webob
pyxattr
diff --git a/files/rpms/ironic b/files/rpms/ironic
index e646f3a..0a46314 100644
--- a/files/rpms/ironic
+++ b/files/rpms/ironic
@@ -9,6 +9,7 @@
openssh-clients
openvswitch
python-libguestfs
+sgabios
syslinux
tftp-server
xinetd
diff --git a/files/rpms/keystone b/files/rpms/keystone
index ce41ee5..45492e0 100644
--- a/files/rpms/keystone
+++ b/files/rpms/keystone
@@ -1,10 +1,10 @@
MySQL-python
python-greenlet
-libxslt-devel # dist:f20
-python-lxml #dist:f19,f20
-python-paste #dist:f19,f20
-python-paste-deploy #dist:f19,f20
-python-paste-script #dist:f19,f20
+libxslt-devel
+python-lxml
+python-paste
+python-paste-deploy
+python-paste-script
python-routes
python-sqlalchemy
python-webob
diff --git a/files/rpms/neutron b/files/rpms/neutron
index 2c9dd3d..d11dab7 100644
--- a/files/rpms/neutron
+++ b/files/rpms/neutron
@@ -1,4 +1,5 @@
MySQL-python
+acl # testonly
dnsmasq # for q-dhcp
dnsmasq-utils # for dhcp_release
ebtables
@@ -8,12 +9,12 @@
mysql-devel # testonly
mysql-server # NOPRIME
openvswitch # NOPRIME
+postgresql-devel # testonly
python-eventlet
python-greenlet
python-iso8601
-#rhel6 gets via pip
-python-paste # dist:f19,f20,rhel7
-python-paste-deploy # dist:f19,f20,rhel7
+python-paste
+python-paste-deploy
python-qpid # NOPRIME
python-routes
python-sqlalchemy
diff --git a/files/rpms/nova b/files/rpms/nova
index f3261c6..557de90 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -29,11 +29,9 @@
python-lockfile
python-migrate
python-mox
-python-paramiko # dist:f19,f20,rhel7
-# ^ on RHEL6, brings in python-crypto which conflicts with version from
-# pip we need
-python-paste # dist:f19,f20,rhel7
-python-paste-deploy # dist:f19,f20,rhel7
+python-paramiko
+python-paste
+python-paste-deploy
python-qpid # NOPRIME
python-routes
python-sqlalchemy
diff --git a/files/rpms/qpid b/files/rpms/qpid
index 9e3f10a..c5e2699 100644
--- a/files/rpms/qpid
+++ b/files/rpms/qpid
@@ -1,4 +1,4 @@
qpid-proton-c-devel # NOPRIME
python-qpid-proton # NOPRIME
cyrus-sasl-lib # NOPRIME
-
+cyrus-sasl-plain # NOPRIME
diff --git a/files/rpms/ryu b/files/rpms/ryu
index 9b85080..354c1b7 100644
--- a/files/rpms/ryu
+++ b/files/rpms/ryu
@@ -1,2 +1 @@
python-eventlet
-python-sphinx
diff --git a/files/rpms/swift b/files/rpms/swift
index 9ec4aab..0fcdb0f 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -6,7 +6,7 @@
python-greenlet
python-netifaces
python-nose
-python-paste-deploy # dist:f19,f20,rhel7
+python-paste-deploy
python-simplejson
python-webob
pyxattr
diff --git a/functions b/functions
index bb40a48..2f976cf 100644
--- a/functions
+++ b/functions
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# functions - DevStack-specific functions
#
# The following variables are assumed to be defined by certain functions:
@@ -11,6 +13,7 @@
# Include the common functions
FUNC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd)
source ${FUNC_DIR}/functions-common
+source ${FUNC_DIR}/inc/python
# Save trace setting
XTRACE=$(set +o | grep xtrace)
@@ -40,7 +43,7 @@
if [[ $image_url != file* ]]; then
# Downloads the image (uec ami+akistyle), then extracts it.
if [[ ! -f $FILES/$image_fname || "$(stat -c "%s" $FILES/$image_fname)" = "0" ]]; then
- wget -c $image_url -O $FILES/$image_fname
+ wget --progress=dot:giga -c $image_url -O $FILES/$image_fname
if [[ $? -ne 0 ]]; then
echo "Not found: $image_url"
return
@@ -82,7 +85,7 @@
# driver will supply default values.
local vmdk_disktype=""
- local vmdk_net_adapter=""
+ local vmdk_net_adapter="e1000"
local path_len
# vmdk adapter type
@@ -114,7 +117,7 @@
if [[ $flat_url != file* ]]; then
if [[ ! -f $FILES/$flat_fname || \
"$(stat -c "%s" $FILES/$flat_fname)" = "0" ]]; then
- wget -c $flat_url -O $FILES/$flat_fname
+ wget --progress=dot:giga -c $flat_url -O $FILES/$flat_fname
fi
image="$FILES/${flat_fname}"
else
@@ -351,7 +354,7 @@
local boot_timeout=$3
local expected=${4:-"True"}
local check_command=""
- MULTI_HOST=`trueorfalse False $MULTI_HOST`
+ MULTI_HOST=$(trueorfalse False MULTI_HOST)
if [[ "$MULTI_HOST" = "True" && "$from_net" = "$PRIVATE_NETWORK_NAME" ]]; then
return
fi
diff --git a/functions-common b/functions-common
index 9f8476e..6beb670 100644
--- a/functions-common
+++ b/functions-common
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# functions-common - Common functions used by DevStack components
#
# The canonical copy of this file is maintained in the DevStack repo.
@@ -13,19 +15,15 @@
# - OpenStack Functions
# - Package Functions
# - Process Functions
-# - Python Functions
# - Service Functions
# - System Functions
#
# The following variables are assumed to be defined by certain functions:
#
-# - ``GIT_DEPTH``
# - ``ENABLED_SERVICES``
# - ``ERROR_ON_CLONE``
# - ``FILES``
# - ``OFFLINE``
-# - ``PIP_DOWNLOAD_CACHE``
-# - ``PIP_USE_MIRRORS``
# - ``RECLONE``
# - ``REQUIREMENTS_DIR``
# - ``STACK_USER``
@@ -43,6 +41,7 @@
declare -A GITBRANCH
declare -A GITDIR
+TRACK_DEPENDS=${TRACK_DEPENDS:-False}
# Config Functions
# ================
@@ -147,6 +146,21 @@
$xtrace
}
+function inidelete {
+ local xtrace=$(set +o | grep xtrace)
+ set +o xtrace
+ local file=$1
+ local section=$2
+ local option=$3
+
+ [[ -z $section || -z $option ]] && return
+
+ # Remove old values
+ sed -i -e "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ d; }" "$file"
+
+ $xtrace
+}
+
# Set an option in an INI file
# iniset config-file section option value
function iniset {
@@ -229,7 +243,8 @@
local xtrace=$(set +o | grep xtrace)
set +o xtrace
local default=$1
- local testval=$2
+ local literal=$2
+ local testval=${!literal:-}
[[ -z "$testval" ]] && { echo "$default"; return; }
[[ "0 no No NO false False FALSE" =~ "$testval" ]] && { echo "False"; return; }
@@ -238,6 +253,14 @@
$xtrace
}
+function isset {
+ nounset=$(set +o | grep nounset)
+ set +o nounset
+ [[ -n "${!1+x}" ]]
+ result=$?
+ $nounset
+ return $result
+}
# Control Functions
# =================
@@ -295,8 +318,8 @@
set +o xtrace
local msg="[ERROR] ${BASH_SOURCE[2]}:$1 $2"
echo $msg 1>&2;
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- echo $msg >> "${SCREEN_LOGDIR}/error.log"
+ if [[ -n ${LOGDIR} ]]; then
+ echo $msg >> "${LOGDIR}/error.log"
fi
$xtrace
return $exitcode
@@ -348,8 +371,8 @@
set +o xtrace
local msg="[WARNING] ${BASH_SOURCE[2]}:$1 $2"
echo $msg 1>&2;
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- echo $msg >> "${SCREEN_LOGDIR}/error.log"
+ if [[ -n ${LOGDIR} ]]; then
+ echo $msg >> "${LOGDIR}/error.log"
fi
$xtrace
return $exitcode
@@ -367,7 +390,11 @@
# ``os_UPDATE`` - update: ex. the ``5`` in ``RHEL6.5``
# ``os_PACKAGE`` - package type: ``deb`` or ``rpm``
# ``os_CODENAME`` - vendor's codename for release: ``snow leopard``, ``trusty``
-declare os_VENDOR os_RELEASE os_UPDATE os_PACKAGE os_CODENAME
+os_VENDOR=""
+os_RELEASE=""
+os_UPDATE=""
+os_PACKAGE=""
+os_CODENAME=""
# GetOSVersion
function GetOSVersion {
@@ -563,8 +590,7 @@
# Set global ``RECLONE=yes`` to simulate a clone when dest-dir exists
# Set global ``ERROR_ON_CLONE=True`` to abort execution with an error if the git repo
# does not exist (default is False, meaning the repo will be cloned).
-# Set global ``GIT_DEPTH=<number>`` to limit the history depth of the git clone
-# Uses globals ``ERROR_ON_CLONE``, ``OFFLINE``, ``RECLONE``, ``GIT_DEPTH``
+# Uses globals ``ERROR_ON_CLONE``, ``OFFLINE``, ``RECLONE``
# git_clone remote dest-dir branch
function git_clone {
local git_remote=$1
@@ -573,9 +599,8 @@
local orig_dir=$(pwd)
local git_clone_flags=""
- RECLONE=$(trueorfalse False $RECLONE)
-
- if [[ -n "${GIT_DEPTH}" ]]; then
+ RECLONE=$(trueorfalse False RECLONE)
+ if [[ "${GIT_DEPTH}" -gt 0 ]]; then
git_clone_flags="$git_clone_flags --depth $GIT_DEPTH"
fi
@@ -800,7 +825,7 @@
# Gets or creates a domain
# Usage: get_or_create_domain <name> <description>
function get_or_create_domain {
- local os_url="$KEYSTONE_SERVICE_URI/v3"
+ local os_url="$KEYSTONE_SERVICE_URI_V3"
# Gets domain id
local domain_id=$(
# Gets domain id
@@ -816,31 +841,46 @@
echo $domain_id
}
+# Gets or creates group
+# Usage: get_or_create_group <groupname> [<domain> <description>]
+function get_or_create_group {
+ local domain=${2:+--domain ${2}}
+ local desc="${3:-}"
+ local os_url="$KEYSTONE_SERVICE_URI_V3"
+ # Gets group id
+ local group_id=$(
+ # Creates new group with --or-show
+ openstack --os-token=$OS_TOKEN --os-url=$os_url \
+ --os-identity-api-version=3 group create $1 \
+ $domain --description "$desc" --or-show \
+ -f value -c id
+ )
+ echo $group_id
+}
+
# Gets or creates user
-# Usage: get_or_create_user <username> <password> <project> [<email> [<domain>]]
+# Usage: get_or_create_user <username> <password> [<email> [<domain>]]
function get_or_create_user {
- if [[ ! -z "$4" ]]; then
- local email="--email=$4"
+ if [[ ! -z "$3" ]]; then
+ local email="--email=$3"
else
local email=""
fi
local os_cmd="openstack"
local domain=""
- if [[ ! -z "$5" ]]; then
- domain="--domain=$5"
- os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI/v3 --os-identity-api-version=3"
+ if [[ ! -z "$4" ]]; then
+ domain="--domain=$4"
+ os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI_V3 --os-identity-api-version=3"
fi
# Gets user id
local user_id=$(
- # Gets user id
- $os_cmd user show $1 $domain -f value -c id 2>/dev/null ||
- # Creates new user
+ # Creates new user with --or-show
$os_cmd user create \
$1 \
--password "$2" \
- --project $3 \
$email \
$domain \
+ --or-show \
-f value -c id
)
echo $user_id
@@ -854,13 +894,11 @@
local domain=""
if [[ ! -z "$2" ]]; then
domain="--domain=$2"
- os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI/v3 --os-identity-api-version=3"
+ os_cmd="$os_cmd --os-url=$KEYSTONE_SERVICE_URI_V3 --os-identity-api-version=3"
fi
local project_id=$(
- # Gets project id
- $os_cmd project show $1 $domain -f value -c id 2>/dev/null ||
- # Creates new project if not exists
- $os_cmd project create $1 $domain -f value -c id
+ # Creates new project with --or-show
+ $os_cmd project create $1 $domain --or-show -f value -c id
)
echo $project_id
}
@@ -869,20 +907,18 @@
# Usage: get_or_create_role <name>
function get_or_create_role {
local role_id=$(
- # Gets role id
- openstack role show $1 -f value -c id 2>/dev/null ||
- # Creates role if not exists
- openstack role create $1 -f value -c id
+ # Creates role with --or-show
+ openstack role create $1 --or-show -f value -c id
)
echo $role_id
}
-# Gets or adds user role
-# Usage: get_or_add_user_role <role> <user> <project>
-function get_or_add_user_role {
+# Gets or adds user role to project
+# Usage: get_or_add_user_project_role <role> <user> <project>
+function get_or_add_user_project_role {
# Gets user role id
- local user_role_id=$(openstack user role list \
- $2 \
+ local user_role_id=$(openstack role list \
+ --user $2 \
--project $3 \
--column "ID" \
--column "Name" \
@@ -907,8 +943,8 @@
openstack service show $1 -f value -c id 2>/dev/null ||
# Creates new service if not exists
openstack service create \
- $1 \
- --type=$2 \
+ $2 \
+ --name $1 \
--description="$3" \
-f value -c id
)
@@ -946,7 +982,7 @@
function _get_package_dir {
local pkg_dir
if is_ubuntu; then
- pkg_dir=$FILES/apts
+ pkg_dir=$FILES/debs
elif is_fedora; then
pkg_dir=$FILES/rpms
elif is_suse; then
@@ -969,14 +1005,15 @@
[[ "$(id -u)" = "0" ]] && sudo="env"
$xtrace
+
$sudo DEBIAN_FRONTEND=noninteractive \
- http_proxy=$http_proxy https_proxy=$https_proxy \
- no_proxy=$no_proxy \
+ http_proxy=${http_proxy:-} https_proxy=${https_proxy:-} \
+ no_proxy=${no_proxy:-} \
apt-get --option "Dpkg::Options::=--force-confold" --assume-yes "$@"
}
# get_packages() collects a list of package names of any type from the
-# prerequisite files in ``files/{apts|rpms}``. The list is intended
+# prerequisite files in ``files/{debs|rpms}``. The list is intended
# to be passed to a package installer such as apt or yum.
#
# Only packages required for the services in 1st argument will be
@@ -990,10 +1027,10 @@
set +o xtrace
local services=$@
local package_dir=$(_get_package_dir)
- local file_to_parse
- local service
+ local file_to_parse=""
+ local service=""
- INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
+ INSTALL_TESTONLY_PACKAGES=$(trueorfalse False INSTALL_TESTONLY_PACKAGES)
if [[ -z "$package_dir" ]]; then
echo "No package directory supplied"
@@ -1001,7 +1038,6 @@
fi
if [[ -z "$DISTRO" ]]; then
GetDistro
- echo "Found Distro $DISTRO"
fi
for service in ${services//,/ }; do
# Allow individual services to specify dependencies
@@ -1104,6 +1140,10 @@
# Uses globals ``NO_UPDATE_REPOS``, ``REPOS_UPDATED``, ``RETRY_UPDATE``
# install_package package [package ...]
function update_package_repo {
+ NO_UPDATE_REPOS=${NO_UPDATE_REPOS:-False}
+ REPOS_UPDATED=${REPOS_UPDATED:-False}
+ RETRY_UPDATE=${RETRY_UPDATE:-False}
+
if [[ "$NO_UPDATE_REPOS" = "True" ]]; then
return 0
fi
@@ -1166,7 +1206,7 @@
if is_ubuntu; then
apt_get purge "$@"
elif is_fedora; then
- sudo yum remove -y "$@"
+ sudo ${YUM:-yum} remove -y "$@" ||:
elif is_suse; then
sudo zypper rm "$@"
else
@@ -1175,7 +1215,7 @@
}
# Wrapper for ``yum`` to set proxy environment variables
-# Uses globals ``OFFLINE``, ``*_proxy``
+# Uses globals ``OFFLINE``, ``*_proxy``, ``YUM``
# yum_install package [package ...]
function yum_install {
[[ "$OFFLINE" = "True" ]] && return
@@ -1187,7 +1227,7 @@
# https://bugzilla.redhat.com/show_bug.cgi?id=965567
$sudo http_proxy=$http_proxy https_proxy=$https_proxy \
no_proxy=$no_proxy \
- yum install -y "$@" 2>&1 | \
+ ${YUM:-yum} install -y "$@" 2>&1 | \
awk '
BEGIN { fail=0 }
/No package/ { fail=1 }
@@ -1197,7 +1237,7 @@
# also ensure we catch a yum failure
if [[ ${PIPESTATUS[0]} != 0 ]]; then
- die $LINENO "Yum install failure"
+ die $LINENO "${YUM:-yum} install failure"
fi
}
@@ -1219,8 +1259,8 @@
# _run_process() is designed to be backgrounded by run_process() to simulate a
# fork. It includes the dirty work of closing extra filehandles and preparing log
# files to produce the same logs as screen_it(). The log filename is derived
-# from the service name and global-and-now-misnamed ``SCREEN_LOGDIR``
-# Uses globals ``CURRENT_LOG_TIME``, ``SCREEN_LOGDIR``, ``SCREEN_NAME``, ``SERVICE_DIR``
+# from the service name.
+# Uses globals ``CURRENT_LOG_TIME``, ``LOGDIR``, ``SCREEN_LOGDIR``, ``SCREEN_NAME``, ``SERVICE_DIR``
# If an optional group is provided sg will be used to set the group of
# the command.
# _run_process service "command-line" [group]
@@ -1235,9 +1275,14 @@
exec 3>&-
exec 6>&-
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- exec 1>&${SCREEN_LOGDIR}/screen-${service}.${CURRENT_LOG_TIME}.log 2>&1
- ln -sf ${SCREEN_LOGDIR}/screen-${service}.${CURRENT_LOG_TIME}.log ${SCREEN_LOGDIR}/screen-${service}.log
+ local real_logfile="${LOGDIR}/${service}.log.${CURRENT_LOG_TIME}"
+ if [[ -n ${LOGDIR} ]]; then
+ exec 1>&"$real_logfile" 2>&1
+ ln -sf "$real_logfile" ${LOGDIR}/${service}.log
+ if [[ -n ${SCREEN_LOGDIR} ]]; then
+ # Drop the backward-compat symlink
+ ln -sf "$real_logfile" ${SCREEN_LOGDIR}/screen-${service}.log
+ fi
# TODO(dtroyer): Hack to get stdout from the Python interpreter for the logs.
export PYTHONUNBUFFERED=1
@@ -1301,7 +1346,7 @@
}
# Helper to launch a process in a named screen
-# Uses globals ``CURRENT_LOG_TIME``, ``SCREEN_NAME``, ``SCREEN_LOGDIR``,
+# Uses globals ``CURRENT_LOG_TIME``, ```LOGDIR``, ``SCREEN_LOGDIR``, `SCREEN_NAME``,
# ``SERVICE_DIR``, ``USE_SCREEN``
# screen_process name "command-line" [group]
# Run a command in a shell in a screen window, if an optional group
@@ -1313,17 +1358,25 @@
SCREEN_NAME=${SCREEN_NAME:-stack}
SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
- USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+ USE_SCREEN=$(trueorfalse True USE_SCREEN)
# Append the process to the screen rc file
screen_rc "$name" "$command"
screen -S $SCREEN_NAME -X screen -t $name
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- screen -S $SCREEN_NAME -p $name -X logfile ${SCREEN_LOGDIR}/screen-${name}.${CURRENT_LOG_TIME}.log
+ local real_logfile="${LOGDIR}/${name}.log.${CURRENT_LOG_TIME}"
+ echo "LOGDIR: $LOGDIR"
+ echo "SCREEN_LOGDIR: $SCREEN_LOGDIR"
+ echo "log: $real_logfile"
+ if [[ -n ${LOGDIR} ]]; then
+ screen -S $SCREEN_NAME -p $name -X logfile "$real_logfile"
screen -S $SCREEN_NAME -p $name -X log on
- ln -sf ${SCREEN_LOGDIR}/screen-${name}.${CURRENT_LOG_TIME}.log ${SCREEN_LOGDIR}/screen-${name}.log
+ ln -sf "$real_logfile" ${LOGDIR}/${name}.log
+ if [[ -n ${SCREEN_LOGDIR} ]]; then
+ # Drop the backward-compat symlink
+ ln -sf "$real_logfile" ${SCREEN_LOGDIR}/screen-${1}.log
+ fi
fi
# sleep to allow bash to be ready to be send the command - we are
@@ -1368,8 +1421,8 @@
echo "screen -t $1 bash" >> $SCREENRC
echo "stuff \"$2$NL\"" >> $SCREENRC
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- echo "logfile ${SCREEN_LOGDIR}/screen-${1}.${CURRENT_LOG_TIME}.log" >>$SCREENRC
+ if [[ -n ${LOGDIR} ]]; then
+ echo "logfile ${LOGDIR}/${1}.log.${CURRENT_LOG_TIME}" >>$SCREENRC
echo "log on" >>$SCREENRC
fi
fi
@@ -1386,7 +1439,7 @@
SCREEN_NAME=${SCREEN_NAME:-stack}
SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
- USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+ USE_SCREEN=$(trueorfalse True USE_SCREEN)
if is_service_enabled $service; then
# Clean up the screen window
@@ -1404,7 +1457,7 @@
local service=$1
SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
- USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+ USE_SCREEN=$(trueorfalse True USE_SCREEN)
if is_service_enabled $service; then
# Kill via pid if we have one available
@@ -1454,7 +1507,7 @@
local name=$1
local logfile=$2
- USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+ USE_SCREEN=$(trueorfalse True USE_SCREEN)
if [[ "$USE_SCREEN" = "True" ]]; then
screen_process "$name" "sudo tail -f $logfile"
fi
@@ -1481,8 +1534,8 @@
exec 6>&-
if [[ -n ${SCREEN_LOGDIR} ]]; then
- exec 1>&${SCREEN_LOGDIR}/screen-${1}.${CURRENT_LOG_TIME}.log 2>&1
- ln -sf ${SCREEN_LOGDIR}/screen-${1}.${CURRENT_LOG_TIME}.log ${SCREEN_LOGDIR}/screen-${1}.log
+ exec 1>&${SCREEN_LOGDIR}/screen-${1}.log.${CURRENT_LOG_TIME} 2>&1
+ ln -sf ${SCREEN_LOGDIR}/screen-${1}.log.${CURRENT_LOG_TIME} ${SCREEN_LOGDIR}/screen-${1}.log
# TODO(dtroyer): Hack to get stdout from the Python interpreter for the logs.
export PYTHONUNBUFFERED=1
@@ -1535,191 +1588,97 @@
}
-# Python Functions
-# ================
+# Plugin Functions
+# =================
-# Get the path to the pip command.
-# get_pip_command
-function get_pip_command {
- which pip || which pip-python
+DEVSTACK_PLUGINS=${DEVSTACK_PLUGINS:-""}
- if [ $? -ne 0 ]; then
- die $LINENO "Unable to find pip; cannot continue"
- fi
+# enable_plugin <name> <url> [branch]
+#
+# ``name`` is an arbitrary name - (aka: glusterfs, nova-docker, zaqar)
+# ``url`` is a git url
+# ``branch`` is a gitref. If it's not set, defaults to master
+function enable_plugin {
+ local name=$1
+ local url=$2
+ local branch=${3:-master}
+ DEVSTACK_PLUGINS+=",$name"
+ GITREPO[$name]=$url
+ GITDIR[$name]=$DEST/$name
+ GITBRANCH[$name]=$branch
}
-# Get the path to the direcotry where python executables are installed.
-# get_python_exec_prefix
-function get_python_exec_prefix {
- if is_fedora || is_suse; then
- echo "/usr/bin"
- else
- echo "/usr/local/bin"
- fi
-}
+# fetch_plugins
+#
+# clones all plugins
+function fetch_plugins {
+ local plugins="${DEVSTACK_PLUGINS}"
+ local plugin
-# Wrapper for ``pip install`` to set cache and proxy environment variables
-# Uses globals ``OFFLINE``, ``PIP_DOWNLOAD_CACHE``, ``PIP_USE_MIRRORS``,
-# ``TRACK_DEPENDS``, ``*_proxy``
-# pip_install package [package ...]
-function pip_install {
- local xtrace=$(set +o | grep xtrace)
- set +o xtrace
- if [[ "$OFFLINE" = "True" || -z "$@" ]]; then
- $xtrace
+ # short circuit if nothing to do
+ if [[ -z $plugins ]]; then
return
fi
- if [[ -z "$os_PACKAGE" ]]; then
- GetOSVersion
+ echo "Fetching devstack plugins"
+ for plugin in ${plugins//,/ }; do
+ git_clone_by_name $plugin
+ done
+}
+
+# load_plugin_settings
+#
+# Load settings from plugins in the order that they were registered
+function load_plugin_settings {
+ local plugins="${DEVSTACK_PLUGINS}"
+ local plugin
+
+ # short circuit if nothing to do
+ if [[ -z $plugins ]]; then
+ return
fi
- if [[ $TRACK_DEPENDS = True && ! "$@" =~ virtualenv ]]; then
- # TRACK_DEPENDS=True installation creates a circular dependency when
- # we attempt to install virtualenv into a virualenv, so we must global
- # that installation.
- source $DEST/.venv/bin/activate
- local cmd_pip=$DEST/.venv/bin/pip
- local sudo_pip="env"
+
+ echo "Loading plugin settings"
+ for plugin in ${plugins//,/ }; do
+ local dir=${GITDIR[$plugin]}
+ # source any known settings
+ if [[ -f $dir/devstack/settings ]]; then
+ source $dir/devstack/settings
+ fi
+ done
+}
+
+# run_plugins
+#
+# Run the devstack/plugin.sh in all the plugin directories. These are
+# run in registration order.
+function run_plugins {
+ local mode=$1
+ local phase=$2
+
+ local plugins="${DEVSTACK_PLUGINS}"
+ local plugin
+ for plugin in ${plugins//,/ }; do
+ local dir=${GITDIR[$plugin]}
+ if [[ -f $dir/devstack/plugin.sh ]]; then
+ source $dir/devstack/plugin.sh $mode $phase
+ fi
+ done
+}
+
+function run_phase {
+ local mode=$1
+ local phase=$2
+ if [[ -d $TOP_DIR/extras.d ]]; then
+ for i in $TOP_DIR/extras.d/*.sh; do
+ [[ -r $i ]] && source $i $mode $phase
+ done
+ fi
+ # the source phase corresponds to settings loading in plugins
+ if [[ "$mode" == "source" ]]; then
+ load_plugin_settings
else
- local cmd_pip=$(get_pip_command)
- local sudo_pip="sudo"
- fi
-
- # Mirror option not needed anymore because pypi has CDN available,
- # but it's useful in certain circumstances
- PIP_USE_MIRRORS=${PIP_USE_MIRRORS:-False}
- local pip_mirror_opt=""
- if [[ "$PIP_USE_MIRRORS" != "False" ]]; then
- pip_mirror_opt="--use-mirrors"
- fi
-
- $xtrace
- $sudo_pip PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-/var/cache/pip} \
- http_proxy=$http_proxy \
- https_proxy=$https_proxy \
- no_proxy=$no_proxy \
- $cmd_pip install \
- $pip_mirror_opt $@
-
- INSTALL_TESTONLY_PACKAGES=$(trueorfalse False $INSTALL_TESTONLY_PACKAGES)
- if [[ "$INSTALL_TESTONLY_PACKAGES" == "True" ]]; then
- local test_req="$@/test-requirements.txt"
- if [[ -e "$test_req" ]]; then
- $sudo_pip PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-/var/cache/pip} \
- http_proxy=$http_proxy \
- https_proxy=$https_proxy \
- no_proxy=$no_proxy \
- $cmd_pip install \
- $pip_mirror_opt -r $test_req
- fi
- fi
-}
-
-# should we use this library from their git repo, or should we let it
-# get pulled in via pip dependencies.
-function use_library_from_git {
- local name=$1
- local enabled=1
- [[ ,${LIBS_FROM_GIT}, =~ ,${name}, ]] && enabled=0
- return $enabled
-}
-
-# setup a library by name. If we are trying to use the library from
-# git, we'll do a git based install, otherwise we'll punt and the
-# library should be installed by a requirements pull from another
-# project.
-function setup_lib {
- local name=$1
- local dir=${GITDIR[$name]}
- setup_install $dir
-}
-
-
-# this should be used if you want to install globally, all libraries should
-# use this, especially *oslo* ones
-function setup_install {
- local project_dir=$1
- setup_package_with_req_sync $project_dir
-}
-
-# this should be used for projects which run services, like all services
-function setup_develop {
- local project_dir=$1
- setup_package_with_req_sync $project_dir -e
-}
-
-# determine if a project as specified by directory is in
-# projects.txt. This will not be an exact match because we throw away
-# the namespacing when we clone, but it should be good enough in all
-# practical ways.
-function is_in_projects_txt {
- local project_dir=$1
- local project_name=$(basename $project_dir)
- return grep "/$project_name\$" $REQUIREMENTS_DIR/projects.txt >/dev/null
-}
-
-# ``pip install -e`` the package, which processes the dependencies
-# using pip before running `setup.py develop`
-#
-# Updates the dependencies in project_dir from the
-# openstack/requirements global list before installing anything.
-#
-# Uses globals ``TRACK_DEPENDS``, ``REQUIREMENTS_DIR``, ``UNDO_REQUIREMENTS``
-# setup_develop directory
-function setup_package_with_req_sync {
- local project_dir=$1
- local flags=$2
-
- # Don't update repo if local changes exist
- # Don't use buggy "git diff --quiet"
- # ``errexit`` requires us to trap the exit code when the repo is changed
- local update_requirements=$(cd $project_dir && git diff --exit-code >/dev/null || echo "changed")
-
- if [[ $update_requirements != "changed" ]]; then
- if [[ "$REQUIREMENTS_MODE" == "soft" ]]; then
- if is_in_projects_txt $project_dir; then
- (cd $REQUIREMENTS_DIR; \
- python update.py $project_dir)
- else
- # soft update projects not found in requirements project.txt
- (cd $REQUIREMENTS_DIR; \
- python update.py -s $project_dir)
- fi
- else
- (cd $REQUIREMENTS_DIR; \
- python update.py $project_dir)
- fi
- fi
-
- setup_package $project_dir $flags
-
- # We've just gone and possibly modified the user's source tree in an
- # automated way, which is considered bad form if it's a development
- # tree because we've screwed up their next git checkin. So undo it.
- #
- # However... there are some circumstances, like running in the gate
- # where we really really want the overridden version to stick. So provide
- # a variable that tells us whether or not we should UNDO the requirements
- # changes (this will be set to False in the OpenStack ci gate)
- if [ $UNDO_REQUIREMENTS = "True" ]; then
- if [[ $update_requirements != "changed" ]]; then
- (cd $project_dir && git reset --hard)
- fi
- fi
-}
-
-# ``pip install -e`` the package, which processes the dependencies
-# using pip before running `setup.py develop`
-# Uses globals ``STACK_USER``
-# setup_develop_no_requirements_update directory
-function setup_package {
- local project_dir=$1
- local flags=$2
-
- pip_install $flags $project_dir
- # ensure that further actions can do things like setup.py sdist
- if [[ "$flags" == "-e" ]]; then
- safe_chown -R $STACK_USER $1/*.egg-info
+ run_plugins $mode $phase
fi
}
@@ -1973,13 +1932,13 @@
# http_proxy=http://proxy.example.com:3128/ no_proxy=repo.example.net ./stack.sh
function export_proxy_variables {
- if [[ -n "$http_proxy" ]]; then
+ if isset http_proxy ; then
export http_proxy=$http_proxy
fi
- if [[ -n "$https_proxy" ]]; then
+ if isset https_proxy ; then
export https_proxy=$https_proxy
fi
- if [[ -n "$no_proxy" ]]; then
+ if isset no_proxy ; then
export no_proxy=$no_proxy
fi
}
diff --git a/gate/updown.sh b/gate/updown.sh
new file mode 100755
index 0000000..d2d7351
--- /dev/null
+++ b/gate/updown.sh
@@ -0,0 +1,24 @@
+#!/bin/bash -xe
+#
+# An up / down test for gate functional testing
+#
+# Note: this is expected to start running as jenkins
+
+# Step 1: give back sudoers permissions to devstack
+TEMPFILE=`mktemp`
+echo "stack ALL=(root) NOPASSWD:ALL" >$TEMPFILE
+chmod 0440 $TEMPFILE
+sudo chown root:root $TEMPFILE
+sudo mv $TEMPFILE /etc/sudoers.d/51_stack_sh
+
+# TODO: do something to start a guest to create crud that should
+# disappear
+
+# Step 2: unstack
+echo "Running unstack.sh"
+sudo -H -u stack stdbuf -oL -eL bash -ex ./unstack.sh
+
+# Step 3: clean
+echo "Running clean.sh"
+sudo -H -u stack stdbuf -oL -eL bash -ex ./clean.sh
+
diff --git a/inc/python b/inc/python
new file mode 100644
index 0000000..0348cb3
--- /dev/null
+++ b/inc/python
@@ -0,0 +1,223 @@
+#!/bin/bash
+#
+# **inc/python** - Python-related functions
+#
+# Support for pip/setuptools interfaces and virtual environments
+#
+# External functions used:
+# - GetOSVersion
+# - is_fedora
+# - is_suse
+# - safe_chown
+
+# Save trace setting
+INC_PY_TRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Python Functions
+# ================
+
+# Get the path to the pip command.
+# get_pip_command
+function get_pip_command {
+ which pip || which pip-python
+
+ if [ $? -ne 0 ]; then
+ die $LINENO "Unable to find pip; cannot continue"
+ fi
+}
+
+# Get the path to the direcotry where python executables are installed.
+# get_python_exec_prefix
+function get_python_exec_prefix {
+ if is_fedora || is_suse; then
+ echo "/usr/bin"
+ else
+ echo "/usr/local/bin"
+ fi
+}
+
+# Wrapper for ``pip install`` to set cache and proxy environment variables
+# Uses globals ``INSTALL_TESTONLY_PACKAGES``, ``OFFLINE``, ``TRACK_DEPENDS``,
+# ``*_proxy``
+# pip_install package [package ...]
+function pip_install {
+ local xtrace=$(set +o | grep xtrace)
+ set +o xtrace
+ local offline=${OFFLINE:-False}
+ if [[ "$offline" == "True" || -z "$@" ]]; then
+ $xtrace
+ return
+ fi
+
+ if [[ -z "$os_PACKAGE" ]]; then
+ GetOSVersion
+ fi
+ if [[ $TRACK_DEPENDS = True && ! "$@" =~ virtualenv ]]; then
+ # TRACK_DEPENDS=True installation creates a circular dependency when
+ # we attempt to install virtualenv into a virualenv, so we must global
+ # that installation.
+ source $DEST/.venv/bin/activate
+ local cmd_pip=$DEST/.venv/bin/pip
+ local sudo_pip="env"
+ else
+ local cmd_pip=$(get_pip_command)
+ local sudo_pip="sudo -H"
+ fi
+
+ local pip_version=$(python -c "import pip; \
+ print(pip.__version__.strip('.')[0])")
+ if (( pip_version<6 )); then
+ die $LINENO "Currently installed pip version ${pip_version} does not" \
+ "meet minimum requirements (>=6)."
+ fi
+
+ $xtrace
+ $sudo_pip \
+ http_proxy=${http_proxy:-} \
+ https_proxy=${https_proxy:-} \
+ no_proxy=${no_proxy:-} \
+ $cmd_pip install \
+ $@
+
+ INSTALL_TESTONLY_PACKAGES=$(trueorfalse False INSTALL_TESTONLY_PACKAGES)
+ if [[ "$INSTALL_TESTONLY_PACKAGES" == "True" ]]; then
+ local test_req="$@/test-requirements.txt"
+ if [[ -e "$test_req" ]]; then
+ $sudo_pip \
+ http_proxy=${http_proxy:-} \
+ https_proxy=${https_proxy:-} \
+ no_proxy=${no_proxy:-} \
+ $cmd_pip install \
+ -r $test_req
+ fi
+ fi
+}
+
+# should we use this library from their git repo, or should we let it
+# get pulled in via pip dependencies.
+function use_library_from_git {
+ local name=$1
+ local enabled=1
+ [[ ,${LIBS_FROM_GIT}, =~ ,${name}, ]] && enabled=0
+ return $enabled
+}
+
+# setup a library by name. If we are trying to use the library from
+# git, we'll do a git based install, otherwise we'll punt and the
+# library should be installed by a requirements pull from another
+# project.
+function setup_lib {
+ local name=$1
+ local dir=${GITDIR[$name]}
+ setup_install $dir
+}
+
+# setup a library by name in editiable mode. If we are trying to use
+# the library from git, we'll do a git based install, otherwise we'll
+# punt and the library should be installed by a requirements pull from
+# another project.
+#
+# use this for non namespaced libraries
+function setup_dev_lib {
+ local name=$1
+ local dir=${GITDIR[$name]}
+ setup_develop $dir
+}
+
+# this should be used if you want to install globally, all libraries should
+# use this, especially *oslo* ones
+function setup_install {
+ local project_dir=$1
+ setup_package_with_req_sync $project_dir
+}
+
+# this should be used for projects which run services, like all services
+function setup_develop {
+ local project_dir=$1
+ setup_package_with_req_sync $project_dir -e
+}
+
+# determine if a project as specified by directory is in
+# projects.txt. This will not be an exact match because we throw away
+# the namespacing when we clone, but it should be good enough in all
+# practical ways.
+function is_in_projects_txt {
+ local project_dir=$1
+ local project_name=$(basename $project_dir)
+ return grep "/$project_name\$" $REQUIREMENTS_DIR/projects.txt >/dev/null
+}
+
+# ``pip install -e`` the package, which processes the dependencies
+# using pip before running `setup.py develop`
+#
+# Updates the dependencies in project_dir from the
+# openstack/requirements global list before installing anything.
+#
+# Uses globals ``TRACK_DEPENDS``, ``REQUIREMENTS_DIR``, ``UNDO_REQUIREMENTS``
+# setup_develop directory
+function setup_package_with_req_sync {
+ local project_dir=$1
+ local flags=$2
+
+ # Don't update repo if local changes exist
+ # Don't use buggy "git diff --quiet"
+ # ``errexit`` requires us to trap the exit code when the repo is changed
+ local update_requirements=$(cd $project_dir && git diff --exit-code >/dev/null || echo "changed")
+
+ if [[ $update_requirements != "changed" ]]; then
+ if [[ "$REQUIREMENTS_MODE" == "soft" ]]; then
+ if is_in_projects_txt $project_dir; then
+ (cd $REQUIREMENTS_DIR; \
+ python update.py $project_dir)
+ else
+ # soft update projects not found in requirements project.txt
+ (cd $REQUIREMENTS_DIR; \
+ python update.py -s $project_dir)
+ fi
+ else
+ (cd $REQUIREMENTS_DIR; \
+ python update.py $project_dir)
+ fi
+ fi
+
+ setup_package $project_dir $flags
+
+ # We've just gone and possibly modified the user's source tree in an
+ # automated way, which is considered bad form if it's a development
+ # tree because we've screwed up their next git checkin. So undo it.
+ #
+ # However... there are some circumstances, like running in the gate
+ # where we really really want the overridden version to stick. So provide
+ # a variable that tells us whether or not we should UNDO the requirements
+ # changes (this will be set to False in the OpenStack ci gate)
+ if [ $UNDO_REQUIREMENTS = "True" ]; then
+ if [[ $update_requirements != "changed" ]]; then
+ (cd $project_dir && git reset --hard)
+ fi
+ fi
+}
+
+# ``pip install -e`` the package, which processes the dependencies
+# using pip before running `setup.py develop`
+# Uses globals ``STACK_USER``
+# setup_develop_no_requirements_update directory
+function setup_package {
+ local project_dir=$1
+ local flags=$2
+
+ pip_install $flags $project_dir
+ # ensure that further actions can do things like setup.py sdist
+ if [[ "$flags" == "-e" ]]; then
+ safe_chown -R $STACK_USER $1/*.egg-info
+ fi
+}
+
+
+# Restore xtrace
+$INC_PY_TRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/lib/apache b/lib/apache
index 2c43681..c7d69f2 100644
--- a/lib/apache
+++ b/lib/apache
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/apache
# Functions to control configuration and operation of apache web server
diff --git a/lib/baremetal b/lib/baremetal
deleted file mode 100644
index af90c06..0000000
--- a/lib/baremetal
+++ /dev/null
@@ -1,439 +0,0 @@
-## vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-## Copyright (c) 2012 Hewlett-Packard Development Company, L.P.
-## 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.
-
-
-# This file provides devstack with the environment and utilities to
-# control nova-compute's baremetal driver.
-# It sets reasonable defaults to run within a single host,
-# using virtual machines in place of physical hardware.
-# However, by changing just a few options, devstack+baremetal can in fact
-# control physical hardware resources on the same network, if you know
-# the MAC address(es) and IPMI credentials.
-#
-# At a minimum, to enable the baremetal driver, you must set these in localrc:
-#
-# VIRT_DRIVER=baremetal
-# ENABLED_SERVICES="$ENABLED_SERVICES,baremetal"
-#
-#
-# We utilize diskimage-builder to create a ramdisk, and then
-# baremetal driver uses that to push a disk image onto the node(s).
-#
-# Below we define various defaults which control the behavior of the
-# baremetal compute service, and inform it of the hardware it will control.
-#
-# Below that, various functions are defined, which are called by devstack
-# in the following order:
-#
-# before nova-cpu starts:
-#
-# - prepare_baremetal_toolchain
-# - configure_baremetal_nova_dirs
-#
-# after nova and glance have started:
-#
-# - build_and_upload_baremetal_deploy_k_and_r $token
-# - create_baremetal_flavor $BM_DEPLOY_KERNEL_ID $BM_DEPLOY_RAMDISK_ID
-# - upload_baremetal_image $url $token
-# - add_baremetal_node <first_mac> <second_mac>
-
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Sub-driver settings
-# -------------------
-
-# sub-driver to use for kernel deployment
-#
-# - nova.virt.baremetal.pxe.PXE
-# - nova.virt.baremetal.tilera.TILERA
-BM_DRIVER=${BM_DRIVER:-nova.virt.baremetal.pxe.PXE}
-
-# sub-driver to use for remote power management
-#
-# - nova.virt.baremetal.fake.FakePowerManager, for manual power control
-# - nova.virt.baremetal.ipmi.IPMI, for remote IPMI
-# - nova.virt.baremetal.tilera_pdu.Pdu, for TilePro hardware
-BM_POWER_MANAGER=${BM_POWER_MANAGER:-nova.virt.baremetal.fake.FakePowerManager}
-
-
-# These should be customized to your environment and hardware
-# -----------------------------------------------------------
-
-# To provide PXE, configure nova-network's dnsmasq rather than run the one
-# dedicated to baremetal. When enable this, make sure these conditions are
-# fulfilled:
-#
-# 1) nova-compute and nova-network runs on the same host
-# 2) nova-network uses FlatDHCPManager
-#
-# NOTE: the other BM_DNSMASQ_* have no effect on the behavior if this option
-# is enabled.
-BM_DNSMASQ_FROM_NOVA_NETWORK=`trueorfalse False $BM_DNSMASQ_FROM_NOVA_NETWORK`
-
-# BM_DNSMASQ_IFACE should match FLAT_NETWORK_BRIDGE
-BM_DNSMASQ_IFACE=${BM_DNSMASQ_IFACE:-eth0}
-# if testing on a physical network,
-# BM_DNSMASQ_RANGE must be changed to suit your network
-BM_DNSMASQ_RANGE=${BM_DNSMASQ_RANGE:-}
-
-# BM_DNSMASQ_DNS provide dns server to bootstrap clients
-BM_DNSMASQ_DNS=${BM_DNSMASQ_DNS:-}
-
-# BM_FIRST_MAC *must* be set to the MAC address of the node you will
-# boot. This is passed to dnsmasq along with the kernel/ramdisk to
-# deploy via PXE.
-BM_FIRST_MAC=${BM_FIRST_MAC:-}
-
-# BM_SECOND_MAC is only important if the host has >1 NIC.
-BM_SECOND_MAC=${BM_SECOND_MAC:-}
-
-# Hostname for the baremetal nova-compute node, if not run on this host
-BM_HOSTNAME=${BM_HOSTNAME:-$(hostname -f)}
-
-# BM_PM_* options are only necessary if BM_POWER_MANAGER=...IPMI
-BM_PM_ADDR=${BM_PM_ADDR:-0.0.0.0}
-BM_PM_USER=${BM_PM_USER:-user}
-BM_PM_PASS=${BM_PM_PASS:-pass}
-
-# BM_FLAVOR_* options are arbitrary and not necessarily related to
-# physical hardware capacity. These can be changed if you are testing
-# BaremetalHostManager with multiple nodes and different flavors.
-BM_CPU_ARCH=${BM_CPU_ARCH:-x86_64}
-BM_FLAVOR_CPU=${BM_FLAVOR_CPU:-1}
-BM_FLAVOR_RAM=${BM_FLAVOR_RAM:-1024}
-BM_FLAVOR_ROOT_DISK=${BM_FLAVOR_ROOT_DISK:-10}
-BM_FLAVOR_EPHEMERAL_DISK=${BM_FLAVOR_EPHEMERAL_DISK:-0}
-BM_FLAVOR_SWAP=${BM_FLAVOR_SWAP:-1}
-BM_FLAVOR_NAME=${BM_FLAVOR_NAME:-bm.small}
-BM_FLAVOR_ID=${BM_FLAVOR_ID:-11}
-BM_FLAVOR_ARCH=${BM_FLAVOR_ARCH:-$BM_CPU_ARCH}
-
-
-# Use DIB to create deploy ramdisk and kernel.
-BM_BUILD_DEPLOY_RAMDISK=`trueorfalse True $BM_BUILD_DEPLOY_RAMDISK`
-# If not use DIB, these files are used as deploy ramdisk/kernel.
-# (The value must be a relative path from $TOP_DIR/files/)
-BM_DEPLOY_RAMDISK=${BM_DEPLOY_RAMDISK:-}
-BM_DEPLOY_KERNEL=${BM_DEPLOY_KERNEL:-}
-
-# If you need to add any extra flavors to the deploy ramdisk image
-# eg, specific network drivers, specify them here
-#
-# NOTE(deva): this will be moved to lib/ironic in a future patch
-# for now, set the default to a suitable value for Ironic's needs
-BM_DEPLOY_FLAVOR=${BM_DEPLOY_FLAVOR:--a amd64 ubuntu deploy-ironic}
-
-# set URL and version for google shell-in-a-box
-BM_SHELL_IN_A_BOX=${BM_SHELL_IN_A_BOX:-http://shellinabox.googlecode.com/files/shellinabox-2.14.tar.gz}
-
-
-# Functions
-# ---------
-
-# Check if baremetal is properly enabled
-# Returns false if VIRT_DRIVER is not baremetal, or if ENABLED_SERVICES
-# does not contain "baremetal"
-function is_baremetal {
- if [[ "$ENABLED_SERVICES" =~ 'baremetal' && "$VIRT_DRIVER" = 'baremetal' ]]; then
- return 0
- fi
- return 1
-}
-
-# Install diskimage-builder and shell-in-a-box
-# so that we can build the deployment kernel & ramdisk
-function prepare_baremetal_toolchain {
- if [[ $(type -P ramdisk-image-create) == "" ]]; then
- pip_install diskimage_builder
- fi
- local shellinabox_basename=$(basename $BM_SHELL_IN_A_BOX)
- if [[ ! -e $DEST/$shellinabox_basename ]]; then
- cd $DEST
- wget $BM_SHELL_IN_A_BOX
- fi
- if [[ ! -d $DEST/${shellinabox_basename%%.tar.gz} ]]; then
- cd $DEST
- tar xzf $shellinabox_basename
- fi
- if [[ ! $(which shellinaboxd) ]]; then
- cd $DEST/${shellinabox_basename%%.tar.gz}
- ./configure
- make
- sudo make install
- fi
-}
-
-# prepare various directories needed by baremetal hypervisor
-function configure_baremetal_nova_dirs {
- # ensure /tftpboot is prepared
- sudo mkdir -p /tftpboot
- sudo mkdir -p /tftpboot/pxelinux.cfg
-
- PXEBIN=/usr/share/syslinux/pxelinux.0
- if [ ! -f $PXEBIN ]; then
- PXEBIN=/usr/lib/syslinux/pxelinux.0
- if [ ! -f $PXEBIN ]; then
- die $LINENO "pxelinux.0 (from SYSLINUX) not found."
- fi
- fi
-
- sudo cp $PXEBIN /tftpboot/
- sudo chown -R $STACK_USER:$LIBVIRT_GROUP /tftpboot
-
- # ensure $NOVA_STATE_PATH/baremetal is prepared
- sudo mkdir -p $NOVA_STATE_PATH/baremetal
- sudo mkdir -p $NOVA_STATE_PATH/baremetal/console
- sudo mkdir -p $NOVA_STATE_PATH/baremetal/dnsmasq
- sudo touch $NOVA_STATE_PATH/baremetal/dnsmasq/dnsmasq-dhcp.host
- sudo chown -R $STACK_USER $NOVA_STATE_PATH/baremetal
-
- # ensure dnsmasq is installed but not running
- # because baremetal driver will reconfigure and restart this as needed
- is_package_installed dnsmasq || install_package dnsmasq
- stop_service dnsmasq
-}
-
-# build deploy kernel+ramdisk, then upload them to glance
-# this function sets BM_DEPLOY_KERNEL_ID and BM_DEPLOY_RAMDISK_ID
-function upload_baremetal_deploy {
- token=$1
-
- if [ "$BM_BUILD_DEPLOY_RAMDISK" = "True" ]; then
- BM_DEPLOY_KERNEL=bm-deploy.kernel
- BM_DEPLOY_RAMDISK=bm-deploy.initramfs
- if [ ! -e "$TOP_DIR/files/$BM_DEPLOY_KERNEL" -o ! -e "$TOP_DIR/files/$BM_DEPLOY_RAMDISK" ]; then
- ramdisk-image-create $BM_DEPLOY_FLAVOR \
- -o $TOP_DIR/files/bm-deploy
- fi
- fi
-
- # load them into glance
- BM_DEPLOY_KERNEL_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- $BM_DEPLOY_KERNEL \
- --public --disk-format=aki \
- --container-format=aki \
- < $TOP_DIR/files/$BM_DEPLOY_KERNEL | grep ' id ' | get_field 2)
- BM_DEPLOY_RAMDISK_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- $BM_DEPLOY_RAMDISK \
- --public --disk-format=ari \
- --container-format=ari \
- < $TOP_DIR/files/$BM_DEPLOY_RAMDISK | grep ' id ' | get_field 2)
-}
-
-# create a basic baremetal flavor, associated with deploy kernel & ramdisk
-#
-# Usage: create_baremetal_flavor <aki_uuid> <ari_uuid>
-function create_baremetal_flavor {
- aki=$1
- ari=$2
- nova flavor-create $BM_FLAVOR_NAME $BM_FLAVOR_ID \
- $BM_FLAVOR_RAM $BM_FLAVOR_ROOT_DISK $BM_FLAVOR_CPU
- nova flavor-key $BM_FLAVOR_NAME set \
- "cpu_arch"="$BM_FLAVOR_ARCH" \
- "baremetal:deploy_kernel_id"="$aki" \
- "baremetal:deploy_ramdisk_id"="$ari"
-
-}
-
-# Pull run-time kernel/ramdisk out of disk image and load into glance.
-# Note that $file is currently expected to be in qcow2 format.
-# Sets KERNEL_ID and RAMDISK_ID
-#
-# Usage: extract_and_upload_k_and_r_from_image $token $file
-function extract_and_upload_k_and_r_from_image {
- token=$1
- file=$2
- image_name=$(basename "$file" ".qcow2")
-
- # this call returns the file names as "$kernel,$ramdisk"
- out=$(disk-image-get-kernel \
- -x -d $TOP_DIR/files -o bm-deploy -i $file)
- if [ $? -ne 0 ]; then
- die $LINENO "Failed to get kernel and ramdisk from $file"
- fi
- XTRACE=$(set +o | grep xtrace)
- set +o xtrace
- out=$(echo "$out" | tail -1)
- $XTRACE
- OUT_KERNEL=${out%%,*}
- OUT_RAMDISK=${out##*,}
-
- # load them into glance
- KERNEL_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- $image_name-kernel \
- --public --disk-format=aki \
- --container-format=aki \
- < $TOP_DIR/files/$OUT_KERNEL | grep ' id ' | get_field 2)
- RAMDISK_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- $image_name-initrd \
- --public --disk-format=ari \
- --container-format=ari \
- < $TOP_DIR/files/$OUT_RAMDISK | grep ' id ' | get_field 2)
-}
-
-
-# Re-implementation of devstack's "upload_image" function
-#
-# Takes the same parameters, but has some peculiarities which made it
-# easier to create a separate method, rather than complicate the logic
-# of the existing function.
-function upload_baremetal_image {
- local image_url=$1
- local token=$2
-
- # Create a directory for the downloaded image tarballs.
- mkdir -p $FILES/images
-
- # Downloads the image (uec ami+aki style), then extracts it.
- IMAGE_FNAME=`basename "$image_url"`
- if [[ ! -f $FILES/$IMAGE_FNAME || \
- "$(stat -c "%s" $FILES/$IMAGE_FNAME)" = "0" ]]; then
- wget -c $image_url -O $FILES/$IMAGE_FNAME
- if [[ $? -ne 0 ]]; then
- echo "Not found: $image_url"
- return
- fi
- fi
-
- local KERNEL=""
- local RAMDISK=""
- local DISK_FORMAT=""
- local CONTAINER_FORMAT=""
- case "$IMAGE_FNAME" in
- *.tar.gz|*.tgz)
- # Extract ami and aki files
- [ "${IMAGE_FNAME%.tar.gz}" != "$IMAGE_FNAME" ] &&
- IMAGE_NAME="${IMAGE_FNAME%.tar.gz}" ||
- IMAGE_NAME="${IMAGE_FNAME%.tgz}"
- xdir="$FILES/images/$IMAGE_NAME"
- rm -Rf "$xdir";
- mkdir "$xdir"
- tar -zxf $FILES/$IMAGE_FNAME -C "$xdir"
- KERNEL=$(for f in "$xdir/"*-vmlinuz* "$xdir/"aki-*/image; do
- [ -f "$f" ] && echo "$f" && break; done; true)
- RAMDISK=$(for f in "$xdir/"*-initrd* "$xdir/"ari-*/image; do
- [ -f "$f" ] && echo "$f" && break; done; true)
- IMAGE=$(for f in "$xdir/"*.img "$xdir/"ami-*/image; do
- [ -f "$f" ] && echo "$f" && break; done; true)
- if [[ -z "$IMAGE_NAME" ]]; then
- IMAGE_NAME=$(basename "$IMAGE" ".img")
- fi
- DISK_FORMAT=ami
- CONTAINER_FORMAT=ami
- ;;
- *.qcow2)
- IMAGE="$FILES/${IMAGE_FNAME}"
- IMAGE_NAME=$(basename "$IMAGE" ".qcow2")
- DISK_FORMAT=qcow2
- CONTAINER_FORMAT=bare
- ;;
- *) echo "Do not know what to do with $IMAGE_FNAME"; false;;
- esac
-
- if [ "$CONTAINER_FORMAT" = "bare" ]; then
- extract_and_upload_k_and_r_from_image $token $IMAGE
- elif [ "$CONTAINER_FORMAT" = "ami" ]; then
- KERNEL_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- "$IMAGE_NAME-kernel" --public \
- --container-format aki \
- --disk-format aki < "$KERNEL" | grep ' id ' | get_field 2)
- RAMDISK_ID=$(openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- "$IMAGE_NAME-ramdisk" --public \
- --container-format ari \
- --disk-format ari < "$RAMDISK" | grep ' id ' | get_field 2)
- else
- # TODO(deva): add support for other image types
- return
- fi
-
- openstack \
- --os-token $token \
- --os-url http://$GLANCE_HOSTPORT \
- image create \
- "${IMAGE_NAME%.img}" --public \
- --container-format $CONTAINER_FORMAT \
- --disk-format $DISK_FORMAT \
- ${KERNEL_ID:+--property kernel_id=$KERNEL_ID} \
- ${RAMDISK_ID:+--property ramdisk_id=$RAMDISK_ID} < "${IMAGE}"
-
- # override DEFAULT_IMAGE_NAME so that tempest can find the image
- # that we just uploaded in glance
- DEFAULT_IMAGE_NAME="${IMAGE_NAME%.img}"
-}
-
-function clear_baremetal_of_all_nodes {
- list=$(nova baremetal-node-list | awk -F '| ' 'NR>3 {print $2}' )
- for node in $list; do
- nova baremetal-node-delete $node
- done
-}
-
-# Inform nova-baremetal about nodes, MACs, etc.
-# Defaults to using BM_FIRST_MAC and BM_SECOND_MAC if parameters not specified
-#
-# Usage: add_baremetal_node <first_mac> <second_mac>
-function add_baremetal_node {
- mac_1=${1:-$BM_FIRST_MAC}
- mac_2=${2:-$BM_SECOND_MAC}
-
- id=$(nova baremetal-node-create \
- --pm_address="$BM_PM_ADDR" \
- --pm_user="$BM_PM_USER" \
- --pm_password="$BM_PM_PASS" \
- "$BM_HOSTNAME" \
- "$BM_FLAVOR_CPU" \
- "$BM_FLAVOR_RAM" \
- "$BM_FLAVOR_ROOT_DISK" \
- "$mac_1" \
- | grep ' id ' | get_field 2 )
- [ $? -eq 0 ] || [ "$id" ] || die $LINENO "Error adding baremetal node"
- if [ -n "$mac_2" ]; then
- id2=$(nova baremetal-interface-add "$id" "$mac_2" )
- [ $? -eq 0 ] || [ "$id2" ] || die $LINENO "Error adding interface to barmetal node $id"
- fi
-}
-
-
-# Restore xtrace
-$XTRACE
-
-# Tell emacs to use shell-script-mode
-## Local variables:
-## mode: shell-script
-## End:
diff --git a/lib/ceilometer b/lib/ceilometer
index 483cd27..698e8b0 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -1,13 +1,34 @@
+#!/bin/bash
+#
# lib/ceilometer
# Install and start **Ceilometer** service
-# To enable a minimal set of Ceilometer services, add the following to localrc:
+# To enable a minimal set of Ceilometer services, add the following to the
+# localrc section of local.conf:
#
# enable_service ceilometer-acompute ceilometer-acentral ceilometer-anotification ceilometer-collector ceilometer-api
#
-# To ensure Ceilometer alarming services are enabled also, further add to the localrc:
+# To ensure Ceilometer alarming services are enabled also, further add to the
+# localrc section of local.conf:
#
# enable_service ceilometer-alarm-notifier ceilometer-alarm-evaluator
+#
+# To ensure events are stored, add the following section to local.conf:
+#
+# [[post-config|$CEILOMETER_CONF]]
+# [notification]
+# store_events=True
+#
+# Several variables set in the localrc section adjust common behaviors
+# of Ceilometer (see within for additional settings):
+#
+# CEILOMETER_USE_MOD_WSGI: When True, run the api under mod_wsgi.
+# CEILOMETER_PIPELINE_INTERVAL: The number of seconds between pipeline processing
+# runs. Default 600.
+# CEILOMETER_BACKEND: The database backend (e.g. 'mysql', 'mongodb')
+# CEILOMETER_COORDINATION_URL: The URL for a group membership service provided
+# by tooz.
+
# Dependencies:
#
@@ -35,8 +56,9 @@
# --------
# Set up default directories
+GITDIR["python-ceilometerclient"]=$DEST/python-ceilometerclient
+
CEILOMETER_DIR=$DEST/ceilometer
-CEILOMETERCLIENT_DIR=$DEST/python-ceilometerclient
CEILOMETER_CONF_DIR=/etc/ceilometer
CEILOMETER_CONF=$CEILOMETER_CONF_DIR/ceilometer.conf
CEILOMETER_API_LOG_DIR=/var/log/ceilometer-api
@@ -53,11 +75,14 @@
CEILOMETER_SERVICE_PROTOCOL=http
CEILOMETER_SERVICE_HOST=$SERVICE_HOST
CEILOMETER_SERVICE_PORT=${CEILOMETER_SERVICE_PORT:-8777}
-CEILOMETER_USE_MOD_WSGI=$(trueorfalse False $CEILOMETER_USE_MOD_WSGI)
+CEILOMETER_USE_MOD_WSGI=$(trueorfalse False CEILOMETER_USE_MOD_WSGI)
# To enable OSprofiler change value of this variable to "notifications,profiler"
CEILOMETER_NOTIFICATION_TOPICS=${CEILOMETER_NOTIFICATION_TOPICS:-notifications}
+CEILOMETER_COORDINATION_URL=${CEILOMETER_COORDINATION_URL:-}
+CEILOMETER_PIPELINE_INTERVAL=${CEILOMETER_PIPELINE_INTERVAL:-}
+
# Tell Tempest this project is present
TEMPEST_SERVICES+=,ceilometer
@@ -80,14 +105,10 @@
# SERVICE_TENANT_NAME ceilometer ResellerAdmin (if Swift is enabled)
function create_ceilometer_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
# Ceilometer
if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
- local ceilometer_user=$(get_or_create_user "ceilometer" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $ceilometer_user $service_tenant
+
+ create_service_user "ceilometer"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
local ceilometer_service=$(get_or_create_service "ceilometer" \
@@ -100,7 +121,7 @@
fi
if is_service_enabled swift; then
# Ceilometer needs ResellerAdmin role to access swift account stats.
- get_or_add_user_role "ResellerAdmin" "ceilometer" $SERVICE_TENANT_NAME
+ get_or_add_user_project_role "ResellerAdmin" "ceilometer" $SERVICE_TENANT_NAME
fi
fi
}
@@ -165,6 +186,7 @@
iniset $CEILOMETER_CONF DEFAULT policy_file $CEILOMETER_CONF_DIR/policy.json
cp $CEILOMETER_DIR/etc/ceilometer/pipeline.yaml $CEILOMETER_CONF_DIR
+ cp $CEILOMETER_DIR/etc/ceilometer/event_pipeline.yaml $CEILOMETER_CONF_DIR
cp $CEILOMETER_DIR/etc/ceilometer/api_paste.ini $CEILOMETER_CONF_DIR
cp $CEILOMETER_DIR/etc/ceilometer/event_definitions.yaml $CEILOMETER_CONF_DIR
@@ -178,14 +200,20 @@
iniset $CEILOMETER_CONF service_credentials os_username ceilometer
iniset $CEILOMETER_CONF service_credentials os_password $SERVICE_PASSWORD
iniset $CEILOMETER_CONF service_credentials os_tenant_name $SERVICE_TENANT_NAME
+ iniset $CEILOMETER_CONF service_credentials os_region_name $REGION_NAME
+ iniset $CEILOMETER_CONF service_credentials os_auth_url $KEYSTONE_SERVICE_URI/v2.0
configure_auth_token_middleware $CEILOMETER_CONF ceilometer $CEILOMETER_AUTH_CACHE_DIR
if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] ; then
- iniset $CEILOMETER_CONF database connection $(database_connection_url ceilometer)
+ iniset $CEILOMETER_CONF database alarm_connection $(database_connection_url ceilometer)
+ iniset $CEILOMETER_CONF database event_connection $(database_connection_url ceilometer)
+ iniset $CEILOMETER_CONF database metering_connection $(database_connection_url ceilometer)
iniset $CEILOMETER_CONF DEFAULT collector_workers $API_WORKERS
else
- iniset $CEILOMETER_CONF database connection mongodb://localhost:27017/ceilometer
+ iniset $CEILOMETER_CONF database alarm_connection mongodb://localhost:27017/ceilometer
+ iniset $CEILOMETER_CONF database event_connection mongodb://localhost:27017/ceilometer
+ iniset $CEILOMETER_CONF database metering_connection mongodb://localhost:27017/ceilometer
configure_mongodb
cleanup_ceilometer
fi
@@ -236,7 +264,7 @@
if is_service_enabled mysql postgresql; then
if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] ; then
- recreate_database ceilometer utf8
+ recreate_database ceilometer
$CEILOMETER_BIN_DIR/ceilometer-dbsync
fi
fi
@@ -246,12 +274,12 @@
function install_redis {
if is_ubuntu; then
install_package redis-server
+ restart_service redis-server
else
# This will fail (correctly) where a redis package is unavailable
install_package redis
+ restart_service redis
fi
-
- restart_service redis
}
# install_ceilometer() - Collect source and prepare
@@ -268,9 +296,11 @@
# install_ceilometerclient() - Collect source and prepare
function install_ceilometerclient {
- git_clone $CEILOMETERCLIENT_REPO $CEILOMETERCLIENT_DIR $CEILOMETERCLIENT_BRANCH
- setup_develop $CEILOMETERCLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$CEILOMETERCLIENT_DIR/tools/,/etc/bash_completion.d/}ceilometer.bash_completion
+ if use_library_from_git "python-ceilometerclient"; then
+ git_clone_by_name "python-ceilometerclient"
+ setup_dev_lib "python-ceilometerclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-ceilometerclient"]}/tools/,/etc/bash_completion.d/}ceilometer.bash_completion
+ fi
}
# start_ceilometer() - Start running processes, including screen
diff --git a/lib/ceph b/lib/ceph
index e55738c..a6b8cc8 100644
--- a/lib/ceph
+++ b/lib/ceph
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/ceph
# Functions to control the configuration and operation of the **Ceph** storage service
@@ -68,9 +70,19 @@
CEPH_REPLICAS=${CEPH_REPLICAS:-1}
CEPH_REPLICAS_SEQ=$(seq ${CEPH_REPLICAS})
+# Connect to an existing Ceph cluster
+REMOTE_CEPH=$(trueorfalse False $REMOTE_CEPH)
+REMOTE_CEPH_ADMIN_KEY_PATH=${REMOTE_CEPH_ADMIN_KEY_PATH:-$CEPH_CONF_DIR/ceph.client.admin.keyring}
+
+
# Functions
# ------------
+function get_ceph_version {
+ local ceph_version_str=$(sudo ceph daemon mon.$(hostname) version | cut -d '"' -f 4 | cut -f 1,2 -d '.')
+ echo $ceph_version_str
+}
+
# import_libvirt_secret_ceph() - Imports Cinder user key into libvirt
# so it can connect to the Ceph cluster while attaching a Cinder block device
function import_libvirt_secret_ceph {
@@ -87,24 +99,69 @@
sudo rm -f secret.xml
}
+# undefine_virsh_secret() - Undefine Cinder key secret from libvirt
+function undefine_virsh_secret {
+ if is_service_enabled cinder || is_service_enabled nova; then
+ local virsh_uuid=$(sudo virsh secret-list | awk '/^ ?[0-9a-z]/ { print $1 }')
+ sudo virsh secret-undefine ${virsh_uuid} >/dev/null 2>&1
+ fi
+}
+
+
+# check_os_support_ceph() - Check if the operating system provides a decent version of Ceph
+function check_os_support_ceph {
+ if [[ ! ${DISTRO} =~ (trusty|f20|f21) ]]; then
+ echo "WARNING: your distro $DISTRO does not provide (at least) the Firefly release. Please use Ubuntu Trusty or Fedora 20 (and higher)"
+ if [[ "$FORCE_CEPH_INSTALL" != "yes" ]]; then
+ die $LINENO "If you wish to install Ceph on this distribution anyway run with FORCE_CEPH_INSTALL=yes"
+ fi
+ NO_UPDATE_REPOS=False
+ fi
+}
+
# cleanup_ceph() - Remove residual data files, anything left over from previous
# runs that a clean run would need to clean up
-function cleanup_ceph {
- sudo pkill -f ceph-mon
- sudo pkill -f ceph-osd
+function cleanup_ceph_remote {
+ # do a proper cleanup from here to avoid leftover on the remote Ceph cluster
+ if is_service_enabled glance; then
+ sudo ceph osd pool delete $GLANCE_CEPH_POOL $GLANCE_CEPH_POOL --yes-i-really-really-mean-it > /dev/null 2>&1
+ sudo ceph auth del client.$GLANCE_CEPH_USER > /dev/null 2>&1
+ fi
+ if is_service_enabled cinder; then
+ sudo ceph osd pool delete $CINDER_CEPH_POOL $CINDER_CEPH_POOL --yes-i-really-really-mean-it > /dev/null 2>&1
+ sudo ceph auth del client.$CINDER_CEPH_USER > /dev/null 2>&1
+ fi
+ if is_service_enabled c-bak; then
+ sudo ceph osd pool delete $CINDER_BAK_CEPH_POOL $CINDER_BAK_CEPH_POOL --yes-i-really-really-mean-it > /dev/null 2>&1
+ sudo ceph auth del client.$CINDER_BAK_CEPH_USER > /dev/null 2>&1
+ fi
+ if is_service_enabled nova; then
+ iniset $NOVA_CONF libvirt rbd_secret_uuid ""
+ sudo ceph osd pool delete $NOVA_CEPH_POOL $NOVA_CEPH_POOL --yes-i-really-really-mean-it > /dev/null 2>&1
+ fi
+}
+
+function cleanup_ceph_embedded {
+ sudo killall -w -9 ceph-mon
+ sudo killall -w -9 ceph-osd
sudo rm -rf ${CEPH_DATA_DIR}/*/*
- sudo rm -rf ${CEPH_CONF_DIR}/*
if egrep -q ${CEPH_DATA_DIR} /proc/mounts; then
sudo umount ${CEPH_DATA_DIR}
fi
if [[ -e ${CEPH_DISK_IMAGE} ]]; then
sudo rm -f ${CEPH_DISK_IMAGE}
fi
- uninstall_package ceph ceph-common python-ceph libcephfs1 > /dev/null 2>&1
- VIRSH_UUID=$(sudo virsh secret-list | awk '/^ ?[0-9a-z]/ { print $1 }')
- sudo virsh secret-undefine ${VIRSH_UUID} >/dev/null 2>&1
}
+function cleanup_ceph_general {
+ undefine_virsh_secret
+ uninstall_package ceph ceph-common python-ceph libcephfs1 > /dev/null 2>&1
+
+ # purge ceph config file and keys
+ sudo rm -rf ${CEPH_CONF_DIR}/*
+}
+
+
# configure_ceph() - Set config files, create data dirs, etc
function configure_ceph {
local count=0
@@ -120,7 +177,7 @@
sudo mkdir /var/lib/ceph/mon/ceph-$(hostname)
# create a default ceph configuration file
- sudo tee -a ${CEPH_CONF_FILE} > /dev/null <<EOF
+ sudo tee ${CEPH_CONF_FILE} > /dev/null <<EOF
[global]
fsid = ${CEPH_FSID}
mon_initial_members = $(hostname)
@@ -154,10 +211,16 @@
sleep 5
done
+ # pools data and metadata were removed in the Giant release so depending on the version we apply different commands
+ local ceph_version=$(get_ceph_version)
# change pool replica size according to the CEPH_REPLICAS set by the user
- sudo ceph -c ${CEPH_CONF_FILE} osd pool set data size ${CEPH_REPLICAS}
- sudo ceph -c ${CEPH_CONF_FILE} osd pool set rbd size ${CEPH_REPLICAS}
- sudo ceph -c ${CEPH_CONF_FILE} osd pool set metadata size ${CEPH_REPLICAS}
+ if [[ ${ceph_version%%.*} -eq 0 ]] && [[ ${ceph_version##*.} -lt 87 ]]; then
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set rbd size ${CEPH_REPLICAS}
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set data size ${CEPH_REPLICAS}
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set metadata size ${CEPH_REPLICAS}
+ else
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set rbd size ${CEPH_REPLICAS}
+ fi
# create a simple rule to take OSDs instead of host with CRUSH
# then apply this rules to the default pool
@@ -187,14 +250,17 @@
done
}
-# configure_ceph_glance() - Glance config needs to come after Glance is set up
-function configure_ceph_glance {
+function configure_ceph_embedded_glance {
# configure Glance service options, ceph pool, ceph user and ceph key
- sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${GLANCE_CEPH_POOL} ${GLANCE_CEPH_POOL_PG} ${GLANCE_CEPH_POOL_PGP}
sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${GLANCE_CEPH_POOL} size ${CEPH_REPLICAS}
if [[ $CEPH_REPLICAS -ne 1 ]]; then
sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${GLANCE_CEPH_POOL} crush_ruleset ${RULE_ID}
fi
+}
+
+# configure_ceph_glance() - Glance config needs to come after Glance is set up
+function configure_ceph_glance {
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${GLANCE_CEPH_POOL} ${GLANCE_CEPH_POOL_PG} ${GLANCE_CEPH_POOL_PGP}
sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${GLANCE_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
@@ -209,14 +275,17 @@
iniset $GLANCE_API_CONF glance_store rbd_store_pool $GLANCE_CEPH_POOL
}
-# configure_ceph_nova() - Nova config needs to come after Nova is set up
-function configure_ceph_nova {
+function configure_ceph_embedded_nova {
# configure Nova service options, ceph pool, ceph user and ceph key
- sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${NOVA_CEPH_POOL} ${NOVA_CEPH_POOL_PG} ${NOVA_CEPH_POOL_PGP}
sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${NOVA_CEPH_POOL} size ${CEPH_REPLICAS}
if [[ $CEPH_REPLICAS -ne 1 ]]; then
sudo -c ${CEPH_CONF_FILE} ceph osd pool set ${NOVA_CEPH_POOL} crush_ruleset ${RULE_ID}
fi
+}
+
+# configure_ceph_nova() - Nova config needs to come after Nova is set up
+function configure_ceph_nova {
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${NOVA_CEPH_POOL} ${NOVA_CEPH_POOL_PG} ${NOVA_CEPH_POOL_PGP}
iniset $NOVA_CONF libvirt rbd_user ${CINDER_CEPH_USER}
iniset $NOVA_CONF libvirt rbd_secret_uuid ${CINDER_CEPH_UUID}
iniset $NOVA_CONF libvirt inject_key false
@@ -232,15 +301,17 @@
fi
}
-# configure_ceph_cinder() - Cinder config needs to come after Cinder is set up
-function configure_ceph_cinder {
+function configure_ceph_embedded_cinder {
# Configure Cinder service options, ceph pool, ceph user and ceph key
- sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${CINDER_CEPH_POOL} ${CINDER_CEPH_POOL_PG} ${CINDER_CEPH_POOL_PGP}
sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_CEPH_POOL} size ${CEPH_REPLICAS}
if [[ $CEPH_REPLICAS -ne 1 ]]; then
sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_CEPH_POOL} crush_ruleset ${RULE_ID}
-
fi
+}
+
+# configure_ceph_cinder() - Cinder config needs to come after Cinder is set up
+function configure_ceph_cinder {
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${CINDER_CEPH_POOL} ${CINDER_CEPH_POOL_PG} ${CINDER_CEPH_POOL_PGP}
sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_CEPH_POOL}, allow rwx pool=${NOVA_CEPH_POOL},allow rx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${CINDER_CEPH_USER}.keyring
}
@@ -254,15 +325,12 @@
}
# install_ceph() - Collect source and prepare
+function install_ceph_remote {
+ install_package ceph-common
+}
+
function install_ceph {
- # NOTE(dtroyer): At some point it'll be easier to test for unsupported distros,
- # leveraging the list in stack.sh
- if [[ ${os_CODENAME} =~ trusty ]] || [[ ${os_CODENAME} =~ Schrödinger’sCat ]] || [[ ${os_CODENAME} =~ Heisenbug ]]; then
- NO_UPDATE_REPOS=False
- install_package ceph
- else
- exit_distro_not_supported "Ceph since your distro doesn't provide (at least) the Firefly release. Please use Ubuntu Trusty or Fedora 19/20"
- fi
+ install_package ceph
}
# start_ceph() - Start running processes, including screen
diff --git a/lib/cinder b/lib/cinder
index 29cda42..0d157dd 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder
# Install and start **Cinder** volume service
@@ -36,8 +38,9 @@
fi
# set up default directories
+GITDIR["python-cinderclient"]=$DEST/python-cinderclient
+
CINDER_DIR=$DEST/cinder
-CINDERCLIENT_DIR=$DEST/python-cinderclient
CINDER_STATE_PATH=${CINDER_STATE_PATH:=$DATA_DIR/cinder}
CINDER_AUTH_CACHE_DIR=${CINDER_AUTH_CACHE_DIR:-/var/cache/cinder}
@@ -62,27 +65,20 @@
fi
-# Maintain this here for backward-compatibility with the old configuration
-# DEPRECATED: Use CINDER_ENABLED_BACKENDS instead
-# Support for multi lvm backend configuration (default is no support)
-CINDER_MULTI_LVM_BACKEND=$(trueorfalse False $CINDER_MULTI_LVM_BACKEND)
-
# Default backends
# The backend format is type:name where type is one of the supported backend
# types (lvm, nfs, etc) and name is the identifier used in the Cinder
# configuration and for the volume type name. Multiple backends are
# comma-separated.
-if [[ $CINDER_MULTI_LVM_BACKEND == "False" ]]; then
- CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1}
-else
- CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1,lvm:lvmdriver-2}
-fi
+# The old ``CINDER_MULTI_LVM_BACKEND=True`` setting had a default of:
+# CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1,lvm:lvmdriver-2}
+CINDER_ENABLED_BACKENDS=${CINDER_ENABLED_BACKENDS:-lvm:lvmdriver-1}
# Should cinder perform secure deletion of volumes?
# Defaults to true, can be set to False to avoid this bug when testing:
# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1023755
-CINDER_SECURE_DELETE=`trueorfalse True $CINDER_SECURE_DELETE`
+CINDER_SECURE_DELETE=$(trueorfalse True CINDER_SECURE_DELETE)
# Cinder reports allocations back to the scheduler on periodic intervals
# it turns out we can get an "out of space" issue when we run tests too
@@ -107,6 +103,12 @@
done
fi
+# Change the default nova_catalog_info and nova_catalog_admin_info values in
+# cinder so that the service name cinder is searching for matches that set for
+# nova in keystone.
+CINDER_NOVA_CATALOG_INFO=${CINDER_NOVA_CATALOG_INFO:-compute:nova:publicURL}
+CINDER_NOVA_CATALOG_ADMIN_INFO=${CINDER_NOVA_CATALOG_ADMIN_INFO:-compute:nova:adminURL}
+
# Functions
# ---------
@@ -202,6 +204,8 @@
cp -p $CINDER_DIR/etc/cinder/policy.json $CINDER_CONF_DIR
+ rm -f $CINDER_CONF
+
configure_cinder_rootwrap
cp $CINDER_DIR/etc/cinder/api-paste.ini $CINDER_API_PASTE_INI
@@ -217,6 +221,9 @@
configure_auth_token_middleware $CINDER_CONF cinder $CINDER_AUTH_CACHE_DIR
+ iniset $CINDER_CONF DEFAULT nova_catalog_info $CINDER_NOVA_CATALOG_INFO
+ iniset $CINDER_CONF DEFAULT nova_catalog_admin_info $CINDER_NOVA_CATALOG_ADMIN_INFO
+
iniset $CINDER_CONF DEFAULT auth_strategy keystone
iniset $CINDER_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
iniset $CINDER_CONF DEFAULT verbose True
@@ -257,7 +264,7 @@
fi
if is_service_enabled swift; then
- iniset $CINDER_CONF DEFAULT backup_swift_url "http://$SERVICE_HOST:8080/v1/AUTH_"
+ iniset $CINDER_CONF DEFAULT backup_swift_url "$SWIFT_SERVICE_PROTOCOL://$SERVICE_HOST:8080/v1/AUTH_"
fi
if is_service_enabled ceilometer; then
@@ -289,18 +296,6 @@
configure_cinder_driver
fi
- if [[ is_fedora && $DISTRO =~ (rhel6) ]]; then
- # Cinder clones are slightly larger due to some extra
- # metadata. RHEL6 will not allow auto-extending of LV's
- # without this, leading to clones giving hard-to-track disk
- # I/O errors.
- # see https://bugzilla.redhat.com/show_bug.cgi?id=975052
- sudo sed -i~ \
- -e 's/snapshot_autoextend_threshold =.*/snapshot_autoextend_threshold = 80/' \
- -e 's/snapshot_autoextend_percent =.*/snapshot_autoextend_percent = 20/' \
- /etc/lvm/lvm.conf
- fi
-
iniset $CINDER_CONF DEFAULT osapi_volume_workers "$API_WORKERS"
iniset $CINDER_CONF DEFAULT glance_api_servers "${GLANCE_SERVICE_PROTOCOL}://${GLANCE_HOSTPORT}"
@@ -328,15 +323,10 @@
# Migrated from keystone_data.sh
function create_cinder_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
# Cinder
if [[ "$ENABLED_SERVICES" =~ "c-api" ]]; then
- local cinder_user=$(get_or_create_user "cinder" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $cinder_user $service_tenant
+ create_service_user "cinder"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -373,7 +363,7 @@
if is_service_enabled $DATABASE_BACKENDS; then
# (Re)create cinder database
- recreate_database cinder utf8
+ recreate_database cinder
# Migrate cinder database
$CINDER_BIN_DIR/cinder-manage db sync
@@ -381,15 +371,36 @@
if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
local be be_name be_type
+ local has_lvm=0
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
be_type=${be%%:*}
be_name=${be##*:}
+
+ if [[ $be_type == 'lvm' ]]; then
+ has_lvm=1
+ fi
+
if type init_cinder_backend_${be_type} >/dev/null 2>&1; then
+ # Always init the default volume group for lvm.
+ if [[ "$be_type" == "lvm" ]]; then
+ init_default_lvm_volume_group
+ fi
init_cinder_backend_${be_type} ${be_name}
fi
done
fi
+ # Keep it simple, set a marker if there's an LVM backend
+ # use the created VG's to setup lvm filters
+ if [[ $has_lvm == 1 ]]; then
+ # Order matters here, not only obviously to make
+ # sure the VG's are created, but also some distros
+ # do some customizations to lvm.conf on init, we
+ # want to make sure we copy those over
+ sudo cp /etc/lvm/lvm.conf /etc/cinder/lvm.conf
+ configure_cinder_backend_conf_lvm
+ fi
+
mkdir -p $CINDER_STATE_PATH/volumes
create_cinder_cache_dir
}
@@ -402,9 +413,11 @@
# install_cinderclient() - Collect source and prepare
function install_cinderclient {
- git_clone $CINDERCLIENT_REPO $CINDERCLIENT_DIR $CINDERCLIENT_BRANCH
- setup_develop $CINDERCLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$CINDERCLIENT_DIR/tools/,/etc/bash_completion.d/}cinder.bash_completion
+ if use_library_from_git "python-cinderclient"; then
+ git_clone_by_name "python-cinderclient"
+ setup_dev_lib "python-cinderclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-cinderclient"]}/tools/,/etc/bash_completion.d/}cinder.bash_completion
+ fi
}
# apply config.d approach for cinder volumes directory
@@ -429,14 +442,7 @@
_configure_tgt_for_config_d
if is_ubuntu; then
sudo service tgt restart
- elif is_fedora; then
- if [[ $DISTRO =~ (rhel6) ]]; then
- sudo /sbin/service tgtd restart
- else
- # bypass redirection to systemctl during restart
- sudo /sbin/service --skip-redirect tgtd restart
- fi
- elif is_suse; then
+ elif is_fedora || is_suse; then
restart_service tgtd
else
# note for other distros: unstack.sh also uses the tgt/tgtd service
diff --git a/lib/cinder_backends/ceph b/lib/cinder_backends/ceph
index e9d2a02..7e9d2d3 100644
--- a/lib/cinder_backends/ceph
+++ b/lib/cinder_backends/ceph
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/ceph
# Configure the ceph backend
@@ -52,11 +54,13 @@
iniset $CINDER_CONF DEFAULT glance_api_version 2
if is_service_enabled c-bak; then
- # Configure Cinder backup service options, ceph pool, ceph user and ceph key
sudo ceph -c ${CEPH_CONF_FILE} osd pool create ${CINDER_BAK_CEPH_POOL} ${CINDER_BAK_CEPH_POOL_PG} ${CINDER_BAK_CEPH_POOL_PGP}
- sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_BAK_CEPH_POOL} size ${CEPH_REPLICAS}
- if [[ $CEPH_REPLICAS -ne 1 ]]; then
- sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_BAK_CEPH_POOL} crush_ruleset ${RULE_ID}
+ if [ "$REMOTE_CEPH" = "False" ]; then
+ # Configure Cinder backup service options, ceph pool, ceph user and ceph key
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_BAK_CEPH_POOL} size ${CEPH_REPLICAS}
+ if [[ $CEPH_REPLICAS -ne 1 ]]; then
+ sudo ceph -c ${CEPH_CONF_FILE} osd pool set ${CINDER_BAK_CEPH_POOL} crush_ruleset ${RULE_ID}
+ fi
fi
sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${CINDER_BAK_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${CINDER_BAK_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${CINDER_BAK_CEPH_USER}.keyring
sudo chown $(whoami):$(whoami) ${CEPH_CONF_DIR}/ceph.client.${CINDER_BAK_CEPH_USER}.keyring
diff --git a/lib/cinder_backends/glusterfs b/lib/cinder_backends/glusterfs
index dd772a8..00c62e0 100644
--- a/lib/cinder_backends/glusterfs
+++ b/lib/cinder_backends/glusterfs
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/glusterfs
# Configure the glusterfs backend
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 8f8ab79..d83c31a 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/lvm
# Configure the LVM backend
@@ -12,10 +14,12 @@
# CINDER_CONF
# DATA_DIR
+# VOLUME_GROUP_NAME
# clean_cinder_backend_lvm - called from clean_cinder()
# configure_cinder_backend_lvm - called from configure_cinder()
# init_cinder_backend_lvm - called from init_cinder()
+# configure_cinder_backend_conf_lvm - called from configure_cinder()
# Save trace setting
@@ -23,156 +27,75 @@
set +o xtrace
-# Defaults
-# --------
-
-# Name of the lvm volume groups to use/create for iscsi volumes
-# This monkey-motion is for compatibility with icehouse-generation Grenade
-# If ``VOLUME_GROUP`` is set, use it, otherwise we'll build a VG name based
-# on ``VOLUME_GROUP_NAME`` that includes the backend name
-# Grenade doesn't use ``VOLUME_GROUP2`` so it is left out
-VOLUME_GROUP_NAME=${VOLUME_GROUP:-${VOLUME_GROUP_NAME:-stack-volumes}}
-
# TODO: resurrect backing device...need to know how to set values
#VOLUME_BACKING_DEVICE=${VOLUME_BACKING_DEVICE:-}
-VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}
-
-
# Entry Points
# ------------
-# Compatibility for getting a volume group name from either ``VOLUME_GROUP``
-# or from ``VOLUME_GROUP_NAME`` plus the backend name
-function get_volume_group_name {
- local be_name=$1
-
- # Again with the icehouse-generation compatibility
- local volume_group_name=$VOLUME_GROUP_NAME
- if [[ -z $VOLUME_GROUP ]]; then
- volume_group_name+="-$be_name"
- fi
- echo $volume_group_name
-}
-
+# cleanup_cinder_backend_lvm - Delete volume group and remove backing file
+# cleanup_cinder_backend_lvm $be_name
function cleanup_cinder_backend_lvm {
local be_name=$1
- # Again with the icehouse-generation compatibility
- local volume_group_name=$(get_volume_group_name $be_name)
-
# Campsite rule: leave behind a volume group at least as clean as we found it
- _clean_lvm_lv ${volume_group_name} $VOLUME_NAME_PREFIX
- _clean_lvm_backing_file ${volume_group_name} $DATA_DIR/${volume_group_name}-backing-file
+ clean_lvm_volume_group $VOLUME_GROUP_NAME-$be_name
}
# configure_cinder_backend_lvm - Set config files, create data dirs, etc
-# configure_cinder_backend_lvm $name
+# configure_cinder_backend_lvm $be_name
function configure_cinder_backend_lvm {
local be_name=$1
- # Again with the icehouse-generation compatibility
- local volume_group_name=$(get_volume_group_name $be_name)
-
iniset $CINDER_CONF $be_name volume_backend_name $be_name
- iniset $CINDER_CONF $be_name volume_driver "cinder.volume.drivers.lvm.LVMISCSIDriver"
- iniset $CINDER_CONF $be_name volume_group $volume_group_name
+ iniset $CINDER_CONF $be_name volume_driver "cinder.volume.drivers.lvm.LVMVolumeDriver"
+ iniset $CINDER_CONF $be_name volume_group $VOLUME_GROUP_NAME-$be_name
+ iniset $CINDER_CONF $be_name iscsi_helper "tgtadm"
if [[ "$CINDER_SECURE_DELETE" == "False" ]]; then
iniset $CINDER_CONF $be_name volume_clear none
fi
}
-
+# init_cinder_backend_lvm - Initialize volume group
+# init_cinder_backend_lvm $be_name
function init_cinder_backend_lvm {
local be_name=$1
- # Again with the icehouse-generation compatibility
- local volume_group_name=$(get_volume_group_name $be_name)
-
# Start with a clean volume group
- _create_cinder_volume_group ${volume_group_name} $DATA_DIR/${volume_group_name}-backing-file
-
- if is_fedora || is_suse; then
- # service is not started by default
- start_service tgtd
- fi
-
- # Remove iscsi targets
- sudo tgtadm --op show --mode target | grep $VOLUME_NAME_PREFIX | grep Target | cut -f3 -d ' ' | sudo xargs -n1 tgt-admin --delete || true
- _clean_lvm_lv ${volume_group_name} $VOLUME_NAME_PREFIX
+ init_lvm_volume_group $VOLUME_GROUP_NAME-$be_name $VOLUME_BACKING_FILE_SIZE
}
+# configure_cinder_backend_conf_lvm - Sets device filter in /etc/cinder/lvm.conf
+# init_cinder_backend_lvm
+function configure_cinder_backend_conf_lvm {
+ local filter_suffix='"r/.*/" ]'
+ local filter_string="filter = [ "
+ local conf_entries=$(grep volume_group /etc/cinder/cinder.conf | sed "s/ //g")
+ local pv
+ local vg
+ local line
-# _clean_lvm_lv removes all cinder LVM volumes
-#
-# Usage: _clean_lvm_lv volume-group-name $VOLUME_NAME_PREFIX
-function _clean_lvm_lv {
- local vg=$1
- local lv_prefix=$2
-
- # Clean out existing volumes
- local lv
- for lv in $(sudo lvs --noheadings -o lv_name $vg 2>/dev/null); do
- # lv_prefix prefixes the LVs we want
- if [[ "${lv#$lv_prefix}" != "$lv" ]]; then
- sudo lvremove -f $vg/$lv
- fi
- done
-}
-
-# _clean_lvm_backing_file() removes the backing file of the
-# volume group used by cinder
-#
-# Usage: _clean_lvm_backing_file() volume-group-name backing-file-name
-function _clean_lvm_backing_file {
- local vg=$1
- local backing_file=$2
-
- # if there is no logical volume left, it's safe to attempt a cleanup
- # of the backing file
- if [[ -z "$(sudo lvs --noheadings -o lv_name $vg 2>/dev/null)" ]]; then
- # if the backing physical device is a loop device, it was probably setup by devstack
- local vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/backing-file/ { print $1}')
- if [[ -n "$vg_dev" ]] && [[ -e "$vg_dev" ]]; then
- sudo losetup -d $vg_dev
- rm -f $backing_file
- fi
- fi
-}
-
-# _create_cinder_volume_group volume-group-name backing-file-name
-function _create_cinder_volume_group {
- # According to the ``CINDER_MULTI_LVM_BACKEND`` value, configure one or two default volumes
- # group called ``stack-volumes`` (and ``stack-volumes2``) for the volume
- # service if it (they) does (do) not yet exist. If you don't wish to use a
- # file backed volume group, create your own volume group called ``stack-volumes``
- # and ``stack-volumes2`` before invoking ``stack.sh``.
- #
- # The two backing files are ``VOLUME_BACKING_FILE_SIZE`` in size, and they are stored in
- # the ``DATA_DIR``.
-
- local vg_name=$1
- local backing_file=$2
-
- if ! sudo vgs $vg_name; then
- # TODO: fix device handling
- if [ -z "$VOLUME_BACKING_DEVICE" ]; then
- # Only create if the file doesn't already exists
- [[ -f $backing_file ]] || truncate -s $VOLUME_BACKING_FILE_SIZE $backing_file
- local vg_dev=`sudo losetup -f --show $backing_file`
-
- # Only create if the loopback device doesn't contain $VOLUME_GROUP
- if ! sudo vgs $vg_name; then
- sudo vgcreate $vg_name $vg_dev
+ for pv_info in $(sudo pvs --noheadings -o name,vg_name --separator ';'); do
+ echo_summary "Evaluate PV info for Cinder lvm.conf: $pv_info"
+ IFS=';' read pv vg <<< $pv_info
+ for line in ${conf_entries}; do
+ IFS='=' read label group <<< $line
+ group=$(echo $group|sed "s/^ *//g")
+ if [[ "$vg" == "$group" ]]; then
+ new="\"a$pv/\", "
+ filter_string=$filter_string$new
fi
- else
- sudo vgcreate $vg_name $VOLUME_BACKING_DEVICE
- fi
- fi
+ done
+ done
+ filter_string=$filter_string$filter_suffix
+
+ # FIXME(jdg): Possible odd case that the lvm.conf file has been modified
+ # and doesn't have a filter entry to search/replace. For devstack don't
+ # know that we care, but could consider adding a check and add
+ sudo sed -i "s#^[ \t]*filter.*# $filter_string#g" /etc/cinder/lvm.conf
+ echo "set LVM filter_strings: $filter_string"
}
-
-
# Restore xtrace
$MY_XTRACE
diff --git a/lib/cinder_backends/netapp_iscsi b/lib/cinder_backends/netapp_iscsi
index 7a67da7..be9442e 100644
--- a/lib/cinder_backends/netapp_iscsi
+++ b/lib/cinder_backends/netapp_iscsi
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/netapp_iscsi
# Configure the NetApp iSCSI driver
diff --git a/lib/cinder_backends/netapp_nfs b/lib/cinder_backends/netapp_nfs
index d90b7f7..dc919ad 100644
--- a/lib/cinder_backends/netapp_nfs
+++ b/lib/cinder_backends/netapp_nfs
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/netapp_nfs
# Configure the NetApp NFS driver
diff --git a/lib/cinder_backends/nfs b/lib/cinder_backends/nfs
index 7648788..fc51b2b 100644
--- a/lib/cinder_backends/nfs
+++ b/lib/cinder_backends/nfs
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/nfs
# Configure the nfs backend
diff --git a/lib/cinder_backends/solidfire b/lib/cinder_backends/solidfire
index 95ffce1..7cc70fc 100644
--- a/lib/cinder_backends/solidfire
+++ b/lib/cinder_backends/solidfire
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/solidfire
# Configure the solidfire driver
diff --git a/lib/cinder_backends/vmdk b/lib/cinder_backends/vmdk
index b32c4b2..d5b9453 100644
--- a/lib/cinder_backends/vmdk
+++ b/lib/cinder_backends/vmdk
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_backends/vmdk
# Configure the VMware vmdk backend
diff --git a/lib/cinder_backends/xiv b/lib/cinder_backends/xiv
index ee5da2d..6eadaae 100644
--- a/lib/cinder_backends/xiv
+++ b/lib/cinder_backends/xiv
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Copyright 2014 IBM Corp.
# Copyright (c) 2014 OpenStack Foundation
# All Rights Reserved.
@@ -82,4 +84,3 @@
# Local variables:
# mode: shell-script
# End:
-
diff --git a/lib/cinder_plugins/XenAPINFS b/lib/cinder_plugins/XenAPINFS
index fa10715..f730695 100644
--- a/lib/cinder_plugins/XenAPINFS
+++ b/lib/cinder_plugins/XenAPINFS
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_plugins/XenAPINFS
# Configure the XenAPINFS driver
diff --git a/lib/cinder_plugins/glusterfs b/lib/cinder_plugins/glusterfs
index b4196e4..35ceb27 100644
--- a/lib/cinder_plugins/glusterfs
+++ b/lib/cinder_plugins/glusterfs
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_plugins/glusterfs
# Configure the glusterfs driver
diff --git a/lib/cinder_plugins/nfs b/lib/cinder_plugins/nfs
index 5f4cc53..83b3993 100644
--- a/lib/cinder_plugins/nfs
+++ b/lib/cinder_plugins/nfs
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_plugins/nfs
# Configure the nfs driver
diff --git a/lib/cinder_plugins/sheepdog b/lib/cinder_plugins/sheepdog
index 30c60c6..ca343f7 100644
--- a/lib/cinder_plugins/sheepdog
+++ b/lib/cinder_plugins/sheepdog
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_plugins/sheepdog
# Configure the sheepdog driver
diff --git a/lib/cinder_plugins/vsphere b/lib/cinder_plugins/vsphere
index 436b060..f14ddf0 100644
--- a/lib/cinder_plugins/vsphere
+++ b/lib/cinder_plugins/vsphere
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/cinder_plugins/vsphere
# Configure the vsphere driver
diff --git a/lib/config b/lib/config
index a4d0328..31c6fa6 100644
--- a/lib/config
+++ b/lib/config
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/config - Configuration file manipulation functions
# These functions have no external dependencies and the following side-effects:
@@ -142,6 +144,7 @@
else {
# For multiline, invoke the ini routines in the reverse order
count = cfg_attr_count[section, attr]
+ print "inidelete " configfile " " section " " attr
print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
for (l = count -2; l >= 0; l--)
print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
diff --git a/lib/database b/lib/database
index e226515..b114e9e 100644
--- a/lib/database
+++ b/lib/database
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/database
# Interface for interacting with different database backends
@@ -21,6 +23,7 @@
XTRACE=$(set +o | grep xtrace)
set +o xtrace
+DATABASE_BACKENDS=""
# Register a database backend
#
@@ -28,7 +31,7 @@
#
# This is required to be defined before the specific database scripts are sourced
function register_database {
- [ -z "$DATABASE_BACKENDS" ] && DATABASE_BACKENDS=$1 || DATABASE_BACKENDS+=" $1"
+ DATABASE_BACKENDS+=" $1"
}
# Sourcing the database libs sets DATABASE_BACKENDS with the available list
@@ -96,11 +99,9 @@
# Recreate a given database
# $1 The name of the database
-# $2 The character set/encoding of the database
function recreate_database {
local db=$1
- local charset=$2
- recreate_database_$DATABASE_TYPE $db $charset
+ recreate_database_$DATABASE_TYPE $db
}
# Install the database
diff --git a/lib/databases/mysql b/lib/databases/mysql
index 67bf85a..c8ceec2 100644
--- a/lib/databases/mysql
+++ b/lib/databases/mysql
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/databases/mysql
# Functions to control the configuration and operation of the **MySQL** database backend
@@ -12,38 +14,39 @@
register_database mysql
+# Linux distros, thank you for being incredibly consistent
+MYSQL=mysql
+if is_fedora; then
+ MYSQL=mariadb
+fi
# Functions
# ---------
# Get rid of everything enough to cleanly change database backends
function cleanup_database_mysql {
+ stop_service $MYSQL
if is_ubuntu; then
# Get ruthless with mysql
- stop_service $MYSQL
- apt_get purge -y mysql*
+ apt_get purge -y mysql* mariadb*
sudo rm -rf /var/lib/mysql
sudo rm -rf /etc/mysql
return
elif is_fedora; then
- if [[ $DISTRO =~ (rhel7) ]]; then
- MYSQL=mariadb
- else
- MYSQL=mysqld
- fi
+ uninstall_package mariadb-server
+ sudo rm -rf /var/lib/mysql
elif is_suse; then
- MYSQL=mysql
+ uninstall_package mysql-community-server
+ sudo rm -rf /var/lib/mysql
else
return
fi
- stop_service $MYSQL
}
function recreate_database_mysql {
local db=$1
- local charset=$2
mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$MYSQL_HOST -e "DROP DATABASE IF EXISTS $db;"
- mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$MYSQL_HOST -e "CREATE DATABASE $db CHARACTER SET $charset;"
+ mysql -u$DATABASE_USER -p$DATABASE_PASSWORD -h$MYSQL_HOST -e "CREATE DATABASE $db CHARACTER SET utf8;"
}
function configure_database_mysql {
@@ -54,11 +57,7 @@
my_conf=/etc/mysql/my.cnf
mysql=mysql
elif is_fedora; then
- if [[ $DISTRO =~ (rhel7) ]]; then
- mysql=mariadb
- else
- mysql=mysqld
- fi
+ mysql=mariadb
my_conf=/etc/my.cnf
elif is_suse; then
my_conf=/etc/my.cnf
@@ -94,7 +93,7 @@
if [[ "$DATABASE_QUERY_LOGGING" == "True" ]]; then
echo_summary "Enabling MySQL query logging"
- if is_fedora && ! [[ $DISTRO =~ (rhel6) ]]; then
+ if is_fedora; then
slow_log=/var/log/mariadb/mariadb-slow.log
else
slow_log=/var/log/mysql/mysql-slow.log
@@ -121,10 +120,10 @@
if is_ubuntu; then
# Seed configuration with mysql password so that apt-get install doesn't
# prompt us for a password upon install.
- cat <<MYSQL_PRESEED | sudo debconf-set-selections
-mysql-server-5.1 mysql-server/root_password password $DATABASE_PASSWORD
-mysql-server-5.1 mysql-server/root_password_again password $DATABASE_PASSWORD
-mysql-server-5.1 mysql-server/start_on_boot boolean true
+ sudo debconf-set-selections <<MYSQL_PRESEED
+mysql-server mysql-server/root_password password $DATABASE_PASSWORD
+mysql-server mysql-server/root_password_again password $DATABASE_PASSWORD
+mysql-server mysql-server/start_on_boot boolean true
MYSQL_PRESEED
fi
@@ -141,12 +140,10 @@
chmod 0600 $HOME/.my.cnf
fi
# Install mysql-server
- if is_ubuntu || is_fedora; then
- if [[ $DISTRO =~ (rhel7) ]]; then
- install_package mariadb-server
- else
- install_package mysql-server
- fi
+ if is_fedora; then
+ install_package mariadb-server
+ elif is_ubuntu; then
+ install_package mysql-server
elif is_suse; then
if ! is_package_installed mariadb; then
install_package mysql-community-server
diff --git a/lib/databases/postgresql b/lib/databases/postgresql
index fb6d304..317e0eb 100644
--- a/lib/databases/postgresql
+++ b/lib/databases/postgresql
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/databases/postgresql
# Functions to control the configuration and operation of the **PostgreSQL** database backend
@@ -35,10 +37,9 @@
function recreate_database_postgresql {
local db=$1
- local charset=$2
# Avoid unsightly error when calling dropdb when the database doesn't exist
psql -h$DATABASE_HOST -U$DATABASE_USER -dtemplate1 -c "DROP DATABASE IF EXISTS $db"
- createdb -h $DATABASE_HOST -U$DATABASE_USER -l C -T template0 -E $charset $db
+ createdb -h $DATABASE_HOST -U$DATABASE_USER -l C -T template0 -E utf8 $db
}
function configure_database_postgresql {
@@ -48,11 +49,7 @@
pg_hba=/var/lib/pgsql/data/pg_hba.conf
pg_conf=/var/lib/pgsql/data/postgresql.conf
if ! sudo [ -e $pg_hba ]; then
- if ! [[ $DISTRO =~ (rhel6) ]]; then
- sudo postgresql-setup initdb
- else
- sudo service postgresql initdb
- fi
+ sudo postgresql-setup initdb
fi
elif is_ubuntu; then
pg_dir=`find /etc/postgresql -name pg_hba.conf|xargs dirname`
diff --git a/lib/dib b/lib/dib
index 30b31ec..88d9fd8 100644
--- a/lib/dib
+++ b/lib/dib
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/dib
# Install and build images with **diskimage-builder**
@@ -24,7 +26,7 @@
# NOTE: Setting DIB_APT_SOURCES assumes you will be building
# Debian/Ubuntu based images. Leave unset for other flavors.
DIB_APT_SOURCES=${DIB_APT_SOURCES:-""}
-DIB_BUILD_OFFLINE=$(trueorfalse False $DIB_BUILD_OFFLINE)
+DIB_BUILD_OFFLINE=$(trueorfalse False DIB_BUILD_OFFLINE)
DIB_IMAGE_CACHE=$DATA_DIR/diskimage-builder/image-create
DIB_PIP_REPO=$DATA_DIR/diskimage-builder/pip-repo
DIB_PIP_REPO_PORT=${DIB_PIP_REPO_PORT:-8899}
@@ -33,15 +35,6 @@
ORC_DIR=$DEST/os-refresh-config
OAC_DIR=$DEST/os-apply-config
-# Include the apt-sources element in builds if we have an
-# alternative sources.list specified.
-if [ -n "$DIB_APT_SOURCES" ]; then
- if [ ! -e "$DIB_APT_SOURCES" ]; then
- die $LINENO "DIB_APT_SOURCES set but not found at $DIB_APT_SOURCES"
- fi
- EXTRA_ELEMENTS="apt-sources"
-fi
-
# Functions
# ---------
@@ -104,6 +97,15 @@
local image_path=$TOP_DIR/files/$image_name.qcow2
+ # Include the apt-sources element in builds if we have an
+ # alternative sources.list specified.
+ if [ -n "$DIB_APT_SOURCES" ]; then
+ if [ ! -e "$DIB_APT_SOURCES" ]; then
+ die $LINENO "DIB_APT_SOURCES set but not found at $DIB_APT_SOURCES"
+ fi
+ local extra_elements="apt-sources"
+ fi
+
# Set the local pip repo as the primary index mirror so the
# image is built with local packages
local pypi_mirror_url=http://$SERVICE_HOST:$DIB_PIP_REPO_PORT/
@@ -125,7 +127,7 @@
DIB_OFFLINE=$DIB_BUILD_OFFLINE \
PYPI_MIRROR_URL=$pypi_mirror_url \
PYPI_MIRROR_URL_1=$pypi_mirror_url_1 \
- disk-image-create -a amd64 $image_elements $EXTRA_ELEMENTS \
+ disk-image-create -a amd64 $image_elements ${extra_elements:-} \
--image-cache $DIB_IMAGE_CACHE \
-o $image_path
diff --git a/lib/dstat b/lib/dstat
index a2c522c..740e48f 100644
--- a/lib/dstat
+++ b/lib/dstat
@@ -1,4 +1,6 @@
-# lib/apache
+#!/bin/bash
+#
+# lib/dstat
# Functions to start and stop dstat
# Dependencies:
@@ -18,15 +20,19 @@
# Defaults
# --------
# for DSTAT logging
-DSTAT_FILE=${DSTAT_FILE:-"dstat.txt"}
+DSTAT_FILE=${DSTAT_FILE:-"dstat.log"}
# start_dstat() - Start running processes, including screen
function start_dstat {
# A better kind of sysstat, with the top process per time slice
- DSTAT_OPTS="-tcmndrylp --top-cpu-adv"
- if [[ -n ${SCREEN_LOGDIR} ]]; then
- screen_it dstat "cd $TOP_DIR; dstat $DSTAT_OPTS | tee $SCREEN_LOGDIR/$DSTAT_FILE"
+ DSTAT_OPTS="-tcmndrylpg --top-cpu-adv --top-io-adv"
+ if [[ -n ${LOGDIR} ]]; then
+ screen_it dstat "cd $TOP_DIR; dstat $DSTAT_OPTS | tee $LOGDIR/$DSTAT_FILE"
+ if [[ -n ${SCREEN_LOGDIR} && ${SCREEN_LOGDIR} != ${LOGDIR} ]]; then
+ # Drop the backward-compat symlink
+ ln -sf $LOGDIR/$DSTAT_FILE ${SCREEN_LOGDIR}/$DSTAT_FILE
+ fi
else
screen_it dstat "dstat $DSTAT_OPTS"
fi
@@ -34,7 +40,10 @@
# stop_dstat() stop dstat process
function stop_dstat {
- screen_stop dstat
+ # dstat runs as a console, not as a service, and isn't trackable
+ # via the normal mechanisms for devstack. So lets just do a
+ # killall and move on.
+ killall dstat || /bin/true
}
# Restore xtrace
diff --git a/lib/gantt b/lib/gantt
deleted file mode 100644
index 485613f..0000000
--- a/lib/gantt
+++ /dev/null
@@ -1,96 +0,0 @@
-# lib/gantt
-# Install and start **Gantt** scheduler service
-
-# Dependencies:
-#
-# - functions
-# - DEST, DATA_DIR, STACK_USER must be defined
-
-# stack.sh
-# ---------
-# - install_gantt
-# - configure_gantt
-# - init_gantt
-# - start_gantt
-# - stop_gantt
-# - cleanup_gantt
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-# Defaults
-# --------
-
-# set up default directories
-GANTT_DIR=$DEST/gantt
-GANTT_STATE_PATH=${GANTT_STATE_PATH:=$DATA_DIR/gantt}
-GANTT_REPO=${GANTT_REPO:-${GIT_BASE}/openstack/gantt.git}
-GANTT_BRANCH=${GANTT_BRANCH:-master}
-
-GANTTCLIENT_DIR=$DEST/python-ganttclient
-GANTTCLIENT_REPO=${GANTT_REPO:-${GIT_BASE}/openstack/python-ganttclient.git}
-GANTTCLIENT_BRANCH=${GANTT_BRANCH:-master}
-
-# eventually we will have a separate gantt config
-# file but for compatibility reasone stick with
-# nova.conf for now
-GANTT_CONF_DIR=${GANTT_CONF_DIR:-/etc/nova}
-GANTT_CONF=$GANTT_CONF_DIR/nova.conf
-
-# Support entry points installation of console scripts
-GANTT_BIN_DIR=$(get_python_exec_prefix)
-
-
-# Functions
-# ---------
-
-# cleanup_gantt() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_gantt {
- echo "Cleanup Gantt"
-}
-
-# configure_gantt() - Set config files, create data dirs, etc
-function configure_gantt {
- echo "Configure Gantt"
-}
-
-# init_gantt() - Initialize database and volume group
-function init_gantt {
- echo "Initialize Gantt"
-}
-
-# install_gantt() - Collect source and prepare
-function install_gantt {
- git_clone $GANTT_REPO $GANTT_DIR $GANTT_BRANCH
- setup_develop $GANTT_DIR
-}
-
-# install_ganttclient() - Collect source and prepare
-function install_ganttclient {
- echo "Install Gantt Client"
-# git_clone $GANTTCLIENT_REPO $GANTTCLIENT_DIR $GANTTCLIENT_BRANCH
-# setup_develop $GANTTCLIENT_DIR
-}
-
-# start_gantt() - Start running processes, including screen
-function start_gantt {
- if is_service_enabled gantt; then
- run_process gantt "$GANTT_BIN_DIR/gantt-scheduler --config-file $GANTT_CONF"
- fi
-}
-
-# stop_gantt() - Stop running processes
-function stop_gantt {
- echo "Stop Gantt"
- stop_process gantt
-}
-
-# Restore xtrace
-$XTRACE
-
-# Tell emacs to use shell-script-mode
-## Local variables:
-## mode: shell-script
-## End:
diff --git a/lib/glance b/lib/glance
old mode 100644
new mode 100755
index 4194842..eb1df2e
--- a/lib/glance
+++ b/lib/glance
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/glance
# Functions to control the configuration and operation of the **Glance** service
@@ -27,9 +29,10 @@
# --------
# Set up default directories
+GITDIR["python-glanceclient"]=$DEST/python-glanceclient
+GITDIR["glance_store"]=$DEST/glance_store
+
GLANCE_DIR=$DEST/glance
-GLANCE_STORE_DIR=$DEST/glance_store
-GLANCECLIENT_DIR=$DEST/python-glanceclient
GLANCE_CACHE_DIR=${GLANCE_CACHE_DIR:=$DATA_DIR/glance/cache}
GLANCE_IMAGE_DIR=${GLANCE_IMAGE_DIR:=$DATA_DIR/glance/images}
GLANCE_AUTH_CACHE_DIR=${GLANCE_AUTH_CACHE_DIR:-/var/cache/glance}
@@ -67,7 +70,6 @@
# Tell Tempest this project is present
TEMPEST_SERVICES+=,glance
-
# Functions
# ---------
@@ -128,6 +130,9 @@
iniset $GLANCE_API_CONF DEFAULT container_formats "ami,ari,aki,bare,ovf,tgz"
iniset $GLANCE_API_CONF DEFAULT disk_formats "ami,ari,aki,vhd,raw,iso"
fi
+ if [ "$VIRT_DRIVER" = 'libvirt' ] && [ "$LIBVIRT_TYPE" = 'parallels' ]; then
+ iniset $GLANCE_API_CONF DEFAULT disk_formats "ami,ari,aki,vhd,vmdk,raw,qcow2,vdi,iso,ploop"
+ fi
# Store specific configs
iniset $GLANCE_API_CONF DEFAULT filesystem_store_datadir $GLANCE_IMAGE_DIR/
@@ -178,6 +183,12 @@
iniset $GLANCE_API_CONF DEFAULT registry_client_protocol https
fi
+ # Format logging
+ if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
+ setup_colorized_logging $GLANCE_API_CONF DEFAULT "project_id" "user_id"
+ setup_colorized_logging $GLANCE_REGISTRY_CONF DEFAULT "project_id" "user_id"
+ fi
+
cp -p $GLANCE_DIR/etc/glance-registry-paste.ini $GLANCE_REGISTRY_PASTE_INI
cp -p $GLANCE_DIR/etc/glance-api-paste.ini $GLANCE_API_PASTE_INI
@@ -226,16 +237,14 @@
function create_glance_accounts {
if is_service_enabled g-api; then
- local glance_user=$(get_or_create_user "glance" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT_NAME)
- get_or_add_user_role service $glance_user $SERVICE_TENANT_NAME
+ create_service_user "glance"
# required for swift access
if is_service_enabled s-proxy; then
local glance_swift_user=$(get_or_create_user "glance-swift" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT_NAME "glance-swift@example.com")
- get_or_add_user_role "ResellerAdmin" $glance_swift_user $SERVICE_TENANT_NAME
+ "$SERVICE_PASSWORD" "glance-swift@example.com")
+ get_or_add_user_project_role "ResellerAdmin" $glance_swift_user $SERVICE_TENANT_NAME
fi
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -273,7 +282,7 @@
mkdir -p $GLANCE_CACHE_DIR
# (Re)create glance database
- recreate_database glance utf8
+ recreate_database glance
# Migrate glance database
$GLANCE_BIN_DIR/glance-manage db_sync
@@ -286,19 +295,28 @@
# install_glanceclient() - Collect source and prepare
function install_glanceclient {
- git_clone $GLANCECLIENT_REPO $GLANCECLIENT_DIR $GLANCECLIENT_BRANCH
- setup_develop $GLANCECLIENT_DIR
+ if use_library_from_git "python-glanceclient"; then
+ git_clone_by_name "python-glanceclient"
+ setup_dev_lib "python-glanceclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-glanceclient"]}/tools/,/etc/bash_completion.d/}glance.bash_completion
+ fi
}
# install_glance() - Collect source and prepare
function install_glance {
# Install glance_store from git so we make sure we're testing
# the latest code.
- git_clone $GLANCE_STORE_REPO $GLANCE_STORE_DIR $GLANCE_STORE_BRANCH
- setup_develop $GLANCE_STORE_DIR
+ if use_library_from_git "glance_store"; then
+ git_clone_by_name "glance_store"
+ setup_dev_lib "glance_store"
+ fi
git_clone $GLANCE_REPO $GLANCE_DIR $GLANCE_BRANCH
setup_develop $GLANCE_DIR
+ if is_service_enabled g-graffiti; then
+ ${TOP_DIR}/pkg/elasticsearch.sh download
+ ${TOP_DIR}/pkg/elasticsearch.sh install
+ fi
}
# start_glance() - Start running processes, including screen
@@ -312,6 +330,9 @@
run_process g-reg "$GLANCE_BIN_DIR/glance-registry --config-file=$GLANCE_CONF_DIR/glance-registry.conf"
run_process g-api "$GLANCE_BIN_DIR/glance-api --config-file=$GLANCE_CONF_DIR/glance-api.conf"
+ if is_service_enabled g-graffiti; then
+ ${TOP_DIR}/pkg/elasticsearch.sh start
+ fi
echo "Waiting for g-api ($GLANCE_HOSTPORT) to start..."
if ! wait_for_service $SERVICE_TIMEOUT $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT; then
die $LINENO "g-api did not start"
@@ -325,7 +346,6 @@
stop_process g-reg
}
-
# Restore xtrace
$XTRACE
diff --git a/lib/heat b/lib/heat
index 53eca25..c102163 100644
--- a/lib/heat
+++ b/lib/heat
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/heat
# Install and start **Heat** service
@@ -8,6 +10,7 @@
# Dependencies:
#
# - functions
+# - dib (if HEAT_CREATE_TEST_IMAGE=True)
# stack.sh
# ---------
@@ -29,19 +32,26 @@
# --------
# set up default directories
+GITDIR["python-heatclient"]=$DEST/python-heatclient
+
HEAT_DIR=$DEST/heat
-HEATCLIENT_DIR=$DEST/python-heatclient
HEAT_CFNTOOLS_DIR=$DEST/heat-cfntools
HEAT_TEMPLATES_REPO_DIR=$DEST/heat-templates
HEAT_AUTH_CACHE_DIR=${HEAT_AUTH_CACHE_DIR:-/var/cache/heat}
-HEAT_STANDALONE=`trueorfalse False $HEAT_STANDALONE`
+HEAT_STANDALONE=$(trueorfalse False HEAT_STANDALONE)
+HEAT_ENABLE_ADOPT_ABANDON=$(trueorfalse False HEAT_ENABLE_ADOPT_ABANDON)
HEAT_CONF_DIR=/etc/heat
HEAT_CONF=$HEAT_CONF_DIR/heat.conf
HEAT_ENV_DIR=$HEAT_CONF_DIR/environment.d
HEAT_TEMPLATES_DIR=$HEAT_CONF_DIR/templates
-HEAT_STACK_DOMAIN=`trueorfalse True $HEAT_STACK_DOMAIN`
+HEAT_STACK_DOMAIN=$(trueorfalse True HEAT_STACK_DOMAIN)
HEAT_API_HOST=${HEAT_API_HOST:-$HOST_IP}
HEAT_API_PORT=${HEAT_API_PORT:-8004}
+HEAT_FUNCTIONAL_IMAGE_ELEMENTS=${HEAT_FUNCTIONAL_IMAGE_ELEMENTS:-\
+vm fedora selinux-permissive pypi os-collect-config os-refresh-config \
+os-apply-config heat-cfntools heat-config heat-config-cfn-init \
+heat-config-puppet heat-config-script}
+
# other default options
HEAT_DEFERRED_AUTH=${HEAT_DEFERRED_AUTH:-trusts}
@@ -70,7 +80,6 @@
# configure_heat() - Set config files, create data dirs, etc
function configure_heat {
- setup_develop $HEAT_DIR
if [[ "$HEAT_STANDALONE" = "True" ]]; then
setup_develop $HEAT_DIR/contrib/heat_keystoneclient_v2
fi
@@ -93,7 +102,6 @@
cp $HEAT_DIR/etc/heat/api-paste.ini $HEAT_API_PASTE_FILE
cp $HEAT_DIR/etc/heat/policy.json $HEAT_POLICY_FILE
- cp $HEAT_DIR/etc/heat/heat.conf.sample $HEAT_CONF
# common options
iniset_rpc_backend heat $HEAT_CONF DEFAULT
@@ -113,11 +121,18 @@
setup_colorized_logging $HEAT_CONF DEFAULT tenant user
fi
- configure_auth_token_middleware $HEAT_CONF heat $HEAT_AUTH_CACHE_DIR
-
- if is_ssl_enabled_service "key"; then
- iniset $HEAT_CONF clients_keystone ca_file $SSL_BUNDLE_FILE
- fi
+ # NOTE(jamielennox): heat re-uses specific values from the
+ # keystone_authtoken middleware group and so currently fails when using the
+ # auth plugin setup. This should be fixed in heat. Heat is also the only
+ # service that requires the auth_uri to include a /v2.0. Remove this custom
+ # setup when bug #1300246 is resolved.
+ iniset $HEAT_CONF keystone_authtoken identity_uri $KEYSTONE_AUTH_URI
+ iniset $HEAT_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_URI/v2.0
+ iniset $HEAT_CONF keystone_authtoken admin_user heat
+ iniset $HEAT_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
+ iniset $HEAT_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
+ iniset $HEAT_CONF keystone_authtoken cafile $SSL_BUNDLE_FILE
+ iniset $HEAT_CONF keystone_authtoken signing_dir $HEAT_AUTH_CACHE_DIR
# ec2authtoken
iniset $HEAT_CONF ec2authtoken auth_uri $KEYSTONE_SERVICE_URI/v2.0
@@ -131,6 +146,7 @@
# OpenStack API
iniset $HEAT_CONF heat_api bind_port $HEAT_API_PORT
+ iniset $HEAT_CONF heat_api workers "$API_WORKERS"
# Cloudformation API
iniset $HEAT_CONF heat_api_cfn bind_port $HEAT_API_CFN_PORT
@@ -150,6 +166,11 @@
iniset $HEAT_CONF clients_cinder ca_file $SSL_BUNDLE_FILE
fi
+ if [[ "$HEAT_ENABLE_ADOPT_ABANDON" = "True" ]]; then
+ iniset $HEAT_CONF DEFAULT enable_stack_adopt true
+ iniset $HEAT_CONF DEFAULT enable_stack_abandon true
+ fi
+
# heat environment
sudo mkdir -p $HEAT_ENV_DIR
sudo chown $STACK_USER $HEAT_ENV_DIR
@@ -168,7 +189,7 @@
function init_heat {
# (re)create heat database
- recreate_database heat utf8
+ recreate_database heat
$HEAT_DIR/bin/heat-manage db_sync
create_heat_cache_dir
@@ -183,14 +204,17 @@
# install_heatclient() - Collect source and prepare
function install_heatclient {
- git_clone $HEATCLIENT_REPO $HEATCLIENT_DIR $HEATCLIENT_BRANCH
- setup_develop $HEATCLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$HEATCLIENT_DIR/tools/,/etc/bash_completion.d/}heat.bash_completion
+ if use_library_from_git "python-heatclient"; then
+ git_clone_by_name "python-heatclient"
+ setup_dev_lib "python-heatclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-heatclient"]}/tools/,/etc/bash_completion.d/}heat.bash_completion
+ fi
}
# install_heat() - Collect source and prepare
function install_heat {
git_clone $HEAT_REPO $HEAT_DIR $HEAT_BRANCH
+ setup_develop $HEAT_DIR
}
# install_heat_other() - Collect source and prepare
@@ -218,13 +242,7 @@
# create_heat_accounts() - Set up common required heat accounts
function create_heat_accounts {
- # migrated from files/keystone_data.sh
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
- local heat_user=$(get_or_create_user "heat" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $heat_user $service_tenant
+ create_service_user "heat" "admin"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -249,40 +267,27 @@
get_or_create_role "heat_stack_user"
if [[ $HEAT_DEFERRED_AUTH == trusts ]]; then
-
- # heat_stack_owner role is given to users who create Heat stacks,
- # it's the default role used by heat to delegate to the heat service
- # user (for performing deferred operations via trusts), see heat.conf
- local heat_owner_role=$(get_or_create_role "heat_stack_owner")
-
- # Give the role to the demo and admin users so they can create stacks
- # in either of the projects created by devstack
- get_or_add_user_role $heat_owner_role demo demo
- get_or_add_user_role $heat_owner_role admin demo
- get_or_add_user_role $heat_owner_role admin admin
iniset $HEAT_CONF DEFAULT deferred_auth_method trusts
fi
if [[ "$HEAT_STACK_DOMAIN" == "True" ]]; then
# Note we have to pass token/endpoint here because the current endpoint and
# version negotiation in OSC means just --os-identity-api-version=3 won't work
- local ks_endpoint_v3="$KEYSTONE_SERVICE_URI/v3"
-
- D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+ D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
--os-identity-api-version=3 domain list | grep ' heat ' | get_field 1)
if [[ -z "$D_ID" ]]; then
- D_ID=$(openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+ D_ID=$(openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
--os-identity-api-version=3 domain create heat \
--description "Owns users and projects created by heat" \
| grep ' id ' | get_field 2)
iniset $HEAT_CONF DEFAULT stack_user_domain_id ${D_ID}
- openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+ openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
--os-identity-api-version=3 user create --password $SERVICE_PASSWORD \
--domain $D_ID heat_domain_admin \
--description "Manages users and projects created by heat"
- openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
+ openstack --os-token $OS_TOKEN --os-url=$KEYSTONE_SERVICE_URI_V3 \
--os-identity-api-version=3 role add \
--user heat_domain_admin --domain ${D_ID} admin
iniset $HEAT_CONF DEFAULT stack_domain_admin heat_domain_admin
@@ -293,19 +298,20 @@
# build_heat_functional_test_image() - Build and upload functional test image
function build_heat_functional_test_image {
- build_dib_pip_repo "$OCC_DIR $OAC_DIR $ORC_DIR $HEAT_CFNTOOLS_DIR"
- local image_name=heat-functional-tests-image
+ if is_service_enabled dib; then
+ build_dib_pip_repo "$OCC_DIR $OAC_DIR $ORC_DIR $HEAT_CFNTOOLS_DIR"
+ local image_name=heat-functional-tests-image
- # The elements to invoke disk-image-create with
- local image_elements="vm fedora selinux-permissive pypi \
- os-collect-config os-refresh-config os-apply-config heat-cfntools \
- heat-config heat-config-cfn-init heat-config-puppet heat-config-script"
+ # Elements path for tripleo-image-elements and heat-templates software-config
+ local elements_path=$TIE_DIR/elements:$HEAT_TEMPLATES_REPO_DIR/hot/software-config/elements
- # Elements path for tripleo-image-elements and heat-templates software-config
- local elements_path=$TIE_DIR/elements:$HEAT_TEMPLATES_REPO_DIR/hot/software-config/elements
-
- disk_image_create_upload "$image_name" "$image_elements" "$elements_path"
- iniset $TEMPEST_CONFIG orchestration image_ref $image_name
+ disk_image_create_upload "$image_name" "$HEAT_FUNCTIONAL_IMAGE_ELEMENTS" "$elements_path"
+ iniset $TEMPEST_CONFIG orchestration image_ref $image_name
+ else
+ echo "Error, HEAT_CREATE_TEST_IMAGE=True requires dib" >&2
+ echo "Add \"enable_service dib\" to your localrc" >&2
+ exit 1
+ fi
}
# Restore xtrace
diff --git a/lib/horizon b/lib/horizon
index 0213948..122d516 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/horizon
# Functions to control the configuration and operation of the horizon service
@@ -25,8 +27,9 @@
# --------
# Set up default directories
+GITDIR["django_openstack_auth"]=$DEST/django_openstack_auth
+
HORIZON_DIR=$DEST/horizon
-HORIZONAUTH_DIR=$DEST/django_openstack_auth
# local_settings.py is used to customize Dashboard settings.
# The example file in Horizon repo is used by default.
@@ -69,14 +72,6 @@
# cleanup_horizon() - Remove residual data files, anything left over from previous
# runs that a clean run would need to clean up
function cleanup_horizon {
- if [[ is_fedora && $DISTRO =~ (rhel6) ]]; then
- # If ``/usr/bin/node`` points into ``$DEST``
- # we installed it via ``install_nodejs``
- if [[ $(readlink -f /usr/bin/node) =~ ($DEST) ]]; then
- sudo rm /usr/bin/node
- fi
- fi
-
local horizon_conf=$(apache_site_config_for horizon)
sudo rm -f $horizon_conf
}
@@ -89,9 +84,7 @@
# Horizon is installed as develop mode, so we can compile here.
# Message catalog compilation is handled by Django admin script,
# so compiling them after the installation avoids Django installation twice.
- cd $HORIZON_DIR
- ./run_tests.sh -N --compilemessages
- cd -
+ (cd $HORIZON_DIR; ./run_tests.sh -N --compilemessages)
}
# init_horizon() - Initialize databases, etc.
@@ -100,11 +93,10 @@
local local_settings=$HORIZON_DIR/openstack_dashboard/local/local_settings.py
cp $HORIZON_SETTINGS $local_settings
+ _horizon_config_set $local_settings "" COMPRESS_OFFLINE True
+
_horizon_config_set $local_settings "" OPENSTACK_HOST \"${KEYSTONE_SERVICE_HOST}\"
_horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_URL "\"${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}/v2.0\""
- if [[ -n "$KEYSTONE_TOKEN_HASH_ALGORITHM" ]]; then
- _horizon_config_set $local_settings "" OPENSTACK_TOKEN_HASH_ALGORITHM \""$KEYSTONE_TOKEN_HASH_ALGORITHM"\"
- fi
if [ -f $SSL_BUNDLE_FILE ]; then
_horizon_config_set $local_settings "" OPENSTACK_SSL_CACERT \"${SSL_BUNDLE_FILE}\"
@@ -141,19 +133,31 @@
# and run_process
sudo rm -f /var/log/$APACHE_NAME/horizon_*
+ # Setup alias for django-admin which could be different depending on distro
+ local django_admin
+ if type -p django-admin > /dev/null; then
+ django_admin=django-admin
+ else
+ django_admin=django-admin.py
+ fi
+
+ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings $django_admin collectstatic --noinput
+ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings $django_admin compress --force
+
}
# install_django_openstack_auth() - Collect source and prepare
function install_django_openstack_auth {
- git_clone $HORIZONAUTH_REPO $HORIZONAUTH_DIR $HORIZONAUTH_BRANCH
-
- # Compile message catalogs before installation
- _prepare_message_catalog_compilation
- cd $HORIZONAUTH_DIR
- python setup.py compile_catalog
- cd -
-
- setup_install $HORIZONAUTH_DIR
+ if use_library_from_git "django_openstack_auth"; then
+ local dir=${GITDIR["django_openstack_auth"]}
+ git_clone_by_name "django_openstack_auth"
+ # Compile message catalogs before installation
+ _prepare_message_catalog_compilation
+ (cd $dir; python setup.py compile_catalog)
+ setup_dev_lib "django_openstack_auth"
+ fi
+ # if we aren't using this library from git, then we just let it
+ # get dragged in by the horizon setup.
}
# install_horizon() - Collect source and prepare
@@ -161,7 +165,7 @@
# Apache installation, because we mark it NOPRIME
install_apache_wsgi
- git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH $HORIZON_TAG
+ git_clone $HORIZON_REPO $HORIZON_DIR $HORIZON_BRANCH
}
# start_horizon() - Start running processes, including screen
diff --git a/lib/infra b/lib/infra
index cd439e7..c825b4e 100644
--- a/lib/infra
+++ b/lib/infra
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/infra
#
# Functions to install infrastructure projects needed by other projects
@@ -35,7 +37,9 @@
git_clone_by_name "pbr"
setup_lib "pbr"
else
- pip_install "pbr"
+ # Always upgrade pbr to latest version as we may have pulled it
+ # in via system packages.
+ pip_install "-U" "pbr"
fi
}
diff --git a/lib/ironic b/lib/ironic
index cf005a7..7ffa6a5 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/ironic
# Functions to control the configuration and operation of the **Ironic** service
@@ -28,28 +30,45 @@
# --------
# Set up default directories
+GITDIR["python-ironicclient"]=$DEST/python-ironicclient
+
IRONIC_DIR=$DEST/ironic
IRONIC_PYTHON_AGENT_DIR=$DEST/ironic-python-agent
IRONIC_DATA_DIR=$DATA_DIR/ironic
IRONIC_STATE_PATH=/var/lib/ironic
-IRONICCLIENT_DIR=$DEST/python-ironicclient
IRONIC_AUTH_CACHE_DIR=${IRONIC_AUTH_CACHE_DIR:-/var/cache/ironic}
IRONIC_CONF_DIR=${IRONIC_CONF_DIR:-/etc/ironic}
IRONIC_CONF_FILE=$IRONIC_CONF_DIR/ironic.conf
IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf
IRONIC_POLICY_JSON=$IRONIC_CONF_DIR/policy.json
+# Deploy callback timeout can be changed from its default (1800), if required.
+IRONIC_CALLBACK_TIMEOUT=${IRONIC_CALLBACK_TIMEOUT:-}
+
+# Deploy to hardware platform
+IRONIC_HW_NODE_CPU=${IRONIC_HW_NODE_CPU:-1}
+IRONIC_HW_NODE_RAM=${IRONIC_HW_NODE_RAM:-512}
+IRONIC_HW_NODE_DISK=${IRONIC_HW_NODE_DISK:-10}
+IRONIC_HW_EPHEMERAL_DISK=${IRONIC_HW_EPHEMERAL_DISK:-0}
+# The file is composed of multiple lines, each line includes four field
+# separated by white space: IPMI address, MAC address, IPMI username
+# and IPMI password.
+# An example:
+# 192.168.110.107 00:1e:67:57:50:4c root otc123
+IRONIC_IPMIINFO_FILE=${IRONIC_IPMIINFO_FILE:-$IRONIC_DATA_DIR/hardware_info}
+
# Set up defaults for functional / integration testing
IRONIC_SCRIPTS_DIR=${IRONIC_SCRIPTS_DIR:-$TOP_DIR/tools/ironic/scripts}
IRONIC_TEMPLATES_DIR=${IRONIC_TEMPLATES_DIR:-$TOP_DIR/tools/ironic/templates}
-IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False $IRONIC_BAREMETAL_BASIC_OPS)
+IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False IRONIC_BAREMETAL_BASIC_OPS)
IRONIC_ENABLED_DRIVERS=${IRONIC_ENABLED_DRIVERS:-fake,pxe_ssh,pxe_ipmitool}
IRONIC_SSH_USERNAME=${IRONIC_SSH_USERNAME:-`whoami`}
IRONIC_SSH_KEY_DIR=${IRONIC_SSH_KEY_DIR:-$IRONIC_DATA_DIR/ssh_keys}
IRONIC_SSH_KEY_FILENAME=${IRONIC_SSH_KEY_FILENAME:-ironic_key}
-IRONIC_KEY_FILE=$IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME
+IRONIC_KEY_FILE=${IRONIC_KEY_FILE:-$IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME}
IRONIC_SSH_VIRT_TYPE=${IRONIC_SSH_VIRT_TYPE:-virsh}
IRONIC_TFTPBOOT_DIR=${IRONIC_TFTPBOOT_DIR:-$IRONIC_DATA_DIR/tftpboot}
+IRONIC_TFTPSERVER_IP=${IRONIC_TFTPSERVER_IP:-$HOST_IP}
IRONIC_VM_SSH_PORT=${IRONIC_VM_SSH_PORT:-22}
IRONIC_VM_SSH_ADDRESS=${IRONIC_VM_SSH_ADDRESS:-$HOST_IP}
IRONIC_VM_COUNT=${IRONIC_VM_COUNT:-1}
@@ -68,7 +87,7 @@
IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/}
# Use DIB to create deploy ramdisk and kernel.
-IRONIC_BUILD_DEPLOY_RAMDISK=`trueorfalse True $IRONIC_BUILD_DEPLOY_RAMDISK`
+IRONIC_BUILD_DEPLOY_RAMDISK=$(trueorfalse True IRONIC_BUILD_DEPLOY_RAMDISK)
# If not use DIB, these files are used as deploy ramdisk/kernel.
# (The value must be a absolute path)
IRONIC_DEPLOY_RAMDISK=${IRONIC_DEPLOY_RAMDISK:-}
@@ -79,7 +98,7 @@
IRONIC_AGENT_RAMDISK_URL=${IRONIC_AGENT_RAMDISK_URL:-http://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe_image-oem.cpio.gz}
# Which deploy driver to use - valid choices right now
-# are 'pxe_ssh' and 'agent_ssh'.
+# are 'pxe_ssh', 'pxe_ipmitool', 'agent_ssh' and 'agent_ipmitool'.
IRONIC_DEPLOY_DRIVER=${IRONIC_DEPLOY_DRIVER:-pxe_ssh}
#TODO(agordeev): replace 'ubuntu' with host distro name getting
@@ -90,13 +109,14 @@
# Ironic connection info. Note the port must be specified.
IRONIC_SERVICE_PROTOCOL=http
-IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:6385}
+IRONIC_SERVICE_PORT=${IRONIC_SERVICE_PORT:-6385}
+IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:$IRONIC_SERVICE_PORT}
# Tell Tempest this project is present
TEMPEST_SERVICES+=,ironic
# Enable iPXE
-IRONIC_IPXE_ENABLED=$(trueorfalse False $IRONIC_IPXE_ENABLED)
+IRONIC_IPXE_ENABLED=$(trueorfalse False IRONIC_IPXE_ENABLED)
IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot}
IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$HOST_IP}
IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-8088}
@@ -132,6 +152,16 @@
return 1
}
+function is_ironic_hardware {
+ is_ironic_enabled && [[ -n "${IRONIC_DEPLOY_DRIVER##*_ssh}" ]] && return 0
+ return 1
+}
+
+function is_deployed_by_agent {
+ [[ -z "${IRONIC_DEPLOY_DRIVER%%agent*}" ]] && return 0
+ return 1
+}
+
# install_ironic() - Collect source and prepare
function install_ironic {
# make sure all needed service were enabled
@@ -150,9 +180,14 @@
# install_ironicclient() - Collect sources and prepare
function install_ironicclient {
- git_clone $IRONICCLIENT_REPO $IRONICCLIENT_DIR $IRONICCLIENT_BRANCH
- setup_develop $IRONICCLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$IRONICCLIENT_DIR/tools/,/etc/bash_completion.d/}ironic.bash_completion
+ if use_library_from_git "python-ironicclient"; then
+ git_clone_by_name "python-ironicclient"
+ setup_dev_lib "python-ironicclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-ironicclient"]}/tools/,/etc/bash_completion.d/}ironic.bash_completion
+ else
+ # nothing actually "requires" ironicclient, so force instally from pypi
+ pip_install python-ironicclient
+ fi
}
# _cleanup_ironic_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
@@ -177,7 +212,7 @@
# cleanup_ironic() - Remove residual data files, anything left over from previous
# runs that would need to clean up.
function cleanup_ironic {
- sudo rm -rf $IRONIC_AUTH_CACHE_DIR
+ sudo rm -rf $IRONIC_AUTH_CACHE_DIR $IRONIC_CONF_DIR
}
# configure_ironic_dirs() - Create all directories required by Ironic and
@@ -245,6 +280,7 @@
iniset $IRONIC_CONF_FILE DEFAULT policy_file $IRONIC_POLICY_JSON
configure_auth_token_middleware $IRONIC_CONF_FILE ironic $IRONIC_AUTH_CACHE_DIR/api
iniset_rpc_backend ironic $IRONIC_CONF_FILE DEFAULT
+ iniset $IRONIC_CONF_FILE api port $IRONIC_SERVICE_PORT
cp -p $IRONIC_DIR/etc/ironic/policy.json $IRONIC_POLICY_JSON
}
@@ -266,14 +302,17 @@
iniset $IRONIC_CONF_FILE DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
iniset $IRONIC_CONF_FILE DEFAULT enabled_drivers $IRONIC_ENABLED_DRIVERS
- iniset $IRONIC_CONF_FILE conductor api_url http://$HOST_IP:6385
- iniset $IRONIC_CONF_FILE pxe tftp_server $HOST_IP
+ iniset $IRONIC_CONF_FILE conductor api_url $IRONIC_SERVICE_PROTOCOL://$HOST_IP:$IRONIC_SERVICE_PORT
+ if [[ -n "$IRONIC_CALLBACK_TIMEOUT" ]]; then
+ iniset $IRONIC_CONF_FILE conductor deploy_callback_timeout $IRONIC_CALLBACK_TIMEOUT
+ fi
+ iniset $IRONIC_CONF_FILE pxe tftp_server $IRONIC_TFTPSERVER_IP
iniset $IRONIC_CONF_FILE pxe tftp_root $IRONIC_TFTPBOOT_DIR
iniset $IRONIC_CONF_FILE pxe tftp_master_path $IRONIC_TFTPBOOT_DIR/master_images
if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
iniset $IRONIC_CONF_FILE pxe pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
fi
- if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+ if is_deployed_by_agent; then
if [[ "$SWIFT_ENABLE_TEMPURLS" == "True" ]] ; then
iniset $IRONIC_CONF_FILE glance swift_temp_url_key $SWIFT_TEMPURL_KEY
else
@@ -287,7 +326,7 @@
iniset $IRONIC_CONF_FILE glance swift_temp_url_duration 3600
iniset $IRONIC_CONF_FILE agent heartbeat_timeout 30
if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
- iniset $IRONIC_CONF_FILE agent agent_pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
+ iniset $IRONIC_CONF_FILE agent agent_pxe_append_params "nofb nomodeset vga=normal console=ttyS0 systemd.journald.forward_to_console=yes"
fi
fi
@@ -319,16 +358,12 @@
# service ironic admin # if enabled
function create_ironic_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
# Ironic
if [[ "$ENABLED_SERVICES" =~ "ir-api" ]]; then
# Get ironic user if exists
- local ironic_user=$(get_or_create_user "ironic" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $ironic_user $service_tenant
+ # NOTE(Shrews): This user MUST have admin level privileges!
+ create_service_user "ironic" "admin"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -347,7 +382,7 @@
# init_ironic() - Initialize databases, etc.
function init_ironic {
# (Re)create ironic database
- recreate_database ironic utf8
+ recreate_database ironic
# Migrate ironic database
$IRONIC_BIN_DIR/ironic-dbsync --config-file=$IRONIC_CONF_FILE
@@ -387,7 +422,7 @@
function start_ironic_api {
run_process ir-api "$IRONIC_BIN_DIR/ironic-api --config-file=$IRONIC_CONF_FILE"
echo "Waiting for ir-api ($IRONIC_HOSTPORT) to start..."
- if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- http://$IRONIC_HOSTPORT; do sleep 1; done"; then
+ if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT; do sleep 1; done"; then
die $LINENO "ir-api did not start"
fi
}
@@ -411,13 +446,6 @@
fi
}
-function is_ironic {
- if ( is_service_enabled ir-cond && is_service_enabled ir-api ); then
- return 0
- fi
- return 1
-}
-
function create_ovs_taps {
local ironic_net_id=$(neutron net-list | grep private | get_field 1)
@@ -429,7 +457,11 @@
# intentional sleep to make sure the tag has been set to port
sleep 10
- local tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep tap | cut -d':' -f2 | cut -b2-)
+ if [[ "$Q_USE_NAMESPACE" = "True" ]]; then
+ local tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
+ else
+ local tapdev=$(sudo ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
+ fi
local tag_id=$(sudo ovs-vsctl show |grep ${tapdev} -A1 -m1 | grep tag | cut -d':' -f2 | cut -b2-)
# make sure veth pair is not existing, otherwise delete its links
@@ -469,52 +501,105 @@
create_ovs_taps
}
-function enroll_vms {
+function wait_for_nova_resources {
+ # After nodes have been enrolled, we need to wait for both ironic and
+ # nova's periodic tasks to populate the resource tracker with available
+ # nodes and resources. Wait up to 2 minutes for a given resource before
+ # timing out.
+ local resource=$1
+ local expected_count=$2
+ echo_summary "Waiting 2 minutes for Nova resource tracker to pick up $resource >= $expected_count"
+ for i in $(seq 1 120); do
+ if [ $(nova hypervisor-stats | grep " $resource " | get_field 2) -ge $expected_count ]; then
+ return 0
+ fi
+ sleep 1
+ done
+ die $LINENO "Timed out waiting for Nova hypervisor-stats $resource >= $expected_count"
+}
+
+function enroll_nodes {
local chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
- local idx=0
if [[ "$IRONIC_DEPLOY_DRIVER" == "pxe_ssh" ]] ; then
local _IRONIC_DEPLOY_KERNEL_KEY=pxe_deploy_kernel
local _IRONIC_DEPLOY_RAMDISK_KEY=pxe_deploy_ramdisk
- elif [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+ elif is_deployed_by_agent; then
local _IRONIC_DEPLOY_KERNEL_KEY=deploy_kernel
local _IRONIC_DEPLOY_RAMDISK_KEY=deploy_ramdisk
fi
- while read MAC; do
-
- local node_id=$(ironic node-create --chassis_uuid $chassis_id \
- --driver $IRONIC_DEPLOY_DRIVER \
+ if ! is_ironic_hardware; then
+ local ironic_node_cpu=$IRONIC_VM_SPECS_CPU
+ local ironic_node_ram=$IRONIC_VM_SPECS_RAM
+ local ironic_node_disk=$IRONIC_VM_SPECS_DISK
+ local ironic_ephemeral_disk=$IRONIC_VM_EPHEMERAL_DISK
+ local ironic_hwinfo_file=$IRONIC_VM_MACS_CSV_FILE
+ local node_options="\
-i $_IRONIC_DEPLOY_KERNEL_KEY=$IRONIC_DEPLOY_KERNEL_ID \
-i $_IRONIC_DEPLOY_RAMDISK_KEY=$IRONIC_DEPLOY_RAMDISK_ID \
-i ssh_virt_type=$IRONIC_SSH_VIRT_TYPE \
-i ssh_address=$IRONIC_VM_SSH_ADDRESS \
-i ssh_port=$IRONIC_VM_SSH_PORT \
-i ssh_username=$IRONIC_SSH_USERNAME \
- -i ssh_key_filename=$IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME \
- -p cpus=$IRONIC_VM_SPECS_CPU \
- -p memory_mb=$IRONIC_VM_SPECS_RAM \
- -p local_gb=$IRONIC_VM_SPECS_DISK \
+ -i ssh_key_filename=$IRONIC_KEY_FILE"
+ else
+ local ironic_node_cpu=$IRONIC_HW_NODE_CPU
+ local ironic_node_ram=$IRONIC_HW_NODE_RAM
+ local ironic_node_disk=$IRONIC_HW_NODE_DISK
+ local ironic_ephemeral_disk=$IRONIC_HW_EPHEMERAL_DISK
+ if [[ -z "${IRONIC_DEPLOY_DRIVER##*_ipmitool}" ]]; then
+ local ironic_hwinfo_file=$IRONIC_IPMIINFO_FILE
+ fi
+ fi
+
+ local total_nodes=0
+ local total_cpus=0
+ while read hardware_info; do
+ if ! is_ironic_hardware; then
+ local mac_address=$hardware_info
+ elif [[ -z "${IRONIC_DEPLOY_DRIVER##*_ipmitool}" ]]; then
+ local ipmi_address=$(echo $hardware_info |awk '{print $1}')
+ local mac_address=$(echo $hardware_info |awk '{print $2}')
+ local ironic_ipmi_username=$(echo $hardware_info |awk '{print $3}')
+ local ironic_ipmi_passwd=$(echo $hardware_info |awk '{print $4}')
+ # Currently we require all hardware platform have same CPU/RAM/DISK info
+ # in future, this can be enhanced to support different type, and then
+ # we create the bare metal flavor with minimum value
+ local node_options="-i ipmi_address=$ipmi_address -i ipmi_password=$ironic_ipmi_passwd\
+ -i ipmi_username=$ironic_ipmi_username"
+ node_options+=" -i $_IRONIC_DEPLOY_KERNEL_KEY=$IRONIC_DEPLOY_KERNEL_ID"
+ node_options+=" -i $_IRONIC_DEPLOY_RAMDISK_KEY=$IRONIC_DEPLOY_RAMDISK_ID"
+ fi
+
+ local node_id=$(ironic node-create --chassis_uuid $chassis_id \
+ --driver $IRONIC_DEPLOY_DRIVER \
+ -p cpus=$ironic_node_cpu\
+ -p memory_mb=$ironic_node_ram\
+ -p local_gb=$ironic_node_disk\
-p cpu_arch=x86_64 \
+ $node_options \
| grep " uuid " | get_field 2)
- ironic port-create --address $MAC --node_uuid $node_id
+ ironic port-create --address $mac_address --node_uuid $node_id
- idx=$((idx+1))
- done < $IRONIC_VM_MACS_CSV_FILE
+ total_nodes=$((total_nodes+1))
+ total_cpus=$((total_cpus+$ironic_node_cpu))
+ done < $ironic_hwinfo_file
# create the nova flavor
# NOTE(adam_g): Attempting to use an autogenerated UUID for flavor id here uncovered
# bug (LP: #1333852) in Trove. This can be changed to use an auto flavor id when the
# bug is fixed in Juno.
- local adjusted_disk=$(($IRONIC_VM_SPECS_DISK - $IRONIC_VM_EPHEMERAL_DISK))
- nova flavor-create --ephemeral $IRONIC_VM_EPHEMERAL_DISK baremetal 551 $IRONIC_VM_SPECS_RAM $adjusted_disk $IRONIC_VM_SPECS_CPU
+ local adjusted_disk=$(($ironic_node_disk - $ironic_ephemeral_disk))
+ nova flavor-create --ephemeral $ironic_ephemeral_disk baremetal 551 $ironic_node_ram $adjusted_disk $ironic_node_cpu
- # TODO(lucasagomes): Remove the 'baremetal:deploy_kernel_id'
- # and 'baremetal:deploy_ramdisk_id' parameters
- # from the flavor after the completion of
- # https://blueprints.launchpad.net/ironic/+spec/add-node-instance-info
- nova flavor-key baremetal set "cpu_arch"="x86_64" "baremetal:deploy_kernel_id"="$IRONIC_DEPLOY_KERNEL_ID" "baremetal:deploy_ramdisk_id"="$IRONIC_DEPLOY_RAMDISK_ID"
+ nova flavor-key baremetal set "cpu_arch"="x86_64"
+
+ if [ "$VIRT_DRIVER" == "ironic" ]; then
+ wait_for_nova_resources "count" $total_nodes
+ wait_for_nova_resources "vcpus" $total_cpus
+ fi
}
function configure_iptables {
@@ -523,8 +608,8 @@
sudo modprobe nf_nat_tftp
# nodes boot from TFTP and callback to the API server listening on $HOST_IP
sudo iptables -I INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
- sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_HOSTPORT -j ACCEPT || true
- if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+ sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true
+ if is_deployed_by_agent; then
# agent ramdisk gets instance image from swift
sudo iptables -I INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
fi
@@ -547,15 +632,16 @@
}
function configure_ironic_ssh_keypair {
- # Generating ssh key pair for stack user
- if [[ ! -d $IRONIC_SSH_KEY_DIR ]]; then
- mkdir -p $IRONIC_SSH_KEY_DIR
- fi
if [[ ! -d $HOME/.ssh ]]; then
mkdir -p $HOME/.ssh
chmod 700 $HOME/.ssh
fi
- echo -e 'n\n' | ssh-keygen -q -t rsa -P '' -f $IRONIC_KEY_FILE
+ if [[ ! -e $IRONIC_KEY_FILE ]]; then
+ if [[ ! -d $(dirname $IRONIC_KEY_FILE) ]]; then
+ mkdir -p $(dirname $IRONIC_KEY_FILE)
+ fi
+ echo -e 'n\n' | ssh-keygen -q -t rsa -P '' -f $IRONIC_KEY_FILE
+ fi
cat $IRONIC_KEY_FILE.pub | tee -a $IRONIC_AUTHORIZED_KEYS_FILE
}
@@ -572,7 +658,7 @@
function configure_ironic_auxiliary {
configure_ironic_ssh_keypair
- ironic_ssh_check $IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME $IRONIC_VM_SSH_ADDRESS $IRONIC_VM_SSH_PORT $IRONIC_SSH_USERNAME 10
+ ironic_ssh_check $IRONIC_KEY_FILE $IRONIC_VM_SSH_ADDRESS $IRONIC_VM_SSH_PORT $IRONIC_SSH_USERNAME 10
}
function build_ipa_coreos_ramdisk {
@@ -600,8 +686,8 @@
fi
if [ -z "$IRONIC_DEPLOY_KERNEL" -o -z "$IRONIC_DEPLOY_RAMDISK" ]; then
- local IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy.kernel
- local IRONIC_DEPLOY_RAMDISK_PATH=$TOP_DIR/files/ir-deploy.initramfs
+ local IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.kernel
+ local IRONIC_DEPLOY_RAMDISK_PATH=$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.initramfs
else
local IRONIC_DEPLOY_KERNEL_PATH=$IRONIC_DEPLOY_KERNEL
local IRONIC_DEPLOY_RAMDISK_PATH=$IRONIC_DEPLOY_RAMDISK
@@ -612,17 +698,17 @@
if [ "$IRONIC_BUILD_DEPLOY_RAMDISK" = "True" ]; then
# we can build them only if we're not offline
if [ "$OFFLINE" != "True" ]; then
- if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+ if is_deployed_by_agent; then
build_ipa_coreos_ramdisk $IRONIC_DEPLOY_KERNEL_PATH $IRONIC_DEPLOY_RAMDISK_PATH
else
ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
- -o $TOP_DIR/files/ir-deploy
+ -o $TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER
fi
else
die $LINENO "Deploy kernel+ramdisk files don't exist and cannot be build in OFFLINE mode"
fi
else
- if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+ if is_deployed_by_agent; then
# download the agent image tarball
wget "$IRONIC_AGENT_KERNEL_URL" -O $IRONIC_DEPLOY_KERNEL_PATH
wget "$IRONIC_AGENT_RAMDISK_URL" -O $IRONIC_DEPLOY_RAMDISK_PATH
@@ -655,12 +741,16 @@
}
function prepare_baremetal_basic_ops {
+ if ! is_ironic_hardware; then
+ configure_ironic_auxiliary
+ fi
upload_baremetal_ironic_deploy
- create_bridge_and_vms
- enroll_vms
+ if ! is_ironic_hardware; then
+ create_bridge_and_vms
+ fi
+ enroll_nodes
configure_tftpd
configure_iptables
- configure_ironic_auxiliary
}
function cleanup_baremetal_basic_ops {
@@ -681,8 +771,8 @@
sudo rm -rf /etc/xinetd.d/tftp /etc/init/tftpd-hpa.override
restart_service xinetd
sudo iptables -D INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
- sudo iptables -D INPUT -d $HOST_IP -p tcp --dport 6385 -j ACCEPT || true
- if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+ sudo iptables -D INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true
+ if is_deployed_by_agent; then
# agent ramdisk gets instance image from swift
sudo iptables -D INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
fi
diff --git a/lib/keystone b/lib/keystone
index 276e971..2da2d1b 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/keystone
# Functions to control the configuration and operation of **Keystone**
@@ -33,6 +35,9 @@
# --------
# Set up default directories
+GITDIR["python-keystoneclient"]=$DEST/python-keystoneclient
+GITDIR["keystonemiddleware"]=$DEST/keystonemiddleware
+
KEYSTONE_DIR=$DEST/keystone
KEYSTONE_CONF_DIR=${KEYSTONE_CONF_DIR:-/etc/keystone}
KEYSTONE_CONF=$KEYSTONE_CONF_DIR/keystone.conf
@@ -44,9 +49,6 @@
KEYSTONE_WSGI_DIR=${KEYSTONE_WSGI_DIR:-/var/www/keystone}
fi
-KEYSTONEMIDDLEWARE_DIR=$DEST/keystonemiddleware
-KEYSTONECLIENT_DIR=$DEST/python-keystoneclient
-
# Set up additional extensions, such as oauth1, federation
# Example of KEYSTONE_EXTENSIONS=oauth1,federation
KEYSTONE_EXTENSIONS=${KEYSTONE_EXTENSIONS:-}
@@ -69,6 +71,7 @@
# Select Keystone's token format
# Choose from 'UUID', 'PKI', or 'PKIZ'
+KEYSTONE_TOKEN_FORMAT=${KEYSTONE_TOKEN_FORMAT:-}
KEYSTONE_TOKEN_FORMAT=$(echo ${KEYSTONE_TOKEN_FORMAT} | tr '[:upper:]' '[:lower:]')
# Set Keystone interface configuration
@@ -104,6 +107,10 @@
KEYSTONE_AUTH_URI=${KEYSTONE_AUTH_PROTOCOL}://${KEYSTONE_AUTH_HOST}:${KEYSTONE_AUTH_PORT}
KEYSTONE_SERVICE_URI=${KEYSTONE_SERVICE_PROTOCOL}://${KEYSTONE_SERVICE_HOST}:${KEYSTONE_SERVICE_PORT}
+# V3 URIs
+KEYSTONE_AUTH_URI_V3=$KEYSTONE_AUTH_URI/v3
+KEYSTONE_SERVICE_URI_V3=$KEYSTONE_SERVICE_URI/v3
+
# Functions
# ---------
# cleanup_keystone() - Remove residual data files, anything left over from previous
@@ -114,7 +121,7 @@
# _cleanup_keystone_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
function _cleanup_keystone_apache_wsgi {
- sudo rm -f $KEYSTONE_WSGI_DIR/*.wsgi
+ sudo rm -f $KEYSTONE_WSGI_DIR/*
sudo rm -f $(apache_site_config_for keystone)
}
@@ -212,6 +219,7 @@
# Configure rabbitmq credentials
if is_service_enabled rabbit; then
+ iniset $KEYSTONE_CONF DEFAULT rabbit_userid $RABBIT_USERID
iniset $KEYSTONE_CONF DEFAULT rabbit_password $RABBIT_PASSWORD
iniset $KEYSTONE_CONF DEFAULT rabbit_host $RABBIT_HOST
fi
@@ -301,8 +309,9 @@
setup_colorized_logging $KEYSTONE_CONF DEFAULT
fi
+ iniset $KEYSTONE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
+
if [ "$KEYSTONE_USE_MOD_WSGI" == "True" ]; then
- iniset $KEYSTONE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
# Eliminate the %(asctime)s.%(msecs)03d from the log format strings
iniset $KEYSTONE_CONF DEFAULT logging_context_format_string "%(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s"
iniset $KEYSTONE_CONF DEFAULT logging_default_format_string "%(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s"
@@ -315,10 +324,6 @@
iniset $KEYSTONE_CONF DEFAULT admin_workers "$API_WORKERS"
# Public workers will use the server default, typically number of CPU.
-
- if [[ -n "$KEYSTONE_TOKEN_HASH_ALGORITHM" ]]; then
- iniset $KEYSTONE_CONF token hash_algorithm "$KEYSTONE_TOKEN_HASH_ALGORITHM"
- fi
}
function configure_keystone_extensions {
@@ -358,10 +363,9 @@
# admin
local admin_tenant=$(get_or_create_project "admin")
- local admin_user=$(get_or_create_user "admin" \
- "$ADMIN_PASSWORD" "$admin_tenant")
+ local admin_user=$(get_or_create_user "admin" "$ADMIN_PASSWORD")
local admin_role=$(get_or_create_role "admin")
- get_or_add_user_role $admin_role $admin_user $admin_tenant
+ get_or_add_user_project_role $admin_role $admin_user $admin_tenant
# Create service project/role
get_or_create_project "$SERVICE_TENANT_NAME"
@@ -388,12 +392,15 @@
# demo
local demo_tenant=$(get_or_create_project "demo")
local demo_user=$(get_or_create_user "demo" \
- "$ADMIN_PASSWORD" "$demo_tenant" "demo@example.com")
+ "$ADMIN_PASSWORD" "demo@example.com")
- get_or_add_user_role $member_role $demo_user $demo_tenant
- get_or_add_user_role $admin_role $admin_user $demo_tenant
- get_or_add_user_role $another_role $demo_user $demo_tenant
- get_or_add_user_role $member_role $demo_user $invis_tenant
+ get_or_add_user_project_role $member_role $demo_user $demo_tenant
+ get_or_add_user_project_role $admin_role $admin_user $demo_tenant
+ get_or_add_user_project_role $another_role $demo_user $demo_tenant
+ get_or_add_user_project_role $member_role $demo_user $invis_tenant
+
+ get_or_create_group "developers" "default" "openstack developers"
+ get_or_create_group "testers" "default"
# Keystone
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -408,13 +415,18 @@
fi
}
-# Configure the API version for the OpenStack projects.
-# configure_API_version conf_file version [section]
-function configure_API_version {
- local conf_file=$1
- local api_version=$2
- local section=${3:-keystone_authtoken}
- iniset $conf_file $section auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v$api_version
+# Create a user that is capable of verifying keystone tokens for use with auth_token middleware.
+#
+# create_service_user <name> [role]
+#
+# The role defaults to the service role. It is allowed to be provided as optional as historically
+# a lot of projects have configured themselves with the admin or other role here if they are
+# using this user for other purposes beyond simply auth_token middleware.
+function create_service_user {
+ local role=${2:-service}
+
+ local user=$(get_or_create_user "$1" "$SERVICE_PASSWORD")
+ get_or_add_user_project_role "$role" "$user" "$SERVICE_TENANT_NAME"
}
# Configure the service to use the auth token middleware.
@@ -430,19 +442,17 @@
local signing_dir=$3
local section=${4:-keystone_authtoken}
- iniset $conf_file $section auth_host $KEYSTONE_AUTH_HOST
- iniset $conf_file $section auth_port $KEYSTONE_AUTH_PORT
- iniset $conf_file $section auth_protocol $KEYSTONE_AUTH_PROTOCOL
- iniset $conf_file $section identity_uri $KEYSTONE_AUTH_URI
+ iniset $conf_file $section auth_plugin password
+ iniset $conf_file $section auth_url $KEYSTONE_AUTH_URI
+ iniset $conf_file $section username $admin_user
+ iniset $conf_file $section password $SERVICE_PASSWORD
+ iniset $conf_file $section user_domain_id default
+ iniset $conf_file $section project_name $SERVICE_TENANT_NAME
+ iniset $conf_file $section project_domain_id default
+
+ iniset $conf_file $section auth_uri $KEYSTONE_SERVICE_URI
iniset $conf_file $section cafile $SSL_BUNDLE_FILE
- configure_API_version $conf_file $IDENTITY_API_VERSION $section
- iniset $conf_file $section admin_tenant_name $SERVICE_TENANT_NAME
- iniset $conf_file $section admin_user $admin_user
- iniset $conf_file $section admin_password $SERVICE_PASSWORD
iniset $conf_file $section signing_dir $signing_dir
- if [[ -n "$KEYSTONE_TOKEN_HASH_ALGORITHM" ]]; then
- iniset $conf_file keystone_authtoken hash_algorithms "$KEYSTONE_TOKEN_HASH_ALGORITHM"
- fi
}
# init_keystone() - Initialize databases, etc.
@@ -452,7 +462,7 @@
fi
# (Re)create keystone database
- recreate_database keystone utf8
+ recreate_database keystone
# Initialize keystone database
$KEYSTONE_DIR/bin/keystone-manage db_sync
@@ -479,15 +489,19 @@
# install_keystoneclient() - Collect source and prepare
function install_keystoneclient {
- git_clone $KEYSTONECLIENT_REPO $KEYSTONECLIENT_DIR $KEYSTONECLIENT_BRANCH
- setup_develop $KEYSTONECLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$KEYSTONECLIENT_DIR/tools/,/etc/bash_completion.d/}keystone.bash_completion
+ if use_library_from_git "python-keystoneclient"; then
+ git_clone_by_name "python-keystoneclient"
+ setup_dev_lib "python-keystoneclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-keystoneclient"]}/tools/,/etc/bash_completion.d/}keystone.bash_completion
+ fi
}
# install_keystonemiddleware() - Collect source and prepare
function install_keystonemiddleware {
- git_clone $KEYSTONEMIDDLEWARE_REPO $KEYSTONEMIDDLEWARE_DIR $KEYSTONEMIDDLEWARE_BRANCH
- setup_install $KEYSTONEMIDDLEWARE_DIR
+ if use_library_from_git "keystonemiddleware"; then
+ git_clone_by_name "keystonemiddleware"
+ setup_dev_lib "keystonemiddleware"
+ fi
}
# install_keystone() - Collect source and prepare
@@ -533,12 +547,8 @@
tail_log key /var/log/$APACHE_NAME/keystone.log
tail_log key-access /var/log/$APACHE_NAME/keystone_access.log
else
- local EXTRA_PARAMS=""
- if [ "$ENABLE_DEBUG_LOG_LEVEL" == "True" ]; then
- EXTRA_PARAMS="--debug"
- fi
# Start Keystone in a screen window
- run_process key "$KEYSTONE_DIR/bin/keystone-all --config-file $KEYSTONE_CONF $EXTRA_PARAMS"
+ run_process key "$KEYSTONE_DIR/bin/keystone-all --config-file $KEYSTONE_CONF"
fi
echo "Waiting for keystone to start..."
diff --git a/lib/ldap b/lib/ldap
index a6fb82f..d69d3f8 100644
--- a/lib/ldap
+++ b/lib/ldap
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/ldap
# Functions to control the installation and configuration of **ldap**
@@ -51,9 +53,10 @@
# _ldap_varsubst file
function _ldap_varsubst {
local infile=$1
+ local slappass=$2
sed -e "
s|\${LDAP_OLCDB_NUMBER}|$LDAP_OLCDB_NUMBER|
- s|\${SLAPPASS}|$SLAPPASS|
+ s|\${SLAPPASS}|$slappass|
s|\${LDAP_ROOTPW_COMMAND}|$LDAP_ROOTPW_COMMAND|
s|\${BASE_DC}|$LDAP_BASE_DC|
s|\${BASE_DN}|$LDAP_BASE_DN|
@@ -130,7 +133,7 @@
printf "LDAP secret is $slappass\n"
# Create manager.ldif and add to olcdb
- _ldap_varsubst $FILES/ldap/manager.ldif.in >$tmp_ldap_dir/manager.ldif
+ _ldap_varsubst $FILES/ldap/manager.ldif.in $slappass >$tmp_ldap_dir/manager.ldif
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f $tmp_ldap_dir/manager.ldif
# On fedora we need to manually add cosine and inetorgperson schemas
diff --git a/lib/lvm b/lib/lvm
new file mode 100644
index 0000000..ed24487
--- /dev/null
+++ b/lib/lvm
@@ -0,0 +1,143 @@
+# lib/lvm
+# Configure the default LVM volume group used by Cinder and Nova
+
+# Dependencies:
+#
+# - ``functions`` file
+# - ``cinder`` configurations
+
+# DATA_DIR
+
+# clean_default_volume_group - called from clean()
+# configure_default_volume_group - called from configure()
+# init_default_volume_group - called from init()
+
+
+# Save trace setting
+MY_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+# Name of the lvm volume groups to use/create for iscsi volumes
+# This monkey-motion is for compatibility with icehouse-generation Grenade
+# If ``VOLUME_GROUP`` is set, use it, otherwise we'll build a VG name based
+# on ``VOLUME_GROUP_NAME`` that includes the backend name
+# Grenade doesn't use ``VOLUME_GROUP2`` so it is left out
+VOLUME_GROUP_NAME=${VOLUME_GROUP:-${VOLUME_GROUP_NAME:-stack-volumes}}
+DEFAULT_VOLUME_GROUP_NAME=$VOLUME_GROUP_NAME-default
+
+# Backing file name is of the form $VOLUME_GROUP$BACKING_FILE_SUFFIX
+BACKING_FILE_SUFFIX=-backing-file
+
+
+# Entry Points
+# ------------
+
+# _clean_lvm_volume_group removes all default LVM volumes
+#
+# Usage: clean_lvm_volume_group $vg
+function _clean_lvm_volume_group {
+ local vg=$1
+
+ # Clean out existing volumes
+ sudo lvremove -f $vg
+}
+
+# _clean_lvm_backing_file() removes the backing file of the
+# volume group
+#
+# Usage: _clean_lvm_backing_file() $backing_file
+function _clean_lvm_backing_file {
+ local backing_file=$1
+
+ # if the backing physical device is a loop device, it was probably setup by devstack
+ if [[ -n "$backing_file" ]] && [[ -e "$backing_file" ]]; then
+ local vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/'$BACKING_FILE_SUFFIX'/ { print $1}')
+ sudo losetup -d $vg_dev
+ rm -f $backing_file
+ fi
+}
+
+# clean_lvm_volume_group() cleans up the volume group and removes the
+# backing file
+#
+# Usage: clean_lvm_volume_group $vg
+function clean_lvm_volume_group {
+ local vg=$1
+
+ _clean_lvm_volume_group $vg
+ # if there is no logical volume left, it's safe to attempt a cleanup
+ # of the backing file
+ if [[ -z "$(sudo lvs --noheadings -o lv_name $vg 2>/dev/null)" ]]; then
+ _clean_lvm_backing_file $DATA_DIR/$vg$BACKING_FILE_SUFFIX
+ fi
+}
+
+
+# _create_volume_group creates default volume group
+#
+# Usage: _create_lvm_volume_group() $vg $size
+function _create_lvm_volume_group {
+ local vg=$1
+ local size=$2
+
+ local backing_file=$DATA_DIR/$vg$BACKING_FILE_SUFFIX
+ if ! sudo vgs $vg; then
+ # Only create if the file doesn't already exists
+ [[ -f $DATA_DIR/$backing_file ]] || truncate -s $size $backing_file
+ local vg_dev=`sudo losetup -f --show $backing_file`
+
+ # Only create volume group if it doesn't already exist
+ if ! sudo vgs $vg; then
+ sudo vgcreate $vg $vg_dev
+ fi
+ fi
+}
+
+# init_lvm_volume_group() initializes the volume group creating the backing
+# file if necessary
+#
+# Usage: init_lvm_volume_group() $vg
+function init_lvm_volume_group {
+ local vg=$1
+ local size=$2
+ # Start with a clean volume group
+ _create_lvm_volume_group $vg $size
+
+ if is_fedora || is_suse; then
+ # service is not started by default
+ start_service tgtd
+ fi
+
+ # Remove iscsi targets
+ sudo tgtadm --op show --mode target | grep Target | cut -f3 -d ' ' | sudo xargs -n1 tgt-admin --delete || true
+
+ _clean_lvm_volume_group $vg
+}
+
+# Sentinal value to ensure that init of default lvm volume group is
+# only performed once across calls of init_default_lvm_volume_group.
+_DEFAULT_LVM_INIT=${_DEFAULT_LVM_INIT:-0}
+
+# init_default_lvm_volume_group() initializes a default volume group
+# intended to be shared between cinder and nova. It is idempotent;
+# the init of the default volume group is guaranteed to be performed
+# only once so that either or both of the dependent services can
+# safely call this function.
+#
+# Usage: init_default_lvm_volume_group()
+function init_default_lvm_volume_group {
+ if [[ "$_DEFAULT_LVM_INIT" = "0" ]]; then
+ init_lvm_volume_group $DEFAULT_VOLUME_GROUP_NAME $VOLUME_BACKING_FILE_SIZE
+ _DEFAULT_LVM_INIT=1
+ fi
+}
+
+
+# Restore xtrace
+$MY_XTRACE
+
+# mode: shell-script
+# End:
diff --git a/lib/neutron b/lib/neutron
old mode 100644
new mode 100755
index eb07f40..15a5f00
--- a/lib/neutron
+++ b/lib/neutron
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/neutron
# functions - functions specific to neutron
@@ -8,24 +10,25 @@
# ``stack.sh`` calls the entry points in this order:
#
-# - install_neutron
-# - install_neutronclient
# - install_neutron_agent_packages
+# - install_neutronclient
+# - install_neutron
# - install_neutron_third_party
# - configure_neutron
# - init_neutron
# - configure_neutron_third_party
# - init_neutron_third_party
# - start_neutron_third_party
-# - create_neutron_cache_dir
# - create_nova_conf_neutron
# - start_neutron_service_and_check
+# - check_neutron_third_party_integration
# - start_neutron_agents
# - create_neutron_initial_network
# - setup_neutron_debug
#
# ``unstack.sh`` calls the entry points in this order:
#
+# - teardown_neutron_debug
# - stop_neutron
# - stop_neutron_third_party
# - cleanup_neutron
@@ -51,10 +54,22 @@
#
# With Neutron networking the NETWORK_MANAGER variable is ignored.
+# Settings
+# --------
+
+# Timeout value in seconds to wait for IPv6 gateway configuration
+GATEWAY_TIMEOUT=30
+
# Neutron Network Configuration
# -----------------------------
+# Subnet IP version
+IP_VERSION=${IP_VERSION:-4}
+# Validate IP_VERSION
+if [[ $IP_VERSION != "4" ]] && [[ $IP_VERSION != "6" ]] && [[ $IP_VERSION != "4+6" ]]; then
+ die $LINENO "IP_VERSION must be either 4, 6, or 4+6"
+fi
# Gateway and subnet defaults, in case they are not customized in localrc
NETWORK_GATEWAY=${NETWORK_GATEWAY:-10.0.0.1}
PUBLIC_NETWORK_GATEWAY=${PUBLIC_NETWORK_GATEWAY:-172.24.4.1}
@@ -65,10 +80,31 @@
Q_PROTOCOL="https"
fi
+# Generate 40-bit IPv6 Global ID to comply with RFC 4193
+IPV6_GLOBAL_ID=`uuidgen | sed s/-//g | cut -c 23- | sed -e "s/\(..\)\(....\)\(....\)/\1:\2:\3/"`
+
+# IPv6 gateway and subnet defaults, in case they are not customized in localrc
+IPV6_RA_MODE=${IPV6_RA_MODE:-slaac}
+IPV6_ADDRESS_MODE=${IPV6_ADDRESS_MODE:-slaac}
+IPV6_PUBLIC_SUBNET_NAME=${IPV6_PUBLIC_SUBNET_NAME:-ipv6-public-subnet}
+IPV6_PRIVATE_SUBNET_NAME=${IPV6_PRIVATE_SUBNET_NAME:-ipv6-private-subnet}
+FIXED_RANGE_V6=${FIXED_RANGE_V6:-fd$IPV6_GLOBAL_ID::/64}
+IPV6_PRIVATE_NETWORK_GATEWAY=${IPV6_PRIVATE_NETWORK_GATEWAY:-fd$IPV6_GLOBAL_ID::1}
+IPV6_PUBLIC_RANGE=${IPV6_PUBLIC_RANGE:-fe80:cafe:cafe::/64}
+IPV6_PUBLIC_NETWORK_GATEWAY=${IPV6_PUBLIC_NETWORK_GATEWAY:-fe80:cafe:cafe::2}
+# IPV6_ROUTER_GW_IP must be defined when IP_VERSION=4+6 as it cannot be
+# obtained conventionally until the l3-agent has support for dual-stack
+# TODO (john-davidge) Remove once l3-agent supports dual-stack
+IPV6_ROUTER_GW_IP=${IPV6_ROUTER_GW_IP:-fe80:cafe:cafe::1}
# Set up default directories
+GITDIR["python-neutronclient"]=$DEST/python-neutronclient
+
+
NEUTRON_DIR=$DEST/neutron
-NEUTRONCLIENT_DIR=$DEST/python-neutronclient
+NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas
+NEUTRON_LBAAS_DIR=$DEST/neutron-lbaas
+NEUTRON_VPNAAS_DIR=$DEST/neutron-vpnaas
NEUTRON_AUTH_CACHE_DIR=${NEUTRON_AUTH_CACHE_DIR:-/var/cache/neutron}
# Support entry points installation of console scripts
@@ -131,6 +167,8 @@
Q_NOTIFY_NOVA_PORT_DATA_CHANGES=${Q_NOTIFY_NOVA_PORT_DATA_CHANGES:-True}
VIF_PLUGGING_IS_FATAL=${VIF_PLUGGING_IS_FATAL:-True}
VIF_PLUGGING_TIMEOUT=${VIF_PLUGGING_TIMEOUT:-300}
+# Specify if the initial private and external networks should be created
+NEUTRON_CREATE_INITIAL_NETWORKS=${NEUTRON_CREATE_INITIAL_NETWORKS:-True}
## Provider Network Information
PROVIDER_SUBNET_NAME=${PROVIDER_SUBNET_NAME:-"provider_net"}
@@ -470,15 +508,9 @@
# Migrated from keystone_data.sh
function create_neutron_accounts {
-
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local service_role=$(openstack role list | awk "/ service / { print \$2 }")
-
if [[ "$ENABLED_SERVICES" =~ "q-svc" ]]; then
- local neutron_user=$(get_or_create_user "neutron" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $service_role $neutron_user $service_tenant
+ create_service_user "neutron"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -497,38 +529,40 @@
TENANT_ID=$(openstack project list | grep " demo " | get_field 1)
die_if_not_set $LINENO TENANT_ID "Failure retrieving TENANT_ID for demo"
- # Create a small network
- # Since neutron command is executed in admin context at this point,
- # ``--tenant-id`` needs to be specified.
- if is_baremetal; then
- if [[ "$PUBLIC_INTERFACE" == '' || "$OVS_PHYSICAL_BRIDGE" == '' ]]; then
- die $LINENO "Neutron settings for baremetal not set.. exiting"
- fi
- sudo ovs-vsctl add-port $OVS_PHYSICAL_BRIDGE $PUBLIC_INTERFACE
- for IP in $(ip addr show dev $PUBLIC_INTERFACE | grep ' inet ' | awk '{print $2}'); do
- sudo ip addr del $IP dev $PUBLIC_INTERFACE
- sudo ip addr add $IP dev $OVS_PHYSICAL_BRIDGE
- done
- NET_ID=$(neutron net-create $PHYSICAL_NETWORK --tenant-id $TENANT_ID --provider:network_type flat --provider:physical_network "$PHYSICAL_NETWORK" | grep ' id ' | get_field 2)
- die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PHYSICAL_NETWORK $TENANT_ID"
- SUBNET_ID=$(neutron subnet-create --tenant-id $TENANT_ID --ip_version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} --gateway $NETWORK_GATEWAY --name $PRIVATE_SUBNET_NAME $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
- die_if_not_set $LINENO SUBNET_ID "Failure creating SUBNET_ID for $TENANT_ID"
- sudo ifconfig $OVS_PHYSICAL_BRIDGE up
- sudo route add default gw $NETWORK_GATEWAY dev $OVS_PHYSICAL_BRIDGE
- elif is_provider_network; then
+ # Allow drivers that need to create an initial network to do so here
+ if type -p neutron_plugin_create_initial_network_profile > /dev/null; then
+ neutron_plugin_create_initial_network_profile $PHYSICAL_NETWORK
+ fi
+
+ if is_provider_network; then
die_if_not_set $LINENO PHYSICAL_NETWORK "You must specify the PHYSICAL_NETWORK"
die_if_not_set $LINENO PROVIDER_NETWORK_TYPE "You must specifiy the PROVIDER_NETWORK_TYPE"
NET_ID=$(neutron net-create $PHYSICAL_NETWORK --tenant_id $TENANT_ID --provider:network_type $PROVIDER_NETWORK_TYPE --provider:physical_network "$PHYSICAL_NETWORK" ${SEGMENTATION_ID:+--provider:segmentation_id $SEGMENTATION_ID} --shared | grep ' id ' | get_field 2)
- SUBNET_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} --name $PROVIDER_SUBNET_NAME $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
- SUBNET_V6_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 6 --ipv6-address-mode slaac --gateway $V6_NETWORK_GATEWAY --name $PROVIDER_SUBNET_NAME_V6 $NET_ID $FIXED_RANGE_V6 | grep 'id' | get_field 2)
+
+ if [[ "$IP_VERSION" =~ 4.* ]]; then
+ SUBNET_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 4 ${ALLOCATION_POOL:+--allocation-pool $ALLOCATION_POOL} --name $PROVIDER_SUBNET_NAME --gateway $NETWORK_GATEWAY $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
+ fi
+
+ if [[ "$IP_VERSION" =~ .*6 ]]; then
+ SUBNET_V6_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 6 --ipv6-address-mode slaac --gateway $V6_NETWORK_GATEWAY --name $PROVIDER_SUBNET_NAME_V6 $NET_ID $FIXED_RANGE_V6 | grep 'id' | get_field 2)
+ fi
+
sudo ip link set $OVS_PHYSICAL_BRIDGE up
sudo ip link set br-int up
sudo ip link set $PUBLIC_INTERFACE up
else
NET_ID=$(neutron net-create --tenant-id $TENANT_ID "$PRIVATE_NETWORK_NAME" | grep ' id ' | get_field 2)
die_if_not_set $LINENO NET_ID "Failure creating NET_ID for $PHYSICAL_NETWORK $TENANT_ID"
- SUBNET_ID=$(neutron subnet-create --tenant-id $TENANT_ID --ip_version 4 --gateway $NETWORK_GATEWAY --name $PRIVATE_SUBNET_NAME $NET_ID $FIXED_RANGE | grep ' id ' | get_field 2)
- die_if_not_set $LINENO SUBNET_ID "Failure creating SUBNET_ID for $TENANT_ID"
+
+ if [[ "$IP_VERSION" =~ 4.* ]]; then
+ # Create IPv4 private subnet
+ SUBNET_ID=$(_neutron_create_private_subnet_v4)
+ fi
+
+ if [[ "$IP_VERSION" =~ .*6 ]]; then
+ # Create IPv6 private subnet
+ IPV6_SUBNET_ID=$(_neutron_create_private_subnet_v6)
+ fi
fi
if [[ "$Q_L3_ENABLED" == "True" ]]; then
@@ -542,7 +576,7 @@
ROUTER_ID=$(neutron router-create $Q_ROUTER_NAME | grep ' id ' | get_field 2)
die_if_not_set $LINENO ROUTER_ID "Failure creating ROUTER_ID for $Q_ROUTER_NAME"
fi
- neutron router-interface-add $ROUTER_ID $SUBNET_ID
+
# Create an external network, and a subnet. Configure the external network as router gw
if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True --provider:network_type=flat --provider:physical_network=${PUBLIC_PHYSICAL_NETWORK} | grep ' id ' | get_field 2)
@@ -550,50 +584,52 @@
EXT_NET_ID=$(neutron net-create "$PUBLIC_NETWORK_NAME" -- --router:external=True | grep ' id ' | get_field 2)
fi
die_if_not_set $LINENO EXT_NET_ID "Failure creating EXT_NET_ID for $PUBLIC_NETWORK_NAME"
- EXT_GW_IP=$(neutron subnet-create --ip_version 4 ${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} --gateway $PUBLIC_NETWORK_GATEWAY --name $PUBLIC_SUBNET_NAME $EXT_NET_ID $FLOATING_RANGE -- --enable_dhcp=False | grep 'gateway_ip' | get_field 2)
- die_if_not_set $LINENO EXT_GW_IP "Failure creating EXT_GW_IP"
- neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
- if is_service_enabled q-l3; then
- # logic is specific to using the l3-agent for l3
- if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
- local ext_gw_interface
+ if [[ "$IP_VERSION" =~ 4.* ]]; then
+ # Configure router for IPv4 public access
+ _neutron_configure_router_v4
+ fi
- if [[ "$Q_USE_PUBLIC_VETH" = "True" ]]; then
- ext_gw_interface=$Q_PUBLIC_VETH_EX
- else
- # Disable in-band as we are going to use local port
- # to communicate with VMs
- sudo ovs-vsctl set Bridge $PUBLIC_BRIDGE \
- other_config:disable-in-band=true
- ext_gw_interface=$PUBLIC_BRIDGE
- fi
- CIDR_LEN=${FLOATING_RANGE#*/}
- sudo ip addr add $EXT_GW_IP/$CIDR_LEN dev $ext_gw_interface
- sudo ip link set $ext_gw_interface up
- ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' '{ print $8; }'`
- die_if_not_set $LINENO ROUTER_GW_IP "Failure retrieving ROUTER_GW_IP"
- sudo route add -net $FIXED_RANGE gw $ROUTER_GW_IP
- fi
- if [[ "$Q_USE_NAMESPACE" == "False" ]]; then
- # Explicitly set router id in l3 agent configuration
- iniset $Q_L3_CONF_FILE DEFAULT router_id $ROUTER_ID
- fi
+ if [[ "$IP_VERSION" =~ .*6 ]]; then
+ # Configure router for IPv6 public access
+ _neutron_configure_router_v6
fi
fi
}
# init_neutron() - Initialize databases, etc.
function init_neutron {
- recreate_database $Q_DB_NAME utf8
+ recreate_database $Q_DB_NAME
# Run Neutron db migrations
$NEUTRON_BIN_DIR/neutron-db-manage --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE upgrade head
+ for svc in fwaas lbaas vpnaas; do
+ if [ "$svc" = "vpnaas" ]; then
+ q_svc="q-vpn"
+ else
+ q_svc="q-$svc"
+ fi
+ if is_service_enabled $q_svc; then
+ $NEUTRON_BIN_DIR/neutron-db-manage --service $svc --config-file $NEUTRON_CONF --config-file /$Q_PLUGIN_CONF_FILE upgrade head
+ fi
+ done
}
# install_neutron() - Collect source and prepare
function install_neutron {
git_clone $NEUTRON_REPO $NEUTRON_DIR $NEUTRON_BRANCH
setup_develop $NEUTRON_DIR
+ if is_service_enabled q-fwaas; then
+ git_clone $NEUTRON_FWAAS_REPO $NEUTRON_FWAAS_DIR $NEUTRON_FWAAS_BRANCH
+ setup_develop $NEUTRON_FWAAS_DIR
+ fi
+ if is_service_enabled q-lbaas; then
+ git_clone $NEUTRON_LBAAS_REPO $NEUTRON_LBAAS_DIR $NEUTRON_LBAAS_BRANCH
+ setup_develop $NEUTRON_LBAAS_DIR
+ fi
+ if is_service_enabled q-vpn; then
+ git_clone $NEUTRON_VPNAAS_REPO $NEUTRON_VPNAAS_DIR $NEUTRON_VPNAAS_BRANCH
+ setup_develop $NEUTRON_VPNAAS_DIR
+ fi
if [ "$VIRT_DRIVER" == 'xenserver' ]; then
local dom0_ip
@@ -616,9 +652,11 @@
# install_neutronclient() - Collect source and prepare
function install_neutronclient {
- git_clone $NEUTRONCLIENT_REPO $NEUTRONCLIENT_DIR $NEUTRONCLIENT_BRANCH
- setup_develop $NEUTRONCLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$NEUTRONCLIENT_DIR/tools/,/etc/bash_completion.d/}neutron.bash_completion
+ if use_library_from_git "python-neutronclient"; then
+ git_clone_by_name "python-neutronclient"
+ setup_dev_lib "python-neutronclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-neutronclient"]}/tools/,/etc/bash_completion.d/}neutron.bash_completion
+ fi
}
# install_neutron_agent_packages() - Collect source and prepare
@@ -668,10 +706,17 @@
run_process q-dhcp "python $AGENT_DHCP_BINARY --config-file $NEUTRON_CONF --config-file=$Q_DHCP_CONF_FILE"
if is_provider_network; then
- sudo ovs-vsctl add-port $OVS_PHYSICAL_BRIDGE $PUBLIC_INTERFACE
+ sudo ovs-vsctl --no-wait -- --may-exist add-port $OVS_PHYSICAL_BRIDGE $PUBLIC_INTERFACE
sudo ip link set $OVS_PHYSICAL_BRIDGE up
sudo ip link set br-int up
sudo ip link set $PUBLIC_INTERFACE up
+ if is_ironic_hardware; then
+ for IP in $(ip addr show dev $PUBLIC_INTERFACE | grep ' inet ' | awk '{print $2}'); do
+ sudo ip addr del $IP dev $PUBLIC_INTERFACE
+ sudo ip addr add $IP dev $OVS_PHYSICAL_BRIDGE
+ done
+ sudo route add -net $FIXED_RANGE gw $NETWORK_GATEWAY dev $OVS_PHYSICAL_BRIDGE
+ fi
fi
if is_service_enabled q-vpn; then
@@ -699,13 +744,21 @@
# stop_neutron() - Stop running processes (non-screen)
function stop_neutron {
if is_service_enabled q-dhcp; then
+ stop_process q-dhcp
pid=$(ps aux | awk '/[d]nsmasq.+interface=(tap|ns-)/ { print $2 }')
[ ! -z "$pid" ] && sudo kill -9 $pid
fi
+
+ stop_process q-svc
+ stop_process q-l3
+
if is_service_enabled q-meta; then
sudo pkill -9 -f neutron-ns-metadata-proxy || :
+ stop_process q-meta
fi
+ stop_process q-agt
+
if is_service_enabled q-lbaas; then
neutron_lbaas_stop
fi
@@ -723,6 +776,14 @@
# cleanup_neutron() - Remove residual data files, anything left over from previous
# runs that a clean run would need to clean up
function cleanup_neutron {
+ if is_provider_network && is_ironic_hardware; then
+ for IP in $(ip addr show dev $OVS_PHYSICAL_BRIDGE | grep ' inet ' | awk '{print $2}'); do
+ sudo ip addr del $IP dev $OVS_PHYSICAL_BRIDGE
+ sudo ip addr add $IP dev $PUBLIC_INTERFACE
+ done
+ sudo route del -net $FIXED_RANGE gw $NETWORK_GATEWAY dev $OVS_PHYSICAL_BRIDGE
+ fi
+
if is_neutron_ovs_base_plugin; then
neutron_ovs_base_cleanup
fi
@@ -733,15 +794,20 @@
done
}
-# _configure_neutron_common()
-# Set common config for all neutron server and agents.
-# This MUST be called before other ``_configure_neutron_*`` functions.
-function _configure_neutron_common {
+
+function _create_neutron_conf_dir {
# Put config files in ``NEUTRON_CONF_DIR`` for everyone to find
if [[ ! -d $NEUTRON_CONF_DIR ]]; then
sudo mkdir -p $NEUTRON_CONF_DIR
fi
sudo chown $STACK_USER $NEUTRON_CONF_DIR
+}
+
+# _configure_neutron_common()
+# Set common config for all neutron server and agents.
+# This MUST be called before other ``_configure_neutron_*`` functions.
+function _configure_neutron_common {
+ _create_neutron_conf_dir
cp $NEUTRON_DIR/etc/neutron.conf $NEUTRON_CONF
@@ -764,7 +830,7 @@
iniset $NEUTRON_CONF database connection `database_connection_url $Q_DB_NAME`
iniset $NEUTRON_CONF DEFAULT state_path $DATA_DIR/neutron
-
+ iniset $NEUTRON_CONF DEFAULT use_syslog $SYSLOG
# If addition config files are set, make sure their path name is set as well
if [[ ${#Q_PLUGIN_EXTRA_CONF_FILES[@]} > 0 && $Q_PLUGIN_EXTRA_CONF_PATH == '' ]]; then
die $LINENO "Neutron additional plugin config not set.. exiting"
@@ -791,6 +857,9 @@
# Format logging
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
setup_colorized_logging $NEUTRON_CONF DEFAULT project_id
+ else
+ # Show user_name and project_name by default like in nova
+ iniset $NEUTRON_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
fi
if is_service_enabled tls-proxy; then
@@ -862,7 +931,7 @@
Q_L3_ROUTER_PER_TENANT=$Q_USE_NAMESPACE
if is_service_enabled q-vpn; then
- cp $NEUTRON_DIR/etc/vpn_agent.ini $Q_VPN_CONF_FILE
+ cp $NEUTRON_VPNAAS_DIR/etc/vpn_agent.ini $Q_VPN_CONF_FILE
fi
cp $NEUTRON_DIR/etc/l3_agent.ini $Q_L3_CONF_FILE
@@ -885,7 +954,9 @@
iniset $Q_META_CONF_FILE DEFAULT nova_metadata_ip $Q_META_DATA_IP
iniset $Q_META_CONF_FILE DEFAULT root_helper "$Q_RR_COMMAND"
- _neutron_setup_keystone $Q_META_CONF_FILE DEFAULT
+ # Configures keystone for metadata_agent
+ # The third argument "True" sets auth_url needed to communicate with keystone
+ _neutron_setup_keystone $Q_META_CONF_FILE DEFAULT True
}
@@ -894,6 +965,9 @@
}
function _configure_neutron_lbaas {
+ if [ -f $NEUTRON_LBAAS_DIR/etc/neutron_lbaas.conf ]; then
+ cp $NEUTRON_LBAAS_DIR/etc/neutron_lbaas.conf $NEUTRON_CONF_DIR
+ fi
neutron_agent_lbaas_configure_common
neutron_agent_lbaas_configure_agent
}
@@ -904,11 +978,17 @@
}
function _configure_neutron_fwaas {
+ if [ -f $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf ]; then
+ cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf $NEUTRON_CONF_DIR
+ fi
neutron_fwaas_configure_common
neutron_fwaas_configure_driver
}
function _configure_neutron_vpn {
+ if [ -f $NEUTRON_VPNAAS_DIR/etc/neutron_vpnaas.conf ]; then
+ cp $NEUTRON_VPNAAS_DIR/etc/neutron_vpnaas.conf $NEUTRON_CONF_DIR
+ fi
neutron_vpn_install_agent_packages
neutron_vpn_configure_common
}
@@ -963,6 +1043,7 @@
iniset $NEUTRON_CONF DEFAULT notify_nova_on_port_status_changes $Q_NOTIFY_NOVA_PORT_STATUS_CHANGES
iniset $NEUTRON_CONF DEFAULT notify_nova_on_port_data_changes $Q_NOTIFY_NOVA_PORT_DATA_CHANGES
iniset $NEUTRON_CONF DEFAULT nova_url "$NOVA_SERVICE_PROTOCOL://$NOVA_SERVICE_HOST:$NOVA_SERVICE_PORT/v2"
+ iniset $NEUTRON_CONF DEFAULT nova_region_name $REGION_NAME
iniset $NEUTRON_CONF DEFAULT nova_admin_username nova
iniset $NEUTRON_CONF DEFAULT nova_admin_password $SERVICE_PASSWORD
ADMIN_TENANT_ID=$(openstack project list | awk "/ service / { print \$2 }")
@@ -986,22 +1067,28 @@
fi
}
+# _neutron_deploy_rootwrap_filters() - deploy rootwrap filters to $Q_CONF_ROOTWRAP_D (owned by root).
+function _neutron_deploy_rootwrap_filters {
+ local srcdir=$1
+ mkdir -p -m 755 $Q_CONF_ROOTWRAP_D
+ sudo cp -pr $srcdir/etc/neutron/rootwrap.d/* $Q_CONF_ROOTWRAP_D/
+ sudo chown -R root:root $Q_CONF_ROOTWRAP_D
+ sudo chmod 644 $Q_CONF_ROOTWRAP_D/*
+}
+
# _neutron_setup_rootwrap() - configure Neutron's rootwrap
function _neutron_setup_rootwrap {
if [[ "$Q_USE_ROOTWRAP" == "False" ]]; then
return
fi
- # Deploy new rootwrap filters files (owned by root).
# Wipe any existing ``rootwrap.d`` files first
Q_CONF_ROOTWRAP_D=$NEUTRON_CONF_DIR/rootwrap.d
if [[ -d $Q_CONF_ROOTWRAP_D ]]; then
sudo rm -rf $Q_CONF_ROOTWRAP_D
fi
- # Deploy filters to ``$NEUTRON_CONF_DIR/rootwrap.d``
- mkdir -p -m 755 $Q_CONF_ROOTWRAP_D
- cp -pr $NEUTRON_DIR/etc/neutron/rootwrap.d/* $Q_CONF_ROOTWRAP_D/
- sudo chown -R root:root $Q_CONF_ROOTWRAP_D
- sudo chmod 644 $Q_CONF_ROOTWRAP_D/*
+
+ _neutron_deploy_rootwrap_filters $NEUTRON_DIR
+
# Set up ``rootwrap.conf``, pointing to ``$NEUTRON_CONF_DIR/rootwrap.d``
# location moved in newer versions, prefer new location
if test -r $NEUTRON_DIR/etc/neutron/rootwrap.conf; then
@@ -1030,6 +1117,13 @@
function _neutron_setup_keystone {
local conf_file=$1
local section=$2
+ local use_auth_url=$3
+
+ # Configures keystone for metadata_agent
+ # metadata_agent needs auth_url to communicate with keystone
+ if [[ "$use_auth_url" == "True" ]]; then
+ iniset $conf_file $section auth_url $KEYSTONE_SERVICE_URI/v2.0
+ fi
create_neutron_cache_dir
configure_auth_token_middleware $conf_file $Q_ADMIN_USERNAME $NEUTRON_AUTH_CACHE_DIR $section
@@ -1044,6 +1138,178 @@
neutron_plugin_setup_interface_driver $1
}
+# Create private IPv4 subnet
+function _neutron_create_private_subnet_v4 {
+ local subnet_params="--tenant-id $TENANT_ID "
+ subnet_params+="--ip_version 4 "
+ subnet_params+="--gateway $NETWORK_GATEWAY "
+ subnet_params+="--name $PRIVATE_SUBNET_NAME "
+ subnet_params+="$NET_ID $FIXED_RANGE"
+ local subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
+ die_if_not_set $LINENO subnet_id "Failure creating private IPv4 subnet for $TENANT_ID"
+ echo $subnet_id
+}
+
+# Create private IPv6 subnet
+function _neutron_create_private_subnet_v6 {
+ die_if_not_set $LINENO IPV6_RA_MODE "IPV6 RA Mode not set"
+ die_if_not_set $LINENO IPV6_ADDRESS_MODE "IPV6 Address Mode not set"
+ local ipv6_modes="--ipv6-ra-mode $IPV6_RA_MODE --ipv6-address-mode $IPV6_ADDRESS_MODE"
+ local subnet_params="--tenant-id $TENANT_ID "
+ subnet_params+="--ip_version 6 "
+ subnet_params+="--gateway $IPV6_PRIVATE_NETWORK_GATEWAY "
+ subnet_params+="--name $IPV6_PRIVATE_SUBNET_NAME "
+ subnet_params+="$NET_ID $FIXED_RANGE_V6 $ipv6_modes"
+ local ipv6_subnet_id=$(neutron subnet-create $subnet_params | grep ' id ' | get_field 2)
+ die_if_not_set $LINENO ipv6_subnet_id "Failure creating private IPv6 subnet for $TENANT_ID"
+ echo $ipv6_subnet_id
+}
+
+# Create public IPv4 subnet
+function _neutron_create_public_subnet_v4 {
+ local subnet_params+="--ip_version 4 "
+ subnet_params+="${Q_FLOATING_ALLOCATION_POOL:+--allocation-pool $Q_FLOATING_ALLOCATION_POOL} "
+ subnet_params+="--gateway $PUBLIC_NETWORK_GATEWAY "
+ subnet_params+="--name $PUBLIC_SUBNET_NAME "
+ subnet_params+="$EXT_NET_ID $FLOATING_RANGE "
+ subnet_params+="-- --enable_dhcp=False"
+ local id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
+ die_if_not_set $LINENO id_and_ext_gw_ip "Failure creating public IPv4 subnet"
+ echo $id_and_ext_gw_ip
+}
+
+# Create public IPv6 subnet
+function _neutron_create_public_subnet_v6 {
+ local subnet_params="--ip_version 6 "
+ subnet_params+="--gateway $IPV6_PUBLIC_NETWORK_GATEWAY "
+ subnet_params+="--name $IPV6_PUBLIC_SUBNET_NAME "
+ subnet_params+="$EXT_NET_ID $IPV6_PUBLIC_RANGE "
+ subnet_params+="-- --enable_dhcp=False"
+ local ipv6_id_and_ext_gw_ip=$(neutron subnet-create $subnet_params | grep -e 'gateway_ip' -e ' id ')
+ die_if_not_set $LINENO ipv6_id_and_ext_gw_ip "Failure creating an IPv6 public subnet"
+ echo $ipv6_id_and_ext_gw_ip
+}
+
+# Configure neutron router for IPv4 public access
+function _neutron_configure_router_v4 {
+ neutron router-interface-add $ROUTER_ID $SUBNET_ID
+ # Create a public subnet on the external network
+ local id_and_ext_gw_ip=$(_neutron_create_public_subnet_v4 $EXT_NET_ID)
+ local ext_gw_ip=$(echo $id_and_ext_gw_ip | get_field 2)
+ PUB_SUBNET_ID=$(echo $id_and_ext_gw_ip | get_field 5)
+ # Configure the external network as the default router gateway
+ neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
+
+ # This logic is specific to using the l3-agent for layer 3
+ if is_service_enabled q-l3; then
+ # Configure and enable public bridge
+ if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
+ local ext_gw_interface=$(_neutron_get_ext_gw_interface)
+ local cidr_len=${FLOATING_RANGE#*/}
+ sudo ip addr add $ext_gw_ip/$cidr_len dev $ext_gw_interface
+ sudo ip link set $ext_gw_interface up
+ ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$PUB_SUBNET_ID '$4 == subnet_id { print $8; }'`
+ die_if_not_set $LINENO ROUTER_GW_IP "Failure retrieving ROUTER_GW_IP"
+ sudo route add -net $FIXED_RANGE gw $ROUTER_GW_IP
+ fi
+ _neutron_set_router_id
+ fi
+}
+
+# Configure neutron router for IPv6 public access
+function _neutron_configure_router_v6 {
+ neutron router-interface-add $ROUTER_ID $IPV6_SUBNET_ID
+ # Create a public subnet on the external network
+ local ipv6_id_and_ext_gw_ip=$(_neutron_create_public_subnet_v6 $EXT_NET_ID)
+ local ipv6_ext_gw_ip=$(echo $ipv6_id_and_ext_gw_ip | get_field 2)
+ local ipv6_pub_subnet_id=$(echo $ipv6_id_and_ext_gw_ip | get_field 5)
+
+ # If the external network has not already been set as the default router
+ # gateway when configuring an IPv4 public subnet, do so now
+ if [[ "$IP_VERSION" == "6" ]]; then
+ neutron router-gateway-set $ROUTER_ID $EXT_NET_ID
+ fi
+
+ # This logic is specific to using the l3-agent for layer 3
+ if is_service_enabled q-l3; then
+ local ipv6_router_gw_port
+ # Ensure IPv6 forwarding is enabled on the host
+ sudo sysctl -w net.ipv6.conf.all.forwarding=1
+ # Configure and enable public bridge
+ if [[ "$IP_VERSION" = "6" ]]; then
+ # Override global IPV6_ROUTER_GW_IP with the true value from neutron
+ IPV6_ROUTER_GW_IP=`neutron port-list -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$ipv6_pub_subnet_id '$4 == subnet_id { print $8; }'`
+ die_if_not_set $LINENO IPV6_ROUTER_GW_IP "Failure retrieving IPV6_ROUTER_GW_IP"
+ ipv6_router_gw_port=`neutron port-list -c id -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$ipv6_pub_subnet_id '$4 == subnet_id { print $1; }' | awk -F ' | ' '{ print $2; }'`
+ die_if_not_set $LINENO ipv6_router_gw_port "Failure retrieving ipv6_router_gw_port"
+ else
+ ipv6_router_gw_port=`neutron port-list -c id -c fixed_ips -c device_owner | grep router_gateway | awk -F '"' -v subnet_id=$PUB_SUBNET_ID '$4 == subnet_id { print $1; }' | awk -F ' | ' '{ print $2; }'`
+ die_if_not_set $LINENO ipv6_router_gw_port "Failure retrieving ipv6_router_gw_port"
+ fi
+
+ # The ovs_base_configure_l3_agent function flushes the public
+ # bridge's ip addresses, so turn IPv6 support in the host off
+ # and then on to recover the public bridge's link local address
+ sudo sysctl -w net.ipv6.conf.${PUBLIC_BRIDGE}.disable_ipv6=1
+ sudo sysctl -w net.ipv6.conf.${PUBLIC_BRIDGE}.disable_ipv6=0
+ if ! ip -6 addr show dev $PUBLIC_BRIDGE | grep 'scope global'; then
+ # Create an IPv6 ULA address for PUBLIC_BRIDGE if one is not present
+ IPV6_BRIDGE_ULA=`uuidgen | sed s/-//g | cut -c 23- | sed -e "s/\(..\)\(....\)\(....\)/\1:\2:\3/"`
+ sudo ip -6 addr add fd$IPV6_BRIDGE_ULA::1 dev $PUBLIC_BRIDGE
+ fi
+
+ if is_neutron_ovs_base_plugin && [[ "$Q_USE_NAMESPACE" = "True" ]]; then
+ local ext_gw_interface=$(_neutron_get_ext_gw_interface)
+ local ipv6_cidr_len=${IPV6_PUBLIC_RANGE#*/}
+
+ # Define router_ns based on whether DVR is enabled
+ local router_ns=qrouter
+ if [[ "$Q_DVR_MODE" == "dvr_snat" ]]; then
+ router_ns=snat
+ fi
+
+ # Configure interface for public bridge
+ sudo ip -6 addr add $ipv6_ext_gw_ip/$ipv6_cidr_len dev $ext_gw_interface
+
+ # Wait until layer 3 agent has configured the gateway port on
+ # the public bridge, then add gateway address to the interface
+ # TODO (john-davidge) Remove once l3-agent supports dual-stack
+ if [[ "$IP_VERSION" == "4+6" ]]; then
+ if ! timeout $GATEWAY_TIMEOUT sh -c "until sudo ip netns exec $router_ns-$ROUTER_ID ip addr show qg-${ipv6_router_gw_port:0:11} | grep $ROUTER_GW_IP; do sleep 1; done"; then
+ die $LINENO "Timeout retrieving ROUTER_GW_IP"
+ fi
+ # Configure the gateway port with the public IPv6 adress
+ sudo ip netns exec $router_ns-$ROUTER_ID ip -6 addr add $IPV6_ROUTER_GW_IP/$ipv6_cidr_len dev qg-${ipv6_router_gw_port:0:11}
+ # Add a default IPv6 route to the neutron router as the
+ # l3-agent does not add one in the dual-stack case
+ sudo ip netns exec $router_ns-$ROUTER_ID ip -6 route replace default via $ipv6_ext_gw_ip dev qg-${ipv6_router_gw_port:0:11}
+ fi
+ sudo ip -6 route add $FIXED_RANGE_V6 via $IPV6_ROUTER_GW_IP dev $ext_gw_interface
+ fi
+ _neutron_set_router_id
+ fi
+}
+
+# Explicitly set router id in l3 agent configuration
+function _neutron_set_router_id {
+ if [[ "$Q_USE_NAMESPACE" == "False" ]]; then
+ iniset $Q_L3_CONF_FILE DEFAULT router_id $ROUTER_ID
+ fi
+}
+
+# Get ext_gw_interface depending on value of Q_USE_PUBLIC_VETH
+function _neutron_get_ext_gw_interface {
+ if [[ "$Q_USE_PUBLIC_VETH" == "True" ]]; then
+ echo $Q_PUBLIC_VETH_EX
+ else
+ # Disable in-band as we are going to use local port
+ # to communicate with VMs
+ sudo ovs-vsctl set Bridge $PUBLIC_BRIDGE \
+ other_config:disable-in-band=true
+ echo $PUBLIC_BRIDGE
+ fi
+}
+
# Functions for Neutron Exercises
#--------------------------------
diff --git a/lib/neutron_plugins/bigswitch_floodlight b/lib/neutron_plugins/bigswitch_floodlight
index 9e84f2e..4166131 100644
--- a/lib/neutron_plugins/bigswitch_floodlight
+++ b/lib/neutron_plugins/bigswitch_floodlight
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neuton Big Switch/FloodLight plugin
# ------------------------------------
diff --git a/lib/neutron_plugins/brocade b/lib/neutron_plugins/brocade
index 511fb71..b8166d9 100644
--- a/lib/neutron_plugins/brocade
+++ b/lib/neutron_plugins/brocade
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Brocade Neutron Plugin
# ----------------------
diff --git a/lib/neutron_plugins/cisco b/lib/neutron_plugins/cisco
index 95e0ab3..90dcd57 100644
--- a/lib/neutron_plugins/cisco
+++ b/lib/neutron_plugins/cisco
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron Cisco plugin
# ---------------------------
@@ -142,6 +144,10 @@
fi
}
+function neutron_plugin_create_initial_network_profile {
+ neutron cisco-network-profile-create default_network_profile vlan --segment_range 1-3000 --physical_network "$1"
+}
+
function neutron_plugin_setup_interface_driver {
local conf_file=$1
iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
diff --git a/lib/neutron_plugins/embrane b/lib/neutron_plugins/embrane
index 7dafdc0..6b4819e 100644
--- a/lib/neutron_plugins/embrane
+++ b/lib/neutron_plugins/embrane
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron Embrane plugin
# ---------------------------
diff --git a/lib/neutron_plugins/ibm b/lib/neutron_plugins/ibm
index 39b0040..3660a9f 100644
--- a/lib/neutron_plugins/ibm
+++ b/lib/neutron_plugins/ibm
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron IBM SDN-VE plugin
# ---------------------------
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
new file mode 100644
index 0000000..c9ea1ca
--- /dev/null
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Neutron Linux Bridge L2 agent
+# -----------------------------
+
+# Save trace setting
+PLUGIN_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+function is_neutron_ovs_base_plugin {
+ # linuxbridge doesn't use OVS
+ return 1
+}
+
+function neutron_plugin_create_nova_conf {
+ :
+}
+
+function neutron_plugin_install_agent_packages {
+ install_package bridge-utils
+}
+
+function neutron_plugin_configure_debug_command {
+ iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge
+}
+
+function neutron_plugin_configure_dhcp_agent {
+ iniset $Q_DHCP_CONF_FILE DEFAULT dhcp_agent_manager neutron.agent.dhcp_agent.DhcpAgentWithStateReport
+}
+
+function neutron_plugin_configure_l3_agent {
+ iniset $Q_L3_CONF_FILE DEFAULT external_network_bridge
+ iniset $Q_L3_CONF_FILE DEFAULT l3_agent_manager neutron.agent.l3_agent.L3NATAgentWithStateReport
+}
+
+function neutron_plugin_configure_plugin_agent {
+ # Setup physical network interface mappings. Override
+ # ``LB_VLAN_RANGES`` and ``LB_INTERFACE_MAPPINGS`` in ``localrc`` for more
+ # complex physical network configurations.
+ if [[ "$LB_INTERFACE_MAPPINGS" == "" ]] && [[ "$PHYSICAL_NETWORK" != "" ]] && [[ "$LB_PHYSICAL_INTERFACE" != "" ]]; then
+ LB_INTERFACE_MAPPINGS=$PHYSICAL_NETWORK:$LB_PHYSICAL_INTERFACE
+ fi
+ if [[ "$LB_INTERFACE_MAPPINGS" != "" ]]; then
+ iniset /$Q_PLUGIN_CONF_FILE linux_bridge physical_interface_mappings $LB_INTERFACE_MAPPINGS
+ fi
+ if [[ "$Q_USE_SECGROUP" == "True" ]]; then
+ iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
+ else
+ iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
+ fi
+ AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
+ iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
+}
+
+function neutron_plugin_setup_interface_driver {
+ local conf_file=$1
+ iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.BridgeInterfaceDriver
+}
+
+function neutron_plugin_check_adv_test_requirements {
+ is_service_enabled q-agt && is_service_enabled q-dhcp && return 0
+}
+
+# Restore xtrace
+$PLUGIN_XTRACE
diff --git a/lib/neutron_plugins/midonet b/lib/neutron_plugins/midonet
index 6ccd502..23ad8b2 100644
--- a/lib/neutron_plugins/midonet
+++ b/lib/neutron_plugins/midonet
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron MidoNet plugin
# ----------------------
@@ -27,6 +29,18 @@
Q_PLUGIN_CONF_PATH=etc/neutron/plugins/midonet
Q_PLUGIN_CONF_FILENAME=midonet.ini
Q_PLUGIN_CLASS="neutron.plugins.midonet.plugin.MidonetPluginV2"
+
+ # MidoNet implements LBaaS API in the plugin, not as an LBaaS driver.
+ # In this model, the plugin references the 'neutron_lbaas' module but
+ # does not require starting an LBaaS service. Devstack, however, clones
+ # 'neutron_lbaas' only if 'lbaas' service is enabled. To get around this,
+ # always clone 'neutron_lbaas' so that it is made available to the plugin.
+ # Also, discontinue if the 'lbaas' service is enabled.
+ if is_service_enabled q-lbaas; then
+ die $LINENO "LBaaS service should be disabled for the MidoNet plugin"
+ fi
+ git_clone $NEUTRON_LBAAS_REPO $NEUTRON_LBAAS_DIR $NEUTRON_LBAAS_BRANCH
+ setup_develop $NEUTRON_LBAAS_DIR
}
function neutron_plugin_configure_debug_command {
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
index 44b947f..e3b2c4d 100644
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron Modular Layer 2 plugin
# ------------------------------
@@ -84,6 +86,11 @@
fi
fi
+
+ # Allow for setup the flat type network
+ if [[ -z "$Q_ML2_PLUGIN_FLAT_TYPE_OPTIONS" && -n "$PHYSICAL_NETWORK" ]]; then
+ Q_ML2_PLUGIN_FLAT_TYPE_OPTIONS="flat_networks=$Q_ML2_FLAT_PHYSNET_OPTIONS"
+ fi
# REVISIT(rkukura): Setting firewall_driver here for
# neutron.agent.securitygroups_rpc.is_firewall_enabled() which is
# used in the server, in case no L2 agent is configured on the
@@ -110,6 +117,8 @@
populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_type_vxlan $Q_ML2_PLUGIN_VXLAN_TYPE_OPTIONS
+ populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_type_flat $Q_ML2_PLUGIN_FLAT_TYPE_OPTIONS
+
populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_type_vlan $Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS
if [[ "$Q_DVR_MODE" != "legacy" ]]; then
diff --git a/lib/neutron_plugins/nec b/lib/neutron_plugins/nec
index f8d98c3..3b1a257 100644
--- a/lib/neutron_plugins/nec
+++ b/lib/neutron_plugins/nec
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron NEC OpenFlow plugin
# ---------------------------
diff --git a/lib/neutron_plugins/nuage b/lib/neutron_plugins/nuage
index 52d85a2..7bce233 100644
--- a/lib/neutron_plugins/nuage
+++ b/lib/neutron_plugins/nuage
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Nuage Neutron Plugin
# ----------------------
@@ -7,7 +9,7 @@
function neutron_plugin_create_nova_conf {
NOVA_OVS_BRIDGE=${NOVA_OVS_BRIDGE:-"br-int"}
- iniset $NOVA_CONF DEFAULT neutron_ovs_bridge $NOVA_OVS_BRIDGE
+ iniset $NOVA_CONF neutron ovs_bridge $NOVA_OVS_BRIDGE
NOVA_VIF_DRIVER=${NOVA_VIF_DRIVER:-"nova.virt.libvirt.vif.LibvirtGenericVIFDriver"}
LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
index 55f3f72..d38fbeb 100644
--- a/lib/neutron_plugins/ofagent_agent
+++ b/lib/neutron_plugins/ofagent_agent
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# OpenFlow Agent plugin
# ----------------------
@@ -18,7 +20,6 @@
# This agent uses ryu to talk with switches
install_package $(get_packages "ryu")
install_ryu
- configure_ryu
}
function neutron_plugin_configure_debug_command {
@@ -83,13 +84,6 @@
AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-ofagent-agent"
iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
- # Define extra "AGENT" configuration options when q-agt is configured by defining
- # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
- # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
- for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
- # Replace the first '=' with ' ' for iniset syntax
- iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
- done
}
function neutron_plugin_setup_interface_driver {
diff --git a/lib/neutron_plugins/oneconvergence b/lib/neutron_plugins/oneconvergence
index 4fd8c7c..48a368a 100644
--- a/lib/neutron_plugins/oneconvergence
+++ b/lib/neutron_plugins/oneconvergence
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron One Convergence plugin
# ------------------------------
diff --git a/lib/neutron_plugins/openvswitch b/lib/neutron_plugins/openvswitch
index 3b6567c..891ab49 100644
--- a/lib/neutron_plugins/openvswitch
+++ b/lib/neutron_plugins/openvswitch
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Common code used by cisco and embrane plugins
# ---------------------------------------------
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
index e1a6f4a..1d24f3b 100644
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron Open vSwitch L2 agent
# -----------------------------
@@ -102,20 +104,6 @@
iniset "/$Q_PLUGIN_CONF_FILE.domU" agent root_helper "$Q_RR_COMMAND"
fi
iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
- # Define extra "AGENT" configuration options when q-agt is configured by defining
- # defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
- # For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
- for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
- # Replace the first '=' with ' ' for iniset syntax
- iniset /$Q_PLUGIN_CONF_FILE agent ${I/=/ }
- done
- # Define extra "OVS" configuration options when q-agt is configured by defining
- # defining the array ``Q_AGENT_EXTRA_SRV_OPTS``.
- # For Example: ``Q_AGENT_EXTRA_SRV_OPTS=(foo=true bar=2)``
- for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
- # Replace the first '=' with ' ' for iniset syntax
- iniset /$Q_PLUGIN_CONF_FILE ovs ${I/=/ }
- done
}
function neutron_plugin_setup_interface_driver {
diff --git a/lib/neutron_plugins/ovs_base b/lib/neutron_plugins/ovs_base
index f0ef194..2997c6c 100644
--- a/lib/neutron_plugins/ovs_base
+++ b/lib/neutron_plugins/ovs_base
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# common functions for ovs based plugin
# -------------------------------------
@@ -26,7 +28,7 @@
function neutron_ovs_base_cleanup {
# remove all OVS ports that look like Neutron created ports
- for port in $(sudo ovs-vsctl list port | grep -o -e tap[0-9a-f\-]* -e q[rg]-[0-9a-f\-]*); do
+ for port in $(sudo ovs-vsctl list port | grep -o -e [a-zA-Z\-]*tap[0-9a-f\-]* -e q[rg]-[0-9a-f\-]*); do
sudo ovs-vsctl del-port ${port}
done
@@ -60,7 +62,11 @@
}
function _neutron_ovs_base_configure_debug_command {
- iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge $PUBLIC_BRIDGE
+ if [ "$Q_USE_PROVIDERNET_FOR_PUBLIC" = "True" ]; then
+ iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge ""
+ else
+ iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge $PUBLIC_BRIDGE
+ fi
}
function _neutron_ovs_base_configure_firewall_driver {
diff --git a/lib/neutron_plugins/plumgrid b/lib/neutron_plugins/plumgrid
index 7950ac0..0d711fe 100644
--- a/lib/neutron_plugins/plumgrid
+++ b/lib/neutron_plugins/plumgrid
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# PLUMgrid Neutron Plugin
# Edgar Magana emagana@plumgrid.com
# ------------------------------------
diff --git a/lib/neutron_plugins/ryu b/lib/neutron_plugins/ryu
deleted file mode 100644
index f45a797..0000000
--- a/lib/neutron_plugins/ryu
+++ /dev/null
@@ -1,79 +0,0 @@
-# Neutron Ryu plugin
-# ------------------
-
-# Save trace setting
-RYU_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-source $TOP_DIR/lib/neutron_plugins/ovs_base
-source $TOP_DIR/lib/neutron_thirdparty/ryu # for configuration value
-
-function neutron_plugin_create_nova_conf {
- _neutron_ovs_base_configure_nova_vif_driver
- iniset $NOVA_CONF DEFAULT libvirt_ovs_integration_bridge "$OVS_BRIDGE"
-}
-
-function neutron_plugin_install_agent_packages {
- _neutron_ovs_base_install_agent_packages
-
- # neutron_ryu_agent requires ryu module
- install_package $(get_packages "ryu")
- install_ryu
- configure_ryu
-}
-
-function neutron_plugin_configure_common {
- Q_PLUGIN_CONF_PATH=etc/neutron/plugins/ryu
- Q_PLUGIN_CONF_FILENAME=ryu.ini
- Q_PLUGIN_CLASS="neutron.plugins.ryu.ryu_neutron_plugin.RyuNeutronPluginV2"
-}
-
-function neutron_plugin_configure_debug_command {
- _neutron_ovs_base_configure_debug_command
- iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT ryu_api_host $RYU_API_HOST:$RYU_API_PORT
-}
-
-function neutron_plugin_configure_dhcp_agent {
- iniset $Q_DHCP_CONF_FILE DEFAULT ryu_api_host $RYU_API_HOST:$RYU_API_PORT
-}
-
-function neutron_plugin_configure_l3_agent {
- iniset $Q_L3_CONF_FILE DEFAULT ryu_api_host $RYU_API_HOST:$RYU_API_PORT
- _neutron_ovs_base_configure_l3_agent
-}
-
-function neutron_plugin_configure_plugin_agent {
- # Set up integration bridge
- _neutron_ovs_base_setup_bridge $OVS_BRIDGE
- if [ -n "$RYU_INTERNAL_INTERFACE" ]; then
- sudo ovs-vsctl --no-wait -- --may-exist add-port $OVS_BRIDGE $RYU_INTERNAL_INTERFACE
- fi
- iniset /$Q_PLUGIN_CONF_FILE ovs integration_bridge $OVS_BRIDGE
- AGENT_BINARY="$NEUTRON_DIR/neutron/plugins/ryu/agent/ryu_neutron_agent.py"
-
- _neutron_ovs_base_configure_firewall_driver
-}
-
-function neutron_plugin_configure_service {
- iniset /$Q_PLUGIN_CONF_FILE ovs openflow_rest_api $RYU_API_HOST:$RYU_API_PORT
-
- _neutron_ovs_base_configure_firewall_driver
-}
-
-function neutron_plugin_setup_interface_driver {
- local conf_file=$1
- iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
- iniset $conf_file DEFAULT ovs_use_veth True
-}
-
-function has_neutron_plugin_security_group {
- # 0 means True here
- return 0
-}
-
-function neutron_plugin_check_adv_test_requirements {
- is_service_enabled q-agt && is_service_enabled q-dhcp && return 0
-}
-
-# Restore xtrace
-$RYU_XTRACE
diff --git a/lib/neutron_plugins/services/firewall b/lib/neutron_plugins/services/firewall
index b5253db..61a148e 100644
--- a/lib/neutron_plugins/services/firewall
+++ b/lib/neutron_plugins/services/firewall
@@ -5,7 +5,7 @@
FW_XTRACE=$(set +o | grep xtrace)
set +o xtrace
-FWAAS_PLUGIN=neutron.services.firewall.fwaas_plugin.FirewallPlugin
+FWAAS_PLUGIN=neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin
function neutron_fwaas_configure_common {
_neutron_service_plugin_class_add $FWAAS_PLUGIN
@@ -13,10 +13,10 @@
function neutron_fwaas_configure_driver {
FWAAS_DRIVER_CONF_FILENAME=/etc/neutron/fwaas_driver.ini
- cp $NEUTRON_DIR/etc/fwaas_driver.ini $FWAAS_DRIVER_CONF_FILENAME
+ cp $NEUTRON_FWAAS_DIR/etc/fwaas_driver.ini $FWAAS_DRIVER_CONF_FILENAME
iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas enabled True
- iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas driver "neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver"
+ iniset_multiline $FWAAS_DRIVER_CONF_FILENAME fwaas driver "neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver"
}
function neutron_fwaas_stop {
diff --git a/lib/neutron_plugins/services/loadbalancer b/lib/neutron_plugins/services/loadbalancer
index f84b710..f465cc9 100644
--- a/lib/neutron_plugins/services/loadbalancer
+++ b/lib/neutron_plugins/services/loadbalancer
@@ -7,7 +7,7 @@
AGENT_LBAAS_BINARY="$NEUTRON_BIN_DIR/neutron-lbaas-agent"
-LBAAS_PLUGIN=neutron.services.loadbalancer.plugin.LoadBalancerPlugin
+LBAAS_PLUGIN=neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPlugin
function neutron_agent_lbaas_install_agent_packages {
if is_ubuntu || is_fedora || is_suse; then
@@ -17,6 +17,7 @@
function neutron_agent_lbaas_configure_common {
_neutron_service_plugin_class_add $LBAAS_PLUGIN
+ _neutron_deploy_rootwrap_filters $NEUTRON_LBAAS_DIR
}
function neutron_agent_lbaas_configure_agent {
@@ -25,7 +26,7 @@
LBAAS_AGENT_CONF_FILENAME="$LBAAS_AGENT_CONF_PATH/lbaas_agent.ini"
- cp $NEUTRON_DIR/etc/lbaas_agent.ini $LBAAS_AGENT_CONF_FILENAME
+ cp $NEUTRON_LBAAS_DIR/etc/lbaas_agent.ini $LBAAS_AGENT_CONF_FILENAME
# ovs_use_veth needs to be set before the plugin configuration
# occurs to allow plugins to override the setting.
diff --git a/lib/neutron_plugins/services/metering b/lib/neutron_plugins/services/metering
index 51123e2..37ba019 100644
--- a/lib/neutron_plugins/services/metering
+++ b/lib/neutron_plugins/services/metering
@@ -23,7 +23,7 @@
}
function neutron_metering_stop {
- :
+ stop_process q-metering
}
# Restore xtrace
diff --git a/lib/neutron_plugins/services/vpn b/lib/neutron_plugins/services/vpn
index 2478c47..5912eab 100644
--- a/lib/neutron_plugins/services/vpn
+++ b/lib/neutron_plugins/services/vpn
@@ -7,7 +7,7 @@
AGENT_VPN_BINARY="$NEUTRON_BIN_DIR/neutron-vpn-agent"
-VPN_PLUGIN=${VPN_PLUGIN:-"neutron.services.vpn.plugin.VPNDriverPlugin"}
+VPN_PLUGIN=${VPN_PLUGIN:-"neutron_vpnaas.services.vpn.plugin.VPNDriverPlugin"}
IPSEC_PACKAGE=${IPSEC_PACKAGE:-"openswan"}
function neutron_vpn_install_agent_packages {
@@ -16,6 +16,7 @@
function neutron_vpn_configure_common {
_neutron_service_plugin_class_add $VPN_PLUGIN
+ _neutron_deploy_rootwrap_filters $NEUTRON_VPNAAS_DIR
}
function neutron_vpn_stop {
@@ -27,6 +28,7 @@
if [ -n "$pids" ]; then
sudo kill $pids
fi
+ stop_process q-vpn
}
# Restore xtrace
diff --git a/lib/neutron_plugins/vmware_nsx b/lib/neutron_plugins/vmware_nsx
index f4eb82d..4cbedd6 100644
--- a/lib/neutron_plugins/vmware_nsx
+++ b/lib/neutron_plugins/vmware_nsx
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Neutron VMware NSX plugin
# -------------------------
diff --git a/lib/neutron_thirdparty/bigswitch_floodlight b/lib/neutron_thirdparty/bigswitch_floodlight
index 033731e..e3f4689 100644
--- a/lib/neutron_thirdparty/bigswitch_floodlight
+++ b/lib/neutron_thirdparty/bigswitch_floodlight
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Big Switch/FloodLight OpenFlow Controller
# ------------------------------------------
diff --git a/lib/neutron_thirdparty/midonet b/lib/neutron_thirdparty/midonet
index 099a66e..2c82d48 100644
--- a/lib/neutron_thirdparty/midonet
+++ b/lib/neutron_thirdparty/midonet
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# MidoNet
# -------
diff --git a/lib/neutron_thirdparty/ryu b/lib/neutron_thirdparty/ryu
index 233f3aa..1f78a21 100644
--- a/lib/neutron_thirdparty/ryu
+++ b/lib/neutron_thirdparty/ryu
@@ -1,56 +1,17 @@
-# Ryu OpenFlow Controller
-# -----------------------
+#!/bin/bash
+#
+# Ryu SDN Framework
+# -----------------
+
+# Used by ofagent.
+# TODO(yamamoto): Switch to pip_install once the development was settled
# Save trace setting
RYU3_XTRACE=$(set +o | grep xtrace)
set +o xtrace
-
RYU_DIR=$DEST/ryu
-# Ryu API Host
-RYU_API_HOST=${RYU_API_HOST:-127.0.0.1}
-# Ryu API Port
-RYU_API_PORT=${RYU_API_PORT:-8080}
-# Ryu OFP Host
-RYU_OFP_HOST=${RYU_OFP_HOST:-127.0.0.1}
-# Ryu OFP Port
-RYU_OFP_PORT=${RYU_OFP_PORT:-6633}
-# Ryu Applications
-RYU_APPS=${RYU_APPS:-ryu.app.simple_isolation,ryu.app.rest}
-function configure_ryu {
- :
-}
-
-function init_ryu {
- RYU_CONF_DIR=/etc/ryu
- if [[ ! -d $RYU_CONF_DIR ]]; then
- sudo mkdir -p $RYU_CONF_DIR
- fi
- sudo chown $STACK_USER $RYU_CONF_DIR
- RYU_CONF=$RYU_CONF_DIR/ryu.conf
- sudo rm -rf $RYU_CONF
-
- # Ryu configuration
- RYU_CONF_CONTENTS=${RYU_CONF_CONTENTS:-"[DEFAULT]
-app_lists=$RYU_APPS
-wsapi_host=$RYU_API_HOST
-wsapi_port=$RYU_API_PORT
-ofp_listen_host=$RYU_OFP_HOST
-ofp_tcp_listen_port=$RYU_OFP_PORT
-neutron_url=http://$Q_HOST:$Q_PORT
-neutron_admin_username=$Q_ADMIN_USERNAME
-neutron_admin_password=$SERVICE_PASSWORD
-neutron_admin_tenant_name=$SERVICE_TENANT_NAME
-neutron_admin_auth_url=$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_AUTH_PORT/v2.0
-neutron_auth_strategy=$Q_AUTH_STRATEGY
-neutron_controller_addr=tcp:$RYU_OFP_HOST:$RYU_OFP_PORT
-"}
- echo "${RYU_CONF_CONTENTS}" > $RYU_CONF
-}
-
-# install_ryu can be called multiple times as neutron_pluing/ryu may call
-# this function for neutron-ryu-agent
# Make this function idempotent and avoid cloning same repo many times
# with RECLONE=yes
_RYU_INSTALLED=${_RYU_INSTALLED:-False}
@@ -63,17 +24,5 @@
fi
}
-function start_ryu {
- run_process ryu "$RYU_DIR/bin/ryu-manager --config-file $RYU_CONF"
-}
-
-function stop_ryu {
- :
-}
-
-function check_ryu {
- :
-}
-
# Restore xtrace
$RYU3_XTRACE
diff --git a/lib/neutron_thirdparty/trema b/lib/neutron_thirdparty/trema
index 3e59ef2..075f013 100644
--- a/lib/neutron_thirdparty/trema
+++ b/lib/neutron_thirdparty/trema
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# Trema Sliceable Switch
# ----------------------
diff --git a/lib/neutron_thirdparty/vmware_nsx b/lib/neutron_thirdparty/vmware_nsx
index 7a20c64..7027a29 100644
--- a/lib/neutron_thirdparty/vmware_nsx
+++ b/lib/neutron_thirdparty/vmware_nsx
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# VMware NSX
# ----------
diff --git a/lib/nova b/lib/nova
index 0f83807..74a3411 100644
--- a/lib/nova
+++ b/lib/nova
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova
# Functions to control the configuration and operation of the **Nova** service
@@ -29,8 +31,10 @@
# --------
# Set up default directories
+GITDIR["python-novaclient"]=$DEST/python-novaclient
+
+
NOVA_DIR=$DEST/nova
-NOVACLIENT_DIR=$DEST/python-novaclient
NOVA_STATE_PATH=${NOVA_STATE_PATH:=$DATA_DIR/nova}
# INSTANCES_PATH is the previous name for this
NOVA_INSTANCES_PATH=${NOVA_INSTANCES_PATH:=${INSTANCES_PATH:=$NOVA_STATE_PATH/instances}}
@@ -43,6 +47,12 @@
NOVA_CELLS_DB=${NOVA_CELLS_DB:-nova_cell}
NOVA_API_PASTE_INI=${NOVA_API_PASTE_INI:-$NOVA_CONF_DIR/api-paste.ini}
+# NOVA_API_VERSION valid options
+# - default - setup API end points as nova does out of the box
+# - v21default - make v21 the default on /v2
+# NOTE(sdague): this is for transitional testing of the Nova v21 API.
+# Expect to remove in L or M.
+NOVA_API_VERSION=${NOVA_API_VERSION-default}
if is_ssl_enabled_service "nova" || is_service_enabled tls-proxy; then
NOVA_SERVICE_PROTOCOL="https"
@@ -96,7 +106,7 @@
# $NOVA_VNC_ENABLED can be used to forcibly enable vnc configuration.
# In multi-node setups allows compute hosts to not run n-novnc.
-NOVA_VNC_ENABLED=$(trueorfalse False $NOVA_VNC_ENABLED)
+NOVA_VNC_ENABLED=$(trueorfalse False NOVA_VNC_ENABLED)
# Get hypervisor configuration
# ----------------------------
@@ -135,11 +145,11 @@
# ``MULTI_HOST`` is a mode where each compute node runs its own network node. This
# allows network operations and routing for a VM to occur on the server that is
# running the VM - removing a SPOF and bandwidth bottleneck.
-MULTI_HOST=`trueorfalse False $MULTI_HOST`
+MULTI_HOST=$(trueorfalse False MULTI_HOST)
# ``NOVA_ALLOW_MOVE_TO_SAME_HOST` can be set to False in multi node devstack,
# where there are at least two nova-computes.
-NOVA_ALLOW_MOVE_TO_SAME_HOST=`trueorfalse True $NOVA_ALLOW_MOVE_TO_SAME_HOST`
+NOVA_ALLOW_MOVE_TO_SAME_HOST=$(trueorfalse True NOVA_ALLOW_MOVE_TO_SAME_HOST)
# Test floating pool and range are used for testing. They are defined
# here until the admin APIs can replace nova-manage
@@ -255,12 +265,14 @@
configure_nova_rootwrap
- if is_service_enabled n-api; then
- # Remove legacy paste config if present
- rm -f $NOVA_DIR/bin/nova-api-paste.ini
-
+ if [[ "$ENABLED_SERVICES" =~ "n-api" ]]; then
# Get the sample configuration file in place
cp $NOVA_DIR/etc/nova/api-paste.ini $NOVA_CONF_DIR
+
+ # For testing v21 is equivalent to v2
+ if [[ "$NOVA_API_VERSION" == "v21default" ]]; then
+ sed -i s/": openstack_compute_api_v2$"/": openstack_compute_api_v21"/ "$NOVA_API_PASTE_INI"
+ fi
fi
if is_service_enabled n-cpu; then
@@ -341,15 +353,12 @@
# SERVICE_TENANT_NAME nova ResellerAdmin (if Swift is enabled)
function create_nova_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
# Nova
if [[ "$ENABLED_SERVICES" =~ "n-api" ]]; then
- local nova_user=$(get_or_create_user "nova" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $nova_user $service_tenant
+ # NOTE(jamielennox): Nova doesn't need the admin role here, however neutron uses
+ # this service user when notifying nova of changes and that requires the admin role.
+ create_service_user "nova" "admin"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -376,7 +385,7 @@
if is_service_enabled swift; then
# Nova needs ResellerAdmin role to download images when accessing
# swift through the s3 api.
- get_or_add_user_role ResellerAdmin nova $SERVICE_TENANT_NAME
+ get_or_add_user_project_role ResellerAdmin nova $SERVICE_TENANT_NAME
fi
# EC2
@@ -386,9 +395,9 @@
"ec2" "EC2 Compatibility Layer")
get_or_create_endpoint $ec2_service \
"$REGION_NAME" \
- "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud" \
- "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Admin" \
- "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud"
+ "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
+ "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/" \
+ "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
fi
fi
@@ -456,8 +465,7 @@
if is_ssl_enabled_service "cinder" || is_service_enabled tls-proxy; then
CINDER_SERVICE_HOST=${CINDER_SERVICE_HOST:-$SERVICE_HOST}
CINDER_SERVICE_PORT=${CINDER_SERVICE_PORT:-8776}
- iniset $NOVA_CONF cinder endpoint_template "https://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/%(project_id)s"
- iniset $NOVA_CONF cinder ca_certificates_file $SSL_BUNDLE_FILE
+ iniset $NOVA_CONF cinder cafile $SSL_BUNDLE_FILE
fi
fi
@@ -536,6 +544,11 @@
iniset $NOVA_CONF DEFAULT ec2_workers "$API_WORKERS"
iniset $NOVA_CONF DEFAULT metadata_workers "$API_WORKERS"
+ if [[ "$NOVA_BACKEND" == "LVM" ]]; then
+ iniset $NOVA_CONF libvirt images_type "lvm"
+ iniset $NOVA_CONF libvirt images_volume_group $DEFAULT_VOLUME_GROUP_NAME
+ fi
+
if is_ssl_enabled_service glance || is_service_enabled tls-proxy; then
iniset $NOVA_CONF DEFAULT glance_protocol https
fi
@@ -576,8 +589,8 @@
fi
$NOVA_BIN_DIR/nova-manage --config-file $NOVA_CELLS_CONF db sync
- $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CELLS_CONF cell create --name=region --cell_type=parent --username=guest --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=/ --woffset=0 --wscale=1
- $NOVA_BIN_DIR/nova-manage cell create --name=child --cell_type=child --username=guest --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=child_cell --woffset=0 --wscale=1
+ $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CELLS_CONF cell create --name=region --cell_type=parent --username=$RABBIT_USERID --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=/ --woffset=0 --wscale=1
+ $NOVA_BIN_DIR/nova-manage cell create --name=child --cell_type=child --username=$RABBIT_USERID --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=child_cell --woffset=0 --wscale=1
fi
}
@@ -612,34 +625,31 @@
# Only do this step once on the API node for an entire cluster.
if is_service_enabled $DATABASE_BACKENDS && is_service_enabled n-api; then
# (Re)create nova database
- # Explicitly use latin1: to avoid lp#829209, nova expects the database to
- # use latin1 by default, and then upgrades the database to utf8 (see the
- # 082_essex.py in nova)
- recreate_database nova latin1
+ recreate_database nova
# Migrate nova database
$NOVA_BIN_DIR/nova-manage db sync
if is_service_enabled n-cell; then
- recreate_database $NOVA_CELLS_DB latin1
- fi
-
- # (Re)create nova baremetal database
- if is_baremetal; then
- recreate_database nova_bm latin1
- $NOVA_BIN_DIR/nova-baremetal-manage db sync
+ recreate_database $NOVA_CELLS_DB
fi
fi
create_nova_cache_dir
create_nova_keys_dir
+
+ if [[ "$NOVA_BACKEND" == "LVM" ]]; then
+ init_default_lvm_volume_group
+ fi
}
# install_novaclient() - Collect source and prepare
function install_novaclient {
- git_clone $NOVACLIENT_REPO $NOVACLIENT_DIR $NOVACLIENT_BRANCH
- setup_develop $NOVACLIENT_DIR
- sudo install -D -m 0644 -o $STACK_USER {$NOVACLIENT_DIR/tools/,/etc/bash_completion.d/}nova.bash_completion
+ if use_library_from_git "python-novaclient"; then
+ git_clone_by_name "python-novaclient"
+ setup_dev_lib "python-novaclient"
+ sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-novaclient"]}/tools/,/etc/bash_completion.d/}nova.bash_completion
+ fi
}
# install_nova() - Collect source and prepare
@@ -650,7 +660,7 @@
if is_service_enabled n-novnc; then
# a websockets/html5 or flash powered VNC console for vm instances
- NOVNC_FROM_PACKAGE=`trueorfalse False $NOVNC_FROM_PACKAGE`
+ NOVNC_FROM_PACKAGE=$(trueorfalse False NOVNC_FROM_PACKAGE)
if [ "$NOVNC_FROM_PACKAGE" = "True" ]; then
NOVNC_WEB_DIR=/usr/share/novnc
install_package novnc
@@ -662,7 +672,7 @@
if is_service_enabled n-spice; then
# a websockets/html5 or flash powered SPICE console for vm instances
- SPICE_FROM_PACKAGE=`trueorfalse True $SPICE_FROM_PACKAGE`
+ SPICE_FROM_PACKAGE=$(trueorfalse True SPICE_FROM_PACKAGE)
if [ "$SPICE_FROM_PACKAGE" = "True" ]; then
SPICE_WEB_DIR=/usr/share/spice-html5
install_package spice-html5
@@ -762,8 +772,8 @@
}
function start_nova {
- start_nova_compute
start_nova_rest
+ start_nova_compute
}
function stop_nova_compute {
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index d3c4eab..4d617e8 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/functions-libvirt
# Common libvirt configuration functions
@@ -13,7 +15,7 @@
# --------
# if we should turn on massive libvirt debugging
-DEBUG_LIBVIRT=$(trueorfalse False $DEBUG_LIBVIRT)
+DEBUG_LIBVIRT=$(trueorfalse False DEBUG_LIBVIRT)
# Installs required distro-specific libvirt packages.
function install_libvirt {
@@ -35,8 +37,7 @@
# Note there is a difference between F20 rackspace cloud images
# and HP images used in the gate; rackspace has firewalld but hp
- # cloud doesn't. RHEL6 doesn't have firewalld either. So we
- # don't care if it fails.
+ # cloud doesn't.
if is_fedora && is_package_installed firewalld; then
sudo service firewalld restart || true
fi
@@ -66,34 +67,12 @@
fi
if is_fedora || is_suse; then
- if is_fedora && [[ $DISTRO =~ (rhel6) || "$os_RELEASE" -le "17" ]]; then
- cat <<EOF | sudo tee /etc/polkit-1/localauthority/50-local.d/50-libvirt-remote-access.pkla
-[libvirt Management Access]
-Identity=unix-group:$LIBVIRT_GROUP
-Action=org.libvirt.unix.manage
-ResultAny=yes
-ResultInactive=yes
-ResultActive=yes
-EOF
- elif is_suse && [[ $os_RELEASE = 12.2 || "$os_VENDOR" = "SUSE LINUX" ]]; then
- # openSUSE < 12.3 or SLE
- # Work around the fact that polkit-default-privs overrules pklas
- # with 'unix-group:$group'.
- cat <<EOF | sudo tee /etc/polkit-1/localauthority/50-local.d/50-libvirt-remote-access.pkla
-[libvirt Management Access]
-Identity=unix-user:$STACK_USER
-Action=org.libvirt.unix.manage
-ResultAny=yes
-ResultInactive=yes
-ResultActive=yes
-EOF
- else
- # Starting with fedora 18 and opensuse-12.3 enable stack-user to
- # virsh -c qemu:///system by creating a policy-kit rule for
- # stack-user using the new Javascript syntax
- rules_dir=/etc/polkit-1/rules.d
- sudo mkdir -p $rules_dir
- cat <<EOF | sudo tee $rules_dir/50-libvirt-$STACK_USER.rules
+ # Starting with fedora 18 and opensuse-12.3 enable stack-user to
+ # virsh -c qemu:///system by creating a policy-kit rule for
+ # stack-user using the new Javascript syntax
+ rules_dir=/etc/polkit-1/rules.d
+ sudo mkdir -p $rules_dir
+ cat <<EOF | sudo tee $rules_dir/50-libvirt-$STACK_USER.rules
polkit.addRule(function(action, subject) {
if (action.id == 'org.libvirt.unix.manage' &&
subject.user == '$STACK_USER') {
@@ -101,8 +80,7 @@
}
});
EOF
- unset rules_dir
- fi
+ unset rules_dir
fi
# The user that nova runs as needs to be member of **libvirtd** group otherwise
diff --git a/lib/nova_plugins/hypervisor-baremetal b/lib/nova_plugins/hypervisor-baremetal
deleted file mode 100644
index 22d16a6..0000000
--- a/lib/nova_plugins/hypervisor-baremetal
+++ /dev/null
@@ -1,87 +0,0 @@
-# lib/nova_plugins/hypervisor-baremetal
-# Configure the baremetal hypervisor
-
-# Enable with:
-# VIRT_DRIVER=baremetal
-
-# Dependencies:
-# ``functions`` file
-# ``nova`` configuration
-
-# install_nova_hypervisor - install any external requirements
-# configure_nova_hypervisor - make configuration changes, including those to other services
-# start_nova_hypervisor - start any external services
-# stop_nova_hypervisor - stop any external services
-# cleanup_nova_hypervisor - remove transient data and cache
-
-# Save trace setting
-MY_XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-NETWORK_MANAGER=${NETWORK_MANAGER:-FlatManager}
-PUBLIC_INTERFACE_DEFAULT=eth0
-FLAT_INTERFACE=${FLAT_INTERFACE:-eth0}
-FLAT_NETWORK_BRIDGE_DEFAULT=br100
-STUB_NETWORK=${STUB_NETWORK:-False}
-
-
-# Entry Points
-# ------------
-
-# clean_nova_hypervisor - Clean up an installation
-function cleanup_nova_hypervisor {
- # This function intentionally left blank
- :
-}
-
-# configure_nova_hypervisor - Set config files, create data dirs, etc
-function configure_nova_hypervisor {
- configure_baremetal_nova_dirs
-
- iniset $NOVA_CONF baremetal sql_connection `database_connection_url nova_bm`
- LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.firewall.NoopFirewallDriver"}
- iniset $NOVA_CONF DEFAULT compute_driver nova.virt.baremetal.driver.BareMetalDriver
- iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
- iniset $NOVA_CONF DEFAULT scheduler_host_manager nova.scheduler.baremetal_host_manager.BaremetalHostManager
- iniset $NOVA_CONF DEFAULT ram_allocation_ratio 1.0
- iniset $NOVA_CONF DEFAULT reserved_host_memory_mb 0
- iniset $NOVA_CONF baremetal flavor_extra_specs cpu_arch:$BM_CPU_ARCH
- iniset $NOVA_CONF baremetal driver $BM_DRIVER
- iniset $NOVA_CONF baremetal power_manager $BM_POWER_MANAGER
- iniset $NOVA_CONF baremetal tftp_root /tftpboot
- if [[ "$BM_DNSMASQ_FROM_NOVA_NETWORK" = "True" ]]; then
- BM_DNSMASQ_CONF=$NOVA_CONF_DIR/dnsmasq-for-baremetal-from-nova-network.conf
- sudo cp "$FILES/dnsmasq-for-baremetal-from-nova-network.conf" "$BM_DNSMASQ_CONF"
- iniset $NOVA_CONF DEFAULT dnsmasq_config_file "$BM_DNSMASQ_CONF"
- fi
-}
-
-# install_nova_hypervisor() - Install external components
-function install_nova_hypervisor {
- # This function intentionally left blank
- :
-}
-
-# start_nova_hypervisor - Start any required external services
-function start_nova_hypervisor {
- # This function intentionally left blank
- :
-}
-
-# stop_nova_hypervisor - Stop any external services
-function stop_nova_hypervisor {
- # This function intentionally left blank
- :
-}
-
-
-# Restore xtrace
-$MY_XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
diff --git a/lib/nova_plugins/hypervisor-fake b/lib/nova_plugins/hypervisor-fake
index dc93633..3180d91 100644
--- a/lib/nova_plugins/hypervisor-fake
+++ b/lib/nova_plugins/hypervisor-fake
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-fake
# Configure the fake hypervisor
diff --git a/lib/nova_plugins/hypervisor-ironic b/lib/nova_plugins/hypervisor-ironic
index 4004cc9..0169d73 100644
--- a/lib/nova_plugins/hypervisor-ironic
+++ b/lib/nova_plugins/hypervisor-ironic
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-ironic
# Configure the ironic hypervisor
@@ -47,15 +49,24 @@
iniset $NOVA_CONF ironic admin_password $ADMIN_PASSWORD
iniset $NOVA_CONF ironic admin_url $KEYSTONE_AUTH_URI/v2.0
iniset $NOVA_CONF ironic admin_tenant_name demo
- iniset $NOVA_CONF ironic api_endpoint http://$SERVICE_HOST:6385/v1
+ iniset $NOVA_CONF ironic api_endpoint $IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT/v1
}
# install_nova_hypervisor() - Install external components
function install_nova_hypervisor {
if ! is_service_enabled neutron; then
die $LINENO "Neutron should be enabled for usage of the Ironic Nova driver."
+ elif is_ironic_hardware; then
+ return
fi
install_libvirt
+ if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] && is_ubuntu; then
+ # Ubuntu packaging+apparmor issue prevents libvirt from loading
+ # the ROM from /usr/share/misc. Workaround by installing it directly
+ # to a directory that it can read from. (LP: #1393548)
+ sudo rm -rf /usr/share/qemu/sgabios.bin
+ sudo cp /usr/share/misc/sgabios.bin /usr/share/qemu/sgabios.bin
+ fi
}
# start_nova_hypervisor - Start any required external services
diff --git a/lib/nova_plugins/hypervisor-libvirt b/lib/nova_plugins/hypervisor-libvirt
index 259bf15..4d1eb6c 100644
--- a/lib/nova_plugins/hypervisor-libvirt
+++ b/lib/nova_plugins/hypervisor-libvirt
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-libvirt
# Configure the libvirt hypervisor
@@ -42,6 +44,7 @@
iniset $NOVA_CONF libvirt virt_type "$LIBVIRT_TYPE"
iniset $NOVA_CONF libvirt cpu_mode "none"
iniset $NOVA_CONF libvirt use_usb_tablet "False"
+ iniset $NOVA_CONF libvirt live_migration_uri "qemu+ssh://$STACK_USER@%s/system"
iniset $NOVA_CONF DEFAULT default_ephemeral_format "ext4"
iniset $NOVA_CONF DEFAULT compute_driver "libvirt.LibvirtDriver"
LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.libvirt.firewall.IptablesFirewallDriver"}
@@ -51,7 +54,7 @@
iniset $NOVA_CONF DEFAULT vnc_enabled "false"
fi
- ENABLE_FILE_INJECTION=$(trueorfalse False $ENABLE_FILE_INJECTION)
+ ENABLE_FILE_INJECTION=$(trueorfalse False ENABLE_FILE_INJECTION)
if [[ "$ENABLE_FILE_INJECTION" = "True" ]] ; then
# When libguestfs is available for file injection, enable using
# libguestfs to inspect the image and figure out the proper
@@ -63,6 +66,11 @@
# disable it here for now to avoid surprises later.
iniset $NOVA_CONF libvirt inject_partition '-2'
fi
+
+ if [[ "$LIBVIRT_TYPE" = "parallels" ]]; then
+ iniset $NOVA_CONF libvirt connection_uri "parallels+unix:///system"
+ iniset $NOVA_CONF libvirt images_type "ploop"
+ fi
}
# install_nova_hypervisor() - Install external components
diff --git a/lib/nova_plugins/hypervisor-openvz b/lib/nova_plugins/hypervisor-openvz
index a1636ad..cce36b8 100644
--- a/lib/nova_plugins/hypervisor-openvz
+++ b/lib/nova_plugins/hypervisor-openvz
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-openvz
# Configure the openvz hypervisor
diff --git a/lib/nova_plugins/hypervisor-vsphere b/lib/nova_plugins/hypervisor-vsphere
index 9933a3c..c406e09 100644
--- a/lib/nova_plugins/hypervisor-vsphere
+++ b/lib/nova_plugins/hypervisor-vsphere
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-vsphere
# Configure the vSphere hypervisor
diff --git a/lib/nova_plugins/hypervisor-xenserver b/lib/nova_plugins/hypervisor-xenserver
index 0dba471..4d0ec89 100644
--- a/lib/nova_plugins/hypervisor-xenserver
+++ b/lib/nova_plugins/hypervisor-xenserver
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/nova_plugins/hypervisor-xenserver
# Configure the XenServer hypervisor
@@ -92,8 +94,7 @@
# install_nova_hypervisor() - Install external components
function install_nova_hypervisor {
- # This function intentionally left blank
- :
+ pip_install xenapi
}
# start_nova_hypervisor - Start any required external services
diff --git a/lib/opendaylight b/lib/opendaylight
deleted file mode 100644
index 374de95..0000000
--- a/lib/opendaylight
+++ /dev/null
@@ -1,189 +0,0 @@
-# lib/opendaylight
-# Functions to control the configuration and operation of the opendaylight service
-
-# Dependencies:
-#
-# ``functions`` file
-# ``DEST`` must be defined
-# ``STACK_USER`` must be defined
-
-# ``stack.sh`` calls the entry points in this order:
-#
-# - is_opendaylight_enabled
-# - is_opendaylight-compute_enabled
-# - install_opendaylight
-# - install_opendaylight-compute
-# - configure_opendaylight
-# - init_opendaylight
-# - start_opendaylight
-# - stop_opendaylight-compute
-# - stop_opendaylight
-# - cleanup_opendaylight
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# For OVS_BRIDGE and PUBLIC_BRIDGE
-source $TOP_DIR/lib/neutron_plugins/ovs_base
-
-# Defaults
-# --------
-
-# The IP address of ODL. Set this in local.conf.
-# ODL_MGR_IP=
-ODL_MGR_IP=${ODL_MGR_IP:-$SERVICE_HOST}
-
-# The ODL endpoint URL
-ODL_ENDPOINT=${ODL_ENDPOINT:-http://${ODL_MGR_IP}:8080/controller/nb/v2/neutron}
-
-# The ODL username
-ODL_USERNAME=${ODL_USERNAME:-admin}
-
-# The ODL password
-ODL_PASSWORD=${ODL_PASSWORD:-admin}
-
-# <define global variables here that belong to this project>
-ODL_DIR=$DEST/opendaylight
-
-# The OpenDaylight Package, currently using 'Hydrogen' release
-ODL_PKG=${ODL_PKG:-distribution-karaf-0.2.0-Helium.zip}
-
-# The OpenDaylight URL
-ODL_URL=${ODL_URL:-https://nexus.opendaylight.org/content/groups/public/org/opendaylight/integration/distribution-karaf/0.2.0-Helium}
-
-# Default arguments for OpenDaylight. This is typically used to set
-# Java memory options.
-# ``ODL_ARGS=Xmx1024m -XX:MaxPermSize=512m``
-ODL_ARGS=${ODL_ARGS:-"-XX:MaxPermSize=384m"}
-
-# How long to pause after ODL starts to let it complete booting
-ODL_BOOT_WAIT=${ODL_BOOT_WAIT:-20}
-
-# The physical provider network to device mapping
-ODL_PROVIDER_MAPPINGS=${ODL_PROVIDER_MAPPINGS:-physnet1:eth1}
-
-# Enable OpenDaylight l3 forwarding
-ODL_L3=${ODL_L3:-False}
-
-
-# Entry Points
-# ------------
-
-# Test if OpenDaylight is enabled
-# is_opendaylight_enabled
-function is_opendaylight_enabled {
- [[ ,${ENABLED_SERVICES} =~ ,"odl-" ]] && return 0
- return 1
-}
-
-# cleanup_opendaylight() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_opendaylight {
- :
-}
-
-# configure_opendaylight() - Set config files, create data dirs, etc
-function configure_opendaylight {
- # Add odl-ovsdb-openstack if it's not already there
- local ODLOVSDB=$(cat $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/org.apache.karaf.features.cfg | grep featuresBoot= | grep odl)
- if [ "$ODLOVSDB" == "" ]; then
- sed -i '/^featuresBoot=/ s/$/,odl-ovsdb-openstack/' $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/org.apache.karaf.features.cfg
- fi
-
- # Configure OpenFlow 1.3 if it's not there
- local OFLOW13=$(cat $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/custom.properties | grep ^of.version)
- if [ "$OFLOW13" == "" ]; then
- echo "ovsdb.of.version=1.3" >> $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/custom.properties
- fi
-
- # Configure L3 if the user wants it
- if [ "${ODL_L3}" == "True" ]; then
- # Configure L3 FWD if it's not there
- local L3FWD=$(cat $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/custom.properties | grep ^ovsdb.l3.fwd.enabled)
- if [ "$L3FWD" == "" ]; then
- echo "ovsdb.l3.fwd.enabled=yes" >> $ODL_DIR/distribution-karaf-0.2.0-Helium/etc/custom.properties
- fi
- fi
-}
-
-function configure_ml2_odl {
- populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl url=$ODL_ENDPOINT
- populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl username=$ODL_USERNAME
- populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl password=$ODL_PASSWORD
-}
-
-# init_opendaylight() - Initialize databases, etc.
-function init_opendaylight {
- # clean up from previous (possibly aborted) runs
- # create required data files
- :
-}
-
-# install_opendaylight() - Collect source and prepare
-function install_opendaylight {
- local _pwd=$(pwd)
-
- if is_ubuntu; then
- install_package maven openjdk-7-jre openjdk-7-jdk
- else
- yum_install maven java-1.7.0-openjdk
- fi
-
- # Download OpenDaylight
- mkdir -p $ODL_DIR
- cd $ODL_DIR
- wget -N $ODL_URL/$ODL_PKG
- unzip -u $ODL_PKG
-}
-
-# install_opendaylight-compute - Make sure OVS is installed
-function install_opendaylight-compute {
- # packages are the same as for Neutron OVS agent
- _neutron_ovs_base_install_agent_packages
-}
-
-# start_opendaylight() - Start running processes, including screen
-function start_opendaylight {
- if is_ubuntu; then
- JHOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64
- else
- JHOME=/usr/lib/jvm/java-1.7.0-openjdk
- fi
-
- # The flags to ODL have the following meaning:
- # -of13: runs ODL using OpenFlow 1.3 protocol support.
- # -virt ovsdb: Runs ODL in "virtualization" mode with OVSDB support
-
- run_process odl-server "cd $ODL_DIR/distribution-karaf-0.2.0-Helium && JAVA_HOME=$JHOME bin/karaf"
-
- # Sleep a bit to let OpenDaylight finish starting up
- sleep $ODL_BOOT_WAIT
-}
-
-# stop_opendaylight() - Stop running processes (non-screen)
-function stop_opendaylight {
- stop_process odl-server
-}
-
-# stop_opendaylight-compute() - Remove OVS bridges
-function stop_opendaylight-compute {
- # remove all OVS ports that look like Neutron created ports
- for port in $(sudo ovs-vsctl list port | grep -o -e tap[0-9a-f\-]* -e q[rg]-[0-9a-f\-]*); do
- sudo ovs-vsctl del-port ${port}
- done
-
- # remove all OVS bridges created by Neutron
- for bridge in $(sudo ovs-vsctl list-br | grep -o -e ${OVS_BRIDGE} -e ${PUBLIC_BRIDGE}); do
- sudo ovs-vsctl del-br ${bridge}
- done
-}
-
-# Restore xtrace
-$XTRACE
-
-# Tell emacs to use shell-script-mode
-## Local variables:
-## mode: shell-script
-## End:
diff --git a/lib/oslo b/lib/oslo
index a20aa14..31c9d34 100644
--- a/lib/oslo
+++ b/lib/oslo
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/oslo
#
# Functions to install oslo libraries from git
@@ -21,13 +23,14 @@
# Defaults
# --------
GITDIR["cliff"]=$DEST/cliff
-GITDIR["oslo.config"]=$DEST/oslo.config
GITDIR["oslo.concurrency"]=$DEST/oslo.concurrency
+GITDIR["oslo.config"]=$DEST/oslo.config
+GITDIR["oslo.context"]=$DEST/oslo.context
GITDIR["oslo.db"]=$DEST/oslo.db
GITDIR["oslo.i18n"]=$DEST/oslo.i18n
GITDIR["oslo.log"]=$DEST/oslo.log
-GITDIR["oslo.middleware"]=$DEST/oslo.middleware
GITDIR["oslo.messaging"]=$DEST/oslo.messaging
+GITDIR["oslo.middleware"]=$DEST/oslo.middleware
GITDIR["oslo.rootwrap"]=$DEST/oslo.rootwrap
GITDIR["oslo.serialization"]=$DEST/oslo.serialization
GITDIR["oslo.utils"]=$DEST/oslo.utils
@@ -35,6 +38,7 @@
GITDIR["pycadf"]=$DEST/pycadf
GITDIR["stevedore"]=$DEST/stevedore
GITDIR["taskflow"]=$DEST/taskflow
+GITDIR["tooz"]=$DEST/tooz
# Support entry points installation of console scripts
OSLO_BIN_DIR=$(get_python_exec_prefix)
@@ -53,20 +57,22 @@
# install_oslo() - Collect source and prepare
function install_oslo {
_do_install_oslo_lib "cliff"
- _do_install_oslo_lib "oslo.i18n"
- _do_install_oslo_lib "oslo.utils"
- _do_install_oslo_lib "oslo.serialization"
- _do_install_oslo_lib "oslo.config"
_do_install_oslo_lib "oslo.concurrency"
- _do_install_oslo_lib "oslo.log"
- _do_install_oslo_lib "oslo.middleware"
- _do_install_oslo_lib "oslo.messaging"
- _do_install_oslo_lib "oslo.rootwrap"
+ _do_install_oslo_lib "oslo.config"
+ _do_install_oslo_lib "oslo.context"
_do_install_oslo_lib "oslo.db"
+ _do_install_oslo_lib "oslo.i18n"
+ _do_install_oslo_lib "oslo.log"
+ _do_install_oslo_lib "oslo.messaging"
+ _do_install_oslo_lib "oslo.middleware"
+ _do_install_oslo_lib "oslo.rootwrap"
+ _do_install_oslo_lib "oslo.serialization"
+ _do_install_oslo_lib "oslo.utils"
_do_install_oslo_lib "oslo.vmware"
_do_install_oslo_lib "pycadf"
_do_install_oslo_lib "stevedore"
_do_install_oslo_lib "taskflow"
+ _do_install_oslo_lib "tooz"
}
# Restore xtrace
diff --git a/lib/rpc_backend b/lib/rpc_backend
index 14c78fb..899748c 100644
--- a/lib/rpc_backend
+++ b/lib/rpc_backend
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/rpc_backend
# Interface for interactig with different rpc backend
# rpc backend settings
@@ -5,7 +7,7 @@
# Dependencies:
#
# - ``functions`` file
-# - ``RABBIT_{HOST|PASSWORD}`` must be defined when RabbitMQ is used
+# - ``RABBIT_{HOST|PASSWORD|USERID}`` must be defined when RabbitMQ is used
# - ``RPC_MESSAGING_PROTOCOL`` option for configuring the messaging protocol
# ``stack.sh`` calls the entry points in this order:
@@ -19,6 +21,11 @@
XTRACE=$(set +o | grep xtrace)
set +o xtrace
+RPC_MESSAGING_PROTOCOL=${RPC_MESSAGING_PROTOCOL:-0.9}
+
+# TODO(sdague): RPC backend selection is super wonky because we treat
+# messaging server as a service, which it really isn't for multi host
+QPID_HOST=${QPID_HOST:-}
# Functions
# ---------
@@ -67,7 +74,8 @@
if is_service_enabled rabbit; then
# Obliterate rabbitmq-server
uninstall_package rabbitmq-server
- sudo killall epmd || sudo killall -9 epmd
+ # in case it's not actually running, /bin/true at the end
+ sudo killall epmd || sudo killall -9 epmd || /bin/true
if is_ubuntu; then
# And the Erlang runtime too
apt_get purge -y erlang*
@@ -82,11 +90,20 @@
fi
elif is_service_enabled zeromq; then
if is_fedora; then
- uninstall_package zeromq python-zmq redis
+ uninstall_package zeromq python-zmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ uninstall_package redis python-redis
+ fi
elif is_ubuntu; then
- uninstall_package libzmq1 python-zmq redis-server
+ uninstall_package libzmq1 python-zmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ uninstall_package redis-server python-redis
+ fi
elif is_suse; then
- uninstall_package libzmq1 python-pyzmq redis
+ uninstall_package libzmq1 python-pyzmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ uninstall_package redis python-redis
+ fi
else
exit_distro_not_supported "zeromq installation"
fi
@@ -145,11 +162,20 @@
# but there is a matchmaker driver that works
# really well & out of the box for multi-node.
if is_fedora; then
- install_package zeromq python-zmq redis
+ install_package zeromq python-zmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ install_package redis python-redis
+ fi
elif is_ubuntu; then
- install_package libzmq1 python-zmq redis-server
+ install_package libzmq1 python-zmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ install_package redis-server python-redis
+ fi
elif is_suse; then
- install_package libzmq1 python-pyzmq redis
+ install_package libzmq1 python-pyzmq
+ if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
+ install_package redis python-redis
+ fi
else
exit_distro_not_supported "zeromq installation"
fi
@@ -171,22 +197,37 @@
echo_summary "Starting RabbitMQ"
# NOTE(bnemec): Retry initial rabbitmq configuration to deal with
# the fact that sometimes it fails to start properly.
- # Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1059028
+ # Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1144100
local i
for i in `seq 10`; do
+ local rc=0
+
+ [[ $i -eq "10" ]] && die $LINENO "Failed to set rabbitmq password"
+
if is_fedora || is_suse; then
# service is not started by default
restart_service rabbitmq-server
fi
+
+ rabbit_setuser "$RABBIT_USERID" "$RABBIT_PASSWORD" || rc=$?
+ if [ $rc -ne 0 ]; then
+ continue
+ fi
+
# change the rabbit password since the default is "guest"
- sudo rabbitmqctl change_password guest $RABBIT_PASSWORD && break
- [[ $i -eq "10" ]] && die $LINENO "Failed to set rabbitmq password"
+ sudo rabbitmqctl change_password \
+ $RABBIT_USERID $RABBIT_PASSWORD || rc=$?
+ if [ $rc -ne 0 ]; then
+ continue;
+ fi
+
+ break
done
if is_service_enabled n-cell; then
# Add partitioned access for the child cell
if [ -z `sudo rabbitmqctl list_vhosts | grep child_cell` ]; then
sudo rabbitmqctl add_vhost child_cell
- sudo rabbitmqctl set_permissions -p child_cell guest ".*" ".*" ".*"
+ sudo rabbitmqctl set_permissions -p child_cell $RABBIT_USERID ".*" ".*" ".*"
fi
fi
elif is_service_enabled qpid; then
@@ -201,9 +242,9 @@
local file=$2
local section=$3
if is_service_enabled zeromq; then
- iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_zmq
+ iniset $file $section rpc_backend "zmq"
iniset $file $section rpc_zmq_matchmaker \
- ${package}.openstack.common.rpc.matchmaker_redis.MatchMakerRedis
+ oslo_messaging._drivers.matchmaker_redis.MatchMakerRedis
# Set MATCHMAKER_REDIS_HOST if running multi-node.
MATCHMAKER_REDIS_HOST=${MATCHMAKER_REDIS_HOST:-127.0.0.1}
iniset $file matchmaker_redis host $MATCHMAKER_REDIS_HOST
@@ -212,7 +253,7 @@
if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
iniset $file $section rpc_backend "amqp"
else
- iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_qpid
+ iniset $file $section rpc_backend "qpid"
fi
iniset $file $section qpid_hostname ${QPID_HOST:-$SERVICE_HOST}
if [ -n "$QPID_USERNAME" ]; then
@@ -220,9 +261,10 @@
iniset $file $section qpid_password $QPID_PASSWORD
fi
elif is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
- iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_kombu
+ iniset $file $section rpc_backend "rabbit"
iniset $file $section rabbit_hosts $RABBIT_HOST
iniset $file $section rabbit_password $RABBIT_PASSWORD
+ iniset $file $section rabbit_userid $RABBIT_USERID
fi
}
@@ -237,6 +279,21 @@
( ! is_suse )
}
+function rabbit_setuser {
+ local user="$1" pass="$2" found="" out=""
+ out=$(sudo rabbitmqctl list_users) ||
+ { echo "failed to list users" 1>&2; return 1; }
+ found=$(echo "$out" | awk '$1 == user { print $1 }' "user=$user")
+ if [ "$found" = "$user" ]; then
+ sudo rabbitmqctl change_password "$user" "$pass" ||
+ { echo "failed changing pass for '$user'" 1>&2; return 1; }
+ else
+ sudo rabbitmqctl add_user "$user" "$pass" ||
+ { echo "failed changing pass for $user"; return 1; }
+ fi
+ sudo rabbitmqctl set_permissions "$user" ".*" ".*" ".*"
+}
+
# Set up the various configuration files used by the qpidd broker
function _configure_qpid {
@@ -286,6 +343,7 @@
install_package sasl2-bin
elif is_fedora; then
install_package cyrus-sasl-lib
+ install_package cyrus-sasl-plain
fi
local sasl_conf_file=/etc/sasl2/qpidd.conf
sudo sed -i.bak '/PLAIN/!s/mech_list: /mech_list: PLAIN /' $sasl_conf_file
diff --git a/lib/sahara b/lib/sahara
index 6d1bef5..da4fbcd 100644
--- a/lib/sahara
+++ b/lib/sahara
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/sahara
# Dependencies:
@@ -9,6 +11,7 @@
# install_sahara
# install_python_saharaclient
# configure_sahara
+# sahara_register_images
# start_sahara
# stop_sahara
# cleanup_sahara
@@ -22,15 +25,10 @@
# --------
# Set up default repos
-SAHARA_REPO=${SAHARA_REPO:-${GIT_BASE}/openstack/sahara.git}
-SAHARA_BRANCH=${SAHARA_BRANCH:-master}
-
-SAHARA_PYTHONCLIENT_REPO=${SAHARA_PYTHONCLIENT_REPO:-${GIT_BASE}/openstack/python-saharaclient.git}
-SAHARA_PYTHONCLIENT_BRANCH=${SAHARA_PYTHONCLIENT_BRANCH:-master}
# Set up default directories
+GITDIR["python-saharaclient"]=$DEST/python-saharaclient
SAHARA_DIR=$DEST/sahara
-SAHARA_PYTHONCLIENT_DIR=$DEST/python-saharaclient
SAHARA_CONF_DIR=${SAHARA_CONF_DIR:-/etc/sahara}
SAHARA_CONF_FILE=${SAHARA_CONF_DIR}/sahara.conf
@@ -63,12 +61,7 @@
# service sahara admin
function create_sahara_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
- local sahara_user=$(get_or_create_user "sahara" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $sahara_user $service_tenant
+ create_service_user "sahara"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -131,14 +124,41 @@
if is_service_enabled neutron; then
iniset $SAHARA_CONF_FILE DEFAULT use_neutron true
iniset $SAHARA_CONF_FILE DEFAULT use_floating_ips true
+
+ if is_ssl_enabled_service "neutron" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE neutron ca_file $SSL_BUNDLE_FILE
+ fi
+ else
+ iniset $SAHARA_CONF_FILE DEFAULT use_neutron false
+ iniset $SAHARA_CONF_FILE DEFAULT use_floating_ips false
fi
if is_service_enabled heat; then
iniset $SAHARA_CONF_FILE DEFAULT infrastructure_engine heat
+
+ if is_ssl_enabled_service "heat" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE heat ca_file $SSL_BUNDLE_FILE
+ fi
else
iniset $SAHARA_CONF_FILE DEFAULT infrastructure_engine direct
fi
+ if is_ssl_enabled_service "cinder" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE cinder ca_file $SSL_BUNDLE_FILE
+ fi
+
+ if is_ssl_enabled_service "nova" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE nova ca_file $SSL_BUNDLE_FILE
+ fi
+
+ if is_ssl_enabled_service "swift" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE swift ca_file $SSL_BUNDLE_FILE
+ fi
+
+ if is_ssl_enabled_service "key" || is_service_enabled tls-proxy; then
+ iniset $SAHARA_CONF_FILE keystone ca_file $SSL_BUNDLE_FILE
+ fi
+
iniset $SAHARA_CONF_FILE DEFAULT use_syslog $SYSLOG
# Format logging
@@ -146,7 +166,7 @@
setup_colorized_logging $SAHARA_CONF_FILE DEFAULT
fi
- recreate_database sahara utf8
+ recreate_database sahara
$SAHARA_BIN_DIR/sahara-db-manage --config-file $SAHARA_CONF_FILE upgrade head
}
@@ -158,8 +178,21 @@
# install_python_saharaclient() - Collect source and prepare
function install_python_saharaclient {
- git_clone $SAHARA_PYTHONCLIENT_REPO $SAHARA_PYTHONCLIENT_DIR $SAHARA_PYTHONCLIENT_BRANCH
- setup_develop $SAHARA_PYTHONCLIENT_DIR
+ if use_library_from_git "python-saharaclient"; then
+ git_clone_by_name "python-saharaclient"
+ setup_dev_lib "python-saharaclient"
+ fi
+}
+
+# sahara_register_images() - Registers images in sahara image registry
+function sahara_register_images {
+ if is_service_enabled heat && [[ ! -z "$HEAT_CFN_IMAGE_URL" ]]; then
+ # Register heat image for Fake plugin
+ local fake_plugin_properties="--property _sahara_tag_0.1=True"
+ fake_plugin_properties+=" --property _sahara_tag_fake=True"
+ fake_plugin_properties+=" --property _sahara_username=fedora"
+ openstack --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT image set $(basename "$HEAT_CFN_IMAGE_URL" ".qcow2") $fake_plugin_properties
+ fi
}
# start_sahara() - Start running processes, including screen
@@ -170,7 +203,7 @@
# stop_sahara() - Stop running processes
function stop_sahara {
# Kill the Sahara screen windows
- screen -S $SCREEN_NAME -p sahara -X kill
+ stop_process sahara
}
diff --git a/lib/stackforge b/lib/stackforge
deleted file mode 100644
index 2d80dad..0000000
--- a/lib/stackforge
+++ /dev/null
@@ -1,54 +0,0 @@
-# lib/stackforge
-#
-# Functions to install stackforge libraries that we depend on so
-# that we can try their git versions during devstack gate.
-#
-# This is appropriate for python libraries that release to pypi and are
-# expected to be used beyond OpenStack like, but are requirements
-# for core services in global-requirements.
-#
-# * wsme
-# * pecan
-#
-# This is not appropriate for stackforge projects which are early stage
-# OpenStack tools
-
-# Dependencies:
-# ``functions`` file
-
-# ``stack.sh`` calls the entry points in this order:
-#
-# install_stackforge
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-WSME_DIR=$DEST/wsme
-PECAN_DIR=$DEST/pecan
-SQLALCHEMY_MIGRATE_DIR=$DEST/sqlalchemy-migrate
-
-# Entry Points
-# ------------
-
-# install_stackforge() - Collect source and prepare
-function install_stackforge {
- git_clone $WSME_REPO $WSME_DIR $WSME_BRANCH
- setup_package $WSME_DIR
-
- git_clone $PECAN_REPO $PECAN_DIR $PECAN_BRANCH
- setup_package $PECAN_DIR
-
- git_clone $SQLALCHEMY_MIGRATE_REPO $SQLALCHEMY_MIGRATE_DIR $SQLALCHEMY_MIGRATE_BRANCH
- setup_package $SQLALCHEMY_MIGRATE_DIR
-}
-
-# Restore xtrace
-$XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
diff --git a/lib/swift b/lib/swift
index 7ef4496..56baa12 100644
--- a/lib/swift
+++ b/lib/swift
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/swift
# Functions to control the configuration and operation of the **Swift** service
@@ -34,8 +36,10 @@
fi
# Set up default directories
+GITDIR["python-swiftclient"]=$DEST/python-swiftclient
+
+
SWIFT_DIR=$DEST/swift
-SWIFTCLIENT_DIR=$DEST/python-swiftclient
SWIFT_AUTH_CACHE_DIR=${SWIFT_AUTH_CACHE_DIR:-/var/cache/swift}
SWIFT_APACHE_WSGI_DIR=${SWIFT_APACHE_WSGI_DIR:-/var/www/swift}
SWIFT3_DIR=$DEST/swift3
@@ -52,8 +56,7 @@
# Set ``SWIFT_CONF_DIR`` to the location of the configuration files.
# Default is ``/etc/swift``.
-# TODO(dtroyer): remove SWIFT_CONFIG_DIR after cutting stable/grizzly
-SWIFT_CONF_DIR=${SWIFT_CONF_DIR:-${SWIFT_CONFIG_DIR:-/etc/swift}}
+SWIFT_CONF_DIR=${SWIFT_CONF_DIR:-/etc/swift}
if is_service_enabled s-proxy && is_service_enabled swift3; then
# If we are using swift3, we can default the s3 port to swift instead
@@ -79,7 +82,7 @@
# Set ``SWIFT_EXTRAS_MIDDLEWARE_LAST`` to extras middlewares that need to be at
# the end of the pipeline.
-SWIFT_EXTRAS_MIDDLEWARE_LAST=${SWIFT_EXTRAS_MIDDLEWARE_LAST}
+SWIFT_EXTRAS_MIDDLEWARE_LAST=${SWIFT_EXTRAS_MIDDLEWARE_LAST:-}
# Set ``SWIFT_EXTRAS_MIDDLEWARE_NO_AUTH`` to extras middlewares that need to be at
# the beginning of the pipeline, before authentication middlewares.
@@ -124,12 +127,12 @@
# Enable tempurl feature
SWIFT_ENABLE_TEMPURLS=${SWIFT_ENABLE_TEMPURLS:-False}
-SWIFT_TEMPURL_KEY=${SWIFT_TEMPURL_KEY}
+SWIFT_TEMPURL_KEY=${SWIFT_TEMPURL_KEY:-}
# Tell Tempest this project is present
TEMPEST_SERVICES+=,swift
-# Toggle for deploying Keystone under HTTPD + mod_wsgi
+# Toggle for deploying Swift under HTTPD + mod_wsgi
SWIFT_USE_MOD_WSGI=${SWIFT_USE_MOD_WSGI:-False}
# Functions
@@ -398,27 +401,44 @@
sed -i "/^pipeline/ { s/tempauth/${swift_pipeline} ${SWIFT_EXTRAS_MIDDLEWARE}/ ;}" ${SWIFT_CONFIG_PROXY_SERVER}
sed -i "/^pipeline/ { s/proxy-server/${SWIFT_EXTRAS_MIDDLEWARE_LAST} proxy-server/ ; }" ${SWIFT_CONFIG_PROXY_SERVER}
- iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth account_autocreate
+
iniset ${SWIFT_CONFIG_PROXY_SERVER} app:proxy-server account_autocreate true
- iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth reseller_prefix
- iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth reseller_prefix "TEMPAUTH"
+
# Configure Crossdomain
iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:crossdomain use "egg:swift#crossdomain"
- # Configure Keystone
- sed -i '/^# \[filter:authtoken\]/,/^# \[filter:keystoneauth\]$/ s/^#[ \t]*//' ${SWIFT_CONFIG_PROXY_SERVER}
- configure_auth_token_middleware ${SWIFT_CONFIG_PROXY_SERVER} swift $SWIFT_AUTH_CACHE_DIR filter:authtoken
+
# This causes the authtoken middleware to use the same python logging
# adapter provided by the swift proxy-server, so that request transaction
# IDs will included in all of its log messages.
iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken log_name swift
- iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:keystoneauth use
- iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:keystoneauth operator_roles
+ # NOTE(jamielennox): swift cannot use the regular configure_auth_token_middleware function because swift
+ # doesn't use oslo.config which is the only way to configure auth plugins with the middleare.
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken paste.filter_factory keystonemiddleware.auth_token:filter_factory
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken identity_uri $KEYSTONE_AUTH_URI
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_user swift
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_password $SERVICE_PASSWORD
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_tenant_name $SERVICE_TENANT_NAME
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_uri $KEYSTONE_SERVICE_URI
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken cafile $SSL_BUNDLE_FILE
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken signing_dir $SWIFT_AUTH_CACHE_DIR
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken delay_auth_decision 1
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken cache swift.cache
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken include_service_catalog False
+
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:keystoneauth use "egg:swift#keystoneauth"
iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:keystoneauth operator_roles "Member, admin"
+ # Configure Tempauth. In the sample config file, Keystoneauth is commented
+ # out. Make sure we uncomment Tempauth after we uncomment Keystoneauth
+ # otherwise, this code also sets the reseller_prefix for Keystoneauth.
+ iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth account_autocreate
+ iniuncomment ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth reseller_prefix
+ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:tempauth reseller_prefix "TEMPAUTH"
+
if is_service_enabled swift3; then
cat <<EOF >>${SWIFT_CONFIG_PROXY_SERVER}
# NOTE(chmou): s3token middleware is not updated yet to use only
@@ -581,13 +601,11 @@
KEYSTONE_CATALOG_BACKEND=${KEYSTONE_CATALOG_BACKEND:-sql}
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
local another_role=$(openstack role list | awk "/ anotherrole / { print \$2 }")
- local swift_user=$(get_or_create_user "swift" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $admin_role $swift_user $service_tenant
+ # NOTE(jroll): Swift doesn't need the admin role here, however Ironic uses
+ # temp urls, which break when uploaded by a non-admin role
+ create_service_user "swift" "admin"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -602,33 +620,30 @@
local swift_tenant_test1=$(get_or_create_project swifttenanttest1)
die_if_not_set $LINENO swift_tenant_test1 "Failure creating swift_tenant_test1"
- SWIFT_USER_TEST1=$(get_or_create_user swiftusertest1 $swiftusertest1_password \
- "$swift_tenant_test1" "test@example.com")
+ SWIFT_USER_TEST1=$(get_or_create_user swiftusertest1 $swiftusertest1_password "test@example.com")
die_if_not_set $LINENO SWIFT_USER_TEST1 "Failure creating SWIFT_USER_TEST1"
- get_or_add_user_role $admin_role $SWIFT_USER_TEST1 $swift_tenant_test1
+ get_or_add_user_project_role admin $SWIFT_USER_TEST1 $swift_tenant_test1
- local swift_user_test3=$(get_or_create_user swiftusertest3 $swiftusertest3_password \
- "$swift_tenant_test1" "test3@example.com")
+ local swift_user_test3=$(get_or_create_user swiftusertest3 $swiftusertest3_password "test3@example.com")
die_if_not_set $LINENO swift_user_test3 "Failure creating swift_user_test3"
- get_or_add_user_role $another_role $swift_user_test3 $swift_tenant_test1
+ get_or_add_user_project_role $another_role $swift_user_test3 $swift_tenant_test1
local swift_tenant_test2=$(get_or_create_project swifttenanttest2)
die_if_not_set $LINENO swift_tenant_test2 "Failure creating swift_tenant_test2"
- local swift_user_test2=$(get_or_create_user swiftusertest2 $swiftusertest2_password \
- "$swift_tenant_test2" "test2@example.com")
+ local swift_user_test2=$(get_or_create_user swiftusertest2 $swiftusertest2_password "test2@example.com")
die_if_not_set $LINENO swift_user_test2 "Failure creating swift_user_test2"
- get_or_add_user_role $admin_role $swift_user_test2 $swift_tenant_test2
+ get_or_add_user_project_role admin $swift_user_test2 $swift_tenant_test2
local swift_domain=$(get_or_create_domain swift_test 'Used for swift functional testing')
die_if_not_set $LINENO swift_domain "Failure creating swift_test domain"
local swift_tenant_test4=$(get_or_create_project swifttenanttest4 $swift_domain)
die_if_not_set $LINENO swift_tenant_test4 "Failure creating swift_tenant_test4"
- local swift_user_test4=$(get_or_create_user swiftusertest4 $swiftusertest4_password \
- $swift_tenant_test4 "test4@example.com" $swift_domain)
+
+ local swift_user_test4=$(get_or_create_user swiftusertest4 $swiftusertest4_password "test4@example.com" $swift_domain)
die_if_not_set $LINENO swift_user_test4 "Failure creating swift_user_test4"
- get_or_add_user_role $admin_role $swift_user_test4 $swift_tenant_test4
+ get_or_add_user_project_role admin $swift_user_test4 $swift_tenant_test4
}
# init_swift() - Initialize rings
@@ -675,8 +690,10 @@
}
function install_swiftclient {
- git_clone $SWIFTCLIENT_REPO $SWIFTCLIENT_DIR $SWIFTCLIENT_BRANCH
- setup_develop $SWIFTCLIENT_DIR
+ if use_library_from_git "python-swiftclient"; then
+ git_clone_by_name "python-swiftclient"
+ setup_dev_lib "python-swiftclient"
+ fi
}
# start_swift() - Start running processes, including screen
diff --git a/lib/tempest b/lib/tempest
index 66f1a78..8396a78 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/tempest
# Install and configure Tempest
@@ -27,7 +29,6 @@
# - ``USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION``
# - ``DEFAULT_INSTANCE_TYPE``
# - ``DEFAULT_INSTANCE_USER``
-# - ``CINDER_MULTI_LVM_BACKEND``
# - ``CINDER_ENABLED_BACKENDS``
#
# ``stack.sh`` calls the entry points in this order:
@@ -45,11 +46,12 @@
# --------
# Set up default directories
+GITDIR["tempest-lib"]=$DEST/tempest-lib
+
TEMPEST_DIR=$DEST/tempest
TEMPEST_CONFIG_DIR=${TEMPEST_CONFIG_DIR:-$TEMPEST_DIR/etc}
TEMPEST_CONFIG=$TEMPEST_CONFIG_DIR/tempest.conf
TEMPEST_STATE_PATH=${TEMPEST_STATE_PATH:=$DATA_DIR/tempest}
-TEMPEST_LIB_DIR=$DEST/tempest-lib
NOVA_SOURCE_DIR=$DEST/nova
@@ -62,6 +64,7 @@
BOTO_MATERIALS_PATH="$FILES/images/s3-materials/cirros-${CIRROS_VERSION}"
+BOTO_CONF=/etc/boto.cfg
# Cinder/Volume variables
TEMPEST_VOLUME_DRIVER=${TEMPEST_VOLUME_DRIVER:-default}
@@ -71,15 +74,28 @@
TEMPEST_STORAGE_PROTOCOL=${TEMPEST_STORAGE_PROTOCOL:-$TEMPEST_DEFAULT_STORAGE_PROTOCOL}
# Neutron/Network variables
-IPV6_ENABLED=$(trueorfalse True $IPV6_ENABLED)
-IPV6_SUBNET_ATTRIBUTES_ENABLED=$(trueorfalse True $IPV6_SUBNET_ATTRIBUTES_ENABLED)
+IPV6_ENABLED=$(trueorfalse True IPV6_ENABLED)
+IPV6_SUBNET_ATTRIBUTES_ENABLED=$(trueorfalse True IPV6_SUBNET_ATTRIBUTES_ENABLED)
# Functions
# ---------
+# remove_disabled_extension - removes disabled extensions from the list of extensions
+# to test for a given service
+function remove_disabled_extensions {
+ local extensions_list=$1
+ shift
+ local disabled_exts=$*
+ for ext_to_remove in ${disabled_exts//,/ } ; do
+ extensions_list=${extensions_list/$ext_to_remove","}
+ done
+ echo $extensions_list
+}
+
# configure_tempest() - Set config files, create data dirs, etc
function configure_tempest {
- setup_develop $TEMPEST_DIR
+ # install testr since its used to process tempest logs
+ pip_install `grep -h testrepository $REQUIREMENTS_DIR/global-requirements.txt | cut -d\# -f1`
local image_lines
local images
local num_images
@@ -110,34 +126,36 @@
# ... Also ensure we only take active images, so we don't get snapshots in process
declare -a images
- while read -r IMAGE_NAME IMAGE_UUID; do
- if [ "$IMAGE_NAME" = "$DEFAULT_IMAGE_NAME" ]; then
- image_uuid="$IMAGE_UUID"
- image_uuid_alt="$IMAGE_UUID"
- fi
- images+=($IMAGE_UUID)
- # TODO(stevemar): update this command to use openstackclient's `openstack image list`
- # when it supports listing by status.
- done < <(glance image-list --status=active | awk -F'|' '!/^(+--)|ID|aki|ari/ { print $3,$2 }')
+ if is_service_enabled glance; then
+ while read -r IMAGE_NAME IMAGE_UUID; do
+ if [ "$IMAGE_NAME" = "$DEFAULT_IMAGE_NAME" ]; then
+ image_uuid="$IMAGE_UUID"
+ image_uuid_alt="$IMAGE_UUID"
+ fi
+ images+=($IMAGE_UUID)
+ # TODO(stevemar): update this command to use openstackclient's `openstack image list`
+ # when it supports listing by status.
+ done < <(glance image-list --status=active | awk -F'|' '!/^(+--)|ID|aki|ari/ { print $3,$2 }')
- case "${#images[*]}" in
- 0)
- echo "Found no valid images to use!"
- exit 1
- ;;
- 1)
- if [ -z "$image_uuid" ]; then
- image_uuid=${images[0]}
- image_uuid_alt=${images[0]}
- fi
- ;;
- *)
- if [ -z "$image_uuid" ]; then
- image_uuid=${images[0]}
- image_uuid_alt=${images[1]}
- fi
- ;;
- esac
+ case "${#images[*]}" in
+ 0)
+ echo "Found no valid images to use!"
+ exit 1
+ ;;
+ 1)
+ if [ -z "$image_uuid" ]; then
+ image_uuid=${images[0]}
+ image_uuid_alt=${images[0]}
+ fi
+ ;;
+ *)
+ if [ -z "$image_uuid" ]; then
+ image_uuid=${images[0]}
+ image_uuid_alt=${images[1]}
+ fi
+ ;;
+ esac
+ fi
# Create tempest.conf from tempest.conf.sample
# copy every time, because the image UUIDS are going to change
@@ -161,63 +179,65 @@
ALT_TENANT_NAME=${ALT_TENANT_NAME:-alt_demo}
ADMIN_TENANT_ID=$(openstack project list | awk "/ admin / { print \$2 }")
- # If the ``DEFAULT_INSTANCE_TYPE`` not declared, use the new behavior
- # Tempest creates instane types for himself
- if [[ -z "$DEFAULT_INSTANCE_TYPE" ]]; then
- available_flavors=$(nova flavor-list)
- if [[ ! ( $available_flavors =~ 'm1.nano' ) ]]; then
- if is_arch "ppc64"; then
- # qemu needs at least 128MB of memory to boot on ppc64
- nova flavor-create m1.nano 42 128 0 1
- else
- nova flavor-create m1.nano 42 64 0 1
+ if is_service_enabled nova; then
+ # If the ``DEFAULT_INSTANCE_TYPE`` not declared, use the new behavior
+ # Tempest creates instane types for himself
+ if [[ -z "$DEFAULT_INSTANCE_TYPE" ]]; then
+ available_flavors=$(nova flavor-list)
+ if [[ ! ( $available_flavors =~ 'm1.nano' ) ]]; then
+ if is_arch "ppc64"; then
+ # qemu needs at least 128MB of memory to boot on ppc64
+ nova flavor-create m1.nano 42 128 0 1
+ else
+ nova flavor-create m1.nano 42 64 0 1
+ fi
fi
- fi
- flavor_ref=42
- boto_instance_type=m1.nano
- if [[ ! ( $available_flavors =~ 'm1.micro' ) ]]; then
- if is_arch "ppc64"; then
- nova flavor-create m1.micro 84 256 0 1
- else
- nova flavor-create m1.micro 84 128 0 1
+ flavor_ref=42
+ boto_instance_type=m1.nano
+ if [[ ! ( $available_flavors =~ 'm1.micro' ) ]]; then
+ if is_arch "ppc64"; then
+ nova flavor-create m1.micro 84 256 0 1
+ else
+ nova flavor-create m1.micro 84 128 0 1
+ fi
fi
- fi
- flavor_ref_alt=84
- else
- # Check Nova for existing flavors and, if set, look for the
- # ``DEFAULT_INSTANCE_TYPE`` and use that.
- boto_instance_type=$DEFAULT_INSTANCE_TYPE
- flavor_lines=`nova flavor-list`
- IFS=$'\r\n'
- flavors=""
- for line in $flavor_lines; do
- f=$(echo $line | awk "/ $DEFAULT_INSTANCE_TYPE / { print \$2 }")
- flavors="$flavors $f"
- done
+ flavor_ref_alt=84
+ else
+ # Check Nova for existing flavors and, if set, look for the
+ # ``DEFAULT_INSTANCE_TYPE`` and use that.
+ boto_instance_type=$DEFAULT_INSTANCE_TYPE
+ flavor_lines=`nova flavor-list`
+ IFS=$'\r\n'
+ flavors=""
+ for line in $flavor_lines; do
+ f=$(echo $line | awk "/ $DEFAULT_INSTANCE_TYPE / { print \$2 }")
+ flavors="$flavors $f"
+ done
- for line in $flavor_lines; do
- flavors="$flavors `echo $line | grep -v "^\(|\s*ID\|+--\)" | cut -d' ' -f2`"
- done
+ for line in $flavor_lines; do
+ flavors="$flavors `echo $line | grep -v "^\(|\s*ID\|+--\)" | cut -d' ' -f2`"
+ done
- IFS=" "
- flavors=($flavors)
- num_flavors=${#flavors[*]}
- echo "Found $num_flavors flavors"
- if [[ $num_flavors -eq 0 ]]; then
- echo "Found no valid flavors to use!"
- exit 1
- fi
- flavor_ref=${flavors[0]}
- flavor_ref_alt=$flavor_ref
-
- # ensure flavor_ref and flavor_ref_alt have different values
- # some resize instance in tempest tests depends on this.
- for f in ${flavors[@]:1}; do
- if [[ $f -ne $flavor_ref ]]; then
- flavor_ref_alt=$f
- break
+ IFS=" "
+ flavors=($flavors)
+ num_flavors=${#flavors[*]}
+ echo "Found $num_flavors flavors"
+ if [[ $num_flavors -eq 0 ]]; then
+ echo "Found no valid flavors to use!"
+ exit 1
fi
- done
+ flavor_ref=${flavors[0]}
+ flavor_ref_alt=$flavor_ref
+
+ # ensure flavor_ref and flavor_ref_alt have different values
+ # some resize instance in tempest tests depends on this.
+ for f in ${flavors[@]:1}; do
+ if [[ $f -ne $flavor_ref ]]; then
+ flavor_ref_alt=$f
+ break
+ fi
+ done
+ fi
fi
if [ "$Q_USE_NAMESPACE" != "False" ]; then
@@ -242,6 +262,7 @@
fi
fi
+ iniset $TEMPEST_CONFIG DEFAULT use_syslog $SYSLOG
# Oslo
iniset $TEMPEST_CONFIG DEFAULT lock_path $TEMPEST_STATE_PATH
mkdir -p $TEMPEST_STATE_PATH
@@ -257,7 +278,7 @@
# Identity
iniset $TEMPEST_CONFIG identity uri "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:5000/v2.0/"
- iniset $TEMPEST_CONFIG identity uri_v3 "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:5000/v3/"
+ iniset $TEMPEST_CONFIG identity uri_v3 "$KEYSTONE_SERVICE_URI_V3"
iniset $TEMPEST_CONFIG identity username $TEMPEST_USERNAME
iniset $TEMPEST_CONFIG identity password "$password"
iniset $TEMPEST_CONFIG identity tenant_name $TEMPEST_TENANT_NAME
@@ -270,6 +291,9 @@
iniset $TEMPEST_CONFIG identity admin_tenant_id $ADMIN_TENANT_ID
iniset $TEMPEST_CONFIG identity admin_domain_name $ADMIN_DOMAIN_NAME
iniset $TEMPEST_CONFIG identity auth_version ${TEMPEST_AUTH_VERSION:-v2}
+ if is_ssl_enabled_service "key" || is_service_enabled tls-proxy; then
+ iniset $TEMPEST_CONFIG identity ca_certificates_file $SSL_BUNDLE_FILE
+ fi
# Image
# for the gate we want to be able to override this variable so we aren't
@@ -278,8 +302,10 @@
iniset $TEMPEST_CONFIG image http_image $TEMPEST_HTTP_IMAGE
fi
+ # Auth
+ iniset $TEMPEST_CONFIG auth allow_tenant_isolation ${TEMPEST_ALLOW_TENANT_ISOLATION:-True}
+
# Compute
- iniset $TEMPEST_CONFIG compute allow_tenant_isolation ${TEMPEST_ALLOW_TENANT_ISOLATION:-True}
iniset $TEMPEST_CONFIG compute ssh_user ${DEFAULT_INSTANCE_USER:-cirros} # DEPRECATED
iniset $TEMPEST_CONFIG compute network_for_ssh $PRIVATE_NETWORK_NAME
iniset $TEMPEST_CONFIG compute ip_version_for_ssh 4
@@ -293,12 +319,25 @@
iniset $TEMPEST_CONFIG compute ssh_connect_method $ssh_connect_method
# Compute Features
+ # Run verify_tempest_config -ur to retrieve enabled extensions on API endpoints
+ # NOTE(mtreinish): This must be done after auth settings are added to the tempest config
+ local tmp_cfg_file=$(mktemp)
+ cd $TEMPEST_DIR
+ tox -evenv -- verify-tempest-config -uro $tmp_cfg_file
+
+ local compute_api_extensions=${COMPUTE_API_EXTENSIONS:-"all"}
+ if [[ ! -z "$DISABLE_COMPUTE_API_EXTENSIONS" ]]; then
+ # Enabled extensions are either the ones explicitly specified or those available on the API endpoint
+ compute_api_extensions=${COMPUTE_API_EXTENSIONS:-$(iniget $tmp_cfg_file compute-feature-enabled api_extensions | tr -d " ")}
+ # Remove disabled extensions
+ compute_api_extensions=$(remove_disabled_extensions $compute_api_extensions $DISABLE_COMPUTE_API_EXTENSIONS)
+ fi
+
iniset $TEMPEST_CONFIG compute-feature-enabled resize True
iniset $TEMPEST_CONFIG compute-feature-enabled live_migration ${LIVE_MIGRATION_AVAILABLE:-False}
iniset $TEMPEST_CONFIG compute-feature-enabled change_password False
iniset $TEMPEST_CONFIG compute-feature-enabled block_migration_for_live_migration ${USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION:-False}
- iniset $TEMPEST_CONFIG compute-feature-enabled api_extensions ${COMPUTE_API_EXTENSIONS:-"all"}
- iniset $TEMPEST_CONFIG compute-feature-disabled api_extensions ${DISABLE_COMPUTE_API_EXTENSIONS}
+ iniset $TEMPEST_CONFIG compute-feature-enabled api_extensions $compute_api_extensions
# Compute admin
iniset $TEMPEST_CONFIG "compute-admin" username $ADMIN_USERNAME
@@ -313,11 +352,18 @@
iniset $TEMPEST_CONFIG network default_network "$FIXED_RANGE"
iniset $TEMPEST_CONFIG network-feature-enabled ipv6 "$IPV6_ENABLED"
iniset $TEMPEST_CONFIG network-feature-enabled ipv6_subnet_attributes "$IPV6_SUBNET_ATTRIBUTES_ENABLED"
- iniset $TEMPEST_CONFIG network-feature-enabled api_extensions ${NETWORK_API_EXTENSIONS:-"all"}
- iniset $TEMPEST_CONFIG network-feature-disabled api_extensions ${DISABLE_NETWORK_API_EXTENSIONS}
+
+ local network_api_extensions=${NETWORK_API_EXTENSIONS:-"all"}
+ if [[ ! -z "$DISABLE_NETWORK_API_EXTENSIONS" ]]; then
+ # Enabled extensions are either the ones explicitly specified or those available on the API endpoint
+ network_api_extensions=${NETWORK_API_EXTENSIONS:-$(iniget $tmp_cfg_file network-feature-enabled api_extensions | tr -d " ")}
+ # Remove disabled extensions
+ network_api_extensions=$(remove_disabled_extensions $network_api_extensions $DISABLE_NETWORK_API_EXTENSIONS)
+ fi
+ iniset $TEMPEST_CONFIG network-feature-enabled api_extensions $network_api_extensions
# boto
- iniset $TEMPEST_CONFIG boto ec2_url "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/services/Cloud"
+ iniset $TEMPEST_CONFIG boto ec2_url "$EC2_SERVICE_PROTOCOL://$SERVICE_HOST:8773/"
iniset $TEMPEST_CONFIG boto s3_url "http://$SERVICE_HOST:${S3_SERVICE_PORT:-3333}"
iniset $TEMPEST_CONFIG boto s3_materials_path "$BOTO_MATERIALS_PATH"
iniset $TEMPEST_CONFIG boto ari_manifest cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-initrd.manifest.xml
@@ -330,7 +376,7 @@
# Orchestration Tests
if is_service_enabled heat; then
if [[ ! -z "$HEAT_CFN_IMAGE_URL" ]]; then
- iniset $TEMPEST_CONFIG orchestration image_ref $(basename "$HEAT_CFN_IMAGE_URL" ".qcow2")
+ iniset $TEMPEST_CONFIG orchestration image_ref $(basename "${HEAT_CFN_IMAGE_URL%.*}")
fi
# build a specialized heat flavor
available_flavors=$(nova flavor-list)
@@ -356,12 +402,25 @@
iniset $TEMPEST_CONFIG telemetry too_slow_to_test "False"
# Object storage
- iniset $TEMPEST_CONFIG object-storage-feature-enabled discoverable_apis ${OBJECT_STORAGE_API_EXTENSIONS:-"all"}
- iniset $TEMPEST_CONFIG object-storage-feature-disabled discoverable_apis ${OBJECT_STORAGE_DISABLE_API_EXTENSIONS}
+ local object_storage_api_extensions=${OBJECT_STORAGE_API_EXTENSIONS:-"all"}
+ if [[ ! -z "$DISABLE_OBJECT_STORAGE_API_EXTENSIONS" ]]; then
+ # Enabled extensions are either the ones explicitly specified or those available on the API endpoint
+ object_storage_api_extensions=${OBJECT_STORAGE_API_EXTENSIONS:-$(iniget $tmp_cfg_file object-storage-feature-enabled discoverable_apis | tr -d " ")}
+ # Remove disabled extensions
+ object_storage_api_extensions=$(remove_disabled_extensions $object_storage_api_extensions $DISABLE_STORAGE_API_EXTENSIONS)
+ fi
+ iniset $TEMPEST_CONFIG object-storage-feature-enabled discoverable_apis $object_storage_api_extensions
# Volume
- iniset $TEMPEST_CONFIG volume-feature-enabled api_extensions ${VOLUME_API_EXTENSIONS:-"all"}
- iniset $TEMPEST_CONFIG volume-feature-disabled api_extensions ${DISABLE_VOLUME_API_EXTENSIONS}
+ local volume_api_extensions=${VOLUME_API_EXTENSIONS:-"all"}
+ if [[ ! -z "$DISABLE_VOLUME_API_EXTENSIONS" ]]; then
+ # Enabled extensions are either the ones explicitly specified or those available on the API endpoint
+ volume_api_extensions=${VOLUME_API_EXTENSIONS:-$(iniget $tmp_cfg_file volume-feature-enabled api_extensions | tr -d " ")}
+ # Remove disabled extensions
+ volume_api_extensions=$(remove_disabled_extensions $volume_api_extensions $DISABLE_VOLUME_API_EXTENSIONS)
+ fi
+ iniset $TEMPEST_CONFIG volume-feature-enabled api_extensions $volume_api_extensions
+
if ! is_service_enabled c-bak; then
iniset $TEMPEST_CONFIG volume-feature-enabled backup False
fi
@@ -383,8 +442,8 @@
iniset $TEMPEST_CONFIG volume vendor_name "$TEMPEST_VOLUME_VENDOR"
fi
if [ $TEMPEST_VOLUME_DRIVER != "default" -o \
- $TEMPEST_STORAGE_PROTOCOL != $TEMPEST_DEFAULT_STORAGE_PROTOCOL ]; then
- iniset $TEMPEST_CONFIG volume storage_protocol $TEMPEST_STORAGE_PROTOCOL
+ "$TEMPEST_STORAGE_PROTOCOL" != "$TEMPEST_DEFAULT_STORAGE_PROTOCOL" ]; then
+ iniset $TEMPEST_CONFIG volume storage_protocol "$TEMPEST_STORAGE_PROTOCOL"
fi
# Dashboard
@@ -409,6 +468,13 @@
iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
fi
+ # Libvirt-LXC
+ if [ "$VIRT_DRIVER" = "libvirt" ] && [ "$LIBVIRT_TYPE" = "lxc" ]; then
+ iniset $TEMPEST_CONFIG compute-feature-enabled rescue False
+ iniset $TEMPEST_CONFIG compute-feature-enabled resize False
+ iniset $TEMPEST_CONFIG compute-feature-enabled suspend False
+ fi
+
# service_available
for service in ${TEMPEST_SERVICES//,/ }; do
if is_service_enabled $service ; then
@@ -418,6 +484,12 @@
fi
done
+ if is_ssl_enabled_service "key" || is_service_enabled tls-proxy; then
+ # Use the BOTO_CONFIG environment variable to point to this file
+ iniset $BOTO_CONF Boto ca_certificates_file $SSL_BUNDLE_FILE
+ sudo chown $STACK_USER $BOTO_CONF
+ fi
+
# Restore IFS
IFS=$ifs
}
@@ -434,15 +506,17 @@
# Tempest has some tests that validate various authorization checks
# between two regular users in separate tenants
get_or_create_project alt_demo
- get_or_create_user alt_demo "$ADMIN_PASSWORD" alt_demo "alt_demo@example.com"
- get_or_add_user_role Member alt_demo alt_demo
+ get_or_create_user alt_demo "$ADMIN_PASSWORD" "alt_demo@example.com"
+ get_or_add_user_project_role Member alt_demo alt_demo
fi
}
# install_tempest_lib() - Collect source, prepare, and install tempest-lib
function install_tempest_lib {
- git_clone $TEMPEST_LIB_REPO $TEMPEST_LIB_DIR $TEMPEST_LIB_BRANCH
- setup_develop $TEMPEST_LIB_DIR
+ if use_library_from_git "tempest-lib"; then
+ git_clone_by_name "tempest-lib"
+ setup_dev_lib "tempest-lib"
+ fi
}
# install_tempest() - Collect source and prepare
@@ -460,20 +534,22 @@
local kernel="$image_dir/${base_image_name}-vmlinuz"
local ramdisk="$image_dir/${base_image_name}-initrd"
local disk_image="$image_dir/${base_image_name}-blank.img"
- # if the cirros uec downloaded and the system is uec capable
- if [ -f "$kernel" -a -f "$ramdisk" -a -f "$disk_image" -a "$VIRT_DRIVER" != "openvz" \
- -a \( "$LIBVIRT_TYPE" != "lxc" -o "$VIRT_DRIVER" != "libvirt" \) ]; then
- echo "Prepare aki/ari/ami Images"
- mkdir -p $BOTO_MATERIALS_PATH
- ( #new namespace
- # tenant:demo ; user: demo
- source $TOP_DIR/accrc/demo/demo
- euca-bundle-image -r ${CIRROS_ARCH} -i "$kernel" --kernel true -d "$BOTO_MATERIALS_PATH"
- euca-bundle-image -r ${CIRROS_ARCH} -i "$ramdisk" --ramdisk true -d "$BOTO_MATERIALS_PATH"
- euca-bundle-image -r ${CIRROS_ARCH} -i "$disk_image" -d "$BOTO_MATERIALS_PATH"
- ) 2>&1 </dev/null | cat
- else
- echo "Boto materials are not prepared"
+ if is_service_enabled nova; then
+ # if the cirros uec downloaded and the system is uec capable
+ if [ -f "$kernel" -a -f "$ramdisk" -a -f "$disk_image" -a "$VIRT_DRIVER" != "openvz" \
+ -a \( "$LIBVIRT_TYPE" != "lxc" -o "$VIRT_DRIVER" != "libvirt" \) ]; then
+ echo "Prepare aki/ari/ami Images"
+ mkdir -p $BOTO_MATERIALS_PATH
+ ( #new namespace
+ # tenant:demo ; user: demo
+ source $TOP_DIR/accrc/demo/demo
+ euca-bundle-image -r ${CIRROS_ARCH} -i "$kernel" --kernel true -d "$BOTO_MATERIALS_PATH"
+ euca-bundle-image -r ${CIRROS_ARCH} -i "$ramdisk" --ramdisk true -d "$BOTO_MATERIALS_PATH"
+ euca-bundle-image -r ${CIRROS_ARCH} -i "$disk_image" -d "$BOTO_MATERIALS_PATH"
+ ) 2>&1 </dev/null | cat
+ else
+ echo "Boto materials are not prepared"
+ fi
fi
}
diff --git a/lib/template b/lib/template
index f77409b..2703788 100644
--- a/lib/template
+++ b/lib/template
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/template
# Functions to control the configuration and operation of the XXXX service
# <do not include this template file in ``stack.sh``!>
diff --git a/lib/tls b/lib/tls
index fdb798f..677895b 100644
--- a/lib/tls
+++ b/lib/tls
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/tls
# Functions to control the configuration and operation of the TLS proxy service
diff --git a/lib/trove b/lib/trove
index 4ac7293..e1b307a 100644
--- a/lib/trove
+++ b/lib/trove
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/trove
# Functions to control the configuration and operation of the **Trove** service
@@ -28,8 +30,9 @@
fi
# Set up default configuration
+GITDIR["python-troveclient"]=$DEST/python-troveclient
+
TROVE_DIR=$DEST/trove
-TROVECLIENT_DIR=$DEST/python-troveclient
TROVE_CONF_DIR=/etc/trove
TROVE_LOCAL_CONF_DIR=$TROVE_DIR/etc/trove
TROVE_AUTH_CACHE_DIR=${TROVE_AUTH_CACHE_DIR:-/var/cache/trove}
@@ -76,14 +79,9 @@
# service trove admin # if enabled
function create_trove_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- local service_role=$(openstack role list | awk "/ admin / { print \$2 }")
-
if [[ "$ENABLED_SERVICES" =~ "trove" ]]; then
- local trove_user=$(get_or_create_user "trove" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $service_role $trove_user $service_tenant
+ create_service_user "trove"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
@@ -109,10 +107,6 @@
rm -fr $TROVE_CONF_DIR/*
}
-# configure_troveclient() - Set config files, create data dirs, etc
-function configure_troveclient {
- setup_develop $TROVECLIENT_DIR
-}
# configure_trove() - Set config files, create data dirs, etc
function configure_trove {
@@ -124,28 +118,28 @@
sudo chown -R $STACK_USER: ${TROVE_CONF_DIR}
sudo chown -R $STACK_USER: ${TROVE_AUTH_CACHE_DIR}
- # Copy api-paste file over to the trove conf dir and configure it
+ # Copy api-paste file over to the trove conf dir
cp $TROVE_LOCAL_CONF_DIR/api-paste.ini $TROVE_CONF_DIR/api-paste.ini
- TROVE_API_PASTE_INI=$TROVE_CONF_DIR/api-paste.ini
-
- configure_auth_token_middleware $TROVE_API_PASTE_INI trove $TROVE_AUTH_CACHE_DIR filter:authtoken
# (Re)create trove conf files
rm -f $TROVE_CONF_DIR/trove.conf
rm -f $TROVE_CONF_DIR/trove-taskmanager.conf
rm -f $TROVE_CONF_DIR/trove-conductor.conf
+ iniset $TROVE_CONF_DIR/trove.conf DEFAULT rabbit_userid $RABBIT_USERID
iniset $TROVE_CONF_DIR/trove.conf DEFAULT rabbit_password $RABBIT_PASSWORD
iniset $TROVE_CONF_DIR/trove.conf DEFAULT sql_connection `database_connection_url trove`
iniset $TROVE_CONF_DIR/trove.conf DEFAULT default_datastore $TROVE_DATASTORE_TYPE
setup_trove_logging $TROVE_CONF_DIR/trove.conf
iniset $TROVE_CONF_DIR/trove.conf DEFAULT trove_api_workers "$API_WORKERS"
+ configure_auth_token_middleware $TROVE_CONF_DIR/trove.conf trove $TROVE_AUTH_CACHE_DIR
# (Re)create trove taskmanager conf file if needed
if is_service_enabled tr-tmgr; then
TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION
+ iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_userid $RABBIT_USERID
iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_password $RABBIT_PASSWORD
iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT sql_connection `database_connection_url trove`
iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT taskmanager_manager trove.taskmanager.manager.Manager
@@ -158,6 +152,7 @@
# (Re)create trove conductor conf file if needed
if is_service_enabled tr-cond; then
+ iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT rabbit_userid $RABBIT_USERID
iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT rabbit_password $RABBIT_PASSWORD
iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT sql_connection `database_connection_url trove`
iniset $TROVE_CONF_DIR/trove-conductor.conf DEFAULT nova_proxy_admin_user radmin
@@ -169,6 +164,7 @@
fi
# Set up Guest Agent conf
+ iniset $TROVE_CONF_DIR/trove-guestagent.conf DEFAULT rabbit_userid $RABBIT_USERID
iniset $TROVE_CONF_DIR/trove-guestagent.conf DEFAULT rabbit_host $TROVE_HOST_GATEWAY
iniset $TROVE_CONF_DIR/trove-guestagent.conf DEFAULT rabbit_password $RABBIT_PASSWORD
iniset $TROVE_CONF_DIR/trove-guestagent.conf DEFAULT nova_proxy_admin_user radmin
@@ -184,7 +180,10 @@
# install_troveclient() - Collect source and prepare
function install_troveclient {
- git_clone $TROVECLIENT_REPO $TROVECLIENT_DIR $TROVECLIENT_BRANCH
+ if use_library_from_git "python-troveclient"; then
+ git_clone_by_name "python-troveclient"
+ setup_dev_lib "python-troveclient"
+ fi
}
# install_trove() - Collect source and prepare
@@ -195,13 +194,13 @@
# init_trove() - Initializes Trove Database as a Service
function init_trove {
# (Re)Create trove db
- recreate_database trove utf8
+ recreate_database trove
# Initialize the trove database
$TROVE_BIN_DIR/trove-manage db_sync
# If no guest image is specified, skip remaining setup
- [ -z "$TROVE_GUEST_IMAGE_URL"] && return 0
+ [ -z "$TROVE_GUEST_IMAGE_URL" ] && return 0
# Find the glance id for the trove guest image
# The image is uploaded by stack.sh -- see $IMAGE_URLS handling
diff --git a/lib/zaqar b/lib/zaqar
index b8570eb..4a24415 100644
--- a/lib/zaqar
+++ b/lib/zaqar
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# lib/zaqar
# Install and start **Zaqar** service
@@ -111,7 +113,9 @@
[ ! -d $ZAQAR_API_LOG_DIR ] && sudo mkdir -m 755 -p $ZAQAR_API_LOG_DIR
sudo chown $USER $ZAQAR_API_LOG_DIR
+ iniset $ZAQAR_CONF DEFAULT debug True
iniset $ZAQAR_CONF DEFAULT verbose True
+ iniset $ZAQAR_CONF DEFAULT admin_mode True
iniset $ZAQAR_CONF DEFAULT use_syslog $SYSLOG
iniset $ZAQAR_CONF DEFAULT log_file $ZAQAR_API_LOG_FILE
iniset $ZAQAR_CONF 'drivers:transport:wsgi' bind $ZAQAR_SERVICE_HOST
@@ -131,6 +135,12 @@
configure_redis
fi
+ if is_service_enabled qpid || [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; then
+ iniset $ZAQAR_CONF DEFAULT notification_driver messaging
+ iniset $ZAQAR_CONF DEFAULT control_exchange zaqar
+ fi
+ iniset_rpc_backend zaqar $ZAQAR_CONF DEFAULT
+
cleanup_zaqar
}
@@ -205,12 +215,7 @@
}
function create_zaqar_accounts {
- local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
-
- local zaqar_user=$(get_or_create_user "zaqar" \
- "$SERVICE_PASSWORD" $service_tenant)
- get_or_add_user_role $ADMIN_ROLE $zaqar_user $service_tenant
+ create_service_user "zaqar"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
diff --git a/pkg/elasticsearch.sh b/pkg/elasticsearch.sh
new file mode 100755
index 0000000..15e1b2b
--- /dev/null
+++ b/pkg/elasticsearch.sh
@@ -0,0 +1,126 @@
+#!/bin/bash -xe
+
+# basic reference point for things like filecache
+#
+# TODO(sdague): once we have a few of these I imagine the download
+# step can probably be factored out to something nicer
+TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
+FILES=$TOP_DIR/files
+source $TOP_DIR/functions
+
+# Package source and version, all pkg files are expected to have
+# something like this, as well as a way to override them.
+ELASTICSEARCH_VERSION=${ELASTICSEARCH_VERSION:-1.4.2}
+ELASTICSEARCH_BASEURL=${ELASTICSEARCH_BASEURL:-https://download.elasticsearch.org/elasticsearch/elasticsearch}
+
+# Elastic search actual implementation
+function wget_elasticsearch {
+ local file=${1}
+
+ if [ ! -f ${FILES}/${file} ]; then
+ wget $ELASTICSEARCH_BASEURL/${file} -O ${FILES}/${file}
+ fi
+
+ if [ ! -f ${FILES}/${file}.sha1.txt ]; then
+ wget $ELASTICSEARCH_BASEURL/${file}.sha1.txt -O ${FILES}/${file}.sha1.txt
+ fi
+
+ pushd ${FILES}; sha1sum ${file} > ${file}.sha1.gen; popd
+
+ if ! diff ${FILES}/${file}.sha1.gen ${FILES}/${file}.sha1.txt; then
+ echo "Invalid elasticsearch download. Could not install."
+ return 1
+ fi
+ return 0
+}
+
+function download_elasticsearch {
+ if is_ubuntu; then
+ wget_elasticsearch elasticsearch-${ELASTICSEARCH_VERSION}.deb
+ elif is_fedora; then
+ wget_elasticsearch elasticsearch-${ELASTICSEARCH_VERSION}.noarch.rpm
+ fi
+}
+
+function configure_elasticsearch {
+ # currently a no op
+ ::
+}
+
+function start_elasticsearch {
+ if is_ubuntu; then
+ sudo /etc/init.d/elasticsearch start
+ elif is_fedora; then
+ sudo /bin/systemctl start elasticsearch.service
+ else
+ echo "Unsupported architecture...can not start elasticsearch."
+ fi
+}
+
+function stop_elasticsearch {
+ if is_ubuntu; then
+ sudo /etc/init.d/elasticsearch stop
+ elif is_fedora; then
+ sudo /bin/systemctl stop elasticsearch.service
+ else
+ echo "Unsupported architecture...can not stop elasticsearch."
+ fi
+}
+
+function install_elasticsearch {
+ if is_package_installed elasticsearch; then
+ echo "Note: elasticsearch was already installed."
+ return
+ fi
+ if is_ubuntu; then
+ is_package_installed openjdk-7-jre-headless || install_package openjdk-7-jre-headless
+
+ sudo dpkg -i ${FILES}/elasticsearch-${ELASTICSEARCH_VERSION}.deb
+ sudo update-rc.d elasticsearch defaults 95 10
+ elif is_fedora; then
+ is_package_installed java-1.7.0-openjdk-headless || install_package java-1.7.0-openjdk-headless
+ yum_install ${FILES}/elasticsearch-${ELASTICSEARCH_VERSION}.noarch.rpm
+ sudo /bin/systemctl daemon-reload
+ sudo /bin/systemctl enable elasticsearch.service
+ else
+ echo "Unsupported install of elasticsearch on this architecture."
+ fi
+}
+
+function uninstall_elasticsearch {
+ if is_package_installed elasticsearch; then
+ if is_ubuntu; then
+ sudo apt-get purge elasticsearch
+ elif is_fedora; then
+ sudo yum remove elasticsearch
+ else
+ echo "Unsupported install of elasticsearch on this architecture."
+ fi
+ fi
+}
+
+# The PHASE dispatcher. All pkg files are expected to basically cargo
+# cult the case statement.
+PHASE=$1
+echo "Phase is $PHASE"
+
+case $PHASE in
+ download)
+ download_elasticsearch
+ ;;
+ install)
+ install_elasticsearch
+ ;;
+ configure)
+ configure_elasticsearch
+ ;;
+ start)
+ start_elasticsearch
+ ;;
+ stop)
+ stop_elasticsearch
+ ;;
+ uninstall)
+ uninstall_elasticsearch
+ ;;
+esac
diff --git a/run_tests.sh b/run_tests.sh
index bf90332..3ba7e10 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -18,47 +18,18 @@
PASSES=""
FAILURES=""
-# Check the return code and add the test to PASSES or FAILURES as appropriate
-# pass_fail <result> <expected> <name>
-function pass_fail {
- local result=$1
- local expected=$2
- local test_name=$3
-
- if [[ $result -ne $expected ]]; then
- FAILURES="$FAILURES $test_name"
- else
- PASSES="$PASSES $test_name"
- fi
-}
-
-if [[ -n $@ ]]; then
- FILES=$@
-else
- LIBS=`find lib -type f | grep -v \.md`
- SCRIPTS=`find . -type f -name \*\.sh`
- EXTRA="functions functions-common stackrc openrc exerciserc eucarc"
- FILES="$SCRIPTS $LIBS $EXTRA"
-fi
-
-echo "Running bash8..."
-
-tox -ebashate
-pass_fail $? 0 bash8
-
-
# Test that no one is trying to land crazy refs as branches
-echo "Ensuring we don't have crazy refs"
+for testfile in tests/test_*.sh; do
+ $testfile
+ if [[ $? -eq 0 ]]; then
+ PASSES="$PASSES $testfile"
+ else
+ FAILURES="$FAILURES $testfile"
+ fi
+done
-REFS=`grep BRANCH stackrc | grep -v -- '-master'`
-rc=$?
-pass_fail $rc 1 crazy-refs
-if [[ $rc -eq 0 ]]; then
- echo "Branch defaults must be master. Found:"
- echo $REFS
-fi
-
+# Summary display now that all is said and done
echo "====================================================================="
for script in $PASSES; do
echo PASS $script
diff --git a/samples/local.conf b/samples/local.conf
index 20c5892..9e0b540 100644
--- a/samples/local.conf
+++ b/samples/local.conf
@@ -93,3 +93,9 @@
# moved by setting ``SWIFT_DATA_DIR``. The directory will be created
# if it does not exist.
SWIFT_DATA_DIR=$DEST/data
+
+# Tempest
+# -------
+
+# Install the tempest test suite
+enable_service tempest
\ No newline at end of file
diff --git a/stack.sh b/stack.sh
index ec13338..43cb991 100755
--- a/stack.sh
+++ b/stack.sh
@@ -13,11 +13,11 @@
# a multi-node developer install.
# To keep this script simple we assume you are running on a recent **Ubuntu**
-# (12.04 Precise or newer) or **Fedora** (F18 or newer) machine. (It may work
-# on other platforms but support for those platforms is left to those who added
-# them to DevStack.) It should work in a VM or physical server. Additionally
-# we maintain a list of ``apt`` and ``rpm`` dependencies and other configuration
-# files in this repo.
+# (14.04 Trusty or newer), **Fedora** (F20 or newer), or **CentOS/RHEL**
+# (7 or newer) machine. (It may work on other platforms but support for those
+# platforms is left to those who added them to DevStack.) It should work in
+# a VM or physical server. Additionally, we maintain a list of ``apt`` and
+# ``rpm`` dependencies and other configuration files in this repo.
# Learn more and get the most recent version at http://devstack.org
@@ -40,6 +40,12 @@
# Keep track of the devstack directory
TOP_DIR=$(cd $(dirname "$0") && pwd)
+# Check for uninitialized variables, a big cause of bugs
+NOUNSET=${NOUNSET:-}
+if [[ -n "$NOUNSET" ]]; then
+ set -o nounset
+fi
+
# Sanity Checks
# -------------
@@ -79,6 +85,9 @@
# Prepare the environment
# -----------------------
+# Initialize variables:
+LAST_SPINNER_PID=""
+
# Import common functions
source $TOP_DIR/functions
@@ -90,16 +99,6 @@
# and ``DISTRO``
GetDistro
-# Warn users who aren't on an explicitly supported distro, but allow them to
-# override check and attempt installation with ``FORCE=yes ./stack``
-if [[ ! ${DISTRO} =~ (precise|trusty|7.0|wheezy|sid|testing|jessie|f19|f20|rhel6|rhel7) ]]; then
- echo "WARNING: this script has not been tested on $DISTRO"
- if [[ "$FORCE" != "yes" ]]; then
- die $LINENO "If you wish to run this script anyway run with FORCE=yes"
- fi
-fi
-
-
# Global Settings
# ---------------
@@ -151,6 +150,15 @@
fi
source $TOP_DIR/stackrc
+# Warn users who aren't on an explicitly supported distro, but allow them to
+# override check and attempt installation with ``FORCE=yes ./stack``
+if [[ ! ${DISTRO} =~ (precise|trusty|7.0|wheezy|sid|testing|jessie|f20|f21|rhel7) ]]; then
+ echo "WARNING: this script has not been tested on $DISTRO"
+ if [[ "$FORCE" != "yes" ]]; then
+ die $LINENO "If you wish to run this script anyway run with FORCE=yes"
+ fi
+fi
+
# Check to see if we are already running DevStack
# Note that this may fail if USE_SCREEN=False
if type -p screen > /dev/null && screen -ls | egrep -q "[0-9]\.$SCREEN_NAME"; then
@@ -173,12 +181,12 @@
disable_negated_services
# Look for obsolete stuff
-if [[ ,${ENABLED_SERVICES}, =~ ,"swift", ]]; then
- echo "FATAL: 'swift' is not supported as a service name"
- echo "FATAL: Use the actual swift service names to enable them as required:"
- echo "FATAL: s-proxy s-object s-container s-account"
- exit 1
-fi
+# if [[ ,${ENABLED_SERVICES}, =~ ,"swift", ]]; then
+# echo "FATAL: 'swift' is not supported as a service name"
+# echo "FATAL: Use the actual swift service names to enable them as required:"
+# echo "FATAL: s-proxy s-object s-container s-account"
+# exit 1
+# fi
# Configure sudo
# --------------
@@ -210,29 +218,10 @@
echo 'APT::Acquire::Retries "20";' | sudo tee /etc/apt/apt.conf.d/80retry >/dev/null
fi
-# upstream Rackspace centos7 images have an issue where cloud-init is
-# installed via pip because there were not official packages when the
-# image was created (fix in the works). Remove all pip packages
-# before we do anything else
-if [[ $DISTRO = "rhel7" && is_rackspace ]]; then
- (sudo pip freeze | xargs sudo pip uninstall -y) || true
-fi
-
# Some distros need to add repos beyond the defaults provided by the vendor
# to pick up required packages.
-if [[ is_fedora && $DISTRO == "rhel6" ]]; then
- # Installing Open vSwitch on RHEL requires enabling the RDO repo.
- RHEL6_RDO_REPO_RPM=${RHEL6_RDO_REPO_RPM:-"http://rdo.fedorapeople.org/openstack-icehouse/rdo-release-icehouse.rpm"}
- RHEL6_RDO_REPO_ID=${RHEL6_RDO_REPO_ID:-"openstack-icehouse"}
- if ! sudo yum repolist enabled $RHEL6_RDO_REPO_ID | grep -q $RHEL6_RDO_REPO_ID; then
- echo "RDO repo not detected; installing"
- yum_install $RHEL6_RDO_REPO_RPM || \
- die $LINENO "Error installing RDO repo, cannot continue"
- fi
-fi
-
-if [[ is_fedora && ( $DISTRO == "rhel6" || $DISTRO == "rhel7" ) ]]; then
+if is_fedora && [[ $DISTRO == "rhel7" ]]; then
# RHEL requires EPEL for many Open Stack dependencies
# note we always remove and install latest -- some environments
@@ -250,16 +239,10 @@
# $releasever directly in .repo file we create below. However
# RHEL gives a $releasever of "6Server" which breaks the path;
# see https://bugzilla.redhat.com/show_bug.cgi?id=1150759
- if [[ $DISTRO == "rhel7" ]]; then
- epel_ver="7"
- elif [[ $DISTRO == "rhel6" ]]; then
- epel_ver="6"
- fi
-
cat <<EOF | sudo tee /etc/yum.repos.d/epel-bootstrap.repo
[epel-bootstrap]
name=Bootstrap EPEL
-mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-$epel_ver&arch=\$basearch
+mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-7&arch=\$basearch
failovermethod=priority
enabled=0
gpgcheck=0
@@ -272,12 +255,17 @@
# ... and also optional to be enabled
is_package_installed yum-utils || install_package yum-utils
- if [[ $DISTRO == "rhel7" ]]; then
- OPTIONAL_REPO=rhel-7-server-optional-rpms
- elif [[ $DISTRO == "rhel6" ]]; then
- OPTIONAL_REPO=rhel-6-server-optional-rpms
+ sudo yum-config-manager --enable rhel-7-server-optional-rpms
+
+ RHEL_RDO_REPO_RPM=${RHEL7_RDO_REPO_RPM:-"https://repos.fedorapeople.org/repos/openstack/openstack-juno/rdo-release-juno-1.noarch.rpm"}
+ RHEL_RDO_REPO_ID=${RHEL7_RDO_REPO_ID:-"openstack-juno"}
+
+ if ! sudo yum repolist enabled $RHEL_RDO_REPO_ID | grep -q $RHEL_RDO_REPO_ID; then
+ echo "RDO repo not detected; installing"
+ yum_install $RHEL_RDO_REPO_RPM || \
+ die $LINENO "Error installing RDO repo, cannot continue"
fi
- sudo yum-config-manager --enable ${OPTIONAL_REPO}
+
fi
@@ -314,7 +302,7 @@
# -----------------
# Set up logging level
-VERBOSE=$(trueorfalse True $VERBOSE)
+VERBOSE=$(trueorfalse True VERBOSE)
# Draw a spinner so the user knows something is happening
function spinner {
@@ -356,32 +344,27 @@
echo $@ >&3
}
-if [[ is_fedora && $DISTRO == "rhel6" ]]; then
- # poor old python2.6 doesn't have argparse by default, which
- # outfilter.py uses
- is_package_installed python-argparse || install_package python-argparse
-fi
-
# Set up logging for ``stack.sh``
# Set ``LOGFILE`` to turn on logging
# Append '.xxxxxxxx' to the given name to maintain history
# where 'xxxxxxxx' is a representation of the date the file was created
TIMESTAMP_FORMAT=${TIMESTAMP_FORMAT:-"%F-%H%M%S"}
-if [[ -n "$LOGFILE" || -n "$SCREEN_LOGDIR" ]]; then
- LOGDAYS=${LOGDAYS:-7}
- CURRENT_LOG_TIME=$(date "+$TIMESTAMP_FORMAT")
+LOGDAYS=${LOGDAYS:-7}
+CURRENT_LOG_TIME=$(date "+$TIMESTAMP_FORMAT")
+
+if [[ -n ${LOGDIR:-} ]]; then
+ mkdir -p $LOGDIR
fi
if [[ -n "$LOGFILE" ]]; then
- # First clean up old log files. Use the user-specified ``LOGFILE``
- # as the template to search for, appending '.*' to match the date
- # we added on earlier runs.
- LOGDIR=$(dirname "$LOGFILE")
- LOGFILENAME=$(basename "$LOGFILE")
- mkdir -p $LOGDIR
- find $LOGDIR -maxdepth 1 -name $LOGFILENAME.\* -mtime +$LOGDAYS -exec rm {} \;
+ # Clean up old log files. Append '.*' to the user-specified
+ # ``LOGFILE`` to match the date in the search template.
+ LOGFILE_DIR="${LOGFILE%/*}" # dirname
+ LOGFILE_NAME="${LOGFILE##*/}" # basename
+ mkdir -p $LOGFILE_DIR
+ find $LOGFILE_DIR -maxdepth 1 -name $LOGFILE_NAME.\* -mtime +$LOGDAYS -exec rm {} \;
LOGFILE=$LOGFILE.${CURRENT_LOG_TIME}
- SUMFILE=$LOGFILE.${CURRENT_LOG_TIME}.summary
+ SUMFILE=$LOGFILE.summary.${CURRENT_LOG_TIME}
# Redirect output according to config
@@ -402,8 +385,8 @@
echo_summary "stack.sh log $LOGFILE"
# Specified logfile name always links to the most recent log
- ln -sf $LOGFILE $LOGDIR/$LOGFILENAME
- ln -sf $SUMFILE $LOGDIR/$LOGFILENAME.summary
+ ln -sf $LOGFILE $LOGFILE_DIR/$LOGFILE_NAME
+ ln -sf $SUMFILE $LOGFILE_DIR/$LOGFILE_NAME.summary
else
# Set up output redirection without log files
# Set fd 3 to a copy of stdout. So we can set fd 1 without losing
@@ -423,6 +406,7 @@
# ``screen-$SERVICE_NAME-$TIMESTAMP.log`` in that dir and have a link
# ``screen-$SERVICE_NAME.log`` to the latest log file.
# Logs are kept for as long specified in ``LOGDAYS``.
+# This is deprecated....logs go in ``LOGDIR``, only symlinks will be here now.
if [[ -n "$SCREEN_LOGDIR" ]]; then
# We make sure the directory is created.
@@ -485,47 +469,6 @@
# an error. It is also useful for following along as the install occurs.
set -o xtrace
-
-# Common Configuration
-# --------------------
-
-# Set ``OFFLINE`` to ``True`` to configure ``stack.sh`` to run cleanly without
-# Internet access. ``stack.sh`` must have been previously run with Internet
-# access to install prerequisites and fetch repositories.
-OFFLINE=`trueorfalse False $OFFLINE`
-
-# Set ``ERROR_ON_CLONE`` to ``True`` to configure ``stack.sh`` to exit if
-# the destination git repository does not exist during the ``git_clone``
-# operation.
-ERROR_ON_CLONE=`trueorfalse False $ERROR_ON_CLONE`
-
-# Whether to enable the debug log level in OpenStack services
-ENABLE_DEBUG_LOG_LEVEL=`trueorfalse True $ENABLE_DEBUG_LOG_LEVEL`
-
-# Set fixed and floating range here so we can make sure not to use addresses
-# from either range when attempting to guess the IP to use for the host.
-# Note that setting FIXED_RANGE may be necessary when running DevStack
-# in an OpenStack cloud that uses either of these address ranges internally.
-FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.0/24}
-FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}
-FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}
-
-HOST_IP=$(get_default_host_ip $FIXED_RANGE $FLOATING_RANGE "$HOST_IP_IFACE" "$HOST_IP")
-if [ "$HOST_IP" == "" ]; then
- die $LINENO "Could not determine host ip address. See local.conf for suggestions on setting HOST_IP."
-fi
-
-# Allow the use of an alternate hostname (such as localhost/127.0.0.1) for service endpoints.
-SERVICE_HOST=${SERVICE_HOST:-$HOST_IP}
-
-# Configure services to use syslog instead of writing to individual log files
-SYSLOG=`trueorfalse False $SYSLOG`
-SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}
-SYSLOG_PORT=${SYSLOG_PORT:-516}
-
-# Use color for logging output (only available if syslog is not used)
-LOG_COLOR=`trueorfalse True $LOG_COLOR`
-
# Reset the bundle of CA certificates
SSL_BUNDLE_FILE="$DATA_DIR/ca-bundle.pem"
rm -f $SSL_BUNDLE_FILE
@@ -538,9 +481,6 @@
# and the specified rpc backend is available on your platform.
check_rpc_backend
-# Use native SSL for servers in SSL_ENABLED_SERVICES
-USE_SSL=$(trueorfalse False $USE_SSL)
-
# Service to enable with SSL if USE_SSL is True
SSL_ENABLED_SERVICES="key,nova,cinder,glance,s-proxy,neutron"
@@ -560,7 +500,7 @@
# Source project function libraries
source $TOP_DIR/lib/infra
source $TOP_DIR/lib/oslo
-source $TOP_DIR/lib/stackforge
+source $TOP_DIR/lib/lvm
source $TOP_DIR/lib/horizon
source $TOP_DIR/lib/keystone
source $TOP_DIR/lib/glance
@@ -570,22 +510,17 @@
source $TOP_DIR/lib/ceilometer
source $TOP_DIR/lib/heat
source $TOP_DIR/lib/neutron
-source $TOP_DIR/lib/baremetal
source $TOP_DIR/lib/ldap
source $TOP_DIR/lib/dstat
+# Clone all external plugins
+fetch_plugins
+
# Extras Source
# --------------
# Phase: source
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i source
- done
-fi
-
-# Set the destination directories for other OpenStack projects
-OPENSTACKCLIENT_DIR=$DEST/python-openstackclient
+run_phase source
# Interactive Configuration
# -------------------------
@@ -655,6 +590,9 @@
# Queue Configuration
# Rabbit connection info
+# In multi node devstack, second node needs RABBIT_USERID, but rabbit
+# isn't enabled.
+RABBIT_USERID=${RABBIT_USERID:-stackrabbit}
if is_service_enabled rabbit; then
RABBIT_HOST=${RABBIT_HOST:-$SERVICE_HOST}
read_password RABBIT_PASSWORD "ENTER A PASSWORD TO USE FOR RABBIT."
@@ -713,32 +651,7 @@
# Configure an appropriate python environment
if [[ "$OFFLINE" != "True" ]]; then
- PYPI_ALTERNATIVE_URL=$PYPI_ALTERNATIVE_URL $TOP_DIR/tools/install_pip.sh
-fi
-
-# Do the ugly hacks for broken packages and distros
-source $TOP_DIR/tools/fixup_stuff.sh
-
-
-# Extras Pre-install
-# ------------------
-
-# Phase: pre-install
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i stack pre-install
- done
-fi
-
-
-install_rpc_backend
-
-if is_service_enabled $DATABASE_BACKENDS; then
- install_database
-fi
-
-if is_service_enabled neutron; then
- install_neutron_agent_packages
+ PYPI_ALTERNATIVE_URL=${PYPI_ALTERNATIVE_URL:-""} $TOP_DIR/tools/install_pip.sh
fi
TRACK_DEPENDS=${TRACK_DEPENDS:-False}
@@ -754,6 +667,26 @@
$DEST/.venv/bin/pip freeze > $DEST/requires-pre-pip
fi
+# Do the ugly hacks for broken packages and distros
+source $TOP_DIR/tools/fixup_stuff.sh
+
+
+# Extras Pre-install
+# ------------------
+
+# Phase: pre-install
+run_phase stack pre-install
+
+install_rpc_backend
+
+if is_service_enabled $DATABASE_BACKENDS; then
+ install_database
+fi
+
+if is_service_enabled neutron; then
+ install_neutron_agent_packages
+fi
+
# Check Out and Install Source
# ----------------------------
@@ -765,11 +698,6 @@
# Install oslo libraries that have graduated
install_oslo
-# Install stackforge libraries for testing
-if is_service_enabled stackforge_libs; then
- install_stackforge
-fi
-
# Install clients libraries
install_keystoneclient
install_glanceclient
@@ -788,8 +716,14 @@
# Install middleware
install_keystonemiddleware
-git_clone $OPENSTACKCLIENT_REPO $OPENSTACKCLIENT_DIR $OPENSTACKCLIENT_BRANCH
-setup_develop $OPENSTACKCLIENT_DIR
+# install the OpenStack client, needed for most setup commands
+if use_library_from_git "python-openstackclient"; then
+ git_clone_by_name "python-openstackclient"
+ setup_dev_lib "python-openstackclient"
+else
+ pip_install 'python-openstackclient>=1.0.2'
+fi
+
if is_service_enabled key; then
if [ "$KEYSTONE_AUTH_HOST" == "$SERVICE_HOST" ]; then
@@ -869,11 +803,7 @@
# --------------
# Phase: install
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i stack install
- done
-fi
+run_phase stack install
if [[ $TRACK_DEPENDS = True ]]; then
$DEST/.venv/bin/pip freeze > $DEST/requires-post-pip
@@ -952,13 +882,14 @@
# Configure screen
# ----------------
-USE_SCREEN=$(trueorfalse True $USE_SCREEN)
+USE_SCREEN=$(trueorfalse True USE_SCREEN)
if [[ "$USE_SCREEN" == "True" ]]; then
# Create a new named screen to run processes in
screen -d -m -S $SCREEN_NAME -t shell -s /bin/bash
sleep 1
# Set a reasonable status bar
+ SCREEN_HARDSTATUS=${SCREEN_HARDSTATUS:-}
if [ -z "$SCREEN_HARDSTATUS" ]; then
SCREEN_HARDSTATUS='%{= .} %-Lw%{= .}%> %n%f %t*%{= .}%+Lw%< %-=%{g}(%{d}%H/%l%{g})'
fi
@@ -1038,6 +969,14 @@
fi
+# ZeroMQ
+# ------
+if is_service_enabled zeromq; then
+ echo_summary "Starting zeromq receiver"
+ run_process zeromq "$OSLO_BIN_DIR/oslo-messaging-zmq-receiver"
+fi
+
+
# Horizon
# -------
@@ -1142,23 +1081,11 @@
init_nova_cells
fi
-# Extra things to prepare nova for baremetal, before nova starts
-if is_service_enabled nova && is_baremetal; then
- echo_summary "Preparing for nova baremetal"
- prepare_baremetal_toolchain
- configure_baremetal_nova_dirs
-fi
-
-
# Extras Configuration
# ====================
# Phase: post-config
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i stack post-config
- done
-fi
+run_phase stack post-config
# Local Configuration
@@ -1203,28 +1130,16 @@
TOKEN=$(keystone token-get | grep ' id ' | get_field 2)
die_if_not_set $LINENO TOKEN "Keystone fail to get token"
- if is_baremetal; then
- echo_summary "Creating and uploading baremetal images"
+ echo_summary "Uploading images"
- # build and upload separate deploy kernel & ramdisk
- upload_baremetal_deploy $TOKEN
-
- # upload images, separating out the kernel & ramdisk for PXE boot
- for image_url in ${IMAGE_URLS//,/ }; do
- upload_baremetal_image $image_url $TOKEN
- done
- else
- echo_summary "Uploading images"
-
- # Option to upload legacy ami-tty, which works with xenserver
- if [[ -n "$UPLOAD_LEGACY_TTY" ]]; then
- IMAGE_URLS="${IMAGE_URLS:+${IMAGE_URLS},}https://github.com/downloads/citrix-openstack/warehouse/tty.tgz"
- fi
-
- for image_url in ${IMAGE_URLS//,/ }; do
- upload_image $image_url $TOKEN
- done
+ # Option to upload legacy ami-tty, which works with xenserver
+ if [[ -n "$UPLOAD_LEGACY_TTY" ]]; then
+ IMAGE_URLS="${IMAGE_URLS:+${IMAGE_URLS},}https://github.com/downloads/citrix-openstack/warehouse/tty.tgz"
fi
+
+ for image_url in ${IMAGE_URLS//,/ }; do
+ upload_image $image_url $TOKEN
+ done
fi
# Create an access key and secret key for nova ec2 register image
@@ -1240,11 +1155,6 @@
iniset $NOVA_CONF keymgr fixed_key $(generate_hex_string 32)
fi
-if is_service_enabled zeromq; then
- echo_summary "Starting zermomq receiver"
- run_process zeromq "$OSLO_BIN_DIR/oslo-messaging-zmq-receiver"
-fi
-
# Launch the nova-api and wait for it to answer before continuing
if is_service_enabled n-api; then
echo_summary "Starting Nova API"
@@ -1275,7 +1185,7 @@
start_neutron_agents
fi
# Once neutron agents are started setup initial network elements
-if is_service_enabled q-svc; then
+if is_service_enabled q-svc && [[ "$NEUTRON_CREATE_INITIAL_NETWORKS" == "True" ]]; then
echo_summary "Creating initial neutron network elements"
create_neutron_initial_network
setup_neutron_debug
@@ -1331,32 +1241,6 @@
fi
-# If we are running nova with baremetal driver, there are a few
-# last-mile configuration bits to attend to, which must happen
-# after n-api and n-sch have started.
-# Also, creating the baremetal flavor must happen after images
-# are loaded into glance, though just knowing the IDs is sufficient here
-if is_service_enabled nova && is_baremetal; then
- # create special flavor for baremetal if we know what images to associate
- [[ -n "$BM_DEPLOY_KERNEL_ID" ]] && [[ -n "$BM_DEPLOY_RAMDISK_ID" ]] && \
- create_baremetal_flavor $BM_DEPLOY_KERNEL_ID $BM_DEPLOY_RAMDISK_ID
-
- # otherwise user can manually add it later by calling nova-baremetal-manage
- [[ -n "$BM_FIRST_MAC" ]] && add_baremetal_node
-
- if [[ "$BM_DNSMASQ_FROM_NOVA_NETWORK" = "False" ]]; then
- # NOTE: we do this here to ensure that our copy of dnsmasq is running
- sudo pkill dnsmasq || true
- sudo dnsmasq --conf-file= --port=0 --enable-tftp --tftp-root=/tftpboot \
- --dhcp-boot=pxelinux.0 --bind-interfaces --pid-file=/var/run/dnsmasq.pid \
- --interface=$BM_DNSMASQ_IFACE --dhcp-range=$BM_DNSMASQ_RANGE \
- ${BM_DNSMASQ_DNS:+--dhcp-option=option:dns-server,$BM_DNSMASQ_DNS}
- fi
- # ensure callback daemon is running
- sudo pkill nova-baremetal-deploy-helper || true
- run_process baremetal "nova-baremetal-deploy-helper"
-fi
-
# Save some values we generated for later use
CURRENT_RUN_TIME=$(date "+$TIMESTAMP_FORMAT")
echo "# $CURRENT_RUN_TIME" >$TOP_DIR/.stackenv
@@ -1378,11 +1262,7 @@
# ==========
# Phase: extra
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i stack extra
- done
-fi
+run_phase stack extra
# Local Configuration
# ===================
@@ -1449,57 +1329,6 @@
echo_summary "WARNING: $DEPRECATED_TEXT"
fi
-if is_service_enabled neutron; then
- # TODO(dtroyer): Remove Q_AGENT_EXTRA_AGENT_OPTS after stable/juno branch is cut
- if [[ -n "$Q_AGENT_EXTRA_AGENT_OPTS" ]]; then
- echo ""
- echo_summary "WARNING: Q_AGENT_EXTRA_AGENT_OPTS is used"
- echo "You are using Q_AGENT_EXTRA_AGENT_OPTS to pass configuration into $NEUTRON_CONF."
- echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
- echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
- echo "
-[[post-config|/\$Q_PLUGIN_CONF_FILE]]
-[DEFAULT]
-"
- for I in "${Q_AGENT_EXTRA_AGENT_OPTS[@]}"; do
- # Replace the first '=' with ' ' for iniset syntax
- echo ${I}
- done
- fi
-
- # TODO(dtroyer): Remove Q_AGENT_EXTRA_SRV_OPTS after stable/juno branch is cut
- if [[ -n "$Q_AGENT_EXTRA_SRV_OPTS" ]]; then
- echo ""
- echo_summary "WARNING: Q_AGENT_EXTRA_SRV_OPTS is used"
- echo "You are using Q_AGENT_EXTRA_SRV_OPTS to pass configuration into $NEUTRON_CONF."
- echo "Please convert that configuration in localrc to a $NEUTRON_CONF section in local.conf:"
- echo "Q_AGENT_EXTRA_AGENT_OPTS will be removed early in the 'K' development cycle"
- echo "
-[[post-config|/\$Q_PLUGIN_CONF_FILE]]
-[DEFAULT]
-"
- for I in "${Q_AGENT_EXTRA_SRV_OPTS[@]}"; do
- # Replace the first '=' with ' ' for iniset syntax
- echo ${I}
- done
- fi
-fi
-
-if is_service_enabled cinder; then
- # TODO(dtroyer): Remove CINDER_MULTI_LVM_BACKEND after stable/juno branch is cut
- if [[ "$CINDER_MULTI_LVM_BACKEND" = "True" ]]; then
- echo ""
- echo_summary "WARNING: CINDER_MULTI_LVM_BACKEND is used"
- echo "You are using CINDER_MULTI_LVM_BACKEND to configure Cinder's multiple LVM backends"
- echo "Please convert that configuration in local.conf to use CINDER_ENABLED_BACKENDS."
- echo "CINDER_MULTI_LVM_BACKEND will be removed early in the 'K' development cycle"
- echo "
-[[local|localrc]]
-CINDER_ENABLED_BACKENDS=lvm:lvmdriver-1,lvm:lvmdriver-2
-"
- fi
-fi
-
# Indicate how long this took to run (bash maintained variable ``SECONDS``)
echo_summary "stack.sh completed in $SECONDS seconds."
diff --git a/stackrc b/stackrc
index 15b0951..7bbde99 100644
--- a/stackrc
+++ b/stackrc
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
# stackrc
#
# Find the other rc files
@@ -31,7 +33,8 @@
# For example, to enable Swift add this to ``local.conf``:
# enable_service s-proxy s-object s-container s-account
# In order to enable Neutron (a single node setup) add the following
-# settings in `` localrc``:
+# settings in ``local.conf``:
+# [[local|localrc]]
# disable_service n-net
# enable_service q-svc
# enable_service q-agt
@@ -40,9 +43,17 @@
# enable_service q-meta
# # Optional, to enable tempest configuration as part of devstack
# enable_service tempest
+function isset {
+ local nounset=$(set +o | grep nounset)
+ set +o nounset
+ [[ -n "${!1+x}" ]]
+ result=$?
+ $nounset
+ return $result
+}
# this allows us to pass ENABLED_SERVICES
-if [[ -z "$ENABLED_SERVICES" ]]; then
+if ! isset ENABLED_SERVICES ; then
# core compute (glance / keystone / nova (+ nova-network))
ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,n-sch,n-novnc,n-xvnc,n-cauth
# cinder
@@ -103,7 +114,7 @@
# This can be used to turn database query logging on and off
# (currently only implemented for MySQL backend)
-DATABASE_QUERY_LOGGING=$(trueorfalse True $DATABASE_QUERY_LOGGING)
+DATABASE_QUERY_LOGGING=$(trueorfalse True DATABASE_QUERY_LOGGING)
# Set a timeout for git operations. If git is still running when the
# timeout expires, the command will be retried up to 3 times. This is
@@ -134,6 +145,19 @@
# Another option is https://git.openstack.org
GIT_BASE=${GIT_BASE:-git://git.openstack.org}
+# Which libraries should we install from git instead of using released
+# versions on pypi?
+#
+# By default devstack is now installing libraries from pypi instead of
+# from git repositories by default. This works great if you are
+# developing server components, but if you want to develop libraries
+# and see them live in devstack you need to tell devstack it should
+# install them from git.
+#
+# ex: LIBS_FROM_GIT=python-keystoneclient,oslo.config
+#
+# Will install those 2 libraries from git, the rest from pypi.
+
##############
#
# OpenStack Server Components
@@ -144,7 +168,7 @@
CEILOMETER_REPO=${CEILOMETER_REPO:-${GIT_BASE}/openstack/ceilometer.git}
CEILOMETER_BRANCH=${CEILOMETER_BRANCH:-master}
-# volume service
+# block storage service
CINDER_REPO=${CINDER_REPO:-${GIT_BASE}/openstack/cinder.git}
CINDER_BRANCH=${CINDER_BRANCH:-master}
@@ -172,11 +196,27 @@
NEUTRON_REPO=${NEUTRON_REPO:-${GIT_BASE}/openstack/neutron.git}
NEUTRON_BRANCH=${NEUTRON_BRANCH:-master}
+# neutron fwaas service
+NEUTRON_FWAAS_REPO=${NEUTRON_FWAAS_REPO:-${GIT_BASE}/openstack/neutron-fwaas.git}
+NEUTRON_FWAAS_BRANCH=${NEUTRON_FWAAS_BRANCH:-master}
+
+# neutron lbaas service
+NEUTRON_LBAAS_REPO=${NEUTRON_LBAAS_REPO:-${GIT_BASE}/openstack/neutron-lbaas.git}
+NEUTRON_LBAAS_BRANCH=${NEUTRON_LBAAS_BRANCH:-master}
+
+# neutron vpnaas service
+NEUTRON_VPNAAS_REPO=${NEUTRON_VPNAAS_REPO:-${GIT_BASE}/openstack/neutron-vpnaas.git}
+NEUTRON_VPNAAS_BRANCH=${NEUTRON_VPNAAS_BRANCH:-master}
+
# compute service
NOVA_REPO=${NOVA_REPO:-${GIT_BASE}/openstack/nova.git}
NOVA_BRANCH=${NOVA_BRANCH:-master}
-# storage service
+# data processing service
+SAHARA_REPO=${SAHARA_REPO:-${GIT_BASE}/openstack/sahara.git}
+SAHARA_BRANCH=${SAHARA_BRANCH:-master}
+
+# object storage service
SWIFT_REPO=${SWIFT_REPO:-${GIT_BASE}/openstack/swift.git}
SWIFT_BRANCH=${SWIFT_BRANCH:-master}
@@ -199,8 +239,8 @@
TEMPEST_BRANCH=${TEMPEST_BRANCH:-master}
# TODO(sdague): this should end up as a library component like below
-TEMPEST_LIB_REPO=${TEMPEST_LIB_REPO:-${GIT_BASE}/openstack/tempest-lib.git}
-TEMPEST_LIB_BRANCH=${TEMPEST_LIB_BRANCH:-master}
+GITREPO["tempest-lib"]=${TEMPEST_LIB_REPO:-${GIT_BASE}/openstack/tempest-lib.git}
+GITBRANCH["tempest-lib"]=${TEMPEST_LIB_BRANCH:-master}
##############
@@ -210,48 +250,54 @@
##############
# ceilometer client library
-CEILOMETERCLIENT_REPO=${CEILOMETERCLIENT_REPO:-${GIT_BASE}/openstack/python-ceilometerclient.git}
-CEILOMETERCLIENT_BRANCH=${CEILOMETERCLIENT_BRANCH:-master}
+GITREPO["python-ceilometerclient"]=${CEILOMETERCLIENT_REPO:-${GIT_BASE}/openstack/python-ceilometerclient.git}
+GITBRANCH["python-ceilometerclient"]=${CEILOMETERCLIENT_BRANCH:-master}
# volume client
-CINDERCLIENT_REPO=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
-CINDERCLIENT_BRANCH=${CINDERCLIENT_BRANCH:-master}
+GITREPO["python-cinderclient"]=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
+GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-master}
# python glance client library
-GLANCECLIENT_REPO=${GLANCECLIENT_REPO:-${GIT_BASE}/openstack/python-glanceclient.git}
-GLANCECLIENT_BRANCH=${GLANCECLIENT_BRANCH:-master}
+GITREPO["python-glanceclient"]=${GLANCECLIENT_REPO:-${GIT_BASE}/openstack/python-glanceclient.git}
+GITBRANCH["python-glanceclient"]=${GLANCECLIENT_BRANCH:-master}
# python heat client library
-HEATCLIENT_REPO=${HEATCLIENT_REPO:-${GIT_BASE}/openstack/python-heatclient.git}
-HEATCLIENT_BRANCH=${HEATCLIENT_BRANCH:-master}
+GITREPO["python-heatclient"]=${HEATCLIENT_REPO:-${GIT_BASE}/openstack/python-heatclient.git}
+GITBRANCH["python-heatclient"]=${HEATCLIENT_BRANCH:-master}
# ironic client
-IRONICCLIENT_REPO=${IRONICCLIENT_REPO:-${GIT_BASE}/openstack/python-ironicclient.git}
-IRONICCLIENT_BRANCH=${IRONICCLIENT_BRANCH:-master}
+GITREPO["python-ironicclient"]=${IRONICCLIENT_REPO:-${GIT_BASE}/openstack/python-ironicclient.git}
+GITBRANCH["python-ironicclient"]=${IRONICCLIENT_BRANCH:-master}
# python keystone client library to nova that horizon uses
-KEYSTONECLIENT_REPO=${KEYSTONECLIENT_REPO:-${GIT_BASE}/openstack/python-keystoneclient.git}
-KEYSTONECLIENT_BRANCH=${KEYSTONECLIENT_BRANCH:-master}
+GITREPO["python-keystoneclient"]=${KEYSTONECLIENT_REPO:-${GIT_BASE}/openstack/python-keystoneclient.git}
+GITBRANCH["python-keystoneclient"]=${KEYSTONECLIENT_BRANCH:-master}
# neutron client
-NEUTRONCLIENT_REPO=${NEUTRONCLIENT_REPO:-${GIT_BASE}/openstack/python-neutronclient.git}
-NEUTRONCLIENT_BRANCH=${NEUTRONCLIENT_BRANCH:-master}
+GITREPO["python-neutronclient"]=${NEUTRONCLIENT_REPO:-${GIT_BASE}/openstack/python-neutronclient.git}
+GITBRANCH["python-neutronclient"]=${NEUTRONCLIENT_BRANCH:-master}
# python client library to nova that horizon (and others) use
-NOVACLIENT_REPO=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
-NOVACLIENT_BRANCH=${NOVACLIENT_BRANCH:-master}
+GITREPO["python-novaclient"]=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
+GITBRANCH["python-novaclient"]=${NOVACLIENT_BRANCH:-master}
+
+# python saharaclient
+GITREPO["python-saharaclient"]=${SAHARACLIENT_REPO:-${GIT_BASE}/openstack/python-saharaclient.git}
+GITBRANCH["python-saharaclient"]=${SAHARACLIENT_BRANCH:-master}
# python swift client library
-SWIFTCLIENT_REPO=${SWIFTCLIENT_REPO:-${GIT_BASE}/openstack/python-swiftclient.git}
-SWIFTCLIENT_BRANCH=${SWIFTCLIENT_BRANCH:-master}
+GITREPO["python-swiftclient"]=${SWIFTCLIENT_REPO:-${GIT_BASE}/openstack/python-swiftclient.git}
+GITBRANCH["python-swiftclient"]=${SWIFTCLIENT_BRANCH:-master}
# trove client library test
-TROVECLIENT_REPO=${TROVECLIENT_REPO:-${GIT_BASE}/openstack/python-troveclient.git}
-TROVECLIENT_BRANCH=${TROVECLIENT_BRANCH:-master}
+GITREPO["python-troveclient"]=${TROVECLIENT_REPO:-${GIT_BASE}/openstack/python-troveclient.git}
+GITBRANCH["python-troveclient"]=${TROVECLIENT_BRANCH:-master}
# consolidated openstack python client
-OPENSTACKCLIENT_REPO=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
-OPENSTACKCLIENT_BRANCH=${OPENSTACKCLIENT_BRANCH:-master}
+GITREPO["python-openstackclient"]=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
+GITBRANCH["python-openstackclient"]=${OPENSTACKCLIENT_BRANCH:-master}
+# this doesn't exist in a lib file, so set it here
+GITDIR["python-openstackclient"]=$DEST/python-openstackclient
###################
#
@@ -271,6 +317,10 @@
GITREPO["oslo.config"]=${OSLOCFG_REPO:-${GIT_BASE}/openstack/oslo.config.git}
GITBRANCH["oslo.config"]=${OSLOCFG_BRANCH:-master}
+# oslo.context
+GITREPO["oslo.context"]=${OSLOCTX_REPO:-${GIT_BASE}/openstack/oslo.context.git}
+GITBRANCH["oslo.context"]=${OSLOCTX_BRANCH:-master}
+
# oslo.db
GITREPO["oslo.db"]=${OSLODB_REPO:-${GIT_BASE}/openstack/oslo.db.git}
GITBRANCH["oslo.db"]=${OSLODB_BRANCH:-master}
@@ -319,6 +369,10 @@
GITREPO["taskflow"]=${TASKFLOW_REPO:-${GIT_BASE}/openstack/taskflow.git}
GITBRANCH["taskflow"]=${TASKFLOW_BRANCH:-master}
+# tooz plugin manager
+GITREPO["tooz"]=${TOOZ_REPO:-${GIT_BASE}/openstack/tooz.git}
+GITBRANCH["tooz"]=${TOOZ_BRANCH:-master}
+
# pbr drives the setuptools configs
GITREPO["pbr"]=${PBR_REPO:-${GIT_BASE}/openstack-dev/pbr.git}
GITBRANCH["pbr"]=${PBR_BRANCH:-master}
@@ -330,8 +384,8 @@
##################
# glance store library
-GLANCE_STORE_REPO=${GLANCE_STORE_REPO:-${GIT_BASE}/openstack/glance_store.git}
-GLANCE_STORE_BRANCH=${GLANCE_STORE_BRANCH:-master}
+GITREPO["glance_store"]=${GLANCE_STORE_REPO:-${GIT_BASE}/openstack/glance_store.git}
+GITBRANCH["glance_store"]=${GLANCE_STORE_BRANCH:-master}
# heat-cfntools server agent
HEAT_CFNTOOLS_REPO=${HEAT_CFNTOOLS_REPO:-${GIT_BASE}/openstack/heat-cfntools.git}
@@ -342,12 +396,12 @@
HEAT_TEMPLATES_BRANCH=${HEAT_TEMPLATES_BRANCH:-master}
# django openstack_auth library
-HORIZONAUTH_REPO=${HORIZONAUTH_REPO:-${GIT_BASE}/openstack/django_openstack_auth.git}
-HORIZONAUTH_BRANCH=${HORIZONAUTH_BRANCH:-master}
+GITREPO["django_openstack_auth"]=${HORIZONAUTH_REPO:-${GIT_BASE}/openstack/django_openstack_auth.git}
+GITBRANCH["django_openstack_auth"]=${HORIZONAUTH_BRANCH:-master}
# keystone middleware
-KEYSTONEMIDDLEWARE_REPO=${KEYSTONEMIDDLEWARE_REPO:-${GIT_BASE}/openstack/keystonemiddleware.git}
-KEYSTONEMIDDLEWARE_BRANCH=${KEYSTONEMIDDLEWARE_BRANCH:-master}
+GITREPO["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_REPO:-${GIT_BASE}/openstack/keystonemiddleware.git}
+GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-master}
# s3 support for swift
SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/stackforge/swift3.git}
@@ -382,26 +436,6 @@
#################
#
-# Additional Libraries
-#
-#################
-
-# stackforge libraries that are used by OpenStack core services
-# wsme
-WSME_REPO=${WSME_REPO:-${GIT_BASE}/stackforge/wsme.git}
-WSME_BRANCH=${WSME_BRANCH:-master}
-
-# pecan
-PECAN_REPO=${PECAN_REPO:-${GIT_BASE}/stackforge/pecan.git}
-PECAN_BRANCH=${PECAN_BRANCH:-master}
-
-# sqlalchemy-migrate
-SQLALCHEMY_MIGRATE_REPO=${SQLALCHEMY_MIGRATE_REPO:-${GIT_BASE}/stackforge/sqlalchemy-migrate.git}
-SQLALCHEMY_MIGRATE_BRANCH=${SQLALCHEMY_MIGRATE_BRANCH:-master}
-
-
-#################
-#
# 3rd Party Components (non pip installable)
#
# NOTE(sdague): these should be converted to release version installs or removed
@@ -484,8 +518,8 @@
CIRROS_ARCH=${CIRROS_ARCH:-"x86_64"}
# Set default image based on ``VIRT_DRIVER`` and ``LIBVIRT_TYPE``, either of
-# which may be set in ``localrc``. Also allow ``DEFAULT_IMAGE_NAME`` and
-# ``IMAGE_URLS`` to be set directly in ``localrc``.
+# which may be set in ``local.conf``. Also allow ``DEFAULT_IMAGE_NAME`` and
+# ``IMAGE_URLS`` to be set in the `localrc` section of ``local.conf``.
case "$VIRT_DRIVER" in
openvz)
DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ubuntu-12.04-x86_64}
@@ -524,7 +558,7 @@
# Use 64bit fedora image if heat is enabled
if [[ "$ENABLED_SERVICES" =~ 'h-api' ]]; then
case "$VIRT_DRIVER" in
- libvirt|baremetal|ironic)
+ libvirt|ironic)
HEAT_CFN_IMAGE_URL=${HEAT_CFN_IMAGE_URL:-"https://download.fedoraproject.org/pub/alt/openstack/20/x86_64/Fedora-x86_64-20-20140618-sda.qcow2"}
IMAGE_URLS+=",$HEAT_CFN_IMAGE_URL"
;;
@@ -536,7 +570,7 @@
# Trove needs a custom image for its work
if [[ "$ENABLED_SERVICES" =~ 'tr-api' ]]; then
case "$VIRT_DRIVER" in
- libvirt|baremetal|ironic|xenapi)
+ libvirt|ironic|xenapi)
TROVE_GUEST_IMAGE_URL=${TROVE_GUEST_IMAGE_URL:-"http://tarballs.openstack.org/trove/images/ubuntu/mysql.qcow2"}
IMAGE_URLS+=",${TROVE_GUEST_IMAGE_URL}"
;;
@@ -547,7 +581,7 @@
# Staging Area for New Images, have them here for at least 24hrs for nodepool
# to cache them otherwise the failure rates in the gate are too high
-PRECACHE_IMAGES=$(trueorfalse False $PRECACHE_IMAGES)
+PRECACHE_IMAGES=$(trueorfalse False PRECACHE_IMAGES)
if [[ "$PRECACHE_IMAGES" == "True" ]]; then
# staging in update for nodepool
IMAGE_URL="https://download.fedoraproject.org/pub/alt/openstack/20/x86_64/Fedora-x86_64-20-20140618-sda.qcow2"
@@ -594,8 +628,110 @@
# Service startup timeout
SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
+# Support alternative yum -- in future Fedora 'dnf' will become the
+# only supported installer, but for now 'yum' and 'dnf' are both
+# available in parallel with compatible CLIs. Allow manual switching
+# till we get to the point we need to handle this automatically
+YUM=${YUM:-yum}
+
+# Common Configuration
+# --------------------
+
+# Set ``OFFLINE`` to ``True`` to configure ``stack.sh`` to run cleanly without
+# Internet access. ``stack.sh`` must have been previously run with Internet
+# access to install prerequisites and fetch repositories.
+OFFLINE=$(trueorfalse False OFFLINE)
+
+# Set ``ERROR_ON_CLONE`` to ``True`` to configure ``stack.sh`` to exit if
+# the destination git repository does not exist during the ``git_clone``
+# operation.
+ERROR_ON_CLONE=$(trueorfalse False ERROR_ON_CLONE)
+
+# Whether to enable the debug log level in OpenStack services
+ENABLE_DEBUG_LOG_LEVEL=$(trueorfalse True ENABLE_DEBUG_LOG_LEVEL)
+
+# Set fixed and floating range here so we can make sure not to use addresses
+# from either range when attempting to guess the IP to use for the host.
+# Note that setting FIXED_RANGE may be necessary when running DevStack
+# in an OpenStack cloud that uses either of these address ranges internally.
+FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.0/24}
+FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}
+FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}
+HOST_IP_IFACE=${HOST_IP_IFACE:-}
+HOST_IP=${HOST_IP:-}
+
+HOST_IP=$(get_default_host_ip $FIXED_RANGE $FLOATING_RANGE "$HOST_IP_IFACE" "$HOST_IP")
+if [ "$HOST_IP" == "" ]; then
+ die $LINENO "Could not determine host ip address. See local.conf for suggestions on setting HOST_IP."
+fi
+
+# Allow the use of an alternate hostname (such as localhost/127.0.0.1) for service endpoints.
+SERVICE_HOST=${SERVICE_HOST:-$HOST_IP}
+
+# Configure services to use syslog instead of writing to individual log files
+SYSLOG=$(trueorfalse False SYSLOG)
+SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}
+SYSLOG_PORT=${SYSLOG_PORT:-516}
+
+# Use color for logging output (only available if syslog is not used)
+LOG_COLOR=$(trueorfalse True LOG_COLOR)
+
+# Set global ``GIT_DEPTH=<number>`` to limit the history depth of the git clone
+# Set to 0 to disable shallow cloning
+GIT_DEPTH=${GIT_DEPTH:-0}
+
+# Use native SSL for servers in SSL_ENABLED_SERVICES
+USE_SSL=$(trueorfalse False USE_SSL)
+
# Following entries need to be last items in file
+# Compatibility bits required by other callers like Grenade
+
+# Old way was using SCREEN_LOGDIR to locate those logs and LOGFILE for the stack.sh trace log.
+# LOGFILE SCREEN_LOGDIR output
+# not set not set no log files
+# set not set stack.sh log to LOGFILE
+# not set set screen logs to SCREEN_LOGDIR
+# set set stack.sh log to LOGFILE, screen logs to SCREEN_LOGDIR
+
+# New way is LOGDIR for all logs and LOGFILE for stack.sh trace log, but if not fully-qualified will be in LOGDIR
+# LOGFILE LOGDIR output
+# not set not set (new) set LOGDIR from default
+# set not set stack.sh log to LOGFILE, (new) set LOGDIR from LOGFILE
+# not set set screen logs to LOGDIR
+# set set stack.sh log to LOGFILE, screen logs to LOGDIR
+
+# For compat, if SCREEN_LOGDIR is set, it will be used to create back-compat symlinks to the LOGDIR
+# symlinks to SCREEN_LOGDIR (compat)
+
+
+# Set up new logging defaults
+if [[ -z "${LOGDIR:-}" ]]; then
+ default_logdir=$DEST/logs
+ if [[ -z "${LOGFILE:-}" ]]; then
+ # Nothing is set, we need a default
+ LOGDIR="$default_logdir"
+ else
+ # Set default LOGDIR
+ LOGDIR="${LOGFILE%/*}"
+ logfile="${LOGFILE##*/}"
+ if [[ -z "$LOGDIR" || "$LOGDIR" == "$logfile" ]]; then
+ # LOGFILE had no path, set a default
+ LOGDIR="$default_logdir"
+ fi
+
+ # Check for duplication
+ if [[ "${SCREEN_LOGDIR:-}" == "${LOGDIR}" ]]; then
+ # We don't need the symlinks since it's the same directory
+ unset SCREEN_LOGDIR
+ fi
+ fi
+ unset default_logdir logfile
+fi
+
+# LOGDIR is always set at this point so it is not useful as a 'enable' for service logs
+# SCREEN_LOGDIR may be set, it is useful to enable the compat symlinks
+
# Local variables:
# mode: shell-script
# End:
diff --git a/tests/test_functions.sh b/tests/test_functions.sh
new file mode 100755
index 0000000..e57948a
--- /dev/null
+++ b/tests/test_functions.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+# Tests for DevStack meta-config functions
+
+TOP=$(cd $(dirname "$0")/.. && pwd)
+
+# Import common functions
+source $TOP/functions
+source $TOP/tests/unittest.sh
+
+function test_truefalse {
+ local one=1
+ local captrue=True
+ local lowtrue=true
+ local abrevtrue=t
+ local zero=0
+ local capfalse=False
+ local lowfalse=false
+ local abrevfalse=f
+ for against in True False; do
+ for name in one captrue lowtrue abrevtrue; do
+ assert_equal "True" $(trueorfalse $against $name) "\$(trueorfalse $against $name)"
+ done
+ done
+ for against in True False; do
+ for name in zero capfalse lowfalse abrevfalse; do
+ assert_equal "False" $(trueorfalse $against $name) "\$(trueorfalse $against $name)"
+ done
+ done
+}
+
+test_truefalse
+
+report_results
diff --git a/tests/test_ini.sh b/tests/test_ini.sh
index 598cd57..106cc95 100755
--- a/tests/test_ini.sh
+++ b/tests/test_ini.sh
@@ -34,6 +34,32 @@
[eee]
multi = foo1
multi = foo2
+
+# inidelete(a)
+[del_separate_options]
+a=b
+b=c
+
+# inidelete(a)
+[del_same_option]
+a=b
+a=c
+
+# inidelete(a)
+[del_missing_option]
+b=c
+
+# inidelete(a)
+[del_missing_option_multi]
+b=c
+b=d
+
+# inidelete(a)
+[del_no_options]
+
+# inidelete(a)
+# no section - del_no_section
+
EOF
# Test with missing arguments
@@ -237,4 +263,33 @@
echo "iniadd with non-exsting failed: $VAL"
fi
+# Test inidelete
+del_cases="
+ del_separate_options
+ del_same_option
+ del_missing_option
+ del_missing_option_multi
+ del_no_options
+ del_no_section"
+
+for x in $del_cases; do
+ inidelete test.ini $x a
+ VAL=$(iniget_multiline test.ini $x a)
+ if [ -z "$VAL" ]; then
+ echo "OK: inidelete $x"
+ else
+ echo "inidelete $x failed: $VAL"
+ fi
+ if [ "$x" = "del_separate_options" -o \
+ "$x" = "del_missing_option" -o \
+ "$x" = "del_missing_option_multi" ]; then
+ VAL=$(iniget_multiline test.ini $x b)
+ if [ "$VAL" = "c" -o "$VAL" = "c d" ]; then
+ echo "OK: inidelete other_options $x"
+ else
+ echo "inidelete other_option $x failed: $VAL"
+ fi
+ fi
+done
+
rm test.ini
diff --git a/tests/test_ip.sh b/tests/test_ip.sh
index e9cbcca..add8d1a 100755
--- a/tests/test_ip.sh
+++ b/tests/test_ip.sh
@@ -8,9 +8,6 @@
# Import common functions
source $TOP/functions
-# Import configuration
-source $TOP/openrc
-
echo "Testing IP addr functions"
diff --git a/tests/test_libs_from_pypi.sh b/tests/test_libs_from_pypi.sh
new file mode 100755
index 0000000..6e1b515
--- /dev/null
+++ b/tests/test_libs_from_pypi.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+#
+# 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.
+
+
+TOP=$(cd $(dirname "$0")/.. && pwd)
+
+export TOP_DIR=$TOP
+
+# we don't actually care about the HOST_IP
+HOST_IP="don't care"
+# Import common functions
+source $TOP/functions
+source $TOP/stackrc
+source $TOP/lib/tls
+for i in $TOP/lib/*; do
+ if [[ -f $i ]]; then
+ source $i
+ fi
+done
+
+ALL_LIBS="python-novaclient oslo.config pbr oslo.context python-troveclient python-keystoneclient taskflow oslo.middleware pycadf python-glanceclient python-ironicclient tempest-lib oslo.messaging oslo.log cliff python-heatclient stevedore python-cinderclient glance_store oslo.concurrency oslo.db oslo.vmware keystonemiddleware oslo.serialization python-saharaclient django_openstack_auth python-openstackclient oslo.rootwrap oslo.i18n python-ceilometerclient oslo.utils python-swiftclient python-neutronclient tooz"
+
+# Generate the above list with
+# echo ${!GITREPO[@]}
+
+function check_exists {
+ local thing=$1
+ local hash=$2
+ local key=$3
+ if [[ ! -z "$VERBOSE" ]]; then
+ echo "Checking for $hash[$key]"
+ fi
+ if [[ -z $thing ]]; then
+ echo "$hash[$key] does not exit!"
+ exit 1
+ else
+ if [[ ! -z "$VERBOSE" ]]; then
+ echo "$hash[$key] => $thing"
+ fi
+ fi
+}
+
+function test_all_libs_upto_date {
+ # this is all the magics
+ local found_libs=${!GITREPO[@]}
+ declare -A all_libs
+ for lib in $ALL_LIBS; do
+ all_libs[$lib]=1
+ done
+
+ for lib in $found_libs; do
+ if [[ -z ${all_libs[$lib]} ]]; then
+ echo "Library '$lib' not listed in unit tests, please add to ALL_LIBS"
+ exit 1
+ fi
+
+ done
+ echo "test_all_libs_upto_date PASSED"
+}
+
+function test_libs_exist {
+ local lib=""
+ for lib in $ALL_LIBS; do
+ check_exists "${GITREPO[$lib]}" "GITREPO" "$lib"
+ check_exists "${GITBRANCH[$lib]}" "GITBRANCH" "$lib"
+ check_exists "${GITDIR[$lib]}" "GITDIR" "$lib"
+ done
+
+ echo "test_libs_exist PASSED"
+}
+
+function test_branch_master {
+ for lib in $ALL_LIBS; do
+ if [[ ${GITBRANCH[$lib]} != "master" ]]; then
+ echo "GITBRANCH for $lib not master (${GITBRANCH[$lib]})"
+ exit 1
+ fi
+ done
+
+ echo "test_branch_master PASSED"
+}
+
+set -o errexit
+
+test_libs_exist
+test_branch_master
+test_all_libs_upto_date
diff --git a/tests/test_refs.sh b/tests/test_refs.sh
new file mode 100755
index 0000000..bccca5d
--- /dev/null
+++ b/tests/test_refs.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+
+
+echo "Ensuring we don't have crazy refs"
+
+REFS=`grep BRANCH stackrc | grep -v -- '-master'`
+rc=$?
+if [[ $rc -eq 0 ]]; then
+ echo "Branch defaults must be master. Found:"
+ echo $REFS
+ exit 1
+fi
diff --git a/tests/unittest.sh b/tests/unittest.sh
new file mode 100644
index 0000000..435cc3a
--- /dev/null
+++ b/tests/unittest.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+# 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.
+
+# we always start with no errors
+ERROR=0
+FAILED_FUNCS=""
+
+function assert_equal {
+ local lineno=`caller 0 | awk '{print $1}'`
+ local function=`caller 0 | awk '{print $2}'`
+ local msg=$3
+ if [[ "$1" != "$2" ]]; then
+ FAILED_FUNCS+="$function:L$lineno\n"
+ echo "ERROR: $1 != $2 in $function:L$lineno!"
+ echo " $msg"
+ ERROR=1
+ else
+ echo "$function:L$lineno - ok"
+ fi
+}
+
+function report_results {
+ if [[ $ERROR -eq 1 ]]; then
+ echo "Tests FAILED"
+ echo $FAILED_FUNCS
+ exit 1
+ fi
+}
diff --git a/tools/build_docs.sh b/tools/build_docs.sh
index f52b179..929d1e0 100755
--- a/tools/build_docs.sh
+++ b/tools/build_docs.sh
@@ -1,35 +1,19 @@
#!/usr/bin/env bash
-# **build_docs.sh** - Build the gh-pages docs for DevStack
+# **build_docs.sh** - Build the docs for DevStack
#
# - Install shocco if not found on PATH and INSTALL_SHOCCO is set
# - Clone MASTER_REPO branch MASTER_BRANCH
# - Re-creates ``doc/build/html`` directory from existing repo + new generated script docs
# Usage:
-## build_docs.sh [-o <out-dir>] [-g] [master|<repo> [<branch>]]
-## <repo> The DevStack repository to clone (default is DevStack github repo)
-## If a repo is not supplied use the current directory
-## (assumed to be a DevStack checkout) as the source.
-## <branch> The DevStack branch to check out (default is master; ignored if
-## repo is not specified)
-## . Use the current repo and branch (do not use with -p to
-## prevent stray files in the workspace being added tot he docs)
+## build_docs.sh [-o <out-dir>]
## -o <out-dir> Write the static HTML output to <out-dir>
## (Note that <out-dir> will be deleted and re-created to ensure it is clean)
-## -g Update the old gh-pages repo (set PUSH=1 to actually push up to RCB)
# Defaults
# --------
-# Source repo/branch for DevStack
-MASTER_REPO=${MASTER_REPO:-git://git.openstack.org/openstack-dev/devstack}
-MASTER_BRANCH=${MASTER_BRANCH:-master}
-
-# http://devstack.org is a GitHub gh-pages site in the https://github.com/cloudbuilders/devtack.git repo
-GH_PAGES_REPO=git@github.com:cloudbuilders/devstack.git
-
-DOCS_SOURCE=doc/source
HTML_BUILD=doc/build/html
# Keep track of the devstack directory
@@ -60,10 +44,8 @@
fi
# Process command-line args
-while getopts go: c; do
+while getopts o: c; do
case $c in
- g) GH_UPDATE=1
- ;;
o) HTML_BUILD=$OPTARG
;;
esac
@@ -71,55 +53,24 @@
shift `expr $OPTIND - 1`
-if [[ -n "$1" ]]; then
- master="master"
- if [[ "${master/#$1}" != "master" ]]; then
- # Partial match on "master"
- REPO=$MASTER_REPO
- else
- REPO=$1
- fi
- REPO_BRANCH=${2:-$MASTER_BRANCH}
-fi
-
-# Check out a specific DevStack branch
-if [[ -n $REPO ]]; then
- # Make a workspace
- TMP_ROOT=$(mktemp -d work-docs-XXXX)
- echo "Building docs in $TMP_ROOT"
- cd $TMP_ROOT
-
- # Get the master branch
- git clone $REPO devstack
- cd devstack
- if [[ -n "$REPO_BRANCH" ]]; then
- git checkout $REPO_BRANCH
- fi
-fi
-
-# Assumption is we are now in the DevStack workspace to be processed
-
# Processing
# ----------
-# Clean up build dir
-rm -rf $HTML_BUILD
+# Ensure build dir exists
mkdir -p $HTML_BUILD
# Get fully qualified dirs
-FQ_DOCS_SOURCE=$(cd $DOCS_SOURCE && pwd)
FQ_HTML_BUILD=$(cd $HTML_BUILD && pwd)
-# Get repo static
-cp -pr $FQ_DOCS_SOURCE/* $FQ_HTML_BUILD
-
# Insert automated bits
GLOG=$(mktemp gitlogXXXX)
+echo "<ul>" >$GLOG
git log \
--pretty=format:' <li>%s - <em>Commit <a href="https://review.openstack.org/#q,%h,n,z">%h</a> %cd</em></li>' \
--date=short \
- --since '6 months ago' | grep -v Merge >$GLOG
-sed -e $"/%GIT_LOG%/r $GLOG" $FQ_DOCS_SOURCE/changes.html >$FQ_HTML_BUILD/changes.html
+ --since '6 months ago' | grep -v Merge >>$GLOG
+echo "</ul>" >>$GLOG
+sed -i~ -e $"/^.*%GIT_LOG%.*$/r $GLOG" -e $"/^.*%GIT_LOG%.*$/s/^.*%GIT_LOG%.*$//" $FQ_HTML_BUILD/changes.html
rm -f $GLOG
# Build list of scripts to process
@@ -138,28 +89,6 @@
done
echo "$FILES" >doc/files
-if [[ -n $GH_UPDATE ]]; then
- GH_ROOT=$(mktemp -d work-gh-XXXX)
- cd $GH_ROOT
-
- # Pull the latest docs branch from devstack.org repo
- git clone -b gh-pages $GH_PAGES_REPO gh-docs
-
- # Get the generated files
- cp -pr $FQ_HTML_BUILD/* gh-docs
-
- # Collect the new generated pages
- (cd gh-docs; find . -name \*.html -print0 | xargs -0 git add)
-
- # Push our changes back up to the docs branch
- if ! git diff-index HEAD --quiet; then
- git commit -a -m "Update script docs"
- if [[ -n $PUSH ]]; then
- git push
- fi
- fi
-fi
-
# Clean up or report the temp workspace
if [[ -n REPO && -n $PUSH_REPO ]]; then
echo rm -rf $TMP_ROOT
diff --git a/tools/create_userrc.sh b/tools/create_userrc.sh
index b43fd88..f067ed1 100755
--- a/tools/create_userrc.sh
+++ b/tools/create_userrc.sh
@@ -131,7 +131,7 @@
EC2_URL=$(openstack endpoint show -f value -c publicurl ec2 || true)
if [[ -z $EC2_URL ]]; then
- EC2_URL=http://localhost:8773/services/Cloud
+ EC2_URL=http://localhost:8773/
fi
S3_URL=$(openstack endpoint show -f value -c publicurl s3 || true)
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index b8beb01..cc5275f 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -12,13 +12,10 @@
# - httplib2 0.8 permissions are 600 in the package and
# pip 1.4 doesn't fix it (1.3 did)
#
-# - RHEL6:
-#
+# - Fedora:
# - set selinux not enforcing
-# - (re)start messagebus daemon
-# - remove distro packages python-crypto and python-lxml
-# - pre-install hgtools to work around a bug in RHEL6 distribute
-# - install nose 1.1 from EPEL
+# - uninstall firewalld (f20 only)
+
# If TOP_DIR is set we're being sourced rather than running stand-alone
# or in a sub-shell
@@ -86,7 +83,7 @@
# Fix prettytable 0.7.2 permissions
# Don't specify --upgrade so we use the existing package if present
-pip_install 'prettytable>0.7'
+pip_install 'prettytable>=0.7'
PACKAGE_DIR=$(get_package_path prettytable)
# Only fix version 0.7.2
dir=$(echo $PACKAGE_DIR/prettytable-0.7.2*)
@@ -112,8 +109,8 @@
fi
FORCE_FIREWALLD=$(trueorfalse False $FORCE_FIREWALLD)
- if [[ ${DISTRO} =~ (f19|f20) && $FORCE_FIREWALLD == "False" ]]; then
- # On Fedora 19 and 20 firewalld interacts badly with libvirt and
+ if [[ ${DISTRO} =~ (f20) && $FORCE_FIREWALLD == "False" ]]; then
+ # On Fedora 20 firewalld interacts badly with libvirt and
# slows things down significantly. However, for those cases
# where that combination is desired, allow this fix to be skipped.
@@ -126,72 +123,3 @@
fi
fi
-
-# RHEL6
-# -----
-
-if [[ $DISTRO =~ (rhel6) ]]; then
-
- # install_pip.sh installs the latest setuptools over the packaged
- # version. We can't really uninstall the packaged version if it
- # is there, because it may remove other important things like
- # cloud-init. Things work, but there can be an old egg file left
- # around from the package that causes some really strange
- # setuptools errors. Remove it, if it is there
- sudo rm -f /usr/lib/python2.6/site-packages/setuptools-0.6*.egg-info
-
- # If the ``dbus`` package was installed by DevStack dependencies the
- # uuid may not be generated because the service was never started (PR#598200),
- # causing Nova to stop later on complaining that ``/var/lib/dbus/machine-id``
- # does not exist.
- sudo service messagebus restart
-
- # The following workarounds break xenserver
- if [ "$VIRT_DRIVER" != 'xenserver' ]; then
- # An old version of ``python-crypto`` (2.0.1) may be installed on a
- # fresh system via Anaconda and the dependency chain
- # ``cas`` -> ``python-paramiko`` -> ``python-crypto``.
- # ``pip uninstall pycrypto`` will remove the packaged ``.egg-info``
- # file but leave most of the actual library files behind in
- # ``/usr/lib64/python2.6/Crypto``. Later ``pip install pycrypto``
- # will install over the packaged files resulting
- # in a useless mess of old, rpm-packaged files and pip-installed files.
- # Remove the package so that ``pip install python-crypto`` installs
- # cleanly.
- # Note: other RPM packages may require ``python-crypto`` as well.
- # For example, RHEL6 does not install ``python-paramiko packages``.
- uninstall_package python-crypto
-
- # A similar situation occurs with ``python-lxml``, which is required by
- # ``ipa-client``, an auditing package we don't care about. The
- # build-dependencies needed for ``pip install lxml`` (``gcc``,
- # ``libxml2-dev`` and ``libxslt-dev``) are present in
- # ``files/rpms/general``.
- uninstall_package python-lxml
- fi
-
- # ``setup.py`` contains a ``setup_requires`` package that is supposed
- # to be transient. However, RHEL6 distribute has a bug where
- # ``setup_requires`` registers entry points that are not cleaned
- # out properly after the setup-phase resulting in installation failures
- # (bz#924038). Pre-install the problem package so the ``setup_requires``
- # dependency is satisfied and it will not be installed transiently.
- # Note we do this before the track-depends in ``stack.sh``.
- pip_install hgtools
-
-
- # RHEL6's version of ``python-nose`` is incompatible with Tempest.
- # Install nose 1.1 (Tempest-compatible) from EPEL
- install_package python-nose1.1
- # Add a symlink for the new nosetests to allow tox for Tempest to
- # work unmolested.
- sudo ln -sf /usr/bin/nosetests1.1 /usr/local/bin/nosetests
-
- # workaround for https://code.google.com/p/unittest-ext/issues/detail?id=79
- install_package python-unittest2 patch
- pip_install discover
- (cd /usr/lib/python2.6/site-packages/; sudo patch <"$FILES/patches/unittest2-discover.patch" || echo 'Assume already applied')
- # Make sure the discover.pyc is up to date
- sudo rm /usr/lib/python2.6/site-packages/discover.pyc || true
- sudo python -c 'import discover'
-fi
diff --git a/tools/install_pip.sh b/tools/install_pip.sh
index 55ef93e..73d0947 100755
--- a/tools/install_pip.sh
+++ b/tools/install_pip.sh
@@ -43,10 +43,10 @@
function install_get_pip {
if [[ ! -r $LOCAL_PIP ]]; then
- curl -o $LOCAL_PIP $PIP_GET_PIP_URL || \
+ curl --retry 6 --retry-delay 5 -o $LOCAL_PIP $PIP_GET_PIP_URL || \
die $LINENO "Download of get-pip.py failed"
fi
- sudo -E python $LOCAL_PIP
+ sudo -H -E python $LOCAL_PIP
}
@@ -68,6 +68,13 @@
}
+# Setuptools 8 implements PEP 440, and 8.0.4 adds a warning triggered any time
+# pkg_resources inspects the list of installed Python packages if there are
+# non-compliant version numbers in the egg-info (for example, from distro
+# system packaged Python libraries). This is off by default after 8.2 but can
+# be enabled by uncommenting the lines below.
+#PYTHONWARNINGS=$PYTHONWARNINGS,always::RuntimeWarning:pkg_resources
+#export PYTHONWARNINGS
# Show starting versions
get_versions
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index 9651083..303cc63 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -8,9 +8,15 @@
#
# -f Force an install run now
-if [[ -n "$1" && "$1" = "-f" ]]; then
- FORCE_PREREQ=1
-fi
+FORCE_PREREQ=0
+
+while getopts ":f" opt; do
+ case $opt in
+ f)
+ FORCE_PREREQ=1
+ ;;
+ esac
+done
# If TOP_DIR is set we're being sourced rather than running stand-alone
# or in a sub-shell
diff --git a/tools/ironic/scripts/configure-vm b/tools/ironic/scripts/configure-vm
index 4c42c49..378fcb8 100755
--- a/tools/ironic/scripts/configure-vm
+++ b/tools/ironic/scripts/configure-vm
@@ -78,8 +78,10 @@
params['emulator'] = "/usr/bin/qemu-kvm"
if args.console_log:
+ params['bios_serial'] = "<bios useserial='yes'/>"
params['console_log'] = CONSOLE_LOG % {'console_log': args.console_log}
else:
+ params['bios_serial'] = ''
params['console_log'] = ''
libvirt_template = source_template % params
conn = libvirt.open("qemu:///system")
diff --git a/tools/ironic/templates/vm.xml b/tools/ironic/templates/vm.xml
index 4f40334..ae7d685 100644
--- a/tools/ironic/templates/vm.xml
+++ b/tools/ironic/templates/vm.xml
@@ -6,6 +6,7 @@
<type arch='%(arch)s' machine='pc-1.0'>hvm</type>
<boot dev='%(bootdev)s'/>
<bootmenu enable='no'/>
+ %(bios_serial)s
</os>
<features>
<acpi/>
diff --git a/tools/upload_image.sh b/tools/upload_image.sh
index d81a5c8..5d23f31 100755
--- a/tools/upload_image.sh
+++ b/tools/upload_image.sh
@@ -37,6 +37,7 @@
# Glance connection info. Note the port must be specified.
GLANCE_HOSTPORT=${GLANCE_HOSTPORT:-$GLANCE_HOST:9292}
+GLANCE_SERVICE_PROTOCOL=${GLANCE_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
for IMAGE in "$*"; do
upload_image $IMAGE $TOKEN
diff --git a/tools/xen/build_xva.sh b/tools/xen/build_xva.sh
index 7c8e620..25bf58c 100755
--- a/tools/xen/build_xva.sh
+++ b/tools/xen/build_xva.sh
@@ -104,7 +104,7 @@
task
pre-start script
- rm -f /var/run/devstack.succeeded
+ rm -f /opt/stack/runsh.succeeded
end script
script
@@ -119,9 +119,7 @@
chown -R $STACK_USER /opt/stack
- if su -c "/opt/stack/run.sh" $STACK_USER; then
- touch /var/run/devstack.succeeded
- fi
+ su -c "/opt/stack/run.sh" $STACK_USER
# Update /etc/issue
{
@@ -129,7 +127,7 @@
IPADDR=\$(ip -4 address show eth0 | sed -n 's/.*inet \\([0-9\.]\\+\\).*/\1/p')
echo " Management IP: \$IPADDR"
echo -n " Devstack run: "
- if [ -e /var/run/devstack.succeeded ]; then
+ if [ -e /opt/stack/runsh.succeeded ]; then
echo "SUCCEEDED"
else
echo "FAILED"
@@ -177,8 +175,19 @@
cat <<EOF >$STAGING_DIR/opt/stack/run.sh
#!/bin/bash
set -eux
-cd /opt/stack/devstack
-./unstack.sh || true
-./stack.sh
+(
+ flock -n 9 || exit 1
+
+ [ -e /opt/stack/runsh.succeeded ] && rm /opt/stack/runsh.succeeded
+ echo \$\$ >> /opt/stack/run_sh.pid
+
+ cd /opt/stack/devstack
+ ./unstack.sh || true
+ ./stack.sh
+
+ # Got to the end - success
+ touch /opt/stack/runsh.succeeded
+ rm /opt/stack/run_sh.pid
+) 9> /opt/stack/.runsh_lock
EOF
chmod 755 $STAGING_DIR/opt/stack/run.sh
diff --git a/tools/xen/functions b/tools/xen/functions
index c8efd57..4e9fede 100644
--- a/tools/xen/functions
+++ b/tools/xen/functions
@@ -10,7 +10,7 @@
}
function xapi_plugin_location {
- for PLUGIN_DIR in "/etc/xapi.d/plugins/" "/usr/lib/xcp/plugins/" "/usr/lib/xapi/plugins"; do
+ for PLUGIN_DIR in "/etc/xapi.d/plugins/" "/usr/lib/xcp/plugins/" "/usr/lib/xapi/plugins" "/usr/lib64/xapi/plugins"; do
if [ -d $PLUGIN_DIR ]; then
echo $PLUGIN_DIR
return 0
diff --git a/tools/xen/install_os_domU.sh b/tools/xen/install_os_domU.sh
index 3a63473..546ead6 100755
--- a/tools/xen/install_os_domU.sh
+++ b/tools/xen/install_os_domU.sh
@@ -365,25 +365,27 @@
if [ "$WAIT_TILL_LAUNCH" = "1" ] && [ -e ~/.ssh/id_rsa.pub ] && [ "$COPYENV" = "1" ]; then
set +x
- echo "VM Launched - Waiting for devstack to start"
- while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "service devstack status | grep -q running"; do
+ echo "VM Launched - Waiting for run.sh"
+ while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "test -e /opt/stack/run_sh.pid"; do
sleep 10
done
echo -n "devstack service is running, waiting for stack.sh to start logging..."
- while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "test -e /tmp/devstack/log/stack.log"; do
- sleep 10
- done
+ pid=`ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "cat /opt/stack/run_sh.pid"`
+ if [ -n "$SCREEN_LOGDIR" ]; then
+ while ! ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "test -e ${SCREEN_LOGDIR}/stack.log"; do
+ sleep 10
+ done
+
+ ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "tail --pid $pid -n +1 -f ${SCREEN_LOGDIR}/stack.log"
+ else
+ echo -n "SCREEN_LOGDIR not set; just waiting for process $pid to finish"
+ ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "wait $pid"
+ fi
+
set -x
-
- # Watch devstack's output (which doesn't start until stack.sh is running,
- # but wait for run.sh (which starts stack.sh) to exit as that is what
- # hopefully writes the succeeded cookie.
- pid=`ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS pgrep run.sh`
- ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS "tail --pid $pid -n +1 -f /tmp/devstack/log/stack.log"
-
# Fail if devstack did not succeed
- ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS 'test -e /var/run/devstack.succeeded'
+ ssh_no_check -q stack@$OS_VM_MANAGEMENT_ADDRESS 'test -e /opt/stack/runsh.succeeded'
set +x
echo "################################################################################"
@@ -401,7 +403,7 @@
echo ""
echo "ssh into your domU now: 'ssh stack@$OS_VM_MANAGEMENT_ADDRESS' using your password"
echo "and then do: 'sudo service devstack status' to check if devstack is still running."
- echo "Check that /var/run/devstack.succeeded exists"
+ echo "Check that /opt/stack/runsh.succeeded exists"
echo ""
echo "When devstack completes, you can visit the OpenStack Dashboard"
echo "at http://$OS_VM_SERVICES_ADDRESS, and contact other services at the usual ports."
diff --git a/tools/xen/prepare_guest.sh b/tools/xen/prepare_guest.sh
index 7383c91..7fe032a 100755
--- a/tools/xen/prepare_guest.sh
+++ b/tools/xen/prepare_guest.sh
@@ -73,9 +73,7 @@
# Install basics
apt-get update
apt-get install -y cracklib-runtime curl wget ssh openssh-server tcpdump ethtool
-apt-get install -y curl wget ssh openssh-server python-pip git sudo python-netaddr
-apt-get install -y coreutils
-pip install xenapi
+apt-get install -y git sudo python-netaddr coreutils
# Install XenServer guest utilities
dpkg -i $XS_TOOLS_PATH
diff --git a/tools/xen/xenrc b/tools/xen/xenrc
index 0cbf861..43a6ce8 100644
--- a/tools/xen/xenrc
+++ b/tools/xen/xenrc
@@ -70,8 +70,8 @@
# XenServer 6.1 and later or XCP 1.6 or later
# 11.10 is only really supported with XenServer 6.0.2 and later
UBUNTU_INST_ARCH="amd64"
-UBUNTU_INST_HTTP_HOSTNAME="mirror.anl.gov"
-UBUNTU_INST_HTTP_DIRECTORY="/pub/ubuntu"
+UBUNTU_INST_HTTP_HOSTNAME="archive.ubuntu.com"
+UBUNTU_INST_HTTP_DIRECTORY="/ubuntu"
UBUNTU_INST_HTTP_PROXY=""
UBUNTU_INST_LOCALE="en_US"
UBUNTU_INST_KEYBOARD="us"
diff --git a/tox.ini b/tox.ini
index c8d3909..a958ae7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -36,5 +36,5 @@
TOP_DIR={toxinidir}
INSTALL_SHOCCO=true
commands =
- bash tools/build_docs.sh
python setup.py build_sphinx
+ bash tools/build_docs.sh
diff --git a/unstack.sh b/unstack.sh
index fee608e..6deeba2 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/bash
# **unstack.sh**
@@ -6,11 +6,22 @@
# mysql and rabbit are left running as OpenStack code refreshes
# do not require them to be restarted.
#
-# Stop all processes by setting ``UNSTACK_ALL`` or specifying ``--all``
+# Stop all processes by setting ``UNSTACK_ALL`` or specifying ``-a``
# on the command line
+UNSTACK_ALL=""
+
+while getopts ":a" opt; do
+ case $opt in
+ a)
+ UNSTACK_ALL=""
+ ;;
+ esac
+done
+
# Keep track of the current devstack directory.
TOP_DIR=$(cd $(dirname "$0") && pwd)
+FILES=$TOP_DIR/files
# Import common functions
source $TOP_DIR/functions
@@ -19,7 +30,7 @@
source $TOP_DIR/lib/database
# Load local configuration
-source $TOP_DIR/stackrc
+source $TOP_DIR/openrc
# Destination path for service data
DATA_DIR=${DATA_DIR:-${DEST}/data}
@@ -43,7 +54,7 @@
# Source project function libraries
source $TOP_DIR/lib/infra
source $TOP_DIR/lib/oslo
-source $TOP_DIR/lib/stackforge
+source $TOP_DIR/lib/lvm
source $TOP_DIR/lib/horizon
source $TOP_DIR/lib/keystone
source $TOP_DIR/lib/glance
@@ -53,7 +64,6 @@
source $TOP_DIR/lib/ceilometer
source $TOP_DIR/lib/heat
source $TOP_DIR/lib/neutron
-source $TOP_DIR/lib/baremetal
source $TOP_DIR/lib/ldap
source $TOP_DIR/lib/dstat
@@ -67,23 +77,17 @@
done
fi
+load_plugin_settings
+
# Determine what system we are running on. This provides ``os_VENDOR``,
# ``os_RELEASE``, ``os_UPDATE``, ``os_PACKAGE``, ``os_CODENAME``
GetOSVersion
-if [[ "$1" == "--all" ]]; then
- UNSTACK_ALL=${UNSTACK_ALL:-1}
-fi
-
# Run extras
# ==========
# Phase: unstack
-if [[ -d $TOP_DIR/extras.d ]]; then
- for i in $TOP_DIR/extras.d/*.sh; do
- [[ -r $i ]] && source $i unstack
- done
-fi
+run_phase unstack
if [[ "$Q_USE_DEBUG_COMMAND" == "True" ]]; then
source $TOP_DIR/openrc
@@ -128,13 +132,19 @@
stop_tls_proxy
cleanup_CA
fi
+if [ "$USE_SSL" == "True" ]; then
+ cleanup_CA
+fi
SCSI_PERSIST_DIR=$CINDER_STATE_PATH/volumes/*
+# BUG: tgt likes to exit 1 on service stop if everything isn't
+# perfect, we should clean up cinder stop paths.
+
# Get the iSCSI volumes
if is_service_enabled cinder; then
- stop_cinder
- cleanup_cinder
+ stop_cinder || /bin/true
+ cleanup_cinder || /bin/true
fi
if [[ -n "$UNSTACK_ALL" ]]; then
@@ -173,3 +183,6 @@
screen -X -S $SESSION quit
fi
fi
+
+# BUG: maybe it doesn't exist? We should isolate this further down.
+clean_lvm_volume_group $DEFAULT_VOLUME_GROUP_NAME || /bin/true