def test_create_check(self, form_data): # Create a UserAS with the given data create_form = UserASForm(user=get_testuser(), data=form_data) self.assertIsNotNone(create_form.as_table()) self.assertTrue(create_form.is_valid(), create_form.errors) user_as = create_form.save() self.assertIsNotNone(user_as.pk) links = [iface.link().pk for iface in user_as.interfaces.all()] self.assertEqual(len(links), form_data['form-TOTAL_FORMS']) # Submittng the ~same data on the previously created instance should have no changes form_data_2 = _get_form_fields_for_edit(form_data, user_as) edit_form = UserASForm(user=get_testuser(), instance=user_as, data=form_data_2) self.assertFalse(edit_form.has_changed(), _get_forms_change_data(edit_form)) self.assertTrue(edit_form.is_valid(), edit_form.errors) # Also, if we save the updated form, nothing should change user_as_edited = edit_form.save() self.assertEqual(user_as.pk, user_as_edited.pk) links_edited = [iface.link().pk for iface in user_as.interfaces.all()] self.assertEqual(links, links_edited)
def test_server_vpn_ip(self): """ Its IP is not at the beginning of the subnet """ attachment_point = AttachmentPoint.objects.filter( vpn__isnull=False).first() vpn = attachment_point.vpn server_orig_ip = ip_address(vpn.server_vpn_ip) vpn.server_vpn_ip = str(server_orig_ip + 1) vpn.save() # create two clients and check their IP addresses c1 = create_and_check_useras( self, owner=get_testuser(), attachment_point=attachment_point, public_port=50000, use_vpn=True).hosts.get().vpn_clients.get() c2 = create_and_check_useras( self, owner=get_testuser(), attachment_point=attachment_point, public_port=50000, use_vpn=True).hosts.get().vpn_clients.get() ip1 = ip_address(c1.ip) ip2 = ip_address(c2.ip) self.assertEqual(ip1, server_orig_ip) self.assertEqual(ip2, ip_address(vpn.server_vpn_ip) + 1)
def test_vpn_client_next_ip(self): attachment_point = AttachmentPoint.objects.filter( AS__as_id='ffaa:0:1404').get() vpn = attachment_point.vpn vpn.clients.all().delete() # check creation of first client user_as = create_and_check_useras(self, attachment_point=attachment_point, owner=get_testuser(), use_vpn=True, public_port=50000) vpn_client = user_as.hosts.get().vpn_clients.get() # consecutive: (assumes server at begin of IP range, not the case for all APs in testdata) self.assertEqual( ip_address(vpn.server_vpn_ip) + 1, ip_address(vpn_client.ip)) # leave a gap at the beginning former_vpn_ip = ip_address(vpn_client.ip) vpn_client.ip = str(former_vpn_ip + 1) vpn_client.save() user_as = create_and_check_useras(self, attachment_point=attachment_point, owner=get_testuser(), use_vpn=True, public_port=50000) vpn_client_new = user_as.hosts.get().vpn_clients.get() self.assertEqual(ip_address(vpn_client_new.ip), former_vpn_ip)
def test_vpn_client_next_ip(self): attachment_point = AttachmentPoint.objects.filter( vpn__isnull=False).first() vpn_server = VPN.objects.first() user_as = create_and_check_useras(self, attachment_point=attachment_point, owner=get_testuser(), use_vpn=True, public_port=50000) vpn_client = user_as.hosts.get().vpn_clients.get() # consecutive: self.assertEqual( ip_address(vpn_server.server_vpn_ip) + 1, ip_address(vpn_client.ip)) # leave a gap at the beginning former_vpn_ip = ip_address(vpn_client.ip) vpn_client.ip = str(former_vpn_ip + 1) vpn_client.save() user_as = create_and_check_useras(self, attachment_point=attachment_point, owner=get_testuser(), use_vpn=True, public_port=50000) vpn_client_new = user_as.hosts.get().vpn_clients.get() self.assertEqual(ip_address(vpn_client_new.ip), former_vpn_ip)
def test_exhaust_vpn_clients(self, _): attachment_point = AttachmentPoint.objects.filter( vpn__isnull=False).first() vpn = attachment_point.vpn vpn.subnet = '10.0.8.0/28' vpn.server_vpn_ip = '10.0.8.10' vpn.save() subnet = ip_network(vpn.subnet) used_ips = list() it = subnet.hosts() next(it) # skip one for the server for i in it: a = create_and_check_useras(self, owner=get_testuser(), attachment_point=attachment_point, public_port=50000, use_vpn=True) used_ips.append(ip_address(a.hosts.get().vpn_clients.get().ip)) self.assertEqual(len(used_ips), 13) # 16 - network, broadcast and server addrs used_ips_set = set(used_ips) self.assertEqual(len(used_ips), len(used_ips_set)) self.assertNotIn(ip_address(vpn.server_vpn_ip), used_ips_set) for ip in used_ips: self.assertIn(ip, subnet) # one too many: with self.assertRaises(RuntimeError): a = create_and_check_useras(self, owner=get_testuser(), attachment_point=attachment_point, public_port=50000, use_vpn=True)
def test_edit_render(self, **kwargs): """ The form can be instantiated with a (freshly created) UserAS. The instantiated form should not show any changes if the same data is submitted. """ form_data = self._get_initial_dict() form_data.update(**kwargs) # Create a UserAS with the given data create_form = UserASForm(user=get_testuser(), data=form_data) self.assertIsNotNone(create_form.as_table()) self.assertTrue(create_form.is_valid(), create_form.errors) user_as = create_form.save() self.assertIsNotNone(user_as) # Check that the form can be instantiated for the object just created # and check that the forms initial values are the same as the # previously submitted values. edit_form_1 = UserASForm(user=get_testuser(), instance=user_as, data=form_data) self.assertIsNotNone(edit_form_1.as_table()) self.assertTrue(edit_form_1.is_valid(), edit_form_1.errors) self.assertFalse(edit_form_1.has_changed(), edit_form_1.changed_data) user_as_edited_1 = edit_form_1.save() self.assertEqual(user_as.pk, user_as_edited_1.pk) # Do it again! edit_form_2 = UserASForm(user=get_testuser(), instance=user_as, data=form_data) self.assertTrue(edit_form_2.is_valid(), edit_form_2.errors) self.assertFalse(edit_form_2.has_changed(), edit_form_2.changed_data) user_as_edited_2 = edit_form_2.save() self.assertEqual(user_as.pk, user_as_edited_2.pk)
def test_get_config(self): _create_ases_for_testuser(get_testuser().max_num_ases()) self.client.force_login(get_testuser()) user_as_pks = [user_as.pk for user_as in UserAS.objects.filter(owner=get_testuser()).iterator()] for pk in user_as_pks: response = self.client.get(reverse('user_as_config', kwargs={'pk': pk})) self.assertEqual(response.status_code, 200) utils.check_tarball_user_as(self, response, UserAS.objects.get(pk=pk))
def test_edit(self, initial_data, new_data): # Create a UserAS with the given data create_form = UserASForm(user=get_testuser(), data=initial_data) self.assertIsNotNone(create_form.as_table()) self.assertTrue(create_form.is_valid(), create_form.errors) user_as = create_form.save() self.assertIsNotNone(user_as) # Submittng the new data on the previously created instance should change it edit_form_1_data = _get_form_fields_for_edit(new_data, user_as) edit_form_1 = UserASForm(user=get_testuser(), instance=user_as, data=edit_form_1_data) rendered_1 = edit_form_1.as_table() self.assertIsNotNone(rendered_1) rendered_formset_1 = edit_form_1.attachment_conf_form_set.as_table() self.assertIsNotNone(rendered_formset_1) self.assertTrue(edit_form_1.is_valid(), edit_form_1.errors) if initial_data == new_data: self.assertFalse(edit_form_1.has_changed(), _get_forms_change_data(edit_form_1)) else: self.assertTrue(edit_form_1.has_changed()) user_as_edited_1 = edit_form_1.save() self.assertEqual(user_as.pk, user_as_edited_1.pk) links_edited_1 = [ iface.link().pk for iface in user_as.interfaces.all() ] self.assertEqual(len(links_edited_1), new_data['form-TOTAL_FORMS']) if initial_data == new_data: return # no need to do this _again_ # Submittng the ~same data on the previously edited instance should have no further changes edit_form_2_data = _get_form_fields_for_edit(new_data, user_as) edit_form_2 = UserASForm(user=get_testuser(), instance=user_as, data=edit_form_2_data) self.assertFalse(edit_form_2.has_changed(), _get_forms_change_data(edit_form_2)) self.assertTrue(edit_form_2.is_valid(), edit_form_2.errors) links_edited_2 = [ iface.link().pk for iface in user_as.interfaces.all() ] self.assertEqual(links_edited_1, links_edited_2)
def test_create_invalid(self, form_data): error_desc = form_data.pop('error', None) form = UserASForm(user=get_testuser(), data=form_data) self.assertFalse(form.is_valid()) if error_desc: error_descs = _get_forms_error_descs(form) self.assertIn(error_desc, error_descs)
def test_create_vpn(self): attachment_point = AttachmentPoint.objects.get(vpn__isnull=False) create_and_check_useras(self, owner=get_testuser(), attachment_point=attachment_point, use_vpn=True, public_port=test_public_port)
def _get_initial_dict(self): """ Return a dict containing the initial value for each UserAS form field """ # Create a form instance, only to extract the initial values form = UserASForm(user=get_testuser()) return {key: field.initial for (key, field) in form.fields.items()}
def test_delete_user(self): testuser = get_testuser() user_as_pks = [] user_as_hosts = [] attachment_point_hosts = set() as_ids_combs = get_random_as_ids_combinations() vpn_choice = VPNChoice.SOME for i in range(testuser.max_num_ases()): seed = 789 + i r = random.Random(seed) as_ids = r.choice(as_ids_combs) user_as, att_confs = create_and_check_random_useras( self, seed, as_ids, vpn_choice) user_as_pks.append(user_as.pk) user_as_hosts += list(user_as.hosts.all()) attachment_point_hosts |= set([ h for c in att_confs for h in c.attachment_point.AS.hosts.all() ]) testuser.delete() for user_as_pk in user_as_pks: self.assertFalse(UserAS.objects.filter(pk=user_as_pk).exists()) self.assertEqual( list(Host.objects.needs_config_deployment()), sorted(user_as_hosts + list(attachment_point_hosts), key=lambda host: host.pk)) utils.check_topology(self)
def test_create_public_ip(self, ap_index): attachment_point = AttachmentPoint.objects.all()[ap_index] create_and_check_useras(self, owner=get_testuser(), attachment_point=attachment_point, use_vpn=False, public_ip=test_public_ip, public_port=test_public_port)
def test_create_too_many(self): """ Submitting the creation form with quota exceeded leads to a form-error. """ form_data = self._get_initial_dict() form_data.update(**self.valid_form_params[0].kwargs) for i in range(get_testuser().max_num_ases()): form = UserASForm(user=get_testuser(), data=form_data) # Check that form is valid, otherwise this test will not make sense self.assertTrue(form.is_valid(), form.errors) form.save() form = UserASForm(user=get_testuser(), data=form_data) self.assertFalse(form.is_valid()) self.assertTrue(any(e.find("quota exceeded") >= 0 for e in form.non_field_errors()), form.errors)
def test_create_valid(self, form_data): """ Check that the form is valid with the given values as user input, and saving the form results in a new UserAS object. """ form = UserASForm(user=get_testuser(), data=form_data) error_descs = _get_forms_error_descs(form) self.assertTrue(form.is_valid(), error_descs) user_as = form.save() self.assertIsNotNone(user_as)
def test_post_create_form_no_quota(self): """ Submitting the create form with quota exceeded is not allowed """ self.app.set_user(TESTUSER_EMAIL) create_page = self.app.get(reverse('user_as_add'), expect_errors=True) _create_ases_for_testuser(get_testuser().max_num_ases()) self._fill_form(create_page.form, **UserASFormTests.valid_form_params[-1].kwargs) response = create_page.form.submit(expect_errors=True) self.assertEqual(response.status_code, 403)
def create_user_as(ap, label='Some label'): user_as = UserAS.objects.create( owner=get_testuser(), installation_type=UserAS.VM, isd=ap.AS.isd, label=label, ) att_conf = AttachmentConf(ap, use_vpn=True, public_port=test_public_port) user_as.update_attachments([att_conf]) return user_as
def test_submit_create_form_valid(self, **kwargs): """ Submitting valid data creates the AS and forwards to the detail page """ self.app.set_user(TESTUSER_EMAIL) create_page = self.app.get(reverse('user_as_add')) self._fill_form(create_page.form, **kwargs) response = create_page.form.submit() user_as = UserAS.objects.filter(owner=get_testuser()).last() self.assertEqual(response.status_code, 302) self.assertEqual(response.location, reverse('user_as_detail', kwargs={'pk': user_as.pk})) self.assertEqual(response.follow().status_code, 200) # submit the form again, forwards to the next AS: response_2 = create_page.form.submit() user_as_2 = UserAS.objects.filter(owner=get_testuser()).last() self.assertEqual(response_2.status_code, 302) self.assertEqual(response_2.location, reverse('user_as_detail', kwargs={'pk': user_as_2.pk})) self.assertEqual(response_2.follow().status_code, 200)
def _create_ases_for_testuser(num): """ Create a number `num` UserASes for testuser """ user = get_testuser() num_existing_ases = UserAS.objects.filter(owner=user).count() for i in range(num_existing_ases, num_existing_ases + num): UserAS.objects.create(owner=user, attachment_point=AttachmentPoint.objects.first(), public_ip=_test_ip, public_port=_test_start_port + i, label="Testuser's AS number %i" % (i + 1), installation_type=UserAS.VM)
def create_user_as(ap, label='Some label'): return UserAS.objects.create( owner=get_testuser(), attachment_point=ap, installation_type=UserAS.VM, label=label, use_vpn=True, public_ip=None, public_port=test_public_port, bind_ip=None, bind_port=None, )
def _get_random_useras_params(seed, force_public_ip=False, force_bind_ip=False, **kwargs): """ Generate some "random" parameters for a UserAS based on `seed`. Any parameters to UserAS.objects.create can be specified to override the generated values. Note: overriding parameters may affect the generated value for other parameters. :returns: kwargs dict for UserAS.objects.create """ r = random.Random(seed) def _randbool(): return r.choice([True, False]) use_vpn = kwargs.setdefault('use_vpn', _randbool()) # Make AP choice both with/without VPN to always consume same amount of random state candidate_AP_vpn = r.choice( AttachmentPoint.objects.filter(vpn__isnull=False)) candidate_AP_novpn = r.choice(AttachmentPoint.objects.all()) if use_vpn: kwargs.setdefault('attachment_point', candidate_AP_vpn) else: kwargs.setdefault('attachment_point', candidate_AP_novpn) kwargs.setdefault('owner', get_testuser()) public_ip = '172.31.0.%i' % r.randint(10, 254) public_port = r.choice(range(DEFAULT_PUBLIC_PORT, DEFAULT_PUBLIC_PORT + 20)) if _randbool() or not use_vpn or force_public_ip: kwargs.setdefault('public_ip', public_ip) else: kwargs.setdefault('public_ip', None) kwargs.setdefault('public_port', public_port) bind_ip = '192.168.1.%i' % r.randint(10, 254) bind_port = r.choice( range(DEFAULT_PUBLIC_PORT + 1000, DEFAULT_PUBLIC_PORT + 1020)) if _randbool() or force_bind_ip: kwargs.setdefault('bind_ip', bind_ip) kwargs.setdefault('bind_port', bind_port) else: kwargs.setdefault('bind_ip', None) kwargs.setdefault('bind_port', None) kwargs.setdefault('installation_type', r.choice((UserAS.VM, UserAS.PKG, UserAS.SRC))) randstr = r.getrandbits(1024).to_bytes(1024 // 8, 'little').decode('utf8', 'ignore') kwargs.setdefault('label', randstr) return kwargs
def test_create_as_button_no_quota(self): """ If the AS quota has been exceeded, the Create AS page is not available anymore. """ _create_ases_for_testuser(get_testuser().max_num_ases()) self.app.set_user(TESTUSER_EMAIL) # The quota should be exceeded: the Create AS button is gone user_page = self.app.get(reverse('user')) user_page.mustcontain(_QUOTA_EXCEEDED_MESSAGE) add_links = user_page.html.find_all("a", href=reverse('user_as_add')) self.assertEqual([], add_links)
def test_create_valid(self, **kwargs): """ Check that the form is valid with the given values as user input, and saving the form results in a new UserAS object. """ form_data = self._get_initial_dict() form_data.update(**kwargs) form = UserASForm(user=get_testuser(), data=form_data) self.assertTrue(form.is_valid(), form.errors) user_as = form.save() self.assertIsNotNone(user_as)
def test_create_valid(self, form_data): """ Check that the form is valid with the given values as user input, and saving the form results in a new UserAS object. """ form = UserASForm(user=get_testuser(), data=form_data) error_descs = _get_forms_error_descs(form) self.assertTrue(form.is_valid(), error_descs) user_as = form.save() self.assertIsNotNone(user_as) if form_data.get("user-as-become_user_ap", "") == "on": self.assertTrue( AttachmentPoint.objects.filter(AS=user_as).count() > 0)
def _create_user_as(attachment_point, label="Some label"): """ Create a UserAS in a transaction, as would happen in a view. This will then trigger the on_commit and run the deployment tasks. """ return UserAS.objects.create( owner=get_testuser(), attachment_point=attachment_point, installation_type=UserAS.PKG, label=label, use_vpn=False, public_ip=str(ipaddress.ip_address(test_public_ip) + 2), public_port=test_public_port, )
def test_as_listing(self): """ The main user page should contain a listing of the user's ASes with one link to the detail page for each of them. """ def check_as_link_count(user_page, expected_count): links = user_page.html.find_all("a", href=lambda x: re.fullmatch(r"/user/as/\d*", x)) self.assertEqual(expected_count, len(links), links) self.app.set_user(TESTUSER_EMAIL) # Start with no ASes self.assertFalse(UserAS.objects.filter(owner=get_testuser()).exists()) user_page = self.app.get(reverse('user')) check_as_link_count(user_page, 0) self.assertTrue(_NO_USER_AS_MESSAGE in user_page, user_page) for n in range(get_testuser().max_num_ases()): _create_ases_for_testuser(1) user_page = self.app.get(reverse('user')) check_as_link_count(user_page, n+1) self.assertFalse(_NO_USER_AS_MESSAGE in user_page, user_page)
def _get_random_useras_params(seed, vpn_choice, **kwargs): """ Generate some "random" parameters for a UserAS based on `seed`. Any parameters to UserAS.objects.create can be specified to override the generated values. :returns: kwargs dict for UserAS.objects.create """ r = random.Random(seed) kwargs.setdefault('owner', get_testuser()) kwargs.setdefault('installation_type', r.choice((UserAS.VM, UserAS.PKG, UserAS.SRC))) randstr = r.getrandbits(1024).to_bytes(1024 // 8, 'little').decode('utf8', 'ignore') kwargs.setdefault('label', randstr) return kwargs
def _create_ases_for_testuser(num): """ Create a number `num` UserASes for testuser """ user = get_testuser() num_existing_ases = UserAS.objects.filter(owner=user).count() for i in range(num_existing_ases, num_existing_ases + num): ap = AttachmentPoint.objects.first() user_as = UserAS.objects.create( owner=user, installation_type=UserAS.VM, isd=ap.AS.isd, label="Testuser's AS number %i" % (i + 1), ) att_conf = AttachmentConf(ap, public_ip=_test_ip, public_port=_test_start_port + i) user_as.update_attachments([att_conf])
def test_cycle_active(self): def check_active(active, user_as, edit_page): self.assertEqual("Activate" in edit_page, not active, edit_page) self.assertEqual("Deactivate" in edit_page, active, edit_page) self.assertEqual(user_as.is_active(), active) _create_ases_for_testuser(3) user_as = UserAS.objects.filter(owner=get_testuser())[1] self.app.set_user(TESTUSER_EMAIL) user_page = self.app.get(reverse('user')) edit_page = user_page.click(href=reverse('user_as_detail', kwargs={'pk': user_as.pk})+'$') check_active(True, user_as, edit_page) edit_page = edit_page.forms['id_deactivate_form'].submit().maybe_follow() check_active(False, user_as, edit_page) edit_page = edit_page.forms['id_activate_form'].submit().maybe_follow() check_active(True, user_as, edit_page)
def _create_user_as(attachment_point, label="Some label"): """ Create a UserAS in a transaction, as would happen in a view. This will then trigger the on_commit and run the deployment tasks. :returns UserAS: the created user_as """ user_as = UserAS.objects.create( owner=get_testuser(), installation_type=UserAS.PKG, isd=attachment_point.AS.isd, label=label, ) att_conf = AttachmentConf(attachment_point, str(ipaddress.ip_address(test_public_ip) + 2), test_public_port, bind_ip=None, bind_port=None, use_vpn=False) user_as.update_attachments([att_conf]) return user_as