Permalink
Browse files

Added support for build tag add/removal.

Updated docs.
Added more unit tests.
  • Loading branch information...
ahonor committed Aug 11, 2011
1 parent 93fa217 commit 2b195623d998d37c4176b71b3e329f69133e330c
@@ -3,8 +3,8 @@
## What is Yana?
Yana is yet another node authority! What is a node authority?
-Yana is an information repository where you store information
-about hosts on your network.
+A node authority is an information repository where one
+stores information about hosts on the network.
Driving tools and other operations management software from
an up to date central repository about the nodes in your
@@ -25,7 +25,7 @@ behavior driven from views of it.
* `Tag`: A symbolic label that can be associated with a Node
* Data: text
- For attributes that are shared between nodes, two other concepts are useful:
+ For attributes that are shared between nodes, two other concepts can be useful:
* `ExternalAttribute`: Like an `Attribute` but shareable between Nodes.
* Data: name, value, description
@@ -59,8 +59,8 @@ behavior driven from views of it.
: Yana is not a CMDB. Yana is a repository of data about your Nodes.
Other kinds of resources exist your environment like services,
- network topology, packaged artefacts. One or more other tools
- might already be providing this info in your environment now.
+ network topology, packaged artifacts. Other tools
+ may already be providing this info in your environment now.
## Installation
@@ -1,4 +1,4 @@
-% NODE-V10(5) YANA User Manuals | Version 1.0
+% NODE-V10(5) YANA Reference
% Alex Honor
% August 03, 2011
@@ -10,15 +10,15 @@ The Node Model XML document declares a node model that can also be
uploaded to a Yana server. This document describes the
format and necessary elements.
-# Elements
+## node
The root (aka "top-level") element of the node file is called `node`.
*Attributes*
name
-: The node name. This is a logical identifier from the node. (required)
+: The node name. This is a logical identifier from the node and must be unique. (required)
description
@@ -32,6 +32,11 @@ osName
: The operating system name such as Linux or Mac OS X. (optional)
+id
+
+: The database identifier. If left out, a new object will be created.
+
+
*Nested Elements*
[attributes](#attributes)
@@ -46,7 +51,7 @@ osName
: Collection of external defined attributes
-## attributes
+### attributes
Defines a collection of attribute elements for the node.
@@ -56,7 +61,7 @@ Defines a collection of attribute elements for the node.
: Further user defined attribute.
-## attribute
+### attribute
A single metadata attribute.
@@ -70,7 +75,7 @@ value
: The value of the attribute
-## tags
+### tags
Collection of symbolic names for the node
@@ -80,15 +85,15 @@ Collection of symbolic names for the node
: Single tag definition
-## tag
+### tag
A single tag definition
*Nexted element*
## name
-: The name
+: The name. Must be unqiue.
@@ -1,4 +1,5 @@
+
class UrlMappings {
static mappings = {
@@ -12,6 +13,7 @@ class UrlMappings {
"/login/$action?"(controller: "login")
"/logout/$action?"(controller: "logout")
+ // Nodes
"/api/nodes"(controller: "nodeRest", parseRequest:true) {
action = [ GET: "list" , POST: "add" ]
}
@@ -20,6 +22,16 @@ class UrlMappings {
action = [ GET: "show" , POST: "save" , DELETE: "delete" ]
}
+ "/api/nodes/$nodeId/tags"(controller: "nodeRest", parseRequest:true) {
+ action = [ GET: "listTags" ]
+ }
+
+ // Apply and list tags on sets of Nodes
+ "/api/nodes/tags/$name"(controller: "tagRest", parseRequest:true) {
+ action = [ GET: "show", POST: "save", DELETE: "remove" ]
+ }
+
+ // Attributes
"/api/attributes"(controller: "attributesRest") {
action = [ GET: "list" ]
}
@@ -203,5 +203,26 @@ class NodeRestController {
}
}
+ //
+ // List the tags. Url: GET /api/nodes/{id}/tags
+ //
+ def listTags = {
+ Node obj = Node.get(params.id)
+ if (obj) {
+ def writer = new StringWriter()
+ def xml = new MarkupBuilder(writer)
+ def list = obj.tags
+ xml.tags() {
+ list.each{ Tag tagInstance->
+ tag(id:tagInstance.id) {
+ name(tagInstance.name)
+ }
+ }
+ }
+ render writer.toString()
+ } else {
+ response.sendError(404)
+ }
+ }
}
@@ -0,0 +1,118 @@
+package yana.node
+
+import groovy.xml.MarkupBuilder
+
+import grails.converters.XML
+import grails.converters.JSON
+
+import yana.node.Node
+
+class TagRestController {
+
+
+ //
+ // Get the tag. Url: GET /api/nodes/tags/$name
+ //
+ def show = {
+ println "DEBUG: inside show()..."
+ def nodes = []
+ if (params?.nodeName) {
+ println "DEBUG: params.nodeName="+params.nodeName
+ nodes = Node.findAllByLikeNameAndTagsByName(params.nodeName,
+ params.name)
+ } else {
+ nodes = Node.findAllTagsByName(params.name)
+ }
+
+ if (nodes.size() >0) {
+ println "DEBUG: there are some nodes"
+ render renderTag(params.name,nodes)
+ } else {
+ println "DEBUG: no nodes were found matching the tag"
+ response.sendError(404)
+ }
+ }
+
+ //
+ // Helper method to generate tag xml
+ //
+ static def renderTag(tagName, nodeList) {
+ def writer = new StringWriter()
+ def xml = new MarkupBuilder(writer)
+ xml.tag() {
+ name "${tagName}"
+ nodes(count: nodeList.size()) {
+ nodeList.each{ Node nodeInstance->
+ node(id: nodeInstance.id) {
+ name(nodeInstance.name)
+ }
+ }
+ }
+ }
+ writer.toString()
+ }
+
+ //
+ // Save a tag to matching Nodes
+ //
+ def save = {
+ println "DEBUG: inside save..."
+ println "DEBUG: looking for nodes like: "+params?.nodeName
+ def nodes = Node.findAllByNameLike(params?.nodeName)
+ if (nodes.size()>0) {
+ println "DEBUG: number nodes found: "+nodes.size()
+ nodes.each{Node nodeInstance->
+ println "DEBUG: addToTags for node: " +nodeInstance
+ nodeInstance.addToTags(new Tag(name: params.name))
+ nodeInstance.save()
+ }
+ render renderTag(params.name,nodes)
+ } else {
+ println "DEBUG: no nodes were found matching: ${params.nodeName}"
+ response.sendError(404)
+ }
+
+ }
+
+ //
+ // Remove a tag from matching Nodes
+ //
+ def remove = {
+ println "DEBUG: inside remove..."
+ println "DEBUG: looking for nodes like: "+params?.nodeName
+ // validate the parameter, nodeName
+ if (params?.nodeName) {
+
+ }
+ def foundNodes = Node.findAllByNameLikeAndTagsByName(params?.nodeName,
+ params.name)
+ println "DEBUG: foundNodes.size()=" + foundNodes.size()
+
+ def writer = new StringWriter()
+ def xml = new MarkupBuilder(writer)
+
+ xml.tag() {
+ name "${params.name}"
+ nodes(count: foundNodes.size()) {
+ foundNodes.each{Node nodeInstance->
+ def tags = nodeInstance.findTagByName(params.name)
+ tags.each {Tag tagInstance->
+ println "DEBUG: removing Tag: ${tagInstance} from node: " +nodeInstance+"..."
+ try {
+ nodeInstance.removeFromTags(tagInstance)
+ nodeInstance.save()
+ println "DEBUG: Tag removed from node: " + nodeInstance
+ node(id: nodeInstance.id) {
+ name "${nodeInstance.name}"
+ }
+ } catch (Exception e) {
+ println "DEBUB: Caught an error removing tag: "+ e
+ println "DEBUG: e instanceof "+ e.getClass().getName()
+ }
+ }
+ }
+ }
+ }
+ render writer.toString()
+ }
+}
@@ -57,12 +57,12 @@ class Node {
if (map.tags.tag instanceof List) {
map.tags.tag.each {
def tagInstance = Tag.fromMap(it)
- tagInstance.save(flush:true)
+ tagInstance.save(flush:false)
nodeInstance.addToTags(tagInstance)
}
} else if (map.tags.tag instanceof Map) {
def tagInstance = Tag.fromMap(map.tags.tag)
- tagInstance.save(flush:true)
+ tagInstance.save(flush:false)
nodeInstance.addToTags(tagInstance)
}
}
@@ -73,12 +73,12 @@ class Node {
if (map.attributes.attribute instanceof List) {
map.attributes.attribute.each {
def attrInstance = Attribute.fromMap(it)
- attrInstance.save(flush:true)
+ attrInstance.save(flush:false)
nodeInstance.addToAttributes(attrInstance)
}
} else if (map.attributes.attribute instanceof Map) {
def attrInstance = Attribute.fromMap(map.attributes.attribute)
- attrInstance.save(flush:true)
+ attrInstance.save(flush:false)
nodeInstance.addToAttributes(attrInstance)
}
@@ -90,12 +90,12 @@ class Node {
if (map.externalAttributes.attributes instanceof List) {
map.externalAttributes.attributes.each {
def attrsInstance = Attributes.fromMap(it)
- attrsInstance.save(flush:true)
+ attrsInstance.save(flush:false)
nodeInstance.addToExternalAttributes(attrsInstance)
}
} else if (map.externalAttributes.attributes instanceof Map) {
def attrsInstance = Attributes.fromMap(map.externalAttributes.attributes)
- attrsInstance.save(flush:true)
+ attrsInstance.save(flush:false)
nodeInstance.addToExternalAttributes(attrsInstance)
}
}
@@ -109,7 +109,38 @@ class Node {
this.tags.each {tag->
list << tag.name
}
- return list.join(delimiter)
+ return list.sort().join(delimiter)
+ }
+
+ // A dynamic (like?) find method
+ static Set<Node> findAllTagsByName(String name) {
+ println "DEBUG: inside findAllTagsByName. name="+name
+ return Node.withCriteria {
+ tags {
+ eq('name',name)
+ }
+ }
+ }
+
+ // A dynamic (like?) find method
+ static Set<Node> findAllByNameLikeAndTagsByName(String nameLike, String tagName) {
+ println "DEBUG: inside findAllByNameLikeAndTagsByName. name="+tagName
+ return Node.withCriteria {
+ ilike('name',nameLike)
+ tags {
+ eq('name',tagName)
+ }
+ }
+ }
+
+ def Set<Tag> findTagByName(String tagName) {
+ println "DEBUG: inside findTagByName. name="+tagName
+ return Tag.withCriteria {
+ eq('name',tagName)
+ node() {
+ eq('id',this.id)
+ }
+ }
}
}
Oops, something went wrong.

0 comments on commit 2b19562

Please sign in to comment.