summaryrefslogtreecommitdiff
path: root/tools/vgimportdevices.c
blob: a20b78ccb7a7f255d5292a6778cae4c3e967c5b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Copyright (C) 2020 Red Hat, Inc. All rights reserved.
 *
 * This file is part of LVM2.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License v.2.1.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "tools.h"
#include "lib/cache/lvmcache.h"
#include "lib/filters/filter.h"
#include "lib/device/device_id.h"

struct vgimportdevices_params {
	uint32_t added_devices;
};

static int _vgimportdevices_single(struct cmd_context *cmd,
				   const char *vg_name,
				   struct volume_group *vg,
				   struct processing_handle *handle)
{
	struct vgimportdevices_params *vp = (struct vgimportdevices_params *) handle->custom_handle;
	struct pv_list *pvl;
	struct physical_volume *pv;
	int update_vg = 1;
	int updated_pvs = 0;
	const char *idtypestr;

	dm_list_iterate_items(pvl, &vg->pvs) {
		if (is_missing_pv(pvl->pv) || !pvl->pv->dev) {
			log_warn("WARNING: not importing VG %s with missing PV.", vg->name);
			return 1;
		}
	}

	/*
	 * We want to allow importing devices of foreign and shared 
	 * VGs, but we do not want to update device_ids in those VGs.
	 *
	 * If --foreign is set, then foreign VGs will be passed
	 * to this function; add devices but don't update vg.
	 * shared VGs are passed to this function; add devices
	 * and do not update.
	 */
	if (vg_is_foreign(vg) || vg_is_shared(vg))
		update_vg = 0;

	/*
	 * When no devices_file is available, do not update device_ids
	 * in the VG.  This command is not meant to be used to just
	 * update device_ids of a VG; for that use vgchange.
	 */
	if (!devices_file_valid(cmd))
		update_vg = 0;

	/*
	 * TODO: let users import devices without updating VG device_ids.
	 * if --nodeviceidupdate; update_vg = 0;
	 */

	/*
	 * User can select the idtype to use when importing.
	 */
	idtypestr = arg_str_value(cmd, deviceidtype_ARG, NULL);

	dm_list_iterate_items(pvl, &vg->pvs) {
		pv = pvl->pv;

		if (!pv->dev && pv->device_id_type && pv->device_id) {
			/* add an entry using the fields we have values for */
			cmd->enable_device_ids = 1;
			device_id_add_nodev(cmd, pv->device_id_type, pv->device_id, NULL, (const char *)&pv->id.uuid);
			cmd->enable_device_ids = 0;

			vp->added_devices++;
			continue;

		}
		if (!pv->dev) {
			log_warn("WARNING: not adding PV with missing device PVID %s",
				 (const char *)&pvl->pv->id.uuid);
			continue;
		}

		if (!idtypestr && pv->device_id_type)
			idtypestr = pv->device_id_type;

		cmd->enable_device_ids = 1;
		device_id_add(cmd, pv->dev, (const char *)&pvl->pv->id.uuid, idtypestr, NULL);
		cmd->enable_device_ids = 0;

		vp->added_devices++;

		/* We could skip update if the device_id has not changed. */

		if (!update_vg)
			continue;

		updated_pvs++;
	}

	if (updated_pvs) {
		if (!vg_write(vg) || !vg_commit(vg))
			goto_bad;
		backup(vg);
	}

	return ECMD_PROCESSED;
bad:
	return ECMD_FAILED;
}

/*
 * This command always scans all devices on the system,
 * any pre-existing devices_file does not limit the scope.
 *
 * This command adds the VG's devices to whichever
 * devices_file is set in config or command line.
 * If devices_file doesn't exist, it's created.
 *
 * If devices_file is "" then this file will scan all devices
 * and show the devices that it would otherwise have added to
 * the devices_file.  The VG is not updated with device_ids.
 *
 * This command updates the VG metadata to add device_ids
 * (if the metadata is missing them), unless an option is
 * set to skip that, e.g. --nodeviceidupdate?
 *
 * If the VG found has a foreign system ID then an error
 * will be printed.  To import devices from a foreign VG:
 * vgimportdevices --foreign -a
 * vgimportdevices --foreign VG
 *
 * If there are duplicate VG names it will do nothing.
 *
 * If there are duplicate PVIDs related to VG it will do nothing,
 * the user would need to add the PVs they want with lvmdevices --add.
 *
 * vgimportdevices -a (no vg arg) will import all accesible VGs.
 */

int vgimportdevices(struct cmd_context *cmd, int argc, char **argv)
{
	struct vgimportdevices_params vp = { 0 };
	struct processing_handle *handle;

	if (arg_is_set(cmd, foreign_ARG))
		cmd->include_foreign_vgs = 1;

	cmd->include_shared_vgs = 1;

	/* So that we can warn about this. */
	cmd->handles_missing_pvs = 1;

	/* Print a notice if a regex filter is being applied?
	   Possibly offer an option to ignore a regex filter? */

	if (!lock_global(cmd, "ex"))
		return ECMD_FAILED;

	clear_hint_file(cmd);

	if (!(handle = init_processing_handle(cmd, NULL))) {
		log_error("Failed to initialize processing handle.");
		return ECMD_FAILED;
	}
	handle->custom_handle = &vp;

	/*
	 * import is an odd case where we do not want to use an
	 * existing devices_file for processing/filtering, because
	 * we want to search outside the devices_file for new devs
	 * to add to it, but we do want devices_file entries on
	 * use_device_ids so we can update and write out that list.
	 */
	cmd->enable_device_ids = 1;
	device_ids_read(cmd);
	cmd->enable_device_ids = 0;

	/*
	 * For each VG:
	 * device_id_add() each PV in the VG
	 * update device_ids in the VG (potentially)
	 */
	process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
			0, handle, _vgimportdevices_single);

	if (!vp.added_devices) {
		log_print("No devices to add.");
		goto out;
	}

	if (!cmd->devices_file || !strlen(cmd->devices_file)) {
		log_print("No devices file to update.");
		goto out;
	}

	cmd->enable_device_ids = 1;
	if (!device_ids_write(cmd)) {
		destroy_processing_handle(cmd, handle);
		log_error("Failed to update devices file.");
		return ECMD_FAILED;
	}

	log_print("Added %u devices to devices file.", vp.added_devices);
out:
	destroy_processing_handle(cmd, handle);
	return ECMD_PROCESSED;
}