Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' into disk
[simgrid.git] / src / plugins / file_system / s4u_FileSystem.cpp
1 /* Copyright (c) 2015-2019. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "simgrid/plugins/file_system.h"
7 #include "simgrid/s4u/Actor.hpp"
8 #include "src/surf/HostImpl.hpp"
9 #include "src/surf/xml/platf_private.hpp"
10 #include "xbt/config.hpp"
11
12 #include <algorithm>
13 #include <boost/algorithm/string.hpp>
14 #include <boost/algorithm/string/join.hpp>
15 #include <boost/algorithm/string/split.hpp>
16 #include <fstream>
17 #include <numeric>
18
19 XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_file, "S4U files");
20 int sg_storage_max_file_descriptors = 1024;
21
22 namespace simgrid {
23 namespace s4u {
24 simgrid::xbt::Extension<Disk, FileSystemDiskExt> FileSystemDiskExt::EXTENSION_ID;
25 simgrid::xbt::Extension<Storage, FileSystemStorageExt> FileSystemStorageExt::EXTENSION_ID;
26 simgrid::xbt::Extension<Host, FileDescriptorHostExt> FileDescriptorHostExt::EXTENSION_ID;
27
28 File::File(const std::string& fullpath, void* userdata) : File(fullpath, Host::current(), userdata){};
29
30 File::File(const std::string& fullpath, sg_host_t host, void* userdata) : fullpath_(fullpath), userdata_(userdata)
31 {
32   // this cannot fail because we get a xbt_die if the mountpoint does not exist
33   if (not host->get_mounted_storages().empty()) {
34     Storage* st                  = nullptr;
35     size_t longest_prefix_length = 0;
36     XBT_DEBUG("Search for storage name for '%s' on '%s'", fullpath_.c_str(), host->get_cname());
37
38     for (auto const& mnt : host->get_mounted_storages()) {
39       XBT_DEBUG("See '%s'", mnt.first.c_str());
40       mount_point_ = fullpath_.substr(0, mnt.first.length());
41
42       if (mount_point_ == mnt.first && mnt.first.length() > longest_prefix_length) {
43         /* The current mount name is found in the full path and is bigger than the previous*/
44         longest_prefix_length = mnt.first.length();
45         st                    = mnt.second;
46       }
47     }
48     if (longest_prefix_length > 0) { /* Mount point found, split fullpath_ into mount_name and path+filename*/
49       mount_point_ = fullpath_.substr(0, longest_prefix_length);
50       path_        = fullpath_.substr(longest_prefix_length, fullpath_.length());
51     } else
52       xbt_die("Can't find mount point for '%s' on '%s'", fullpath_.c_str(), host->get_cname());
53
54     local_storage_ = st;
55   }
56   if (not host->get_disks().empty()) {
57     Disk* d                      = nullptr;
58     size_t longest_prefix_length = 0;
59     for (auto const& disk : host->get_disks()) {
60       const char* current_mount_str = disk->get_property("mount");
61       if (current_mount_str) {
62         std::string current_mount = std::string(current_mount_str);
63         mount_point_              = fullpath_.substr(0, current_mount.length());
64         if (mount_point_ == current_mount && current_mount.length() > longest_prefix_length) {
65           /* The current mount name is found in the full path and is bigger than the previous*/
66           longest_prefix_length = current_mount.length();
67           d                     = disk;
68         }
69         if (longest_prefix_length > 0) { /* Mount point found, split fullpath_ into mount_name and path+filename*/
70           mount_point_ = fullpath_.substr(0, longest_prefix_length);
71           path_        = fullpath_.substr(longest_prefix_length, fullpath_.length());
72         } else
73           xbt_die("Can't find mount point for '%s' on '%s'", fullpath_.c_str(), host->get_cname());
74       }
75     }
76     local_disk_ = d;
77   }
78
79   // assign a file descriptor id to the newly opened File
80   FileDescriptorHostExt* ext = host->extension<simgrid::s4u::FileDescriptorHostExt>();
81   if (ext->file_descriptor_table == nullptr) {
82     ext->file_descriptor_table.reset(new std::vector<int>(sg_storage_max_file_descriptors));
83     std::iota(ext->file_descriptor_table->rbegin(), ext->file_descriptor_table->rend(), 0); // Fill with ..., 1, 0.
84   }
85   xbt_assert(not ext->file_descriptor_table->empty(), "Too much files are opened! Some have to be closed.");
86   desc_id = ext->file_descriptor_table->back();
87   ext->file_descriptor_table->pop_back();
88
89   XBT_DEBUG("\tOpen file '%s'", path_.c_str());
90   std::map<std::string, sg_size_t>* content;
91   if (local_storage_)
92     content = local_storage_->extension<FileSystemStorageExt>()->get_content();
93   else
94     content = local_disk_->extension<FileSystemDiskExt>()->get_content();
95   // if file does not exist create an empty file
96   auto sz = content->find(path_);
97   if (sz != content->end()) {
98     size_ = sz->second;
99   } else {
100     size_ = 0;
101     content->insert({path_, size_});
102     XBT_DEBUG("File '%s' was not found, file created.", path_.c_str());
103   }
104 }
105
106 File::~File()
107 {
108   Host::current()->extension<simgrid::s4u::FileDescriptorHostExt>()->file_descriptor_table->push_back(desc_id);
109 }
110
111 void File::dump()
112 {
113   XBT_INFO("File Descriptor information:\n"
114            "\t\tFull path: '%s'\n"
115            "\t\tSize: %llu\n"
116            "\t\tMount point: '%s'\n"
117            "\t\tStorage Id: '%s'\n"
118            "\t\tStorage Type: '%s'\n"
119            "\t\tFile Descriptor Id: %d",
120            get_path(), size_, mount_point_.c_str(), local_storage_->get_cname(), local_storage_->get_type(), desc_id);
121 }
122
123 sg_size_t File::read(sg_size_t size)
124 {
125   if (size_ == 0) /* Nothing to read, return */
126     return 0;
127   sg_size_t read_size = 0;
128
129   if (local_storage_) {
130     /* Find the host where the file is physically located and read it */
131     Host* host = local_storage_->get_host();
132     XBT_DEBUG("READ %s on disk '%s'", get_path(), local_storage_->get_cname());
133     // if the current position is close to the end of the file, we may not be able to read the requested size
134     read_size = local_storage_->read(std::min(size, size_ - current_position_));
135     current_position_ += read_size;
136
137     if (host->get_name() != Host::current()->get_name() && read_size > 0) {
138       /* the file is hosted on a remote host, initiate a communication between src and dest hosts for data transfer */
139       XBT_DEBUG("File is on %s remote host, initiate data transfer of %llu bytes.", host->get_cname(), read_size);
140       host->send_to(Host::current(), read_size);
141     }
142   }
143
144   if (local_disk_) {
145     /* Find the host where the file is physically located and read it */
146     XBT_DEBUG("READ %s on disk '%s'", get_path(), local_disk_->get_cname());
147     // if the current position is close to the end of the file, we may not be able to read the requested size
148     read_size = local_disk_->read(std::min(size, size_ - current_position_));
149     current_position_ += read_size;
150   }
151
152   return read_size;
153 }
154
155 /** @brief Write into a file (local or remote)
156  *
157  * @param size of the file to write
158  * @return the number of bytes successfully write or -1 if an error occurred
159  */
160 sg_size_t File::write(sg_size_t size, int write_inside)
161 {
162   if (size == 0) /* Nothing to write, return */
163     return 0;
164   sg_size_t write_size = 0;
165
166   if (local_storage_) {
167     /* Find the host where the file is physically located (remote or local)*/
168     Host* host = local_storage_->get_host();
169
170     if (host->get_name() != Host::current()->get_name()) {
171       /* the file is hosted on a remote host, initiate a communication between src and dest hosts for data transfer */
172       XBT_DEBUG("File is on %s remote host, initiate data transfer of %llu bytes.", host->get_cname(), size);
173       Host::current()->send_to(host, size);
174     }
175
176     XBT_DEBUG("WRITE %s on disk '%s'. size '%llu/%llu' '%llu:%llu'", get_path(), local_storage_->get_cname(), size,
177               size_, sg_storage_get_size_used(local_storage_), sg_storage_get_size(local_storage_));
178     // If the storage is full before even starting to write
179     if (sg_storage_get_size_used(local_storage_) >= sg_storage_get_size(local_storage_))
180       return 0;
181     if (write_inside == 0) {
182       /* Substract the part of the file that might disappear from the used sized on the storage element */
183       local_storage_->extension<FileSystemStorageExt>()->decr_used_size(size_ - current_position_);
184       write_size = local_storage_->write(size);
185       local_storage_->extension<FileSystemStorageExt>()->incr_used_size(write_size);
186       current_position_ += write_size;
187       size_ = current_position_;
188     } else {
189       write_size = local_storage_->write(size);
190       current_position_ += write_size;
191       if (current_position_ > size_)
192         size_ = current_position_;
193     }
194     std::map<std::string, sg_size_t>* content = local_storage_->extension<FileSystemStorageExt>()->get_content();
195
196     content->erase(path_);
197     content->insert({path_, size_});
198   }
199
200   if (local_disk_) {
201     XBT_DEBUG("WRITE %s on disk '%s'. size '%llu/%llu' '%llu:%llu'", get_path(), local_disk_->get_cname(), size, size_,
202               sg_disk_get_size_used(local_disk_), sg_disk_get_size(local_disk_));
203     // If the storage is full before even starting to write
204     if (sg_disk_get_size_used(local_disk_) >= sg_disk_get_size(local_disk_))
205       return 0;
206     if (write_inside == 0) {
207       /* Substract the part of the file that might disappear from the used sized on the storage element */
208       local_disk_->extension<FileSystemDiskExt>()->decr_used_size(size_ - current_position_);
209       write_size = local_disk_->write(size);
210       local_disk_->extension<FileSystemDiskExt>()->incr_used_size(write_size);
211       current_position_ += write_size;
212       size_ = current_position_;
213     } else {
214       write_size = local_disk_->write(size);
215       current_position_ += write_size;
216       if (current_position_ > size_)
217         size_ = current_position_;
218     }
219     std::map<std::string, sg_size_t>* content = local_disk_->extension<FileSystemDiskExt>()->get_content();
220
221     content->erase(path_);
222     content->insert({path_, size_});
223   }
224   return write_size;
225 }
226
227 sg_size_t File::size()
228 {
229   return size_;
230 }
231
232 void File::seek(sg_offset_t offset)
233 {
234   current_position_ = offset;
235 }
236
237 void File::seek(sg_offset_t offset, int origin)
238 {
239   switch (origin) {
240     case SEEK_SET:
241       current_position_ = offset;
242       break;
243     case SEEK_CUR:
244       current_position_ += offset;
245       break;
246     case SEEK_END:
247       current_position_ = size_ + offset;
248       break;
249     default:
250       break;
251   }
252 }
253
254 sg_size_t File::tell()
255 {
256   return current_position_;
257 }
258
259 void File::move(const std::string& fullpath)
260 {
261   /* Check if the new full path is on the same mount point */
262   if (fullpath.compare(0, mount_point_.length(), mount_point_) == 0) {
263     std::map<std::string, sg_size_t>* content;
264     if (local_storage_)
265       content = local_storage_->extension<FileSystemStorageExt>()->get_content();
266     if (local_disk_)
267       content = local_disk_->extension<FileSystemDiskExt>()->get_content();
268     auto sz = content->find(path_);
269     if (sz != content->end()) { // src file exists
270       sg_size_t new_size = sz->second;
271       content->erase(path_);
272       std::string path = fullpath.substr(mount_point_.length(), fullpath.length());
273       content->insert({path.c_str(), new_size});
274       XBT_DEBUG("Move file from %s to %s, size '%llu'", path_.c_str(), fullpath.c_str(), new_size);
275     } else {
276       XBT_WARN("File %s doesn't exist", path_.c_str());
277     }
278   } else {
279     XBT_WARN("New full path %s is not on the same mount point: %s.", fullpath.c_str(), mount_point_.c_str());
280   }
281 }
282
283 int File::unlink()
284 {
285   /* Check if the file is on local storage */
286   std::map<std::string, sg_size_t>* content;
287   const char* name = nullptr;
288   if (local_storage_) {
289     content = local_storage_->extension<FileSystemStorageExt>()->get_content();
290     name    = local_storage_->get_cname();
291   }
292   if (local_disk_) {
293     content = local_disk_->extension<FileSystemDiskExt>()->get_content();
294     name    = local_disk_->get_cname();
295   }
296
297   if (content->find(path_) == content->end()) {
298     XBT_WARN("File %s is not on disk %s. Impossible to unlink", path_.c_str(), name);
299     return -1;
300   } else {
301     XBT_DEBUG("UNLINK %s on disk '%s'", path_.c_str(), name);
302     if (local_storage_)
303       local_storage_->extension<FileSystemStorageExt>()->decr_used_size(size_);
304     if (local_disk_)
305       local_disk_->extension<FileSystemDiskExt>()->decr_used_size(size_);
306
307     // Remove the file from storage
308     content->erase(fullpath_);
309
310     return 0;
311   }
312 }
313
314 int File::remote_copy(sg_host_t host, const char* fullpath)
315 {
316   /* Find the host where the file is physically located and read it */
317   Storage* storage_src = local_storage_;
318   Host* src_host       = storage_src->get_host();
319   seek(0, SEEK_SET);
320   XBT_DEBUG("READ %s on disk '%s'", get_path(), local_storage_->get_cname());
321   // if the current position is close to the end of the file, we may not be able to read the requested size
322   sg_size_t read_size = local_storage_->read(size_);
323   current_position_ += read_size;
324
325   /* Find the host that owns the storage where the file has to be copied */
326   Storage* storage_dest = nullptr;
327   Host* dst_host;
328   size_t longest_prefix_length = 0;
329
330   for (auto const& elm : host->get_mounted_storages()) {
331     std::string mount_point = std::string(fullpath).substr(0, elm.first.size());
332     if (mount_point == elm.first && elm.first.length() > longest_prefix_length) {
333       /* The current mount name is found in the full path and is bigger than the previous*/
334       longest_prefix_length = elm.first.length();
335       storage_dest          = elm.second;
336     }
337   }
338
339   if (storage_dest != nullptr) {
340     /* Mount point found, retrieve the host the storage is attached to */
341     dst_host = storage_dest->get_host();
342   } else {
343     XBT_WARN("Can't find mount point for '%s' on destination host '%s'", fullpath, host->get_cname());
344     return -1;
345   }
346
347   XBT_DEBUG("Initiate data transfer of %llu bytes between %s and %s.", read_size, src_host->get_cname(),
348             storage_dest->get_host()->get_cname());
349   src_host->send_to(dst_host, read_size);
350
351   /* Create file on remote host, write it and close it */
352   File* fd = new File(fullpath, dst_host, nullptr);
353   sg_size_t write_size = fd->local_storage_->write(read_size);
354   fd->local_storage_->extension<FileSystemStorageExt>()->incr_used_size(write_size);
355   (*(fd->local_storage_->extension<FileSystemStorageExt>()->get_content()))[path_] = size_;
356   delete fd;
357   return 0;
358 }
359
360 int File::remote_move(sg_host_t host, const char* fullpath)
361 {
362   int res = remote_copy(host, fullpath);
363   unlink();
364   return res;
365 }
366
367 FileSystemDiskExt::FileSystemDiskExt(simgrid::s4u::Disk* ptr)
368 {
369   const char* size_str    = ptr->get_property("size");
370   const char* content_str = ptr->get_property("content");
371   size_                   = size_str ? surf_parse_get_size(size_str, "disk size", ptr->get_name()) : 0;
372   if (content_str)
373     content_.reset(parse_content(content_str));
374 }
375
376 FileSystemStorageExt::FileSystemStorageExt(simgrid::s4u::Storage* ptr)
377 {
378   content_.reset(parse_content(ptr->get_impl()->content_name_));
379   size_    = ptr->get_impl()->size_;
380 }
381
382 std::map<std::string, sg_size_t>* FileSystemDiskExt::parse_content(const std::string& filename)
383 {
384   if (filename.empty())
385     return nullptr;
386
387   std::map<std::string, sg_size_t>* parse_content = new std::map<std::string, sg_size_t>();
388
389   std::ifstream* fs = surf_ifsopen(filename);
390
391   std::string line;
392   std::vector<std::string> tokens;
393   do {
394     std::getline(*fs, line);
395     boost::trim(line);
396     if (line.length() > 0) {
397       boost::split(tokens, line, boost::is_any_of(" \t"), boost::token_compress_on);
398       xbt_assert(tokens.size() == 2, "Parse error in %s: %s", filename.c_str(), line.c_str());
399       sg_size_t size = std::stoull(tokens.at(1));
400
401       used_size_ += size;
402       parse_content->insert({tokens.front(), size});
403     }
404   } while (not fs->eof());
405   delete fs;
406   return parse_content;
407 }
408
409 std::map<std::string, sg_size_t>* FileSystemStorageExt::parse_content(const std::string& filename)
410 {
411   if (filename.empty())
412     return nullptr;
413
414   std::map<std::string, sg_size_t>* parse_content = new std::map<std::string, sg_size_t>();
415
416   std::ifstream* fs = surf_ifsopen(filename);
417
418   std::string line;
419   std::vector<std::string> tokens;
420   do {
421     std::getline(*fs, line);
422     boost::trim(line);
423     if (line.length() > 0) {
424       boost::split(tokens, line, boost::is_any_of(" \t"), boost::token_compress_on);
425       xbt_assert(tokens.size() == 2, "Parse error in %s: %s", filename.c_str(), line.c_str());
426       sg_size_t size = std::stoull(tokens.at(1));
427
428       used_size_ += size;
429       parse_content->insert({tokens.front(), size});
430     }
431   } while (not fs->eof());
432   delete fs;
433   return parse_content;
434 }
435 }
436 }
437
438 using simgrid::s4u::FileDescriptorHostExt;
439 using simgrid::s4u::FileSystemDiskExt;
440 using simgrid::s4u::FileSystemStorageExt;
441
442 static void on_disk_creation(simgrid::s4u::Disk& d)
443 {
444   d.extension_set(new FileSystemDiskExt(&d));
445 }
446 static void on_storage_creation(simgrid::s4u::Storage& st)
447 {
448   st.extension_set(new FileSystemStorageExt(&st));
449 }
450
451 static void on_host_creation(simgrid::s4u::Host& host)
452 {
453   host.extension_set<FileDescriptorHostExt>(new FileDescriptorHostExt());
454 }
455
456 /* **************************** Public interface *************************** */
457 void sg_storage_file_system_init()
458 {
459   sg_storage_max_file_descriptors = 1024;
460   simgrid::config::bind_flag(sg_storage_max_file_descriptors, "storage/max_file_descriptors",
461                              "Maximum number of concurrently opened files per host. Default is 1024");
462
463   if (not FileSystemStorageExt::EXTENSION_ID.valid()) {
464     FileSystemStorageExt::EXTENSION_ID = simgrid::s4u::Storage::extension_create<FileSystemStorageExt>();
465     simgrid::s4u::Storage::on_creation.connect(&on_storage_creation);
466   }
467
468   if (not FileSystemDiskExt::EXTENSION_ID.valid()) {
469     FileSystemDiskExt::EXTENSION_ID = simgrid::s4u::Disk::extension_create<FileSystemDiskExt>();
470     simgrid::s4u::Disk::on_creation.connect(&on_disk_creation);
471   }
472
473   if (not FileDescriptorHostExt::EXTENSION_ID.valid()) {
474     FileDescriptorHostExt::EXTENSION_ID = simgrid::s4u::Host::extension_create<FileDescriptorHostExt>();
475     simgrid::s4u::Host::on_creation.connect(&on_host_creation);
476   }
477 }
478
479 sg_file_t sg_file_open(const char* fullpath, void* data)
480 {
481   return new simgrid::s4u::File(fullpath, data);
482 }
483
484 sg_size_t sg_file_read(sg_file_t fd, sg_size_t size)
485 {
486   return fd->read(size);
487 }
488
489 sg_size_t sg_file_write(sg_file_t fd, sg_size_t size)
490 {
491   return fd->write(size);
492 }
493
494 void sg_file_close(sg_file_t fd)
495 {
496   delete fd;
497 }
498
499 const char* sg_file_get_name(sg_file_t fd)
500 {
501   xbt_assert((fd != nullptr), "Invalid file descriptor");
502   return fd->get_path();
503 }
504
505 sg_size_t sg_file_get_size(sg_file_t fd)
506 {
507   return fd->size();
508 }
509
510 void sg_file_dump(sg_file_t fd)
511 {
512   fd->dump();
513 }
514
515 void* sg_file_get_data(sg_file_t fd)
516 {
517   return fd->get_userdata();
518 }
519
520 void sg_file_set_data(sg_file_t fd, void* data)
521 {
522   fd->set_userdata(data);
523 }
524
525 /**
526  * @brief Set the file position indicator in the sg_file_t by adding offset bytes
527  * to the position specified by origin (either SEEK_SET, SEEK_CUR, or SEEK_END).
528  *
529  * @param fd : file object that identifies the stream
530  * @param offset : number of bytes to offset from origin
531  * @param origin : Position used as reference for the offset. It is specified by one of the following constants defined
532  *                 in \<stdio.h\> exclusively to be used as arguments for this function (SEEK_SET = beginning of file,
533  *                 SEEK_CUR = current position of the file pointer, SEEK_END = end of file)
534  */
535 void sg_file_seek(sg_file_t fd, sg_offset_t offset, int origin)
536 {
537   fd->seek(offset, origin);
538 }
539
540 sg_size_t sg_file_tell(sg_file_t fd)
541 {
542   return fd->tell();
543 }
544
545 void sg_file_move(sg_file_t fd, const char* fullpath)
546 {
547   fd->move(fullpath);
548 }
549
550 void sg_file_unlink(sg_file_t fd)
551 {
552   fd->unlink();
553   delete fd;
554 }
555
556 /**
557  * @brief Copy a file to another location on a remote host.
558  * @param file : the file to move
559  * @param host : the remote host where the file has to be copied
560  * @param fullpath : the complete path destination on the remote host
561  * @return If successful, the function returns 0. Otherwise, it returns -1.
562  */
563 int sg_file_rcopy(sg_file_t file, sg_host_t host, const char* fullpath)
564 {
565   return file->remote_copy(host, fullpath);
566 }
567
568 /**
569  * @brief Move a file to another location on a remote host.
570  * @param file : the file to move
571  * @param host : the remote host where the file has to be moved
572  * @param fullpath : the complete path destination on the remote host
573  * @return If successful, the function returns 0. Otherwise, it returns -1.
574  */
575 int sg_file_rmove(sg_file_t file, sg_host_t host, const char* fullpath)
576 {
577   return file->remote_move(host, fullpath);
578 }
579
580 sg_size_t sg_disk_get_size_free(sg_disk_t d)
581 {
582   return d->extension<FileSystemDiskExt>()->get_size() - d->extension<FileSystemDiskExt>()->get_used_size();
583 }
584
585 sg_size_t sg_disk_get_size_used(sg_disk_t d)
586 {
587   return d->extension<FileSystemDiskExt>()->get_used_size();
588 }
589
590 sg_size_t sg_disk_get_size(sg_disk_t d)
591 {
592   return d->extension<FileSystemDiskExt>()->get_size();
593 }
594
595 sg_size_t sg_storage_get_size_free(sg_storage_t st)
596 {
597   return st->extension<FileSystemStorageExt>()->get_size() - st->extension<FileSystemStorageExt>()->get_used_size();
598 }
599
600 sg_size_t sg_storage_get_size_used(sg_storage_t st)
601 {
602   return st->extension<FileSystemStorageExt>()->get_used_size();
603 }
604
605 sg_size_t sg_storage_get_size(sg_storage_t st)
606 {
607   return st->extension<FileSystemStorageExt>()->get_size();
608 }
609
610 xbt_dict_t sg_storage_get_content(sg_storage_t storage)
611 {
612   std::map<std::string, sg_size_t>* content = storage->extension<simgrid::s4u::FileSystemStorageExt>()->get_content();
613   // Note: ::operator delete is ok here (no destructor called) since the dict elements are of POD type sg_size_t.
614   xbt_dict_t content_as_dict = xbt_dict_new_homogeneous(::operator delete);
615
616   for (auto const& entry : *content) {
617     sg_size_t* psize = new sg_size_t;
618     *psize           = entry.second;
619     xbt_dict_set(content_as_dict, entry.first.c_str(), psize, nullptr);
620   }
621   return content_as_dict;
622 }
623
624 xbt_dict_t sg_host_get_storage_content(sg_host_t host)
625 {
626   xbt_assert((host != nullptr), "Invalid parameters");
627   xbt_dict_t contents = xbt_dict_new_homogeneous(nullptr);
628   for (auto const& elm : host->get_mounted_storages())
629     xbt_dict_set(contents, elm.first.c_str(), sg_storage_get_content(elm.second), nullptr);
630
631   return contents;
632 }