forked from coreos/afterburn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
openstack: use config-drive by default
This changes afterburn to use config-drive as default for scraping metadata and then try metadata API if config-drive is not available. Also changes the name of the platform from `openstack-metadata` to `openstack`. Closes: coreos/fedora-coreos-tracker#422 Signed-off-by: Allen Bai <[email protected]>
- Loading branch information
Allen Bai
committed
Jul 22, 2020
1 parent
ff48266
commit 4f52a9e
Showing
4 changed files
with
189 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
//! configdrive metadata fetcher for OpenStack | ||
//! reference: https://docs.openstack.org/nova/latest/user/metadata.html | ||
use std::collections::HashMap; | ||
use std::fs::File; | ||
use std::io::Read; | ||
use std::path::{Path, PathBuf}; | ||
|
||
use openssh_keys::PublicKey; | ||
use slog_scope::{error, warn}; | ||
use tempfile::TempDir; | ||
|
||
use crate::errors::*; | ||
use crate::network; | ||
use crate::providers::MetadataProvider; | ||
|
||
const CONFIG_DRIVE_LABEL: &str = "config-2"; | ||
|
||
/// OpenStack config-drive. | ||
#[derive(Debug)] | ||
pub struct OpenstackConfigDrive { | ||
/// Path to the top directory of the mounted config-drive. | ||
drive_path: PathBuf, | ||
/// Temporary directory for own mountpoint (if any). | ||
temp_dir: Option<TempDir>, | ||
} | ||
|
||
impl OpenstackConfigDrive { | ||
/// Try to build a new provider client. | ||
/// | ||
/// This internally tries to mount (and own) the config-drive. | ||
pub fn try_new() -> Result<Self> { | ||
// Short-circuit if the config-drive is already mounted. | ||
let path = Path::new("/media/ConfigDrive/openstack/latest/"); | ||
if path.exists() { | ||
return Ok(OpenstackConfigDrive { | ||
temp_dir: None, | ||
drive_path: PathBuf::from("/media/ConfigDrive/"), | ||
}); | ||
} | ||
|
||
// Otherwise, try and mount with the label. | ||
let target = tempfile::Builder::new() | ||
.prefix("afterburn-") | ||
.tempdir() | ||
.chain_err(|| "failed to create temporary directory")?; | ||
crate::util::mount_ro( | ||
&Path::new("/dev/disk/by-label/").join(CONFIG_DRIVE_LABEL), | ||
target.path(), | ||
"iso9660", | ||
3, | ||
)?; | ||
|
||
let cd = OpenstackConfigDrive { | ||
drive_path: target.path().to_owned(), | ||
temp_dir: Some(target), | ||
}; | ||
Ok(cd) | ||
} | ||
|
||
/// Return the path to the metadata directory. | ||
fn metadata_dir(&self, platform: &str) -> PathBuf { | ||
self.drive_path.clone().join(platform).join("latest") | ||
} | ||
|
||
// The metadata is stored as key:value pair in ec2/meta-data.json file | ||
fn fetch_value(&self, key: &str) -> Result<Option<String>> { | ||
let filename = self.metadata_dir("ec2").join("meta-data.json"); | ||
|
||
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))?; | ||
let contents_json: serde_json::Value = serde_json::from_str(&contents)?; | ||
|
||
if let Some(value) = contents_json[key].as_str() { | ||
Ok(Some(value.to_owned())) | ||
} else { | ||
Ok(None) | ||
} | ||
} | ||
|
||
fn fetch_publickeys(&self) -> Result<Vec<PublicKey>> { | ||
let filename = self.metadata_dir("openstack").join("meta_data.json"); | ||
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))?; | ||
let contents_json: serde_json::Value = serde_json::from_str(&contents)?; | ||
|
||
if let Some(public_keys_map) = contents_json["public_keys"].as_object() { | ||
let public_keys_vec: Vec<&std::string::String> = | ||
public_keys_map.keys().collect(); | ||
let mut out = vec![]; | ||
for key in public_keys_vec { | ||
let key = PublicKey::parse(&key)?; | ||
out.push(key); | ||
} | ||
Ok(out) | ||
} else { | ||
Ok(vec![]) | ||
} | ||
} | ||
} | ||
|
||
impl MetadataProvider for OpenstackConfigDrive { | ||
fn attributes(&self) -> Result<HashMap<String, String>> { | ||
let mut out = HashMap::with_capacity(5); | ||
let add_value = |map: &mut HashMap<_, _>, key: &str, name| -> Result<()> { | ||
let value = self.fetch_value(name)?; | ||
|
||
if let Some(value) = value { | ||
map.insert(key.to_string(), value); | ||
} | ||
|
||
Ok(()) | ||
}; | ||
|
||
add_value(&mut out, "OPENSTACK_HOSTNAME", "hostname")?; | ||
add_value(&mut out, "OPENSTACK_INSTANCE_ID", "instance-id")?; | ||
add_value(&mut out, "OPENSTACK_INSTANCE_TYPE", "instance-type")?; | ||
add_value(&mut out, "OPENSTACK_IPV4_LOCAL", "local-ipv4")?; | ||
add_value(&mut out, "OPENSTACK_IPV4_PUBLIC", "public-ipv4")?; | ||
|
||
Ok(out) | ||
} | ||
|
||
fn hostname(&self) -> Result<Option<String>> { | ||
Ok(None) | ||
} | ||
|
||
fn ssh_keys(&self) -> Result<Vec<PublicKey>> { | ||
self.fetch_publickeys() | ||
} | ||
|
||
fn networks(&self) -> Result<Vec<network::Interface>> { | ||
Ok(vec![]) | ||
} | ||
|
||
fn virtual_network_devices(&self) -> Result<Vec<network::VirtualNetDev>> { | ||
warn!("virtual network devices metadata requested, but not supported on this platform"); | ||
Ok(vec![]) | ||
} | ||
|
||
fn boot_checkin(&self) -> Result<()> { | ||
warn!("boot check-in requested, but not supported on this platform"); | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Drop for OpenstackConfigDrive { | ||
fn drop(&mut self) { | ||
if self.temp_dir.is_some() { | ||
if let Err(e) = crate::util::unmount(&self.drive_path, 3) { | ||
error!("failed to cleanup OpenStack config-drive: {}", e); | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,5 @@ | |
|
||
//! openstack metadata fetcher | ||
pub mod configdrive; | ||
pub mod network; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters