mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-13 06:30:10 +00:00
Rebased the branch to trunk
This commit is contained in:
@@ -3581,6 +3581,32 @@ sub name {
|
|||||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub remove_duplicate_cxns {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @cxns = @{$args{cxns}};
|
||||||
|
my $seen_ids = $args{seen_ids} || {};
|
||||||
|
PTDEBUG && _d("Removing duplicates from ", join(" ", map { $_->name } @cxns));
|
||||||
|
my @trimmed_cxns;
|
||||||
|
|
||||||
|
for my $cxn ( @cxns ) {
|
||||||
|
my $dbh = $cxn->dbh();
|
||||||
|
my $sql = q{SELECT @@server_id};
|
||||||
|
PTDEBUG && _d($sql);
|
||||||
|
my ($id) = $dbh->selectrow_array($sql);
|
||||||
|
PTDEBUG && _d('Server ID for ', $cxn->name, ': ', $id);
|
||||||
|
|
||||||
|
if ( ! $seen_ids->{$id}++ ) {
|
||||||
|
push @trimmed_cxns, $cxn
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PTDEBUG && _d("Removing ", $cxn->name,
|
||||||
|
", ID ", $id, ", because we've already seen it");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@trimmed_cxns;
|
||||||
|
}
|
||||||
|
|
||||||
sub DESTROY {
|
sub DESTROY {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
@@ -3634,6 +3660,8 @@ use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
|||||||
use Lmo;
|
use Lmo;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
|
||||||
|
{ local $EVAL_ERROR; eval { require Cxn } };
|
||||||
|
|
||||||
sub get_cluster_name {
|
sub get_cluster_name {
|
||||||
my ($self, $cxn) = @_;
|
my ($self, $cxn) = @_;
|
||||||
my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'";
|
my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'";
|
||||||
@@ -3667,6 +3695,57 @@ sub same_node {
|
|||||||
return ($val1 || '') eq ($val2 || '');
|
return ($val1 || '') eq ($val2 || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub find_cluster_nodes {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
|
my $dbh = $args{dbh};
|
||||||
|
my $dsn = $args{dsn};
|
||||||
|
my $dp = $args{DSNParser};
|
||||||
|
my $make_cxn = $args{make_cxn};
|
||||||
|
|
||||||
|
|
||||||
|
my $sql = q{SHOW STATUS LIKE 'wsrep\_incoming\_addresses'};
|
||||||
|
PTDEBUG && _d($sql);
|
||||||
|
my (undef, $addresses) = $dbh->selectrow_array($sql);
|
||||||
|
PTDEBUG && _d("Cluster nodes found: ", $addresses);
|
||||||
|
return unless $addresses;
|
||||||
|
|
||||||
|
my @addresses = grep { !/\Aunspecified\z/i }
|
||||||
|
split /,\s*/, $addresses;
|
||||||
|
|
||||||
|
my @nodes;
|
||||||
|
foreach my $address ( @addresses ) {
|
||||||
|
my ($host, $port) = split /:/, $address;
|
||||||
|
my $spec = "h=$host"
|
||||||
|
. ($port ? ",P=$port" : "");
|
||||||
|
my $node_dsn = $dp->parse($spec, $dsn);
|
||||||
|
my $node_dbh = eval { $dp->get_dbh(
|
||||||
|
$dp->get_cxn_params($node_dsn), { AutoCommit => 1 }) };
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
print STDERR "Cannot connect to ", $dp->as_string($node_dsn),
|
||||||
|
", discovered through $sql: $EVAL_ERROR\n";
|
||||||
|
if ( !$port && $dsn->{P} != 3306 ) {
|
||||||
|
$address .= ":3306";
|
||||||
|
redo;
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
PTDEBUG && _d('Connected to', $dp->as_string($node_dsn));
|
||||||
|
$node_dbh->disconnect();
|
||||||
|
|
||||||
|
push @nodes, $make_cxn->(dsn => $node_dsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub remove_duplicate_cxns {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @cxns = @{$args{cxns}};
|
||||||
|
my $seen_ids = $args{seen_ids};
|
||||||
|
return Cxn->remove_duplicate_cxns(%args);
|
||||||
|
}
|
||||||
|
|
||||||
sub same_cluster {
|
sub same_cluster {
|
||||||
my ($self, $cxn1, $cxn2) = @_;
|
my ($self, $cxn1, $cxn2) = @_;
|
||||||
|
|
||||||
@@ -3678,6 +3757,59 @@ sub same_cluster {
|
|||||||
return ($cluster1 || '') eq ($cluster2 || '');
|
return ($cluster1 || '') eq ($cluster2 || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub autodetect_nodes {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my $ms = $args{MasterSlave};
|
||||||
|
my $dp = $args{DSNParser};
|
||||||
|
my $make_cxn = $args{make_cxn};
|
||||||
|
my $nodes = $args{nodes};
|
||||||
|
my $seen_ids = $args{seen_ids};
|
||||||
|
|
||||||
|
my $new_nodes = [];
|
||||||
|
|
||||||
|
return $new_nodes unless @$nodes;
|
||||||
|
|
||||||
|
for my $node ( @$nodes ) {
|
||||||
|
my $nodes_found = $self->find_cluster_nodes(
|
||||||
|
dbh => $node->dbh(),
|
||||||
|
dsn => $node->dsn(),
|
||||||
|
make_cxn => $make_cxn,
|
||||||
|
DSNParser => $dp,
|
||||||
|
);
|
||||||
|
push @$new_nodes, @$nodes_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_nodes = $self->remove_duplicate_cxns(
|
||||||
|
cxns => $new_nodes,
|
||||||
|
seen_ids => $seen_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
my $new_slaves = [];
|
||||||
|
foreach my $node (@$new_nodes) {
|
||||||
|
my $node_slaves = $ms->get_slaves(
|
||||||
|
dbh => $node->dbh(),
|
||||||
|
dsn => $node->dsn(),
|
||||||
|
make_cxn => $make_cxn,
|
||||||
|
);
|
||||||
|
push @$new_slaves, @$node_slaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_slaves = $self->remove_duplicate_cxns(
|
||||||
|
cxns => $new_slaves,
|
||||||
|
seen_ids => $seen_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
my @new_slave_nodes = grep { $self->is_cluster_node($_) } @$new_slaves;
|
||||||
|
|
||||||
|
my $slaves_of_slaves = $self->autodetect_nodes(
|
||||||
|
%args,
|
||||||
|
nodes => \@new_slave_nodes,
|
||||||
|
);
|
||||||
|
|
||||||
|
my @autodetected_nodes = ( @$new_nodes, @$new_slaves, @$slaves_of_slaves );
|
||||||
|
return \@autodetected_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
sub _d {
|
sub _d {
|
||||||
my ($package, undef, $line) = caller 0;
|
my ($package, undef, $line) = caller 0;
|
||||||
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
||||||
@@ -4770,6 +4902,8 @@ sub get_slaves {
|
|||||||
my $dp = $self->{DSNParser};
|
my $dp = $self->{DSNParser};
|
||||||
my $methods = $self->_resolve_recursion_methods($args{dsn});
|
my $methods = $self->_resolve_recursion_methods($args{dsn});
|
||||||
|
|
||||||
|
return $slaves unless @$methods;
|
||||||
|
|
||||||
if ( grep { m/processlist|hosts/i } @$methods ) {
|
if ( grep { m/processlist|hosts/i } @$methods ) {
|
||||||
my @required_args = qw(dbh dsn);
|
my @required_args = qw(dbh dsn);
|
||||||
foreach my $arg ( @required_args ) {
|
foreach my $arg ( @required_args ) {
|
||||||
@@ -8776,11 +8910,22 @@ sub main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $autodiscover_cluster;
|
||||||
|
my $recursion_method = [];
|
||||||
|
foreach my $method ( @{$o->get('recursion-method')} ) {
|
||||||
|
if ( $method eq 'cluster' ) {
|
||||||
|
$autodiscover_cluster = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push @$recursion_method, $method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$o->set('recursion-method', $recursion_method);
|
||||||
eval {
|
eval {
|
||||||
MasterSlave::check_recursion_method($o->get('recursion-method'));
|
MasterSlave::check_recursion_method($o->get('recursion-method'));
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
$o->save_error("Invalid --recursion-method: $EVAL_ERROR")
|
$o->save_error($EVAL_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
$o->usage_or_errors();
|
$o->usage_or_errors();
|
||||||
@@ -8959,33 +9104,44 @@ sub main {
|
|||||||
# #####################################################################
|
# #####################################################################
|
||||||
# Find and connect to slaves.
|
# Find and connect to slaves.
|
||||||
# #####################################################################
|
# #####################################################################
|
||||||
$slaves = $ms->get_slaves(
|
my $make_cxn_cluster = sub {
|
||||||
dbh => $master_dbh,
|
|
||||||
dsn => $master_dsn,
|
|
||||||
make_cxn => sub {
|
|
||||||
my $cxn = $make_cxn->(@_, prev_dsn => $master_cxn->dsn());
|
my $cxn = $make_cxn->(@_, prev_dsn => $master_cxn->dsn());
|
||||||
$cluster_name_for{$cxn} = $cluster->is_cluster_node($cxn);
|
$cluster_name_for{$cxn} = $cluster->is_cluster_node($cxn);
|
||||||
return $cxn;
|
return $cxn;
|
||||||
},
|
};
|
||||||
|
|
||||||
|
$slaves = $ms->get_slaves(
|
||||||
|
dbh => $master_dbh,
|
||||||
|
dsn => $master_dsn,
|
||||||
|
make_cxn => $make_cxn_cluster,
|
||||||
);
|
);
|
||||||
|
|
||||||
# If the "master" is a cluster node, then a DSN table should have been
|
my %seen_ids;
|
||||||
# used, and it may have all nodes' DSNs so the user can run the tool
|
for my $cxn ($master_cxn, @$slaves) {
|
||||||
# on any node, in which case it has the "master" node, the DSN given
|
my $dbh = $cxn->dbh();
|
||||||
# on the command line. So detect and remove this dupe.
|
my $sql = q{SELECT @@server_id};
|
||||||
if ( $cluster_name_for{$master_cxn} ) {
|
PTDEBUG && _d($cxn, $dbh, $sql);
|
||||||
@$slaves = grep {
|
my ($id) = $dbh->selectrow_array($sql);
|
||||||
my $slave_cxn = $_;
|
$seen_ids{$id}++;
|
||||||
if ( $cluster->same_node($master_cxn, $slave_cxn) ) {
|
|
||||||
PTDEBUG && _d('Removing ', $slave_cxn->name, 'from slaves',
|
|
||||||
'because it is the master');
|
|
||||||
0;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$slave_cxn;
|
if ( $autodiscover_cluster ) {
|
||||||
}
|
my @known_nodes = grep { $cluster_name_for{$_} } $master_cxn, @$slaves;
|
||||||
} @$slaves;
|
my $new_cxns = $cluster->autodetect_nodes(
|
||||||
|
nodes => \@known_nodes,
|
||||||
|
MasterSlave => $ms,
|
||||||
|
DSNParser => $dp,
|
||||||
|
make_cxn => $make_cxn_cluster,
|
||||||
|
seen_ids => \%seen_ids,
|
||||||
|
);
|
||||||
|
push @$slaves, @$new_cxns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $trimmed_nodes = Cxn->remove_duplicate_cxns(
|
||||||
|
cxns => [ $master_cxn, @$slaves ],
|
||||||
|
);
|
||||||
|
($master_cxn, @$slaves) = @$trimmed_nodes;
|
||||||
|
|
||||||
PTDEBUG && _d(scalar @$slaves, 'slaves found');
|
PTDEBUG && _d(scalar @$slaves, 'slaves found');
|
||||||
if ( !@$slaves && $o->get('recursion-method')->[0] ne 'none' ) {
|
if ( !@$slaves && $o->get('recursion-method')->[0] ne 'none' ) {
|
||||||
$exit_status |= 1;
|
$exit_status |= 1;
|
||||||
@@ -9090,7 +9246,7 @@ sub main {
|
|||||||
warn "Diffs will only be detected if the cluster is "
|
warn "Diffs will only be detected if the cluster is "
|
||||||
. "consistent with " . $direct_slave->name . " because "
|
. "consistent with " . $direct_slave->name . " because "
|
||||||
. $master_cxn->name . " is a traditional replication master "
|
. $master_cxn->name . " is a traditional replication master "
|
||||||
. " but these replicas are cluster nodes:\n"
|
. "but these replicas are cluster nodes:\n"
|
||||||
. join("\n", map { ' ' . $_->name } @nodes) . "\n"
|
. join("\n", map { ' ' . $_->name } @nodes) . "\n"
|
||||||
. "For more information, please read the Percona XtraDB "
|
. "For more information, please read the Percona XtraDB "
|
||||||
. "Cluster section of the tool's documentation.\n";
|
. "Cluster section of the tool's documentation.\n";
|
||||||
@@ -11765,6 +11921,7 @@ Possible methods are:
|
|||||||
=========== ==================
|
=========== ==================
|
||||||
processlist SHOW PROCESSLIST
|
processlist SHOW PROCESSLIST
|
||||||
hosts SHOW SLAVE HOSTS
|
hosts SHOW SLAVE HOSTS
|
||||||
|
cluster SHOW STATUS LIKE 'wsrep\_incoming\_addresses'
|
||||||
dsn=DSN DSNs from a table
|
dsn=DSN DSNs from a table
|
||||||
none Do not find slaves
|
none Do not find slaves
|
||||||
|
|
||||||
@@ -11775,6 +11932,13 @@ the C<hosts> method becomes the default because it works better in this case.
|
|||||||
The C<hosts> method requires replicas to be configured with C<report_host>,
|
The C<hosts> method requires replicas to be configured with C<report_host>,
|
||||||
C<report_port>, etc.
|
C<report_port>, etc.
|
||||||
|
|
||||||
|
The C<cluster> method requires a cluster based on Galera 23.7.3 or newer,
|
||||||
|
such as Percona XtraDB Cluster versions 5.5.29 and above. This will
|
||||||
|
autodiscover nodes in a cluster using C<SHOW STATUS LIKE 'wsrep\_incoming\_addresses'>.
|
||||||
|
Note that you can combine C<cluster> with C<processlist> and C<hosts> to
|
||||||
|
have the tool autodiscover all cluster nodes and traditional slaves,
|
||||||
|
but this is considered highly experimental.
|
||||||
|
|
||||||
The C<dsn> method is special: rather than automatically discovering replicas,
|
The C<dsn> method is special: rather than automatically discovering replicas,
|
||||||
this method specifies a table with replica DSNs. The tool will only connect
|
this method specifies a table with replica DSNs. The tool will only connect
|
||||||
to these replicas. This method works best when replicas do not use the same
|
to these replicas. This method works best when replicas do not use the same
|
||||||
|
36
lib/Cxn.pm
36
lib/Cxn.pm
@@ -220,6 +220,42 @@ sub name {
|
|||||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# There's two reasons why there might be dupes:
|
||||||
|
# If the "master" is a cluster node, then a DSN table might have been
|
||||||
|
# used, and it may have all nodes' DSNs so the user can run the tool
|
||||||
|
# on any node, in which case it has the "master" node, the DSN given
|
||||||
|
# on the command line.
|
||||||
|
# On the other hand, maybe find_cluster_nodes worked, in which case
|
||||||
|
# we definitely have a dupe for the master cxn, but we may also have a
|
||||||
|
# dupe for every other node if this was unsed in conjunction with a
|
||||||
|
# DSN table.
|
||||||
|
# So try to detect and remove those.
|
||||||
|
sub remove_duplicate_cxns {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @cxns = @{$args{cxns}};
|
||||||
|
my $seen_ids = $args{seen_ids} || {};
|
||||||
|
PTDEBUG && _d("Removing duplicates from ", join(" ", map { $_->name } @cxns));
|
||||||
|
my @trimmed_cxns;
|
||||||
|
|
||||||
|
for my $cxn ( @cxns ) {
|
||||||
|
my $dbh = $cxn->dbh();
|
||||||
|
my $sql = q{SELECT @@server_id};
|
||||||
|
PTDEBUG && _d($sql);
|
||||||
|
my ($id) = $dbh->selectrow_array($sql);
|
||||||
|
PTDEBUG && _d('Server ID for ', $cxn->name, ': ', $id);
|
||||||
|
|
||||||
|
if ( ! $seen_ids->{$id}++ ) {
|
||||||
|
push @trimmed_cxns, $cxn
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PTDEBUG && _d("Removing ", $cxn->name,
|
||||||
|
", ID ", $id, ", because we've already seen it");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@trimmed_cxns;
|
||||||
|
}
|
||||||
|
|
||||||
sub DESTROY {
|
sub DESTROY {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
@@ -73,6 +73,8 @@ sub get_slaves {
|
|||||||
my $dp = $self->{DSNParser};
|
my $dp = $self->{DSNParser};
|
||||||
my $methods = $self->_resolve_recursion_methods($args{dsn});
|
my $methods = $self->_resolve_recursion_methods($args{dsn});
|
||||||
|
|
||||||
|
return $slaves unless @$methods;
|
||||||
|
|
||||||
if ( grep { m/processlist|hosts/i } @$methods ) {
|
if ( grep { m/processlist|hosts/i } @$methods ) {
|
||||||
my @required_args = qw(dbh dsn);
|
my @required_args = qw(dbh dsn);
|
||||||
foreach my $arg ( @required_args ) {
|
foreach my $arg ( @required_args ) {
|
||||||
@@ -114,7 +116,6 @@ sub _resolve_recursion_methods {
|
|||||||
my ($self, $dsn) = @_;
|
my ($self, $dsn) = @_;
|
||||||
my $o = $self->{OptionParser};
|
my $o = $self->{OptionParser};
|
||||||
if ( $o->got('recursion-method') ) {
|
if ( $o->got('recursion-method') ) {
|
||||||
# Use whatever the user explicitly gave on the command line.
|
|
||||||
return $o->get('recursion-method');
|
return $o->get('recursion-method');
|
||||||
}
|
}
|
||||||
elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
|
elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
|
||||||
|
@@ -30,6 +30,8 @@ use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
|||||||
use Lmo;
|
use Lmo;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
|
||||||
|
{ local $EVAL_ERROR; eval { require Cxn } };
|
||||||
|
|
||||||
sub get_cluster_name {
|
sub get_cluster_name {
|
||||||
my ($self, $cxn) = @_;
|
my ($self, $cxn) = @_;
|
||||||
my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'";
|
my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'";
|
||||||
@@ -63,6 +65,70 @@ sub same_node {
|
|||||||
return ($val1 || '') eq ($val2 || '');
|
return ($val1 || '') eq ($val2 || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO: Check that the PXC version supports wsrep_incoming_addresses
|
||||||
|
# Not really necessary, actually. But in case it's needed,
|
||||||
|
# wsrep_provider_version =~ /[0-9]+\.[0-9]+\(r([0-9]+)\)/ && $1 >= 137
|
||||||
|
sub find_cluster_nodes {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
|
my $dbh = $args{dbh};
|
||||||
|
my $dsn = $args{dsn};
|
||||||
|
my $dp = $args{DSNParser};
|
||||||
|
my $make_cxn = $args{make_cxn};
|
||||||
|
|
||||||
|
# Ostensibly the caller should've done this already, but
|
||||||
|
# useful for safety.
|
||||||
|
# TODO this fails with a strange error.
|
||||||
|
#$dp->fill_in_dsn($dbh, $dsn);
|
||||||
|
|
||||||
|
my $sql = q{SHOW STATUS LIKE 'wsrep\_incoming\_addresses'};
|
||||||
|
PTDEBUG && _d($sql);
|
||||||
|
my (undef, $addresses) = $dbh->selectrow_array($sql);
|
||||||
|
PTDEBUG && _d("Cluster nodes found: ", $addresses);
|
||||||
|
return unless $addresses;
|
||||||
|
|
||||||
|
my @addresses = grep { !/\Aunspecified\z/i }
|
||||||
|
split /,\s*/, $addresses;
|
||||||
|
|
||||||
|
my @nodes;
|
||||||
|
foreach my $address ( @addresses ) {
|
||||||
|
my ($host, $port) = split /:/, $address;
|
||||||
|
my $spec = "h=$host"
|
||||||
|
. ($port ? ",P=$port" : "");
|
||||||
|
my $node_dsn = $dp->parse($spec, $dsn);
|
||||||
|
my $node_dbh = eval { $dp->get_dbh(
|
||||||
|
$dp->get_cxn_params($node_dsn), { AutoCommit => 1 }) };
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
print STDERR "Cannot connect to ", $dp->as_string($node_dsn),
|
||||||
|
", discovered through $sql: $EVAL_ERROR\n";
|
||||||
|
# This is a bit strange, so an explanation is called for.
|
||||||
|
# If there wasn't a port, that means that this bug
|
||||||
|
# https://bugs.launchpad.net/percona-toolkit/+bug/1082406
|
||||||
|
# isn't fixed on this version of PXC. We tried using the
|
||||||
|
# master's port, but that didn't work. So try again, using
|
||||||
|
# the default port.
|
||||||
|
if ( !$port && $dsn->{P} != 3306 ) {
|
||||||
|
$address .= ":3306";
|
||||||
|
redo;
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
PTDEBUG && _d('Connected to', $dp->as_string($node_dsn));
|
||||||
|
$node_dbh->disconnect();
|
||||||
|
|
||||||
|
push @nodes, $make_cxn->(dsn => $node_dsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub remove_duplicate_cxns {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @cxns = @{$args{cxns}};
|
||||||
|
my $seen_ids = $args{seen_ids};
|
||||||
|
return Cxn->remove_duplicate_cxns(%args);
|
||||||
|
}
|
||||||
|
|
||||||
sub same_cluster {
|
sub same_cluster {
|
||||||
my ($self, $cxn1, $cxn2) = @_;
|
my ($self, $cxn1, $cxn2) = @_;
|
||||||
|
|
||||||
@@ -75,6 +141,61 @@ sub same_cluster {
|
|||||||
return ($cluster1 || '') eq ($cluster2 || '');
|
return ($cluster1 || '') eq ($cluster2 || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub autodetect_nodes {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my $ms = $args{MasterSlave};
|
||||||
|
my $dp = $args{DSNParser};
|
||||||
|
my $make_cxn = $args{make_cxn};
|
||||||
|
my $nodes = $args{nodes};
|
||||||
|
my $seen_ids = $args{seen_ids};
|
||||||
|
|
||||||
|
my $new_nodes = [];
|
||||||
|
|
||||||
|
return $new_nodes unless @$nodes;
|
||||||
|
|
||||||
|
for my $node ( @$nodes ) {
|
||||||
|
my $nodes_found = $self->find_cluster_nodes(
|
||||||
|
dbh => $node->dbh(),
|
||||||
|
dsn => $node->dsn(),
|
||||||
|
make_cxn => $make_cxn,
|
||||||
|
DSNParser => $dp,
|
||||||
|
);
|
||||||
|
push @$new_nodes, @$nodes_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_nodes = $self->remove_duplicate_cxns(
|
||||||
|
cxns => $new_nodes,
|
||||||
|
seen_ids => $seen_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
my $new_slaves = [];
|
||||||
|
foreach my $node (@$new_nodes) {
|
||||||
|
my $node_slaves = $ms->get_slaves(
|
||||||
|
dbh => $node->dbh(),
|
||||||
|
dsn => $node->dsn(),
|
||||||
|
make_cxn => $make_cxn,
|
||||||
|
);
|
||||||
|
push @$new_slaves, @$node_slaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_slaves = $self->remove_duplicate_cxns(
|
||||||
|
cxns => $new_slaves,
|
||||||
|
seen_ids => $seen_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
# If some of the new slaves is a cluster node, autodetect new nodes
|
||||||
|
# from there too.
|
||||||
|
my @new_slave_nodes = grep { $self->is_cluster_node($_) } @$new_slaves;
|
||||||
|
|
||||||
|
my $slaves_of_slaves = $self->autodetect_nodes(
|
||||||
|
%args,
|
||||||
|
nodes => \@new_slave_nodes,
|
||||||
|
);
|
||||||
|
|
||||||
|
my @autodetected_nodes = ( @$new_nodes, @$new_slaves, @$slaves_of_slaves );
|
||||||
|
return \@autodetected_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
sub _d {
|
sub _d {
|
||||||
my ($package, undef, $line) = caller 0;
|
my ($package, undef, $line) = caller 0;
|
||||||
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
||||||
|
@@ -31,7 +31,7 @@ binlog_format = ROW
|
|||||||
wsrep_provider = LIBGALERA
|
wsrep_provider = LIBGALERA
|
||||||
wsrep_cluster_address = CLUSTER_AD
|
wsrep_cluster_address = CLUSTER_AD
|
||||||
wsrep_sst_receive_address = ADDR:RECEIVE_PRT
|
wsrep_sst_receive_address = ADDR:RECEIVE_PRT
|
||||||
wsrep_node_incoming_address= ADDR
|
wsrep_node_incoming_address= ADDR:PORT
|
||||||
wsrep_slave_threads = 2
|
wsrep_slave_threads = 2
|
||||||
wsrep_cluster_name = CLUSTER_NAME
|
wsrep_cluster_name = CLUSTER_NAME
|
||||||
wsrep_provider_options = "gmcast.listen_addr=tcp://ADDR:LISTEN_PRT;"
|
wsrep_provider_options = "gmcast.listen_addr=tcp://ADDR:LISTEN_PRT;"
|
||||||
|
@@ -22,6 +22,8 @@ use PerconaTest;
|
|||||||
use Sandbox;
|
use Sandbox;
|
||||||
require "$trunk/bin/pt-table-checksum";
|
require "$trunk/bin/pt-table-checksum";
|
||||||
|
|
||||||
|
my $ip = qr/\Q127.1\E|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
|
||||||
|
|
||||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||||
my $node1 = $sb->get_dbh_for('node1');
|
my $node1 = $sb->get_dbh_for('node1');
|
||||||
@@ -75,34 +77,41 @@ $output = output(
|
|||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/h=127.1,P=12345 is a cluster node but no other nodes/,
|
qr/h=127(?:\Q.0.0\E)?.1,P=12345 is a cluster node but no other nodes/,
|
||||||
"Dies if no other nodes are found"
|
"Dies if no other nodes are found"
|
||||||
);
|
);
|
||||||
|
|
||||||
$output = output(
|
for my $args (
|
||||||
|
["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"],
|
||||||
|
["using recursion-method=cluster", '--recursion-method', 'cluster']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $test = shift @$args;
|
||||||
|
$output = output(
|
||||||
sub { pt_table_checksum::main(@args,
|
sub { pt_table_checksum::main(@args,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns")
|
@$args)
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'errors'),
|
PerconaTest::count_checksum_results($output, 'errors'),
|
||||||
0,
|
0,
|
||||||
"No diffs: no errors"
|
"No diffs: no errors ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'skipped'),
|
PerconaTest::count_checksum_results($output, 'skipped'),
|
||||||
0,
|
0,
|
||||||
"No diffs: no skips"
|
"No diffs: no skips ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
0,
|
0,
|
||||||
"No diffs: no diffs"
|
"No diffs: no diffs ($test)"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
# Now really test checksumming a cluster. To create a diff we have to disable
|
# Now really test checksumming a cluster. To create a diff we have to disable
|
||||||
# the binlog. Although PXC doesn't need or use the binlog to communicate
|
# the binlog. Although PXC doesn't need or use the binlog to communicate
|
||||||
@@ -135,33 +144,40 @@ is(
|
|||||||
"Node3 not changed"
|
"Node3 not changed"
|
||||||
);
|
);
|
||||||
|
|
||||||
$output = output(
|
for my $args (
|
||||||
|
["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"],
|
||||||
|
["using recursion-method=cluster", '--recursion-method', 'cluster']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $test = shift @$args;
|
||||||
|
|
||||||
|
$output = output(
|
||||||
sub { pt_table_checksum::main(@args,
|
sub { pt_table_checksum::main(@args,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns")
|
@$args)
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'errors'),
|
PerconaTest::count_checksum_results($output, 'errors'),
|
||||||
0,
|
0,
|
||||||
"1 diff: no errors"
|
"1 diff: no errors ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'skipped'),
|
PerconaTest::count_checksum_results($output, 'skipped'),
|
||||||
0,
|
0,
|
||||||
"1 diff: no skips"
|
"1 diff: no skips ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
1,
|
1,
|
||||||
"1 diff: 1 diff"
|
"1 diff: 1 diff ($test)"
|
||||||
) or diag($output);
|
) or diag($output);
|
||||||
|
|
||||||
# 11-17T13:02:54 0 1 26 1 0 0.021 test.t
|
# 11-17T13:02:54 0 1 26 1 0 0.021 test.t
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/^\S+\s+ # ts
|
qr/^\S+\s+ # ts
|
||||||
0\s+ # errors
|
0\s+ # errors
|
||||||
@@ -172,8 +188,9 @@ like(
|
|||||||
\S+\s+ # time
|
\S+\s+ # time
|
||||||
test.t$ # table
|
test.t$ # table
|
||||||
/xm,
|
/xm,
|
||||||
"1 diff: it's in test.t"
|
"1 diff: it's in test.t ($test)"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# cluster, node1 -> slave, run on node1
|
# cluster, node1 -> slave, run on node1
|
||||||
@@ -205,33 +222,48 @@ $slave_dbh->do("update test.t set c='zebra' where c='z'");
|
|||||||
# https://bugs.launchpad.net/percona-toolkit/+bug/1080385
|
# https://bugs.launchpad.net/percona-toolkit/+bug/1080385
|
||||||
# Cluster nodes default to ROW format because that's what Galeara
|
# Cluster nodes default to ROW format because that's what Galeara
|
||||||
# works best with, even though it doesn't really use binlogs.
|
# works best with, even though it doesn't really use binlogs.
|
||||||
$output = output(
|
for my $args (
|
||||||
|
["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"],
|
||||||
|
["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $test = shift @$args;
|
||||||
|
|
||||||
|
# Wait for the slave to apply the binlogs from node1 (its master).
|
||||||
|
# Then change it so it's not consistent.
|
||||||
|
PerconaTest::wait_for_table($slave_dbh, 'test.t');
|
||||||
|
$sb->wait_for_slaves('cslave1');
|
||||||
|
$slave_dbh->do("update test.t set c='zebra' where c='z'");
|
||||||
|
|
||||||
|
$output = output(
|
||||||
sub { pt_table_checksum::main(@args,
|
sub { pt_table_checksum::main(@args,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns")
|
@$args)
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/replica h=127.1,P=12348 has binlog_format ROW/i,
|
qr/replica h=127(?:\Q.0.0\E)?\.1,P=12348 has binlog_format ROW/i,
|
||||||
"--check-binlog-format warns about slave's binlog format"
|
"--check-binlog-format warns about slave's binlog format ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Now really test that diffs on the slave are detected.
|
# Now really test that diffs on the slave are detected.
|
||||||
$output = output(
|
$output = output(
|
||||||
sub { pt_table_checksum::main(@args,
|
sub { pt_table_checksum::main(@args,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns",
|
@$args,
|
||||||
qw(--no-check-binlog-format)),
|
qw(--no-check-binlog-format)),
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
1,
|
1,
|
||||||
"Detects diffs on slave of cluster node1"
|
"Detects diffs on slave of cluster node1 ($test)"
|
||||||
) or diag($output);
|
) or diag($output);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$slave_dbh->disconnect;
|
$slave_dbh->disconnect;
|
||||||
$sb->stop_sandbox('cslave1');
|
$sb->stop_sandbox('cslave1');
|
||||||
@@ -267,19 +299,27 @@ is(
|
|||||||
"Slave is changed"
|
"Slave is changed"
|
||||||
);
|
);
|
||||||
|
|
||||||
$output = output(
|
for my $args (
|
||||||
|
["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"],
|
||||||
|
["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $test = shift @$args;
|
||||||
|
|
||||||
|
$output = output(
|
||||||
sub { pt_table_checksum::main(@args,
|
sub { pt_table_checksum::main(@args,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns",
|
@$args,
|
||||||
qw(--no-check-binlog-format -d test)),
|
qw(--no-check-binlog-format -d test)),
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
0,
|
0,
|
||||||
"Limitation: does not detect diffs on slave of cluster node2"
|
"Limitation: does not detect diffs on slave of cluster node2 ($test)"
|
||||||
) or diag($output);
|
) or diag($output);
|
||||||
|
}
|
||||||
|
|
||||||
$slave_dbh->disconnect;
|
$slave_dbh->disconnect;
|
||||||
$sb->stop_sandbox('cslave1');
|
$sb->stop_sandbox('cslave1');
|
||||||
@@ -454,28 +494,43 @@ like(
|
|||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/the direct replica of h=127.1,P=12349 was not found or specified/,
|
qr/the direct replica of h=$ip,P=12349 was not found or specified/,
|
||||||
"Warns that direct replica of the master isn't found or specified",
|
"Warns that direct replica of the master isn't found or specified",
|
||||||
);
|
);
|
||||||
|
|
||||||
# Use the other DSN table with all three nodes. Now the tool should
|
# Use the other DSN table with all three nodes. Now the tool should
|
||||||
# give a more specific warning than that ^.
|
# give a more specific warning than that ^.
|
||||||
$output = output(
|
# Originally, these tested a dsn table with all nodes; now we hijack
|
||||||
|
# those tests to also try the autodetection
|
||||||
|
for my $args (
|
||||||
|
["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"],
|
||||||
|
["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $test = shift @$args;
|
||||||
|
|
||||||
|
# Make a diff on node1. If ptc is really auto-detecting node1, then it
|
||||||
|
# should report this diff.
|
||||||
|
$node1->do("set sql_log_bin=0");
|
||||||
|
$node1->do("update test.t set c='zebra' where c='z'");
|
||||||
|
$node1->do("set sql_log_bin=1");
|
||||||
|
|
||||||
|
$output = output(
|
||||||
sub { pt_table_checksum::main($master_dsn,
|
sub { pt_table_checksum::main($master_dsn,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns",
|
@$args,
|
||||||
qw(-d test))
|
qw(-d test))
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
1,
|
1,
|
||||||
"...check all nodes: 1 diff"
|
"...check all nodes: 1 diff ($test)"
|
||||||
) or diag($output);
|
) or diag($output);
|
||||||
|
|
||||||
# 11-17T13:02:54 0 1 26 1 0 0.021 test.t
|
# 11-17T13:02:54 0 1 26 1 0 0.021 test.t
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/^\S+\s+ # ts
|
qr/^\S+\s+ # ts
|
||||||
0\s+ # errors
|
0\s+ # errors
|
||||||
@@ -486,64 +541,66 @@ like(
|
|||||||
\S+\s+ # time
|
\S+\s+ # time
|
||||||
test.t$ # table
|
test.t$ # table
|
||||||
/xm,
|
/xm,
|
||||||
"...check all nodes: it's in test.t"
|
"...check all nodes: it's in test.t ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/Diffs will only be detected if the cluster is consistent with h=127.1,P=12345 because h=127.1,P=12349/,
|
qr/Diffs will only be detected if the cluster is consistent with h=$ip,P=12345 because h=$ip,P=12349/,
|
||||||
"Warns that diffs only detected if cluster consistent with direct replica",
|
"Warns that diffs only detected if cluster consistent with direct replica ($test)",
|
||||||
);
|
);
|
||||||
|
|
||||||
# Restore node1 so the cluster is consistent, but then make node2 differ.
|
# Restore node1 so the cluster is consistent, but then make node2 differ.
|
||||||
# ptc should NOT detect this diff because the checksum query will replicate
|
# ptc should NOT detect this diff because the checksum query will replicate
|
||||||
# to node1, node1 isn't different, so it broadcasts the result in ROW format
|
# to node1, node1 isn't different, so it broadcasts the result in ROW format
|
||||||
# that all is ok, which node2 gets and thus false reports. This is why
|
# that all is ok, which node2 gets and thus false reports. This is why
|
||||||
# those ^ warnings exist.
|
# those ^ warnings exist.
|
||||||
$node1->do("set sql_log_bin=0");
|
$node1->do("set sql_log_bin=0");
|
||||||
$node1->do("update test.t set c='z' where c='zebra'");
|
$node1->do("update test.t set c='z' where c='zebra'");
|
||||||
$node1->do("set sql_log_bin=1");
|
$node1->do("set sql_log_bin=1");
|
||||||
|
|
||||||
$node2->do("set sql_log_bin=0");
|
$node2->do("set sql_log_bin=0");
|
||||||
$node2->do("update test.t set c='zebra' where c='z'");
|
$node2->do("update test.t set c='zebra' where c='z'");
|
||||||
$node2->do("set sql_log_bin=1");
|
$node2->do("set sql_log_bin=1");
|
||||||
|
|
||||||
($row) = $node2->selectrow_array("select c from test.t order by c desc limit 1");
|
($row) = $node2->selectrow_array("select c from test.t order by c desc limit 1");
|
||||||
is(
|
is(
|
||||||
$row,
|
$row,
|
||||||
"zebra",
|
"zebra",
|
||||||
"Node2 is changed again"
|
"Node2 is changed again ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
($row) = $node1->selectrow_array("select c from test.t order by c desc limit 1");
|
($row) = $node1->selectrow_array("select c from test.t order by c desc limit 1");
|
||||||
is(
|
is(
|
||||||
$row,
|
$row,
|
||||||
"z",
|
"z",
|
||||||
"Node1 not changed again"
|
"Node1 not changed again ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
($row) = $node3->selectrow_array("select c from test.t order by c desc limit 1");
|
($row) = $node3->selectrow_array("select c from test.t order by c desc limit 1");
|
||||||
is(
|
is(
|
||||||
$row,
|
$row,
|
||||||
"z",
|
"z",
|
||||||
"Node3 not changed again"
|
"Node3 not changed again ($test)"
|
||||||
);
|
);
|
||||||
|
|
||||||
# the other DSN table with all three nodes, but it won't matter because
|
# the other DSN table with all three nodes, but it won't matter because
|
||||||
# node1 is going to broadcast the false-positive that there are no diffs.
|
# node1 is going to broadcast the false-positive that there are no diffs.
|
||||||
$output = output(
|
$output = output(
|
||||||
sub { pt_table_checksum::main($master_dsn,
|
sub { pt_table_checksum::main($master_dsn,
|
||||||
'--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns",
|
@$args,
|
||||||
qw(-d test))
|
qw(-d test))
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is(
|
is(
|
||||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||||
0,
|
0,
|
||||||
"Limitation: diff not on direct replica not detected"
|
"Limitation: diff not on direct replica not detected ($test)"
|
||||||
) or diag($output);
|
) or diag($output);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
# Be sure to stop the slave on node1, else further test will die with:
|
# Be sure to stop the slave on node1, else further test will die with:
|
||||||
@@ -587,13 +644,13 @@ $output = output(
|
|||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/h=127.1,P=12345 is in cluster pt_sandbox_cluster/,
|
qr/h=127(?:\Q.0.0\E)?.1,P=12345 is in cluster pt_sandbox_cluster/,
|
||||||
"Detects that node1 is in pt_sandbox_cluster"
|
"Detects that node1 is in pt_sandbox_cluster"
|
||||||
);
|
);
|
||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr/h=127.1,P=2900 is in cluster cluster2/,
|
qr/h=127(?:\Q.0.0\E)?.1,P=2900 is in cluster cluster2/,
|
||||||
"Detects that node4 is in cluster2"
|
"Detects that node4 is in cluster2"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user