1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2011-2015 Planets Communications B.V.
5 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Marco van Wieringen, May 2015
24 */
25 /**
26 * @file
27 * Storage specific NDMP Data Management Application (DMA) routines
28 */
29
30 #include "include/bareos.h"
31 #include "dird.h"
32 #include "dird/dird_globals.h"
33 #include "dird/jcr_private.h"
34 #include "dird/sd_cmds.h"
35 #include "dird/storage.h"
36 #include "dird/ndmp_slot2elemaddr.h"
37
38 #if HAVE_NDMP
39 #include "ndmp/ndmagents.h"
40 #include "ndmp_dma_priv.h"
41 #endif
42
43 namespace directordaemon {
44
45 #if HAVE_NDMP
46 /* Imported variables */
47
48 /* Forward referenced functions */
49
50 /**
51 * ndmp query callback
52 */
get_tape_info_cb(struct ndm_session * sess,ndmp9_device_info * info,unsigned n_info)53 int get_tape_info_cb(struct ndm_session* sess,
54 ndmp9_device_info* info,
55 unsigned n_info)
56 {
57 Dmsg0(100, "Get tape info called\n");
58 unsigned int i = 0;
59 unsigned int j;
60 unsigned int k;
61 const char* what = "tape";
62 JobControlRecord* jcr = NULL;
63 StorageResource* store = NULL;
64 NIS* nis = (NIS*)sess->param->log.ctx;
65
66 if (nis->jcr) {
67 jcr = nis->jcr;
68 } else if (nis->ua && nis->ua->jcr) {
69 jcr = nis->ua->jcr;
70 } else {
71 return -1;
72 }
73
74 if (jcr->is_JobType(JT_BACKUP)) {
75 store = jcr->impl->res.write_storage;
76
77 } else if (jcr->is_JobType(JT_RESTORE)) {
78 store = jcr->impl->res.read_storage;
79
80 } else {
81 return -1;
82 }
83
84 /* if (store->runtime_storage_status->ndmp_deviceinfo) { */
85 /* delete(store->runtime_storage_status->ndmp_deviceinfo); */
86 /* store->runtime_storage_status->ndmp_deviceinfo = NULL; */
87 /* } */
88 if (store->runtime_storage_status->ndmp_deviceinfo.empty()) {
89 for (i = 0; i < n_info; i++) {
90 Dmsg2(100, " %s %s\n", what, info[i].model);
91
92 ndmp_deviceinfo_t* devinfo = new (ndmp_deviceinfo_t);
93 devinfo->JobIdUsingDevice = 0;
94
95 ndmp9_device_capability* info_dc;
96 info_dc = info[i].caplist.caplist_val;
97 devinfo->model = info[i].model;
98 devinfo->device = info_dc->device;
99 store->runtime_storage_status->ndmp_deviceinfo.push_back(*devinfo);
100
101 for (j = 0; j < info[i].caplist.caplist_len; j++) {
102 ndmp9_device_capability* dc;
103 uint32_t attr;
104 dc = &info[i].caplist.caplist_val[j];
105 Dmsg1(100, " device %s\n", dc->device);
106
107
108 if (!strcmp(what, "tape\n")) {
109 #ifndef NDMOS_OPTION_NO_NDMP3
110 if (sess->plumb.tape->protocol_version == 3) {
111 attr = dc->v3attr.value;
112 Dmsg1(100, " attr 0x%lx\n", attr);
113 if (attr & NDMP3_TAPE_ATTR_REWIND) Dmsg0(100, " REWIND\n");
114 if (attr & NDMP3_TAPE_ATTR_UNLOAD) Dmsg0(100, " UNLOAD\n");
115 }
116 #endif /* !NDMOS_OPTION_NO_NDMP3 */
117 #ifndef NDMOS_OPTION_NO_NDMP4
118 if (sess->plumb.tape->protocol_version == 4) {
119 attr = dc->v4attr.value;
120 Dmsg1(100, " attr 0x%lx\n", attr);
121 if (attr & NDMP4_TAPE_ATTR_REWIND) Dmsg0(100, " REWIND\n");
122 if (attr & NDMP4_TAPE_ATTR_UNLOAD) Dmsg0(100, " UNLOAD\n");
123 }
124 #endif /* !NDMOS_OPTION_NO_NDMP4 */
125 }
126 for (k = 0; k < dc->capability.capability_len; k++) {
127 Dmsg2(100, " set %s=%s\n",
128 dc->capability.capability_val[k].name,
129 dc->capability.capability_val[k].value);
130 }
131 if (k == 0) Dmsg0(100, " empty capabilities\n");
132 }
133 if (j == 0) Dmsg0(100, " empty caplist\n");
134 Dmsg0(100, "\n");
135 }
136 }
137 if (i == 0) Dmsg1(100, " Empty %s info\n", what);
138 return 0;
139 }
140
141 /**
142 * execute NDMP_QUERY_AGENTS on Tape and Robot
143 */
do_ndmp_native_query_tape_and_robot_agents(JobControlRecord * jcr,StorageResource * store)144 bool do_ndmp_native_query_tape_and_robot_agents(JobControlRecord* jcr,
145 StorageResource* store)
146 {
147 struct ndm_job_param ndmp_job;
148
149 if (!NdmpBuildStorageJob(jcr, store, true, /* Query Tape Agent */
150 true, /* Query Robot Agent */
151 NDM_JOB_OP_QUERY_AGENTS, &ndmp_job)) {
152 Dmsg0(100, "error in NdmpBuildStorageJob");
153 return false;
154 }
155
156 struct ndmca_query_callbacks query_callbacks;
157 query_callbacks.get_tape_info = get_tape_info_cb;
158 ndmca_query_callbacks* query_cbs = &query_callbacks;
159
160 NdmpDoQuery(NULL, jcr, &ndmp_job, me->ndmp_loglevel, query_cbs);
161
162 /*
163 * Debug output
164 */
165
166 if (!store->runtime_storage_status->ndmp_deviceinfo.empty()) {
167 Jmsg(jcr, M_INFO, 0, "NDMP Devices for storage %s:(%s)\n",
168 store->resource_name_, store->runtime_storage_status->smc_ident);
169 } else {
170 Jmsg(jcr, M_INFO, 0, "No NDMP Devices for storage %s:(%s)\n",
171 store->resource_name_, store->runtime_storage_status->smc_ident);
172 return false;
173 }
174 for (auto devinfo = store->runtime_storage_status->ndmp_deviceinfo.begin();
175 devinfo != store->runtime_storage_status->ndmp_deviceinfo.end();
176 devinfo++) {
177 Jmsg(jcr, M_INFO, 0, " %s\n", devinfo->device.c_str(),
178 devinfo->model.c_str());
179 }
180 return true;
181 }
182
183 /**
184 * get status of a NDMP Native storage and store the information
185 * coming in via the NDMP protocol
186 */
DoNdmpNativeStorageStatus(UaContext * ua,StorageResource * store,char * cmd)187 void DoNdmpNativeStorageStatus(UaContext* ua, StorageResource* store, char* cmd)
188 {
189 struct ndm_job_param ndmp_job;
190
191 ua->jcr->impl->res.write_storage = store;
192
193 if (!NdmpBuildStorageJob(ua->jcr, store, true, /* Query Tape Agent */
194 true, /* Query Robot Agent */
195 NDM_JOB_OP_QUERY_AGENTS, &ndmp_job)) {
196 ua->InfoMsg("build_storage_job failed\n");
197 }
198
199 struct ndmca_query_callbacks query_callbacks;
200 query_callbacks.get_tape_info = get_tape_info_cb;
201 ndmca_query_callbacks* query_cbs = &query_callbacks;
202
203 NdmpDoQuery(ua, NULL, &ndmp_job, me->ndmp_loglevel, query_cbs);
204
205 int i = 0;
206 if (!store->runtime_storage_status->ndmp_deviceinfo.empty()) {
207 ua->InfoMsg("NDMP Devices for storage %s:(%s)\n", store->resource_name_,
208 store->runtime_storage_status->smc_ident);
209 ua->InfoMsg(" element_address Device Model (JobId) \n");
210 for (auto devinfo = store->runtime_storage_status->ndmp_deviceinfo.begin();
211 devinfo != store->runtime_storage_status->ndmp_deviceinfo.end();
212 devinfo++) {
213 ua->InfoMsg(" %d %s %s (%d)\n", i++, devinfo->device.c_str(),
214 devinfo->model.c_str(), devinfo->JobIdUsingDevice);
215 }
216 }
217 }
218
219 /**
220 * Output the status of a storage daemon when its a normal storage
221 * daemon accessed via the NDMP protocol or query the TAPE and ROBOT
222 * agent of a native NDMP server.
223 */
DoNdmpStorageStatus(UaContext * ua,StorageResource * store,char * cmd)224 void DoNdmpStorageStatus(UaContext* ua, StorageResource* store, char* cmd)
225 {
226 /*
227 * See if the storage is just a NDMP instance of a normal storage daemon.
228 */
229 if (store->paired_storage) {
230 DoNativeStorageStatus(ua, store->paired_storage, cmd);
231 } else {
232 DoNdmpNativeStorageStatus(ua, store, cmd);
233 }
234 }
235
236 /**
237 * Interface function which glues the logging infra of the NDMP lib for
238 * debugging.
239 */
NdmpRobotStatusHandler(struct ndmlog * log,char * tag,int lev,char * msg)240 extern "C" void NdmpRobotStatusHandler(struct ndmlog* log,
241 char* tag,
242 int lev,
243 char* msg)
244 {
245 NIS* nis;
246
247 /*
248 * Make sure if the logging system was setup properly.
249 */
250 nis = (NIS*)log->ctx;
251 if (!nis) { return; }
252
253 Dmsg1(100, "%s\n", msg);
254 }
255
256 /**
257 * Generic cleanup function that can be used after a successful or failed NDMP
258 * Job ran.
259 */
CleanupNdmpSession(struct ndm_session * ndmp_sess)260 static void CleanupNdmpSession(struct ndm_session* ndmp_sess)
261 {
262 Dmsg0(200, "Start to clean up ndmp session.\n");
263
264 /*
265 * Destroy the session.
266 */
267 ndma_session_destroy(ndmp_sess);
268
269 /*
270 * Free the param block.
271 */
272 free(ndmp_sess->param->log_tag);
273 free(ndmp_sess->param->log.ctx);
274 free(ndmp_sess->param);
275 free(ndmp_sess);
276 }
277
278 /**
279 * Generic function to run a storage Job on a remote NDMP server.
280 */
NdmpRunStorageJob(JobControlRecord * jcr,StorageResource * store,struct ndm_session * ndmp_sess,struct ndm_job_param * ndmp_job)281 static bool NdmpRunStorageJob(JobControlRecord* jcr,
282 StorageResource* store,
283 struct ndm_session* ndmp_sess,
284 struct ndm_job_param* ndmp_job)
285 {
286 NIS* nis;
287
288 ndmp_sess->conn_snooping = (me->ndmp_snooping) ? 1 : 0;
289 ndmp_sess->control_agent_enabled = 1;
290
291 ndmp_sess->param =
292 (struct ndm_session_param*)malloc(sizeof(struct ndm_session_param));
293 memset(ndmp_sess->param, 0, sizeof(struct ndm_session_param));
294 ndmp_sess->param->log.deliver = NdmpRobotStatusHandler;
295 nis = (NIS*)malloc(sizeof(NIS));
296 memset(nis, 0, sizeof(NIS));
297 ndmp_sess->param->log_level =
298 NativeToNdmpLoglevel(me->ndmp_loglevel, debug_level, nis);
299 ndmp_sess->param->log.ctx = nis;
300 ndmp_sess->param->log_tag = strdup("DIR-NDMP");
301 nis->jcr = jcr;
302
303 /*
304 * Initialize the session structure.
305 */
306 if (ndma_session_initialize(ndmp_sess)) {
307 Dmsg0(200, "Could not initialize ndma session.\n");
308 return false;
309 }
310
311 /*
312 * Copy the actual job to perform.
313 */
314 memcpy(&ndmp_sess->control_acb->job, ndmp_job, sizeof(struct ndm_job_param));
315 if (!NdmpValidateJob(jcr, &ndmp_sess->control_acb->job)) {
316 Dmsg0(200, "Could not validate ndma job.\n");
317 return false;
318 }
319
320 /*
321 * Commission the session for a run.
322 */
323 if (ndma_session_commission(ndmp_sess)) {
324 Dmsg0(200, "Could not commission the ndma session.\n");
325 return false;
326 }
327
328 /*
329 * Setup the DMA.
330 */
331 if (ndmca_connect_control_agent(ndmp_sess)) {
332 Dmsg0(200, "Could not connect to control agent.\n");
333 return false;
334 }
335
336 ndmp_sess->conn_open = 1;
337 ndmp_sess->conn_authorized = 1;
338
339 char ndm_job_type = ndmp_sess->control_acb->job.operation & 0xff;
340 Dmsg2(200, "ndma job.operation - job_type: %#x - %c\n",
341 ndmp_sess->control_acb->job.operation, ndm_job_type);
342
343 /*
344 * Let the DMA perform its magic.
345 */
346 int err{};
347 if ((err = ndmca_control_agent(ndmp_sess)) != 0) {
348 Dmsg1(200, "Ndma control agent error: %d\n", err);
349 return false;
350 }
351
352 return true;
353 }
354
355 /**
356 * Generic function to get the current element status of a NDMP robot.
357 */
GetRobotElementStatus(JobControlRecord * jcr,StorageResource * store,struct ndm_session ** ndmp_sess)358 static bool GetRobotElementStatus(JobControlRecord* jcr,
359 StorageResource* store,
360 struct ndm_session** ndmp_sess)
361 {
362 struct ndm_job_param ndmp_job;
363
364 /*
365 * See if this is an autochanger.
366 */
367 if (!store->autochanger || !store->ndmp_changer_device) {
368 Dmsg2(200, "Autochanger: %s - NDMP Changer device: %s\n",
369 store->autochanger ? "true" : "false",
370 store->ndmp_changer_device ? store->ndmp_changer_device : "(NULL)");
371 return false;
372 }
373
374 if (!NdmpBuildStorageJob(jcr, store, false, /* Setup Tape Agent */
375 true, /* Setup Robot Agent */
376 NDM_JOB_OP_INIT_ELEM_STATUS, &ndmp_job)) {
377 Dmsg0(200, "Could not build NDMP storage job\n");
378 return false;
379 }
380
381 /*
382 * Set the remote robotics name to use.
383 * We use the ndmscsi_target_from_str() function which parses the NDMJOB
384 * format of a device in the form NAME[,[CNUM,]SID[,LUN]
385 */
386 ndmp_job.robot_target =
387 (struct ndmscsi_target*)malloc(sizeof(struct ndmscsi_target));
388 int error_number = ndmscsi_target_from_str(ndmp_job.robot_target,
389 store->ndmp_changer_device);
390 if (error_number != 0) {
391 free(ndmp_job.robot_target);
392 Dmsg1(200, "Could not create NDMP target name from string: %d\n",
393 error_number);
394 return false;
395 }
396 ndmp_job.have_robot = 1;
397 ndmp_job.auto_remedy = 1;
398
399 /*
400 * Initialize a new NDMP session
401 */
402 *ndmp_sess = (struct ndm_session*)malloc(sizeof(struct ndm_session));
403 memset(*ndmp_sess, 0, sizeof(struct ndm_session));
404
405 if (!NdmpRunStorageJob(jcr, store, *ndmp_sess, &ndmp_job)) {
406 CleanupNdmpSession(*ndmp_sess);
407 Dmsg0(200, "NdmpRunStorageJob failed.\n");
408 return false;
409 }
410
411 return true;
412 }
413
414 /**
415 * Get the volume names from a smc_element_descriptor.
416 */
FillVolumeName(vol_list_t * vl,struct smc_element_descriptor * edp)417 static void FillVolumeName(vol_list_t* vl, struct smc_element_descriptor* edp)
418 {
419 if (edp->PVolTag) {
420 vl->VolName = strdup((char*)edp->primary_vol_tag->volume_id);
421 StripTrailingJunk(vl->VolName);
422 } else if (edp->AVolTag) {
423 vl->VolName = strdup((char*)edp->alternate_vol_tag->volume_id);
424 StripTrailingJunk(vl->VolName);
425 }
426 }
427
428 /**
429 * Get the information to map logical addresses (index) to
430 * physical address (scsi element address)
431 *
432 * Everything that is needed for that is stored in the
433 * smc smc_element_address_assignment.
434 *
435 * For each type of element a start address and the
436 * number of entries (count) is stored there.
437 */
NdmpFillStorageMappings(StorageResource * store,struct ndm_session * ndmp_sess)438 static void NdmpFillStorageMappings(StorageResource* store,
439 struct ndm_session* ndmp_sess)
440 {
441 struct smc_ctrl_block* smc;
442
443 smc = ndmp_sess->control_acb->smc_cb;
444 memcpy(store->runtime_storage_status->smc_ident, smc->ident,
445 sizeof(store->runtime_storage_status->smc_ident));
446
447 if (smc->valid_elem_aa) {
448 memcpy(&store->runtime_storage_status->storage_mapping, &smc->elem_aa,
449 sizeof(store->runtime_storage_status->storage_mapping));
450
451 } else {
452 Dmsg0(0, "Warning, smc does not have valid elem_aa info\n");
453 }
454 }
455
456 /**
457 * Get the current content of the autochanger as a generic vol_list dlist.
458 */
ndmp_get_vol_list(UaContext * ua,StorageResource * store,bool listall,bool scan)459 dlist* ndmp_get_vol_list(UaContext* ua,
460 StorageResource* store,
461 bool listall,
462 bool scan)
463 {
464 struct ndm_session* ndmp_sess;
465 struct smc_ctrl_block* smc;
466 struct smc_element_descriptor* edp;
467 vol_list_t* vl = NULL;
468 dlist* vol_list = NULL;
469
470 ua->WarningMsg(_("get ndmp_vol_list...\n"));
471 if (!GetRobotElementStatus(ua->jcr, store, &ndmp_sess)) {
472 return (dlist*)NULL;
473 }
474
475 /*
476 * If we have no storage mappings create them now from the data we just
477 * retrieved.
478 */
479 NdmpFillStorageMappings(store, ndmp_sess);
480
481 /*
482 * Start with an empty dlist().
483 */
484 vol_list = new dlist(vl, &vl->link);
485
486 /*
487 * Process the robot element status retrieved.
488 */
489 smc = ndmp_sess->control_acb->smc_cb;
490 for (edp = smc->elem_desc; edp; edp = edp->next) {
491 vl = (vol_list_t*)malloc(sizeof(vol_list_t));
492 *vl = vol_list_t{};
493
494 if (scan && !listall) {
495 /*
496 * Scanning -- require only valid slot
497 */
498 switch (edp->element_type_code) {
499 case SMC_ELEM_TYPE_SE:
500 /*
501 * Normal slot
502 */
503 vl->slot_type = slot_type_t::kSlotTypeStorage;
504 if (edp->Full) {
505 vl->slot_status = slot_status_t::kSlotStatusFull;
506 FillVolumeName(vl, edp);
507 } else {
508 vl->slot_status = slot_status_t::kSlotStatusEmpty;
509 }
510 vl->element_address = edp->element_address;
511 break;
512 default:
513 free(vl);
514 continue;
515 }
516 } else if (!listall) {
517 /*
518 * Not scanning and not listall.
519 */
520 switch (edp->element_type_code) {
521 case SMC_ELEM_TYPE_SE:
522 /*
523 * Normal slot
524 */
525 vl->slot_type = slot_type_t::kSlotTypeStorage;
526 vl->element_address = edp->element_address;
527 if (!edp->Full) {
528 free(vl);
529 continue;
530 } else {
531 vl->slot_status = slot_status_t::kSlotStatusFull;
532 FillVolumeName(vl, edp);
533 }
534 break;
535 default:
536 free(vl);
537 continue;
538 }
539 } else {
540 /*
541 * Listall.
542 */
543 switch (edp->element_type_code) {
544 case SMC_ELEM_TYPE_MTE:
545 /*
546 * Transport
547 */
548 free(vl);
549 continue;
550 case SMC_ELEM_TYPE_SE:
551 /*
552 * Normal slot
553 */
554 vl->slot_type = slot_type_t::kSlotTypeStorage;
555 vl->element_address = edp->element_address;
556 if (edp->Full) {
557 vl->slot_status = slot_status_t::kSlotStatusFull;
558 FillVolumeName(vl, edp);
559 } else {
560 vl->slot_status = slot_status_t::kSlotStatusEmpty;
561 }
562 break;
563 case SMC_ELEM_TYPE_IEE:
564 /*
565 * Import/Export bareos_slot_number
566 */
567 vl->slot_type = slot_type_t::kSlotTypeImport;
568 vl->element_address = edp->element_address;
569 if (edp->Full) {
570 vl->slot_status = slot_status_t::kSlotStatusFull;
571 FillVolumeName(vl, edp);
572 } else {
573 vl->slot_status = slot_status_t::kSlotStatusEmpty;
574 }
575 if (edp->InEnab) { vl->flags |= can_import; }
576 if (edp->ExEnab) { vl->flags |= can_export; }
577 if (edp->ImpExp) {
578 vl->flags |= by_oper;
579 } else {
580 vl->flags |= by_mte;
581 }
582 break;
583 case SMC_ELEM_TYPE_DTE:
584 /*
585 * Drive
586 */
587 vl->slot_type = slot_type_t::kSlotTypeDrive;
588 vl->element_address = edp->element_address;
589 if (edp->Full) {
590 slot_number_t slot_mapping;
591 vl->slot_status = slot_status_t::kSlotStatusFull;
592 slot_mapping = GetBareosSlotNumberByElementAddress(
593 &store->runtime_storage_status->storage_mapping,
594 slot_type_t::kSlotTypeStorage, edp->src_se_addr);
595 vl->currently_loaded_slot_number = slot_mapping;
596 FillVolumeName(vl, edp);
597 } else {
598 vl->slot_status = slot_status_t::kSlotStatusEmpty;
599 }
600 break;
601 default:
602 vl->slot_type = slot_type_t::kSlotTypeUnknown;
603 vl->element_address = edp->element_address;
604 break;
605 }
606 }
607
608
609 /*
610 * Map physical storage address to logical one using the storage mappings.
611 */
612 vl->bareos_slot_number = GetBareosSlotNumberByElementAddress(
613 &store->runtime_storage_status->storage_mapping, vl->slot_type,
614 edp->element_address);
615 if (vl->VolName) {
616 Dmsg6(100,
617 "Add phys_slot = %hd logi_slot=%hd loaded=%hd type=%hd status=%hd "
618 "Vol=%s to SD list.\n",
619 vl->element_address, vl->bareos_slot_number,
620 vl->currently_loaded_slot_number, vl->slot_type, vl->slot_status,
621 NPRT(vl->VolName));
622 } else {
623 Dmsg5(100,
624 "Add phys_slot = %hd logi_slot=%hd loaded=%hd type=%hd status=%hd "
625 "Vol=NULL to SD list.\n",
626 vl->element_address, vl->bareos_slot_number,
627 vl->currently_loaded_slot_number, vl->slot_type, vl->slot_status);
628 }
629
630 vol_list->binary_insert(vl, StorageCompareVolListEntry);
631 } /* for */
632
633 if (vol_list->size() == 0) {
634 delete vol_list;
635 vol_list = NULL;
636 }
637
638 CleanupNdmpSession(ndmp_sess);
639
640 return vol_list;
641 }
642
643 /**
644 * Update the mapping table from logical to physical storage addresses.
645 */
NdmpUpdateStorageMappings(JobControlRecord * jcr,StorageResource * store)646 bool NdmpUpdateStorageMappings(JobControlRecord* jcr, StorageResource* store)
647 {
648 struct ndm_session* ndmp_sess;
649
650 if (!GetRobotElementStatus(jcr, store, &ndmp_sess)) {
651 Dmsg0(200, "Could not get robot element status.\n");
652 return false;
653 }
654
655 NdmpFillStorageMappings(store, ndmp_sess);
656
657 CleanupNdmpSession(ndmp_sess);
658
659 return true;
660 }
661
662 /**
663 * Update the mapping table from logical to physical storage addresses.
664 */
NdmpUpdateStorageMappings(UaContext * ua,StorageResource * store)665 bool NdmpUpdateStorageMappings(UaContext* ua, StorageResource* store)
666 {
667 struct ndm_session* ndmp_sess;
668
669 if (!GetRobotElementStatus(ua->jcr, store, &ndmp_sess)) {
670 Dmsg0(200, "Could not get robot element status.\n");
671 return false;
672 }
673
674 NdmpFillStorageMappings(store, ndmp_sess);
675
676 CleanupNdmpSession(ndmp_sess);
677
678 return true;
679 }
680
681 /**
682 * Number of slots in a NDMP autochanger.
683 */
NdmpGetNumSlots(UaContext * ua,StorageResource * store)684 slot_number_t NdmpGetNumSlots(UaContext* ua, StorageResource* store)
685 {
686 slot_number_t slots = 0;
687
688 /*
689 * See if the mappings are already determined.
690 */
691 if (!NdmpUpdateStorageMappings(ua, store)) {
692 Dmsg0(200, "NdmpUpdateStorageMappings failed\n");
693 return slots;
694 }
695
696 return store->runtime_storage_status->storage_mapping.se_count +
697 store->runtime_storage_status->storage_mapping.iee_count;
698 }
699
700 /**
701 * Number of drives in a NDMP autochanger.
702 */
NdmpGetNumDrives(UaContext * ua,StorageResource * store)703 drive_number_t NdmpGetNumDrives(UaContext* ua, StorageResource* store)
704 {
705 drive_number_t drives = 0;
706
707 /*
708 * See if the mappings are already determined.
709 */
710 if (!NdmpUpdateStorageMappings(ua, store)) {
711 Dmsg0(200, "NdmpUpdateStorageMappings failed\n");
712 return drives;
713 }
714
715 return store->runtime_storage_status->storage_mapping.dte_count;
716 }
717
718 /**
719 * Move a volume from one slot to an other in a NDMP autochanger.
720 */
NdmpTransferVolume(UaContext * ua,StorageResource * store,slot_number_t src_slot,slot_number_t dst_slot)721 bool NdmpTransferVolume(UaContext* ua,
722 StorageResource* store,
723 slot_number_t src_slot,
724 slot_number_t dst_slot)
725 {
726 bool retval = false;
727 slot_number_t from_addr;
728 slot_number_t to_addr;
729 struct ndm_job_param ndmp_job;
730 struct ndm_session* ndmp_sess;
731
732 /*
733 * See if this is an autochanger.
734 */
735 if (!store->autochanger || !store->ndmp_changer_device) { return retval; }
736
737 if (!NdmpBuildStorageJob(ua->jcr, store, false, /* Setup Tape Agent */
738 true, /* Setup Robot Agent */
739 NDM_JOB_OP_MOVE_TAPE, &ndmp_job)) {
740 return retval;
741 }
742
743 /*
744 * Fill in the from and to address.
745 *
746 * As the upper level functions work with logical slot numbers convert them
747 * to physical slot numbers for the actual NDMP operation.
748 */
749 from_addr = GetBareosSlotNumberByElementAddress(
750 &store->runtime_storage_status->storage_mapping,
751 slot_type_t::kSlotTypeStorage, src_slot);
752 if (from_addr == kInvalidSlotNumber) {
753 ua->ErrorMsg("No slot mapping for slot %hd\n", src_slot);
754 return retval;
755 }
756 ndmp_job.from_addr = from_addr;
757 ndmp_job.from_addr_given = 1;
758
759 to_addr = GetElementAddressByBareosSlotNumber(
760 &store->runtime_storage_status->storage_mapping,
761 slot_type_t::kSlotTypeStorage, dst_slot);
762 if (to_addr == kInvalidSlotNumber) {
763 ua->ErrorMsg("No slot mapping for slot %hd\n", dst_slot);
764 return retval;
765 }
766 ndmp_job.to_addr = to_addr;
767 ndmp_job.to_addr_given = 1;
768
769 ua->WarningMsg(_("transferring form slot %hd to slot %hd...\n"), src_slot,
770 dst_slot);
771
772 /*
773 * Set the remote robotics name to use.
774 * We use the ndmscsi_target_from_str() function which parses the NDMJOB
775 * format of a device in the form NAME[,[CNUM,]SID[,LUN]
776 */
777 ndmp_job.robot_target =
778 (struct ndmscsi_target*)malloc(sizeof(struct ndmscsi_target));
779 if (ndmscsi_target_from_str(ndmp_job.robot_target,
780 store->ndmp_changer_device) != 0) {
781 free(ndmp_job.robot_target);
782 return retval;
783 }
784 ndmp_job.have_robot = 1;
785 ndmp_job.auto_remedy = 1;
786
787 /*
788 * Initialize a new NDMP session
789 */
790 ndmp_sess = (struct ndm_session*)malloc(sizeof(struct ndm_session));
791 memset(ndmp_sess, 0, sizeof(struct ndm_session));
792
793 if (!NdmpRunStorageJob(ua->jcr, store, ndmp_sess, &ndmp_job)) {
794 CleanupNdmpSession(ndmp_sess);
795 return retval;
796 }
797
798 retval = true;
799
800 CleanupNdmpSession(ndmp_sess);
801
802 return retval;
803 }
804
805 /**
806 * reserve a NDMP Tape drive for a certain job
807 * lock the devinfo list
808 * check if any of the devices is available (deviceinfo.JobUsingDevice == 0)
809 * set the JobId into deviceinfo.JobUsingDevice
810 * unlock devinfo
811 * return name of device that was reserved
812 */
reserve_ndmp_tapedevice_for_job(StorageResource * store,JobControlRecord * jcr)813 std::string reserve_ndmp_tapedevice_for_job(StorageResource* store,
814 JobControlRecord* jcr)
815 {
816 JobId_t jobid = jcr->JobId;
817 std::string returnvalue;
818 P(store->runtime_storage_status->ndmp_deviceinfo_lock);
819
820 if (!store->runtime_storage_status->ndmp_deviceinfo.empty()) {
821 for (auto devinfo = store->runtime_storage_status->ndmp_deviceinfo.begin();
822 devinfo != store->runtime_storage_status->ndmp_deviceinfo.end();
823 devinfo++) {
824 if (devinfo->JobIdUsingDevice == 0) {
825 devinfo->JobIdUsingDevice = jobid;
826 returnvalue = devinfo->device;
827 Jmsg(jcr, M_INFO, 0,
828 _("successfully reserved NDMP Tape Device %s for job %d\n"),
829 returnvalue.c_str(), jobid);
830 break;
831 } else {
832 Jmsg(jcr, M_INFO, 0,
833 _("NDMP Tape Device %s is already reserved for for job %d\n"),
834 devinfo->device.c_str(), devinfo->JobIdUsingDevice);
835 }
836 }
837 }
838 V(store->runtime_storage_status->ndmp_deviceinfo_lock);
839 return returnvalue;
840 }
841
842 /*
843 * remove job from tapedevice
844 */
unreserve_ndmp_tapedevice_for_job(StorageResource * store,JobControlRecord * jcr)845 bool unreserve_ndmp_tapedevice_for_job(StorageResource* store,
846 JobControlRecord* jcr)
847 {
848 JobId_t jobid = jcr->JobId;
849 bool retval = false;
850 P(store->runtime_storage_status->ndmp_deviceinfo_lock);
851
852 if (!store->runtime_storage_status->ndmp_deviceinfo.empty()) {
853 for (auto devinfo = store->runtime_storage_status->ndmp_deviceinfo.begin();
854 devinfo != store->runtime_storage_status->ndmp_deviceinfo.end();
855 devinfo++) {
856 if (devinfo->JobIdUsingDevice == jobid) {
857 devinfo->JobIdUsingDevice = 0;
858 retval = true;
859 Jmsg(jcr, M_INFO, 0,
860 _("removed reservation of NDMP Tape Device %s for job %d\n"),
861 devinfo->device.c_str(), jobid);
862 break;
863 }
864 }
865 }
866 V(store->runtime_storage_status->ndmp_deviceinfo_lock);
867 return retval;
868 }
869
870 /**
871 * Lookup the name of a drive in a NDMP autochanger.
872 */
lookup_ndmp_drive(StorageResource * store,drive_number_t drivenumber)873 char* lookup_ndmp_drive(StorageResource* store, drive_number_t drivenumber)
874 {
875 int cnt = 0;
876 char* tapedevice;
877 BareosResource* tapedeviceres = nullptr;
878
879 if (store->device) {
880 foreach_alist (tapedeviceres, store->device) {
881 if (cnt == drivenumber) {
882 tapedevice = tapedeviceres->resource_name_;
883 return tapedevice;
884 }
885 cnt++;
886 }
887 }
888
889 return NULL;
890 }
891
892 /**
893 * Lookup the drive index by device name in a NDMP autochanger.
894 */
lookup_ndmp_driveindex_by_name(StorageResource * store,char * drivename)895 int lookup_ndmp_driveindex_by_name(StorageResource* store, char* drivename)
896 {
897 int cnt = 0;
898
899 if (!drivename) { return -1; }
900
901 if (!store->runtime_storage_status->ndmp_deviceinfo.empty()) {
902 for (auto devinfo = store->runtime_storage_status->ndmp_deviceinfo.begin();
903 devinfo != store->runtime_storage_status->ndmp_deviceinfo.end();
904 devinfo++) {
905 if ((drivename == devinfo->device)) { return cnt; }
906 cnt++;
907 }
908 }
909 return -1;
910 }
911
912 /**
913 * Perform an autochanger operation in a NDMP autochanger.
914 */
NdmpAutochangerVolumeOperation(UaContext * ua,StorageResource * store,const char * operation,drive_number_t drive,slot_number_t slot)915 bool NdmpAutochangerVolumeOperation(UaContext* ua,
916 StorageResource* store,
917 const char* operation,
918 drive_number_t drive,
919 slot_number_t slot)
920 {
921 drive_number_t drive_mapping;
922 int ndmp_operation;
923 bool retval = false;
924 struct ndm_job_param ndmp_job;
925 struct ndm_session* ndmp_sess;
926
927 Dmsg3(100,
928 "NdmpAutochangerVolumeOperation: operation %s, drive %hd, slot %hd\n",
929 operation, drive, slot);
930 ua->WarningMsg(
931 _("NdmpAutochangerVolumeOperation: operation %s, drive %hd, slot %hd\n"),
932 operation, drive, slot);
933 /*
934 * See if this is an autochanger.
935 */
936 if (!store->autochanger || !store->ndmp_changer_device) { return retval; }
937
938 if (bstrcmp(operation, "unmount") || bstrcmp(operation, "release")) {
939 ndmp_operation = NDM_JOB_OP_UNLOAD_TAPE;
940 } else if (bstrcmp(operation, "mount")) {
941 ndmp_operation = NDM_JOB_OP_LOAD_TAPE;
942 } else {
943 ua->ErrorMsg("Illegal autochanger operation %s\n", operation);
944 return retval;
945 }
946
947 if (!NdmpBuildStorageJob(ua->jcr, store, false, /* Setup Tape Agent */
948 true, /* Setup Robot Agent */
949 ndmp_operation, &ndmp_job)) {
950 return retval;
951 }
952
953 /*
954 * See if the mappings are already determined.
955 */
956 if (!NdmpUpdateStorageMappings(ua, store)) { return false; }
957
958 if (slot != kInvalidSlotNumber) {
959 slot_number_t slot_mapping;
960
961 /*
962 * Map the logical address to a physical one.
963 */
964 slot_mapping = GetElementAddressByBareosSlotNumber(
965 &store->runtime_storage_status->storage_mapping,
966 slot_type_t::kSlotTypeStorage, slot);
967 if (slot_mapping == kInvalidSlotNumber) {
968 ua->ErrorMsg("No slot mapping for slot %hd\n", slot);
969 return retval;
970 }
971 ndmp_job.from_addr = slot_mapping;
972 ndmp_job.from_addr_given = 1;
973 }
974
975 /*
976 * Map the logical address to a physical one.
977 */
978 drive_mapping = GetElementAddressByBareosSlotNumber(
979 &store->runtime_storage_status->storage_mapping,
980 slot_type_t::kSlotTypeDrive, slot);
981 if (drive_mapping == kInvalidSlotNumber) {
982 ua->ErrorMsg("No slot mapping for drive %hd\n", drive);
983 return retval;
984 }
985 ndmp_job.drive_addr = drive_mapping;
986 ndmp_job.drive_addr_given = 1;
987
988 /*
989 * Set the remote robotics name to use.
990 * We use the ndmscsi_target_from_str() function which parses the NDMJOB
991 * format of a device in the form NAME[,[CNUM,]SID[,LUN]
992 */
993 ndmp_job.robot_target =
994 (struct ndmscsi_target*)malloc(sizeof(struct ndmscsi_target));
995 if (ndmscsi_target_from_str(ndmp_job.robot_target,
996 store->ndmp_changer_device) != 0) {
997 free(ndmp_job.robot_target);
998 return retval;
999 }
1000 ndmp_job.have_robot = 1;
1001 ndmp_job.auto_remedy = 1;
1002
1003 /*
1004 * Initialize a new NDMP session
1005 */
1006 ndmp_sess = (struct ndm_session*)malloc(sizeof(struct ndm_session));
1007 memset(ndmp_sess, 0, sizeof(struct ndm_session));
1008
1009 if (!NdmpRunStorageJob(ua->jcr, store, ndmp_sess, &ndmp_job)) {
1010 CleanupNdmpSession(ndmp_sess);
1011 return retval;
1012 }
1013
1014 retval = true;
1015
1016 CleanupNdmpSession(ndmp_sess);
1017
1018 return retval;
1019 }
1020
1021 /**
1022 * Label a volume in a NDMP autochanger.
1023 */
NdmpSendLabelRequest(UaContext * ua,StorageResource * store,MediaDbRecord * mr,MediaDbRecord * omr,PoolDbRecord * pr,bool relabel,drive_number_t drive,slot_number_t slot)1024 bool NdmpSendLabelRequest(UaContext* ua,
1025 StorageResource* store,
1026 MediaDbRecord* mr,
1027 MediaDbRecord* omr,
1028 PoolDbRecord* pr,
1029 bool relabel,
1030 drive_number_t drive,
1031 slot_number_t slot)
1032 {
1033 bool retval = false;
1034 struct ndm_job_param ndmp_job;
1035 struct ndm_session* ndmp_sess;
1036 struct ndmmedia* media;
1037
1038 Dmsg4(100,
1039 "ndmp_send_label_request: VolumeName=%s MediaType=%s PoolName=%s "
1040 "drive=%hd\n",
1041 mr->VolumeName, mr->MediaType, pr->Name, drive);
1042
1043 /*
1044 * See if this is an autochanger.
1045 */
1046 if (!store->autochanger || !store->ndmp_changer_device) { return retval; }
1047
1048 if (!NdmpBuildStorageJob(ua->jcr, store, true, /* Setup Tape Agent */
1049 true, /* Setup Robot Agent */
1050 NDM_JOB_OP_INIT_LABELS, &ndmp_job)) {
1051 return retval;
1052 }
1053
1054 /*
1055 * Set the remote robotics name to use.
1056 * We use the ndmscsi_target_from_str() function which parses the NDMJOB
1057 * format of a device in the form NAME[,[CNUM,]SID[,LUN]
1058 */
1059 ndmp_job.robot_target =
1060 (struct ndmscsi_target*)malloc(sizeof(struct ndmscsi_target));
1061 if (ndmscsi_target_from_str(ndmp_job.robot_target,
1062 store->ndmp_changer_device) != 0) {
1063 free(ndmp_job.robot_target);
1064 Dmsg0(100, "NdmpSendLabelRequest: no robot to use\n");
1065 return retval;
1066 }
1067 ndmp_job.have_robot = 1;
1068 ndmp_job.auto_remedy = 1;
1069
1070 /*
1071 * Set the remote tape drive to use.
1072 */
1073 ndmp_job.tape_device =
1074 strdup(((DeviceResource*)(store->device->first()))->resource_name_);
1075 if (!ndmp_job.tape_device) { free(ndmp_job.robot_target); }
1076
1077 /*
1078 * Insert a media entry of the slot to label.
1079 */
1080 if (IsSlotNumberValid(slot)) {
1081 slot_number_t slot_mapping;
1082
1083 slot_mapping = GetElementAddressByBareosSlotNumber(
1084 &store->runtime_storage_status->storage_mapping,
1085 slot_type_t::kSlotTypeStorage, slot);
1086 if (slot_mapping == kInvalidSlotNumber) {
1087 ua->ErrorMsg("No slot mapping for slot %hd\n", slot);
1088 return retval;
1089 }
1090 media = ndma_store_media(&ndmp_job.media_tab, slot_mapping);
1091 } else {
1092 media = ndma_store_media(&ndmp_job.media_tab, 0);
1093 }
1094 bstrncpy(media->label, mr->VolumeName, NDMMEDIA_LABEL_MAX - 1);
1095 media->valid_label = NDMP9_VALIDITY_VALID;
1096
1097
1098 /*
1099 * Initialize a new NDMP session
1100 */
1101 ndmp_sess = (struct ndm_session*)malloc(sizeof(struct ndm_session));
1102 memset(ndmp_sess, 0, sizeof(struct ndm_session));
1103
1104 if (!NdmpRunStorageJob(ua->jcr, store, ndmp_sess, &ndmp_job)) {
1105 CleanupNdmpSession(ndmp_sess);
1106 return retval;
1107 }
1108
1109 retval = true;
1110
1111 CleanupNdmpSession(ndmp_sess);
1112
1113 return retval;
1114 }
1115
1116
ndmp_native_update_runtime_storage_status(JobControlRecord * jcr,StorageResource * store)1117 static bool ndmp_native_update_runtime_storage_status(JobControlRecord* jcr,
1118 StorageResource* store)
1119 {
1120 bool retval = true;
1121
1122 P(store->runtime_storage_status->changer_lock);
1123 if (!do_ndmp_native_query_tape_and_robot_agents(jcr, store)) {
1124 retval = false;
1125 }
1126 V(store->runtime_storage_status->changer_lock);
1127
1128 P(store->runtime_storage_status->changer_lock);
1129 if (!NdmpUpdateStorageMappings(jcr, store)) { retval = false; }
1130 V(store->runtime_storage_status->changer_lock);
1131
1132 return retval;
1133 }
1134
ndmp_native_setup_robot_and_tape_for_native_backup_job(JobControlRecord * jcr,StorageResource * store,ndm_job_param & ndmp_job)1135 bool ndmp_native_setup_robot_and_tape_for_native_backup_job(
1136 JobControlRecord* jcr,
1137 StorageResource* store,
1138 ndm_job_param& ndmp_job)
1139 {
1140 slot_number_t driveaddress;
1141 std::string tapedevice;
1142 bool retval = false;
1143 if (!ndmp_native_update_runtime_storage_status(jcr, store)) {
1144 Jmsg(jcr, M_ERROR, 0,
1145 _("Failed getting updating runtime storage status\n"));
1146 return retval;
1147 }
1148
1149 ndmp_job.robot_target =
1150 (struct ndmscsi_target*)malloc(sizeof(struct ndmscsi_target));
1151 if (ndmscsi_target_from_str(ndmp_job.robot_target,
1152 store->ndmp_changer_device) != 0) {
1153 free(ndmp_job.robot_target);
1154 Dmsg0(100, "no robot to use\n");
1155 return retval;
1156 }
1157
1158 tapedevice = reserve_ndmp_tapedevice_for_job(store, jcr);
1159 ndmp_job.tape_device = (char*)tapedevice.c_str();
1160
1161 int driveindex = lookup_ndmp_driveindex_by_name(store, ndmp_job.tape_device);
1162
1163 if (driveindex == -1) {
1164 Jmsg(jcr, M_ERROR, 0, _("Could not find driveindex of drive %s\n"),
1165 ndmp_job.tape_device);
1166 return retval;
1167 }
1168
1169 driveaddress = GetElementAddressByBareosSlotNumber(
1170 &store->runtime_storage_status->storage_mapping,
1171 slot_type_t::kSlotTypeDrive, driveindex);
1172 if (driveaddress == kInvalidSlotNumber) {
1173 Jmsg(jcr, M_ERROR, 0,
1174 _("Could not lookup driveaddress for driveindex %d\n"), driveaddress);
1175 return retval;
1176 }
1177 ndmp_job.drive_addr = driveaddress;
1178 ndmp_job.drive_addr_given = 1;
1179
1180 ndmp_job.have_robot = 1;
1181 /*
1182 * unload tape if tape is in drive
1183 */
1184 ndmp_job.auto_remedy = 1;
1185 ndmp_job.record_size = jcr->impl->res.client->ndmp_blocksize;
1186
1187 Jmsg(jcr, M_INFO, 0, _("Using Data host %s\n"), ndmp_job.data_agent.host);
1188 Jmsg(jcr, M_INFO, 0, _("Using Tape host:device:address %s:%s:@%d\n"),
1189 ndmp_job.tape_agent.host, ndmp_job.tape_device, ndmp_job.drive_addr);
1190 Jmsg(jcr, M_INFO, 0, _("Using Robot host:device(ident) %s:%s(%s)\n"),
1191 ndmp_job.robot_agent.host, ndmp_job.robot_target,
1192 store->runtime_storage_status->smc_ident);
1193 Jmsg(jcr, M_INFO, 0, _("Using Tape record size %d\n"), ndmp_job.record_size);
1194
1195 return true;
1196 }
1197
1198
1199 #else
1200 /**
1201 * Dummy entry points when NDMP not enabled.
1202 */
1203 void DoNdmpStorageStatus(UaContext* ua, StorageResource* store, char* cmd)
1204 {
1205 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1206 }
1207
1208 dlist* ndmp_get_vol_list(UaContext* ua,
1209 StorageResource* store,
1210 bool listall,
1211 bool scan)
1212 {
1213 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1214 return (dlist*)NULL;
1215 }
1216
1217 slot_number_t NdmpGetNumSlots(UaContext* ua, StorageResource* store)
1218 {
1219 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1220 return 0;
1221 }
1222
1223 drive_number_t NdmpGetNumDrives(UaContext* ua, StorageResource* store)
1224 {
1225 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1226 return 0;
1227 }
1228
1229 bool NdmpTransferVolume(UaContext* ua,
1230 StorageResource* store,
1231 slot_number_t src_slot,
1232 slot_number_t dst_slot)
1233 {
1234 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1235 return false;
1236 }
1237
1238 bool NdmpAutochangerVolumeOperation(UaContext* ua,
1239 StorageResource* store,
1240 const char* operation,
1241 drive_number_t drive,
1242 slot_number_t slot)
1243 {
1244 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1245 return false;
1246 }
1247
1248 bool NdmpSendLabelRequest(UaContext* ua,
1249 StorageResource* store,
1250 MediaDbRecord* mr,
1251 MediaDbRecord* omr,
1252 PoolDbRecord* pr,
1253 bool relabel,
1254 drive_number_t drive,
1255 slot_number_t slot)
1256 {
1257 Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
1258 return false;
1259 }
1260
1261 #endif /* HAVE_NDMP */
1262 } /* namespace directordaemon */
1263