Reduce duplicated horizon code
Change-Id: I106858cee2f9dcb87b59022790f48c263db7b7e0 Implements: blueprint reduce-horizon-code
This commit is contained in:
parent
9b190ac678
commit
ecb7598bf9
@ -14,33 +14,25 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import collections
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core import urlresolvers
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django import template
|
from django import template
|
||||||
from django.template.defaultfilters import truncatechars
|
|
||||||
from django.template.loader import render_to_string
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils import http
|
from django.utils import http
|
||||||
from django.utils.http import urlencode
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils import termcolors
|
from django.utils import termcolors
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import conf
|
from horizon import conf
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
from horizon.tables.actions import FilterAction
|
|
||||||
from horizon.tables.actions import LinkAction
|
from horizon.tables.actions import LinkAction
|
||||||
from horizon.utils import html
|
from horizon.tables import base as horizon_tables
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -48,152 +40,7 @@ PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE]
|
|||||||
STRING_SEPARATOR = "__"
|
STRING_SEPARATOR = "__"
|
||||||
|
|
||||||
|
|
||||||
class Column(html.HTMLElement):
|
class Column(horizon_tables.Column):
|
||||||
""" A class which represents a single column in a :class:`.DataTable`.
|
|
||||||
|
|
||||||
.. attribute:: transform
|
|
||||||
|
|
||||||
A string or callable. If ``transform`` is a string, it should be the
|
|
||||||
name of the attribute on the underlying data class which
|
|
||||||
should be displayed in this column. If it is a callable, it
|
|
||||||
will be passed the current row's data at render-time and should
|
|
||||||
return the contents of the cell. Required.
|
|
||||||
|
|
||||||
.. attribute:: verbose_name
|
|
||||||
|
|
||||||
The name for this column which should be used for display purposes.
|
|
||||||
Defaults to the value of ``transform`` with the first letter
|
|
||||||
of each word capitalized.
|
|
||||||
|
|
||||||
.. attribute:: sortable
|
|
||||||
|
|
||||||
Boolean to determine whether this column should be sortable or not.
|
|
||||||
Defaults to ``True``.
|
|
||||||
|
|
||||||
.. attribute:: hidden
|
|
||||||
|
|
||||||
Boolean to determine whether or not this column should be displayed
|
|
||||||
when rendering the table. Default: ``False``.
|
|
||||||
|
|
||||||
.. attribute:: link
|
|
||||||
|
|
||||||
A string or callable which returns a URL which will be wrapped around
|
|
||||||
this column's text as a link.
|
|
||||||
|
|
||||||
.. attribute:: allowed_data_types
|
|
||||||
|
|
||||||
A list of data types for which the link should be created.
|
|
||||||
Default is an empty list (``[]``).
|
|
||||||
|
|
||||||
When the list is empty and the ``link`` attribute is not None, all the
|
|
||||||
rows under this column will be links.
|
|
||||||
|
|
||||||
.. attribute:: status
|
|
||||||
|
|
||||||
Boolean designating whether or not this column represents a status
|
|
||||||
(i.e. "enabled/disabled", "up/down", "active/inactive").
|
|
||||||
Default: ``False``.
|
|
||||||
|
|
||||||
.. attribute:: status_choices
|
|
||||||
|
|
||||||
A tuple of tuples representing the possible data values for the
|
|
||||||
status column and their associated boolean equivalent. Positive
|
|
||||||
states should equate to ``True``, negative states should equate
|
|
||||||
to ``False``, and indeterminate states should be ``None``.
|
|
||||||
|
|
||||||
Values are compared in a case-insensitive manner.
|
|
||||||
|
|
||||||
Example (these are also the default values)::
|
|
||||||
|
|
||||||
status_choices = (
|
|
||||||
('enabled', True),
|
|
||||||
('true', True)
|
|
||||||
('up', True),
|
|
||||||
('active', True),
|
|
||||||
('on', True),
|
|
||||||
('none', None),
|
|
||||||
('unknown', None),
|
|
||||||
('', None),
|
|
||||||
('disabled', False),
|
|
||||||
('down', False),
|
|
||||||
('false', False),
|
|
||||||
('inactive', False),
|
|
||||||
('off', False),
|
|
||||||
)
|
|
||||||
|
|
||||||
.. attribute:: display_choices
|
|
||||||
|
|
||||||
A tuple of tuples representing the possible values to substitute
|
|
||||||
the data when displayed in the column cell.
|
|
||||||
|
|
||||||
.. attribute:: empty_value
|
|
||||||
|
|
||||||
A string or callable to be used for cells which have no data.
|
|
||||||
Defaults to the string ``"-"``.
|
|
||||||
|
|
||||||
.. attribute:: summation
|
|
||||||
|
|
||||||
A string containing the name of a summation method to be used in
|
|
||||||
the generation of a summary row for this column. By default the
|
|
||||||
options are ``"sum"`` or ``"average"``, which behave as expected.
|
|
||||||
Optional.
|
|
||||||
|
|
||||||
.. attribute:: filters
|
|
||||||
|
|
||||||
A list of functions (often template filters) to be applied to the
|
|
||||||
value of the data for this column prior to output. This is effectively
|
|
||||||
a shortcut for writing a custom ``transform`` function in simple cases.
|
|
||||||
|
|
||||||
.. attribute:: classes
|
|
||||||
|
|
||||||
An iterable of CSS classes which should be added to this column.
|
|
||||||
Example: ``classes=('foo', 'bar')``.
|
|
||||||
|
|
||||||
.. attribute:: attrs
|
|
||||||
|
|
||||||
A dict of HTML attribute strings which should be added to this column.
|
|
||||||
Example: ``attrs={"data-foo": "bar"}``.
|
|
||||||
|
|
||||||
.. attribute:: truncate
|
|
||||||
|
|
||||||
An integer for the maximum length of the string in this column. If the
|
|
||||||
data in this column is larger than the supplied number, the data for
|
|
||||||
this column will be truncated and an ellipsis will be appended to the
|
|
||||||
truncated data.
|
|
||||||
Defaults to ``None``.
|
|
||||||
|
|
||||||
.. attribute:: link_classes
|
|
||||||
|
|
||||||
An iterable of CSS classes which will be added when the column's text
|
|
||||||
is displayed as a link.
|
|
||||||
Example: ``classes=('link-foo', 'link-bar')``.
|
|
||||||
Defaults to ``None``.
|
|
||||||
"""
|
|
||||||
summation_methods = {
|
|
||||||
"sum": sum,
|
|
||||||
"average": lambda data: sum(data, 0.0) / len(data)
|
|
||||||
}
|
|
||||||
# Used to retain order when instantiating columns on a table
|
|
||||||
creation_counter = 0
|
|
||||||
|
|
||||||
transform = None
|
|
||||||
name = None
|
|
||||||
verbose_name = None
|
|
||||||
status_choices = (
|
|
||||||
('enabled', True),
|
|
||||||
('true', True),
|
|
||||||
('up', True),
|
|
||||||
('active', True),
|
|
||||||
('on', True),
|
|
||||||
('none', None),
|
|
||||||
('unknown', None),
|
|
||||||
('', None),
|
|
||||||
('disabled', False),
|
|
||||||
('down', False),
|
|
||||||
('false', False),
|
|
||||||
('inactive', False),
|
|
||||||
('off', False),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, transform, verbose_name=None, sortable=True,
|
def __init__(self, transform, verbose_name=None, sortable=True,
|
||||||
link=None, allowed_data_types=[], hidden=False, attrs=None,
|
link=None, allowed_data_types=[], hidden=False, attrs=None,
|
||||||
@ -203,237 +50,17 @@ class Column(html.HTMLElement):
|
|||||||
# FIXME: Added for TableStep:
|
# FIXME: Added for TableStep:
|
||||||
form_widget=None, form_widget_attributes=None
|
form_widget=None, form_widget_attributes=None
|
||||||
):
|
):
|
||||||
|
super(Column, self).__init__(
|
||||||
|
transform, verbose_name, sortable, link, allowed_data_types,
|
||||||
|
hidden, attrs, status, status_choices, display_choices,
|
||||||
|
empty_value, filters, classes, summation, auto, truncate,
|
||||||
|
link_classes)
|
||||||
|
|
||||||
self.classes = list(classes or getattr(self, "classes", []))
|
|
||||||
super(Column, self).__init__()
|
|
||||||
self.attrs.update(attrs or {})
|
|
||||||
|
|
||||||
if callable(transform):
|
|
||||||
self.transform = transform
|
|
||||||
self.name = transform.__name__
|
|
||||||
else:
|
|
||||||
self.transform = unicode(transform)
|
|
||||||
self.name = self.transform
|
|
||||||
|
|
||||||
# Empty string is a valid value for verbose_name
|
|
||||||
if verbose_name is None:
|
|
||||||
verbose_name = self.transform.title()
|
|
||||||
else:
|
|
||||||
verbose_name = verbose_name
|
|
||||||
|
|
||||||
self.auto = auto
|
|
||||||
self.sortable = sortable
|
|
||||||
self.verbose_name = verbose_name
|
|
||||||
self.link = link
|
|
||||||
self.allowed_data_types = allowed_data_types
|
|
||||||
self.hidden = hidden
|
|
||||||
self.status = status
|
|
||||||
self.empty_value = empty_value or '-'
|
|
||||||
self.filters = filters or []
|
|
||||||
self.truncate = truncate
|
|
||||||
self.link_classes = link_classes or []
|
|
||||||
self.form_widget = form_widget # FIXME: TableStep
|
self.form_widget = form_widget # FIXME: TableStep
|
||||||
self.form_widget_attributes = form_widget_attributes or {} # TableStep
|
self.form_widget_attributes = form_widget_attributes or {} # TableStep
|
||||||
|
|
||||||
if status_choices:
|
|
||||||
self.status_choices = status_choices
|
|
||||||
self.display_choices = display_choices
|
|
||||||
|
|
||||||
if summation is not None and summation not in self.summation_methods:
|
class Row(horizon_tables.Row):
|
||||||
raise ValueError("Summation method %s must be one of %s."
|
|
||||||
% (summation,
|
|
||||||
", ".join(self.summation_methods.keys())))
|
|
||||||
self.summation = summation
|
|
||||||
|
|
||||||
self.creation_counter = Column.creation_counter
|
|
||||||
Column.creation_counter += 1
|
|
||||||
|
|
||||||
if self.sortable and not self.auto:
|
|
||||||
self.classes.append("sortable")
|
|
||||||
if self.hidden:
|
|
||||||
self.classes.append("hide")
|
|
||||||
if self.link is not None:
|
|
||||||
self.classes.append('anchor')
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.verbose_name)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.name)
|
|
||||||
|
|
||||||
def get_raw_data(self, datum):
|
|
||||||
"""
|
|
||||||
Returns the raw data for this column, before any filters or formatting
|
|
||||||
are applied to it. This is useful when doing calculations on data in
|
|
||||||
the table.
|
|
||||||
"""
|
|
||||||
# Callable transformations
|
|
||||||
if callable(self.transform):
|
|
||||||
data = self.transform(datum)
|
|
||||||
# Basic object lookups
|
|
||||||
elif hasattr(datum, self.transform):
|
|
||||||
data = getattr(datum, self.transform, None)
|
|
||||||
# Dict lookups
|
|
||||||
elif isinstance(datum, collections.Iterable) and \
|
|
||||||
self.transform in datum:
|
|
||||||
data = datum.get(self.transform)
|
|
||||||
else:
|
|
||||||
if settings.DEBUG:
|
|
||||||
msg = _("The attribute %(attr)s doesn't exist on "
|
|
||||||
"%(obj)s.") % {'attr': self.transform, 'obj': datum}
|
|
||||||
msg = termcolors.colorize(msg, **PALETTE['ERROR'])
|
|
||||||
LOG.warning(msg)
|
|
||||||
data = None
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_data(self, datum):
|
|
||||||
"""
|
|
||||||
Returns the final display data for this column from the given inputs.
|
|
||||||
|
|
||||||
The return value will be either the attribute specified for this column
|
|
||||||
or the return value of the attr:`~horizon.tables.Column.transform`
|
|
||||||
method for this column.
|
|
||||||
"""
|
|
||||||
datum_id = self.table.get_object_id(datum)
|
|
||||||
|
|
||||||
if datum_id in self.table._data_cache[self]:
|
|
||||||
return self.table._data_cache[self][datum_id]
|
|
||||||
|
|
||||||
data = self.get_raw_data(datum)
|
|
||||||
display_value = None
|
|
||||||
|
|
||||||
if self.display_choices:
|
|
||||||
display_value = [display for (value, display) in
|
|
||||||
self.display_choices
|
|
||||||
if value.lower() == (data or '').lower()]
|
|
||||||
|
|
||||||
if display_value:
|
|
||||||
data = display_value[0]
|
|
||||||
else:
|
|
||||||
for filter_func in self.filters:
|
|
||||||
data = filter_func(data)
|
|
||||||
|
|
||||||
if data and self.truncate:
|
|
||||||
data = truncatechars(data, self.truncate)
|
|
||||||
|
|
||||||
self.table._data_cache[self][datum_id] = data
|
|
||||||
|
|
||||||
return self.table._data_cache[self][datum_id]
|
|
||||||
|
|
||||||
def get_link_url(self, datum):
|
|
||||||
""" Returns the final value for the column's ``link`` property.
|
|
||||||
|
|
||||||
If ``allowed_data_types`` of this column is not empty and the datum
|
|
||||||
has an assigned type, check if the datum's type is in the
|
|
||||||
``allowed_data_types`` list. If not, the datum won't be displayed
|
|
||||||
as a link.
|
|
||||||
|
|
||||||
If ``link`` is a callable, it will be passed the current data object
|
|
||||||
and should return a URL. Otherwise ``get_link_url`` will attempt to
|
|
||||||
call ``reverse`` on ``link`` with the object's id as a parameter.
|
|
||||||
Failing that, it will simply return the value of ``link``.
|
|
||||||
"""
|
|
||||||
if self.allowed_data_types:
|
|
||||||
data_type_name = self.table._meta.data_type_name
|
|
||||||
data_type = getattr(datum, data_type_name, None)
|
|
||||||
if data_type and (data_type not in self.allowed_data_types):
|
|
||||||
return None
|
|
||||||
obj_id = self.table.get_object_id(datum)
|
|
||||||
if callable(self.link):
|
|
||||||
return self.link(datum)
|
|
||||||
try:
|
|
||||||
return urlresolvers.reverse(self.link, args=(obj_id,))
|
|
||||||
except urlresolvers.NoReverseMatch:
|
|
||||||
return self.link
|
|
||||||
|
|
||||||
def get_summation(self):
|
|
||||||
"""
|
|
||||||
Returns the summary value for the data in this column if a
|
|
||||||
valid summation method is specified for it. Otherwise returns ``None``.
|
|
||||||
"""
|
|
||||||
if self.summation not in self.summation_methods:
|
|
||||||
return None
|
|
||||||
|
|
||||||
summation_function = self.summation_methods[self.summation]
|
|
||||||
data = [self.get_raw_data(datum) for datum in self.table.data]
|
|
||||||
data = filter(lambda datum: datum is not None, data)
|
|
||||||
|
|
||||||
if len(data):
|
|
||||||
summation = summation_function(data)
|
|
||||||
for filter_func in self.filters:
|
|
||||||
summation = filter_func(summation)
|
|
||||||
return summation
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Row(html.HTMLElement):
|
|
||||||
""" Represents a row in the table.
|
|
||||||
|
|
||||||
When iterated, the ``Row`` instance will yield each of its cells.
|
|
||||||
|
|
||||||
Rows are capable of AJAX updating, with a little added work:
|
|
||||||
|
|
||||||
The ``ajax`` property needs to be set to ``True``, and
|
|
||||||
subclasses need to define a ``get_data`` method which returns a data
|
|
||||||
object appropriate for consumption by the table (effectively the "get"
|
|
||||||
lookup versus the table's "list" lookup).
|
|
||||||
|
|
||||||
The automatic update interval is configurable by setting the key
|
|
||||||
``ajax_poll_interval`` in the ``HORIZON_CONFIG`` dictionary.
|
|
||||||
Default: ``2500`` (measured in milliseconds).
|
|
||||||
|
|
||||||
.. attribute:: table
|
|
||||||
|
|
||||||
The table which this row belongs to.
|
|
||||||
|
|
||||||
.. attribute:: datum
|
|
||||||
|
|
||||||
The data object which this row represents.
|
|
||||||
|
|
||||||
.. attribute:: id
|
|
||||||
|
|
||||||
A string uniquely representing this row composed of the table name
|
|
||||||
and the row data object's identifier.
|
|
||||||
|
|
||||||
.. attribute:: cells
|
|
||||||
|
|
||||||
The cells belonging to this row stored in a ``SortedDict`` object.
|
|
||||||
This attribute is populated during instantiation.
|
|
||||||
|
|
||||||
.. attribute:: status
|
|
||||||
|
|
||||||
Boolean value representing the status of this row calculated from
|
|
||||||
the values of the table's ``status_columns`` if they are set.
|
|
||||||
|
|
||||||
.. attribute:: status_class
|
|
||||||
|
|
||||||
Returns a css class for the status of the row based on ``status``.
|
|
||||||
|
|
||||||
.. attribute:: ajax
|
|
||||||
|
|
||||||
Boolean value to determine whether ajax updating for this row is
|
|
||||||
enabled.
|
|
||||||
|
|
||||||
.. attribute:: ajax_action_name
|
|
||||||
|
|
||||||
String that is used for the query parameter key to request AJAX
|
|
||||||
updates. Generally you won't need to change this value.
|
|
||||||
Default: ``"row_update"``.
|
|
||||||
"""
|
|
||||||
ajax = False
|
|
||||||
ajax_action_name = "row_update"
|
|
||||||
|
|
||||||
def __init__(self, table, datum=None):
|
|
||||||
super(Row, self).__init__()
|
|
||||||
self.table = table
|
|
||||||
self.datum = datum
|
|
||||||
self.selected = False
|
|
||||||
if self.datum:
|
|
||||||
self.load_cells()
|
|
||||||
else:
|
|
||||||
self.id = None
|
|
||||||
self.cells = []
|
|
||||||
|
|
||||||
def load_cells(self, datum=None):
|
def load_cells(self, datum=None):
|
||||||
"""
|
"""
|
||||||
@ -501,7 +128,7 @@ class Row(html.HTMLElement):
|
|||||||
table._data_cache[column][table.get_object_id(datum)] = data
|
table._data_cache[column][table.get_object_id(datum)] = data
|
||||||
else:
|
else:
|
||||||
data = column.get_data(datum)
|
data = column.get_data(datum)
|
||||||
cell = Cell(datum, data, column, self)
|
cell = horizon_tables.Cell(datum, data, column, self)
|
||||||
cells.append((column.name or column.auto, cell))
|
cells.append((column.name or column.auto, cell))
|
||||||
self.cells = SortedDict(cells)
|
self.cells = SortedDict(cells)
|
||||||
|
|
||||||
@ -524,328 +151,18 @@ class Row(html.HTMLElement):
|
|||||||
if display_name:
|
if display_name:
|
||||||
self.attrs['data-display'] = escape(display_name)
|
self.attrs['data-display'] = escape(display_name)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.id)
|
|
||||||
|
|
||||||
def __iter__(self):
|
class DataTableOptions(horizon_tables.DataTableOptions):
|
||||||
return iter(self.cells.values())
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self):
|
|
||||||
column_names = self.table._meta.status_columns
|
|
||||||
if column_names:
|
|
||||||
statuses = dict([(column_name, self.cells[column_name].status) for
|
|
||||||
column_name in column_names])
|
|
||||||
return self.table.calculate_row_status(statuses)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status_class(self):
|
|
||||||
column_names = self.table._meta.status_columns
|
|
||||||
if column_names:
|
|
||||||
return self.table.get_row_status_class(self.status)
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
return render_to_string("horizon/common/_data_table_row.html",
|
|
||||||
{"row": self})
|
|
||||||
|
|
||||||
def get_cells(self):
|
|
||||||
""" Returns the bound cells for this row in order. """
|
|
||||||
return self.cells.values()
|
|
||||||
|
|
||||||
def get_ajax_update_url(self):
|
|
||||||
table_url = self.table.get_absolute_url()
|
|
||||||
params = urlencode({"table": self.table.name,
|
|
||||||
"action": self.ajax_action_name,
|
|
||||||
"obj_id": self.table.get_object_id(self.datum)})
|
|
||||||
return "%s?%s" % (table_url, params)
|
|
||||||
|
|
||||||
def get_data(self, request, obj_id):
|
|
||||||
"""
|
|
||||||
Fetches the updated data for the row based on the object id
|
|
||||||
passed in. Must be implemented by a subclass to allow AJAX updating.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError("You must define a get_data method on %s"
|
|
||||||
% self.__class__.__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Cell(html.HTMLElement):
|
|
||||||
""" Represents a single cell in the table. """
|
|
||||||
def __init__(self, datum, data, column, row, attrs=None, classes=None):
|
|
||||||
self.classes = classes or getattr(self, "classes", [])
|
|
||||||
super(Cell, self).__init__()
|
|
||||||
self.attrs.update(attrs or {})
|
|
||||||
|
|
||||||
self.datum = datum
|
|
||||||
self.data = data
|
|
||||||
self.column = column
|
|
||||||
self.row = row
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<%s: %s, %s>' % (self.__class__.__name__,
|
|
||||||
self.column.name,
|
|
||||||
self.row.id)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def value(self):
|
|
||||||
"""
|
|
||||||
Returns a formatted version of the data for final output.
|
|
||||||
|
|
||||||
This takes into consideration the
|
|
||||||
:attr:`~horizon.tables.Column.link`` and
|
|
||||||
:attr:`~horizon.tables.Column.empty_value`
|
|
||||||
attributes.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
data = self.column.get_data(self.datum)
|
|
||||||
if data is None:
|
|
||||||
if callable(self.column.empty_value):
|
|
||||||
data = self.column.empty_value(self.datum)
|
|
||||||
else:
|
|
||||||
data = self.column.empty_value
|
|
||||||
except Exception:
|
|
||||||
data = None
|
|
||||||
exc_info = sys.exc_info()
|
|
||||||
raise template.TemplateSyntaxError, exc_info[1], exc_info[2]
|
|
||||||
if self.url:
|
|
||||||
link_classes = ' '.join(self.column.link_classes)
|
|
||||||
# Escape the data inside while allowing our HTML to render
|
|
||||||
data = mark_safe('<a href="%s" class="%s">%s</a>' %
|
|
||||||
(self.url, link_classes, escape(data)))
|
|
||||||
return data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def url(self):
|
|
||||||
if self.column.link:
|
|
||||||
url = self.column.get_link_url(self.datum)
|
|
||||||
if url:
|
|
||||||
return url
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self):
|
|
||||||
""" Gets the status for the column based on the cell's data. """
|
|
||||||
# Deal with status column mechanics based in this cell's data
|
|
||||||
if hasattr(self, '_status'):
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
if self.column.status or \
|
|
||||||
self.column.name in self.column.table._meta.status_columns:
|
|
||||||
#returns the first matching status found
|
|
||||||
data_value_lower = unicode(self.data).lower()
|
|
||||||
for status_name, status_value in self.column.status_choices:
|
|
||||||
if unicode(status_name).lower() == data_value_lower:
|
|
||||||
self._status = status_value
|
|
||||||
return self._status
|
|
||||||
self._status = None
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
def get_status_class(self, status):
|
|
||||||
""" Returns a css class name determined by the status value. """
|
|
||||||
if status is True:
|
|
||||||
return "status_up"
|
|
||||||
elif status is False:
|
|
||||||
return "status_down"
|
|
||||||
else:
|
|
||||||
return "status_unknown"
|
|
||||||
|
|
||||||
def get_default_classes(self):
|
|
||||||
""" Returns a flattened string of the cell's CSS classes. """
|
|
||||||
if not self.url:
|
|
||||||
self.column.classes = [cls for cls in self.column.classes
|
|
||||||
if cls != "anchor"]
|
|
||||||
column_class_string = self.column.get_final_attrs().get('class', "")
|
|
||||||
classes = set(column_class_string.split(" "))
|
|
||||||
if self.column.status:
|
|
||||||
classes.add(self.get_status_class(self.status))
|
|
||||||
return list(classes)
|
|
||||||
|
|
||||||
|
|
||||||
class DataTableOptions(object):
|
|
||||||
""" Contains options for :class:`.DataTable` objects.
|
|
||||||
|
|
||||||
.. attribute:: name
|
|
||||||
|
|
||||||
A short name or slug for the table.
|
|
||||||
|
|
||||||
.. attribute:: verbose_name
|
|
||||||
|
|
||||||
A more verbose name for the table meant for display purposes.
|
|
||||||
|
|
||||||
.. attribute:: columns
|
|
||||||
|
|
||||||
A list of column objects or column names. Controls ordering/display
|
|
||||||
of the columns in the table.
|
|
||||||
|
|
||||||
.. attribute:: table_actions
|
|
||||||
|
|
||||||
A list of action classes derived from the
|
|
||||||
:class:`~horizon.tables.Action` class. These actions will handle tasks
|
|
||||||
such as bulk deletion, etc. for multiple objects at once.
|
|
||||||
|
|
||||||
.. attribute:: row_actions
|
|
||||||
|
|
||||||
A list similar to ``table_actions`` except tailored to appear for
|
|
||||||
each row. These actions act on a single object at a time.
|
|
||||||
|
|
||||||
.. attribute:: actions_column
|
|
||||||
|
|
||||||
Boolean value to control rendering of an additional column containing
|
|
||||||
the various actions for each row. Defaults to ``True`` if any actions
|
|
||||||
are specified in the ``row_actions`` option.
|
|
||||||
|
|
||||||
.. attribute:: multi_select
|
|
||||||
|
|
||||||
Boolean value to control rendering of an extra column with checkboxes
|
|
||||||
for selecting multiple objects in the table. Defaults to ``True`` if
|
|
||||||
any actions are specified in the ``table_actions`` option.
|
|
||||||
|
|
||||||
.. attribute:: filter
|
|
||||||
|
|
||||||
Boolean value to control the display of the "filter" search box
|
|
||||||
in the table actions. By default it checks whether or not an instance
|
|
||||||
of :class:`.FilterAction` is in :attr:`.table_actions`.
|
|
||||||
|
|
||||||
.. attribute:: template
|
|
||||||
|
|
||||||
String containing the template which should be used to render the
|
|
||||||
table. Defaults to ``"horizon/common/_data_table.html"``.
|
|
||||||
|
|
||||||
.. attribute:: context_var_name
|
|
||||||
|
|
||||||
The name of the context variable which will contain the table when
|
|
||||||
it is rendered. Defaults to ``"table"``.
|
|
||||||
|
|
||||||
.. attribute:: pagination_param
|
|
||||||
|
|
||||||
The name of the query string parameter which will be used when
|
|
||||||
paginating this table. When using multiple tables in a single
|
|
||||||
view this will need to be changed to differentiate between the
|
|
||||||
tables. Default: ``"marker"``.
|
|
||||||
|
|
||||||
.. attribute:: status_columns
|
|
||||||
|
|
||||||
A list or tuple of column names which represents the "state"
|
|
||||||
of the data object being represented.
|
|
||||||
|
|
||||||
If ``status_columns`` is set, when the rows are rendered the value
|
|
||||||
of this column will be used to add an extra class to the row in
|
|
||||||
the form of ``"status_up"`` or ``"status_down"`` for that row's
|
|
||||||
data.
|
|
||||||
|
|
||||||
The row status is used by other Horizon components to trigger tasks
|
|
||||||
such as dynamic AJAX updating.
|
|
||||||
|
|
||||||
.. attribute:: row_class
|
|
||||||
|
|
||||||
The class which should be used for rendering the rows of this table.
|
|
||||||
Optional. Default: :class:`~horizon.tables.Row`.
|
|
||||||
|
|
||||||
.. attribute:: column_class
|
|
||||||
|
|
||||||
The class which should be used for handling the columns of this table.
|
|
||||||
Optional. Default: :class:`~horizon.tables.Column`.
|
|
||||||
|
|
||||||
.. attribute:: mixed_data_type
|
|
||||||
|
|
||||||
A toggle to indicate if the table accepts two or more types of data.
|
|
||||||
Optional. Default: :``False``
|
|
||||||
|
|
||||||
.. attribute:: data_types
|
|
||||||
|
|
||||||
A list of data types that this table would accept. Default to be an
|
|
||||||
empty list, but if the attibute ``mixed_data_type`` is set to ``True``,
|
|
||||||
then this list must have at least one element.
|
|
||||||
|
|
||||||
.. attribute:: data_type_name
|
|
||||||
|
|
||||||
The name of an attribute to assign to data passed to the table when it
|
|
||||||
accepts mix data. Default: ``"_table_data_type"``
|
|
||||||
|
|
||||||
.. attribute:: footer
|
|
||||||
|
|
||||||
Boolean to control whether or not to show the table's footer.
|
|
||||||
Default: ``True``.
|
|
||||||
|
|
||||||
.. attribute:: permissions
|
|
||||||
|
|
||||||
A list of permission names which this table requires in order to be
|
|
||||||
displayed. Defaults to an empty list (``[]``).
|
|
||||||
"""
|
|
||||||
def __init__(self, options):
|
def __init__(self, options):
|
||||||
self.name = getattr(options, 'name', self.__class__.__name__)
|
super(DataTableOptions, self).__init__(options)
|
||||||
verbose_name = getattr(options, 'verbose_name', None) \
|
|
||||||
or self.name.title()
|
# FIXME: TableStep
|
||||||
self.verbose_name = verbose_name
|
|
||||||
self.columns = getattr(options, 'columns', None)
|
|
||||||
self.status_columns = getattr(options, 'status_columns', [])
|
|
||||||
self.table_actions = getattr(options, 'table_actions', [])
|
|
||||||
self.row_actions = getattr(options, 'row_actions', [])
|
|
||||||
self.row_class = getattr(options, 'row_class', Row)
|
self.row_class = getattr(options, 'row_class', Row)
|
||||||
self.column_class = getattr(options, 'column_class', Column)
|
self.column_class = getattr(options, 'column_class', Column)
|
||||||
self.pagination_param = getattr(options, 'pagination_param', 'marker')
|
|
||||||
self.browser_table = getattr(options, 'browser_table', None)
|
|
||||||
self.footer = getattr(options, 'footer', True)
|
|
||||||
self.no_data_message = getattr(options,
|
|
||||||
"no_data_message",
|
|
||||||
_("No items to display."))
|
|
||||||
self.permissions = getattr(options, 'permissions', [])
|
|
||||||
|
|
||||||
# Set self.filter if we have any FilterActions
|
|
||||||
filter_actions = [action for action in self.table_actions if
|
|
||||||
issubclass(action, FilterAction)]
|
|
||||||
if len(filter_actions) > 1:
|
|
||||||
raise NotImplementedError("Multiple filter actions is not "
|
|
||||||
"currently supported.")
|
|
||||||
self.filter = getattr(options, 'filter', len(filter_actions) > 0)
|
|
||||||
if len(filter_actions) == 1:
|
|
||||||
self._filter_action = filter_actions.pop()
|
|
||||||
else:
|
|
||||||
self._filter_action = None
|
|
||||||
|
|
||||||
self.template = 'horizon/common/_data_table.html'
|
|
||||||
self.row_actions_template = \
|
|
||||||
'horizon/common/_data_table_row_actions.html'
|
|
||||||
self.table_actions_template = \
|
|
||||||
'horizon/common/_data_table_table_actions.html'
|
|
||||||
self.context_var_name = unicode(getattr(options,
|
|
||||||
'context_var_name',
|
|
||||||
'table'))
|
|
||||||
self.actions_column = getattr(options,
|
|
||||||
'actions_column',
|
|
||||||
len(self.row_actions) > 0)
|
|
||||||
# FIXME: TableStep
|
|
||||||
self.multi_select_name = getattr(options,
|
self.multi_select_name = getattr(options,
|
||||||
'multi_select_name',
|
'multi_select_name',
|
||||||
'object_ids')
|
'object_ids')
|
||||||
self.multi_select = getattr(options,
|
|
||||||
'multi_select',
|
|
||||||
len(self.table_actions) > 0)
|
|
||||||
|
|
||||||
# Set runtime table defaults; not configurable.
|
|
||||||
self.has_more_data = False
|
|
||||||
|
|
||||||
# Set mixed data type table attr
|
|
||||||
self.mixed_data_type = getattr(options, 'mixed_data_type', False)
|
|
||||||
self.data_types = getattr(options, 'data_types', [])
|
|
||||||
|
|
||||||
# If the data_types has more than 2 elements, set mixed_data_type
|
|
||||||
# to True automatically.
|
|
||||||
if len(self.data_types) > 1:
|
|
||||||
self.mixed_data_type = True
|
|
||||||
|
|
||||||
# However, if the mixed_data_type is set to True manually and the
|
|
||||||
# the data_types is empty, raise an errror.
|
|
||||||
if self.mixed_data_type and len(self.data_types) <= 1:
|
|
||||||
raise ValueError("If mixed_data_type is set to True in class %s, "
|
|
||||||
"data_types should has more than one types" %
|
|
||||||
self.name)
|
|
||||||
|
|
||||||
self.data_type_name = getattr(options,
|
|
||||||
'data_type_name',
|
|
||||||
"_table_data_type")
|
|
||||||
|
|
||||||
|
|
||||||
class DataTableMetaclass(type):
|
class DataTableMetaclass(type):
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from horizon import exceptions
|
|
||||||
|
|
||||||
from openstack_dashboard.api import base as api_base
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class APIResource(api_base.APIResourceWrapper):
|
|
||||||
""" Simple APIResource for testing """
|
|
||||||
_attrs = ['foo', 'bar', 'baz']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_instance(innerObject=None):
|
|
||||||
if innerObject is None:
|
|
||||||
|
|
||||||
class InnerAPIResource(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
innerObject = InnerAPIResource()
|
|
||||||
innerObject.foo = 'foo'
|
|
||||||
innerObject.bar = 'bar'
|
|
||||||
return APIResource(innerObject)
|
|
||||||
|
|
||||||
|
|
||||||
class APIDict(api_base.APIDictWrapper):
|
|
||||||
""" Simple APIDict for testing """
|
|
||||||
_attrs = ['foo', 'bar', 'baz']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_instance(innerDict=None):
|
|
||||||
if innerDict is None:
|
|
||||||
innerDict = {'foo': 'foo',
|
|
||||||
'bar': 'bar'}
|
|
||||||
return APIDict(innerDict)
|
|
||||||
|
|
||||||
|
|
||||||
# Wrapper classes that only define _attrs don't need extra testing.
|
|
||||||
class APIResourceWrapperTests(test.TestCase):
|
|
||||||
def test_get_attribute(self):
|
|
||||||
resource = APIResource.get_instance()
|
|
||||||
self.assertEqual(resource.foo, 'foo')
|
|
||||||
|
|
||||||
def test_get_invalid_attribute(self):
|
|
||||||
resource = APIResource.get_instance()
|
|
||||||
self.assertNotIn('missing', resource._attrs,
|
|
||||||
msg="Test assumption broken. Find new missing attribute")
|
|
||||||
with self.assertRaises(AttributeError):
|
|
||||||
resource.missing
|
|
||||||
|
|
||||||
def test_get_inner_missing_attribute(self):
|
|
||||||
resource = APIResource.get_instance()
|
|
||||||
with self.assertRaises(AttributeError):
|
|
||||||
resource.baz
|
|
||||||
|
|
||||||
def test_repr(self):
|
|
||||||
resource = APIResource.get_instance()
|
|
||||||
resource_str = resource.__repr__()
|
|
||||||
self.assertIn('foo', resource_str)
|
|
||||||
self.assertIn('bar', resource_str)
|
|
||||||
self.assertNotIn('baz', resource_str)
|
|
||||||
|
|
||||||
|
|
||||||
class APIDictWrapperTests(test.TestCase):
|
|
||||||
# APIDict allows for both attribute access and dictionary style [element]
|
|
||||||
# style access. Test both
|
|
||||||
def test_get_item(self):
|
|
||||||
resource = APIDict.get_instance()
|
|
||||||
self.assertEqual(resource.foo, 'foo')
|
|
||||||
self.assertEqual(resource['foo'], 'foo')
|
|
||||||
|
|
||||||
def test_get_invalid_item(self):
|
|
||||||
resource = APIDict.get_instance()
|
|
||||||
self.assertNotIn('missing', resource._attrs,
|
|
||||||
msg="Test assumption broken. Find new missing attribute")
|
|
||||||
with self.assertRaises(AttributeError):
|
|
||||||
resource.missing
|
|
||||||
with self.assertRaises(KeyError):
|
|
||||||
resource['missing']
|
|
||||||
|
|
||||||
def test_get_inner_missing_attribute(self):
|
|
||||||
resource = APIDict.get_instance()
|
|
||||||
with self.assertRaises(AttributeError):
|
|
||||||
resource.baz
|
|
||||||
with self.assertRaises(KeyError):
|
|
||||||
resource['baz']
|
|
||||||
|
|
||||||
def test_get_with_default(self):
|
|
||||||
resource = APIDict.get_instance()
|
|
||||||
|
|
||||||
self.assertEqual(resource.get('foo'), 'foo')
|
|
||||||
|
|
||||||
self.assertIsNone(resource.get('baz'))
|
|
||||||
|
|
||||||
self.assertEqual('retValue', resource.get('baz', 'retValue'))
|
|
||||||
|
|
||||||
|
|
||||||
class ApiHelperTests(test.TestCase):
|
|
||||||
""" Tests for functions that don't use one of the api objects """
|
|
||||||
|
|
||||||
def test_url_for(self):
|
|
||||||
url = api_base.url_for(self.request, 'image')
|
|
||||||
self.assertEqual(url, 'http://public.glance.example.com:9292/v1')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'image', endpoint_type='adminURL')
|
|
||||||
self.assertEqual(url, 'http://admin.glance.example.com:9292/v1')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'compute')
|
|
||||||
self.assertEqual(url, 'http://public.nova.example.com:8774/v2')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'compute',
|
|
||||||
endpoint_type='adminURL')
|
|
||||||
self.assertEqual(url, 'http://admin.nova.example.com:8774/v2')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'volume')
|
|
||||||
self.assertEqual(url, 'http://public.nova.example.com:8776/v1')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'volume',
|
|
||||||
endpoint_type="internalURL")
|
|
||||||
self.assertEqual(url, 'http://int.nova.example.com:8776/v1')
|
|
||||||
|
|
||||||
url = api_base.url_for(self.request, 'volume',
|
|
||||||
endpoint_type='adminURL')
|
|
||||||
self.assertEqual(url, 'http://admin.nova.example.com:8776/v1')
|
|
||||||
|
|
||||||
self.assertNotIn('notAnApi', self.request.user.service_catalog,
|
|
||||||
'Select a new nonexistent service catalog key')
|
|
||||||
with self.assertRaises(exceptions.ServiceCatalogException):
|
|
||||||
url = api_base.url_for(self.request, 'notAnApi')
|
|
||||||
|
|
||||||
self.request.user.services_region = "RegionTwo"
|
|
||||||
url = api_base.url_for(self.request, 'compute')
|
|
||||||
self.assertEqual(url, 'http://public.nova2.example.com:8774/v2')
|
|
||||||
|
|
||||||
self.request.user.services_region = "RegionTwo"
|
|
||||||
url = api_base.url_for(self.request, 'compute',
|
|
||||||
endpoint_type='adminURL')
|
|
||||||
self.assertEqual(url, 'http://admin.nova2.example.com:8774/v2')
|
|
||||||
|
|
||||||
self.request.user.services_region = "RegionTwo"
|
|
||||||
with self.assertRaises(exceptions.ServiceCatalogException):
|
|
||||||
url = api_base.url_for(self.request, 'image')
|
|
||||||
|
|
||||||
self.request.user.services_region = "bogus_value"
|
|
||||||
url = api_base.url_for(self.request, 'identity',
|
|
||||||
endpoint_type='adminURL')
|
|
||||||
self.assertEqual(url, 'http://admin.keystone.example.com:35357/v2.0')
|
|
||||||
|
|
||||||
self.request.user.services_region = "bogus_value"
|
|
||||||
with self.assertRaises(exceptions.ServiceCatalogException):
|
|
||||||
url = api_base.url_for(self.request, 'image')
|
|
@ -1,56 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 Red Hat, 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.
|
|
||||||
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class CinderApiTests(test.APITestCase):
|
|
||||||
def test_volume_list(self):
|
|
||||||
search_opts = {'all_tenants': 1}
|
|
||||||
volumes = self.volumes.list()
|
|
||||||
cinderclient = self.stub_cinderclient()
|
|
||||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
|
||||||
cinderclient.volumes.list(search_opts=search_opts,).AndReturn(volumes)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
# No assertions are necessary. Verification is handled by mox.
|
|
||||||
api.cinder.volume_list(self.request, search_opts=search_opts)
|
|
||||||
|
|
||||||
def test_volume_snapshot_list(self):
|
|
||||||
volume_snapshots = self.volume_snapshots.list()
|
|
||||||
cinderclient = self.stub_cinderclient()
|
|
||||||
cinderclient.volume_snapshots = self.mox.CreateMockAnything()
|
|
||||||
cinderclient.volume_snapshots.list().AndReturn(volume_snapshots)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.cinder.volume_snapshot_list(self.request)
|
|
||||||
|
|
||||||
def test_volume_snapshot_list_no_volume_configured(self):
|
|
||||||
# remove volume from service catalog
|
|
||||||
catalog = self.service_catalog
|
|
||||||
for service in catalog:
|
|
||||||
if service["type"] == "volume":
|
|
||||||
self.service_catalog.remove(service)
|
|
||||||
volume_snapshots = self.volume_snapshots.list()
|
|
||||||
|
|
||||||
cinderclient = self.stub_cinderclient()
|
|
||||||
cinderclient.volume_snapshots = self.mox.CreateMockAnything()
|
|
||||||
cinderclient.volume_snapshots.list().AndReturn(volume_snapshots)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.cinder.volume_snapshot_list(self.request)
|
|
@ -1,106 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class GlanceApiTests(test.APITestCase):
|
|
||||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
|
||||||
def test_image_list_detailed_no_pagination(self):
|
|
||||||
# Verify that all images are returned even with a small page size
|
|
||||||
api_images = self.images.list()
|
|
||||||
filters = {}
|
|
||||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
|
||||||
|
|
||||||
glanceclient = self.stub_glanceclient()
|
|
||||||
glanceclient.images = self.mox.CreateMockAnything()
|
|
||||||
glanceclient.images.list(page_size=limit,
|
|
||||||
limit=limit,
|
|
||||||
filters=filters,).AndReturn(iter(api_images))
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
images, has_more = api.glance.image_list_detailed(self.request)
|
|
||||||
self.assertItemsEqual(images, api_images)
|
|
||||||
self.assertFalse(has_more)
|
|
||||||
|
|
||||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
|
||||||
def test_image_list_detailed_pagination(self):
|
|
||||||
# The total snapshot count is over page size, should return
|
|
||||||
# page_size images.
|
|
||||||
filters = {}
|
|
||||||
page_size = settings.API_RESULT_PAGE_SIZE
|
|
||||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
|
||||||
|
|
||||||
api_images = self.images.list()
|
|
||||||
images_iter = iter(api_images)
|
|
||||||
|
|
||||||
glanceclient = self.stub_glanceclient()
|
|
||||||
glanceclient.images = self.mox.CreateMockAnything()
|
|
||||||
# Pass back all images, ignoring filters
|
|
||||||
glanceclient.images.list(limit=limit,
|
|
||||||
page_size=page_size + 1,
|
|
||||||
filters=filters,).AndReturn(images_iter)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
images, has_more = api.glance.image_list_detailed(self.request,
|
|
||||||
marker=None,
|
|
||||||
filters=filters,
|
|
||||||
paginate=True)
|
|
||||||
expected_images = api_images[:page_size]
|
|
||||||
self.assertItemsEqual(images, expected_images)
|
|
||||||
self.assertTrue(has_more)
|
|
||||||
# Ensure that only the needed number of images are consumed
|
|
||||||
# from the iterator (page_size + 1).
|
|
||||||
self.assertEqual(len(list(images_iter)),
|
|
||||||
len(api_images) - len(expected_images) - 1)
|
|
||||||
|
|
||||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
|
||||||
def test_image_list_detailed_pagination_marker(self):
|
|
||||||
# Tests getting a second page with a marker.
|
|
||||||
filters = {}
|
|
||||||
page_size = settings.API_RESULT_PAGE_SIZE
|
|
||||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
|
||||||
marker = 'nonsense'
|
|
||||||
|
|
||||||
api_images = self.images.list()[page_size:]
|
|
||||||
images_iter = iter(api_images)
|
|
||||||
|
|
||||||
glanceclient = self.stub_glanceclient()
|
|
||||||
glanceclient.images = self.mox.CreateMockAnything()
|
|
||||||
# Pass back all images, ignoring filters
|
|
||||||
glanceclient.images.list(limit=limit,
|
|
||||||
page_size=page_size + 1,
|
|
||||||
filters=filters,
|
|
||||||
marker=marker).AndReturn(images_iter)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
images, has_more = api.glance.image_list_detailed(self.request,
|
|
||||||
marker=marker,
|
|
||||||
filters=filters,
|
|
||||||
paginate=True)
|
|
||||||
expected_images = api_images[:page_size]
|
|
||||||
self.assertItemsEqual(images, expected_images)
|
|
||||||
self.assertTrue(has_more)
|
|
||||||
self.assertEqual(len(list(images_iter)),
|
|
||||||
len(api_images) - len(expected_images) - 1)
|
|
@ -1,29 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class HeatApiTests(test.APITestCase):
|
|
||||||
def test_stack_list(self):
|
|
||||||
api_stacks = self.stacks.list()
|
|
||||||
|
|
||||||
heatclient = self.stub_heatclient()
|
|
||||||
heatclient.stacks = self.mox.CreateMockAnything()
|
|
||||||
heatclient.stacks.list().AndReturn(iter(api_stacks))
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
stacks = api.heat.stacks_list(self.request)
|
|
||||||
self.assertItemsEqual(stacks, api_stacks)
|
|
@ -1,118 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from keystoneclient.v2_0 import client as keystone_client
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class FakeConnection(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ClientConnectionTests(test.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(ClientConnectionTests, self).setUp()
|
|
||||||
self.mox.StubOutWithMock(keystone_client, "Client")
|
|
||||||
self.internal_url = api.base.url_for(self.request,
|
|
||||||
'identity',
|
|
||||||
endpoint_type='internalURL')
|
|
||||||
self.admin_url = api.base.url_for(self.request,
|
|
||||||
'identity',
|
|
||||||
endpoint_type='adminURL')
|
|
||||||
self.conn = FakeConnection()
|
|
||||||
|
|
||||||
|
|
||||||
class RoleAPITests(test.APITestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(RoleAPITests, self).setUp()
|
|
||||||
self.role = self.roles.member
|
|
||||||
self.roles = self.roles.list()
|
|
||||||
|
|
||||||
def test_remove_tenant_user(self):
|
|
||||||
"""
|
|
||||||
Tests api.keystone.remove_tenant_user
|
|
||||||
|
|
||||||
Verifies that remove_tenant_user is called with the right arguments
|
|
||||||
after iterating the user's roles.
|
|
||||||
|
|
||||||
There are no assertions in this test because the checking is handled
|
|
||||||
by mox in the VerifyAll() call in tearDown().
|
|
||||||
"""
|
|
||||||
keystoneclient = self.stub_keystoneclient()
|
|
||||||
tenant = self.tenants.first()
|
|
||||||
|
|
||||||
keystoneclient.roles = self.mox.CreateMockAnything()
|
|
||||||
keystoneclient.roles.roles_for_user(self.user.id,
|
|
||||||
tenant.id).AndReturn(self.roles)
|
|
||||||
for role in self.roles:
|
|
||||||
keystoneclient.roles.revoke(role.id,
|
|
||||||
domain=None,
|
|
||||||
group=None,
|
|
||||||
project=tenant.id,
|
|
||||||
user=self.user.id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
api.keystone.remove_tenant_user(self.request, tenant.id, self.user.id)
|
|
||||||
|
|
||||||
def test_get_default_role(self):
|
|
||||||
keystoneclient = self.stub_keystoneclient()
|
|
||||||
keystoneclient.roles = self.mox.CreateMockAnything()
|
|
||||||
keystoneclient.roles.list().AndReturn(self.roles)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
role = api.keystone.get_default_role(self.request)
|
|
||||||
self.assertEqual(role, self.role)
|
|
||||||
# Verify that a second call doesn't hit the API again,
|
|
||||||
# (it would show up in mox as an unexpected method call)
|
|
||||||
role = api.keystone.get_default_role(self.request)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceAPITests(test.APITestCase):
|
|
||||||
def test_service_wrapper(self):
|
|
||||||
catalog = self.service_catalog
|
|
||||||
identity_data = api.base.get_service_from_catalog(catalog, "identity")
|
|
||||||
identity_data['id'] = 1
|
|
||||||
region = identity_data["endpoints"][0]["region"]
|
|
||||||
service = api.keystone.Service(identity_data, region)
|
|
||||||
self.assertEqual(unicode(service), u"identity (native backend)")
|
|
||||||
self.assertEqual(service.region,
|
|
||||||
identity_data["endpoints"][0]["region"])
|
|
||||||
self.assertEqual(service.url,
|
|
||||||
"http://int.keystone.example.com:5000/v2.0")
|
|
||||||
self.assertEqual(service.public_url,
|
|
||||||
"http://public.keystone.example.com:5000/v2.0")
|
|
||||||
self.assertEqual(service.host, "int.keystone.example.com")
|
|
||||||
|
|
||||||
def test_service_wrapper_service_in_region(self):
|
|
||||||
catalog = self.service_catalog
|
|
||||||
compute_data = api.base.get_service_from_catalog(catalog, "compute")
|
|
||||||
compute_data['id'] = 1
|
|
||||||
region = compute_data["endpoints"][1]["region"]
|
|
||||||
service = api.keystone.Service(compute_data, region)
|
|
||||||
self.assertEqual(unicode(service), u"compute")
|
|
||||||
self.assertEqual(service.region,
|
|
||||||
compute_data["endpoints"][1]["region"])
|
|
||||||
self.assertEqual(service.url,
|
|
||||||
"http://int.nova2.example.com:8774/v2")
|
|
||||||
self.assertEqual(service.public_url,
|
|
||||||
"http://public.nova2.example.com:8774/v2")
|
|
||||||
self.assertEqual(service.host, "int.nova2.example.com")
|
|
@ -1,339 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2013, Big Switch Networks, 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.
|
|
||||||
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
from neutronclient.v2_0.client import Client as neutronclient
|
|
||||||
|
|
||||||
|
|
||||||
class LbaasApiTests(test.APITestCase):
|
|
||||||
@test.create_stubs({neutronclient: ('create_vip',)})
|
|
||||||
def test_vip_create(self):
|
|
||||||
vip1 = self.api_vips.first()
|
|
||||||
form_data = {'address': vip1['address'],
|
|
||||||
'name': vip1['name'],
|
|
||||||
'description': vip1['description'],
|
|
||||||
'subnet_id': vip1['subnet_id'],
|
|
||||||
'protocol_port': vip1['protocol_port'],
|
|
||||||
'protocol': vip1['protocol'],
|
|
||||||
'pool_id': vip1['pool_id'],
|
|
||||||
'session_persistence': vip1['session_persistence'],
|
|
||||||
'connection_limit': vip1['connection_limit'],
|
|
||||||
'admin_state_up': vip1['admin_state_up']
|
|
||||||
}
|
|
||||||
vip = {'vip': self.api_vips.first()}
|
|
||||||
neutronclient.create_vip({'vip': form_data}).AndReturn(vip)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.vip_create(self.request, **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('list_vips',)})
|
|
||||||
def test_vips_get(self):
|
|
||||||
vips = {'vips': [{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.0.100',
|
|
||||||
'name': 'vip1name',
|
|
||||||
'description': 'vip1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
|
||||||
'connection_limit': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}, ]}
|
|
||||||
neutronclient.list_vips().AndReturn(vips)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.vips_get(self.request)
|
|
||||||
for v in ret_val:
|
|
||||||
self.assertIsInstance(v, api.lbaas.Vip)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('show_vip',)})
|
|
||||||
def test_vip_get(self):
|
|
||||||
vip = {'vip': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.0.100',
|
|
||||||
'name': 'vip1name',
|
|
||||||
'description': 'vip1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
|
||||||
'connection_limit': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
neutronclient.show_vip(vip['vip']['id']).AndReturn(vip)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.vip_get(self.request, vip['vip']['id'])
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('update_vip',)})
|
|
||||||
def test_vip_update(self):
|
|
||||||
form_data = {'address': '10.0.0.100',
|
|
||||||
'name': 'vip1name',
|
|
||||||
'description': 'vip1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
|
||||||
'connection_limit': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
|
|
||||||
vip = {'vip': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.0.100',
|
|
||||||
'name': 'vip1name',
|
|
||||||
'description': 'vip1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
|
||||||
'connection_limit': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
neutronclient.update_vip(vip['vip']['id'], form_data).AndReturn(vip)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.vip_update(self.request,
|
|
||||||
vip['vip']['id'], **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('create_pool',)})
|
|
||||||
def test_pool_create(self):
|
|
||||||
form_data = {'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
neutronclient.create_pool({'pool': form_data}).AndReturn(pool)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_create(self.request, **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('list_pools',)})
|
|
||||||
def test_pools_get(self):
|
|
||||||
pools = {'pools': [{
|
|
||||||
'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'admin_state_up': True}, ]}
|
|
||||||
neutronclient.list_pools().AndReturn(pools)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pools_get(self.request)
|
|
||||||
for v in ret_val:
|
|
||||||
self.assertIsInstance(v, api.lbaas.Pool)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('show_pool',)})
|
|
||||||
def test_pool_get(self):
|
|
||||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
neutronclient.show_pool(pool['pool']['id']).AndReturn(pool)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_get(self.request, pool['pool']['id'])
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('update_pool',)})
|
|
||||||
def test_pool_update(self):
|
|
||||||
form_data = {'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTPS',
|
|
||||||
'lb_method': 'LEAST_CONNECTION',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'pool1name',
|
|
||||||
'description': 'pool1description',
|
|
||||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'protocol': 'HTTPS',
|
|
||||||
'lb_method': 'LEAST_CONNECTION',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
neutronclient.update_pool(pool['pool']['id'],
|
|
||||||
form_data).AndReturn(pool)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_update(self.request,
|
|
||||||
pool['pool']['id'], **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('create_health_monitor',)})
|
|
||||||
def test_pool_health_monitor_create(self):
|
|
||||||
form_data = {'type': 'PING',
|
|
||||||
'delay': '10',
|
|
||||||
'timeout': '10',
|
|
||||||
'max_retries': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
monitor = {'health_monitor': {
|
|
||||||
'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'type': 'PING',
|
|
||||||
'delay': '10',
|
|
||||||
'timeout': '10',
|
|
||||||
'max_retries': '10',
|
|
||||||
'admin_state_up': True}}
|
|
||||||
neutronclient.create_health_monitor({
|
|
||||||
'health_monitor': form_data}).AndReturn(monitor)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_health_monitor_create(
|
|
||||||
self.request, **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.PoolMonitor)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('list_health_monitors',)})
|
|
||||||
def test_pool_health_monitors_get(self):
|
|
||||||
monitors = {'health_monitors': [
|
|
||||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'type': 'PING',
|
|
||||||
'delay': '10',
|
|
||||||
'timeout': '10',
|
|
||||||
'max_retries': '10',
|
|
||||||
'http_method': 'GET',
|
|
||||||
'url_path': '/monitor',
|
|
||||||
'expected_codes': '200',
|
|
||||||
'admin_state_up': True}, ]}
|
|
||||||
|
|
||||||
neutronclient.list_health_monitors().AndReturn(monitors)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_health_monitors_get(self.request)
|
|
||||||
for v in ret_val:
|
|
||||||
self.assertIsInstance(v, api.lbaas.PoolMonitor)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('show_health_monitor',)})
|
|
||||||
def test_pool_health_monitor_get(self):
|
|
||||||
monitor = {'health_monitor':
|
|
||||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'type': 'PING',
|
|
||||||
'delay': '10',
|
|
||||||
'timeout': '10',
|
|
||||||
'max_retries': '10',
|
|
||||||
'http_method': 'GET',
|
|
||||||
'url_path': '/monitor',
|
|
||||||
'expected_codes': '200',
|
|
||||||
'admin_state_up': True}}
|
|
||||||
neutronclient.show_health_monitor(
|
|
||||||
monitor['health_monitor']['id']).AndReturn(monitor)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.pool_health_monitor_get(
|
|
||||||
self.request, monitor['health_monitor']['id'])
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.PoolMonitor)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('create_member', )})
|
|
||||||
def test_member_create(self):
|
|
||||||
form_data = {'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.2',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
|
|
||||||
member = {'member':
|
|
||||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.2',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True}}
|
|
||||||
|
|
||||||
neutronclient.create_member({'member': form_data}).AndReturn(member)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.member_create(self.request, **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('list_members',)})
|
|
||||||
def test_members_get(self):
|
|
||||||
members = {'members': [
|
|
||||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.2',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}, ]}
|
|
||||||
neutronclient.list_members().AndReturn(members)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.members_get(self.request)
|
|
||||||
for v in ret_val:
|
|
||||||
self.assertIsInstance(v, api.lbaas.Member)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('show_member',)})
|
|
||||||
def test_member_get(self):
|
|
||||||
member = {'member': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.2',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True}}
|
|
||||||
neutronclient.show_member(member['member']['id']).AndReturn(member)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.member_get(self.request, member['member']['id'])
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
|
||||||
|
|
||||||
@test.create_stubs({neutronclient: ('update_member',)})
|
|
||||||
def test_member_update(self):
|
|
||||||
form_data = {'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.4',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}
|
|
||||||
|
|
||||||
member = {'member': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'address': '10.0.1.2',
|
|
||||||
'protocol_port': '80',
|
|
||||||
'weight': '10',
|
|
||||||
'admin_state_up': True
|
|
||||||
}}
|
|
||||||
|
|
||||||
neutronclient.update_member(member['member']['id'],
|
|
||||||
form_data).AndReturn(member)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.lbaas.member_update(self.request,
|
|
||||||
member['member']['id'], **form_data)
|
|
||||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
|
@ -1,457 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2013 NEC Corporation
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import copy
|
|
||||||
import itertools
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django import http
|
|
||||||
from mox import IsA
|
|
||||||
|
|
||||||
from novaclient.v1_1.floating_ip_pools import FloatingIPPool
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkApiNovaTestBase(test.APITestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(NetworkApiNovaTestBase, self).setUp()
|
|
||||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
|
||||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
|
||||||
.AndReturn(False)
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkApiNovaFloatingIpTests(NetworkApiNovaTestBase):
|
|
||||||
def test_floating_ip_pools_list(self):
|
|
||||||
pool_names = ['pool1', 'pool2']
|
|
||||||
pools = [FloatingIPPool(None, {'name': pool}) for pool in pool_names]
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ip_pools = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ip_pools.list().AndReturn(pools)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.floating_ip_pools_list(self.request)
|
|
||||||
self.assertEqual([p.name for p in ret], pool_names)
|
|
||||||
|
|
||||||
def test_floating_ip_list(self):
|
|
||||||
fips = self.api_floating_ips.list()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ips.list().AndReturn(fips)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_list(self.request)
|
|
||||||
for r, e in zip(ret, fips):
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'instance_id']:
|
|
||||||
self.assertEqual(r.__getattr__(attr), e.__getattr__(attr))
|
|
||||||
self.assertEqual(r.port_id, e.instance_id)
|
|
||||||
|
|
||||||
def test_floating_ip_get(self):
|
|
||||||
fip = self.api_floating_ips.first()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ips.get(fip.id).AndReturn(fip)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_get(self.request, fip.id)
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'instance_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), fip.__getattr__(attr))
|
|
||||||
self.assertEqual(ret.port_id, fip.instance_id)
|
|
||||||
|
|
||||||
def test_floating_ip_allocate(self):
|
|
||||||
pool_name = 'fip_pool'
|
|
||||||
fip = self.api_floating_ips.first()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ips.create(pool=pool_name).AndReturn(fip)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_allocate(self.request, pool_name)
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'instance_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), fip.__getattr__(attr))
|
|
||||||
self.assertEqual(ret.port_id, fip.instance_id)
|
|
||||||
|
|
||||||
def test_floating_ip_release(self):
|
|
||||||
fip = self.api_floating_ips.first()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ips.delete(fip.id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.tenant_floating_ip_release(self.request, fip.id)
|
|
||||||
|
|
||||||
def test_floating_ip_associate(self):
|
|
||||||
server = api.nova.Server(self.servers.first(), self.request)
|
|
||||||
floating_ip = self.floating_ips.first()
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get(server.id).AndReturn(server)
|
|
||||||
novaclient.floating_ips.get(floating_ip.id).AndReturn(floating_ip)
|
|
||||||
novaclient.servers.add_floating_ip(server.id, floating_ip.ip) \
|
|
||||||
.AndReturn(server)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.floating_ip_associate(self.request,
|
|
||||||
floating_ip.id,
|
|
||||||
server.id)
|
|
||||||
|
|
||||||
def test_floating_ip_disassociate(self):
|
|
||||||
server = api.nova.Server(self.servers.first(), self.request)
|
|
||||||
floating_ip = self.api_floating_ips.first()
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.floating_ips = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get(server.id).AndReturn(server)
|
|
||||||
novaclient.floating_ips.get(floating_ip.id).AndReturn(floating_ip)
|
|
||||||
novaclient.servers.remove_floating_ip(server.id, floating_ip.ip) \
|
|
||||||
.AndReturn(server)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.floating_ip_disassociate(self.request,
|
|
||||||
floating_ip.id,
|
|
||||||
server.id)
|
|
||||||
|
|
||||||
def test_floating_ip_target_list(self):
|
|
||||||
servers = self.servers.list()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.list().AndReturn(servers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
targets = api.network.floating_ip_target_list(self.request)
|
|
||||||
for target, server in zip(targets, servers):
|
|
||||||
self.assertEqual(target.id, server.id)
|
|
||||||
self.assertEqual(target.name, '%s (%s)' % (server.name, server.id))
|
|
||||||
|
|
||||||
def test_floating_ip_target_get_by_instance(self):
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
instance_id = self.servers.first().id
|
|
||||||
ret = api.network.floating_ip_target_get_by_instance(self.request,
|
|
||||||
instance_id)
|
|
||||||
self.assertEqual(instance_id, ret)
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkApiNeutronTestBase(test.APITestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(NetworkApiNeutronTestBase, self).setUp()
|
|
||||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
|
||||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
|
||||||
.AndReturn(True)
|
|
||||||
self.qclient = self.stub_neutronclient()
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkApiNeutronSecurityGroupTests(NetworkApiNeutronTestBase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(NetworkApiNeutronSecurityGroupTests, self).setUp()
|
|
||||||
self.sg_dict = dict([(sg['id'], sg['name']) for sg
|
|
||||||
in self.api_q_secgroups.list()])
|
|
||||||
|
|
||||||
def _cmp_sg_rule(self, exprule, retrule):
|
|
||||||
self.assertEqual(exprule['id'], retrule.id)
|
|
||||||
self.assertEqual(exprule['security_group_id'],
|
|
||||||
retrule.parent_group_id)
|
|
||||||
self.assertEqual(exprule['direction'], retrule.direction)
|
|
||||||
self.assertEqual(exprule['ethertype'], retrule.ethertype)
|
|
||||||
self.assertEqual(exprule['port_range_min'], retrule.from_port)
|
|
||||||
self.assertEqual(exprule['port_range_max'], retrule.to_port)
|
|
||||||
if (exprule['remote_ip_prefix'] is None and
|
|
||||||
exprule['remote_group_id'] is None):
|
|
||||||
expcidr = ('::/0' if exprule['ethertype'] == 'IPv6'
|
|
||||||
else '0.0.0.0/0')
|
|
||||||
else:
|
|
||||||
expcidr = exprule['remote_ip_prefix']
|
|
||||||
self.assertEqual(expcidr, retrule.ip_range.get('cidr'))
|
|
||||||
self.assertEqual(self.sg_dict.get(exprule['remote_group_id']),
|
|
||||||
retrule.group.get('name'))
|
|
||||||
|
|
||||||
def _cmp_sg(self, exp_sg, ret_sg):
|
|
||||||
self.assertEqual(exp_sg['id'], ret_sg.id)
|
|
||||||
self.assertEqual(exp_sg['name'], ret_sg.name)
|
|
||||||
exp_rules = exp_sg['security_group_rules']
|
|
||||||
self.assertEqual(len(exp_rules), len(ret_sg.rules))
|
|
||||||
for (exprule, retrule) in itertools.izip(exp_rules, ret_sg.rules):
|
|
||||||
self._cmp_sg_rule(exprule, retrule)
|
|
||||||
|
|
||||||
def test_security_group_list(self):
|
|
||||||
sgs = self.api_q_secgroups.list()
|
|
||||||
tenant_id = self.request.user.tenant_id
|
|
||||||
# use deepcopy to ensure self.api_q_secgroups is not modified.
|
|
||||||
self.qclient.list_security_groups(tenant_id=tenant_id) \
|
|
||||||
.AndReturn({'security_groups': copy.deepcopy(sgs)})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
rets = api.network.security_group_list(self.request)
|
|
||||||
self.assertEqual(len(sgs), len(rets))
|
|
||||||
for (exp, ret) in itertools.izip(sgs, rets):
|
|
||||||
self._cmp_sg(exp, ret)
|
|
||||||
|
|
||||||
def test_security_group_get(self):
|
|
||||||
secgroup = self.api_q_secgroups.first()
|
|
||||||
sg_ids = set([secgroup['id']] +
|
|
||||||
[rule['remote_group_id'] for rule
|
|
||||||
in secgroup['security_group_rules']
|
|
||||||
if rule['remote_group_id']])
|
|
||||||
related_sgs = [sg for sg in self.api_q_secgroups.list()
|
|
||||||
if sg['id'] in sg_ids]
|
|
||||||
# use deepcopy to ensure self.api_q_secgroups is not modified.
|
|
||||||
self.qclient.show_security_group(secgroup['id']) \
|
|
||||||
.AndReturn({'security_group': copy.deepcopy(secgroup)})
|
|
||||||
self.qclient.list_security_groups(id=sg_ids, fields=['id', 'name']) \
|
|
||||||
.AndReturn({'security_groups': related_sgs})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
ret = api.network.security_group_get(self.request, secgroup['id'])
|
|
||||||
self._cmp_sg(secgroup, ret)
|
|
||||||
|
|
||||||
def test_security_group_create(self):
|
|
||||||
secgroup = self.api_q_secgroups.list()[1]
|
|
||||||
body = {'security_group':
|
|
||||||
{'name': secgroup['name'],
|
|
||||||
'description': secgroup['description']}}
|
|
||||||
self.qclient.create_security_group(body) \
|
|
||||||
.AndReturn({'security_group': copy.deepcopy(secgroup)})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
ret = api.network.security_group_create(self.request, secgroup['name'],
|
|
||||||
secgroup['description'])
|
|
||||||
self._cmp_sg(secgroup, ret)
|
|
||||||
|
|
||||||
def test_security_group_delete(self):
|
|
||||||
secgroup = self.api_q_secgroups.first()
|
|
||||||
self.qclient.delete_security_group(secgroup['id'])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
api.network.security_group_delete(self.request, secgroup['id'])
|
|
||||||
|
|
||||||
def test_security_group_rule_create(self):
|
|
||||||
sg_rule = [r for r in self.api_q_secgroup_rules.list()
|
|
||||||
if r['protocol'] == 'tcp' and r['remote_ip_prefix']][0]
|
|
||||||
sg_id = sg_rule['security_group_id']
|
|
||||||
secgroup = [sg for sg in self.api_q_secgroups.list()
|
|
||||||
if sg['id'] == sg_id][0]
|
|
||||||
|
|
||||||
post_rule = copy.deepcopy(sg_rule)
|
|
||||||
del post_rule['id']
|
|
||||||
del post_rule['tenant_id']
|
|
||||||
post_body = {'security_group_rule': post_rule}
|
|
||||||
self.qclient.create_security_group_rule(post_body) \
|
|
||||||
.AndReturn({'security_group_rule': copy.deepcopy(sg_rule)})
|
|
||||||
self.qclient.list_security_groups(id=set([sg_id]),
|
|
||||||
fields=['id', 'name']) \
|
|
||||||
.AndReturn({'security_groups': [copy.deepcopy(secgroup)]})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.security_group_rule_create(
|
|
||||||
self.request, sg_rule['security_group_id'],
|
|
||||||
sg_rule['direction'], sg_rule['ethertype'], sg_rule['protocol'],
|
|
||||||
sg_rule['port_range_min'], sg_rule['port_range_max'],
|
|
||||||
sg_rule['remote_ip_prefix'], sg_rule['remote_group_id'])
|
|
||||||
self._cmp_sg_rule(sg_rule, ret)
|
|
||||||
|
|
||||||
def test_security_group_rule_delete(self):
|
|
||||||
sg_rule = self.api_q_secgroup_rules.first()
|
|
||||||
self.qclient.delete_security_group_rule(sg_rule['id'])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
api.network.security_group_rule_delete(self.request, sg_rule['id'])
|
|
||||||
|
|
||||||
def _get_instance(self, cur_sg_ids):
|
|
||||||
instance_port = [p for p in self.api_ports.list()
|
|
||||||
if p['device_owner'].startswith('compute:')][0]
|
|
||||||
instance_id = instance_port['device_id']
|
|
||||||
# Emulate an intance with two ports
|
|
||||||
instance_ports = []
|
|
||||||
for _i in range(2):
|
|
||||||
p = copy.deepcopy(instance_port)
|
|
||||||
p['id'] = str(uuid.uuid4())
|
|
||||||
p['security_groups'] = cur_sg_ids
|
|
||||||
instance_ports.append(p)
|
|
||||||
return (instance_id, instance_ports)
|
|
||||||
|
|
||||||
def test_server_security_groups(self):
|
|
||||||
cur_sg_ids = [sg['id'] for sg in self.api_q_secgroups.list()[:2]]
|
|
||||||
instance_id, instance_ports = self._get_instance(cur_sg_ids)
|
|
||||||
|
|
||||||
self.qclient.list_ports(device_id=instance_id) \
|
|
||||||
.AndReturn({'ports': instance_ports})
|
|
||||||
secgroups = copy.deepcopy(self.api_q_secgroups.list())
|
|
||||||
self.qclient.list_security_groups(id=set(cur_sg_ids)) \
|
|
||||||
.AndReturn({'security_groups': secgroups})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.server_security_groups(self.request, instance_id)
|
|
||||||
|
|
||||||
def test_server_update_security_groups(self):
|
|
||||||
cur_sg_ids = [self.api_q_secgroups.first()['id']]
|
|
||||||
new_sg_ids = [sg['id'] for sg in self.api_q_secgroups.list()[:2]]
|
|
||||||
instance_id, instance_ports = self._get_instance(cur_sg_ids)
|
|
||||||
|
|
||||||
self.qclient.list_ports(device_id=instance_id) \
|
|
||||||
.AndReturn({'ports': instance_ports})
|
|
||||||
for p in instance_ports:
|
|
||||||
body = {'port': {'security_groups': new_sg_ids}}
|
|
||||||
self.qclient.update_port(p['id'], body=body).AndReturn({'port': p})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
api.network.server_update_security_groups(
|
|
||||||
self.request, instance_id, new_sg_ids)
|
|
||||||
|
|
||||||
def test_security_group_backend(self):
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
self.assertEqual(api.network.security_group_backend(self.request),
|
|
||||||
'neutron')
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkApiNeutronFloatingIpTests(NetworkApiNeutronTestBase):
|
|
||||||
def test_floating_ip_pools_list(self):
|
|
||||||
search_opts = {'router:external': True}
|
|
||||||
ext_nets = [n for n in self.api_networks.list()
|
|
||||||
if n['router:external']]
|
|
||||||
self.qclient.list_networks(**search_opts) \
|
|
||||||
.AndReturn({'networks': ext_nets})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
rets = api.network.floating_ip_pools_list(self.request)
|
|
||||||
for attr in ['id', 'name']:
|
|
||||||
self.assertEqual([p.__getattr__(attr) for p in rets],
|
|
||||||
[p[attr] for p in ext_nets])
|
|
||||||
|
|
||||||
def test_floating_ip_list(self):
|
|
||||||
fips = self.api_q_floating_ips.list()
|
|
||||||
self.qclient.list_floatingips().AndReturn({'floatingips': fips})
|
|
||||||
self.qclient.list_ports().AndReturn({'ports': self.api_ports.list()})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
rets = api.network.tenant_floating_ip_list(self.request)
|
|
||||||
assoc_port = self.api_ports.list()[1]
|
|
||||||
self.assertEqual(len(fips), len(rets))
|
|
||||||
for ret, exp in zip(rets, fips):
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'port_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), exp[attr])
|
|
||||||
if exp['port_id']:
|
|
||||||
dev_id = assoc_port['device_id'] if exp['port_id'] else None
|
|
||||||
self.assertEqual(ret.instance_id, dev_id)
|
|
||||||
|
|
||||||
def test_floating_ip_get_associated(self):
|
|
||||||
fip = self.api_q_floating_ips.list()[1]
|
|
||||||
assoc_port = self.api_ports.list()[1]
|
|
||||||
self.qclient.show_floatingip(fip['id']).AndReturn({'floatingip': fip})
|
|
||||||
self.qclient.show_port(assoc_port['id']) \
|
|
||||||
.AndReturn({'port': assoc_port})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_get(self.request, fip['id'])
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'port_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), fip[attr])
|
|
||||||
self.assertEqual(ret.instance_id, assoc_port['device_id'])
|
|
||||||
|
|
||||||
def test_floating_ip_get_unassociated(self):
|
|
||||||
fip = self.api_q_floating_ips.list()[0]
|
|
||||||
self.qclient.show_floatingip(fip['id']).AndReturn({'floatingip': fip})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_get(self.request, fip['id'])
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'port_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), fip[attr])
|
|
||||||
self.assertEqual(ret.instance_id, None)
|
|
||||||
|
|
||||||
def test_floating_ip_allocate(self):
|
|
||||||
ext_nets = [n for n in self.api_networks.list()
|
|
||||||
if n['router:external']]
|
|
||||||
ext_net = ext_nets[0]
|
|
||||||
fip = self.api_q_floating_ips.first()
|
|
||||||
self.qclient.create_floatingip(
|
|
||||||
{'floatingip': {'floating_network_id': ext_net['id']}}) \
|
|
||||||
.AndReturn({'floatingip': fip})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.tenant_floating_ip_allocate(self.request,
|
|
||||||
ext_net['id'])
|
|
||||||
for attr in ['id', 'ip', 'pool', 'fixed_ip', 'port_id']:
|
|
||||||
self.assertEqual(ret.__getattr__(attr), fip[attr])
|
|
||||||
self.assertEqual(ret.instance_id, None)
|
|
||||||
|
|
||||||
def test_floating_ip_release(self):
|
|
||||||
fip = self.api_q_floating_ips.first()
|
|
||||||
self.qclient.delete_floatingip(fip['id'])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.tenant_floating_ip_release(self.request, fip['id'])
|
|
||||||
|
|
||||||
def test_floating_ip_associate(self):
|
|
||||||
fip = self.api_q_floating_ips.list()[1]
|
|
||||||
assoc_port = self.api_ports.list()[1]
|
|
||||||
ip_address = assoc_port['fixed_ips'][0]['ip_address']
|
|
||||||
target_id = '%s_%s' % (assoc_port['id'], ip_address)
|
|
||||||
params = {'port_id': assoc_port['id'],
|
|
||||||
'fixed_ip_address': ip_address}
|
|
||||||
self.qclient.update_floatingip(fip['id'],
|
|
||||||
{'floatingip': params})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.floating_ip_associate(self.request, fip['id'], target_id)
|
|
||||||
|
|
||||||
def test_floating_ip_disassociate(self):
|
|
||||||
fip = self.api_q_floating_ips.list()[1]
|
|
||||||
assoc_port = self.api_ports.list()[1]
|
|
||||||
ip_address = assoc_port['fixed_ips'][0]['ip_address']
|
|
||||||
target_id = '%s_%s' % (assoc_port['id'], ip_address)
|
|
||||||
self.qclient.update_floatingip(fip['id'],
|
|
||||||
{'floatingip': {'port_id': None}})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.network.floating_ip_disassociate(self.request, fip['id'],
|
|
||||||
target_id)
|
|
||||||
|
|
||||||
def _get_target_id(self, port):
|
|
||||||
param = {'id': port['id'],
|
|
||||||
'addr': port['fixed_ips'][0]['ip_address']}
|
|
||||||
return '%(id)s_%(addr)s' % param
|
|
||||||
|
|
||||||
def _get_target_name(self, port):
|
|
||||||
param = {'svrid': port['device_id'],
|
|
||||||
'addr': port['fixed_ips'][0]['ip_address']}
|
|
||||||
return 'server_%(svrid)s: %(addr)s' % param
|
|
||||||
|
|
||||||
def test_floating_ip_target_list(self):
|
|
||||||
ports = self.api_ports.list()
|
|
||||||
target_ports = [(self._get_target_id(p),
|
|
||||||
self._get_target_name(p)) for p in ports
|
|
||||||
if not p['device_owner'].startswith('network:')]
|
|
||||||
|
|
||||||
self.qclient.list_ports().AndReturn({'ports': ports})
|
|
||||||
servers = self.servers.list()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
search_opts = {'project_id': self.request.user.tenant_id}
|
|
||||||
novaclient.servers.list(True, search_opts).AndReturn(servers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
rets = api.network.floating_ip_target_list(self.request)
|
|
||||||
self.assertEqual(len(rets), len(target_ports))
|
|
||||||
for ret, exp in zip(rets, target_ports):
|
|
||||||
self.assertEqual(ret.id, exp[0])
|
|
||||||
self.assertEqual(ret.name, exp[1])
|
|
||||||
|
|
||||||
def test_floating_ip_target_get_by_instance(self):
|
|
||||||
ports = self.api_ports.list()
|
|
||||||
candidates = [p for p in ports if p['device_id'] == '1']
|
|
||||||
search_opts = {'device_id': '1'}
|
|
||||||
self.qclient.list_ports(**search_opts).AndReturn({'ports': candidates})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret = api.network.floating_ip_target_get_by_instance(self.request, '1')
|
|
||||||
self.assertEqual(ret, self._get_target_id(candidates[0]))
|
|
@ -1,272 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 NEC Corporation
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class NeutronApiTests(test.APITestCase):
|
|
||||||
def test_network_list(self):
|
|
||||||
networks = {'networks': self.api_networks.list()}
|
|
||||||
subnets = {'subnets': self.api_subnets.list()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.list_networks().AndReturn(networks)
|
|
||||||
neutronclient.list_subnets().AndReturn(subnets)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.network_list(self.request)
|
|
||||||
for n in ret_val:
|
|
||||||
self.assertIsInstance(n, api.neutron.Network)
|
|
||||||
|
|
||||||
def test_network_get(self):
|
|
||||||
network = {'network': self.api_networks.first()}
|
|
||||||
subnet = {'subnet': self.api_subnets.first()}
|
|
||||||
network_id = self.api_networks.first()['id']
|
|
||||||
subnet_id = self.api_networks.first()['subnets'][0]
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.show_network(network_id).AndReturn(network)
|
|
||||||
neutronclient.show_subnet(subnet_id).AndReturn(subnet)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.network_get(self.request, network_id)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Network)
|
|
||||||
|
|
||||||
def test_network_create(self):
|
|
||||||
network = {'network': self.api_networks.first()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
form_data = {'network': {'name': 'net1'}}
|
|
||||||
neutronclient.create_network(body=form_data).AndReturn(network)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.network_create(self.request, name='net1')
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Network)
|
|
||||||
|
|
||||||
def test_network_modify(self):
|
|
||||||
network = {'network': self.api_networks.first()}
|
|
||||||
network_id = self.api_networks.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
form_data = {'network': {'name': 'net1'}}
|
|
||||||
neutronclient.update_network(network_id, body=form_data)\
|
|
||||||
.AndReturn(network)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.network_modify(self.request, network_id,
|
|
||||||
name='net1')
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Network)
|
|
||||||
|
|
||||||
def test_network_delete(self):
|
|
||||||
network_id = self.api_networks.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.delete_network(network_id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.network_delete(self.request, network_id)
|
|
||||||
|
|
||||||
def test_subnet_list(self):
|
|
||||||
subnets = {'subnets': self.api_subnets.list()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.list_subnets().AndReturn(subnets)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.subnet_list(self.request)
|
|
||||||
for n in ret_val:
|
|
||||||
self.assertIsInstance(n, api.neutron.Subnet)
|
|
||||||
|
|
||||||
def test_subnet_get(self):
|
|
||||||
subnet = {'subnet': self.api_subnets.first()}
|
|
||||||
subnet_id = self.api_subnets.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.show_subnet(subnet_id).AndReturn(subnet)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.subnet_get(self.request, subnet_id)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Subnet)
|
|
||||||
|
|
||||||
def test_subnet_create(self):
|
|
||||||
subnet_data = self.api_subnets.first()
|
|
||||||
params = {'network_id': subnet_data['network_id'],
|
|
||||||
'tenant_id': subnet_data['tenant_id'],
|
|
||||||
'name': subnet_data['name'],
|
|
||||||
'cidr': subnet_data['cidr'],
|
|
||||||
'ip_version': subnet_data['ip_version'],
|
|
||||||
'gateway_ip': subnet_data['gateway_ip']}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.create_subnet(body={'subnet': params})\
|
|
||||||
.AndReturn({'subnet': subnet_data})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.subnet_create(self.request, **params)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Subnet)
|
|
||||||
|
|
||||||
def test_subnet_modify(self):
|
|
||||||
subnet_data = self.api_subnets.first()
|
|
||||||
subnet_id = subnet_data['id']
|
|
||||||
params = {'name': subnet_data['name'],
|
|
||||||
'gateway_ip': subnet_data['gateway_ip']}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.update_subnet(subnet_id, body={'subnet': params})\
|
|
||||||
.AndReturn({'subnet': subnet_data})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.subnet_modify(self.request, subnet_id, **params)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Subnet)
|
|
||||||
|
|
||||||
def test_subnet_delete(self):
|
|
||||||
subnet_id = self.api_subnets.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.delete_subnet(subnet_id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.subnet_delete(self.request, subnet_id)
|
|
||||||
|
|
||||||
def test_port_list(self):
|
|
||||||
ports = {'ports': self.api_ports.list()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.list_ports().AndReturn(ports)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.port_list(self.request)
|
|
||||||
for p in ret_val:
|
|
||||||
self.assertIsInstance(p, api.neutron.Port)
|
|
||||||
|
|
||||||
def test_port_get(self):
|
|
||||||
port = {'port': self.api_ports.first()}
|
|
||||||
port_id = self.api_ports.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.show_port(port_id).AndReturn(port)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.port_get(self.request, port_id)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Port)
|
|
||||||
|
|
||||||
def test_port_create(self):
|
|
||||||
port_data = self.api_ports.first()
|
|
||||||
params = {'network_id': port_data['network_id'],
|
|
||||||
'tenant_id': port_data['tenant_id'],
|
|
||||||
'name': port_data['name'],
|
|
||||||
'device_id': port_data['device_id']}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.create_port(body={'port': params})\
|
|
||||||
.AndReturn({'port': port_data})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.port_create(self.request, **params)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Port)
|
|
||||||
self.assertEqual(ret_val.id, api.neutron.Port(port_data).id)
|
|
||||||
|
|
||||||
def test_port_modify(self):
|
|
||||||
port_data = self.api_ports.first()
|
|
||||||
port_id = port_data['id']
|
|
||||||
params = {'name': port_data['name'],
|
|
||||||
'device_id': port_data['device_id']}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.update_port(port_id, body={'port': params})\
|
|
||||||
.AndReturn({'port': port_data})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.port_modify(self.request, port_id, **params)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Port)
|
|
||||||
self.assertEqual(ret_val.id, api.neutron.Port(port_data).id)
|
|
||||||
|
|
||||||
def test_port_delete(self):
|
|
||||||
port_id = self.api_ports.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.delete_port(port_id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.port_delete(self.request, port_id)
|
|
||||||
|
|
||||||
def test_router_list(self):
|
|
||||||
routers = {'routers': self.api_routers.list()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.list_routers().AndReturn(routers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.router_list(self.request)
|
|
||||||
for n in ret_val:
|
|
||||||
self.assertIsInstance(n, api.neutron.Router)
|
|
||||||
|
|
||||||
def test_router_get(self):
|
|
||||||
router = {'router': self.api_routers.first()}
|
|
||||||
router_id = self.api_routers.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.show_router(router_id).AndReturn(router)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.router_get(self.request, router_id)
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Router)
|
|
||||||
|
|
||||||
def test_router_create(self):
|
|
||||||
router = {'router': self.api_routers.first()}
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
form_data = {'router': {'name': 'router1'}}
|
|
||||||
neutronclient.create_router(body=form_data).AndReturn(router)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.neutron.router_create(self.request, name='router1')
|
|
||||||
self.assertIsInstance(ret_val, api.neutron.Router)
|
|
||||||
|
|
||||||
def test_router_delete(self):
|
|
||||||
router_id = self.api_routers.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.delete_router(router_id)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.router_delete(self.request, router_id)
|
|
||||||
|
|
||||||
def test_router_add_interface(self):
|
|
||||||
subnet_id = self.api_subnets.first()['id']
|
|
||||||
router_id = self.api_routers.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
form_data = {'subnet_id': subnet_id}
|
|
||||||
neutronclient.add_interface_router(
|
|
||||||
router_id, form_data).AndReturn(None)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.router_add_interface(
|
|
||||||
self.request, router_id, subnet_id=subnet_id)
|
|
||||||
|
|
||||||
def test_router_remove_interface(self):
|
|
||||||
router_id = self.api_routers.first()['id']
|
|
||||||
fake_port = self.api_ports.first()['id']
|
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
|
||||||
neutronclient.remove_interface_router(
|
|
||||||
router_id, {'port_id': fake_port})
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.neutron.router_remove_interface(
|
|
||||||
self.request, router_id, port_id=fake_port)
|
|
@ -1,220 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, Inc.
|
|
||||||
# Copyright (c) 2012 X.commerce, a business unit of eBay 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.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django import http
|
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from mox import IsA
|
|
||||||
from novaclient.v1_1 import servers
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class ServerWrapperTests(test.TestCase):
|
|
||||||
|
|
||||||
def test_get_base_attribute(self):
|
|
||||||
server = api.nova.Server(self.servers.first(), self.request)
|
|
||||||
self.assertEqual(server.id, self.servers.first().id)
|
|
||||||
|
|
||||||
def test_image_name(self):
|
|
||||||
image = self.images.first()
|
|
||||||
self.mox.StubOutWithMock(api.glance, 'image_get')
|
|
||||||
api.glance.image_get(IsA(http.HttpRequest),
|
|
||||||
image.id).AndReturn(image)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
server = api.nova.Server(self.servers.first(), self.request)
|
|
||||||
self.assertEqual(server.image_name, image.name)
|
|
||||||
|
|
||||||
|
|
||||||
class ComputeApiTests(test.APITestCase):
|
|
||||||
|
|
||||||
def test_server_reboot(self):
|
|
||||||
server = self.servers.first()
|
|
||||||
HARDNESS = servers.REBOOT_HARD
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get(server.id).AndReturn(server)
|
|
||||||
novaclient.servers.reboot(server.id, HARDNESS)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.server_reboot(self.request, server.id)
|
|
||||||
self.assertIsNone(ret_val)
|
|
||||||
|
|
||||||
def test_server_soft_reboot(self):
|
|
||||||
server = self.servers.first()
|
|
||||||
HARDNESS = servers.REBOOT_SOFT
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get(server.id).AndReturn(server)
|
|
||||||
novaclient.servers.reboot(server.id, HARDNESS)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.server_reboot(self.request, server.id, HARDNESS)
|
|
||||||
self.assertIsNone(ret_val)
|
|
||||||
|
|
||||||
def test_server_vnc_console(self):
|
|
||||||
server = self.servers.first()
|
|
||||||
console = self.servers.vnc_console_data
|
|
||||||
console_type = console["console"]["type"]
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get_vnc_console(server.id,
|
|
||||||
console_type).AndReturn(console)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.server_vnc_console(self.request,
|
|
||||||
server.id,
|
|
||||||
console_type)
|
|
||||||
self.assertIsInstance(ret_val, api.nova.VNCConsole)
|
|
||||||
|
|
||||||
def test_server_spice_console(self):
|
|
||||||
server = self.servers.first()
|
|
||||||
console = self.servers.spice_console_data
|
|
||||||
console_type = console["console"]["type"]
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get_spice_console(server.id,
|
|
||||||
console_type).AndReturn(console)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.server_spice_console(self.request,
|
|
||||||
server.id,
|
|
||||||
console_type)
|
|
||||||
self.assertIsInstance(ret_val, api.nova.SPICEConsole)
|
|
||||||
|
|
||||||
def test_server_list(self):
|
|
||||||
servers = self.servers.list()
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.list(True, {'all_tenants': True}).AndReturn(servers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val, has_more = api.nova.server_list(self.request,
|
|
||||||
all_tenants=True)
|
|
||||||
for server in ret_val:
|
|
||||||
self.assertIsInstance(server, api.nova.Server)
|
|
||||||
|
|
||||||
def test_server_list_pagination(self):
|
|
||||||
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20)
|
|
||||||
servers = self.servers.list()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.list(True,
|
|
||||||
{'all_tenants': True,
|
|
||||||
'marker': None,
|
|
||||||
'limit': page_size + 1}).AndReturn(servers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val, has_more = api.nova.server_list(self.request,
|
|
||||||
{'marker': None,
|
|
||||||
'paginate': True},
|
|
||||||
all_tenants=True)
|
|
||||||
for server in ret_val:
|
|
||||||
self.assertIsInstance(server, api.nova.Server)
|
|
||||||
self.assertFalse(has_more)
|
|
||||||
|
|
||||||
@override_settings(API_RESULT_PAGE_SIZE=1)
|
|
||||||
def test_server_list_pagination_more(self):
|
|
||||||
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 1)
|
|
||||||
servers = self.servers.list()
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.list(True,
|
|
||||||
{'all_tenants': True,
|
|
||||||
'marker': None,
|
|
||||||
'limit': page_size + 1}) \
|
|
||||||
.AndReturn(servers[:page_size + 1])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val, has_more = api.nova.server_list(self.request,
|
|
||||||
{'marker': None,
|
|
||||||
'paginate': True},
|
|
||||||
all_tenants=True)
|
|
||||||
for server in ret_val:
|
|
||||||
self.assertIsInstance(server, api.nova.Server)
|
|
||||||
self.assertEquals(page_size, len(ret_val))
|
|
||||||
self.assertTrue(has_more)
|
|
||||||
|
|
||||||
def test_usage_get(self):
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.usage = self.mox.CreateMockAnything()
|
|
||||||
novaclient.usage.get(self.tenant.id,
|
|
||||||
'start',
|
|
||||||
'end').AndReturn(self.usages.first())
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.usage_get(self.request, self.tenant.id,
|
|
||||||
'start', 'end')
|
|
||||||
self.assertIsInstance(ret_val, api.nova.NovaUsage)
|
|
||||||
|
|
||||||
def test_usage_list(self):
|
|
||||||
usages = self.usages.list()
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.usage = self.mox.CreateMockAnything()
|
|
||||||
novaclient.usage.list('start', 'end', True).AndReturn(usages)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.usage_list(self.request, 'start', 'end')
|
|
||||||
for usage in ret_val:
|
|
||||||
self.assertIsInstance(usage, api.nova.NovaUsage)
|
|
||||||
|
|
||||||
def test_server_get(self):
|
|
||||||
server = self.servers.first()
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.servers = self.mox.CreateMockAnything()
|
|
||||||
novaclient.servers.get(server.id).AndReturn(server)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.server_get(self.request, server.id)
|
|
||||||
self.assertIsInstance(ret_val, api.nova.Server)
|
|
||||||
|
|
||||||
def test_absolute_limits_handle_unlimited(self):
|
|
||||||
values = {"maxTotalCores": -1, "maxTotalInstances": 10}
|
|
||||||
limits = self.mox.CreateMockAnything()
|
|
||||||
limits.absolute = []
|
|
||||||
for key, val in values.iteritems():
|
|
||||||
limit = self.mox.CreateMockAnything()
|
|
||||||
limit.name = key
|
|
||||||
limit.value = val
|
|
||||||
limits.absolute.append(limit)
|
|
||||||
|
|
||||||
novaclient = self.stub_novaclient()
|
|
||||||
novaclient.limits = self.mox.CreateMockAnything()
|
|
||||||
novaclient.limits.get(reserved=True).AndReturn(limits)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
ret_val = api.nova.tenant_absolute_limits(self.request, reserved=True)
|
|
||||||
expected_results = {"maxTotalCores": float("inf"),
|
|
||||||
"maxTotalInstances": 10}
|
|
||||||
for key in expected_results.keys():
|
|
||||||
self.assertEquals(ret_val[key], expected_results[key])
|
|
@ -1,122 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from mox import IsA
|
|
||||||
|
|
||||||
from horizon import exceptions
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class SwiftApiTests(test.APITestCase):
|
|
||||||
def test_swift_get_containers(self):
|
|
||||||
containers = self.containers.list()
|
|
||||||
cont_data = [c._apidict for c in containers]
|
|
||||||
swift_api = self.stub_swiftclient()
|
|
||||||
swift_api.get_account(limit=1001,
|
|
||||||
marker=None,
|
|
||||||
full_listing=True).AndReturn([{}, cont_data])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
(conts, more) = api.swift.swift_get_containers(self.request)
|
|
||||||
self.assertEqual(len(conts), len(containers))
|
|
||||||
self.assertFalse(more)
|
|
||||||
|
|
||||||
def test_swift_create_duplicate_container(self):
|
|
||||||
container = self.containers.first()
|
|
||||||
swift_api = self.stub_swiftclient(expected_calls=2)
|
|
||||||
# Check for existence, then create
|
|
||||||
exc = self.exceptions.swift
|
|
||||||
swift_api.head_container(container.name).AndRaise(exc)
|
|
||||||
swift_api.put_container(container.name).AndReturn(container)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
# Verification handled by mox, no assertions needed.
|
|
||||||
api.swift.swift_create_container(self.request, container.name)
|
|
||||||
|
|
||||||
def test_swift_create_container(self):
|
|
||||||
container = self.containers.first()
|
|
||||||
swift_api = self.stub_swiftclient()
|
|
||||||
swift_api.head_container(container.name).AndReturn(container)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
# Verification handled by mox, no assertions needed.
|
|
||||||
with self.assertRaises(exceptions.AlreadyExists):
|
|
||||||
api.swift.swift_create_container(self.request, container.name)
|
|
||||||
|
|
||||||
def test_swift_get_objects(self):
|
|
||||||
container = self.containers.first()
|
|
||||||
objects = self.objects.list()
|
|
||||||
|
|
||||||
swift_api = self.stub_swiftclient()
|
|
||||||
swift_api.get_container(container.name,
|
|
||||||
limit=1001,
|
|
||||||
marker=None,
|
|
||||||
prefix=None,
|
|
||||||
delimiter='/',
|
|
||||||
full_listing=True).AndReturn([{}, objects])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
(objs, more) = api.swift.swift_get_objects(self.request,
|
|
||||||
container.name)
|
|
||||||
self.assertEqual(len(objs), len(objects))
|
|
||||||
self.assertFalse(more)
|
|
||||||
|
|
||||||
def test_swift_upload_object(self):
|
|
||||||
container = self.containers.first()
|
|
||||||
obj = self.objects.first()
|
|
||||||
fake_name = 'fake_object.jpg'
|
|
||||||
|
|
||||||
class FakeFile(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.name = fake_name
|
|
||||||
self.data = obj.data
|
|
||||||
self.size = len(obj.data)
|
|
||||||
|
|
||||||
headers = {'X-Object-Meta-Orig-Filename': fake_name}
|
|
||||||
|
|
||||||
swift_api = self.stub_swiftclient()
|
|
||||||
swift_api.put_object(container.name,
|
|
||||||
obj.name,
|
|
||||||
IsA(FakeFile),
|
|
||||||
headers=headers)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
api.swift.swift_upload_object(self.request,
|
|
||||||
container.name,
|
|
||||||
obj.name,
|
|
||||||
FakeFile())
|
|
||||||
|
|
||||||
def test_swift_object_exists(self):
|
|
||||||
container = self.containers.first()
|
|
||||||
obj = self.objects.first()
|
|
||||||
|
|
||||||
swift_api = self.stub_swiftclient(expected_calls=2)
|
|
||||||
swift_api.head_object(container.name, obj.name).AndReturn(container)
|
|
||||||
|
|
||||||
exc = self.exceptions.swift
|
|
||||||
swift_api.head_object(container.name, obj.name).AndRaise(exc)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
args = self.request, container.name, obj.name
|
|
||||||
self.assertTrue(api.swift.swift_object_exists(*args))
|
|
||||||
# Again, for a "non-existent" object
|
|
||||||
self.assertFalse(api.swift.swift_object_exists(*args))
|
|
@ -1,7 +0,0 @@
|
|||||||
from django.conf.urls import patterns
|
|
||||||
|
|
||||||
from openstack_dashboard.urls import urlpatterns
|
|
||||||
|
|
||||||
urlpatterns += patterns('',
|
|
||||||
(r'^500/$', 'django.views.defaults.server_error')
|
|
||||||
)
|
|
@ -18,39 +18,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.middleware import AuthenticationMiddleware
|
|
||||||
from django.contrib.messages.storage import default_storage
|
|
||||||
from django.core.handlers import wsgi
|
from django.core.handlers import wsgi
|
||||||
from django import http
|
|
||||||
from django.test.client import RequestFactory
|
|
||||||
from django.utils.importlib import import_module
|
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
|
||||||
from cinderclient import client as cinder_client
|
|
||||||
import glanceclient
|
|
||||||
from heatclient import client as heat_client
|
|
||||||
from keystoneclient.v2_0 import client as keystone_client
|
|
||||||
from neutronclient.v2_0 import client as neutron_client
|
|
||||||
from novaclient.v1_1 import client as nova_client
|
|
||||||
from novaclient.v1_1.contrib import baremetal
|
from novaclient.v1_1.contrib import baremetal
|
||||||
from swiftclient import client as swift_client
|
|
||||||
from tuskarclient.v1 import client as tuskar_client
|
from tuskarclient.v1 import client as tuskar_client
|
||||||
|
|
||||||
import httplib2
|
from openstack_dashboard.test import helpers as openstack_dashboard_helpers
|
||||||
import mox
|
|
||||||
|
|
||||||
from openstack_auth import user
|
|
||||||
from openstack_auth import utils
|
|
||||||
|
|
||||||
from horizon import middleware
|
|
||||||
from horizon.test import helpers as horizon_helpers
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard import context_processors
|
|
||||||
from tuskar_ui import api as tuskar_api
|
from tuskar_ui import api as tuskar_api
|
||||||
from tuskar_ui.test.test_data.utils import load_test_data
|
from tuskar_ui.test.test_data.utils import load_test_data
|
||||||
|
|
||||||
@ -60,47 +36,12 @@ wsgi.WSGIRequest.__repr__ = lambda self: "<class 'django.http.HttpRequest'>"
|
|||||||
|
|
||||||
|
|
||||||
def create_stubs(stubs_to_create={}):
|
def create_stubs(stubs_to_create={}):
|
||||||
if not isinstance(stubs_to_create, dict):
|
return openstack_dashboard_helpers.create_stubs(stubs_to_create)
|
||||||
raise TypeError("create_stub must be passed a dict, but a %s was "
|
|
||||||
"given." % type(stubs_to_create).__name__)
|
|
||||||
|
|
||||||
def inner_stub_out(fn):
|
|
||||||
@wraps(fn)
|
|
||||||
def instance_stub_out(self):
|
|
||||||
for key in stubs_to_create:
|
|
||||||
if not (isinstance(stubs_to_create[key], tuple) or
|
|
||||||
isinstance(stubs_to_create[key], list)):
|
|
||||||
raise TypeError("The values of the create_stub "
|
|
||||||
"dict must be lists or tuples, but "
|
|
||||||
"is a %s."
|
|
||||||
% type(stubs_to_create[key]).__name__)
|
|
||||||
|
|
||||||
for value in stubs_to_create[key]:
|
|
||||||
self.mox.StubOutWithMock(key, value)
|
|
||||||
return fn(self)
|
|
||||||
return instance_stub_out
|
|
||||||
return inner_stub_out
|
|
||||||
|
|
||||||
|
|
||||||
class RequestFactoryWithMessages(RequestFactory):
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
req = super(RequestFactoryWithMessages, self).get(*args, **kwargs)
|
|
||||||
req.user = utils.get_user(req)
|
|
||||||
req.session = []
|
|
||||||
req._messages = default_storage(req)
|
|
||||||
return req
|
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
|
||||||
req = super(RequestFactoryWithMessages, self).post(*args, **kwargs)
|
|
||||||
req.user = utils.get_user(req)
|
|
||||||
req.session = []
|
|
||||||
req._messages = default_storage(req)
|
|
||||||
return req
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
||||||
"The SKIP_UNITTESTS env variable is set.")
|
"The SKIP_UNITTESTS env variable is set.")
|
||||||
class TestCase(horizon_helpers.TestCase):
|
class TestCase(openstack_dashboard_helpers.TestCase):
|
||||||
"""
|
"""
|
||||||
Specialized base test case class for Horizon which gives access to
|
Specialized base test case class for Horizon which gives access to
|
||||||
numerous additional features:
|
numerous additional features:
|
||||||
@ -118,129 +59,25 @@ class TestCase(horizon_helpers.TestCase):
|
|||||||
* Several handy additional assertion methods.
|
* Several handy additional assertion methods.
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super(TestCase, self).setUp()
|
||||||
|
|
||||||
|
# load tuskar-specific test data
|
||||||
load_test_data(self)
|
load_test_data(self)
|
||||||
self.mox = mox.Mox()
|
|
||||||
self.factory = RequestFactoryWithMessages()
|
|
||||||
self.context = {'authorized_tenants': self.tenants.list()}
|
|
||||||
|
|
||||||
def fake_conn_request(*args, **kwargs):
|
|
||||||
raise Exception("An external URI request tried to escape through "
|
|
||||||
"an httplib2 client. Args: %s, kwargs: %s"
|
|
||||||
% (args, kwargs))
|
|
||||||
|
|
||||||
self._real_conn_request = httplib2.Http._conn_request
|
|
||||||
httplib2.Http._conn_request = fake_conn_request
|
|
||||||
|
|
||||||
self._real_context_processor = context_processors.openstack
|
|
||||||
context_processors.openstack = lambda request: self.context
|
|
||||||
|
|
||||||
self._real_get_user = utils.get_user
|
|
||||||
tenants = self.context['authorized_tenants']
|
|
||||||
self.setActiveUser(id=self.user.id,
|
|
||||||
token=self.token,
|
|
||||||
username=self.user.name,
|
|
||||||
tenant_id=self.tenant.id,
|
|
||||||
service_catalog=self.service_catalog,
|
|
||||||
authorized_tenants=tenants)
|
|
||||||
self.request = http.HttpRequest()
|
|
||||||
self.request.session = self.client._session()
|
|
||||||
self.request.session['token'] = self.token.id
|
|
||||||
middleware.HorizonMiddleware().process_request(self.request)
|
|
||||||
AuthenticationMiddleware().process_request(self.request)
|
|
||||||
os.environ["HORIZON_TEST_RUN"] = "True"
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
httplib2.Http._conn_request = self._real_conn_request
|
|
||||||
context_processors.openstack = self._real_context_processor
|
|
||||||
utils.get_user = self._real_get_user
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
del os.environ["HORIZON_TEST_RUN"]
|
|
||||||
|
|
||||||
def setActiveUser(self, id=None, token=None, username=None, tenant_id=None,
|
|
||||||
service_catalog=None, tenant_name=None, roles=None,
|
|
||||||
authorized_tenants=None, enabled=True):
|
|
||||||
def get_user(request):
|
|
||||||
return user.User(id=id,
|
|
||||||
token=token,
|
|
||||||
user=username,
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
service_catalog=service_catalog,
|
|
||||||
roles=roles,
|
|
||||||
enabled=enabled,
|
|
||||||
authorized_tenants=authorized_tenants,
|
|
||||||
endpoint=settings.OPENSTACK_KEYSTONE_URL)
|
|
||||||
utils.get_user = get_user
|
|
||||||
|
|
||||||
def assertRedirectsNoFollow(self, response, expected_url):
|
|
||||||
"""
|
|
||||||
Asserts that the given response issued a 302 redirect without
|
|
||||||
processing the view which is redirected to.
|
|
||||||
"""
|
|
||||||
assert (response.status_code / 100 == 3), \
|
|
||||||
"The response did not return a redirect."
|
|
||||||
self.assertEqual(response._headers.get('location', None),
|
|
||||||
('Location', settings.TESTSERVER + expected_url))
|
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
|
|
||||||
def assertNoFormErrors(self, response, context_name="form"):
|
|
||||||
"""
|
|
||||||
Asserts that the response either does not contain a form in it's
|
|
||||||
context, or that if it does, that form has no errors.
|
|
||||||
"""
|
|
||||||
context = getattr(response, "context", {})
|
|
||||||
if not context or context_name not in context:
|
|
||||||
return True
|
|
||||||
errors = response.context[context_name]._errors
|
|
||||||
assert len(errors) == 0, \
|
|
||||||
"Unexpected errors were found on the form: %s" % errors
|
|
||||||
|
|
||||||
def assertFormErrors(self, response, count=0, message=None,
|
|
||||||
context_name="form"):
|
|
||||||
"""
|
|
||||||
Asserts that the response does contain a form in it's
|
|
||||||
context, and that form has errors, if count were given,
|
|
||||||
it must match the exact numbers of errors
|
|
||||||
"""
|
|
||||||
context = getattr(response, "context", {})
|
|
||||||
assert (context and context_name in context), \
|
|
||||||
"The response did not contain a form."
|
|
||||||
errors = response.context[context_name]._errors
|
|
||||||
if count:
|
|
||||||
assert len(errors) == count, \
|
|
||||||
"%d errors were found on the form, %d expected" % \
|
|
||||||
(len(errors), count)
|
|
||||||
if message and message not in unicode(errors):
|
|
||||||
self.fail("Expected message not found, instead found: %s"
|
|
||||||
% ["%s: %s" % (key, [e for e in field_errors]) for
|
|
||||||
(key, field_errors) in errors.items()])
|
|
||||||
else:
|
|
||||||
assert len(errors) > 0, "No errors were found on the form"
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAdminViewTests(TestCase):
|
class BaseAdminViewTests(openstack_dashboard_helpers.BaseAdminViewTests):
|
||||||
"""
|
"""
|
||||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||||
for testing admin-only views and functionality.
|
for testing admin-only views and functionality.
|
||||||
"""
|
"""
|
||||||
def setActiveUser(self, *args, **kwargs):
|
def setUp(self):
|
||||||
if "roles" not in kwargs:
|
super(BaseAdminViewTests, self).setUp()
|
||||||
kwargs['roles'] = [self.roles.admin._info]
|
|
||||||
super(BaseAdminViewTests, self).setActiveUser(*args, **kwargs)
|
|
||||||
|
|
||||||
def setSessionValues(self, **kwargs):
|
# load tuskar-specific test data
|
||||||
settings.SESSION_ENGINE = 'django.contrib.sessions.backends.file'
|
load_test_data(self)
|
||||||
engine = import_module(settings.SESSION_ENGINE)
|
|
||||||
store = engine.SessionStore()
|
|
||||||
for key in kwargs:
|
|
||||||
store[key] = kwargs[key]
|
|
||||||
self.request.session[key] = kwargs[key]
|
|
||||||
store.save()
|
|
||||||
self.session = store
|
|
||||||
self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key
|
|
||||||
|
|
||||||
|
|
||||||
class APITestCase(TestCase):
|
class APITestCase(openstack_dashboard_helpers.APITestCase):
|
||||||
"""
|
"""
|
||||||
The ``APITestCase`` class is for use with tests which deal with the
|
The ``APITestCase`` class is for use with tests which deal with the
|
||||||
underlying clients rather than stubbing out the
|
underlying clients rather than stubbing out the
|
||||||
@ -249,102 +86,23 @@ class APITestCase(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(APITestCase, self).setUp()
|
super(APITestCase, self).setUp()
|
||||||
|
|
||||||
def fake_keystoneclient(request, admin=False):
|
# load tuskar-specfic test data
|
||||||
"""
|
load_test_data(self)
|
||||||
Wrapper function which returns the stub keystoneclient. Only
|
|
||||||
necessary because the function takes too many arguments to
|
|
||||||
conveniently be a lambda.
|
|
||||||
"""
|
|
||||||
return self.stub_keystoneclient()
|
|
||||||
|
|
||||||
# Store the original clients
|
# Store the original clients
|
||||||
self._original_glanceclient = api.glance.glanceclient
|
|
||||||
self._original_keystoneclient = api.keystone.keystoneclient
|
|
||||||
self._original_novaclient = api.nova.novaclient
|
|
||||||
self._original_neutronclient = api.neutron.neutronclient
|
|
||||||
self._original_cinderclient = api.cinder.cinderclient
|
|
||||||
self._original_heatclient = api.heat.heatclient
|
|
||||||
self._original_tuskarclient = tuskar_api.tuskarclient
|
self._original_tuskarclient = tuskar_api.tuskarclient
|
||||||
self._original_baremetalclient = tuskar_api.baremetalclient
|
self._original_baremetalclient = tuskar_api.baremetalclient
|
||||||
|
|
||||||
# Replace the clients with our stubs.
|
# Replace the clients with our stubs.
|
||||||
api.glance.glanceclient = lambda request: self.stub_glanceclient()
|
|
||||||
api.keystone.keystoneclient = fake_keystoneclient
|
|
||||||
api.nova.novaclient = lambda request: self.stub_novaclient()
|
|
||||||
api.neutron.neutronclient = lambda request: self.stub_neutronclient()
|
|
||||||
api.cinder.cinderclient = lambda request: self.stub_cinderclient()
|
|
||||||
api.heat.heatclient = lambda request: self.stub_heatclient()
|
|
||||||
tuskar_api.tuskarclient = lambda request: self.stub_tuskarclient()
|
tuskar_api.tuskarclient = lambda request: self.stub_tuskarclient()
|
||||||
tuskar_api.baremetalclient = lambda request:\
|
tuskar_api.baremetalclient = lambda request:\
|
||||||
self.stub_baremetalclient()
|
self.stub_baremetalclient()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(APITestCase, self).tearDown()
|
super(APITestCase, self).tearDown()
|
||||||
api.glance.glanceclient = self._original_glanceclient
|
|
||||||
api.nova.novaclient = self._original_novaclient
|
|
||||||
api.keystone.keystoneclient = self._original_keystoneclient
|
|
||||||
api.neutron.neutronclient = self._original_neutronclient
|
|
||||||
api.cinder.cinderclient = self._original_cinderclient
|
|
||||||
api.heat.heatclient = self._original_heatclient
|
|
||||||
tuskar_api.tuskarclient = self._original_tuskarclient
|
tuskar_api.tuskarclient = self._original_tuskarclient
|
||||||
tuskar_api.baremetalclient = self._original_baremetalclient
|
tuskar_api.baremetalclient = self._original_baremetalclient
|
||||||
|
|
||||||
def stub_novaclient(self):
|
|
||||||
if not hasattr(self, "novaclient"):
|
|
||||||
self.mox.StubOutWithMock(nova_client, 'Client')
|
|
||||||
self.novaclient = self.mox.CreateMock(nova_client.Client)
|
|
||||||
return self.novaclient
|
|
||||||
|
|
||||||
def stub_cinderclient(self):
|
|
||||||
if not hasattr(self, "cinderclient"):
|
|
||||||
self.mox.StubOutWithMock(cinder_client, 'Client')
|
|
||||||
self.cinderclient = self.mox.CreateMock(cinder_client.Client)
|
|
||||||
return self.cinderclient
|
|
||||||
|
|
||||||
def stub_keystoneclient(self):
|
|
||||||
if not hasattr(self, "keystoneclient"):
|
|
||||||
self.mox.StubOutWithMock(keystone_client, 'Client')
|
|
||||||
# NOTE(saschpe): Mock properties, MockObject.__init__ ignores them:
|
|
||||||
keystone_client.Client.auth_token = 'foo'
|
|
||||||
keystone_client.Client.service_catalog = None
|
|
||||||
keystone_client.Client.tenant_id = '1'
|
|
||||||
keystone_client.Client.tenant_name = 'tenant_1'
|
|
||||||
self.keystoneclient = self.mox.CreateMock(keystone_client.Client)
|
|
||||||
return self.keystoneclient
|
|
||||||
|
|
||||||
def stub_glanceclient(self):
|
|
||||||
if not hasattr(self, "glanceclient"):
|
|
||||||
self.mox.StubOutWithMock(glanceclient, 'Client')
|
|
||||||
self.glanceclient = self.mox.CreateMock(glanceclient.Client)
|
|
||||||
return self.glanceclient
|
|
||||||
|
|
||||||
def stub_neutronclient(self):
|
|
||||||
if not hasattr(self, "neutronclient"):
|
|
||||||
self.mox.StubOutWithMock(neutron_client, 'Client')
|
|
||||||
self.neutronclient = self.mox.CreateMock(neutron_client.Client)
|
|
||||||
return self.neutronclient
|
|
||||||
|
|
||||||
def stub_swiftclient(self, expected_calls=1):
|
|
||||||
if not hasattr(self, "swiftclient"):
|
|
||||||
self.mox.StubOutWithMock(swift_client, 'Connection')
|
|
||||||
self.swiftclient = self.mox.CreateMock(swift_client.Connection)
|
|
||||||
while expected_calls:
|
|
||||||
swift_client.Connection(None,
|
|
||||||
mox.IgnoreArg(),
|
|
||||||
None,
|
|
||||||
preauthtoken=mox.IgnoreArg(),
|
|
||||||
preauthurl=mox.IgnoreArg(),
|
|
||||||
auth_version="2.0") \
|
|
||||||
.AndReturn(self.swiftclient)
|
|
||||||
expected_calls -= 1
|
|
||||||
return self.swiftclient
|
|
||||||
|
|
||||||
def stub_heatclient(self):
|
|
||||||
if not hasattr(self, "heatclient"):
|
|
||||||
self.mox.StubOutWithMock(heat_client, 'Client')
|
|
||||||
self.heatclient = self.mox.CreateMock(heat_client.Client)
|
|
||||||
return self.heatclient
|
|
||||||
|
|
||||||
def stub_tuskarclient(self):
|
def stub_tuskarclient(self):
|
||||||
if not hasattr(self, "tuskarclient"):
|
if not hasattr(self, "tuskarclient"):
|
||||||
self.mox.StubOutWithMock(tuskar_client, 'Client')
|
self.mox.StubOutWithMock(tuskar_client, 'Client')
|
||||||
@ -355,55 +113,3 @@ class APITestCase(TestCase):
|
|||||||
if not hasattr(self, "baremetalclient"):
|
if not hasattr(self, "baremetalclient"):
|
||||||
self.baremetalclient = baremetal.BareMetalNodeManager(None)
|
self.baremetalclient = baremetal.BareMetalNodeManager(None)
|
||||||
return self.baremetalclient
|
return self.baremetalclient
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(os.environ.get('WITH_SELENIUM', False),
|
|
||||||
"The WITH_SELENIUM env variable is not set.")
|
|
||||||
class SeleniumTestCase(horizon_helpers.SeleniumTestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(SeleniumTestCase, self).setUp()
|
|
||||||
|
|
||||||
load_test_data(self)
|
|
||||||
self.mox = mox.Mox()
|
|
||||||
|
|
||||||
self._real_get_user = utils.get_user
|
|
||||||
self.setActiveUser(id=self.user.id,
|
|
||||||
token=self.token,
|
|
||||||
username=self.user.name,
|
|
||||||
tenant_id=self.tenant.id,
|
|
||||||
service_catalog=self.service_catalog,
|
|
||||||
authorized_tenants=self.tenants.list())
|
|
||||||
os.environ["HORIZON_TEST_RUN"] = "True"
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
utils.get_user = self._real_get_user
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
del os.environ["HORIZON_TEST_RUN"]
|
|
||||||
|
|
||||||
def setActiveUser(self, id=None, token=None, username=None, tenant_id=None,
|
|
||||||
service_catalog=None, tenant_name=None, roles=None,
|
|
||||||
authorized_tenants=None, enabled=True):
|
|
||||||
def get_user(request):
|
|
||||||
return user.User(id=id,
|
|
||||||
token=token,
|
|
||||||
user=username,
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
service_catalog=service_catalog,
|
|
||||||
roles=roles,
|
|
||||||
enabled=enabled,
|
|
||||||
authorized_tenants=authorized_tenants,
|
|
||||||
endpoint=settings.OPENSTACK_KEYSTONE_URL)
|
|
||||||
utils.get_user = get_user
|
|
||||||
|
|
||||||
|
|
||||||
class SeleniumAdminTestCase(SeleniumTestCase):
|
|
||||||
"""
|
|
||||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
|
||||||
for testing admin-only views and functionality.
|
|
||||||
"""
|
|
||||||
def setActiveUser(self, *args, **kwargs):
|
|
||||||
if "roles" not in kwargs:
|
|
||||||
kwargs['roles'] = [self.roles.admin._info]
|
|
||||||
super(SeleniumAdminTestCase, self).setActiveUser(*args, **kwargs)
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
404 NOT FOUND
|
|
@ -1 +0,0 @@
|
|||||||
500 ERROR
|
|
@ -1 +0,0 @@
|
|||||||
{{ tab.name }}
|
|
@ -1 +0,0 @@
|
|||||||
{{ tab_group.render }}
|
|
@ -1 +0,0 @@
|
|||||||
{{ workflow.render }}
|
|
@ -1,47 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from cinderclient.v1 import quotas
|
|
||||||
from openstack_dashboard.api.base import Quota
|
|
||||||
from openstack_dashboard.api.base import QuotaSet as QuotaSetWrapper
|
|
||||||
from openstack_dashboard.usage.quotas import QuotaUsage
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.cinder_quotas = TestDataContainer()
|
|
||||||
TEST.cinder_quota_usages = TestDataContainer()
|
|
||||||
|
|
||||||
# Quota Sets
|
|
||||||
quota_data = dict(volumes='1',
|
|
||||||
snapshots='1',
|
|
||||||
gigabytes='1000')
|
|
||||||
quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data)
|
|
||||||
#TEST.quotas.cinder = QuotaSetWrapper(quota)
|
|
||||||
TEST.cinder_quotas.add(QuotaSetWrapper(quota))
|
|
||||||
|
|
||||||
# Quota Usages
|
|
||||||
quota_usage_data = {'gigabytes': {'used': 0,
|
|
||||||
'quota': 1000},
|
|
||||||
'instances': {'used': 0,
|
|
||||||
'quota': 10},
|
|
||||||
'snapshots': {'used': 0,
|
|
||||||
'quota': 10}}
|
|
||||||
quota_usage = QuotaUsage()
|
|
||||||
for k, v in quota_usage_data.items():
|
|
||||||
quota_usage.add_quota(Quota(k, v['quota']))
|
|
||||||
quota_usage.tally(k, v['used'])
|
|
||||||
|
|
||||||
TEST.cinder_quota_usages.add(quota_usage)
|
|
@ -12,63 +12,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from cinderclient import exceptions as cinder_exceptions
|
from openstack_dashboard.test.test_data import exceptions
|
||||||
import glanceclient.exc as glance_exceptions
|
|
||||||
from keystoneclient import exceptions as keystone_exceptions
|
|
||||||
from neutronclient.common import exceptions as neutron_exceptions
|
|
||||||
from novaclient import exceptions as nova_exceptions
|
|
||||||
from swiftclient import client as swift_exceptions
|
|
||||||
import tuskarclient.exc as tuskar_exceptions
|
import tuskarclient.exc as tuskar_exceptions
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
def create_stubbed_exception(cls, status_code=500):
|
|
||||||
msg = "Expected failure."
|
|
||||||
|
|
||||||
def fake_init_exception(self, code, message, **kwargs):
|
|
||||||
self.code = code
|
|
||||||
self.message = message
|
|
||||||
|
|
||||||
def fake_str(self):
|
|
||||||
return str(self.message)
|
|
||||||
|
|
||||||
def fake_unicode(self):
|
|
||||||
return unicode(self.message)
|
|
||||||
|
|
||||||
cls.__init__ = fake_init_exception
|
|
||||||
cls.__str__ = fake_str
|
|
||||||
cls.__unicode__ = fake_unicode
|
|
||||||
cls.silence_logging = True
|
|
||||||
return cls(status_code, msg)
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
def data(TEST):
|
||||||
TEST.exceptions = TestDataContainer()
|
TEST.exceptions = exceptions.data
|
||||||
|
|
||||||
unauth = keystone_exceptions.Unauthorized
|
|
||||||
TEST.exceptions.keystone_unauthorized = create_stubbed_exception(unauth)
|
|
||||||
|
|
||||||
keystone_exception = keystone_exceptions.ClientException
|
|
||||||
TEST.exceptions.keystone = create_stubbed_exception(keystone_exception)
|
|
||||||
|
|
||||||
nova_exception = nova_exceptions.ClientException
|
|
||||||
TEST.exceptions.nova = create_stubbed_exception(nova_exception)
|
|
||||||
|
|
||||||
nova_unauth = nova_exceptions.Unauthorized
|
|
||||||
TEST.exceptions.nova_unauthorized = create_stubbed_exception(nova_unauth)
|
|
||||||
|
|
||||||
glance_exception = glance_exceptions.ClientException
|
|
||||||
TEST.exceptions.glance = create_stubbed_exception(glance_exception)
|
|
||||||
|
|
||||||
neutron_exception = neutron_exceptions.NeutronClientException
|
|
||||||
TEST.exceptions.neutron = create_stubbed_exception(neutron_exception)
|
|
||||||
|
|
||||||
swift_exception = swift_exceptions.ClientException
|
|
||||||
TEST.exceptions.swift = create_stubbed_exception(swift_exception)
|
|
||||||
|
|
||||||
cinder_exception = cinder_exceptions.BadRequest
|
|
||||||
TEST.exceptions.cinder = create_stubbed_exception(cinder_exception)
|
|
||||||
|
|
||||||
tuskar_exception = tuskar_exceptions.ClientException
|
tuskar_exception = tuskar_exceptions.ClientException
|
||||||
TEST.exceptions.tuskar = create_stubbed_exception(tuskar_exception)
|
TEST.exceptions.tuskar = exceptions. \
|
||||||
|
create_stubbed_exception(tuskar_exception)
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from glanceclient.v1.images import Image
|
|
||||||
from glanceclient.v1.images import ImageManager
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.images = TestDataContainer()
|
|
||||||
TEST.snapshots = TestDataContainer()
|
|
||||||
|
|
||||||
# Snapshots
|
|
||||||
snapshot_dict = {'name': u'snapshot',
|
|
||||||
'container_format': u'ami',
|
|
||||||
'id': 3,
|
|
||||||
'status': "active",
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'properties': {'image_type': u'snapshot'},
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
snapshot_dict_no_owner = {'name': u'snapshot 2',
|
|
||||||
'container_format': u'ami',
|
|
||||||
'id': 4,
|
|
||||||
'status': "active",
|
|
||||||
'owner': None,
|
|
||||||
'properties': {'image_type': u'snapshot'},
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
snapshot_dict_queued = {'name': u'snapshot 2',
|
|
||||||
'container_format': u'ami',
|
|
||||||
'id': 5,
|
|
||||||
'status': "queued",
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'properties': {'image_type': u'snapshot'},
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
snapshot = Image(ImageManager(None), snapshot_dict)
|
|
||||||
TEST.snapshots.add(snapshot)
|
|
||||||
snapshot = Image(ImageManager(None), snapshot_dict_no_owner)
|
|
||||||
TEST.snapshots.add(snapshot)
|
|
||||||
snapshot = Image(ImageManager(None), snapshot_dict_queued)
|
|
||||||
TEST.snapshots.add(snapshot)
|
|
||||||
|
|
||||||
# Images
|
|
||||||
image_dict = {'id': '007e7d55-fe1e-4c5c-bf08-44b4a4964822',
|
|
||||||
'name': 'public_image',
|
|
||||||
'status': "active",
|
|
||||||
'size': 20 * 1024 ** 3,
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'container_format': 'novaImage',
|
|
||||||
'properties': {'image_type': u'image'},
|
|
||||||
'is_public': True,
|
|
||||||
'protected': False}
|
|
||||||
public_image = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
image_dict = {'id': 'a001c047-22f8-47d0-80a1-8ec94a9524fe',
|
|
||||||
'name': 'private_image',
|
|
||||||
'status': "active",
|
|
||||||
'size': 10 * 1024 ** 2,
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'container_format': 'aki',
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
private_image = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
image_dict = {'id': 'd6936c86-7fec-474a-85c5-5e467b371c3c',
|
|
||||||
'name': 'protected_images',
|
|
||||||
'status': "active",
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'size': 2 * 1024 ** 3,
|
|
||||||
'container_format': 'novaImage',
|
|
||||||
'properties': {'image_type': u'image'},
|
|
||||||
'is_public': True,
|
|
||||||
'protected': True}
|
|
||||||
protected_image = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
image_dict = {'id': '278905a6-4b52-4d1e-98f9-8c57bb25ba32',
|
|
||||||
'name': 'public_image 2',
|
|
||||||
'status': "active",
|
|
||||||
'size': 5 * 1024 ** 3,
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'container_format': 'novaImage',
|
|
||||||
'properties': {'image_type': u'image'},
|
|
||||||
'is_public': True,
|
|
||||||
'protected': False}
|
|
||||||
public_image2 = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
image_dict = {'id': '710a1acf-a3e3-41dd-a32d-5d6b6c86ea10',
|
|
||||||
'name': 'private_image 2',
|
|
||||||
'status': "active",
|
|
||||||
'size': 30 * 1024 ** 3,
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'container_format': 'aki',
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
private_image2 = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
image_dict = {'id': '7cd892fd-5652-40f3-a450-547615680132',
|
|
||||||
'name': 'private_image 3',
|
|
||||||
'status': "active",
|
|
||||||
'size': 2 * 1024 ** 3,
|
|
||||||
'owner': TEST.tenant.id,
|
|
||||||
'container_format': 'aki',
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
private_image3 = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
# A shared image. Not public and not local tenant.
|
|
||||||
image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849',
|
|
||||||
'name': 'shared_image 1',
|
|
||||||
'status': "active",
|
|
||||||
'size': 8 * 1024 ** 3,
|
|
||||||
'owner': 'someothertenant',
|
|
||||||
'container_format': 'aki',
|
|
||||||
'is_public': False,
|
|
||||||
'protected': False}
|
|
||||||
shared_image1 = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
# "Official" image. Public and tenant matches an entry
|
|
||||||
# in IMAGES_LIST_FILTER_TENANTS.
|
|
||||||
image_dict = {'id': 'f448704f-0ce5-4d34-8441-11b6581c6619',
|
|
||||||
'name': 'official_image 1',
|
|
||||||
'status': "active",
|
|
||||||
'size': 2 * 1024 ** 3,
|
|
||||||
'owner': 'officialtenant',
|
|
||||||
'container_format': 'aki',
|
|
||||||
'is_public': True,
|
|
||||||
'protected': False}
|
|
||||||
official_image1 = Image(ImageManager(None), image_dict)
|
|
||||||
|
|
||||||
TEST.images.add(public_image, private_image, protected_image,
|
|
||||||
public_image2, private_image2, private_image3,
|
|
||||||
shared_image1, official_image1)
|
|
@ -1,340 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from heatclient.v1.stacks import Stack
|
|
||||||
from heatclient.v1.stacks import StackManager
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
# A slightly hacked up copy of a sample cloudformation template for testing.
|
|
||||||
TEMPLATE = """
|
|
||||||
{
|
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
|
||||||
"Description": "AWS CloudFormation Sample Template.",
|
|
||||||
"Parameters": {
|
|
||||||
"KeyName": {
|
|
||||||
"Description": "Name of an EC2 KeyPair to enable SSH access to the instances",
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
"InstanceType": {
|
|
||||||
"Description": "WebServer EC2 instance type",
|
|
||||||
"Type": "String",
|
|
||||||
"Default": "m1.small",
|
|
||||||
"AllowedValues": [
|
|
||||||
"m1.tiny",
|
|
||||||
"m1.small",
|
|
||||||
"m1.medium",
|
|
||||||
"m1.large",
|
|
||||||
"m1.xlarge"
|
|
||||||
],
|
|
||||||
"ConstraintDescription": "must be a valid EC2 instance type."
|
|
||||||
},
|
|
||||||
"DBName": {
|
|
||||||
"Default": "wordpress",
|
|
||||||
"Description": "The WordPress database name",
|
|
||||||
"Type": "String",
|
|
||||||
"MinLength": "1",
|
|
||||||
"MaxLength": "64",
|
|
||||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
|
||||||
"ConstraintDescription": "must begin with a letter and..."
|
|
||||||
},
|
|
||||||
"DBUsername": {
|
|
||||||
"Default": "admin",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"Description": "The WordPress database admin account username",
|
|
||||||
"Type": "String",
|
|
||||||
"MinLength": "1",
|
|
||||||
"MaxLength": "16",
|
|
||||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
|
||||||
"ConstraintDescription": "must begin with a letter and..."
|
|
||||||
},
|
|
||||||
"DBPassword": {
|
|
||||||
"Default": "admin",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"Description": "The WordPress database admin account password",
|
|
||||||
"Type": "String",
|
|
||||||
"MinLength": "1",
|
|
||||||
"MaxLength": "41",
|
|
||||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
|
||||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
|
||||||
},
|
|
||||||
"DBRootPassword": {
|
|
||||||
"Default": "admin",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"Description": "Root password for MySQL",
|
|
||||||
"Type": "String",
|
|
||||||
"MinLength": "1",
|
|
||||||
"MaxLength": "41",
|
|
||||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
|
||||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
|
||||||
},
|
|
||||||
"LinuxDistribution": {
|
|
||||||
"Default": "F17",
|
|
||||||
"Description": "Distribution of choice",
|
|
||||||
"Type": "String",
|
|
||||||
"AllowedValues": [
|
|
||||||
"F18",
|
|
||||||
"F17",
|
|
||||||
"U10",
|
|
||||||
"RHEL-6.1",
|
|
||||||
"RHEL-6.2",
|
|
||||||
"RHEL-6.3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Mappings": {
|
|
||||||
"AWSInstanceType2Arch": {
|
|
||||||
"m1.tiny": {
|
|
||||||
"Arch": "32"
|
|
||||||
},
|
|
||||||
"m1.small": {
|
|
||||||
"Arch": "64"
|
|
||||||
},
|
|
||||||
"m1.medium": {
|
|
||||||
"Arch": "64"
|
|
||||||
},
|
|
||||||
"m1.large": {
|
|
||||||
"Arch": "64"
|
|
||||||
},
|
|
||||||
"m1.xlarge": {
|
|
||||||
"Arch": "64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DistroArch2AMI": {
|
|
||||||
"F18": {
|
|
||||||
"32": "F18-i386-cfntools",
|
|
||||||
"64": "F18-x86_64-cfntools"
|
|
||||||
},
|
|
||||||
"F17": {
|
|
||||||
"32": "F17-i386-cfntools",
|
|
||||||
"64": "F17-x86_64-cfntools"
|
|
||||||
},
|
|
||||||
"U10": {
|
|
||||||
"32": "U10-i386-cfntools",
|
|
||||||
"64": "U10-x86_64-cfntools"
|
|
||||||
},
|
|
||||||
"RHEL-6.1": {
|
|
||||||
"32": "rhel61-i386-cfntools",
|
|
||||||
"64": "rhel61-x86_64-cfntools"
|
|
||||||
},
|
|
||||||
"RHEL-6.2": {
|
|
||||||
"32": "rhel62-i386-cfntools",
|
|
||||||
"64": "rhel62-x86_64-cfntools"
|
|
||||||
},
|
|
||||||
"RHEL-6.3": {
|
|
||||||
"32": "rhel63-i386-cfntools",
|
|
||||||
"64": "rhel63-x86_64-cfntools"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Resources": {
|
|
||||||
"WikiDatabase": {
|
|
||||||
"Type": "AWS::EC2::Instance",
|
|
||||||
"Metadata": {
|
|
||||||
"AWS::CloudFormation::Init": {
|
|
||||||
"config": {
|
|
||||||
"packages": {
|
|
||||||
"yum": {
|
|
||||||
"mysql": [],
|
|
||||||
"mysql-server": [],
|
|
||||||
"httpd": [],
|
|
||||||
"wordpress": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"systemd": {
|
|
||||||
"mysqld": {
|
|
||||||
"enabled": "true",
|
|
||||||
"ensureRunning": "true"
|
|
||||||
},
|
|
||||||
"httpd": {
|
|
||||||
"enabled": "true",
|
|
||||||
"ensureRunning": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Properties": {
|
|
||||||
"ImageId": {
|
|
||||||
"Fn::FindInMap": [
|
|
||||||
"DistroArch2AMI",
|
|
||||||
{
|
|
||||||
"Ref": "LinuxDistribution"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Fn::FindInMap": [
|
|
||||||
"AWSInstanceType2Arch",
|
|
||||||
{
|
|
||||||
"Ref": "InstanceType"
|
|
||||||
},
|
|
||||||
"Arch"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"InstanceType": {
|
|
||||||
"Ref": "InstanceType"
|
|
||||||
},
|
|
||||||
"KeyName": {
|
|
||||||
"Ref": "KeyName"
|
|
||||||
},
|
|
||||||
"UserData": {
|
|
||||||
"Fn::Base64": {
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
"#!/bin/bash -v\n",
|
|
||||||
"/opt/aws/bin/cfn-init\n"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Outputs": {
|
|
||||||
"WebsiteURL": {
|
|
||||||
"Value": {
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
"http://",
|
|
||||||
{
|
|
||||||
"Fn::GetAtt": [
|
|
||||||
"WikiDatabase",
|
|
||||||
"PublicIp"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/wordpress"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Description": "URL for Wordpress wiki"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
VALIDATE = """
|
|
||||||
{
|
|
||||||
"Description": "AWS CloudFormation Sample Template.",
|
|
||||||
"Parameters": {
|
|
||||||
"DBUsername": {
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "The WordPress database admin account username",
|
|
||||||
"Default": "admin",
|
|
||||||
"MinLength": "1",
|
|
||||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"MaxLength": "16",
|
|
||||||
"ConstraintDescription": "must begin with a letter and..."
|
|
||||||
},
|
|
||||||
"LinuxDistribution": {
|
|
||||||
"Default": "F17",
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "Distribution of choice",
|
|
||||||
"AllowedValues": [
|
|
||||||
"F18",
|
|
||||||
"F17",
|
|
||||||
"U10",
|
|
||||||
"RHEL-6.1",
|
|
||||||
"RHEL-6.2",
|
|
||||||
"RHEL-6.3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"DBRootPassword": {
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "Root password for MySQL",
|
|
||||||
"Default": "admin",
|
|
||||||
"MinLength": "1",
|
|
||||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"MaxLength": "41",
|
|
||||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
|
||||||
},
|
|
||||||
"KeyName": {
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "Name of an EC2 KeyPair to enable SSH access to the instances"
|
|
||||||
},
|
|
||||||
"DBName": {
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "The WordPress database name",
|
|
||||||
"Default": "wordpress",
|
|
||||||
"MinLength": "1",
|
|
||||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
|
||||||
"MaxLength": "64",
|
|
||||||
"ConstraintDescription": "must begin with a letter and..."
|
|
||||||
},
|
|
||||||
"DBPassword": {
|
|
||||||
"Type": "String",
|
|
||||||
"Description": "The WordPress database admin account password",
|
|
||||||
"Default": "admin",
|
|
||||||
"MinLength": "1",
|
|
||||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
|
||||||
"NoEcho": "true",
|
|
||||||
"MaxLength": "41",
|
|
||||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
|
||||||
},
|
|
||||||
"InstanceType": {
|
|
||||||
"Default": "m1.small",
|
|
||||||
"Type": "String",
|
|
||||||
"ConstraintDescription": "must be a valid EC2 instance type.",
|
|
||||||
"Description": "WebServer EC2 instance type",
|
|
||||||
"AllowedValues": [
|
|
||||||
"m1.tiny",
|
|
||||||
"m1.small",
|
|
||||||
"m1.medium",
|
|
||||||
"m1.large",
|
|
||||||
"m1.xlarge"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Template(object):
|
|
||||||
def __init__(self, data, validate):
|
|
||||||
self.data = data
|
|
||||||
self.validate = validate
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.stacks = TestDataContainer()
|
|
||||||
TEST.stack_templates = TestDataContainer()
|
|
||||||
|
|
||||||
# Stacks
|
|
||||||
stack1 = {
|
|
||||||
"description": "No description",
|
|
||||||
"links": [{
|
|
||||||
"href": "http://192.168.1.70:8004/v1/"
|
|
||||||
"051c727ee67040d6a7b7812708485a97/"
|
|
||||||
"stacks/stack-1211-38/"
|
|
||||||
"05b4f39f-ea96-4d91-910c-e758c078a089",
|
|
||||||
"rel": "self"
|
|
||||||
}],
|
|
||||||
"stack_status_reason": "Stack successfully created",
|
|
||||||
"stack_name": "stack-test",
|
|
||||||
"creation_time": "2013-04-22T00:11:39Z",
|
|
||||||
"updated_time": "2013-04-22T00:11:39Z",
|
|
||||||
"stack_status": "CREATE_COMPLETE",
|
|
||||||
"id": "05b4f39f-ea96-4d91-910c-e758c078a089"
|
|
||||||
}
|
|
||||||
stack = Stack(StackManager(None), stack1)
|
|
||||||
TEST.stacks.add(stack)
|
|
||||||
|
|
||||||
TEST.stack_templates.add(Template(TEMPLATE, VALIDATE))
|
|
@ -1,269 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.utils import datetime_safe
|
|
||||||
|
|
||||||
from keystoneclient.access import AccessInfo
|
|
||||||
from keystoneclient.v2_0 import ec2
|
|
||||||
from keystoneclient.v2_0 import roles
|
|
||||||
from keystoneclient.v2_0 import tenants
|
|
||||||
from keystoneclient.v2_0 import users
|
|
||||||
from keystoneclient.v3 import domains
|
|
||||||
from keystoneclient.v3 import groups
|
|
||||||
|
|
||||||
from openstack_auth.user import Token
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
# Dummy service catalog with all service.
|
|
||||||
# All endpoint URLs should point to example.com.
|
|
||||||
# Try to keep them as accurate to real data as possible (ports, URIs, etc.)
|
|
||||||
SERVICE_CATALOG = [
|
|
||||||
{"type": "compute",
|
|
||||||
"name": "nova",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.nova.example.com:8774/v2",
|
|
||||||
"internalURL": "http://int.nova.example.com:8774/v2",
|
|
||||||
"publicURL": "http://public.nova.example.com:8774/v2"},
|
|
||||||
{"region": "RegionTwo",
|
|
||||||
"adminURL": "http://admin.nova2.example.com:8774/v2",
|
|
||||||
"internalURL": "http://int.nova2.example.com:8774/v2",
|
|
||||||
"publicURL": "http://public.nova2.example.com:8774/v2"}]},
|
|
||||||
{"type": "volume",
|
|
||||||
"name": "nova",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.nova.example.com:8776/v1",
|
|
||||||
"internalURL": "http://int.nova.example.com:8776/v1",
|
|
||||||
"publicURL": "http://public.nova.example.com:8776/v1"},
|
|
||||||
{"region": "RegionTwo",
|
|
||||||
"adminURL": "http://admin.nova.example.com:8776/v1",
|
|
||||||
"internalURL": "http://int.nova.example.com:8776/v1",
|
|
||||||
"publicURL": "http://public.nova.example.com:8776/v1"}]},
|
|
||||||
{"type": "image",
|
|
||||||
"name": "glance",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.glance.example.com:9292/v1",
|
|
||||||
"internalURL": "http://int.glance.example.com:9292/v1",
|
|
||||||
"publicURL": "http://public.glance.example.com:9292/v1"}]},
|
|
||||||
{"type": "identity",
|
|
||||||
"name": "keystone",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.keystone.example.com:35357/v2.0",
|
|
||||||
"internalURL": "http://int.keystone.example.com:5000/v2.0",
|
|
||||||
"publicURL": "http://public.keystone.example.com:5000/v2.0"}]},
|
|
||||||
{"type": "object-store",
|
|
||||||
"name": "swift",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.swift.example.com:8080/",
|
|
||||||
"internalURL": "http://int.swift.example.com:8080/",
|
|
||||||
"publicURL": "http://public.swift.example.com:8080/"}]},
|
|
||||||
{"type": "network",
|
|
||||||
"name": "neutron",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.neutron.example.com:9696/",
|
|
||||||
"internalURL": "http://int.neutron.example.com:9696/",
|
|
||||||
"publicURL": "http://public.neutron.example.com:9696/"}]},
|
|
||||||
{"type": "ec2",
|
|
||||||
"name": "EC2 Service",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.nova.example.com:8773/services/Admin",
|
|
||||||
"publicURL": "http://public.nova.example.com:8773/services/Cloud",
|
|
||||||
"internalURL": "http://int.nova.example.com:8773/services/Cloud"}]},
|
|
||||||
{"type": "orchestration",
|
|
||||||
"name": "Heat",
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{"region": "RegionOne",
|
|
||||||
"adminURL": "http://admin.heat.example.com:8004/v1",
|
|
||||||
"publicURL": "http://public.heat.example.com:8004/v1",
|
|
||||||
"internalURL": "http://int.heat.example.com:8004/v1"}]}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.service_catalog = SERVICE_CATALOG
|
|
||||||
TEST.tokens = TestDataContainer()
|
|
||||||
TEST.domains = TestDataContainer()
|
|
||||||
TEST.users = TestDataContainer()
|
|
||||||
TEST.groups = TestDataContainer()
|
|
||||||
TEST.tenants = TestDataContainer()
|
|
||||||
TEST.roles = TestDataContainer()
|
|
||||||
TEST.ec2 = TestDataContainer()
|
|
||||||
|
|
||||||
admin_role_dict = {'id': '1',
|
|
||||||
'name': 'admin'}
|
|
||||||
admin_role = roles.Role(roles.RoleManager, admin_role_dict)
|
|
||||||
member_role_dict = {'id': "2",
|
|
||||||
'name': settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE}
|
|
||||||
member_role = roles.Role(roles.RoleManager, member_role_dict)
|
|
||||||
TEST.roles.add(admin_role, member_role)
|
|
||||||
TEST.roles.admin = admin_role
|
|
||||||
TEST.roles.member = member_role
|
|
||||||
|
|
||||||
domain_dict = {'id': "1",
|
|
||||||
'name': 'test_domain',
|
|
||||||
'description': "a test domain.",
|
|
||||||
'enabled': True}
|
|
||||||
domain_dict_2 = {'id': "2",
|
|
||||||
'name': 'disabled_domain',
|
|
||||||
'description': "a disabled test domain.",
|
|
||||||
'enabled': False}
|
|
||||||
domain = domains.Domain(domains.DomainManager, domain_dict)
|
|
||||||
disabled_domain = domains.Domain(domains.DomainManager, domain_dict_2)
|
|
||||||
TEST.domains.add(domain, disabled_domain)
|
|
||||||
TEST.domain = domain # Your "current" domain
|
|
||||||
|
|
||||||
user_dict = {'id': "1",
|
|
||||||
'name': 'test_user',
|
|
||||||
'email': 'test@example.com',
|
|
||||||
'password': 'password',
|
|
||||||
'token': 'test_token',
|
|
||||||
'project_id': '1',
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': "1"}
|
|
||||||
user = users.User(users.UserManager(None), user_dict)
|
|
||||||
user_dict = {'id': "2",
|
|
||||||
'name': 'user_two',
|
|
||||||
'email': 'two@example.com',
|
|
||||||
'password': 'password',
|
|
||||||
'token': 'test_token',
|
|
||||||
'project_id': '1',
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': "1"}
|
|
||||||
user2 = users.User(users.UserManager(None), user_dict)
|
|
||||||
user_dict = {'id': "3",
|
|
||||||
'name': 'user_three',
|
|
||||||
'email': 'three@example.com',
|
|
||||||
'password': 'password',
|
|
||||||
'token': 'test_token',
|
|
||||||
'project_id': '1',
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': "1"}
|
|
||||||
user3 = users.User(users.UserManager(None), user_dict)
|
|
||||||
user_dict = {'id': "4",
|
|
||||||
'name': 'user_four',
|
|
||||||
'email': 'four@example.com',
|
|
||||||
'password': 'password',
|
|
||||||
'token': 'test_token',
|
|
||||||
'project_id': '2',
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': "2"}
|
|
||||||
user4 = users.User(users.UserManager(None), user_dict)
|
|
||||||
TEST.users.add(user, user2, user3, user4)
|
|
||||||
TEST.user = user # Your "current" user
|
|
||||||
TEST.user.service_catalog = SERVICE_CATALOG
|
|
||||||
|
|
||||||
group_dict = {'id': "1",
|
|
||||||
'name': 'group_one',
|
|
||||||
'description': 'group one description',
|
|
||||||
'domain_id': '1'}
|
|
||||||
group = groups.Group(groups.GroupManager(None), group_dict)
|
|
||||||
group_dict = {'id': "2",
|
|
||||||
'name': 'group_two',
|
|
||||||
'description': 'group two description',
|
|
||||||
'domain_id': '1'}
|
|
||||||
group2 = groups.Group(groups.GroupManager(None), group_dict)
|
|
||||||
group_dict = {'id': "3",
|
|
||||||
'name': 'group_three',
|
|
||||||
'description': 'group three description',
|
|
||||||
'domain_id': '2'}
|
|
||||||
group3 = groups.Group(groups.GroupManager(None), group_dict)
|
|
||||||
TEST.groups.add(group, group2, group3)
|
|
||||||
|
|
||||||
tenant_dict = {'id': "1",
|
|
||||||
'name': 'test_tenant',
|
|
||||||
'description': "a test tenant.",
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': '1'}
|
|
||||||
tenant_dict_2 = {'id': "2",
|
|
||||||
'name': 'disabled_tenant',
|
|
||||||
'description': "a disabled test tenant.",
|
|
||||||
'enabled': False,
|
|
||||||
'domain_id': '2'}
|
|
||||||
tenant_dict_3 = {'id': "3",
|
|
||||||
'name': u'\u4e91\u89c4\u5219',
|
|
||||||
'description': "an unicode-named tenant.",
|
|
||||||
'enabled': True,
|
|
||||||
'domain_id': '2'}
|
|
||||||
tenant = tenants.Tenant(tenants.TenantManager, tenant_dict)
|
|
||||||
disabled_tenant = tenants.Tenant(tenants.TenantManager, tenant_dict_2)
|
|
||||||
tenant_unicode = tenants.Tenant(tenants.TenantManager, tenant_dict_3)
|
|
||||||
|
|
||||||
TEST.tenants.add(tenant, disabled_tenant, tenant_unicode)
|
|
||||||
TEST.tenant = tenant # Your "current" tenant
|
|
||||||
|
|
||||||
tomorrow = datetime_safe.datetime.now() + timedelta(days=1)
|
|
||||||
expiration = datetime_safe.datetime.isoformat(tomorrow)
|
|
||||||
|
|
||||||
scoped_token_dict = {
|
|
||||||
'access': {
|
|
||||||
'token': {
|
|
||||||
'id': "test_token_id",
|
|
||||||
'expires': expiration,
|
|
||||||
'tenant': tenant_dict,
|
|
||||||
'tenants': [tenant_dict]},
|
|
||||||
'user': {
|
|
||||||
'id': "test_user_id",
|
|
||||||
'name': "test_user",
|
|
||||||
'roles': [member_role_dict]},
|
|
||||||
'serviceCatalog': TEST.service_catalog
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_access_info = AccessInfo.factory(resp=None,
|
|
||||||
body=scoped_token_dict)
|
|
||||||
|
|
||||||
unscoped_token_dict = {
|
|
||||||
'access': {
|
|
||||||
'token': {
|
|
||||||
'id': "test_token_id",
|
|
||||||
'expires': expiration},
|
|
||||||
'user': {
|
|
||||||
'id': "test_user_id",
|
|
||||||
'name': "test_user",
|
|
||||||
'roles': [member_role_dict]},
|
|
||||||
'serviceCatalog': TEST.service_catalog
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unscoped_access_info = AccessInfo.factory(resp=None,
|
|
||||||
body=unscoped_token_dict)
|
|
||||||
|
|
||||||
scoped_token = Token(scoped_access_info)
|
|
||||||
unscoped_token = Token(unscoped_access_info)
|
|
||||||
TEST.tokens.add(scoped_token, unscoped_token)
|
|
||||||
TEST.token = scoped_token # your "current" token.
|
|
||||||
TEST.tokens.scoped_token = scoped_token
|
|
||||||
TEST.tokens.unscoped_token = unscoped_token
|
|
||||||
|
|
||||||
access_secret = ec2.EC2(ec2.CredentialsManager, {"access": "access",
|
|
||||||
"secret": "secret"})
|
|
||||||
TEST.ec2.add(access_secret)
|
|
@ -1,458 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
import copy
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from openstack_dashboard.api.lbaas import Member
|
|
||||||
from openstack_dashboard.api.lbaas import Pool
|
|
||||||
from openstack_dashboard.api.lbaas import PoolMonitor
|
|
||||||
from openstack_dashboard.api.lbaas import Vip
|
|
||||||
|
|
||||||
from openstack_dashboard.api.neutron import FloatingIp
|
|
||||||
from openstack_dashboard.api.neutron import Network
|
|
||||||
from openstack_dashboard.api.neutron import Port
|
|
||||||
from openstack_dashboard.api.neutron import Router
|
|
||||||
from openstack_dashboard.api.neutron import SecurityGroup
|
|
||||||
from openstack_dashboard.api.neutron import SecurityGroupRule
|
|
||||||
from openstack_dashboard.api.neutron import Subnet
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
# data returned by openstack_dashboard.api.neutron wrapper
|
|
||||||
TEST.networks = TestDataContainer()
|
|
||||||
TEST.subnets = TestDataContainer()
|
|
||||||
TEST.ports = TestDataContainer()
|
|
||||||
TEST.routers = TestDataContainer()
|
|
||||||
TEST.q_floating_ips = TestDataContainer()
|
|
||||||
TEST.q_secgroups = TestDataContainer()
|
|
||||||
TEST.q_secgroup_rules = TestDataContainer()
|
|
||||||
TEST.pools = TestDataContainer()
|
|
||||||
TEST.vips = TestDataContainer()
|
|
||||||
TEST.members = TestDataContainer()
|
|
||||||
TEST.monitors = TestDataContainer()
|
|
||||||
|
|
||||||
# data return by neutronclient
|
|
||||||
TEST.api_networks = TestDataContainer()
|
|
||||||
TEST.api_subnets = TestDataContainer()
|
|
||||||
TEST.api_ports = TestDataContainer()
|
|
||||||
TEST.api_routers = TestDataContainer()
|
|
||||||
TEST.api_q_floating_ips = TestDataContainer()
|
|
||||||
TEST.api_q_secgroups = TestDataContainer()
|
|
||||||
TEST.api_q_secgroup_rules = TestDataContainer()
|
|
||||||
TEST.api_pools = TestDataContainer()
|
|
||||||
TEST.api_vips = TestDataContainer()
|
|
||||||
TEST.api_members = TestDataContainer()
|
|
||||||
TEST.api_monitors = TestDataContainer()
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# 1st network
|
|
||||||
network_dict = {'admin_state_up': True,
|
|
||||||
'id': '82288d84-e0a5-42ac-95be-e6af08727e42',
|
|
||||||
'name': 'net1',
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9'],
|
|
||||||
'tenant_id': '1',
|
|
||||||
'router:external': False,
|
|
||||||
'shared': False}
|
|
||||||
subnet_dict = {'allocation_pools': [{'end': '10.0.0.254',
|
|
||||||
'start': '10.0.0.2'}],
|
|
||||||
'dns_nameservers': [],
|
|
||||||
'host_routes': [],
|
|
||||||
'cidr': '10.0.0.0/24',
|
|
||||||
'enable_dhcp': True,
|
|
||||||
'gateway_ip': '10.0.0.1',
|
|
||||||
'id': network_dict['subnets'][0],
|
|
||||||
'ip_version': 4,
|
|
||||||
'name': 'mysubnet1',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
|
|
||||||
TEST.api_networks.add(network_dict)
|
|
||||||
TEST.api_subnets.add(subnet_dict)
|
|
||||||
|
|
||||||
network = copy.deepcopy(network_dict)
|
|
||||||
subnet = Subnet(subnet_dict)
|
|
||||||
network['subnets'] = [subnet]
|
|
||||||
TEST.networks.add(Network(network))
|
|
||||||
TEST.subnets.add(subnet)
|
|
||||||
|
|
||||||
# ports on 1st network
|
|
||||||
port_dict = {'admin_state_up': True,
|
|
||||||
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
|
||||||
'device_owner': 'network:dhcp',
|
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.3',
|
|
||||||
'subnet_id': subnet_dict['id']}],
|
|
||||||
'id': '063cf7f3-ded1-4297-bc4c-31eae876cc91',
|
|
||||||
'mac_address': 'fa:16:3e:9c:d5:7e',
|
|
||||||
'name': '',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
TEST.api_ports.add(port_dict)
|
|
||||||
TEST.ports.add(Port(port_dict))
|
|
||||||
|
|
||||||
port_dict = {'admin_state_up': True,
|
|
||||||
'device_id': '1',
|
|
||||||
'device_owner': 'compute:nova',
|
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.4',
|
|
||||||
'subnet_id': subnet_dict['id']}],
|
|
||||||
'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406',
|
|
||||||
'mac_address': 'fa:16:3e:9d:e6:2f',
|
|
||||||
'name': '',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
TEST.api_ports.add(port_dict)
|
|
||||||
TEST.ports.add(Port(port_dict))
|
|
||||||
assoc_port = port_dict
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# 2nd network
|
|
||||||
network_dict = {'admin_state_up': True,
|
|
||||||
'id': '72c3ab6c-c80f-4341-9dc5-210fa31ac6c2',
|
|
||||||
'name': 'net2',
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'subnets': ['3f7c5d79-ee55-47b0-9213-8e669fb03009'],
|
|
||||||
'tenant_id': '2',
|
|
||||||
'router:external': False,
|
|
||||||
'shared': True}
|
|
||||||
subnet_dict = {'allocation_pools': [{'end': '172.16.88.254',
|
|
||||||
'start': '172.16.88.2'}],
|
|
||||||
'dns_nameservers': ['10.56.1.20', '10.56.1.21'],
|
|
||||||
'host_routes': [{'destination': '192.168.20.0/24',
|
|
||||||
'nexthop': '172.16.88.253'},
|
|
||||||
{'destination': '192.168.21.0/24',
|
|
||||||
'nexthop': '172.16.88.252'}],
|
|
||||||
'cidr': '172.16.88.0/24',
|
|
||||||
'enable_dhcp': True,
|
|
||||||
'gateway_ip': '172.16.88.1',
|
|
||||||
'id': '3f7c5d79-ee55-47b0-9213-8e669fb03009',
|
|
||||||
'ip_version': 4,
|
|
||||||
'name': 'aaaa',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
|
|
||||||
TEST.api_networks.add(network_dict)
|
|
||||||
TEST.api_subnets.add(subnet_dict)
|
|
||||||
|
|
||||||
network = copy.deepcopy(network_dict)
|
|
||||||
subnet = Subnet(subnet_dict)
|
|
||||||
network['subnets'] = [subnet]
|
|
||||||
TEST.networks.add(Network(network))
|
|
||||||
TEST.subnets.add(subnet)
|
|
||||||
|
|
||||||
port_dict = {'admin_state_up': True,
|
|
||||||
'device_id': '2',
|
|
||||||
'device_owner': 'compute:nova',
|
|
||||||
'fixed_ips': [{'ip_address': '172.16.88.3',
|
|
||||||
'subnet_id': subnet_dict['id']}],
|
|
||||||
'id': '1db2cc37-3553-43fa-b7e2-3fc4eb4f9905',
|
|
||||||
'mac_address': 'fa:16:3e:56:e6:2f',
|
|
||||||
'name': '',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
|
|
||||||
TEST.api_ports.add(port_dict)
|
|
||||||
TEST.ports.add(Port(port_dict))
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# external network
|
|
||||||
network_dict = {'admin_state_up': True,
|
|
||||||
'id': '9b466b94-213a-4cda-badf-72c102a874da',
|
|
||||||
'name': 'ext_net',
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'subnets': ['d6bdc71c-7566-4d32-b3ff-36441ce746e8'],
|
|
||||||
'tenant_id': '3',
|
|
||||||
'router:external': True,
|
|
||||||
'shared': False}
|
|
||||||
subnet_dict = {'allocation_pools': [{'start': '172.24.4.226.',
|
|
||||||
'end': '172.24.4.238'}],
|
|
||||||
'dns_nameservers': [],
|
|
||||||
'host_routes': [],
|
|
||||||
'cidr': '172.24.4.0/28',
|
|
||||||
'enable_dhcp': False,
|
|
||||||
'gateway_ip': '172.24.4.225',
|
|
||||||
'id': 'd6bdc71c-7566-4d32-b3ff-36441ce746e8',
|
|
||||||
'ip_version': 4,
|
|
||||||
'name': 'ext_subnet',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'tenant_id': network_dict['tenant_id']}
|
|
||||||
ext_net = network_dict
|
|
||||||
|
|
||||||
TEST.api_networks.add(network_dict)
|
|
||||||
TEST.api_subnets.add(subnet_dict)
|
|
||||||
|
|
||||||
network = copy.deepcopy(network_dict)
|
|
||||||
subnet = Subnet(subnet_dict)
|
|
||||||
network['subnets'] = [subnet]
|
|
||||||
TEST.networks.add(Network(network))
|
|
||||||
TEST.subnets.add(subnet)
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# Set up router data
|
|
||||||
port_dict = {'admin_state_up': True,
|
|
||||||
'device_id': '7180cede-bcd8-4334-b19f-f7ef2f331f53',
|
|
||||||
'device_owner': 'network:router_gateway',
|
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.3',
|
|
||||||
'subnet_id': subnet_dict['id']}],
|
|
||||||
'id': '44ec6726-4bdc-48c5-94d4-df8d1fbf613b',
|
|
||||||
'mac_address': 'fa:16:3e:9c:d5:7e',
|
|
||||||
'name': '',
|
|
||||||
'network_id': network_dict['id'],
|
|
||||||
'status': 'ACTIVE',
|
|
||||||
'tenant_id': '1'}
|
|
||||||
TEST.api_ports.add(port_dict)
|
|
||||||
TEST.ports.add(Port(port_dict))
|
|
||||||
|
|
||||||
router_dict = {'id': '279989f7-54bb-41d9-ba42-0d61f12fda61',
|
|
||||||
'name': 'router1',
|
|
||||||
'external_gateway_info':
|
|
||||||
{'network_id': ext_net['id']},
|
|
||||||
'tenant_id': '1'}
|
|
||||||
TEST.api_routers.add(router_dict)
|
|
||||||
TEST.routers.add(Router(router_dict))
|
|
||||||
router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c',
|
|
||||||
'name': 'router2',
|
|
||||||
'external_gateway_info':
|
|
||||||
{'network_id': ext_net['id']},
|
|
||||||
'tenant_id': '1'}
|
|
||||||
TEST.api_routers.add(router_dict)
|
|
||||||
TEST.routers.add(Router(router_dict))
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# floating IP
|
|
||||||
# unassociated
|
|
||||||
fip_dict = {'tenant_id': '1',
|
|
||||||
'floating_ip_address': '172.16.88.227',
|
|
||||||
'floating_network_id': ext_net['id'],
|
|
||||||
'id': '9012cd70-cfae-4e46-b71e-6a409e9e0063',
|
|
||||||
'fixed_ip_address': None,
|
|
||||||
'port_id': None,
|
|
||||||
'router_id': None}
|
|
||||||
TEST.api_q_floating_ips.add(fip_dict)
|
|
||||||
TEST.q_floating_ips.add(FloatingIp(fip_dict))
|
|
||||||
|
|
||||||
# associated (with compute port on 1st network)
|
|
||||||
fip_dict = {'tenant_id': '1',
|
|
||||||
'floating_ip_address': '172.16.88.228',
|
|
||||||
'floating_network_id': ext_net['id'],
|
|
||||||
'id': 'a97af8f2-3149-4b97-abbd-e49ad19510f7',
|
|
||||||
'fixed_ip_address': assoc_port['fixed_ips'][0]['ip_address'],
|
|
||||||
'port_id': assoc_port['id'],
|
|
||||||
'router_id': router_dict['id']}
|
|
||||||
TEST.api_q_floating_ips.add(fip_dict)
|
|
||||||
TEST.q_floating_ips.add(FloatingIp(fip_dict))
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# security group
|
|
||||||
|
|
||||||
sec_group_1 = {'tenant_id': '1',
|
|
||||||
'description': 'default',
|
|
||||||
'id': 'faad7c80-3b62-4440-967c-13808c37131d',
|
|
||||||
'name': 'default'}
|
|
||||||
sec_group_2 = {'tenant_id': '1',
|
|
||||||
'description': 'NotDefault',
|
|
||||||
'id': '27a5c9a1-bdbb-48ac-833a-2e4b5f54b31d',
|
|
||||||
'name': 'other_group'}
|
|
||||||
sec_group_3 = {'tenant_id': '1',
|
|
||||||
'description': 'NotDefault',
|
|
||||||
'id': '443a4d7a-4bd2-4474-9a77-02b35c9f8c95',
|
|
||||||
'name': 'another_group'}
|
|
||||||
|
|
||||||
def add_rule_to_group(secgroup, default_only=True):
|
|
||||||
rule_egress_ipv4 = {
|
|
||||||
'id': str(uuid.uuid4()),
|
|
||||||
'direction': u'egress', 'ethertype': u'IPv4',
|
|
||||||
'port_range_min': None, 'port_range_max': None,
|
|
||||||
'protocol': None, 'remote_group_id': None,
|
|
||||||
'remote_ip_prefix': None,
|
|
||||||
'security_group_id': secgroup['id'],
|
|
||||||
'tenant_id': secgroup['tenant_id']}
|
|
||||||
rule_egress_ipv6 = {
|
|
||||||
'id': str(uuid.uuid4()),
|
|
||||||
'direction': u'egress', 'ethertype': u'IPv6',
|
|
||||||
'port_range_min': None, 'port_range_max': None,
|
|
||||||
'protocol': None, 'remote_group_id': None,
|
|
||||||
'remote_ip_prefix': None,
|
|
||||||
'security_group_id': secgroup['id'],
|
|
||||||
'tenant_id': secgroup['tenant_id']}
|
|
||||||
|
|
||||||
rule_tcp_80 = {
|
|
||||||
'id': str(uuid.uuid4()),
|
|
||||||
'direction': u'ingress', 'ethertype': u'IPv4',
|
|
||||||
'port_range_min': 80, 'port_range_max': 80,
|
|
||||||
'protocol': u'tcp', 'remote_group_id': None,
|
|
||||||
'remote_ip_prefix': u'0.0.0.0/0',
|
|
||||||
'security_group_id': secgroup['id'],
|
|
||||||
'tenant_id': secgroup['tenant_id']}
|
|
||||||
rule_icmp = {
|
|
||||||
'id': str(uuid.uuid4()),
|
|
||||||
'direction': u'ingress', 'ethertype': u'IPv4',
|
|
||||||
'port_range_min': 5, 'port_range_max': 8,
|
|
||||||
'protocol': u'icmp', 'remote_group_id': None,
|
|
||||||
'remote_ip_prefix': u'0.0.0.0/0',
|
|
||||||
'security_group_id': secgroup['id'],
|
|
||||||
'tenant_id': secgroup['tenant_id']}
|
|
||||||
rule_group = {
|
|
||||||
'id': str(uuid.uuid4()),
|
|
||||||
'direction': u'ingress', 'ethertype': u'IPv4',
|
|
||||||
'port_range_min': 80, 'port_range_max': 80,
|
|
||||||
'protocol': u'tcp', 'remote_group_id': sec_group_1['id'],
|
|
||||||
'remote_ip_prefix': None,
|
|
||||||
'security_group_id': secgroup['id'],
|
|
||||||
'tenant_id': secgroup['tenant_id']}
|
|
||||||
|
|
||||||
rules = []
|
|
||||||
if not default_only:
|
|
||||||
rules += [rule_tcp_80, rule_icmp, rule_group]
|
|
||||||
rules += [rule_egress_ipv4, rule_egress_ipv6]
|
|
||||||
secgroup['security_group_rules'] = rules
|
|
||||||
|
|
||||||
add_rule_to_group(sec_group_1, default_only=False)
|
|
||||||
add_rule_to_group(sec_group_2)
|
|
||||||
add_rule_to_group(sec_group_3)
|
|
||||||
|
|
||||||
groups = [sec_group_1, sec_group_2, sec_group_3]
|
|
||||||
sg_name_dict = dict([(sg['id'], sg['name']) for sg in groups])
|
|
||||||
for sg in groups:
|
|
||||||
# Neutron API
|
|
||||||
TEST.api_q_secgroups.add(sg)
|
|
||||||
for rule in sg['security_group_rules']:
|
|
||||||
TEST.api_q_secgroup_rules.add(copy.copy(rule))
|
|
||||||
# OpenStack Dashboard internaly API
|
|
||||||
TEST.q_secgroups.add(SecurityGroup(copy.deepcopy(sg), sg_name_dict))
|
|
||||||
for rule in sg['security_group_rules']:
|
|
||||||
TEST.q_secgroup_rules.add(
|
|
||||||
SecurityGroupRule(copy.copy(rule), sg_name_dict))
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# LBaaS
|
|
||||||
|
|
||||||
# 1st pool
|
|
||||||
pool_dict = {'id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
|
||||||
'tenant_id': '1',
|
|
||||||
'vip_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'pool1',
|
|
||||||
'description': 'pool description',
|
|
||||||
'subnet_id': TEST.subnets.first().id,
|
|
||||||
'protocol': 'HTTP',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'health_monitors': ['d4a0500f-db2b-4cc4-afcf-ec026febff96'],
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_pools.add(pool_dict)
|
|
||||||
TEST.pools.add(Pool(pool_dict))
|
|
||||||
|
|
||||||
# 1st vip
|
|
||||||
vip_dict = {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
|
||||||
'name': 'vip1',
|
|
||||||
'address': '10.0.0.100',
|
|
||||||
'floatip_address': '',
|
|
||||||
'other_address': '10.0.0.100',
|
|
||||||
'description': 'vip description',
|
|
||||||
'subnet_id': TEST.subnets.first().id,
|
|
||||||
'subnet': TEST.subnets.first().cidr,
|
|
||||||
'protocol_port': 80,
|
|
||||||
'protocol': pool_dict['protocol'],
|
|
||||||
'pool_id': pool_dict['id'],
|
|
||||||
'session_persistence': {'type': 'APP_COOKIE',
|
|
||||||
'cookie_name': 'jssessionid'},
|
|
||||||
'connection_limit': 10,
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_vips.add(vip_dict)
|
|
||||||
TEST.vips.add(Vip(vip_dict))
|
|
||||||
|
|
||||||
# 2nd vip
|
|
||||||
vip_dict = {'id': 'f0881d38-c3eb-4fee-9763-12de3338041d',
|
|
||||||
'name': 'vip2',
|
|
||||||
'address': '10.0.0.110',
|
|
||||||
'floatip_address': '',
|
|
||||||
'other_address': '10.0.0.110',
|
|
||||||
'description': 'vip description',
|
|
||||||
'subnet_id': TEST.subnets.first().id,
|
|
||||||
'subnet': TEST.subnets.first().cidr,
|
|
||||||
'protocol_port': 80,
|
|
||||||
'protocol': pool_dict['protocol'],
|
|
||||||
'pool_id': pool_dict['id'],
|
|
||||||
'session_persistence': {'type': 'APP_COOKIE',
|
|
||||||
'cookie_name': 'jssessionid'},
|
|
||||||
'connection_limit': 10,
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_vips.add(vip_dict)
|
|
||||||
TEST.vips.add(Vip(vip_dict))
|
|
||||||
|
|
||||||
# 1st member
|
|
||||||
member_dict = {'id': '78a46e5e-eb1a-418a-88c7-0e3f5968b08',
|
|
||||||
'tenant_id': '1',
|
|
||||||
'pool_id': pool_dict['id'],
|
|
||||||
'address': '10.0.0.11',
|
|
||||||
'protocol_port': 80,
|
|
||||||
'weight': 10,
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_members.add(member_dict)
|
|
||||||
TEST.members.add(Member(member_dict))
|
|
||||||
|
|
||||||
# 2nd member
|
|
||||||
member_dict = {'id': '41ac1f8d-6d9c-49a4-a1bf-41955e651f91',
|
|
||||||
'tenant_id': '1',
|
|
||||||
'pool_id': pool_dict['id'],
|
|
||||||
'address': '10.0.0.12',
|
|
||||||
'protocol_port': 80,
|
|
||||||
'weight': 10,
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_members.add(member_dict)
|
|
||||||
TEST.members.add(Member(member_dict))
|
|
||||||
|
|
||||||
# 2nd pool
|
|
||||||
pool_dict = {'id': '8913dde8-4915-4b90-8d3e-b95eeedb0d50',
|
|
||||||
'tenant_id': '1',
|
|
||||||
'vip_id': 'f0881d38-c3eb-4fee-9763-12de3338041d',
|
|
||||||
'name': 'pool2',
|
|
||||||
'description': 'pool description',
|
|
||||||
'subnet_id': TEST.subnets.first().id,
|
|
||||||
'protocol': 'HTTPS',
|
|
||||||
'lb_method': 'ROUND_ROBIN',
|
|
||||||
'health_monitors': ['d4a0500f-db2b-4cc4-afcf-ec026febff97'],
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_pools.add(pool_dict)
|
|
||||||
TEST.pools.add(Pool(pool_dict))
|
|
||||||
|
|
||||||
# 1st monitor
|
|
||||||
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff96',
|
|
||||||
'type': 'ping',
|
|
||||||
'delay': 10,
|
|
||||||
'timeout': 10,
|
|
||||||
'max_retries': 10,
|
|
||||||
'http_method': 'GET',
|
|
||||||
'url_path': '/',
|
|
||||||
'expected_codes': '200',
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_monitors.add(monitor_dict)
|
|
||||||
TEST.monitors.add(PoolMonitor(monitor_dict))
|
|
||||||
|
|
||||||
# 2nd monitor
|
|
||||||
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff97',
|
|
||||||
'type': 'ping',
|
|
||||||
'delay': 10,
|
|
||||||
'timeout': 10,
|
|
||||||
'max_retries': 10,
|
|
||||||
'http_method': 'GET',
|
|
||||||
'url_path': '/',
|
|
||||||
'expected_codes': '200',
|
|
||||||
'admin_state_up': True}
|
|
||||||
TEST.api_monitors.add(monitor_dict)
|
|
||||||
TEST.monitors.add(PoolMonitor(monitor_dict))
|
|
@ -1,583 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from novaclient.v1_1 import aggregates
|
|
||||||
from novaclient.v1_1 import availability_zones
|
|
||||||
from novaclient.v1_1 import certs
|
|
||||||
from novaclient.v1_1 import flavors
|
|
||||||
from novaclient.v1_1 import floating_ips
|
|
||||||
from novaclient.v1_1 import hypervisors
|
|
||||||
from novaclient.v1_1 import keypairs
|
|
||||||
from novaclient.v1_1 import quotas
|
|
||||||
from novaclient.v1_1 import security_group_rules as rules
|
|
||||||
from novaclient.v1_1 import security_groups as sec_groups
|
|
||||||
from novaclient.v1_1 import servers
|
|
||||||
from novaclient.v1_1 import services
|
|
||||||
from novaclient.v1_1 import usage
|
|
||||||
from novaclient.v1_1 import volume_snapshots as vol_snaps
|
|
||||||
from novaclient.v1_1 import volume_types
|
|
||||||
from novaclient.v1_1 import volumes
|
|
||||||
|
|
||||||
from openstack_dashboard.api.base import Quota
|
|
||||||
from openstack_dashboard.api.base import QuotaSet as QuotaSetWrapper
|
|
||||||
from openstack_dashboard.api.nova import FloatingIp as NetFloatingIp
|
|
||||||
from openstack_dashboard.usage.quotas import QuotaUsage
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
SERVER_DATA = """
|
|
||||||
{
|
|
||||||
"server": {
|
|
||||||
"OS-EXT-SRV-ATTR:instance_name": "instance-00000005",
|
|
||||||
"OS-EXT-SRV-ATTR:host": "instance-host",
|
|
||||||
"OS-EXT-STS:task_state": null,
|
|
||||||
"addresses": {
|
|
||||||
"private": [
|
|
||||||
{
|
|
||||||
"version": 4,
|
|
||||||
"addr": "10.0.0.1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"href": "%(host)s/v1.1/%(tenant_id)s/servers/%(server_id)s",
|
|
||||||
"rel": "self"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"href": "%(host)s/%(tenant_id)s/servers/%(server_id)s",
|
|
||||||
"rel": "bookmark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"image": {
|
|
||||||
"id": "%(image_id)s",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"href": "%(host)s/%(tenant_id)s/images/%(image_id)s",
|
|
||||||
"rel": "bookmark"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"OS-EXT-STS:vm_state": "active",
|
|
||||||
"flavor": {
|
|
||||||
"id": "%(flavor_id)s",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"href": "%(host)s/%(tenant_id)s/flavors/%(flavor_id)s",
|
|
||||||
"rel": "bookmark"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"id": "%(server_id)s",
|
|
||||||
"user_id": "%(user_id)s",
|
|
||||||
"OS-DCF:diskConfig": "MANUAL",
|
|
||||||
"accessIPv4": "",
|
|
||||||
"accessIPv6": "",
|
|
||||||
"progress": null,
|
|
||||||
"OS-EXT-STS:power_state": 1,
|
|
||||||
"config_drive": "",
|
|
||||||
"status": "%(status)s",
|
|
||||||
"updated": "2012-02-28T19:51:27Z",
|
|
||||||
"hostId": "c461ea283faa0ab5d777073c93b126c68139e4e45934d4fc37e403c2",
|
|
||||||
"key_name": "%(key_name)s",
|
|
||||||
"name": "%(name)s",
|
|
||||||
"created": "2012-02-28T19:51:17Z",
|
|
||||||
"tenant_id": "%(tenant_id)s",
|
|
||||||
"metadata": {"someMetaLabel": "someMetaData",
|
|
||||||
"some<b>html</b>label": "<!--",
|
|
||||||
"empty": ""}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
USAGE_DATA = """
|
|
||||||
{
|
|
||||||
"total_memory_mb_usage": 64246.89777777778,
|
|
||||||
"total_vcpus_usage": 125.48222222222223,
|
|
||||||
"total_hours": 125.48222222222223,
|
|
||||||
"total_local_gb_usage": 0,
|
|
||||||
"tenant_id": "%(tenant_id)s",
|
|
||||||
"stop": "2012-01-31 23:59:59",
|
|
||||||
"start": "2012-01-01 00:00:00",
|
|
||||||
"server_usages": [
|
|
||||||
{
|
|
||||||
"memory_mb": %(flavor_ram)s,
|
|
||||||
"uptime": 442321,
|
|
||||||
"started_at": "2012-01-26 20:38:21",
|
|
||||||
"ended_at": null,
|
|
||||||
"name": "%(instance_name)s",
|
|
||||||
"tenant_id": "%(tenant_id)s",
|
|
||||||
"state": "active",
|
|
||||||
"hours": 122.87361111111112,
|
|
||||||
"vcpus": %(flavor_vcpus)s,
|
|
||||||
"flavor": "%(flavor_name)s",
|
|
||||||
"local_gb": %(flavor_disk)s
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"memory_mb": %(flavor_ram)s,
|
|
||||||
"uptime": 9367,
|
|
||||||
"started_at": "2012-01-31 20:54:15",
|
|
||||||
"ended_at": null,
|
|
||||||
"name": "%(instance_name)s",
|
|
||||||
"tenant_id": "%(tenant_id)s",
|
|
||||||
"state": "active",
|
|
||||||
"hours": 2.608611111111111,
|
|
||||||
"vcpus": %(flavor_vcpus)s,
|
|
||||||
"flavor": "%(flavor_name)s",
|
|
||||||
"local_gb": %(flavor_disk)s
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.servers = TestDataContainer()
|
|
||||||
TEST.flavors = TestDataContainer()
|
|
||||||
TEST.keypairs = TestDataContainer()
|
|
||||||
TEST.security_groups = TestDataContainer()
|
|
||||||
TEST.security_groups_uuid = TestDataContainer()
|
|
||||||
TEST.security_group_rules = TestDataContainer()
|
|
||||||
TEST.security_group_rules_uuid = TestDataContainer()
|
|
||||||
TEST.volumes = TestDataContainer()
|
|
||||||
TEST.quotas = TestDataContainer()
|
|
||||||
TEST.quota_usages = TestDataContainer()
|
|
||||||
TEST.floating_ips = TestDataContainer()
|
|
||||||
TEST.floating_ips_uuid = TestDataContainer()
|
|
||||||
TEST.usages = TestDataContainer()
|
|
||||||
TEST.certs = TestDataContainer()
|
|
||||||
TEST.volume_snapshots = TestDataContainer()
|
|
||||||
TEST.volume_types = TestDataContainer()
|
|
||||||
TEST.availability_zones = TestDataContainer()
|
|
||||||
TEST.hypervisors = TestDataContainer()
|
|
||||||
TEST.services = TestDataContainer()
|
|
||||||
TEST.aggregates = TestDataContainer()
|
|
||||||
|
|
||||||
# Data return by novaclient.
|
|
||||||
# It is used if API layer does data conversion.
|
|
||||||
TEST.api_floating_ips = TestDataContainer()
|
|
||||||
TEST.api_floating_ips_uuid = TestDataContainer()
|
|
||||||
|
|
||||||
# Volumes
|
|
||||||
volume = volumes.Volume(volumes.VolumeManager(None),
|
|
||||||
dict(id="41023e92-8008-4c8b-8059-7f2293ff3775",
|
|
||||||
name='test_volume',
|
|
||||||
status='available',
|
|
||||||
size=40,
|
|
||||||
display_name='Volume name',
|
|
||||||
created_at='2012-04-01 10:30:00',
|
|
||||||
volume_type=None,
|
|
||||||
attachments=[]))
|
|
||||||
nameless_volume = volumes.Volume(volumes.VolumeManager(None),
|
|
||||||
dict(id="3b189ac8-9166-ac7f-90c9-16c8bf9e01ac",
|
|
||||||
name='',
|
|
||||||
status='in-use',
|
|
||||||
size=10,
|
|
||||||
display_name='',
|
|
||||||
display_description='',
|
|
||||||
device="/dev/hda",
|
|
||||||
created_at='2010-11-21 18:34:25',
|
|
||||||
volume_type='vol_type_1',
|
|
||||||
attachments=[{"id": "1", "server_id": '1',
|
|
||||||
"device": "/dev/hda"}]))
|
|
||||||
attached_volume = volumes.Volume(volumes.VolumeManager(None),
|
|
||||||
dict(id="8cba67c1-2741-6c79-5ab6-9c2bf8c96ab0",
|
|
||||||
name='my_volume',
|
|
||||||
status='in-use',
|
|
||||||
size=30,
|
|
||||||
display_name='My Volume',
|
|
||||||
display_description='',
|
|
||||||
device="/dev/hdk",
|
|
||||||
created_at='2011-05-01 11:54:33',
|
|
||||||
volume_type='vol_type_2',
|
|
||||||
attachments=[{"id": "2", "server_id": '1',
|
|
||||||
"device": "/dev/hdk"}]))
|
|
||||||
TEST.volumes.add(volume)
|
|
||||||
TEST.volumes.add(nameless_volume)
|
|
||||||
TEST.volumes.add(attached_volume)
|
|
||||||
|
|
||||||
vol_type1 = volume_types.VolumeType(volume_types.VolumeTypeManager(None),
|
|
||||||
{'id': 1,
|
|
||||||
'name': 'vol_type_1'})
|
|
||||||
vol_type2 = volume_types.VolumeType(volume_types.VolumeTypeManager(None),
|
|
||||||
{'id': 2,
|
|
||||||
'name': 'vol_type_2'})
|
|
||||||
TEST.volume_types.add(vol_type1, vol_type2)
|
|
||||||
|
|
||||||
# Flavors
|
|
||||||
flavor_1 = flavors.Flavor(flavors.FlavorManager(None),
|
|
||||||
{'id': "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
|
||||||
'name': 'm1.tiny',
|
|
||||||
'vcpus': 1,
|
|
||||||
'disk': 0,
|
|
||||||
'ram': 512,
|
|
||||||
'swap': 0,
|
|
||||||
'extra_specs': {},
|
|
||||||
'OS-FLV-EXT-DATA:ephemeral': 0})
|
|
||||||
flavor_2 = flavors.Flavor(flavors.FlavorManager(None),
|
|
||||||
{'id': "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
|
||||||
'name': 'm1.massive',
|
|
||||||
'vcpus': 1000,
|
|
||||||
'disk': 1024,
|
|
||||||
'ram': 10000,
|
|
||||||
'swap': 0,
|
|
||||||
'extra_specs': {'Trusted': True, 'foo': 'bar'},
|
|
||||||
'OS-FLV-EXT-DATA:ephemeral': 2048})
|
|
||||||
TEST.flavors.add(flavor_1, flavor_2)
|
|
||||||
|
|
||||||
# Keypairs
|
|
||||||
keypair = keypairs.Keypair(keypairs.KeypairManager(None),
|
|
||||||
dict(name='keyName'))
|
|
||||||
TEST.keypairs.add(keypair)
|
|
||||||
|
|
||||||
# Security Groups and Rules
|
|
||||||
def generate_security_groups(is_uuid=False):
|
|
||||||
|
|
||||||
def get_id(is_uuid):
|
|
||||||
global current_int_id
|
|
||||||
if is_uuid:
|
|
||||||
return str(uuid.uuid4())
|
|
||||||
else:
|
|
||||||
get_id.current_int_id += 1
|
|
||||||
return get_id.current_int_id
|
|
||||||
|
|
||||||
get_id.current_int_id = 0
|
|
||||||
|
|
||||||
sg_manager = sec_groups.SecurityGroupManager(None)
|
|
||||||
rule_manager = rules.SecurityGroupRuleManager(None)
|
|
||||||
|
|
||||||
sec_group_1 = sec_groups.SecurityGroup(sg_manager,
|
|
||||||
{"rules": [],
|
|
||||||
"tenant_id": TEST.tenant.id,
|
|
||||||
"id": get_id(is_uuid),
|
|
||||||
"name": u"default",
|
|
||||||
"description": u"default"})
|
|
||||||
sec_group_2 = sec_groups.SecurityGroup(sg_manager,
|
|
||||||
{"rules": [],
|
|
||||||
"tenant_id": TEST.tenant.id,
|
|
||||||
"id": get_id(is_uuid),
|
|
||||||
"name": u"other_group",
|
|
||||||
"description": u"NotDefault."})
|
|
||||||
sec_group_3 = sec_groups.SecurityGroup(sg_manager,
|
|
||||||
{"rules": [],
|
|
||||||
"tenant_id": TEST.tenant.id,
|
|
||||||
"id": get_id(is_uuid),
|
|
||||||
"name": u"another_group",
|
|
||||||
"description": u"NotDefault."})
|
|
||||||
|
|
||||||
rule = {'id': get_id(is_uuid),
|
|
||||||
'group': {},
|
|
||||||
'ip_protocol': u"tcp",
|
|
||||||
'from_port': u"80",
|
|
||||||
'to_port': u"80",
|
|
||||||
'parent_group_id': sec_group_1.id,
|
|
||||||
'ip_range': {'cidr': u"0.0.0.0/32"}}
|
|
||||||
|
|
||||||
icmp_rule = {'id': get_id(is_uuid),
|
|
||||||
'group': {},
|
|
||||||
'ip_protocol': u"icmp",
|
|
||||||
'from_port': u"9",
|
|
||||||
'to_port': u"5",
|
|
||||||
'parent_group_id': sec_group_1.id,
|
|
||||||
'ip_range': {'cidr': u"0.0.0.0/32"}}
|
|
||||||
|
|
||||||
group_rule = {'id': 3,
|
|
||||||
'group': {},
|
|
||||||
'ip_protocol': u"tcp",
|
|
||||||
'from_port': u"80",
|
|
||||||
'to_port': u"80",
|
|
||||||
'parent_group_id': sec_group_1.id,
|
|
||||||
'source_group_id': sec_group_1.id}
|
|
||||||
|
|
||||||
rule_obj = rules.SecurityGroupRule(rule_manager, rule)
|
|
||||||
rule_obj2 = rules.SecurityGroupRule(rule_manager, icmp_rule)
|
|
||||||
rule_obj3 = rules.SecurityGroupRule(rule_manager, group_rule)
|
|
||||||
|
|
||||||
sec_group_1.rules = [rule_obj]
|
|
||||||
sec_group_2.rules = [rule_obj]
|
|
||||||
|
|
||||||
return {"rules": [rule_obj, rule_obj2, rule_obj3],
|
|
||||||
"groups": [sec_group_1, sec_group_2, sec_group_3]}
|
|
||||||
|
|
||||||
sg_data = generate_security_groups()
|
|
||||||
TEST.security_group_rules.add(*sg_data["rules"])
|
|
||||||
TEST.security_groups.add(*sg_data["groups"])
|
|
||||||
|
|
||||||
sg_uuid_data = generate_security_groups(is_uuid=True)
|
|
||||||
TEST.security_group_rules_uuid.add(*sg_uuid_data["rules"])
|
|
||||||
TEST.security_groups_uuid.add(*sg_uuid_data["groups"])
|
|
||||||
|
|
||||||
# Quota Sets
|
|
||||||
quota_data = dict(metadata_items='1',
|
|
||||||
injected_file_content_bytes='1',
|
|
||||||
volumes='1',
|
|
||||||
gigabytes='1000',
|
|
||||||
ram=10000,
|
|
||||||
floating_ips='1',
|
|
||||||
fixed_ips='10',
|
|
||||||
instances='10',
|
|
||||||
injected_files='1',
|
|
||||||
cores='10',
|
|
||||||
security_groups='10',
|
|
||||||
security_group_rules='20')
|
|
||||||
quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data)
|
|
||||||
TEST.quotas.nova = QuotaSetWrapper(quota)
|
|
||||||
TEST.quotas.add(QuotaSetWrapper(quota))
|
|
||||||
|
|
||||||
# Quota Usages
|
|
||||||
quota_usage_data = {'gigabytes': {'used': 0,
|
|
||||||
'quota': 1000},
|
|
||||||
'instances': {'used': 0,
|
|
||||||
'quota': 10},
|
|
||||||
'ram': {'used': 0,
|
|
||||||
'quota': 10000},
|
|
||||||
'cores': {'used': 0,
|
|
||||||
'quota': 20}}
|
|
||||||
quota_usage = QuotaUsage()
|
|
||||||
for k, v in quota_usage_data.items():
|
|
||||||
quota_usage.add_quota(Quota(k, v['quota']))
|
|
||||||
quota_usage.tally(k, v['used'])
|
|
||||||
|
|
||||||
TEST.quota_usages.add(quota_usage)
|
|
||||||
|
|
||||||
# Limits
|
|
||||||
limits = {"absolute": {"maxImageMeta": 128,
|
|
||||||
"maxPersonality": 5,
|
|
||||||
"maxPersonalitySize": 10240,
|
|
||||||
"maxSecurityGroupRules": 20,
|
|
||||||
"maxSecurityGroups": 10,
|
|
||||||
"maxServerMeta": 128,
|
|
||||||
"maxTotalCores": 20,
|
|
||||||
"maxTotalFloatingIps": 10,
|
|
||||||
"maxTotalInstances": 10,
|
|
||||||
"maxTotalKeypairs": 100,
|
|
||||||
"maxTotalRAMSize": 10000,
|
|
||||||
"totalCoresUsed": 0,
|
|
||||||
"totalInstancesUsed": 0,
|
|
||||||
"totalKeyPairsUsed": 0,
|
|
||||||
"totalRAMUsed": 0,
|
|
||||||
"totalSecurityGroupsUsed": 0}}
|
|
||||||
TEST.limits = limits
|
|
||||||
|
|
||||||
# Servers
|
|
||||||
tenant3 = TEST.tenants.list()[2]
|
|
||||||
|
|
||||||
vals = {"host": "http://nova.example.com:8774",
|
|
||||||
"name": "server_1",
|
|
||||||
"status": "ACTIVE",
|
|
||||||
"tenant_id": TEST.tenants.first().id,
|
|
||||||
"user_id": TEST.user.id,
|
|
||||||
"server_id": "1",
|
|
||||||
"flavor_id": flavor_1.id,
|
|
||||||
"image_id": TEST.images.first().id,
|
|
||||||
"key_name": keypair.name}
|
|
||||||
server_1 = servers.Server(servers.ServerManager(None),
|
|
||||||
json.loads(SERVER_DATA % vals)['server'])
|
|
||||||
vals.update({"name": "server_2",
|
|
||||||
"status": "BUILD",
|
|
||||||
"server_id": "2"})
|
|
||||||
server_2 = servers.Server(servers.ServerManager(None),
|
|
||||||
json.loads(SERVER_DATA % vals)['server'])
|
|
||||||
vals.update({"name": u'\u4e91\u89c4\u5219',
|
|
||||||
"status": "ACTIVE",
|
|
||||||
"tenant_id": tenant3.id,
|
|
||||||
"server_id": "3"})
|
|
||||||
server_3 = servers.Server(servers.ServerManager(None),
|
|
||||||
json.loads(SERVER_DATA % vals)['server'])
|
|
||||||
TEST.servers.add(server_1, server_2, server_3)
|
|
||||||
|
|
||||||
# VNC Console Data
|
|
||||||
console = {u'console': {u'url': u'http://example.com:6080/vnc_auto.html',
|
|
||||||
u'type': u'novnc'}}
|
|
||||||
TEST.servers.vnc_console_data = console
|
|
||||||
# SPICE Console Data
|
|
||||||
console = {u'console': {u'url': u'http://example.com:6080/spice_auto.html',
|
|
||||||
u'type': u'spice'}}
|
|
||||||
TEST.servers.spice_console_data = console
|
|
||||||
|
|
||||||
# Floating IPs
|
|
||||||
def generate_fip(conf):
|
|
||||||
return floating_ips.FloatingIP(floating_ips.FloatingIPManager(None),
|
|
||||||
conf)
|
|
||||||
|
|
||||||
fip_1 = {'id': 1,
|
|
||||||
'fixed_ip': '10.0.0.4',
|
|
||||||
'instance_id': server_1.id,
|
|
||||||
'ip': '58.58.58.58',
|
|
||||||
'pool': 'pool1'}
|
|
||||||
fip_2 = {'id': 2,
|
|
||||||
'fixed_ip': None,
|
|
||||||
'instance_id': None,
|
|
||||||
'ip': '58.58.58.58',
|
|
||||||
'pool': 'pool2'}
|
|
||||||
TEST.api_floating_ips.add(generate_fip(fip_1), generate_fip(fip_2))
|
|
||||||
|
|
||||||
TEST.floating_ips.add(NetFloatingIp(generate_fip(fip_1)),
|
|
||||||
NetFloatingIp(generate_fip(fip_2)))
|
|
||||||
|
|
||||||
# Floating IP with UUID id (for Floating IP with Neutron Proxy)
|
|
||||||
fip_3 = {'id': str(uuid.uuid4()),
|
|
||||||
'fixed_ip': '10.0.0.4',
|
|
||||||
'instance_id': server_1.id,
|
|
||||||
'ip': '58.58.58.58',
|
|
||||||
'pool': 'pool1'}
|
|
||||||
fip_4 = {'id': str(uuid.uuid4()),
|
|
||||||
'fixed_ip': None,
|
|
||||||
'instance_id': None,
|
|
||||||
'ip': '58.58.58.58',
|
|
||||||
'pool': 'pool2'}
|
|
||||||
TEST.api_floating_ips_uuid.add(generate_fip(fip_3), generate_fip(fip_4))
|
|
||||||
|
|
||||||
TEST.floating_ips_uuid.add(NetFloatingIp(generate_fip(fip_3)),
|
|
||||||
NetFloatingIp(generate_fip(fip_4)))
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
usage_vals = {"tenant_id": TEST.tenant.id,
|
|
||||||
"instance_name": server_1.name,
|
|
||||||
"flavor_name": flavor_1.name,
|
|
||||||
"flavor_vcpus": flavor_1.vcpus,
|
|
||||||
"flavor_disk": flavor_1.disk,
|
|
||||||
"flavor_ram": flavor_1.ram}
|
|
||||||
usage_obj = usage.Usage(usage.UsageManager(None),
|
|
||||||
json.loads(USAGE_DATA % usage_vals))
|
|
||||||
TEST.usages.add(usage_obj)
|
|
||||||
|
|
||||||
usage_2_vals = {"tenant_id": tenant3.id,
|
|
||||||
"instance_name": server_3.name,
|
|
||||||
"flavor_name": flavor_1.name,
|
|
||||||
"flavor_vcpus": flavor_1.vcpus,
|
|
||||||
"flavor_disk": flavor_1.disk,
|
|
||||||
"flavor_ram": flavor_1.ram}
|
|
||||||
usage_obj_2 = usage.Usage(usage.UsageManager(None),
|
|
||||||
json.loads(USAGE_DATA % usage_2_vals))
|
|
||||||
TEST.usages.add(usage_obj_2)
|
|
||||||
|
|
||||||
volume_snapshot = vol_snaps.Snapshot(vol_snaps.SnapshotManager(None),
|
|
||||||
{'id': '40f3fabf-3613-4f5e-90e5-6c9a08333fc3',
|
|
||||||
'display_name': 'test snapshot',
|
|
||||||
'display_description': 'vol snap!',
|
|
||||||
'size': 40,
|
|
||||||
'status': 'available',
|
|
||||||
'volume_id': '41023e92-8008-4c8b-8059-7f2293ff3775'})
|
|
||||||
TEST.volume_snapshots.add(volume_snapshot)
|
|
||||||
|
|
||||||
cert_data = {'private_key': 'private',
|
|
||||||
'data': 'certificate_data'}
|
|
||||||
certificate = certs.Certificate(certs.CertificateManager(None), cert_data)
|
|
||||||
TEST.certs.add(certificate)
|
|
||||||
|
|
||||||
# Availability Zones
|
|
||||||
TEST.availability_zones.add(
|
|
||||||
availability_zones.AvailabilityZone(
|
|
||||||
availability_zones.AvailabilityZoneManager(None),
|
|
||||||
{'zoneName': 'nova', 'zoneState': {'available': True}}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# hypervisors
|
|
||||||
hypervisor_1 = hypervisors.Hypervisor(hypervisors.HypervisorManager(None),
|
|
||||||
{
|
|
||||||
"service": {"host": "devstack001", "id": 3},
|
|
||||||
"vcpus_used": 1,
|
|
||||||
"hypervisor_type": "QEMU",
|
|
||||||
"local_gb_used": 20,
|
|
||||||
"hypervisor_hostname": "devstack001",
|
|
||||||
"memory_mb_used": 1500,
|
|
||||||
"memory_mb": 2000,
|
|
||||||
"current_workload": 0,
|
|
||||||
"vcpus": 1,
|
|
||||||
"cpu_info": '{"vendor": "Intel", "model": "core2duo",'
|
|
||||||
'"arch": "x86_64", "features": ["lahf_lm"'
|
|
||||||
', "rdtscp"], "topology": {"cores": 1, "t'
|
|
||||||
'hreads": 1, "sockets": 1}}',
|
|
||||||
"running_vms": 1,
|
|
||||||
"free_disk_gb": 9,
|
|
||||||
"hypervisor_version": 1002000,
|
|
||||||
"disk_available_least": 6,
|
|
||||||
"local_gb": 29,
|
|
||||||
"free_ram_mb": 500,
|
|
||||||
"id": 1
|
|
||||||
}
|
|
||||||
)
|
|
||||||
TEST.hypervisors.add(hypervisor_1)
|
|
||||||
|
|
||||||
# Services
|
|
||||||
service_1 = services.Service(services.ServiceManager(None),
|
|
||||||
{
|
|
||||||
"status": "enabled",
|
|
||||||
"binary": "nova-conductor",
|
|
||||||
"zone": "internal",
|
|
||||||
"state": "up",
|
|
||||||
"updated_at": "2013-07-08T05:21:00.000000",
|
|
||||||
"host": "devstack001",
|
|
||||||
"disabled_reason": None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
service_2 = services.Service(services.ServiceManager(None),
|
|
||||||
{
|
|
||||||
"status": "enabled",
|
|
||||||
"binary": "nova-compute",
|
|
||||||
"zone": "nova",
|
|
||||||
"state": "up",
|
|
||||||
"updated_at": "2013-07-08T05:20:51.000000",
|
|
||||||
"host": "devstack001",
|
|
||||||
"disabled_reason": None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
TEST.services.add(service_1)
|
|
||||||
TEST.services.add(service_2)
|
|
||||||
|
|
||||||
# Aggregates
|
|
||||||
aggregate_1 = aggregates.Aggregate(aggregates.AggregateManager(None),
|
|
||||||
{
|
|
||||||
"name": "foo",
|
|
||||||
"availability_zone": None,
|
|
||||||
"deleted": 0,
|
|
||||||
"created_at": "2013-07-04T13:34:38.000000",
|
|
||||||
"updated_at": None,
|
|
||||||
"hosts": ["foo", "bar"],
|
|
||||||
"deleted_at": None,
|
|
||||||
"id": 1,
|
|
||||||
"metadata": {
|
|
||||||
"foo": "testing",
|
|
||||||
"bar": "testing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
aggregate_2 = aggregates.Aggregate(aggregates.AggregateManager(None),
|
|
||||||
{
|
|
||||||
"name": "bar",
|
|
||||||
"availability_zone": "testing",
|
|
||||||
"deleted": 0,
|
|
||||||
"created_at": "2013-07-04T13:34:38.000000",
|
|
||||||
"updated_at": None,
|
|
||||||
"hosts": ["foo", "bar"],
|
|
||||||
"deleted_at": None,
|
|
||||||
"id": 2,
|
|
||||||
"metadata": {
|
|
||||||
"foo": "testing",
|
|
||||||
"bar": "testing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
TEST.aggregates.add(aggregate_1)
|
|
||||||
TEST.aggregates.add(aggregate_2)
|
|
@ -1,40 +0,0 @@
|
|||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from openstack_dashboard.api import swift
|
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data.utils import TestDataContainer
|
|
||||||
|
|
||||||
|
|
||||||
def data(TEST):
|
|
||||||
TEST.containers = TestDataContainer()
|
|
||||||
TEST.objects = TestDataContainer()
|
|
||||||
|
|
||||||
container_1 = swift.Container(dict(name=u"container_one\u6346"))
|
|
||||||
container_2 = swift.Container(dict(name=u"container_two\u6346"))
|
|
||||||
TEST.containers.add(container_1, container_2)
|
|
||||||
|
|
||||||
object_dict = {"name": u"test_object\u6346",
|
|
||||||
"content_type": u"text/plain",
|
|
||||||
"bytes": 128,
|
|
||||||
"last_modified": None,
|
|
||||||
"hash": u"object_hash"}
|
|
||||||
obj_dicts = [object_dict]
|
|
||||||
obj_data = "Fake Data"
|
|
||||||
|
|
||||||
for obj_dict in obj_dicts:
|
|
||||||
swift_object = swift.StorageObject(obj_dict,
|
|
||||||
container_1.name,
|
|
||||||
data=obj_data)
|
|
||||||
TEST.objects.add(swift_object)
|
|
@ -13,6 +13,9 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from openstack_dashboard.test.test_data import utils
|
||||||
|
|
||||||
|
|
||||||
def load_test_data(load_onto=None):
|
def load_test_data(load_onto=None):
|
||||||
from openstack_dashboard.test.test_data import cinder_data
|
from openstack_dashboard.test.test_data import cinder_data
|
||||||
from openstack_dashboard.test.test_data import glance_data
|
from openstack_dashboard.test.test_data import glance_data
|
||||||
@ -39,94 +42,4 @@ def load_test_data(load_onto=None):
|
|||||||
data_func(load_onto)
|
data_func(load_onto)
|
||||||
return load_onto
|
return load_onto
|
||||||
else:
|
else:
|
||||||
return TestData(*loaders)
|
return utils.TestData(*loaders)
|
||||||
|
|
||||||
|
|
||||||
class TestData(object):
|
|
||||||
"""
|
|
||||||
Holder object for test data. Any functions passed to the init method
|
|
||||||
will be called with the ``TestData`` object as their only argument. They
|
|
||||||
can then load data onto the object as desired.
|
|
||||||
|
|
||||||
The idea is to use the instantiated object like this::
|
|
||||||
|
|
||||||
>>> import glance_data
|
|
||||||
>>> TEST = TestData(glance_data.data)
|
|
||||||
>>> TEST.images.list()
|
|
||||||
... [<Image: visible_image>, <Image: invisible_image>]
|
|
||||||
>>> TEST.images.first()
|
|
||||||
... <Image: visible_image>
|
|
||||||
|
|
||||||
You can load as little or as much data as you like as long as the loaders
|
|
||||||
don't conflict with each other.
|
|
||||||
|
|
||||||
See the :class:`~horizon.tests.test_data.utils.TestDataContainer` class
|
|
||||||
for a list of available methods.
|
|
||||||
"""
|
|
||||||
def __init__(self, *args):
|
|
||||||
for data_func in args:
|
|
||||||
data_func(self)
|
|
||||||
|
|
||||||
|
|
||||||
class TestDataContainer(object):
|
|
||||||
""" A container for test data objects.
|
|
||||||
|
|
||||||
The behavior of this class is meant to mimic a "manager" class, which
|
|
||||||
has convenient shortcuts for common actions like "list", "filter", "get",
|
|
||||||
and "add".
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self._objects = []
|
|
||||||
|
|
||||||
def add(self, *args):
|
|
||||||
""" Add a new object to this container.
|
|
||||||
|
|
||||||
Generally this method should only be used during data loading, since
|
|
||||||
adding data during a test can affect the results of other tests.
|
|
||||||
"""
|
|
||||||
for obj in args:
|
|
||||||
if obj not in self._objects:
|
|
||||||
self._objects.append(obj)
|
|
||||||
|
|
||||||
def list(self):
|
|
||||||
""" Returns a list of all objects in this container. """
|
|
||||||
return self._objects
|
|
||||||
|
|
||||||
def filter(self, filtered=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns objects in this container whose attributes match the given
|
|
||||||
keyword arguments.
|
|
||||||
"""
|
|
||||||
if filtered is None:
|
|
||||||
filtered = self._objects
|
|
||||||
try:
|
|
||||||
key, value = kwargs.popitem()
|
|
||||||
except KeyError:
|
|
||||||
# We're out of filters, return
|
|
||||||
return filtered
|
|
||||||
|
|
||||||
def get_match(obj):
|
|
||||||
return hasattr(obj, key) and getattr(obj, key) == value
|
|
||||||
|
|
||||||
return self.filter(filtered=filter(get_match, filtered), **kwargs)
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the single object in this container whose attributes match
|
|
||||||
the given keyword arguments. An error will be raised if the arguments
|
|
||||||
provided don't return exactly one match.
|
|
||||||
"""
|
|
||||||
matches = self.filter(**kwargs)
|
|
||||||
if not matches:
|
|
||||||
raise Exception("No matches found.")
|
|
||||||
elif len(matches) > 1:
|
|
||||||
raise Exception("Multiple matches found.")
|
|
||||||
else:
|
|
||||||
return matches.pop()
|
|
||||||
|
|
||||||
def first(self):
|
|
||||||
""" Returns the first object from this container. """
|
|
||||||
return self._objects[0]
|
|
||||||
|
|
||||||
def count(self):
|
|
||||||
return len(self._objects)
|
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright (c) 2012 OpenStack, LLC.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from os import path
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorPageTests(test.TestCase):
|
|
||||||
""" Tests for error pages """
|
|
||||||
urls = 'openstack_dashboard.test.error_pages_urls'
|
|
||||||
|
|
||||||
def test_500_error(self):
|
|
||||||
TEMPLATE_DIRS = (path.join(settings.ROOT_PATH, 'templates'),)
|
|
||||||
with self.settings(TEMPLATE_DIRS=TEMPLATE_DIRS):
|
|
||||||
response = self.client.get('/500/')
|
|
||||||
self.assertTrue('Server error' in response.content)
|
|
@ -1,187 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Copyright 2012 Nebula, Inc.
|
|
||||||
# Copyright (c) 2012 X.commerce, a business unit of eBay 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.
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django import http
|
|
||||||
from mox import IsA
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.api import cinder
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaTests(test.APITestCase):
|
|
||||||
|
|
||||||
def get_usages(self, with_volume=True):
|
|
||||||
quotas = {'injected_file_content_bytes': {'quota': 1},
|
|
||||||
'metadata_items': {'quota': 1},
|
|
||||||
'injected_files': {'quota': 1},
|
|
||||||
'security_groups': {'quota': 10},
|
|
||||||
'security_group_rules': {'quota': 20},
|
|
||||||
'fixed_ips': {'quota': 10},
|
|
||||||
'ram': {'available': 8976, 'used': 1024, 'quota': 10000},
|
|
||||||
'floating_ips': {'available': 0, 'used': 2, 'quota': 1},
|
|
||||||
'instances': {'available': 8, 'used': 2, 'quota': 10},
|
|
||||||
'cores': {'available': 8, 'used': 2, 'quota': 10}}
|
|
||||||
if with_volume:
|
|
||||||
quotas.update({'volumes': {'available': 0, 'used': 3, 'quota': 1},
|
|
||||||
'snapshots': {'available': 0, 'used': 3,
|
|
||||||
'quota': 1},
|
|
||||||
'gigabytes': {'available': 920, 'used': 80,
|
|
||||||
'quota': 1000}})
|
|
||||||
return quotas
|
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('server_list',
|
|
||||||
'flavor_list',
|
|
||||||
'tenant_quota_get',),
|
|
||||||
api.network: ('tenant_floating_ip_list',),
|
|
||||||
quotas: ('is_service_enabled',),
|
|
||||||
cinder: ('volume_list', 'volume_snapshot_list',
|
|
||||||
'tenant_quota_get',)})
|
|
||||||
def test_tenant_quota_usages(self):
|
|
||||||
servers = [s for s in self.servers.list()
|
|
||||||
if s.tenant_id == self.request.user.tenant_id]
|
|
||||||
|
|
||||||
quotas.is_service_enabled(IsA(http.HttpRequest),
|
|
||||||
'volume').AndReturn(True)
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.flavors.list())
|
|
||||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(self.quotas.first())
|
|
||||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.floating_ips.list())
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn([servers, False])
|
|
||||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.volumes.list())
|
|
||||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.snapshots.list())
|
|
||||||
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(self.cinder_quotas.first())
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
quota_usages = quotas.tenant_quota_usages(self.request)
|
|
||||||
expected_output = self.get_usages()
|
|
||||||
|
|
||||||
# Compare internal structure of usages to expected.
|
|
||||||
self.assertEquals(quota_usages.usages, expected_output)
|
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('server_list',
|
|
||||||
'flavor_list',
|
|
||||||
'tenant_quota_get',),
|
|
||||||
api.network: ('tenant_floating_ip_list',),
|
|
||||||
quotas: ('is_service_enabled',)})
|
|
||||||
def test_tenant_quota_usages_without_volume(self):
|
|
||||||
servers = [s for s in self.servers.list()
|
|
||||||
if s.tenant_id == self.request.user.tenant_id]
|
|
||||||
|
|
||||||
quotas.is_service_enabled(IsA(http.HttpRequest),
|
|
||||||
'volume').AndReturn(False)
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.flavors.list())
|
|
||||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(self.quotas.first())
|
|
||||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.floating_ips.list())
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn([servers, False])
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
quota_usages = quotas.tenant_quota_usages(self.request)
|
|
||||||
expected_output = self.get_usages(with_volume=False)
|
|
||||||
|
|
||||||
# Compare internal structure of usages to expected.
|
|
||||||
self.assertEquals(quota_usages.usages, expected_output)
|
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('server_list',
|
|
||||||
'flavor_list',
|
|
||||||
'tenant_quota_get',),
|
|
||||||
api.network: ('tenant_floating_ip_list',),
|
|
||||||
quotas: ('is_service_enabled',)})
|
|
||||||
def test_tenant_quota_usages_no_instances_running(self):
|
|
||||||
quotas.is_service_enabled(IsA(http.HttpRequest),
|
|
||||||
'volume').AndReturn(False)
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.flavors.list())
|
|
||||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(self.quotas.first())
|
|
||||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn([])
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest)).AndReturn([[], False])
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
quota_usages = quotas.tenant_quota_usages(self.request)
|
|
||||||
expected_output = self.get_usages(with_volume=False)
|
|
||||||
|
|
||||||
expected_output.update({
|
|
||||||
'ram': {'available': 10000, 'used': 0, 'quota': 10000},
|
|
||||||
'floating_ips': {'available': 1, 'used': 0, 'quota': 1},
|
|
||||||
'instances': {'available': 10, 'used': 0, 'quota': 10},
|
|
||||||
'cores': {'available': 10, 'used': 0, 'quota': 10}})
|
|
||||||
|
|
||||||
# Compare internal structure of usages to expected.
|
|
||||||
self.assertEquals(quota_usages.usages, expected_output)
|
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('server_list',
|
|
||||||
'flavor_list',
|
|
||||||
'tenant_quota_get',),
|
|
||||||
api.network: ('tenant_floating_ip_list',),
|
|
||||||
quotas: ('is_service_enabled',),
|
|
||||||
cinder: ('volume_list', 'volume_snapshot_list',
|
|
||||||
'tenant_quota_get',)})
|
|
||||||
def test_tenant_quota_usages_unlimited_quota(self):
|
|
||||||
inf_quota = self.quotas.first()
|
|
||||||
inf_quota['ram'] = -1
|
|
||||||
servers = [s for s in self.servers.list()
|
|
||||||
if s.tenant_id == self.request.user.tenant_id]
|
|
||||||
|
|
||||||
quotas.is_service_enabled(IsA(http.HttpRequest),
|
|
||||||
'volume').AndReturn(True)
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.flavors.list())
|
|
||||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(inf_quota)
|
|
||||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.floating_ips.list())
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn([servers, False])
|
|
||||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.volumes.list())
|
|
||||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.snapshots.list())
|
|
||||||
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
|
||||||
.AndReturn(self.cinder_quotas.first())
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
quota_usages = quotas.tenant_quota_usages(self.request)
|
|
||||||
expected_output = self.get_usages()
|
|
||||||
expected_output.update({'ram': {'available': float("inf"),
|
|
||||||
'used': 1024,
|
|
||||||
'quota': float("inf")}})
|
|
||||||
|
|
||||||
# Compare internal structure of usages to expected.
|
|
||||||
self.assertEquals(quota_usages.usages, expected_output)
|
|
@ -1,24 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 Nebula, 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.
|
|
||||||
|
|
||||||
from horizon.test import helpers as test
|
|
||||||
|
|
||||||
|
|
||||||
class BrowserTests(test.SeleniumTestCase):
|
|
||||||
def test_splash(self):
|
|
||||||
self.selenium.get(self.live_server_url)
|
|
||||||
button = self.selenium.find_element_by_tag_name("button")
|
|
||||||
self.assertEqual(button.text, "Sign In")
|
|
@ -1,42 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
from openstack_dashboard.utils.filters import get_int_or_uuid
|
|
||||||
|
|
||||||
|
|
||||||
class UtilsFilterTests(test.TestCase):
|
|
||||||
def test_accept_valid_integer(self):
|
|
||||||
val = 100
|
|
||||||
ret = get_int_or_uuid(val)
|
|
||||||
self.assertEqual(val, ret)
|
|
||||||
|
|
||||||
def test_accept_valid_integer_string(self):
|
|
||||||
val = '100'
|
|
||||||
ret = get_int_or_uuid(val)
|
|
||||||
self.assertEqual(int(val), ret)
|
|
||||||
|
|
||||||
def test_accept_valid_uuid(self):
|
|
||||||
val = str(uuid.uuid4())
|
|
||||||
ret = get_int_or_uuid(val)
|
|
||||||
self.assertEqual(val, ret)
|
|
||||||
|
|
||||||
def test_reject_random_string(self):
|
|
||||||
val = '55WbJTpJDf'
|
|
||||||
self.assertRaises(ValueError, get_int_or_uuid, val)
|
|
Loading…
x
Reference in New Issue
Block a user