use crate::tasks::*;
use crate::handle::handle::{TaskHandle,CheckRc};
use crate::tasks::fields::Field;
use crate::inventory::hosts::PackagePreference;
use serde::{Deserialize};
use std::sync::Arc;
use std::vec::Vec;
const MODULE: &str = "yum_dnf";
#[derive(Deserialize,Debug)]
#[serde(deny_unknown_fields)]
pub struct YumDnfTask {
pub name: Option<String>,
pub package: String,
pub version: Option<String>,
pub update: Option<String>,
pub remove: Option<String>,
pub with: Option<PreLogicInput>,
pub and: Option<PostLogicInput>
}
struct YumDnfAction {
pub package: String,
pub version: Option<String>,
pub update: bool,
pub remove: bool,
}
#[derive(Clone,PartialEq,Debug)]
struct PackageDetails {
name: String,
version: String,
}
impl IsTask for YumDnfTask {
fn get_module(&self) -> String { String::from(MODULE) }
fn get_name(&self) -> Option<String> { self.name.clone() }
fn get_with(&self) -> Option<PreLogicInput> { self.with.clone() }
fn evaluate(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>, tm: TemplateMode) -> Result<EvaluatedTask, Arc<TaskResponse>> {
return Ok(
EvaluatedTask {
action: Arc::new(YumDnfAction {
package: handle.template.string_no_spaces(request, tm, &String::from("package"), &self.package)?,
version: handle.template.string_option_no_spaces(&request, tm, &String::from("version"), &self.version)?,
update: handle.template.boolean_option_default_false(&request, tm, &String::from("update"), &self.update)?,
remove: handle.template.boolean_option_default_false(&request, tm, &String::from("remove"), &self.remove)?
}),
with: Arc::new(PreLogicInput::template(&handle, &request, tm, &self.with)?),
and: Arc::new(PostLogicInput::template(&handle, &request, tm, &self.and)?),
}
);
}
}
impl IsAction for YumDnfAction {
fn dispatch(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<Arc<TaskResponse>, Arc<TaskResponse>> {
match request.request_type {
TaskRequestType::Query => {
let mut changes : Vec<Field> = Vec::new();
self.set_package_preference(handle, request)?;
let package_details = self.get_package_details(handle, request)?;
if package_details.is_some() {
if self.remove {
return Ok(handle.response.needs_removal(request));
}
let pkg = package_details.unwrap();
if self.update {
changes.push(Field::Version);
} else if self.version.is_some() {
let specified_version = self.version.as_ref().unwrap();
if ! pkg.version.eq(specified_version) { changes.push(Field::Version); }
}
if changes.len() > 0 {
return Ok(handle.response.needs_modification(request, &changes));
} else {
return Ok(handle.response.is_matched(request));
}
} else {
return match self.remove {
true => Ok(handle.response.is_matched(request)),
false => Ok(handle.response.needs_creation(request))
}
}
},
TaskRequestType::Create => {
self.install_package(handle, request)?;
return Ok(handle.response.is_created(request));
}
TaskRequestType::Modify => {
if request.changes.contains(&Field::Version) {
self.update_package(handle, request)?;
}
return Ok(handle.response.is_modified(request, request.changes.clone()));
}
TaskRequestType::Remove => {
self.remove_package(handle, request)?;
return Ok(handle.response.is_removed(request));
}
_ => { return Err(handle.response.not_supported(request)); }
}
}
}
impl YumDnfAction {
pub fn set_package_preference(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<(),Arc<TaskResponse>> {
if handle.host.read().unwrap().package_preference.is_some() {
return Ok(());
}
match handle.remote.get_mode(request, &String::from("/usr/bin/dnf"))? {
Some(_) => {
handle.host.write().unwrap().package_preference = Some(PackagePreference::Dnf);
}
None => match handle.remote.get_mode(request, &String::from("/usr/bin/yum"))? {
Some(_) => {
handle.host.write().unwrap().package_preference = Some(PackagePreference::Yum);
}
None => { return Err(handle.response.is_failed(request, &String::from("neither dnf nor yum detected"))); }
}
}
Ok(())
}
pub fn get_package_preference(&self, handle: &Arc<TaskHandle>) -> Option<PackagePreference> {
handle.host.read().unwrap().package_preference
}
pub fn get_package_manager(&self, handle: &Arc<TaskHandle>) -> String {
match self.get_package_preference(handle) {
Some(PackagePreference::Yum) => String::from("yum"),
Some(PackagePreference::Dnf) => String::from("dnf"),
_ => { panic!("internal error, package preference not set correctly"); }
}
}
pub fn get_package_details(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<Option<PackageDetails>,Arc<TaskResponse>> {
let which = self.get_package_manager(handle);
let cmd = match self.version.is_none() {
true => format!("{} info {}", which, self.package),
false => format!("{} info {}-{}", which, self.package, self.version.as_ref().unwrap())
};
let result = handle.remote.run(request, &cmd, CheckRc::Unchecked)?;
let (_rc,out) = cmd_info(&result);
let details = self.parse_package_details(&out.clone())?;
return Ok(details);
}
pub fn parse_package_details(&self, out: &String) -> Result<Option<PackageDetails>,Arc<TaskResponse>> {
let mut name: Option<String> = None;
let mut version: Option<String> = None;
for line in out.lines() {
if line.starts_with("Available") {
return Ok(None);
}
let mut tokens = line.split(":");
let key = tokens.nth(0);
let value = tokens.nth(0);
if key.is_some() && value.is_some() {
let key2 = key.unwrap().trim();
let value2 = value.unwrap().trim();
if key2.eq("Name") { name = Some(value2.to_string()); }
if key2.eq("Version") { version = Some(value2.to_string()); break; }
}
}
if name.is_some() && version.is_some() {
return Ok(Some(PackageDetails { name: name.unwrap().clone(), version: version.unwrap().clone() }));
} else {
return Ok(None);
}
}
pub fn install_package(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<Arc<TaskResponse>,Arc<TaskResponse>>{
let which = self.get_package_manager(handle);
let cmd = match self.version.is_none() {
true => format!("{} install '{}' -y", which, self.package),
false => format!("{}f install '{}-{}' -y", which, self.package, self.version.as_ref().unwrap())
};
return handle.remote.run(request, &cmd, CheckRc::Checked);
}
pub fn update_package(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<Arc<TaskResponse>,Arc<TaskResponse>>{
let which = self.get_package_manager(handle);
let cmd = format!("{} update '{}' -y", which, self.package);
return handle.remote.run(request, &cmd, CheckRc::Checked);
}
pub fn remove_package(&self, handle: &Arc<TaskHandle>, request: &Arc<TaskRequest>) -> Result<Arc<TaskResponse>,Arc<TaskResponse>>{
let which = self.get_package_manager(handle);
let cmd = format!("{} remove '{}' -y", which, self.package);
return handle.remote.run(request, &cmd, CheckRc::Checked);
}
}