mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 09:58:01 +00:00
If a new database server added to the cluster, or if one of the database servers changed its IP address or port, then you need to update the list of remotes for the client. For example, if a new OVN_Southbound database server is added, you need to update the ovn-remote for the ovn-controller. However, in the current implementation, the ovsdb-cs module always closes the current connection and creates a new one. This can lead to a storm of re-connections if all ovn-controllers will be updated simultaneously. They can also start re-dowloading the database content, creating even more load on the database servers. Correct this by saving an existing connection if it is still in the list of remotes after the update. 'reconnect' module will report connection state updates, but that is OK since no real re-connection happened and we only updated the state of a new 'reconnect' instance. If required, re-connection can be forced after the update of remotes with ovsdb_cs_force_reconnect(). Acked-by: Dumitru Ceara <dceara@redhat.com> Acked-by: Han Zhou <hzhou@ovn.org> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
418 lines
8.4 KiB
C
418 lines
8.4 KiB
C
/*
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012 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.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "svec.h"
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "openvswitch/dynamic-string.h"
|
|
#include "random.h"
|
|
#include "util.h"
|
|
#include "openvswitch/vlog.h"
|
|
|
|
VLOG_DEFINE_THIS_MODULE(svec);
|
|
|
|
void
|
|
svec_init(struct svec *svec)
|
|
{
|
|
svec->names = NULL;
|
|
svec->n = 0;
|
|
svec->allocated = 0;
|
|
}
|
|
|
|
void
|
|
svec_clone(struct svec *svec, const struct svec *other)
|
|
{
|
|
svec_init(svec);
|
|
svec_append(svec, other);
|
|
}
|
|
|
|
void
|
|
svec_destroy(struct svec *svec)
|
|
{
|
|
svec_clear(svec);
|
|
free(svec->names);
|
|
}
|
|
|
|
void
|
|
svec_clear(struct svec *svec)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < svec->n; i++) {
|
|
free(svec->names[i]);
|
|
}
|
|
svec->n = 0;
|
|
}
|
|
|
|
bool
|
|
svec_is_empty(const struct svec *svec)
|
|
{
|
|
return svec->n == 0;
|
|
}
|
|
|
|
void
|
|
svec_add(struct svec *svec, const char *name)
|
|
{
|
|
svec_add_nocopy(svec, xstrdup(name));
|
|
}
|
|
|
|
void
|
|
svec_del(struct svec *svec, const char *name)
|
|
{
|
|
size_t offset;
|
|
|
|
offset = svec_find(svec, name);
|
|
if (offset != SIZE_MAX) {
|
|
free(svec->names[offset]);
|
|
memmove(&svec->names[offset], &svec->names[offset + 1],
|
|
sizeof *svec->names * (svec->n - offset - 1));
|
|
svec->n--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
svec_expand(struct svec *svec)
|
|
{
|
|
if (svec->n >= svec->allocated) {
|
|
svec->names = x2nrealloc(svec->names, &svec->allocated,
|
|
sizeof *svec->names);
|
|
}
|
|
}
|
|
|
|
void
|
|
svec_add_nocopy(struct svec *svec, char *name)
|
|
{
|
|
svec_expand(svec);
|
|
svec->names[svec->n++] = name;
|
|
}
|
|
|
|
void
|
|
svec_append(struct svec *svec, const struct svec *other)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < other->n; i++) {
|
|
svec_add(svec, other->names[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
svec_terminate(struct svec *svec)
|
|
{
|
|
svec_expand(svec);
|
|
svec->names[svec->n] = NULL;
|
|
}
|
|
|
|
static int
|
|
compare_strings(const void *a_, const void *b_)
|
|
{
|
|
char *const *a = a_;
|
|
char *const *b = b_;
|
|
return strcmp(*a, *b);
|
|
}
|
|
|
|
void
|
|
svec_sort(struct svec *svec)
|
|
{
|
|
if (svec->n) {
|
|
qsort(svec->names, svec->n, sizeof *svec->names, compare_strings);
|
|
}
|
|
}
|
|
|
|
void
|
|
svec_sort_unique(struct svec *svec)
|
|
{
|
|
svec_sort(svec);
|
|
svec_unique(svec);
|
|
}
|
|
|
|
void
|
|
svec_unique(struct svec *svec)
|
|
{
|
|
ovs_assert(svec_is_sorted(svec));
|
|
if (svec->n > 1) {
|
|
/* This algorithm is lazy and sub-optimal, but it's "obviously correct"
|
|
* and asymptotically optimal . */
|
|
struct svec tmp;
|
|
size_t i;
|
|
|
|
svec_init(&tmp);
|
|
svec_add(&tmp, svec->names[0]);
|
|
for (i = 1; i < svec->n; i++) {
|
|
if (strcmp(svec->names[i - 1], svec->names[i])) {
|
|
svec_add(&tmp, svec->names[i]);
|
|
}
|
|
}
|
|
svec_swap(&tmp, svec);
|
|
svec_destroy(&tmp);
|
|
}
|
|
}
|
|
|
|
void
|
|
svec_compact(struct svec *svec)
|
|
{
|
|
size_t i, j;
|
|
|
|
for (i = j = 0; i < svec->n; i++) {
|
|
if (svec->names[i] != NULL) {
|
|
svec->names[j++] = svec->names[i];
|
|
}
|
|
}
|
|
svec->n = j;
|
|
}
|
|
|
|
static void
|
|
swap_strings(char **a, char **b)
|
|
{
|
|
char *tmp = *a;
|
|
*a = *b;
|
|
*b = tmp;
|
|
}
|
|
|
|
void
|
|
svec_shuffle(struct svec *svec)
|
|
{
|
|
for (size_t i = 0; i < svec->n; i++) {
|
|
size_t j = i + random_range(svec->n - i);
|
|
swap_strings(&svec->names[i], &svec->names[j]);
|
|
}
|
|
}
|
|
|
|
void
|
|
svec_diff(const struct svec *a, const struct svec *b,
|
|
struct svec *a_only, struct svec *both, struct svec *b_only)
|
|
{
|
|
size_t i, j;
|
|
|
|
ovs_assert(svec_is_sorted(a));
|
|
ovs_assert(svec_is_sorted(b));
|
|
if (a_only) {
|
|
svec_init(a_only);
|
|
}
|
|
if (both) {
|
|
svec_init(both);
|
|
}
|
|
if (b_only) {
|
|
svec_init(b_only);
|
|
}
|
|
for (i = j = 0; i < a->n && j < b->n; ) {
|
|
int cmp = strcmp(a->names[i], b->names[j]);
|
|
if (cmp < 0) {
|
|
if (a_only) {
|
|
svec_add(a_only, a->names[i]);
|
|
}
|
|
i++;
|
|
} else if (cmp > 0) {
|
|
if (b_only) {
|
|
svec_add(b_only, b->names[j]);
|
|
}
|
|
j++;
|
|
} else {
|
|
if (both) {
|
|
svec_add(both, a->names[i]);
|
|
}
|
|
i++;
|
|
j++;
|
|
}
|
|
}
|
|
if (a_only) {
|
|
for (; i < a->n; i++) {
|
|
svec_add(a_only, a->names[i]);
|
|
}
|
|
}
|
|
if (b_only) {
|
|
for (; j < b->n; j++) {
|
|
svec_add(b_only, b->names[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
svec_contains(const struct svec *svec, const char *name)
|
|
{
|
|
return svec_find(svec, name) != SIZE_MAX;
|
|
}
|
|
|
|
bool
|
|
svec_contains_unsorted(const struct svec *svec, const char *name)
|
|
{
|
|
for (size_t i = 0; i < svec->n; i++) {
|
|
if (!strcmp(svec->names[i], name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t
|
|
svec_find(const struct svec *svec, const char *name)
|
|
{
|
|
char **p;
|
|
|
|
ovs_assert(svec_is_sorted(svec));
|
|
p = bsearch(&name, svec->names, svec->n, sizeof *svec->names,
|
|
compare_strings);
|
|
return p ? p - svec->names : SIZE_MAX;
|
|
}
|
|
|
|
bool
|
|
svec_is_sorted(const struct svec *svec)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 1; i < svec->n; i++) {
|
|
if (strcmp(svec->names[i - 1], svec->names[i]) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
svec_is_unique(const struct svec *svec)
|
|
{
|
|
return svec_get_duplicate(svec) == NULL;
|
|
}
|
|
|
|
const char *
|
|
svec_get_duplicate(const struct svec *svec)
|
|
{
|
|
ovs_assert(svec_is_sorted(svec));
|
|
if (svec->n > 1) {
|
|
size_t i;
|
|
for (i = 1; i < svec->n; i++) {
|
|
if (!strcmp(svec->names[i - 1], svec->names[i])) {
|
|
return svec->names[i];
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
svec_swap(struct svec *a, struct svec *b)
|
|
{
|
|
struct svec tmp = *a;
|
|
*a = *b;
|
|
*b = tmp;
|
|
}
|
|
|
|
void
|
|
svec_print(const struct svec *svec, const char *title)
|
|
{
|
|
size_t i;
|
|
|
|
printf("%s:\n", title);
|
|
for (i = 0; i < svec->n; i++) {
|
|
printf("\"%s\"\n", svec->names[i]);
|
|
}
|
|
}
|
|
|
|
/* Breaks 'words' into words at white space, respecting shell-like quoting
|
|
* conventions, and appends the words to 'svec'. */
|
|
void
|
|
svec_parse_words(struct svec *svec, const char *words)
|
|
{
|
|
struct ds word = DS_EMPTY_INITIALIZER;
|
|
const char *p, *q;
|
|
|
|
for (p = words; *p != '\0'; p = q) {
|
|
int quote = 0;
|
|
|
|
while (isspace((unsigned char) *p)) {
|
|
p++;
|
|
}
|
|
if (*p == '\0') {
|
|
break;
|
|
}
|
|
|
|
ds_clear(&word);
|
|
for (q = p; *q != '\0'; q++) {
|
|
if (*q == quote) {
|
|
quote = 0;
|
|
} else if (*q == '\'' || *q == '"') {
|
|
quote = *q;
|
|
} else if (*q == '\\' && (!quote || quote == '"')) {
|
|
q++;
|
|
if (*q == '\0') {
|
|
VLOG_WARN("%s: ends in trailing backslash", words);
|
|
break;
|
|
}
|
|
ds_put_char(&word, *q);
|
|
} else if (isspace((unsigned char) *q) && !quote) {
|
|
q++;
|
|
break;
|
|
} else {
|
|
ds_put_char(&word, *q);
|
|
}
|
|
}
|
|
svec_add(svec, ds_cstr(&word));
|
|
if (quote) {
|
|
VLOG_WARN("%s: word ends inside quoted string", words);
|
|
}
|
|
}
|
|
ds_destroy(&word);
|
|
}
|
|
|
|
bool
|
|
svec_equal(const struct svec *a, const struct svec *b)
|
|
{
|
|
size_t i;
|
|
|
|
if (a->n != b->n) {
|
|
return false;
|
|
}
|
|
for (i = 0; i < a->n; i++) {
|
|
if (strcmp(a->names[i], b->names[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char *
|
|
svec_join(const struct svec *svec,
|
|
const char *delimiter, const char *terminator)
|
|
{
|
|
struct ds ds;
|
|
size_t i;
|
|
|
|
ds_init(&ds);
|
|
for (i = 0; i < svec->n; i++) {
|
|
if (i) {
|
|
ds_put_cstr(&ds, delimiter);
|
|
}
|
|
ds_put_cstr(&ds, svec->names[i]);
|
|
}
|
|
ds_put_cstr(&ds, terminator);
|
|
return ds_cstr(&ds);
|
|
}
|
|
|
|
const char *
|
|
svec_back(const struct svec *svec)
|
|
{
|
|
ovs_assert(svec->n);
|
|
return svec->names[svec->n - 1];
|
|
}
|
|
|
|
void
|
|
svec_pop_back(struct svec *svec)
|
|
{
|
|
ovs_assert(svec->n);
|
|
free(svec->names[--svec->n]);
|
|
}
|