Whamcloud - gitweb
LU-1430 mgs: "replace_nids" lctl command added
authorArtem Blagodarenko <artem_blagodarenko@xyratex.com>
Thu, 30 Aug 2012 12:35:57 +0000 (16:35 +0400)
committerOleg Drokin <green@whamcloud.com>
Wed, 16 Jan 2013 12:35:39 +0000 (07:35 -0500)
Currently after device address is changed tunefs
should be started with "writeconf" options. This
erase configuration logs for the filesystem that
this server is part of, and regenerate them. This
is very dangerous. All clients must be unmounted
and servers  for  this filesystem should be stopped.
All targets (OSTs/MDTs) must then be restarted to
regenerate the logs.  No clients should be started
until all targets have restarted. The targets start
order is also important. Wrong order can be cause
of failure.

This patch adds "replace_nids" command to lctl
that should be executed when only MGS server
started. This command replace nids and uuid for
given device. Command can be used to change
OST or MDT nids.

Xyratex-bug-id: MRP-397
Reviewed-by: Alexey Lyashkov <alexey_lyashkov@xyratex.com>
Reviewed-by: Nathan Rutman <nathan.rutman@xyratex.com>
Signed-off-by: Artem Blagodarenko <artem_blagodarenko@xyratex.com>
Change-Id: I74b72281ce3b5e48188735035e58250596a16bf7
Reviewed-on: http://review.whamcloud.com/2896
Tested-by: Hudson
Tested-by: Maloo <whamcloud.maloo@gmail.com>
Reviewed-by: Jian Yu <jian.yu@intel.com>
Reviewed-by: Mike Pershin <mike.pershin@intel.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
16 files changed:
lustre/doc/lctl.8
lustre/include/lustre/lustre_idl.h
lustre/include/lustre_lib.h
lustre/include/lustre_log.h
lustre/include/obd_class.h
lustre/mgc/mgc_request.c
lustre/mgs/mgs_handler.c
lustre/mgs/mgs_internal.h
lustre/mgs/mgs_llog.c
lustre/obdclass/genops.c
lustre/obdclass/llog.c
lustre/ofd/ofd_dev.c
lustre/tests/conf-sanity.sh
lustre/utils/lctl.c
lustre/utils/obd.c
lustre/utils/obdctl.h

index 1051513..288a685 100644 (file)
@@ -56,6 +56,16 @@ Print all Network Identifiers on the local node. LNET must be running.
 From a list of nids for a remote node, show which interface communication
 will take place on.
 .TP
+.BI replace_nids " <devicename> <nid1>[,nid2,nid3 ...]"
+Replace the LNET Network Identifiers for a given device,
+as when the server's IP address has changed.
+This command must be run on the MGS node.
+Only MGS server should be started (command execution returns error
+in another cases). To start the MGS service only:
+mount -t lustre <MDT partition> -o nosvc <mount point>
+Note the replace_nids command skips any invalidated records in the configuration log.
+The previous log is backed up with the suffix '.bak'.
+.TP
 .BI ping " <nid> "
 Check LNET connectivity via an LNET ping. This will use the fabric
 appropriate to the specified NID.
index 6dc2a5b..fa0e8b8 100644 (file)
@@ -2773,6 +2773,14 @@ struct llog_rec_tail {
        __u32   lrt_index;
 };
 
+/* Where data follow just after header */
+#define REC_DATA(ptr)                                          \
+       ((void *)((char *)ptr + sizeof(struct llog_rec_hdr)))
+
+#define REC_DATA_LEN(rec)                                      \
+       (rec->lrh_len - sizeof(struct llog_rec_hdr) -           \
+        sizeof(struct llog_rec_tail))
+
 struct llog_logid_rec {
        struct llog_rec_hdr     lid_hdr;
        struct llog_logid       lid_id;
index 94dba22..99cd735 100644 (file)
@@ -535,6 +535,7 @@ static inline void obd_ioctl_freedata(char *buf, int len)
 #define OBD_IOC_CLEAR_LOG              _IOWR('f', 186, OBD_IOC_DATA_TYPE)
 #define OBD_IOC_PARAM                  _IOW ('f', 187, OBD_IOC_DATA_TYPE)
 #define OBD_IOC_POOL                   _IOWR('f', 188, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_REPLACE_NIDS           _IOWR('f', 189, OBD_IOC_DATA_TYPE)
 
 #define OBD_IOC_CATLOGLIST             _IOWR('f', 190, OBD_IOC_DATA_TYPE)
 #define OBD_IOC_LLOG_INFO              _IOWR('f', 191, OBD_IOC_DATA_TYPE)
index 45c248b..a0a11a8 100644 (file)
@@ -118,6 +118,8 @@ struct llog_handle;
 /* llog.c  -  general API */
 int llog_init_handle(const struct lu_env *env, struct llog_handle *handle,
                     int flags, struct obd_uuid *uuid);
+int llog_copy_handler(const struct lu_env *env, struct llog_handle *llh,
+                     struct llog_rec_hdr *rec, void *data);
 int llog_process(const struct lu_env *env, struct llog_handle *loghandle,
                 llog_cb_t cb, void *data, void *catdata);
 int llog_process_or_fork(const struct lu_env *env,
index 224f594..beff126 100644 (file)
@@ -104,6 +104,7 @@ struct obd_device * class_find_client_obd(struct obd_uuid *tgt_uuid,
 struct obd_device * class_devices_in_group(struct obd_uuid *grp_uuid,
                                            int *next);
 struct obd_device * class_num2obd(int num);
+int get_devices_count(void);
 
 int class_notify_sptlrpc_conf(const char *fsname, int namelen);
 
index 7e3c889..7c1e087 100644 (file)
@@ -1601,29 +1601,6 @@ static int mgc_llog_is_empty(struct obd_device *obd, struct llog_ctxt *ctxt,
        return (rc <= 1);
 }
 
-static int mgc_copy_handler(const struct lu_env *env, struct llog_handle *llh,
-                           struct llog_rec_hdr *rec, void *data)
-{
-        struct llog_rec_hdr local_rec = *rec;
-        struct llog_handle *local_llh = (struct llog_handle *)data;
-        char *cfg_buf = (char*) (rec + 1);
-        struct lustre_cfg *lcfg;
-        int rc = 0;
-        ENTRY;
-
-        /* Append all records */
-        local_rec.lrh_len -= sizeof(*rec) + sizeof(struct llog_rec_tail);
-       rc = llog_write(env, local_llh, &local_rec, NULL, 0,
-                       (void *)cfg_buf, -1);
-
-        lcfg = (struct lustre_cfg *)cfg_buf;
-        CDEBUG(D_INFO, "idx=%d, rc=%d, len=%d, cmd %x %s %s\n",
-               rec->lrh_index, rc, rec->lrh_len, lcfg->lcfg_command,
-               lustre_cfg_string(lcfg, 0), lustre_cfg_string(lcfg, 1));
-
-        RETURN(rc);
-}
-
 /* Copy a remote log locally */
 static int mgc_copy_llog(struct obd_device *obd, struct llog_ctxt *rctxt,
                          struct llog_ctxt *lctxt, char *logname)
@@ -1674,7 +1651,7 @@ static int mgc_copy_llog(struct obd_device *obd, struct llog_ctxt *rctxt,
                GOTO(out_closer, rc);
 
        /* Copy remote log */
-       rc = llog_process(NULL, remote_llh, mgc_copy_handler,
+       rc = llog_process(NULL, remote_llh, llog_copy_handler,
                          (void *)local_llh, NULL);
 
 out_closer:
index ae68653..048561d 100644 (file)
@@ -940,6 +940,42 @@ out_free:
                break;
         }
 
+       case OBD_IOC_REPLACE_NIDS: {
+               if (!data->ioc_inllen1 || !data->ioc_inlbuf1) {
+                       CERROR("No device name specified!\n");
+                       RETURN(-EINVAL);
+               }
+
+               if (data->ioc_inlbuf1[data->ioc_inllen1 - 1] != 0) {
+                       CERROR("Device name is not NUL terminated!\n");
+                       RETURN(-EINVAL);
+               }
+
+               if (data->ioc_plen1 > MTI_NAME_MAXLEN) {
+                       CERROR("Device name is too long\n");
+                       RETURN(-EOVERFLOW);
+               }
+
+               if (!data->ioc_inllen2 || !data->ioc_inlbuf2) {
+                       CERROR("No NIDs were specified!\n");
+                       RETURN(-EINVAL);
+               }
+
+               if (data->ioc_inlbuf2[data->ioc_inllen2 - 1] != 0) {
+                       CERROR("NID list is not NUL terminated!\n");
+                       RETURN(-EINVAL);
+               }
+
+               /* replace nids in llog */
+               rc = mgs_replace_nids(&env, mgs, data->ioc_inlbuf1,
+                                     data->ioc_inlbuf2);
+               if (rc)
+                       CERROR("%s: error replacing nids: rc = %d\n",
+                              exp->exp_obd->obd_name, rc);
+
+               RETURN(rc);
+       }
+
        case OBD_IOC_POOL:
                rc = mgs_iocontrol_pool(&env, mgs, data);
                break;
index 0432d14..f62413f 100644 (file)
@@ -192,7 +192,8 @@ int mgs_get_fsdb_srpc_from_llog(const struct lu_env *env, struct mgs_device *mgs
 int mgs_check_index(const struct lu_env *env, struct mgs_device *mgs, struct mgs_target_info *mti);
 int mgs_check_failnid(const struct lu_env *env, struct mgs_device *mgs, struct mgs_target_info *mti);
 int mgs_write_log_target(const struct lu_env *env, struct mgs_device *mgs, struct mgs_target_info *mti,
-                         struct fs_db *fsdb);
+                        struct fs_db *fsdb);
+int mgs_replace_nids(const struct lu_env *env, struct mgs_device *mgs, char *devname, char *nids);
 int mgs_erase_log(const struct lu_env *env, struct mgs_device *mgs,
                  char *name);
 int mgs_erase_logs(const struct lu_env *env, struct mgs_device *mgs,
index 0b8fcad..49a12d7 100644 (file)
@@ -616,9 +616,8 @@ static int mgs_modify_handler(const struct lu_env *env,
 {
        struct mgs_modify_lookup *mml = data;
         struct cfg_marker *marker;
-        struct lustre_cfg *lcfg = (struct lustre_cfg *)(rec + 1);
-        int cfg_len = rec->lrh_len - sizeof(struct llog_rec_hdr) -
-                sizeof(struct llog_rec_tail);
+       struct lustre_cfg *lcfg = REC_DATA(rec);
+       int cfg_len = REC_DATA_LEN(rec);
         int rc;
         ENTRY;
 
@@ -725,7 +724,66 @@ out_pop:
         RETURN(rc);
 }
 
-/******************** config log recording functions *********************/
+/** This structure is passed to mgs_replace_handler */
+struct mgs_replace_uuid_lookup {
+       /* Nids are replaced for this target device */
+       struct mgs_target_info target;
+       /* Temporary modified llog */
+       struct llog_handle *temp_llh;
+       /* Flag is set if in target block*/
+       int in_target_device;
+       /* Nids already added. Just skip (multiple nids) */
+       int device_nids_added;
+       /* Flag is set if this block should not be copied */
+       int skip_it;
+};
+
+/**
+ * Check: a) if block should be skipped
+ * b) is it target block
+ *
+ * \param[in] lcfg
+ * \param[in] mrul
+ *
+ * \retval 0 should not to be skipped
+ * \retval 1 should to be skipped
+ */
+static int check_markers(struct lustre_cfg *lcfg,
+                        struct mgs_replace_uuid_lookup *mrul)
+{
+        struct cfg_marker *marker;
+
+       /* Track markers. Find given device */
+       if (lcfg->lcfg_command == LCFG_MARKER) {
+               marker = lustre_cfg_buf(lcfg, 1);
+               /* Clean llog from records marked as CM_EXCLUDE.
+                  CM_SKIP records are used for "active" command
+                  and can be restored if needed */
+               if ((marker->cm_flags & (CM_EXCLUDE | CM_START)) ==
+                   (CM_EXCLUDE | CM_START)) {
+                       mrul->skip_it = 1;
+                       return 1;
+               }
+
+               if ((marker->cm_flags & (CM_EXCLUDE | CM_END)) ==
+                   (CM_EXCLUDE | CM_END)) {
+                       mrul->skip_it = 0;
+                       return 1;
+               }
+
+               if (strcmp(mrul->target.mti_svname, marker->cm_tgtname) == 0) {
+                       LASSERT(!(marker->cm_flags & CM_START) ||
+                               !(marker->cm_flags & CM_END));
+                       if (marker->cm_flags & CM_START) {
+                               mrul->in_target_device = 1;
+                               mrul->device_nids_added = 0;
+                       } else if (marker->cm_flags & CM_END)
+                               mrul->in_target_device = 0;
+               }
+       }
+
+       return 0;
+}
 
 static int record_lcfg(const struct lu_env *env, struct llog_handle *llh,
                       struct lustre_cfg *lcfg)
@@ -787,13 +845,11 @@ static int record_base(const struct lu_env *env, struct llog_handle *llh,
        return rc;
 }
 
-
 static inline int record_add_uuid(const struct lu_env *env,
                                  struct llog_handle *llh,
                                  uint64_t nid, char *uuid)
 {
        return record_base(env, llh, NULL, nid, LCFG_ADD_UUID, uuid, 0, 0, 0);
-
 }
 
 static inline int record_add_conn(const struct lu_env *env,
@@ -817,6 +873,465 @@ static inline int record_setup(const struct lu_env *env,
        return record_base(env, llh, devname, 0, LCFG_SETUP, s1, s2, s3, s4);
 }
 
+/**
+ * \retval <0 record processing error
+ * \retval n record is processed. No need copy original one.
+ * \retval 0 record is not processed.
+ */
+static int process_command(const struct lu_env *env, struct lustre_cfg *lcfg,
+                          struct mgs_replace_uuid_lookup *mrul)
+{
+       int nids_added = 0;
+       lnet_nid_t nid;
+       char *ptr;
+       int rc;
+
+       if (lcfg->lcfg_command == LCFG_ADD_UUID) {
+               /* LCFG_ADD_UUID command found. Let's skip original command
+                  and add passed nids */
+               ptr = mrul->target.mti_params;
+               while (class_parse_nid(ptr, &nid, &ptr) == 0) {
+                       CDEBUG(D_MGS, "add nid %s with uuid %s, "
+                              "device %s\n", libcfs_nid2str(nid),
+                               mrul->target.mti_params,
+                               mrul->target.mti_svname);
+                       rc = record_add_uuid(env,
+                                            mrul->temp_llh, nid,
+                                            mrul->target.mti_params);
+                       if (!rc)
+                               nids_added++;
+               }
+
+               if (nids_added == 0) {
+                       CERROR("No new nids were added, nid %s with uuid %s, "
+                              "device %s\n", libcfs_nid2str(nid),
+                              mrul->target.mti_params,
+                              mrul->target.mti_svname);
+                       RETURN(-ENXIO);
+               } else {
+                       mrul->device_nids_added = 1;
+               }
+
+               return nids_added;
+       }
+
+       if (mrul->device_nids_added && lcfg->lcfg_command == LCFG_SETUP) {
+               /* LCFG_SETUP command found. UUID should be changed */
+               rc = record_setup(env,
+                                 mrul->temp_llh,
+                                 /* devname the same */
+                                 lustre_cfg_string(lcfg, 0),
+                                 /* s1 is not changed */
+                                 lustre_cfg_string(lcfg, 1),
+                                 /* new uuid should be
+                                 the full nidlist */
+                                 mrul->target.mti_params,
+                                 /* s3 is not changed */
+                                 lustre_cfg_string(lcfg, 3),
+                                 /* s4 is not changed */
+                                 lustre_cfg_string(lcfg, 4));
+               return rc ? rc : 1;
+       }
+
+       /* Another commands in target device block */
+       return 0;
+}
+
+/**
+ * Handler that called for every record in llog.
+ * Records are processed in order they placed in llog.
+ *
+ * \param[in] llh       log to be processed
+ * \param[in] rec       current record
+ * \param[in] data      mgs_replace_uuid_lookup structure
+ *
+ * \retval 0    success
+ */
+static int mgs_replace_handler(const struct lu_env *env,
+                              struct llog_handle *llh,
+                              struct llog_rec_hdr *rec,
+                              void *data)
+{
+       struct llog_rec_hdr local_rec = *rec;
+       struct mgs_replace_uuid_lookup *mrul;
+       struct lustre_cfg *lcfg = REC_DATA(rec);
+       int cfg_len = REC_DATA_LEN(rec);
+       int rc;
+       ENTRY;
+
+       mrul = (struct mgs_replace_uuid_lookup *)data;
+
+       if (rec->lrh_type != OBD_CFG_REC) {
+               CERROR("unhandled lrh_type: %#x, cmd %x %s %s\n",
+                      rec->lrh_type, lcfg->lcfg_command,
+                      lustre_cfg_string(lcfg, 0),
+                      lustre_cfg_string(lcfg, 1));
+               RETURN(-EINVAL);
+       }
+
+       rc = lustre_cfg_sanity_check(lcfg, cfg_len);
+       if (rc) {
+               /* Do not copy any invalidated records */
+               GOTO(skip_out, rc = 0);
+       }
+
+       rc = check_markers(lcfg, mrul);
+       if (rc || mrul->skip_it)
+               GOTO(skip_out, rc = 0);
+
+       /* Write to new log all commands outside target device block */
+       if (!mrul->in_target_device)
+               GOTO(copy_out, rc = 0);
+
+       /* Skip all other LCFG_ADD_UUID and LCFG_ADD_CONN records
+          (failover nids) for this target, assuming that if then
+          primary is changing then so is the failover */
+       if (mrul->device_nids_added &&
+           (lcfg->lcfg_command == LCFG_ADD_UUID ||
+            lcfg->lcfg_command == LCFG_ADD_CONN))
+               GOTO(skip_out, rc = 0);
+
+       rc = process_command(env, lcfg, mrul);
+       if (rc < 0)
+               RETURN(rc);
+
+       if (rc)
+               RETURN(0);
+copy_out:
+       /* Record is placed in temporary llog as is */
+       local_rec.lrh_len -= sizeof(*rec) + sizeof(struct llog_rec_tail);
+       rc = llog_write(env, mrul->temp_llh, &local_rec, NULL, 0,
+                        (void *)lcfg, -1);
+
+       CDEBUG(D_MGS, "Copied idx=%d, rc=%d, len=%d, cmd %x %s %s\n",
+              rec->lrh_index, rc, rec->lrh_len, lcfg->lcfg_command,
+              lustre_cfg_string(lcfg, 0), lustre_cfg_string(lcfg, 1));
+       RETURN(rc);
+
+skip_out:
+       CDEBUG(D_MGS, "Skipped idx=%d, rc=%d, len=%d, cmd %x %s %s\n",
+              rec->lrh_index, rc, rec->lrh_len, lcfg->lcfg_command,
+              lustre_cfg_string(lcfg, 0), lustre_cfg_string(lcfg, 1));
+       RETURN(rc);
+}
+
+static int mgs_backup_llog(const struct lu_env *env,
+                          struct obd_device *mgs,
+                          char *fsname, char *backup)
+{
+       struct obd_uuid *uuid;
+       struct llog_handle *orig_llh, *bak_llh;
+       struct llog_ctxt *lctxt;
+       int rc, rc2;
+       ENTRY;
+
+       lctxt = llog_get_context(mgs, LLOG_CONFIG_ORIG_CTXT);
+       if (!lctxt) {
+               CERROR("%s: missing llog context\n", mgs->obd_name);
+               GOTO(out, rc = -EINVAL);
+       }
+
+       /* Make sure there's no old backup log */
+       rc = llog_erase(env, lctxt, NULL, backup);
+       if (rc < 0 && rc != -ENOENT)
+               GOTO(out_put, rc);
+
+       /* open backup log */
+       rc = llog_open_create(env, lctxt, &bak_llh, NULL, backup);
+       if (rc) {
+               CERROR("%s: backup logfile open %s: rc = %d\n",
+                      mgs->obd_name, backup, rc);
+               GOTO(out_put, rc);
+       }
+
+       /* set the log header uuid */
+       OBD_ALLOC_PTR(uuid);
+       if (uuid == NULL)
+                GOTO(out_put, rc = -ENOMEM);
+       obd_str2uuid(uuid, backup);
+       rc = llog_init_handle(env, bak_llh, LLOG_F_IS_PLAIN, uuid);
+       OBD_FREE_PTR(uuid);
+       if (rc)
+               GOTO(out_close1, rc);
+
+       /* open original log */
+       rc = llog_open(env, lctxt, &orig_llh, NULL, fsname,
+                      LLOG_OPEN_EXISTS);
+       if (rc < 0) {
+               if (rc == -ENOENT)
+                       rc = 0;
+               GOTO(out_close1, rc);
+       }
+
+       rc = llog_init_handle(env, orig_llh, LLOG_F_IS_PLAIN, NULL);
+       if (rc)
+               GOTO(out_close2, rc);
+
+       /* Copy remote log */
+       rc = llog_process(env, orig_llh, llog_copy_handler,
+                         (void *)bak_llh, NULL);
+
+out_close2:
+       rc2 = llog_close(env, orig_llh);
+       if (!rc)
+               rc = rc2;
+out_close1:
+       rc2 = llog_close(env, bak_llh);
+        if (!rc)
+                rc = rc2;
+out_put:
+       if (lctxt)
+               llog_ctxt_put(lctxt);
+out:
+       if (rc)
+               CERROR("%s: Failed to backup log %s: rc = %d\n",
+                      mgs->obd_name, fsname, rc);
+       RETURN(rc);
+}
+
+static int mgs_log_is_empty(const struct lu_env *env, struct mgs_device *mgs,
+                           char *name);
+
+static int mgs_replace_nids_log(const struct lu_env *env,
+                               struct obd_device *mgs, struct fs_db *fsdb,
+                               char *logname, char *devname, char *nids)
+{
+       struct llog_handle *orig_llh, *backup_llh;
+       struct llog_ctxt *ctxt;
+       struct mgs_replace_uuid_lookup *mrul;
+       struct mgs_device *mgs_dev = lu2mgs_dev(mgs->obd_lu_dev);
+       char *backup;
+       int rc, rc2;
+       ENTRY;
+
+       CDEBUG(D_MGS, "Replace nids for %s in %s\n", devname, logname);
+
+       ctxt = llog_get_context(mgs, LLOG_CONFIG_ORIG_CTXT);
+       LASSERT(ctxt != NULL);
+
+       if (mgs_log_is_empty(env, mgs_dev, logname)) {
+               /* Log is empty. Nothing to replace */
+               GOTO(out_put, rc = 0);
+       }
+
+       OBD_ALLOC(backup, strlen(logname) + 5);
+       if (backup == NULL)
+               GOTO(out_put, rc = -ENOMEM);
+
+       sprintf(backup, "%s.bak", logname);
+
+       rc = mgs_backup_llog(env, mgs, logname, backup);
+       if (rc < 0) {
+               CERROR("%s: can't make backup for %s: rc = %d\n",
+                      mgs->obd_name, logname, rc);
+               GOTO(out_free,rc);
+       }
+
+       /* Now erase original log file. Connections are not allowed.
+          Backup is already saved */
+       rc = llog_erase(env, ctxt, NULL, logname);
+       if (rc < 0 && rc != -ENOENT)
+               GOTO(out_free, rc);
+
+       /* open local log */
+       rc = llog_open_create(env, ctxt, &orig_llh, NULL, logname);
+       if (rc)
+               GOTO(out_restore, rc);
+
+       rc = llog_init_handle(env, orig_llh, LLOG_F_IS_PLAIN, NULL);
+       if (rc)
+               GOTO(out_closel, rc);
+
+       /* open backup llog */
+       rc = llog_open(env, ctxt, &backup_llh, NULL, backup,
+                      LLOG_OPEN_EXISTS);
+       if (rc)
+               GOTO(out_closel, rc);
+
+       rc = llog_init_handle(env, backup_llh, LLOG_F_IS_PLAIN, NULL);
+       if (rc)
+               GOTO(out_close, rc);
+
+       if (llog_get_size(backup_llh) <= 1)
+               GOTO(out_close, rc = 0);
+
+       OBD_ALLOC_PTR(mrul);
+       if (!mrul)
+               GOTO(out_close, rc = -ENOMEM);
+       /* devname is only needed information to replace UUID records */
+       strncpy(mrul->target.mti_svname, devname, MTI_NAME_MAXLEN);
+       /* parse nids later */
+       strncpy(mrul->target.mti_params, nids, MTI_PARAM_MAXLEN);
+       /* Copy records to this temporary llog */
+       mrul->temp_llh = orig_llh;
+
+       rc = llog_process(env, backup_llh, mgs_replace_handler,
+                         (void *)mrul, NULL);
+       OBD_FREE_PTR(mrul);
+out_close:
+       rc2 = llog_close(NULL, backup_llh);
+       if (!rc)
+               rc = rc2;
+out_closel:
+       rc2 = llog_close(NULL, orig_llh);
+       if (!rc)
+               rc = rc2;
+
+out_restore:
+       if (rc) {
+               CERROR("%s: llog should be restored: rc = %d\n",
+                      mgs->obd_name, rc);
+               rc2 = mgs_backup_llog(env, mgs, backup, logname);
+               if (rc2 < 0)
+                       CERROR("%s: can't restore backup %s: rc = %d\n",
+                              mgs->obd_name, logname, rc2);
+       }
+
+out_free:
+       OBD_FREE(backup, strlen(backup) + 5);
+
+out_put:
+       llog_ctxt_put(ctxt);
+
+       if (rc)
+               CERROR("%s: failed to replace nids in log %s: rc = %d\n",
+                      mgs->obd_name, logname, rc);
+
+       RETURN(rc);
+}
+
+/**
+ * Parse device name and get file system name and/or device index
+ *
+ * \param[in]   devname device name (ex. lustre-MDT0000)
+ * \param[out]  fsname  file system name(optional)
+ * \param[out]  index   device index(optional)
+ *
+ * \retval 0    success
+ */
+static int mgs_parse_devname(char *devname, char *fsname, __u32 *index)
+{
+       char *ptr;
+       ENTRY;
+
+       /* Extract fsname */
+       ptr = strrchr(devname, '-');
+
+       if (fsname) {
+               if (!ptr) {
+                       CDEBUG(D_MGS, "Device name %s without fsname\n",
+                              devname);
+                       RETURN(-EINVAL);
+               }
+               memset(fsname, 0, MTI_NAME_MAXLEN);
+               strncpy(fsname, devname, ptr - devname);
+               fsname[MTI_NAME_MAXLEN - 1] = 0;
+       }
+
+       if (index) {
+               if (server_name2index(ptr, index, NULL) < 0) {
+                       CDEBUG(D_MGS, "Device name with wrong index\n");
+                       RETURN(-EINVAL);
+               }
+       }
+
+       RETURN(0);
+}
+
+static int only_mgs_is_running(struct obd_device *mgs_obd)
+{
+       /* TDB: Is global variable with devices count exists? */
+       int num_devices = get_devices_count();
+       /* osd, MGS and MGC + self_export
+          (wc -l /proc/fs/lustre/devices <= 2) && (num_exports <= 2) */
+       return (num_devices <= 3) && (mgs_obd->obd_num_exports <= 2);
+}
+
+static int name_create_mdt(char **logname, char *fsname, int i)
+{
+       char mdt_index[9];
+
+       sprintf(mdt_index, "-MDT%04x", i);
+       return name_create(logname, fsname, mdt_index);
+}
+
+/**
+ * Replace nids for \a device to \a nids values
+ *
+ * \param obd           MGS obd device
+ * \param devname       nids need to be replaced for this device
+ * (ex. lustre-OST0000)
+ * \param nids          nids list (ex. nid1,nid2,nid3)
+ *
+ * \retval 0    success
+ */
+int mgs_replace_nids(const struct lu_env *env,
+                    struct mgs_device *mgs,
+                    char *devname, char *nids)
+{
+       /* Assume fsname is part of device name */
+       char fsname[MTI_NAME_MAXLEN];
+       int rc;
+       __u32 index;
+       char *logname;
+       struct fs_db *fsdb;
+       unsigned int i;
+       int conn_state;
+       struct obd_device *mgs_obd = mgs->mgs_obd;
+       ENTRY;
+
+       /* We can only change NIDs if no other nodes are connected */
+       spin_lock(&mgs_obd->obd_dev_lock);
+       conn_state = mgs_obd->obd_no_conn;
+       mgs_obd->obd_no_conn = 1;
+       spin_unlock(&mgs_obd->obd_dev_lock);
+
+       /* We can not change nids if not only MGS is started */
+       if (!only_mgs_is_running(mgs_obd)) {
+               CERROR("Only MGS is allowed to be started\n");
+               GOTO(out, rc = -EINPROGRESS);
+       }
+
+       /* Get fsname and index*/
+       rc = mgs_parse_devname(devname, fsname, &index);
+       if (rc)
+               GOTO(out, rc);
+
+       rc = mgs_find_or_make_fsdb(env, mgs, fsname, &fsdb);
+       if (rc) {
+               CERROR("%s: can't find fsdb: rc = %d\n", fsname, rc);
+               GOTO(out, rc);
+       }
+
+       /* Process client llogs */
+       name_create(&logname, fsname, "-client");
+       rc = mgs_replace_nids_log(env, mgs_obd, fsdb, logname, devname, nids);
+       name_destroy(&logname);
+       if (rc) {
+               CERROR("%s: error while replacing NIDs for %s: rc = %d\n",
+                      fsname, devname, rc);
+               GOTO(out, rc);
+       }
+
+       /* Process MDT llogs */
+       for (i = 0; i < INDEX_MAP_SIZE * 8; i++) {
+               if (!test_bit(i, fsdb->fsdb_mdt_index_map))
+                       continue;
+               name_create_mdt(&logname, fsname, i);
+               rc = mgs_replace_nids_log(env, mgs_obd, fsdb, logname, devname, nids);
+               name_destroy(&logname);
+               if (rc)
+                       GOTO(out, rc);
+       }
+
+out:
+       spin_lock(&mgs_obd->obd_dev_lock);
+       mgs_obd->obd_no_conn = conn_state;
+       spin_unlock(&mgs_obd->obd_dev_lock);
+
+       RETURN(rc);
+}
+
 static int record_lov_setup(const struct lu_env *env, struct llog_handle *llh,
                            char *devname, struct lov_desc *desc)
 {
@@ -1715,16 +2230,8 @@ out_free:
         RETURN(rc);
 }
 
-static inline int name_create_mdt(char **logname, char *fsname, int i)
-{
-        char mdt_index[9];
-
-        sprintf(mdt_index, "-MDT%04x", i);
-       return name_create(logname, fsname, mdt_index);
-}
-
 static int name_create_mdt_and_lov(char **logname, char **lovname,
-                                    struct fs_db *fsdb, int i)
+                                   struct fs_db *fsdb, int i)
 {
        int rc;
 
@@ -2597,7 +3104,7 @@ static int mgs_srpc_read_handler(const struct lu_env *env,
 {
        struct mgs_srpc_read_data *msrd = data;
         struct cfg_marker         *marker;
-        struct lustre_cfg         *lcfg = (struct lustre_cfg *)(rec + 1);
+       struct lustre_cfg         *lcfg = REC_DATA(rec);
         char                      *svname, *param;
         int                        cfg_len, rc;
         ENTRY;
@@ -3244,20 +3751,18 @@ int mgs_setparam(const struct lu_env *env, struct mgs_device *mgs,
                 RETURN(-ENOSYS);
         }
 
-        /* Extract fsname */
-        ptr = strrchr(devname, '-');
-        memset(fsname, 0, MTI_NAME_MAXLEN);
-        if (ptr && (server_name2index(ptr, &index, NULL) >= 0)) {
+       rc = mgs_parse_devname(devname, fsname, NULL);
+       if (rc == 0 && !mgs_parse_devname(devname, NULL, &index)) {
                 /* param related to llite isn't allowed to set by OST or MDT */
-                if (strncmp(param, PARAM_LLITE, sizeof(PARAM_LLITE)) == 0)
+               if (rc == 0 && strncmp(param, PARAM_LLITE,
+                                  sizeof(PARAM_LLITE)) == 0)
                         RETURN(-EINVAL);
-
-                strncpy(fsname, devname, ptr - devname);
         } else {
                 /* assume devname is the fsname */
+               memset(fsname, 0, MTI_NAME_MAXLEN);
                 strncpy(fsname, devname, MTI_NAME_MAXLEN);
+               fsname[MTI_NAME_MAXLEN - 1] = 0;
         }
-        fsname[MTI_NAME_MAXLEN - 1] = 0;
         CDEBUG(D_MGS, "setparam fs='%s' device='%s'\n", fsname, devname);
 
        rc = mgs_find_or_make_fsdb(env, mgs, fsname, &fsdb);
@@ -3475,63 +3980,4 @@ out_label:
         return rc;
 }
 
-#if 0
-/******************** unused *********************/
-static int mgs_backup_llog(struct obd_device *obd, char* fsname)
-{
-        struct file *filp, *bak_filp;
-        struct lvfs_run_ctxt saved;
-        char *logname, *buf;
-        loff_t soff = 0 , doff = 0;
-        int count = 4096, len;
-        int rc = 0;
-
-        OBD_ALLOC(logname, PATH_MAX);
-        if (logname == NULL)
-                return -ENOMEM;
-
-        OBD_ALLOC(buf, count);
-        if (!buf)
-                GOTO(out , rc = -ENOMEM);
-
-        len = snprintf(logname, PATH_MAX, "%s/%s.bak",
-                       MOUNT_CONFIGS_DIR, fsname);
-
-        if (len >= PATH_MAX - 1) {
-                GOTO(out, -ENAMETOOLONG);
-        }
-
-        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
-        bak_filp = l_filp_open(logname, O_RDWR|O_CREAT|O_TRUNC, 0660);
-        if (IS_ERR(bak_filp)) {
-                rc = PTR_ERR(bak_filp);
-                CERROR("backup logfile open %s: %d\n", logname, rc);
-                GOTO(pop, rc);
-        }
-        sprintf(logname, "%s/%s", MOUNT_CONFIGS_DIR, fsname);
-        filp = l_filp_open(logname, O_RDONLY, 0);
-        if (IS_ERR(filp)) {
-                rc = PTR_ERR(filp);
-                CERROR("logfile open %s: %d\n", logname, rc);
-                GOTO(close1f, rc);
-        }
-
-        while ((rc = lustre_fread(filp, buf, count, &soff)) > 0) {
-                rc = lustre_fwrite(bak_filp, buf, count, &doff);
-                break;
-        }
-
-        filp_close(filp, 0);
-close1f:
-        filp_close(bak_filp, 0);
-pop:
-        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
-out:
-        if (buf)
-                OBD_FREE(buf, count);
-        OBD_FREE(logname, PATH_MAX);
-        return rc;
-}
-
-#endif
index 63d4124..d39caa9 100644 (file)
@@ -479,6 +479,27 @@ struct obd_device *class_num2obd(int num)
 }
 EXPORT_SYMBOL(class_num2obd);
 
+/**
+ * Get obd devices count. Device in any
+ *    state are counted
+ * \retval obd device count
+ */
+int get_devices_count(void)
+{
+       int index, max_index = class_devno_max(), dev_count = 0;
+
+       read_lock(&obd_dev_lock);
+       for (index = 0; index <= max_index; index++) {
+               struct obd_device *obd = class_num2obd(index);
+               if (obd != NULL)
+                       dev_count++;
+       }
+       read_unlock(&obd_dev_lock);
+
+       return dev_count;
+}
+EXPORT_SYMBOL(get_devices_count);
+
 void class_obd_list(void)
 {
         char *status;
index 21bd4d6..f08fd6e 100644 (file)
@@ -272,6 +272,32 @@ out:
 }
 EXPORT_SYMBOL(llog_init_handle);
 
+int llog_copy_handler(const struct lu_env *env,
+                     struct llog_handle *llh,
+                     struct llog_rec_hdr *rec,
+                     void *data)
+{
+       struct llog_rec_hdr local_rec = *rec;
+       struct llog_handle *local_llh = (struct llog_handle *)data;
+       char *cfg_buf = (char*) (rec + 1);
+       struct lustre_cfg *lcfg;
+       int rc = 0;
+       ENTRY;
+
+       /* Append all records */
+       local_rec.lrh_len -= sizeof(*rec) + sizeof(struct llog_rec_tail);
+       rc = llog_write(env, local_llh, &local_rec, NULL, 0,
+                       (void *)cfg_buf, -1);
+
+       lcfg = (struct lustre_cfg *)cfg_buf;
+       CDEBUG(D_INFO, "idx=%d, rc=%d, len=%d, cmd %x %s %s\n",
+              rec->lrh_index, rc, rec->lrh_len, lcfg->lcfg_command,
+              lustre_cfg_string(lcfg, 0), lustre_cfg_string(lcfg, 1));
+
+       RETURN(rc);
+}
+EXPORT_SYMBOL(llog_copy_handler);
+
 static int llog_process_thread(void *arg)
 {
        struct llog_process_info        *lpi = arg;
@@ -417,7 +443,7 @@ static int llog_process_thread_daemonize(void *arg)
        cfs_daemonize_ctxt("llog_process_thread");
 
        /* client env has no keys, tags is just 0 */
-       rc = lu_env_init(&env, LCT_LOCAL);
+       rc = lu_env_init(&env, LCT_LOCAL | LCT_MG_THREAD);
        if (rc)
                goto out;
        lpi->lpi_env = &env;
index d12729a..13245d0 100644 (file)
@@ -624,7 +624,9 @@ static int ofd_init0(const struct lu_env *env, struct ofd_device *m,
        }
 
        /* No connection accepted until configurations will finish */
+       spin_lock(&obd->obd_dev_lock);
        obd->obd_no_conn = 1;
+       spin_unlock(&obd->obd_dev_lock);
        obd->obd_replayable = 1;
        if (cfg->lcfg_bufcount > 4 && LUSTRE_CFG_BUFLEN(cfg, 4) > 0) {
                char *str = lustre_cfg_string(cfg, 4);
index 639e706..71519de 100644 (file)
@@ -3386,6 +3386,8 @@ test_64() {
        echo "$LFS df"
        $LFS df --lazy || error "lfs df failed"
        cleanup || return $?
+       #writeconf to remove all ost2 traces for subsequent tests
+       writeconf_or_reformat
 }
 run_test 64 "check lfs df --lazy "
 
@@ -3429,6 +3431,73 @@ test_65() { # LU-2237
 }
 run_test 65 "re-create the lost last_rcvd file when server mount"
 
+test_66() {
+       setup
+       local OST1_NID=$(do_facet ost1 $LCTL list_nids | head -1)
+       local MDS_NID=$(do_facet $SINGLEMDS $LCTL list_nids | head -1)
+
+       echo "replace_nids should fail if MDS, OSTs and clients are UP"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 $OST1_NID &&
+               error "replace_nids fail"
+
+       umount_client $MOUNT || error "unmounting client failed"
+       echo "replace_nids should fail if MDS and OSTs are UP"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 $OST1_NID &&
+               error "replace_nids fail"
+
+       stop_ost
+       echo "replace_nids should fail if MDS is UP"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 $OST1_NID &&
+               error "replace_nids fail"
+
+       stop_mds || error "stopping mds failed"
+
+       if combined_mgs_mds; then
+               start_mds "-o nosvc" ||
+                       error "starting mds with nosvc option failed"
+       fi
+
+       echo "command should accept two parameters"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 &&
+               error "command should accept two params"
+
+       echo "correct device name should be passed"
+       do_facet mgs $LCTL replace_nids $FSNAME-WRONG0000 $OST1_NID &&
+               error "wrong devname"
+
+       echo "wrong nids list should not destroy the system"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 "wrong nids list" &&
+               error "wrong parse"
+
+       echo "replace OST nid"
+       do_facet mgs $LCTL replace_nids $FSNAME-OST0000 $OST1_NID ||
+               error "replace nids failed"
+
+       echo "command should accept two parameters"
+       do_facet mgs $LCTL replace_nids $FSNAME-MDT0000 &&
+               error "command should accept two params"
+
+       echo "wrong nids list should not destroy the system"
+       do_facet mgs $LCTL replace_nids $FSNAME-MDT0000 "wrong nids list" &&
+               error "wrong parse"
+
+       echo "replace MDS nid"
+       do_facet mgs $LCTL replace_nids $FSNAME-MDT0000 $MDS_NID ||
+               error "replace nids failed"
+
+       if ! combined_mgs_mds ; then
+               stop_mgs
+       else
+               stop_mds
+       fi
+
+       setup_noconfig
+       check_mount || error "error after nid replace"
+       cleanup
+       reformat
+}
+run_test 66 "replace nids"
+
 test_70a() {
        [ $MDSCOUNT -lt 2 ] && skip "needs >= 2 MDTs" && return
        local MDTIDX=1
index 50c45ba..f7a1a29 100644 (file)
@@ -94,6 +94,9 @@ command_t cmdlist[] = {
          "usage: list_nids [all]"},
         {"which_nid", jt_ptl_which_nid, 0, "choose a NID"
          "usage: which_nid NID [NID...]"},
+       {"replace_nids", jt_replace_nids, 0,
+        "replace primary NIDs for a device\n"
+        "usage: replace_nids <device> <nid1>[,nid2,nid3]"},
         {"interface_list", jt_ptl_print_interfaces,0,"print interface entries\n"
          "usage: interface_list"},
         {"peer_list", jt_ptl_print_peers, 0, "print peer entries\n"
index b0aa312..f59b738 100644 (file)
@@ -2417,6 +2417,56 @@ static int do_activate(int argc, char **argv, int flag)
         return rc;
 }
 
+/**
+ * Replace nids for given device.
+ * lctl replace_nids <devicename> <nid1>[,nid2,nid3]
+ * Command should be started on MGS server.
+ * Only MGS server should be started (command execution
+ * returns error in another cases). Command mount
+ * -t lustre <MDT partition> -o nosvc <mount point>
+ * can be used for that.
+ *
+ * llogs for MDTs and clients are processed. All
+ * records copied as is except add_uuid and setup. This records
+ * are skipped and recorded with new nids and uuid.
+ *
+ * \see mgs_replace_nids
+ * \see mgs_replace_nids_log
+ * \see mgs_replace_handler
+ */
+int jt_replace_nids(int argc, char **argv)
+{
+       int rc;
+       char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
+       struct obd_ioctl_data data;
+
+       memset(&data, 0, sizeof(data));
+       data.ioc_dev = get_mgs_device();
+       if (argc != 3)
+               return CMD_HELP;
+
+       data.ioc_inllen1 = strlen(argv[1]) + 1;
+       data.ioc_inlbuf1 = argv[1];
+
+       data.ioc_inllen2 = strlen(argv[2]) + 1;
+       data.ioc_inlbuf2 = argv[2];
+       memset(buf, 0, sizeof(rawbuf));
+       rc = obd_ioctl_pack(&data, &buf, sizeof(rawbuf));
+       if (rc) {
+               fprintf(stderr, "error: %s: invalid ioctl\n",
+                       jt_cmdname(argv[0]));
+               return rc;
+       }
+
+       rc = l2_ioctl(OBD_DEV_ID, OBD_IOC_REPLACE_NIDS, buf);
+       if (rc < 0) {
+               fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
+                       strerror(rc = errno));
+       }
+
+       return rc;
+}
+
 int jt_obd_deactivate(int argc, char **argv)
 {
         return do_activate(argc, argv, 0);
index 8f99e44..27be83e 100644 (file)
@@ -85,6 +85,7 @@ int jt_obd_lov_getconfig(int argc, char **argv);
 int jt_obd_test_ldlm(int argc, char **argv);
 int jt_obd_ldlm_regress_start(int argc, char **argv);
 int jt_obd_ldlm_regress_stop(int argc, char **argv);
+int jt_replace_nids(int arc, char **argv);
 int jt_obd_activate(int argc, char **argv);
 int jt_obd_deactivate(int argc, char **argv);
 int jt_obd_recover(int argc, char **argv);