Guest User

Untitled

a guest
Mar 14th, 2018
201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.82 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2.  
  3. require 'rubygems'
  4. require 'thor'
  5. require 'chef'
  6. require 'chef/node'
  7. require 'chef/rest'
  8.  
  9. # Please see the readme for overview documentation.
  10. #
  11. class JsonPrinter
  12. attr_reader :buf, :indent
  13.  
  14. # ==== Arguments
  15. # obj<Object>::
  16. # The object to be rendered into JSON. This object and all of its
  17. # associated objects must be either nil, true, false, a String, a Symbol,
  18. # a Numeric, an Array, or a Hash.
  19. #
  20. # ==== Returns
  21. # <String>::
  22. # The pretty-printed JSON ecoding of the given <i>obj</i>. This string
  23. # can be parsed by any compliant JSON parser without modification.
  24. #
  25. # ==== Examples
  26. # See <tt>JsonPrinter</tt> docs.
  27. #
  28. def self.render(obj)
  29. new(obj).buf
  30. end
  31.  
  32.  
  33. private
  34.  
  35. # Execute the JSON rendering of <i>obj</i>, storing the result in the
  36. # <tt>buf</tt>.
  37. #
  38. def initialize(obj)
  39. @buf = ""
  40. @indent = ""
  41. render(obj)
  42. end
  43.  
  44. # Increase the indentation level.
  45. #
  46. def indent_out
  47. @indent << " "
  48. end
  49.  
  50. # Decrease the indendation level.
  51. #
  52. def indent_in
  53. @indent.slice!(-1, 1)
  54. end
  55.  
  56. # Append the given <i>str</i> to the <tt>buf</tt>.
  57. #
  58. def print(str)
  59. @buf << str
  60. end
  61.  
  62. # Recursive rendering method. Primitive values, like nil, true, false,
  63. # numbers, symbols, and strings are converted to JSON and appended to the
  64. # buffer. Enumerables are treated specially to generate pretty whitespace.
  65. #
  66. def render(obj)
  67. # We can't use a case statement here becuase "when Hash" doesn't work for
  68. # ActiveSupport::OrderedHash - respond_to?(:values) is a more reliable
  69. # indicator of hash-like behavior.
  70. if NilClass === obj
  71. print("null")
  72.  
  73. elsif TrueClass === obj
  74. print("true")
  75.  
  76. elsif FalseClass === obj
  77. print("false")
  78.  
  79. elsif String === obj
  80. print(escape_json_string(obj))
  81.  
  82. elsif Symbol === obj
  83. print("\"#{obj}\"")
  84.  
  85. elsif Numeric === obj
  86. print(obj.to_s)
  87.  
  88. elsif Time === obj
  89. print(obj.to_s)
  90.  
  91. elsif obj.respond_to?(:keys)
  92. print("{")
  93. indent_out
  94. last_key = obj.keys.last
  95. obj.each do |(key, val)|
  96. render(key)
  97. case val
  98. when Hash, Array
  99. indent_out
  100. print(":\n#{indent}")
  101. render(val)
  102. indent_in
  103. else
  104. print(": ")
  105. render(val)
  106. end
  107. print(",\n#{indent}") unless key == last_key
  108. end
  109. indent_in
  110. print("}")
  111.  
  112. elsif Array === obj
  113. print("[")
  114. indent_out
  115. last_index = obj.size - 1
  116. obj.each_with_index do |elem, index|
  117. render(elem)
  118. print(",\n#{indent}") unless index == last_index
  119. end
  120. indent_in
  121. print("]")
  122.  
  123. else
  124. raise "unrenderable object: #{obj.inspect}"
  125. end
  126. end
  127.  
  128. # Special JSON character escape cases.
  129. ESCAPED_CHARS = {
  130. "\010" => '\b',
  131. "\f" => '\f',
  132. "\n" => '\n',
  133. "\r" => '\r',
  134. "\t" => '\t',
  135. '"' => '\"',
  136. '\\' => '\\\\',
  137. '>' => '\u003E',
  138. '<' => '\u003C',
  139. '&' => '\u0026'}
  140.  
  141. # String#to_json extracted from ActiveSupport, using interpolation for speed.
  142. #
  143. def escape_json_string(str)
  144. "\"#{
  145. str.gsub(/[\010\f\n\r\t"\\><&]/) { |s| ESCAPED_CHARS[s] }.
  146. gsub(/([\xC0-\xDF][\x80-\xBF]|
  147. [\xE0-\xEF][\x80-\xBF]{2}|
  148. [\xF0-\xF7][\x80-\xBF]{3})+/nx) do |s|
  149. s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
  150. end
  151. }\""
  152. end
  153. end
  154.  
  155. Chef::Config.from_file("/etc/chef/server.rb")
  156. Chef::Log.level(:info)
  157.  
  158. API_USERNAME=ENV['CHEF_USERNAME']
  159. API_PASSWORD=ENV['CHEF_PASSWORD']
  160.  
  161. raise StandardError, "Please set CHEF_USERNAME and CHEF_PASSWORD" unless ENV['CHEF_USERNAME'] && ENV['CHEF_PASSWORD']
  162.  
  163. class Knife < Thor
  164.  
  165. desc "register", "Register an openid for an API user"
  166. method_options :username => :required, :password => :required
  167. def register
  168. @rest = Chef::REST.new(Chef::Config[:registration_url])
  169. @rest.register(options[:username], options[:password])
  170. end
  171.  
  172.  
  173. desc "add_recipe", "Add a recipe to a node"
  174. method_options :recipe => :required, :after => :string, :node => :required
  175. def add_recipe
  176. authenticate
  177. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  178. node.recipes << options[:recipe] if !node.recipes.include?(options[:recipe])
  179. @rest.put_rest("nodes/#{expand_node(options[:node])}", node)
  180. list_recipes
  181. end
  182.  
  183. desc "remove_recipe", "Remove a recipe from a node"
  184. method_options :recipe => :required, :node => :required
  185. def remove_recipe
  186. authenticate
  187. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  188. node.recipes.delete(options[:recipe]) if node.recipes.include?(options[:recipe])
  189. @rest.put_rest("nodes/#{expand_node(options[:node])}", node)
  190. list_recipes
  191. end
  192.  
  193. desc "show_node", "Display a node"
  194. method_options :node => :required
  195. def show_node
  196. authenticate
  197. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  198. puts JsonPrinter.render(node.attribute)
  199. end
  200.  
  201. desc "show_attr", "Display a node attribute"
  202. method_options :node => :required, :attr => :required
  203. def show_attr
  204. authenticate
  205. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  206. puts JsonPrinter.render(node.attribute[options[:attr]])
  207. end
  208.  
  209. desc "edit_attr", "Display a node attribute"
  210. method_options :node => :required, :attr => :required
  211. def edit_attr
  212. editor = ENV['EDITOR'] || "vi"
  213. puts "Authenticating..."
  214. authenticate
  215. puts "Fetching node data for #{expand_node(options[:node])}..."
  216. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  217. filename = "/tmp/.chef-#{node[:hostname]}"
  218. File.open(filename, "w") {|f| f.write(JsonPrinter.render(node.attribute[options[:attr]])) }
  219. system("#{editor} #{filename}") or raise StandardError, "Error communicating with #{editor}"
  220. node[options[:attr]] = JSON.parse(File.read(filename))
  221. puts "Storing node data for #{expand_node(options[:node])}..."
  222. begin
  223. retries = 5
  224. @rest.put_rest("nodes/#{expand_node(options[:node])}", node)
  225. rescue Net::HTTPFatalError
  226. retry if (retries -= 1) > 0
  227. end
  228. puts "Done."
  229. end
  230.  
  231. desc "list_recipes", "List a node's recipes"
  232. method_options :node => :required
  233. def list_recipes
  234. authenticate
  235. node = @rest.get_rest("nodes/#{expand_node(options[:node])}")
  236. puts node.recipes.inspect
  237. end
  238.  
  239. desc "list_nodes", "List all nodes"
  240. def list_nodes
  241. authenticate
  242. nodes = @rest.get_rest("nodes")
  243. nodes.collect! { |l| l =~ /^.+\/(.+)$/; $1.gsub("_",".") }
  244. puts nodes
  245. end
  246.  
  247. no_tasks{
  248. def authenticate
  249. @rest = Chef::REST.new(Chef::Config[:registration_url])
  250. @rest.authenticate(API_USERNAME, API_PASSWORD)
  251. end
  252. }
  253. end
  254.  
  255. def expand_node(name)
  256. name.chomp.gsub(".","_")
  257. #name + "_" + (ENV['CHEF_DOMAIN'] || `hostname -d`.chomp.gsub(".", "_"))
  258. end
  259.  
  260. Knife.start
Add Comment
Please, Sign In to add comment