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
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import copy
|
||||
import logging
|
||||
from operator import attrgetter
|
||||
import sys
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import urlresolvers
|
||||
from django import forms
|
||||
from django.http import HttpResponse
|
||||
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.html import escape
|
||||
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.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import conf
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon.tables.actions import FilterAction
|
||||
from horizon.tables.actions import LinkAction
|
||||
from horizon.utils import html
|
||||
from horizon.tables import base as horizon_tables
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -48,152 +40,7 @@ PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE]
|
||||
STRING_SEPARATOR = "__"
|
||||
|
||||
|
||||
class Column(html.HTMLElement):
|
||||
""" 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),
|
||||
)
|
||||
class Column(horizon_tables.Column):
|
||||
|
||||
def __init__(self, transform, verbose_name=None, sortable=True,
|
||||
link=None, allowed_data_types=[], hidden=False, attrs=None,
|
||||
@ -203,237 +50,17 @@ class Column(html.HTMLElement):
|
||||
# FIXME: Added for TableStep:
|
||||
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_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:
|
||||
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 = []
|
||||
class Row(horizon_tables.Row):
|
||||
|
||||
def load_cells(self, datum=None):
|
||||
"""
|
||||
@ -501,7 +128,7 @@ class Row(html.HTMLElement):
|
||||
table._data_cache[column][table.get_object_id(datum)] = data
|
||||
else:
|
||||
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))
|
||||
self.cells = SortedDict(cells)
|
||||
|
||||
@ -524,328 +151,18 @@ class Row(html.HTMLElement):
|
||||
if display_name:
|
||||
self.attrs['data-display'] = escape(display_name)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.id)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.cells.values())
|
||||
class DataTableOptions(horizon_tables.DataTableOptions):
|
||||
|
||||
@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):
|
||||
self.name = getattr(options, 'name', self.__class__.__name__)
|
||||
verbose_name = getattr(options, 'verbose_name', None) \
|
||||
or self.name.title()
|
||||
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', [])
|
||||
super(DataTableOptions, self).__init__(options)
|
||||
|
||||
# FIXME: TableStep
|
||||
self.row_class = getattr(options, 'row_class', Row)
|
||||
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,
|
||||
'multi_select_name',
|
||||
'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):
|
||||
|
@ -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
|
||||
# under the License.
|
||||
|
||||
from functools import wraps
|
||||
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 import http
|
||||
from django.test.client import RequestFactory
|
||||
from django.utils.importlib import import_module
|
||||
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 swiftclient import client as swift_client
|
||||
from tuskarclient.v1 import client as tuskar_client
|
||||
|
||||
import httplib2
|
||||
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 openstack_dashboard.test import helpers as openstack_dashboard_helpers
|
||||
from tuskar_ui import api as tuskar_api
|
||||
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={}):
|
||||
if not isinstance(stubs_to_create, dict):
|
||||
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
|
||||
return openstack_dashboard_helpers.create_stubs(stubs_to_create)
|
||||
|
||||
|
||||
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
||||
"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
|
||||
numerous additional features:
|
||||
@ -118,129 +59,25 @@ class TestCase(horizon_helpers.TestCase):
|
||||
* Several handy additional assertion methods.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
|
||||
# load tuskar-specific test data
|
||||
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
|
||||
for testing admin-only views and functionality.
|
||||
"""
|
||||
def setActiveUser(self, *args, **kwargs):
|
||||
if "roles" not in kwargs:
|
||||
kwargs['roles'] = [self.roles.admin._info]
|
||||
super(BaseAdminViewTests, self).setActiveUser(*args, **kwargs)
|
||||
def setUp(self):
|
||||
super(BaseAdminViewTests, self).setUp()
|
||||
|
||||
def setSessionValues(self, **kwargs):
|
||||
settings.SESSION_ENGINE = 'django.contrib.sessions.backends.file'
|
||||
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
|
||||
# load tuskar-specific test data
|
||||
load_test_data(self)
|
||||
|
||||
|
||||
class APITestCase(TestCase):
|
||||
class APITestCase(openstack_dashboard_helpers.APITestCase):
|
||||
"""
|
||||
The ``APITestCase`` class is for use with tests which deal with the
|
||||
underlying clients rather than stubbing out the
|
||||
@ -249,102 +86,23 @@ class APITestCase(TestCase):
|
||||
def setUp(self):
|
||||
super(APITestCase, self).setUp()
|
||||
|
||||
def fake_keystoneclient(request, admin=False):
|
||||
"""
|
||||
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()
|
||||
# load tuskar-specfic test data
|
||||
load_test_data(self)
|
||||
|
||||
# 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_baremetalclient = tuskar_api.baremetalclient
|
||||
|
||||
# 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.baremetalclient = lambda request:\
|
||||
self.stub_baremetalclient()
|
||||
|
||||
def tearDown(self):
|
||||
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.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):
|
||||
if not hasattr(self, "tuskarclient"):
|
||||
self.mox.StubOutWithMock(tuskar_client, 'Client')
|
||||
@ -355,55 +113,3 @@ class APITestCase(TestCase):
|
||||
if not hasattr(self, "baremetalclient"):
|
||||
self.baremetalclient = baremetal.BareMetalNodeManager(None)
|
||||
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
|
||||
# under the License.
|
||||
|
||||
from cinderclient import exceptions as cinder_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
|
||||
from openstack_dashboard.test.test_data import 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):
|
||||
TEST.exceptions = TestDataContainer()
|
||||
|
||||
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)
|
||||
TEST.exceptions = exceptions.data
|
||||
|
||||
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.
|
||||
|
||||
|
||||
from openstack_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
def load_test_data(load_onto=None):
|
||||
from openstack_dashboard.test.test_data import cinder_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)
|
||||
return load_onto
|
||||
else:
|
||||
return 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)
|
||||
return utils.TestData(*loaders)
|
||||
|
@ -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