Merge release v2.4.6

This commit is contained in:
Jeremy Stretch
2018-10-10 09:36:51 -04:00
23 changed files with 508 additions and 21 deletions

View File

@@ -54,6 +54,16 @@ class ProviderTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_providers_brief(self):
url = reverse('circuits-api:provider-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_provider(self):
data = {
@@ -145,6 +155,16 @@ class CircuitTypeTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_circuittypes_brief(self):
url = reverse('circuits-api:circuittype-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_circuittype(self):
data = {
@@ -214,6 +234,16 @@ class CircuitTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_circuits_brief(self):
url = reverse('circuits-api:circuit-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['cid', 'id', 'url']
)
def test_create_circuit(self):
data = {

View File

@@ -490,6 +490,15 @@ class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
fields = ['id', 'device', 'name', 'cs_port', 'connection_status', 'tags']
class NestedConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
device = NestedDeviceSerializer(read_only=True)
class Meta:
model = ConsolePort
fields = ['id', 'url', 'device', 'name']
#
# Power outlets
#
@@ -527,6 +536,15 @@ class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
fields = ['id', 'device', 'name', 'power_outlet', 'connection_status', 'tags']
class NestedPowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
device = NestedDeviceSerializer(read_only=True)
class Meta:
model = PowerPort
fields = ['id', 'url', 'device', 'name']
#
# Interfaces
#
@@ -650,10 +668,11 @@ class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
class NestedDeviceBaySerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
device = NestedDeviceSerializer(read_only=True)
class Meta:
model = DeviceBay
fields = ['id', 'url', 'name']
fields = ['id', 'url', 'device', 'name']
#

View File

@@ -236,6 +236,11 @@ class DeviceViewSet(CustomFieldModelViewSet):
"""
if self.action == 'retrieve':
return serializers.DeviceWithConfigContextSerializer
request = self.get_serializer_context()['request']
if request.query_params.get('brief', False):
return serializers.NestedDeviceSerializer
return serializers.DeviceSerializer
@action(detail=True, url_path='napalm')

View File

@@ -1370,7 +1370,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
})
# Validate manufacturer/platform
if self.device_type and self.platform:
if hasattr(self, 'device_type') and self.platform:
if self.platform.manufacturer and self.platform.manufacturer != self.device_type.manufacturer:
raise ValidationError({
'platform': "The assigned platform is limited to {} device types, but this device's type belongs "

View File

@@ -42,6 +42,16 @@ class RegionTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_regions_brief(self):
url = reverse('dcim-api:region-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_region(self):
data = {
@@ -156,6 +166,16 @@ class SiteTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_sites_brief(self):
url = reverse('dcim-api:site-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_site(self):
data = {
@@ -260,6 +280,16 @@ class RackGroupTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_rackgroups_brief(self):
url = reverse('dcim-api:rackgroup-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_rackgroup(self):
data = {
@@ -358,6 +388,16 @@ class RackRoleTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_rackroles_brief(self):
url = reverse('dcim-api:rackrole-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_rackrole(self):
data = {
@@ -475,6 +515,16 @@ class RackTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_racks_brief(self):
url = reverse('dcim-api:rack-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'name', 'url']
)
def test_create_rack(self):
data = {
@@ -691,6 +741,16 @@ class ManufacturerTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_manufacturers_brief(self):
url = reverse('dcim-api:manufacturer-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_manufacturer(self):
data = {
@@ -790,6 +850,16 @@ class DeviceTypeTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_devicetypes_brief(self):
url = reverse('dcim-api:devicetype-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'manufacturer', 'model', 'slug', 'url']
)
def test_create_devicetype(self):
data = {
@@ -1494,6 +1564,16 @@ class DeviceRoleTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_deviceroles_brief(self):
url = reverse('dcim-api:devicerole-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_devicerole(self):
data = {
@@ -1592,6 +1672,16 @@ class PlatformTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_platforms_brief(self):
url = reverse('dcim-api:platform-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_platform(self):
data = {
@@ -1720,6 +1810,16 @@ class DeviceTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_devices_brief(self):
url = reverse('dcim-api:device-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'name', 'url']
)
def test_create_device(self):
data = {
@@ -1846,6 +1946,16 @@ class ConsolePortTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_consoleports_brief(self):
url = reverse('dcim-api:consoleport-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_consoleport(self):
data = {
@@ -1951,6 +2061,16 @@ class ConsoleServerPortTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_consoleserverports_brief(self):
url = reverse('dcim-api:consoleserverport-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_consoleserverport(self):
data = {
@@ -2052,6 +2172,16 @@ class PowerPortTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_powerports_brief(self):
url = reverse('dcim-api:powerport-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_powerport(self):
data = {
@@ -2157,6 +2287,16 @@ class PowerOutletTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_poweroutlets_brief(self):
url = reverse('dcim-api:poweroutlet-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_poweroutlet(self):
data = {
@@ -2283,6 +2423,16 @@ class InterfaceTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_interfaces_brief(self):
url = reverse('dcim-api:interface-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_interface(self):
data = {
@@ -2454,6 +2604,16 @@ class DeviceBayTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_devicebays_brief(self):
url = reverse('dcim-api:devicebay-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['device', 'id', 'name', 'url']
)
def test_create_devicebay(self):
data = {
@@ -2776,6 +2936,16 @@ class InterfaceConnectionTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_interfaceconnections_brief(self):
url = reverse('dcim-api:interfaceconnection-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['connection_status', 'id', 'url']
)
def test_create_interfaceconnection(self):
data = {
@@ -2971,6 +3141,16 @@ class VirtualChassisTest(APITestCase):
self.assertEqual(response.data['count'], 2)
def test_list_virtualchassis_brief(self):
url = reverse('dcim-api:virtualchassis-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'url']
)
def test_create_virtualchassis(self):
data = {

View File

@@ -32,6 +32,16 @@ class VRFTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_vrfs_brief(self):
url = reverse('ipam-api:vrf-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'rd', 'url']
)
def test_create_vrf(self):
data = {
@@ -123,6 +133,16 @@ class RIRTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_rirs_brief(self):
url = reverse('ipam-api:rir-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_rir(self):
data = {
@@ -216,6 +236,16 @@ class AggregateTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_aggregates_brief(self):
url = reverse('ipam-api:aggregate-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['family', 'id', 'prefix', 'url']
)
def test_create_aggregate(self):
data = {
@@ -307,6 +337,16 @@ class RoleTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_roles_brief(self):
url = reverse('ipam-api:role-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_role(self):
data = {
@@ -395,13 +435,23 @@ class PrefixTest(APITestCase):
self.assertEqual(response.data['prefix'], str(self.prefix1.prefix))
def test_list_prefixs(self):
def test_list_prefixes(self):
url = reverse('ipam-api:prefix-list')
response = self.client.get(url, **self.header)
self.assertEqual(response.data['count'], 3)
def test_list_prefixes_brief(self):
url = reverse('ipam-api:prefix-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['family', 'id', 'prefix', 'url']
)
def test_create_prefix(self):
data = {
@@ -628,6 +678,16 @@ class IPAddressTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_ipaddresses_brief(self):
url = reverse('ipam-api:ipaddress-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['address', 'family', 'id', 'url']
)
def test_create_ipaddress(self):
data = {
@@ -716,6 +776,16 @@ class VLANGroupTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_vlangroups_brief(self):
url = reverse('ipam-api:vlangroup-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_vlangroup(self):
data = {
@@ -807,6 +877,16 @@ class VLANTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_vlans_brief(self):
url = reverse('ipam-api:vlan-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'name', 'url', 'vid']
)
def test_create_vlan(self):
data = {

View File

@@ -989,6 +989,9 @@ class ServiceCreateView(PermissionRequiredMixin, ObjectEditView):
obj.virtual_machine = get_object_or_404(VirtualMachine, pk=url_kwargs['virtualmachine'])
return obj
def get_return_url(self, request, service):
return service.parent.get_absolute_url()
class ServiceEditView(ServiceCreateView):
permission_required = 'ipam.change_service'

View File

@@ -82,7 +82,7 @@ $(document).ready(function() {
}
if ($(parent).val() || $(parent).attr('nullable') == 'true') {
var api_url = child_field.attr('api-url');
var api_url = child_field.attr('api-url') + '&limit=0&brief=1';
var disabled_indicator = child_field.attr('disabled-indicator');
var initial_value = child_field.attr('initial');
var display_field = child_field.attr('display-field') || 'name';

View File

@@ -71,6 +71,16 @@ class SecretRoleTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_secretroles_brief(self):
url = reverse('secrets-api:secretrole-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_secretrole(self):
data = {

View File

@@ -64,8 +64,10 @@ $(document).ready(function() {
}
// Clean up hostnames/interfaces learned via LLDP
var lldp_device = neighbor['hostname'].split(".")[0]; // Strip off any trailing domain name
var lldp_interface = neighbor['port'].split(".")[0]; // Strip off any trailing subinterface ID
var neighbor_host = neighbor['hostname'] || ""; // sanitize hostname if it's null to avoid breaking the split func
var neighbor_port = neighbor['port'] || ""; // sanitize port if it's null to avoid breaking the split func
var lldp_device = neighbor_host.split(".")[0]; // Strip off any trailing domain name
var lldp_interface = neighbor_port.split(".")[0]; // Strip off any trailing subinterface ID
// Add LLDP neighbors to table
row.children('td.device').html(lldp_device);

View File

@@ -10,8 +10,12 @@
<div class="panel panel-{% if token.is_expired %}danger{% else %}default{% endif %}">
<div class="panel-heading">
<div class="pull-right">
<a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
<a href="{% url 'user:token_delete' pk=token.pk %}" class="btn btn-xs btn-danger">Delete</a>
{% if perms.users.change_token %}
<a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
{% endif %}
{% if perms.users.delete_token %}
<a href="{% url 'user:token_delete' pk=token.pk %}" class="btn btn-xs btn-danger">Delete</a>
{% endif %}
</div>
<i class="fa fa-key"></i> {{ token.key }}
{% if token.is_expired %}
@@ -49,10 +53,16 @@
{% empty %}
<p>You do not have any API tokens.</p>
{% endfor %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="fa fa-plus" aria-hidden="true"></span>
Add a token
</a>
{% if perms.users.add_token %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="fa fa-plus" aria-hidden="true"></span>
Add a token
</a>
{% else %}
<div class="alert alert-info text-center" role="alert">
You do not have permission to create new API tokens. If needed, ask an administrator to enable token creation for your account or an assigned group.
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -29,6 +29,16 @@ class TenantGroupTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_tenantgroups_brief(self):
url = reverse('tenancy-api:tenantgroup-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_tenantgroup(self):
data = {
@@ -122,6 +132,16 @@ class TenantTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_tenants_brief(self):
url = reverse('tenancy-api:tenant-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_tenant(self):
data = {

View File

@@ -0,0 +1,17 @@
# Generated by Django 2.0.8 on 2018-10-05 14:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0001_api_tokens_squashed_0002_unicode_literals'),
]
operations = [
migrations.AlterModelOptions(
name='token',
options={},
),
]

View File

@@ -39,7 +39,7 @@ class Token(models.Model):
)
class Meta:
default_permissions = []
pass
def __str__(self):
# Only display the last 24 bits of the token to avoid accidental exposure.

View File

@@ -1,8 +1,8 @@
from django.contrib import messages
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
@@ -217,8 +217,12 @@ class TokenEditView(LoginRequiredMixin, View):
def get(self, request, pk=None):
if pk is not None:
if not request.user.has_perm('users.change_token'):
return HttpResponseForbidden()
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
else:
if not request.user.has_perm('users.add_token'):
return HttpResponseForbidden()
token = Token(user=request.user)
form = TokenForm(instance=token)
@@ -260,7 +264,8 @@ class TokenEditView(LoginRequiredMixin, View):
})
class TokenDeleteView(LoginRequiredMixin, View):
class TokenDeleteView(PermissionRequiredMixin, View):
permission_required = 'users.delete_token'
def get(self, request, pk):

View File

@@ -190,6 +190,19 @@ class ModelViewSet(_ModelViewSet):
return super(ModelViewSet, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
# If 'brief' has been passed as a query param, find and return the nested serializer for this model, if one
# exists
request = self.get_serializer_context()['request']
if request.query_params.get('brief', False):
serializer_class = get_serializer_for_model(self.queryset.model, prefix='Nested')
if serializer_class is not None:
return serializer_class
# Fall back to the hard-coded serializer class
return self.serializer_class
class FieldChoicesViewSet(ViewSet):
"""

View File

@@ -2,6 +2,7 @@ import csv
from io import StringIO
import json
import re
import sys
from django import forms
from django.conf import settings
@@ -148,6 +149,11 @@ def add_blank_choice(choices):
return ((None, '---------'),) + tuple(choices)
def utf8_encoder(data):
for line in data:
yield line.encode('utf-8')
#
# Widgets
#
@@ -301,7 +307,12 @@ class CSVDataField(forms.CharField):
def to_python(self, value):
records = []
reader = csv.reader(StringIO(value))
# Python 2 hack for Unicode support in the CSV reader
if sys.version_info[0] < 3:
reader = csv.reader(utf8_encoder(StringIO(value)))
else:
reader = csv.reader(StringIO(value))
# Consume and validate the first line of CSV data as column headers
headers = next(reader)

View File

@@ -167,7 +167,8 @@ class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
class NestedInterfaceSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:interface-detail')
virtual_machine = NestedVirtualMachineSerializer(read_only=True)
class Meta:
model = Interface
fields = ['id', 'url', 'name']
fields = ['id', 'url', 'virtual_machine', 'name']

View File

@@ -54,6 +54,11 @@ class VirtualMachineViewSet(CustomFieldModelViewSet):
"""
if self.action == 'retrieve':
return serializers.VirtualMachineWithConfigContextSerializer
request = self.get_serializer_context()['request']
if request.query_params.get('brief', False):
return serializers.NestedVirtualMachineSerializer
return serializers.VirtualMachineSerializer
@@ -63,3 +68,10 @@ class InterfaceViewSet(ModelViewSet):
).select_related('virtual_machine').prefetch_related('tags')
serializer_class = serializers.InterfaceSerializer
filter_class = filters.InterfaceFilter
def get_serializer_class(self):
request = self.get_serializer_context()['request']
if request.query_params.get('brief', False):
# Override get_serializer_for_model(), which will return the DCIM NestedInterfaceSerializer
return serializers.NestedInterfaceSerializer
return serializers.InterfaceSerializer

View File

@@ -251,7 +251,7 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
model = VirtualMachine
fields = [
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
'vcpus', 'memory', 'disk', 'comments', 'tags',
'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data',
]
help_texts = {
'local_context_data': "Local config context data overwrites all sources contexts in the final rendered config context",

View File

@@ -33,6 +33,16 @@ class ClusterTypeTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_clustertypes_brief(self):
url = reverse('virtualization-api:clustertype-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_clustertype(self):
data = {
@@ -124,6 +134,16 @@ class ClusterGroupTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_clustergroups_brief(self):
url = reverse('virtualization-api:clustergroup-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
)
def test_create_clustergroup(self):
data = {
@@ -218,6 +238,16 @@ class ClusterTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_clusters_brief(self):
url = reverse('virtualization-api:cluster-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url']
)
def test_create_cluster(self):
data = {
@@ -322,6 +352,16 @@ class VirtualMachineTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_virtualmachines_brief(self):
url = reverse('virtualization-api:virtualmachine-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url']
)
def test_create_virtualmachine(self):
data = {
@@ -445,6 +485,16 @@ class InterfaceTest(APITestCase):
self.assertEqual(response.data['count'], 3)
def test_list_interfaces_brief(self):
url = reverse('virtualization-api:interface-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url', 'virtual_machine']
)
def test_create_interface(self):
data = {