Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphaelGauthier committed Dec 8, 2023
1 parent 79fd607 commit c4e796e
Show file tree
Hide file tree
Showing 13 changed files with 729 additions and 717 deletions.
Expand Up @@ -95,24 +95,6 @@ function refuseEnter(event)
}
}

/* portlet */

$(function() {

$(".portlet").addClass("ui-widget ui-widget-content ui-helper-clearfix arrondis")
.find(".portlet-header")
.addClass("ui-widget-header")
.end()
.find(".portlet-content");

$(".portlet-header .ui-icon").click(function() {
$(this).toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick");
$(this).parents(".portlet:first").find(".portlet-content").toggle();
});

});


/**
* Check all checkbox named name according to the status of the checkbox with id id
* @param id
Expand Down
Expand Up @@ -43,29 +43,41 @@ import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.AcceptedInventory
import com.normation.inventory.domain.FullInventory
import com.normation.inventory.domain.NodeId
import com.normation.inventory.domain.NodeSummary
import com.normation.plugins.DefaultExtendableSnippet
import com.normation.rudder.batch.AutomaticStartDeployment
import com.normation.rudder.domain.Constants
import com.normation.rudder.domain.logger.NodeLoggerPure
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.nodes.NodeState
import com.normation.rudder.domain.policies.GlobalPolicyMode
import com.normation.rudder.facts.nodes.ChangeContext
import com.normation.rudder.hooks.HookReturnCode
import com.normation.rudder.reports.AgentRunInterval
import com.normation.rudder.reports.GlobalComplianceMode
import com.normation.rudder.reports.HeartbeatConfiguration
import com.normation.rudder.reports.NodeComplianceMode
import com.normation.rudder.repository.FullNodeGroupCategory
import com.normation.rudder.services.servers.DeleteMode
import com.normation.rudder.web.ChooseTemplate
import com.normation.rudder.web.model.JsNodeId
import com.normation.rudder.web.services.CurrentUser
import com.normation.rudder.web.services.DisplayNode
import com.normation.rudder.web.services.DisplayNode.displayMachineType
import com.normation.rudder.web.services.DisplayNode.getNodeState
import com.normation.rudder.web.services.DisplayNodeGroupTree
import com.normation.rudder.web.snippet.RegisterToasts
import com.normation.rudder.web.snippet.ToastNotification
import net.liftweb.common._
import net.liftweb.http.DispatchSnippet
import net.liftweb.http.S
import net.liftweb.http.SHtml
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.js.JsCmd
import net.liftweb.http.js.JsCmds._
import net.liftweb.http.js.JsExp
import net.liftweb.util.Helpers._
import org.joda.time.DateTime
import scala.xml.NodeSeq

object ShowNodeDetailsFromNode {
Expand Down Expand Up @@ -261,12 +273,12 @@ class ShowNodeDetailsFromNode(
val jsId = JsNodeId(nodeId, "")
def htmlId(jsId: JsNodeId, prefix: String): String = prefix + jsId.toString
val detailsId = htmlId(jsId, "details_")

configService.rudder_global_policy_mode().toBox match {
case Full(globalMode) =>
bindNode(node, sm, withinPopup, globalMode) ++ Script(
DisplayNode.jsInit(node.id, sm.node.softwareIds, "") &
JsRaw(s"""
$$('#nodeHostname').html("${xml.Utility.escape(sm.node.main.hostname)}");
$$( "#${detailsId}" ).tabs({ active : ${tab} } );
$$('#nodeInventory .ui-tabs-vertical .ui-tabs-nav li a').on('click',function(){
var tab = $$(this).attr('href');
Expand All @@ -275,6 +287,7 @@ class ShowNodeDetailsFromNode(
$$('#nodeInventory > .sInventory > .sInventory').hide();
$$(tab).show();
});
new ClipboardJS('[data-clipboard-text]');
""") &
buildJsTree(groupTreeId)
)
Expand All @@ -299,9 +312,155 @@ class ShowNodeDetailsFromNode(
* Show the content of a node in the portlet
* @return
*/
private[this] def removeNode(node: NodeSummary): JsCmd = {
implicit val cc: ChangeContext = ChangeContext(
ModificationId(uuidGen.newUuid),
CurrentUser.actor,
DateTime.now(),
None,
S.request.map(_.remoteAddr).toOption
)

// only erase for Rudder 8.0
RudderConfig.removeNodeService.removeNodePure(node.id, DeleteMode.Erase).toBox match {
case Full(_) =>
asyncDeploymentAgent ! AutomaticStartDeployment(cc.modId, cc.actor)
onSuccess(node)
case eb: EmptyBox =>
val message = s"There was an error while deleting node '${node.hostname}' [${node.id.value}]"
val e = eb ?~! message
NodeLoggerPure.Delete.logEffect.error(e.messageChain)
onFailure(node, message, e.messageChain, None)
}
}

private[this] def onFailure(
node: NodeSummary,
message: String,
details: String,
hookError: Option[HookReturnCode.Error]
): JsCmd = {
RegisterToasts.register(
ToastNotification.Error(
s"An error happened when trying to delete node '${node.hostname}' [${node.id.value}]. " +
"Please contact your server admin to resolve the problem. " +
s"Error was: '${message}'"
)
)
RedirectTo("/secure/nodeManager/nodes")
}

private[this] def onSuccess(node: NodeSummary): JsCmd = {
RegisterToasts.register(ToastNotification.Success(s"Node '${node.hostname}' [${node.id.value}] was correctly deleted"))
RedirectTo("/secure/nodeManager/nodes")
}

private[this] def isRootNode(n: NodeId): Boolean = {
n.value.equals("root");
}

private def bindNode(node: NodeInfo, inventory: FullInventory, withinPopup: Boolean, globalMode: GlobalPolicyMode): NodeSeq = {
val id = JsNodeId(node.id)
("#node_groupTree" #>

val machineTooltip: String = {
s"""
|<h4>Machine details</h4>
|<div class='tooltip-content'>
| <ul>
| <li><b>Type:</b> ${displayMachineType(inventory.machine)}</li>
| <li><b>Total physical memory (RAM):</b> ${xml.Utility.escape(
node.ram.map(_.toStringMo).getOrElse("-")
)}</li>
| <li><b>Manufacturer:</b> ${xml.Utility.escape(
inventory.machine.flatMap(x => x.manufacturer).map(x => x.name).getOrElse("-")
)}</li>
| <li><b>Total swap space:</b> ${xml.Utility.escape(inventory.node.swap.map(_.toStringMo).getOrElse("-"))}</li>
| <li><b>System serial number:</b> ${xml.Utility.escape(
inventory.machine.flatMap(x => x.systemSerialNumber).getOrElse("-")
)}</li>
| <li><b>Time zone:</b> ${xml.Utility.escape(
inventory.node.timezone
.map(x => if (x.name.toLowerCase == "utc") "UTC" else s"${x.name} (UTC ${x.offset})")
.getOrElse("unknown")
)}</li>
| <li>${inventory.machine
.map(_.id.value)
.map(machineId => "<b>Machine ID:</b> " ++ { xml.Utility.escape(machineId) })
.getOrElse("<span class='error'>Machine Information are missing for that node</span>")}</li>
| </ul>
|</div>
|""".stripMargin.replaceAll("\n", " ")
}

val headerInfo = {
<div id="nodeHeaderInfo">
<span class={"node-state " ++ getNodeState(node.state).toLowerCase} title={getNodeState(node.state)}></span>
<span>{node.hostname}</span>
<span class="machine-os-info">
<span class="machine-info">{node.osDetails.fullName}</span>
<span class="machine-info ram">{node.ram.map(_.toStringMo).getOrElse("-")}</span>
<span class="fa fa-info-circle icon-info" data-bs-toggle="tooltip" data-bs-placement="bottom" title={
machineTooltip
}></span>
</span>
</div>
}

val headerSubtitle = {
<div class="header-subtitle">
<a class="clipboard" title="Copy to clipboard" data-clipboard-text={inventory.node.main.id.value}>
<span id="nodeHeaderId">{inventory.node.main.id.value}</span>
<i class="ion ion-clipboard"></i>
</a>
</div>
}

val deleteBtn = {
inventory.node.main.status match {
case AcceptedInventory =>
<lift:authz role="node_write">
{
if (!isRootNode(inventory.node.main.id)) {
<button type="button" class="btn btn-danger btn-icon" data-bs-toggle="modal" data-bs-target="#nodeDeleteModal">
Delete
<i class="fa fa-times-circle"></i>
</button>
} else { NodeSeq.Empty }
}
</lift:authz>
case _ => NodeSeq.Empty
}
}
<lift:authz role="node_write">

</lift:authz>

val osTooltip: String = {
s"""
|<h4>Operating system details</h4>
|<div class='tooltip-content'>
| <ul>
| <li><b>Type:</b> ${xml.Utility.escape(inventory.node.main.osDetails.os.kernelName)}</li>
| <li><b>Name:</b> ${xml.Utility.escape(S.?("os.name." + inventory.node.main.osDetails.os.name))}</li>
| <li><b>Version:</b> ${xml.Utility.escape(inventory.node.main.osDetails.version.value)}</li>
| <li><b>Service pack:</b> ${xml.Utility.escape(inventory.node.main.osDetails.servicePack.getOrElse("None"))}</li>
| <li><b>Architecture:</b> ${xml.Utility.escape(inventory.node.archDescription.getOrElse("None"))}</li>
| <li><b>Kernel version:</b> ${xml.Utility.escape(inventory.node.main.osDetails.kernelVersion.value)}</li>
| </ul>
|</div>""".stripMargin.replaceAll("\n", " ")
}
("#nodeHeaderInfo" #> headerInfo &
"#nodeHeaderSubtitle" #> headerSubtitle &
"#nodeHeaderLogo" #> <div class={
"os-logo " ++ inventory.node.main.osDetails.os.name.toLowerCase()
} data-bs-toggle="tooltip" title={osTooltip}></div> &
"#nodeDeleteBtn" #> deleteBtn &
"#confirmNodeDeletion" #> SHtml.ajaxButton(
"Confirm",
() => { removeNode(inventory.node.main) },
("class", "btn btn-danger")
) &
"#node_groupTree" #>
<div id={groupTreeId}>
<ul>{DisplayNodeGroupTree.buildTreeKeepingGroupWithNode(groupLib, node, None, None, Map(("info", _ => Noop)))}</ul>
</div> &
Expand Down

0 comments on commit c4e796e

Please sign in to comment.