mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 18:07:40 +00:00
ovs-dpctl and ovs-ofctl lack a read-only option to prevent running of commands that perform read-write operations. Add it and the necessary scaffolding to each. Signed-off-by: Ryan Moats <rmoats@us.ibm.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
490 lines
12 KiB
C
490 lines
12 KiB
C
/*
|
|
* Copyright (c) 2012, 2014 Nicira, 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.
|
|
*/
|
|
|
|
/* A test for for functions and macros declared in heap.h. */
|
|
|
|
#include <config.h>
|
|
#undef NDEBUG
|
|
#include "heap.h"
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include "command-line.h"
|
|
#include "ovstest.h"
|
|
#include "random.h"
|
|
#include "util.h"
|
|
|
|
/* Sample heap element. */
|
|
struct element {
|
|
uint32_t full_pri;
|
|
struct heap_node heap_node;
|
|
};
|
|
|
|
static struct element *
|
|
element_from_heap_node(const struct heap_node *node)
|
|
{
|
|
return CONTAINER_OF(node, struct element, heap_node);
|
|
}
|
|
|
|
static int
|
|
compare_uint32s(const void *a_, const void *b_)
|
|
{
|
|
const uint32_t *a = a_;
|
|
const uint32_t *b = b_;
|
|
return *a < *b ? -1 : *a > *b;
|
|
}
|
|
|
|
/* Verifies that 'heap' is internally consistent and contains all 'n' of the
|
|
* 'priorities'. */
|
|
static void
|
|
check_heap(const struct heap *heap, const uint32_t priorities[], size_t n)
|
|
{
|
|
uint32_t *priorities_copy;
|
|
uint32_t *elements_copy;
|
|
struct element *element;
|
|
size_t i;
|
|
|
|
assert(heap_count(heap) == n);
|
|
assert(heap_is_empty(heap) == !n);
|
|
if (n > 0) {
|
|
assert(heap_max(heap) == heap->array[1]);
|
|
}
|
|
|
|
/* Check indexes. */
|
|
for (i = 1; i <= n; i++) {
|
|
assert(heap->array[i]->idx == i);
|
|
}
|
|
|
|
/* Check that priority values are internally consistent. */
|
|
for (i = 1; i <= n; i++) {
|
|
element = element_from_heap_node(heap->array[i]);
|
|
assert(element->heap_node.priority == (element->full_pri >> 16));
|
|
}
|
|
|
|
/* Check the heap property. */
|
|
for (i = 1; i <= n; i++) {
|
|
size_t parent = heap_parent__(i);
|
|
size_t left = heap_left__(i);
|
|
size_t right = heap_right__(i);
|
|
|
|
if (parent >= 1) {
|
|
assert(heap->array[parent]->priority >= heap->array[i]->priority);
|
|
}
|
|
if (left <= n) {
|
|
assert(heap->array[left]->priority <= heap->array[i]->priority);
|
|
}
|
|
if (right <= n) {
|
|
assert(heap->array[right]->priority <= heap->array[i]->priority);
|
|
}
|
|
}
|
|
|
|
/* Check that HEAP_FOR_EACH iterates all the nodes in order. */
|
|
i = 0;
|
|
HEAP_FOR_EACH (element, heap_node, heap) {
|
|
assert(i < n);
|
|
assert(&element->heap_node == heap->array[i + 1]);
|
|
i++;
|
|
}
|
|
assert(i == n);
|
|
|
|
priorities_copy = xmemdup(priorities, n * sizeof *priorities);
|
|
elements_copy = xmalloc(n * sizeof *priorities);
|
|
i = 0;
|
|
HEAP_FOR_EACH (element, heap_node, heap) {
|
|
elements_copy[i++] = element->heap_node.priority;
|
|
}
|
|
|
|
qsort(priorities_copy, n, sizeof *priorities_copy, compare_uint32s);
|
|
qsort(elements_copy, n, sizeof *elements_copy, compare_uint32s);
|
|
for (i = 0; i < n; i++) {
|
|
assert((priorities_copy[i] >> 16) == elements_copy[i]);
|
|
}
|
|
|
|
free(priorities_copy);
|
|
free(elements_copy);
|
|
}
|
|
|
|
static void
|
|
shuffle(uint32_t *p, size_t n)
|
|
{
|
|
for (; n > 1; n--, p++) {
|
|
uint32_t *q = &p[random_range(n)];
|
|
uint32_t tmp = *p;
|
|
*p = *q;
|
|
*q = tmp;
|
|
}
|
|
}
|
|
|
|
/* Prints the values in 'heap', plus 'name' as a title. */
|
|
static void OVS_UNUSED
|
|
print_heap(const char *name, struct heap *heap)
|
|
{
|
|
struct element *e;
|
|
|
|
printf("%s:", name);
|
|
HEAP_FOR_EACH (e, heap_node, heap) {
|
|
printf(" %"PRIu32":%"PRIu32, e->full_pri >> 16, e->full_pri & 0xffff);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static int
|
|
factorial(int n_items)
|
|
{
|
|
int n, i;
|
|
|
|
n = 1;
|
|
for (i = 2; i <= n_items; i++) {
|
|
n *= i;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
swap(uint32_t *a, uint32_t *b)
|
|
{
|
|
uint32_t tmp = *a;
|
|
*a = *b;
|
|
*b = tmp;
|
|
}
|
|
|
|
static void
|
|
reverse(uint32_t *a, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n / 2; i++) {
|
|
int j = n - (i + 1);
|
|
swap(&a[i], &a[j]);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
next_permutation(uint32_t *a, int n)
|
|
{
|
|
int k;
|
|
|
|
for (k = n - 2; k >= 0; k--) {
|
|
if ((a[k] >> 16) < (a[k + 1] >> 16)) {
|
|
int l;
|
|
|
|
for (l = n - 1; ; l--) {
|
|
if ((a[l] >> 16) > (a[k] >> 16)) {
|
|
swap(&a[k], &a[l]);
|
|
reverse(a + (k + 1), n - (k + 1));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
test_insert_delete__(struct element *elements,
|
|
const uint32_t *insert,
|
|
const uint32_t *delete,
|
|
size_t n)
|
|
{
|
|
struct heap heap;
|
|
size_t i;
|
|
|
|
heap_init(&heap);
|
|
check_heap(&heap, NULL, 0);
|
|
for (i = 0; i < n; i++) {
|
|
uint32_t priority = insert[i];
|
|
|
|
elements[i].full_pri = priority;
|
|
heap_insert(&heap, &elements[i].heap_node, priority >> 16);
|
|
check_heap(&heap, insert, i + 1);
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
struct element *element;
|
|
|
|
HEAP_FOR_EACH (element, heap_node, &heap) {
|
|
if (element->full_pri == delete[i]) {
|
|
goto found;
|
|
}
|
|
}
|
|
OVS_NOT_REACHED();
|
|
|
|
found:
|
|
heap_remove(&heap, &element->heap_node);
|
|
check_heap(&heap, delete + i + 1, n - (i + 1));
|
|
}
|
|
heap_destroy(&heap);
|
|
}
|
|
|
|
static void
|
|
test_insert_delete_raw__(struct element *elements,
|
|
const uint32_t *insert, unsigned int insert_pattern,
|
|
const uint32_t *delete, unsigned int delete_pattern,
|
|
size_t n)
|
|
{
|
|
struct heap heap;
|
|
size_t i;
|
|
|
|
heap_init(&heap);
|
|
check_heap(&heap, NULL, 0);
|
|
for (i = 0; i < n; i++) {
|
|
uint32_t priority = insert[i];
|
|
|
|
elements[i].full_pri = priority;
|
|
heap_raw_insert(&heap, &elements[i].heap_node, priority >> 16);
|
|
if (insert_pattern & (1u << i)) {
|
|
heap_rebuild(&heap);
|
|
check_heap(&heap, insert, i + 1);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
struct element *element;
|
|
|
|
HEAP_FOR_EACH (element, heap_node, &heap) {
|
|
if (element->full_pri == delete[i]) {
|
|
goto found;
|
|
}
|
|
}
|
|
OVS_NOT_REACHED();
|
|
|
|
found:
|
|
heap_raw_remove(&heap, &element->heap_node);
|
|
if (delete_pattern & (1u << i)) {
|
|
heap_rebuild(&heap);
|
|
check_heap(&heap, delete + i + 1, n - (i + 1));
|
|
}
|
|
}
|
|
heap_destroy(&heap);
|
|
}
|
|
|
|
static void
|
|
test_heap_insert_delete_same_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 7 };
|
|
|
|
uint32_t insert[N_ELEMS];
|
|
int n_permutations;
|
|
size_t i;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
insert[i] = i << 16;
|
|
}
|
|
|
|
n_permutations = 0;
|
|
do {
|
|
struct element elements[N_ELEMS];
|
|
|
|
n_permutations++;
|
|
test_insert_delete__(elements, insert, insert, N_ELEMS);
|
|
} while (next_permutation(insert, N_ELEMS));
|
|
assert(n_permutations == factorial(N_ELEMS));
|
|
}
|
|
|
|
static void
|
|
test_heap_insert_delete_reverse_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 7 };
|
|
|
|
uint32_t insert[N_ELEMS];
|
|
int n_permutations;
|
|
size_t i;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
insert[i] = i << 16;
|
|
}
|
|
|
|
n_permutations = 0;
|
|
do {
|
|
struct element elements[N_ELEMS];
|
|
uint32_t delete[N_ELEMS];
|
|
|
|
n_permutations++;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
delete[N_ELEMS - i - 1] = insert[i];
|
|
}
|
|
|
|
test_insert_delete__(elements, insert, delete, N_ELEMS);
|
|
} while (next_permutation(insert, N_ELEMS));
|
|
assert(n_permutations == factorial(N_ELEMS));
|
|
}
|
|
|
|
static void
|
|
test_heap_insert_delete_every_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 5 };
|
|
|
|
uint32_t insert[N_ELEMS];
|
|
int outer_permutations;
|
|
size_t i;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
insert[i] = i << 16;
|
|
}
|
|
|
|
outer_permutations = 0;
|
|
do {
|
|
struct element elements[N_ELEMS];
|
|
uint32_t delete[N_ELEMS];
|
|
int inner_permutations;
|
|
|
|
outer_permutations++;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
delete[i] = i << 16;
|
|
}
|
|
|
|
inner_permutations = 0;
|
|
do {
|
|
inner_permutations++;
|
|
test_insert_delete__(elements, insert, delete, N_ELEMS);
|
|
} while (next_permutation(delete, N_ELEMS));
|
|
assert(inner_permutations == factorial(N_ELEMS));
|
|
} while (next_permutation(insert, N_ELEMS));
|
|
assert(outer_permutations == factorial(N_ELEMS));
|
|
}
|
|
|
|
static void
|
|
test_heap_insert_delete_same_order_with_dups(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 7 };
|
|
|
|
unsigned int pattern;
|
|
size_t i;
|
|
|
|
for (pattern = 0; pattern < (1u << N_ELEMS); pattern += 2) {
|
|
int n_permutations, expected_permutations;
|
|
uint32_t insert[N_ELEMS];
|
|
int j;
|
|
|
|
j = 0;
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
if (i && !(pattern & (1u << i))) {
|
|
j++;
|
|
}
|
|
insert[i] = (j << 16) | i;
|
|
}
|
|
|
|
expected_permutations = factorial(N_ELEMS);
|
|
for (i = 0; i < N_ELEMS; ) {
|
|
j = i + 1;
|
|
if (pattern & (1u << i)) {
|
|
for (; j < N_ELEMS; j++) {
|
|
if (!(pattern & (1u << j))) {
|
|
break;
|
|
}
|
|
}
|
|
expected_permutations /= factorial(j - i + 1);
|
|
}
|
|
i = j;
|
|
}
|
|
|
|
n_permutations = 0;
|
|
do {
|
|
struct element elements[N_ELEMS];
|
|
|
|
n_permutations++;
|
|
test_insert_delete__(elements, insert, insert, N_ELEMS);
|
|
} while (next_permutation(insert, N_ELEMS));
|
|
assert(n_permutations == expected_permutations);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_heap_raw_insert(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 7 };
|
|
|
|
uint32_t insert[N_ELEMS];
|
|
int n_permutations;
|
|
size_t i;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
insert[i] = i << 16;
|
|
}
|
|
|
|
n_permutations = 0;
|
|
do {
|
|
struct element elements[N_ELEMS];
|
|
|
|
n_permutations++;
|
|
test_insert_delete_raw__(elements,
|
|
insert, 1u << (N_ELEMS - 1),
|
|
insert, UINT_MAX,
|
|
N_ELEMS);
|
|
} while (next_permutation(insert, N_ELEMS));
|
|
assert(n_permutations == factorial(N_ELEMS));
|
|
}
|
|
|
|
static void
|
|
test_heap_raw_delete(struct ovs_cmdl_context *ctx OVS_UNUSED)
|
|
{
|
|
enum { N_ELEMS = 16 };
|
|
|
|
uint32_t insert[N_ELEMS];
|
|
uint32_t delete[N_ELEMS];
|
|
size_t i;
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
insert[i] = i << 16;
|
|
delete[i] = i << 16;
|
|
}
|
|
|
|
for (i = 0; i < 1000; i++) {
|
|
struct element elements[N_ELEMS];
|
|
|
|
shuffle(insert, N_ELEMS);
|
|
shuffle(delete, N_ELEMS);
|
|
|
|
test_insert_delete_raw__(elements,
|
|
insert, 0,
|
|
delete,
|
|
(1u << (N_ELEMS - 1)) | (1u << (N_ELEMS / 2)),
|
|
N_ELEMS);
|
|
}
|
|
}
|
|
|
|
static const struct ovs_cmdl_command commands[] = {
|
|
{ "insert-delete-same-order", NULL, 0, 0,
|
|
test_heap_insert_delete_same_order, OVS_RO },
|
|
{ "insert-delete-reverse-order", NULL, 0, 0,
|
|
test_heap_insert_delete_reverse_order, OVS_RO },
|
|
{ "insert-delete-every-order", NULL, 0, 0,
|
|
test_heap_insert_delete_every_order, OVS_RO },
|
|
{ "insert-delete-same-order-with-dups", NULL, 0, 0,
|
|
test_heap_insert_delete_same_order_with_dups, OVS_RO },
|
|
{ "raw-insert", NULL, 0, 0, test_heap_raw_insert, OVS_RO },
|
|
{ "raw-delete", NULL, 0, 0, test_heap_raw_delete, OVS_RO },
|
|
{ NULL, NULL, 0, 0, NULL, OVS_RO },
|
|
};
|
|
|
|
static void
|
|
test_heap_main(int argc, char *argv[])
|
|
{
|
|
struct ovs_cmdl_context ctx = {
|
|
.argc = argc - 1,
|
|
.argv = argv + 1,
|
|
};
|
|
|
|
set_program_name(argv[0]);
|
|
|
|
ovs_cmdl_run_command(&ctx, commands);
|
|
}
|
|
|
|
OVSTEST_REGISTER("test-heap", test_heap_main);
|