Add a command the displays and modifies the pNFS server's extended attribute.
This command allows a sysadmin to display or modify the pnfsd.dsfile extended attribute used by the pNFS MDS server in various ways. Its main use is to set a DS's IP address to 0.0.0.0 when that DS has failed, so that it will not be used for the file when brought back online after being repaired.
This commit is contained in:
parent
984e340bf1
commit
49d2ead8c5
@ -59,6 +59,7 @@ SUBDIR= adduser \
|
||||
nologin \
|
||||
pciconf \
|
||||
periodic \
|
||||
pnfsdsfile \
|
||||
pnfsdskill \
|
||||
powerd \
|
||||
prometheus_sysctl_exporter \
|
||||
|
6
usr.sbin/pnfsdsfile/Makefile
Normal file
6
usr.sbin/pnfsdsfile/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= pnfsdsfile
|
||||
MAN= pnfsdsfile.8
|
||||
|
||||
.include <bsd.prog.mk>
|
133
usr.sbin/pnfsdsfile/pnfsdsfile.8
Normal file
133
usr.sbin/pnfsdsfile/pnfsdsfile.8
Normal file
@ -0,0 +1,133 @@
|
||||
.\" Copyright (c) 2017 Rick Macklem
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 11, 2018
|
||||
.Dt PNFSDSFILE 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pnfsdsfile
|
||||
.Nd display
|
||||
a pNFS data storage file's location(s) and/or modify the
|
||||
.Dq pnfsd.dsfile
|
||||
extended attribute for them
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl qz
|
||||
.Op Fl s Ar dshostname
|
||||
.Op Fl c Ar old-dshostname,new-dshostname
|
||||
.Op Fl r Ar dshostname
|
||||
.Ar mdsfile
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
command displays the data storage file's location(s) for a pNFS service and/or
|
||||
modifies the
|
||||
.Dq pnfsd.dsfile
|
||||
extended attribute on the
|
||||
.Ar mdsfile .
|
||||
A pNFS service maintains a data storage file for each regular file on
|
||||
the MetaData Server (MDS) on one or more of the Data Servers (DS).
|
||||
If mirroring is enabled, the data storage file will be on more that one of the DSs.
|
||||
Unless command options are specified, this command displays the location(s)
|
||||
of the data storage file for the MDS file
|
||||
.Ar mdsfile .
|
||||
It must be used on the MDS and the
|
||||
.Ar mdsfile
|
||||
must be a file on the exported local file system and not an NFSv4.1 mount.
|
||||
This information is stored in the
|
||||
.Dq pnfsd.dsfile
|
||||
extended attribute for this
|
||||
.Ar mdsfile .
|
||||
The command line options allow the information in the
|
||||
.Dq pnfsd.dsfile
|
||||
extended attribute to be changed.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl q
|
||||
This option suppresses printing of the DS file's location(s).
|
||||
.It Fl z
|
||||
This option specifies that the file handle field of the pnfsd.dsfile
|
||||
extended attribute is to filled with all zero bits.
|
||||
This forces the pNFS MDS to do a Lookup RPC against the DS to acquire the file
|
||||
handle to update it.
|
||||
Normally this will only be necessary after the DS file has been recovered
|
||||
from a backup, causing the file handle to change.
|
||||
.It Fl s Ar dshostname
|
||||
This option can be used with
|
||||
.Fl z
|
||||
so that the zeroing out of the file handle is only done if the DS server
|
||||
is the one specified by this option.
|
||||
.It Fl c Ar old-dshostname,new-dshostname
|
||||
This option allows a sysadmin to replace the host IP# for the DS in the
|
||||
pnfsd.dsfile extended attribute.
|
||||
The old-hostname must resolve to the IP# already in the pnfsd.dsfile extended
|
||||
attribute or the replacement will not be done.
|
||||
If the old-dshostname matches, then the IP# is replaced by the first AF_INET
|
||||
or AF_INET6 address that
|
||||
.Xr getaddrinfo 3
|
||||
returns for the new-dshostname.
|
||||
Changing a DS server's host IP# should be avoided, but this option will
|
||||
allow it to be changed, if the change is unavoidable.
|
||||
.It Fl r Ar dshostname
|
||||
This option sets the IP address of the extended attribute entry for the
|
||||
.Ar dshostname
|
||||
to 0.0.0.0 so that it will no longer be used.
|
||||
.Pp
|
||||
This is meant to be used when mirroring is enabled and the
|
||||
.Ar dshostname
|
||||
DS is disabled, so that it can be re-enabled once it is repaired.
|
||||
This needs to be done for all files in the exported MDS tree where
|
||||
the data may not be up-to-date on the repaired DS when it is re-enabled.
|
||||
After being re-enabled, the command
|
||||
.Xr pnfsdscopymr 1
|
||||
with the
|
||||
.Dq -r
|
||||
option
|
||||
will be used to copy the the file's data to this repaired DS and then update the
|
||||
extended attribute to use it.
|
||||
.Pp
|
||||
A typical use of this will be within a
|
||||
.Xr find 1
|
||||
for all regular files in the MDS's exported tree.
|
||||
.sp
|
||||
For example, if the disabled DS is nfsv4-data3:
|
||||
.br
|
||||
# cd <top-level-exported-directory-on-MDS>
|
||||
.br
|
||||
# find . -type f -exec pnfsdsfile -q -r nfsv4-data3 {} \\;
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr find 1 ,
|
||||
.Xr getaddrinfo 3 ,
|
||||
.Xr nfsv4 4 ,
|
||||
.Xr pnfs 4 ,
|
||||
.Xr nfsd 8 ,
|
||||
.Xr pnfsdscopymr 8 ,
|
||||
.Xr pnfsdskill 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in FreeBSD12.
|
330
usr.sbin/pnfsdsfile/pnfsdsfile.c
Normal file
330
usr.sbin/pnfsdsfile/pnfsdsfile.c
Normal file
@ -0,0 +1,330 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Rick Macklem
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <err.h>
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/extattr.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fs/nfs/nfskpiport.h>
|
||||
#include <fs/nfs/nfsproto.h>
|
||||
#include <fs/nfs/nfs.h>
|
||||
#include <fs/nfs/nfsrvstate.h>
|
||||
|
||||
static void usage(void);
|
||||
|
||||
static struct option longopts[] = {
|
||||
{ "changeds", required_argument, NULL, 'c' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "zerods", required_argument, NULL, 'r' },
|
||||
{ "ds", required_argument, NULL, 's' },
|
||||
{ "zerofh", no_argument, NULL, 'z' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This program displays the location information of a data storage file
|
||||
* for a given file on a MetaData Server (MDS) in a pNFS service. This program
|
||||
* must be run on the MDS and the file argument must be a file in a local
|
||||
* file system that has been exported for the pNFS service.
|
||||
*/
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct addrinfo *res, *ad, *newres;
|
||||
struct sockaddr_in *sin, adsin;
|
||||
struct sockaddr_in6 *sin6, adsin6;
|
||||
char hostn[2 * NI_MAXHOST + 2], *cp;
|
||||
struct pnfsdsfile dsfile[NFSDEV_MAXMIRRORS];
|
||||
int ch, dosetxattr, i, mirrorcnt, quiet, zerods, zerofh;
|
||||
in_port_t tport;
|
||||
ssize_t xattrsize, xattrsize2;
|
||||
|
||||
zerods = 0;
|
||||
zerofh = 0;
|
||||
quiet = 0;
|
||||
dosetxattr = 0;
|
||||
res = NULL;
|
||||
newres = NULL;
|
||||
cp = NULL;
|
||||
while ((ch = getopt_long(argc, argv, "c:qr:s:z", longopts, NULL)) != -1)
|
||||
{
|
||||
switch (ch) {
|
||||
case 'c':
|
||||
/* Replace the first DS server with the second one. */
|
||||
if (zerofh != 0 || zerods != 0)
|
||||
errx(1, "-c, -r and -z are mutually "
|
||||
"exclusive");
|
||||
if (res != NULL)
|
||||
errx(1, "-c and -s are mutually exclusive");
|
||||
strlcpy(hostn, optarg, 2 * NI_MAXHOST + 2);
|
||||
cp = strchr(hostn, ',');
|
||||
if (cp == NULL)
|
||||
errx(1, "Bad -c argument %s", hostn);
|
||||
*cp = '\0';
|
||||
if (getaddrinfo(hostn, NULL, NULL, &res) != 0)
|
||||
errx(1, "Can't get IP# for %s", hostn);
|
||||
*cp++ = ',';
|
||||
if (getaddrinfo(cp, NULL, NULL, &newres) != 0)
|
||||
errx(1, "Can't get IP# for %s", cp);
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 'r':
|
||||
/* Reset the DS server in a mirror with 0.0.0.0. */
|
||||
if (zerofh != 0 || res != NULL || newres != NULL)
|
||||
errx(1, "-r and -s, -z or -c are mutually "
|
||||
"exclusive");
|
||||
zerods = 1;
|
||||
/* Translate the server name to an IP address. */
|
||||
if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
|
||||
errx(1, "Can't get IP# for %s", optarg);
|
||||
break;
|
||||
case 's':
|
||||
if (res != NULL)
|
||||
errx(1, "-s, -c and -r are mutually "
|
||||
"exclusive");
|
||||
/* Translate the server name to an IP address. */
|
||||
if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
|
||||
errx(1, "Can't get IP# for %s", optarg);
|
||||
break;
|
||||
case 'z':
|
||||
if (newres != NULL || zerods != 0)
|
||||
errx(1, "-c, -r and -z are mutually "
|
||||
"exclusive");
|
||||
zerofh = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
if (argc != 1)
|
||||
usage();
|
||||
argv += optind;
|
||||
|
||||
/*
|
||||
* The host address and directory where the data storage file is
|
||||
* located is in the extended attribute "pnfsd.dsfile".
|
||||
*/
|
||||
xattrsize = extattr_get_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
|
||||
"pnfsd.dsfile", dsfile, sizeof(dsfile));
|
||||
mirrorcnt = xattrsize / sizeof(struct pnfsdsfile);
|
||||
xattrsize2 = mirrorcnt * sizeof(struct pnfsdsfile);
|
||||
if (mirrorcnt < 1 || xattrsize != xattrsize2)
|
||||
err(1, "Can't get extattr pnfsd.dsfile for %s", *argv);
|
||||
|
||||
if (quiet == 0)
|
||||
printf("%s:\t", *argv);
|
||||
for (i = 0; i < mirrorcnt; i++) {
|
||||
if (i > 0 && quiet == 0)
|
||||
printf("\t");
|
||||
/* Do the zerofh option. You must be root. */
|
||||
if (zerofh != 0) {
|
||||
if (geteuid() != 0)
|
||||
errx(1, "Must be root/su to zerofh");
|
||||
|
||||
/*
|
||||
* Do it for the server specified by -s/--ds or all
|
||||
* servers, if -s/--ds was not specified.
|
||||
*/
|
||||
sin = &dsfile[i].dsf_sin;
|
||||
sin6 = &dsfile[i].dsf_sin6;
|
||||
ad = res;
|
||||
while (ad != NULL) {
|
||||
if (ad->ai_addr->sa_family == AF_INET &&
|
||||
sin->sin_family == AF_INET &&
|
||||
ad->ai_addrlen >= sizeof(adsin)) {
|
||||
memcpy(&adsin, ad->ai_addr,
|
||||
sizeof(adsin));
|
||||
if (sin->sin_addr.s_addr ==
|
||||
adsin.sin_addr.s_addr)
|
||||
break;
|
||||
}
|
||||
if (ad->ai_addr->sa_family == AF_INET6 &&
|
||||
sin6->sin6_family == AF_INET6 &&
|
||||
ad->ai_addrlen >= sizeof(adsin6)) {
|
||||
memcpy(&adsin6, ad->ai_addr,
|
||||
sizeof(adsin6));
|
||||
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
|
||||
&adsin6.sin6_addr))
|
||||
break;
|
||||
}
|
||||
ad = ad->ai_next;
|
||||
}
|
||||
if (res == NULL || ad != NULL) {
|
||||
memset(&dsfile[i].dsf_fh, 0, sizeof(fhandle_t));
|
||||
dosetxattr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do the zerods option. You must be root. */
|
||||
if (zerods != 0 && mirrorcnt > 1) {
|
||||
if (geteuid() != 0)
|
||||
errx(1, "Must be root/su to zerods");
|
||||
|
||||
/*
|
||||
* Do it for the server specified.
|
||||
*/
|
||||
sin = &dsfile[i].dsf_sin;
|
||||
sin6 = &dsfile[i].dsf_sin6;
|
||||
ad = res;
|
||||
while (ad != NULL) {
|
||||
if (ad->ai_addr->sa_family == AF_INET &&
|
||||
sin->sin_family == AF_INET &&
|
||||
ad->ai_addrlen >= sizeof(adsin)) {
|
||||
memcpy(&adsin, ad->ai_addr,
|
||||
sizeof(adsin));
|
||||
if (sin->sin_addr.s_addr ==
|
||||
adsin.sin_addr.s_addr)
|
||||
break;
|
||||
}
|
||||
if (ad->ai_addr->sa_family == AF_INET6 &&
|
||||
sin6->sin6_family == AF_INET6 &&
|
||||
ad->ai_addrlen >= sizeof(adsin6)) {
|
||||
memcpy(&adsin6, ad->ai_addr,
|
||||
sizeof(adsin6));
|
||||
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
|
||||
&adsin6.sin6_addr))
|
||||
break;
|
||||
}
|
||||
ad = ad->ai_next;
|
||||
}
|
||||
if (ad != NULL) {
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
sin->sin_port = 0;
|
||||
sin->sin_addr.s_addr = 0;
|
||||
dosetxattr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do the -c option to replace the DS host address. */
|
||||
if (newres != NULL) {
|
||||
if (geteuid() != 0)
|
||||
errx(1, "Must be root/su to replace the host"
|
||||
" addr");
|
||||
|
||||
/*
|
||||
* Check that the old host address matches.
|
||||
*/
|
||||
sin = &dsfile[i].dsf_sin;
|
||||
sin6 = &dsfile[i].dsf_sin6;
|
||||
ad = res;
|
||||
while (ad != NULL) {
|
||||
if (ad->ai_addr->sa_family == AF_INET &&
|
||||
sin->sin_family == AF_INET &&
|
||||
ad->ai_addrlen >= sizeof(adsin)) {
|
||||
memcpy(&adsin, ad->ai_addr,
|
||||
sizeof(adsin));
|
||||
if (sin->sin_addr.s_addr ==
|
||||
adsin.sin_addr.s_addr)
|
||||
break;
|
||||
}
|
||||
if (ad->ai_addr->sa_family == AF_INET6 &&
|
||||
sin6->sin6_family == AF_INET6 &&
|
||||
ad->ai_addrlen >= sizeof(adsin6)) {
|
||||
memcpy(&adsin6, ad->ai_addr,
|
||||
sizeof(adsin6));
|
||||
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
|
||||
&adsin6.sin6_addr))
|
||||
break;
|
||||
}
|
||||
ad = ad->ai_next;
|
||||
}
|
||||
if (ad != NULL) {
|
||||
if (sin->sin_family == AF_INET)
|
||||
tport = sin->sin_port;
|
||||
else
|
||||
tport = sin6->sin6_port;
|
||||
/*
|
||||
* We have a match, so replace it with the first
|
||||
* AF_INET or AF_INET6 address in the newres
|
||||
* list.
|
||||
*/
|
||||
while (newres->ai_addr->sa_family != AF_INET &&
|
||||
newres->ai_addr->sa_family != AF_INET6) {
|
||||
newres = newres->ai_next;
|
||||
if (newres == NULL)
|
||||
errx(1, "Hostname %s has no"
|
||||
" IP#", cp);
|
||||
}
|
||||
if (newres->ai_addr->sa_family == AF_INET) {
|
||||
memcpy(sin, newres->ai_addr,
|
||||
sizeof(*sin));
|
||||
sin->sin_port = tport;
|
||||
} else if (newres->ai_addr->sa_family ==
|
||||
AF_INET6) {
|
||||
memcpy(sin6, newres->ai_addr,
|
||||
sizeof(*sin6));
|
||||
sin6->sin6_port = tport;
|
||||
}
|
||||
dosetxattr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (quiet == 0) {
|
||||
/* Translate the IP address to a hostname. */
|
||||
if (getnameinfo((struct sockaddr *)&dsfile[i].dsf_sin,
|
||||
dsfile[i].dsf_sin.sin_len, hostn, sizeof(hostn),
|
||||
NULL, 0, 0) < 0)
|
||||
err(1, "Can't get hostname");
|
||||
printf("%s\tds%d/%s", hostn, dsfile[i].dsf_dir,
|
||||
dsfile[i].dsf_filename);
|
||||
}
|
||||
}
|
||||
if (quiet == 0)
|
||||
printf("\n");
|
||||
if (dosetxattr != 0 && extattr_set_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
|
||||
"pnfsd.dsfile", dsfile, xattrsize) != xattrsize)
|
||||
err(1, "Can't set pnfsd.dsfile");
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "pnfsdsfile [-q/--quiet] [-z/--zerofh] "
|
||||
"[-c/--changeds <old dshostname> <new dshostname>] "
|
||||
"[-r/--zerods <dshostname>] "
|
||||
"[-s/--ds <dshostname>] "
|
||||
"<filename>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user