Fixes some ipaddress api changes, adds puppet host validation endpoint with test
This commit is contained in:
1 parent
a7b3e577b3
commit
33c6d61d2d
9 files changed
+2849
-23
No files matched your search
@@ -0,0 +1,46 @@
|
||||
from mdb.models import Host, Interface, Domain
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
class HostValidateSecretSerializer(serializers.Serializer):
|
||||
certname = serializers.CharField(required=True)
|
||||
pxe_key = serializers.CharField(required=True)
|
||||
host = None
|
||||
|
||||
def _host_in_domain_exists(self, attrs):
|
||||
fqdn = attrs['certname']
|
||||
host_name = fqdn.split('.')[0]
|
||||
domain = '.'.join(fqdn.split('.')[1:])
|
||||
|
||||
try:
|
||||
return Host.objects.get(hostname=host_name, interface__domain__domain_name=domain)
|
||||
except (Host.DoesNotExist, Host.MultipleObjectsReturned):
|
||||
return None
|
||||
|
||||
def validate(self, attrs):
|
||||
# custom validation
|
||||
host = self._host_in_domain_exists(attrs)
|
||||
|
||||
if host is None:
|
||||
raise ValidationError('Host \'{}\' not found'.format(attrs['certname']))
|
||||
|
||||
if not host.pxe_installable:
|
||||
raise ValidationError('Host is marked as not installable via PXE.')
|
||||
|
||||
if not host.pxe_key:
|
||||
raise ValidationError('Host has no pxe_key.')
|
||||
|
||||
if host.pxe_key != attrs['pxe_key']:
|
||||
raise ValidationError('Supplied pxe_key does not match host.')
|
||||
|
||||
self.host = host
|
||||
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
# Make requests kind of idempotent by only allowing 1 request / installation
|
||||
self.host.pxe_installable = False
|
||||
self.host.save()
|
||||
|
||||
return self.host
|
||||
+16
-3
@@ -1,7 +1,20 @@
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from mdb.api.serializers import HostValidateSecretSerializer
|
||||
|
||||
|
||||
class ValidatePuppetHostSecret(APIView):
|
||||
def post(self):
|
||||
# TODO
|
||||
pass
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
serializer = HostValidateSecretSerializer(data=request.data)
|
||||
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
serializer.save() # sets pxe_installable=False
|
||||
|
||||
return Response({'valid_secret': True})
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mdb', '0006_auto_20151001_2235'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='host',
|
||||
name='pxe_installable',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='host',
|
||||
name='pxe_key',
|
||||
field=models.CharField(max_length=254, blank=True),
|
||||
),
|
||||
]
|
||||
+14
-12
@@ -346,19 +346,19 @@ class Ip4Subnet(models.Model):
|
||||
|
||||
def num_addresses(self):
|
||||
subnet = ipaddress.IPv4Network(self.network + "/" + self.netmask)
|
||||
return subnet.numhosts
|
||||
return subnet.num_addresses
|
||||
|
||||
def broadcast_address(self):
|
||||
subnet = ipaddress.IPv4Network(self.network + "/" + self.netmask)
|
||||
return subnet.broadcast
|
||||
return subnet.broadcast_address
|
||||
|
||||
def first_address(self):
|
||||
subnet = ipaddress.IPv4Network(self.network + "/" + self.netmask)
|
||||
return subnet.iterhosts().next()
|
||||
return next(subnet.hosts())
|
||||
|
||||
def last_address(self):
|
||||
subnet = ipaddress.IPv4Network(self.network + "/" + self.netmask)
|
||||
for curr in subnet.iterhosts():
|
||||
for curr in subnet.hosts():
|
||||
pass # horribly inefficient
|
||||
|
||||
return curr
|
||||
@@ -448,15 +448,14 @@ class Ip4Address(models.Model):
|
||||
ping_avg_rtt = models.FloatField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
# FIXME: generates 2 SQL queries
|
||||
_if = self.interface_set.all()
|
||||
if not _if:
|
||||
# FIXME: generates 2 SQL queries?
|
||||
if not hasattr(self, 'interface'):
|
||||
return self.address
|
||||
|
||||
return "{} ({})".format(self.address, _if.get().host.hostname)
|
||||
return "{} ({})".format(self.address, self.interface.host.hostname)
|
||||
|
||||
def assigned_to_host(self):
|
||||
return self.interface_set.get().host
|
||||
return self.interface.host
|
||||
|
||||
assigned_to_host.short_description = "Assigned to Host"
|
||||
|
||||
@@ -523,12 +522,15 @@ class Host(models.Model):
|
||||
kerberos_principal_name = models.CharField(max_length=256, editable=False)
|
||||
kerberos_principal_created_date = models.DateTimeField(null=True, blank=True, editable=False)
|
||||
|
||||
pxe_key = models.CharField(max_length=254, blank=True)
|
||||
pxe_installable = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.hostname
|
||||
|
||||
def in_domain(self):
|
||||
domains = self.interface_set.values_list('domain__domain_name', flat=True)
|
||||
return u",".join(domains)
|
||||
return ",".join(domains)
|
||||
|
||||
in_domain.short_description = "in domains"
|
||||
|
||||
@@ -562,7 +564,7 @@ class Interface(models.Model):
|
||||
return "%s (%s on %s)" % (self.macaddr, self.name, self.host.hostname)
|
||||
|
||||
def ipv6_enabled(self):
|
||||
return self.ip6address_set.count() > 0
|
||||
return self.ip6address_set.exists()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
@@ -624,7 +626,7 @@ def create_ips_for_subnet(sender, instance, created, **kwargs):
|
||||
|
||||
subnet = ipaddress.IPv4Network(instance.network + "/" + instance.netmask)
|
||||
|
||||
for addr in subnet.iterhosts():
|
||||
for addr in subnet.hosts():
|
||||
address = Ip4Address(address=str(addr), subnet=instance)
|
||||
address.save()
|
||||
|
||||
|
||||
+21
-5
@@ -1,12 +1,28 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from mdb.models import Host
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
|
||||
class APITestCases(APITestCase):
|
||||
class MyAPITestCases(APITestCase):
|
||||
fixtures = ['test_data']
|
||||
|
||||
def create_user_with_token(self):
|
||||
self.user = User.objects.create(username='admin')
|
||||
self.token = Token.objects.create(user=self.user)
|
||||
self.host = Host.objects.first()
|
||||
|
||||
def setUp(self):
|
||||
self.create_user_with_token()
|
||||
return super(MyAPITestCases, self).setUp()
|
||||
|
||||
def test_validate_puppet_host_by_secret(self):
|
||||
data = {
|
||||
'secret': 'something',
|
||||
'certname': 'arnold.neuf.no'
|
||||
'pxe_key': self.host.pxe_key,
|
||||
'certname': '{}.{}'.format(self.host.hostname, self.host.interface_set.first().domain.domain_name)
|
||||
}
|
||||
res = self.client.post(reverse('api-validate-host-secret'), data)
|
||||
self.fail()
|
||||
self.client.credentials(HTTP_AUTHORIZATION='Token {}'.format(self.token.key))
|
||||
res = self.client.post(reverse('api-validate-host-secret'), data, format='json')
|
||||
self.assertEquals(res.status_code, 200, res.content)
|
||||
self.assertFalse(Host.objects.get(pk=self.host.pk).pxe_installable)
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.core.validators import RegexValidator
|
||||
import re
|
||||
|
||||
@@ -5,11 +6,11 @@ import re
|
||||
hostname_re = re.compile(r'^(?!-)[-a-z0-9]+(?<!-)$', re.IGNORECASE)
|
||||
validate_hostname = RegexValidator(
|
||||
regex=hostname_re,
|
||||
message=u'Enter a valid hostname',
|
||||
message='Enter a valid hostname',
|
||||
code='invalid')
|
||||
|
||||
macaddr_re = re.compile(r'^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$', re.IGNORECASE)
|
||||
validate_macaddr = RegexValidator(
|
||||
regex=macaddr_re,
|
||||
message=u'Enter a valid MAC address',
|
||||
message='Enter a valid MAC address',
|
||||
code='invalid')
|
||||
@@ -75,6 +75,7 @@ INSTALLED_APPS = (
|
||||
'suit',
|
||||
'django.contrib.admin',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
|
||||
'mdb',
|
||||
)
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
Django>=1.8,<1.9
|
||||
django-auth-ldap
|
||||
django-suit==0.2.15
|
||||
django-extensions==1.5.7
|
||||
djangorestframework==3.2.4
|
||||
django-suit==0.2.15
|
||||
ipaddress==1.0.14
|
||||
mysqlclient==1.3.6
|
||||
Reference in new issue
Block a user