-
Notifications
You must be signed in to change notification settings - Fork 102
/
configdrive.rs
104 lines (86 loc) · 3.62 KB
/
configdrive.rs
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
//! configdrive metadata fetcher for cloudstack
use errors::*;
use metadata::Metadata;
use nix::mount;
use openssh_keys::PublicKey;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Read;
use tempdir::TempDir;
const CONFIG_DRIVE_LABEL_1: &'static str = "config-2";
const CONFIG_DRIVE_LABEL_2: &'static str = "CONFIG-2";
#[derive(Debug)]
struct ConfigDrive {
temp_dir: Option<TempDir>,
target: PathBuf,
path: PathBuf,
}
impl ConfigDrive {
fn new() -> Result<Self> {
// maybe its already mounted
let path = Path::new("/media/ConfigDrive/cloudstack/metadata/");
if path.exists() {
return Ok(ConfigDrive {
temp_dir: None,
path: path.to_owned(),
target: path.to_owned(),
})
}
// if not try and mount with each of the labels
let target = TempDir::new("coreos-metadata")
.chain_err(|| "failed to create temporary directory")?;
mount_ro(&Path::new("/dev/disk/by-label/").join(CONFIG_DRIVE_LABEL_1), target.path(), "iso9660")
.or_else(|_| mount_ro(&Path::new("/dev/disk/by-label/").join(CONFIG_DRIVE_LABEL_2), target.path(), "iso9660"))?;
Ok(ConfigDrive {
path: target.path().join("cloudstack").join("metadata"),
target: target.path().to_owned(),
temp_dir: Some(target),
})
}
fn fetch_value(&self, key: &str) -> Result<Option<String>> {
let filename = self.path.join(format!("{}.txt", key));
if !filename.exists() {
return Ok(None)
}
let mut file = File::open(&filename)
.chain_err(|| format!("failed to open file '{:?}'", filename))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.chain_err(|| format!("failed to read from file '{:?}'", filename))?;
Ok(Some(contents))
}
fn fetch_publickeys(&self) -> Result<Vec<PublicKey>> {
let filename = self.path.join("public_keys.txt");
let file = File::open(&filename)
.chain_err(|| format!("failed to open file '{:?}'", filename))?;
PublicKey::read_keys(file)
.chain_err(|| "failed to read public keys from config drive file")
}
}
impl ::std::ops::Drop for ConfigDrive {
fn drop(&mut self) {
if self.temp_dir.is_some() {
unmount(&self.path).unwrap();
}
}
}
fn mount_ro(source: &Path, target: &Path, fstype: &str) -> Result<()> {
mount::mount(Some(source), target, Some(fstype), mount::MS_RDONLY, None::<&str>)
.chain_err(|| format!("failed to read-only mount source '{:?}' to target '{:?}' with filetype '{}'", source, target, fstype))
}
fn unmount(target: &Path) -> Result<()> {
mount::umount(target)
.chain_err(|| format!("failed to unmount target '{:?}'", target))
}
pub fn fetch_metadata() -> Result<Metadata> {
let drive = ConfigDrive::new()?;
Ok(Metadata::builder()
.add_publickeys(drive.fetch_publickeys()?)
.add_attribute_if_exists("CLOUDSTACK_AVAILABILITY_ZONE".into(), drive.fetch_value("availability_zone")?)
.add_attribute_if_exists("CLOUDSTACK_INSTANCE_ID".into(), drive.fetch_value("instance_id")?)
.add_attribute_if_exists("CLOUDSTACK_SERVICE_OFFERING".into(), drive.fetch_value("service_offering")?)
.add_attribute_if_exists("CLOUDSTACK_CLOUD_IDENTIFIER".into(), drive.fetch_value("cloud_identifier")?)
.add_attribute_if_exists("CLOUDSTACK_LOCAL_HOSTNAME".into(), drive.fetch_value("local_hostname")?)
.add_attribute_if_exists("CLOUDSTACK_VM_ID".into(), drive.fetch_value("vm_id")?)
.build())
}