Commit e17e20eee0cae1d2766afeb8953ad43c893ad4af

Authored by Alexandre Chapellon
1 parent e6a2bdcb60
Exists in master and in 1 other branch rheltarget

first stage playbook

Showing 52 changed files with 1254 additions and 0 deletions   Show diff stats
... ... @@ -0,0 +1,22 @@
  1 +#Alfresco benchmark playbook
  2 +
  3 +This ansible playbook allows one to easily deploy an Alfresco benchmark infrastructure on multiple servers.
  4 +
  5 +##Requirements
  6 +
  7 +The playbook has been written and tested with Ansible 2.2. Some additionnal modules have been added to the playbook:
  8 +
  9 + * ansible-xml by cmprescott (requires python-lxml on remote end)
  10 + * selenium
  11 +
  12 +At the moment the target hosts need to have direct acces to internet as we are using public repos.
  13 +
  14 +##How to use:
  15 +
  16 +Edit the _inventory_ file and add your hosts to the appropriate section. If you don't understand the roles of each server, you should probably start reading [Derek Huley's documentation](https://github.com/AlfrescoBenchmark/alfresco-benchmark/tree/master/docs) about the [Alfresco benchmark framework](https://github.com/AlfrescoBenchmark).
  17 +
  18 +##TODO
  19 +
  20 + * Make it possible to download software from the Ansible machine instead of target machine (in case target cannot access internet).
  21 + * Allow Oracle JDK usage
  22 + * RedHat like environments
... ...
... ... @@ -0,0 +1 @@
  1 +alfbmsrv
... ...
... ... @@ -0,0 +1,7 @@
  1 +---
  2 +- include: yml/all.yml
  3 +- include: yml/mongodb.yml
  4 +- include: yml/bm-server.yml
  5 +- include: yml/load-driver.yml
  6 +- include: yml/selenium-grid.yml
  7 +
... ...
... ... @@ -0,0 +1,48 @@
  1 +artifact_repo: https://nexus.alfresco.com/nexus/service/local/repositories/snapshots/content
  2 +
  3 +bm_server_path: /org/alfresco/alfresco-benchmark-server/2.2.0-SNAPSHOT/alfresco-benchmark-server-2.2.0-20170403.100924-4.war
  4 +bm_sample: /org/alfresco/alfresco-benchmark-sample/2.2.0-SNAPSHOT/alfresco-benchmark-sample-2.2.0-20170403.101041-4.war
  5 +tests_signup_path: /org/alfresco/alfresco-benchmark-tests-ent-signup/2.5-SNAPSHOT/alfresco-benchmark-tests-ent-signup-2.5-20170306.103110-4.war
  6 +tests_cmis_path: /org/alfresco/alfresco-benchmark-tests-cmis/1.7-SNAPSHOT/alfresco-benchmark-tests-cmis-1.7-20160818.133550-1.war
  7 +tests_dataload_path: /org/alfresco/alfresco-benchmark-tests-dataload/2.8-SNAPSHOT/alfresco-benchmark-tests-dataload-2.8-20170306.105256-3.war
  8 +tests_share_path: /org/alfresco/alfresco-benchmark-tests-share/5.2.0-SNAPSHOT/alfresco-benchmark-tests-share-5.2.0-20170306.102441-46.war
  9 +tests_workflow_path: /org/alfresco/alfresco-benchmark-tests-workflow/1.3-SNAPSHOT/alfresco-benchmark-tests-workflow-1.3-20170403.102028-12.war
  10 +tests_desktopsync_path: /org/alfresco/alfresco-benchmark-tests-desktop-sync/1.3-SNAPSHOT/alfresco-benchmark-tests-desktop-sync-1.3-20160615.142236-21.war
  11 +
  12 +# Use either Java 7 or 8
  13 +#java_ver: 8
  14 +
  15 +# Set java flavor (only openjdk for now)
  16 +#java_flav: openjdk
  17 +
  18 +# Use either Tomcat 7 or 8
  19 +#tomcat_ver: 8
  20 +
  21 +# Set port for Tomcat to listen on. Set "tomcat_port" in host_vars to specify different ports on each host
  22 +#tomcat_prt: 9080
  23 +
  24 +# Should we remove existing war files
  25 +#tomcat_cln: False
  26 +
  27 +# Selenium version MUST be X.Y.Z
  28 +#selenium_ver: 3.3.1
  29 +
  30 +# Specify a host to endorse the role of a Selenium Hub. defaults to the first bm-server (there should be only one)
  31 +#selenium_hub_port: 4444
  32 +# DO NOT EDIT FURTHER.
  33 +# Thereafter we only set defaults
  34 +
  35 +java_version: "{{ java_ver | default(8) }}"
  36 +java_flavor: "{{ java_flav | default('openjdk') }}"
  37 +tomcat_version: "{{ tomcat_ver | default(7) }}"
  38 +tomcat_clean: "{{ tomcat_cln | default(False) }}"
  39 +tomcat_port: "{{ tomcat_prt | default(8080) }}"
  40 +
  41 +selenium_version: "{{ selenium_ver | default('3.3.1') }}"
  42 +selenium_release: "{{ selenium_version | regex_replace('\\.\\d+$','') }}"
  43 +selenium_url: "http://selenium-release.storage.googleapis.com/{{ selenium_release }}/selenium-server-standalone-{{ selenium_version }}.jar"
  44 +
  45 +selenium_hub_portnumber: "{{ selenium_hub_port | default(4444) }}"
  46 +
  47 +mongodb_version: "{{ mongodb_ver | default('3.4') }}"
  48 +
... ...
... ... @@ -0,0 +1,12 @@
  1 +[bm-server]
  2 +alfbmsrv
  3 +
  4 +[load-driver]
  5 +alfbmsrv
  6 +
  7 +[selenium-hub]
  8 +alfbmsrv
  9 +
  10 +[mongodb]
  11 +alfbmsrv
  12 +
... ...
... ... @@ -0,0 +1,638 @@
  1 +#!/usr/bin/python
  2 +# -*- coding: utf-8 -*-
  3 +#
  4 +# xml - Manage bits and pieces of XML files
  5 +#
  6 +# Copyright 2014, Red Hat, Inc.
  7 +# Tim Bielawa <tbielawa@redhat.com>
  8 +# Magnus Hedemark <mhedemar@redhat.com>
  9 +#
  10 +# This software may be freely redistributed under the terms of the GNU
  11 +# general public license version 2.
  12 +#
  13 +# You should have received a copy of the GNU General Public License
  14 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  15 +
  16 +
  17 +DOCUMENTATION = '''
  18 +---
  19 +module: xml
  20 +short_description: Manage bits and pieces of XML files or strings
  21 +description:
  22 + - A CRUD-like interface to managing bits of XML files. You might also be interested in a brief tutorial, U(http://www.w3schools.com/xpath/). Note that module this does not handle complicated xpath expressions. So limit xpath selectors to simple expressions.
  23 +version_added: "1.0"
  24 +options:
  25 + path:
  26 + description:
  27 + - Path to the file to operate on. File must exist ahead of time.
  28 + required: true unless xmlstring is given
  29 + default: null
  30 + choices: []
  31 + aliases: ['dest', 'file']
  32 + xmlstring:
  33 + description:
  34 + - A string containing XML on which to operate.
  35 + required: true unless file is given
  36 + default: null
  37 + choices: []
  38 + xpath:
  39 + description:
  40 + - A valid XPath expression describing the item(s) you want to manipulate. Operates on the document root, C(/), by default.
  41 + required: false
  42 + default: /
  43 + choices: []
  44 + namespaces:
  45 + description:
  46 + - The namespace prefix:uri mapping for the XPath expression. Needs to be a *map*, not a list of items.
  47 + required: false
  48 + default: null
  49 + choices: []
  50 + ensure:
  51 + description:
  52 + - Set or remove an xpath selection (node(s), attribute(s))
  53 + required: false
  54 + default: present
  55 + choices:
  56 + - "absent"
  57 + - "present"
  58 + value:
  59 + description:
  60 + - Desired state of the selected attribute. Either a string, or to unset a value, the Python C(None) keyword (YAML Equivalent, C(null)).
  61 + required: false
  62 + default: Elements default to no value (but present). Attributes default to an empty string.
  63 + choices: []
  64 + add_children:
  65 + description:
  66 + - 'Add additional child-element(s) to a selected element. Child elements must be given in a list and each item may be either a string (ex: C(children=ansible) to add an empty C(<ansible/>) child element), or a hash where the key is an element name and the value is the element value.'
  67 + required: false
  68 + default: null
  69 + choices: []
  70 + set_children:
  71 + description:
  72 + - 'Set the the child-element(s) of a selected element. Removes any existing children. Child elements must be specified as in C(add_children).'
  73 + required: false
  74 + default: null
  75 + choices: []
  76 + count:
  77 + description:
  78 + - "Search for a given C(xpath) and provide the count of any matches"
  79 + required: false
  80 + default: null
  81 + choices: []
  82 + print_match:
  83 + description:
  84 + - "Search for a given C(xpath) and print out any matches"
  85 + required: false
  86 + default: null
  87 + choices: []
  88 + pretty_print:
  89 + description:
  90 + - "Pretty print output XML"
  91 + required: false
  92 + default: false
  93 + choices: []
  94 + content:
  95 + description:
  96 + - "Search for a given C(xpath) and get content"
  97 + required: false
  98 + default: false
  99 + choices:
  100 + - "attribute"
  101 + - "text"
  102 + input_type:
  103 + description:
  104 + - "Type of input for add_children and set_children"
  105 + required: false
  106 + default: "yaml"
  107 + choices:
  108 + - "yaml"
  109 + - "xml"
  110 +requirements:
  111 + - The remote end must have the Python C(lxml) library installed
  112 +author: Tim Bielawa, Magnus Hedemark
  113 +'''
  114 +
  115 +
  116 +from io import BytesIO
  117 +from lxml import etree
  118 +try:
  119 + import json
  120 +except:
  121 + import simplejson as json
  122 +import lxml
  123 +import sys, os, re, traceback
  124 +
  125 +def print_match(module, tree, xpath, namespaces):
  126 + match = tree.xpath(xpath, namespaces=namespaces)
  127 + match_xpaths = []
  128 + for m in match:
  129 + match_xpaths.append(tree.getpath(m))
  130 + match_str = json.dumps(match_xpaths)
  131 + msg = "selector '%s' match: %s" % (xpath, match_str)
  132 + finish(module, tree, xpath, namespaces, changed=False, msg=msg)
  133 +
  134 +def count(module, tree, xpath, namespaces):
  135 + """ Return the count of nodes matching the xpath """
  136 + hits = tree.xpath("count(/%s)" % xpath, namespaces=namespaces)
  137 + finish(module, tree, xpath, namespaces, changed=False, msg=int(hits), hitcount=int(hits))
  138 +
  139 +def is_node(tree, xpath, namespaces):
  140 + """ Test if a given xpath matches anything and if that match is a node.
  141 +
  142 + For now we just assume you're only searching for one specific thing."""
  143 + if xpath_matches(tree, xpath, namespaces):
  144 + # OK, it found something
  145 + match = tree.xpath(xpath, namespaces=namespaces)
  146 + if type(match[0]) == lxml.etree._Element:
  147 + return True
  148 +
  149 + return False
  150 +
  151 +def is_attribute(tree, xpath, namespaces):
  152 + """ Test if a given xpath matches and that match is an attribute
  153 +
  154 +An xpath attribute search will only match one item"""
  155 + if xpath_matches(tree, xpath, namespaces):
  156 + match = tree.xpath(xpath, namespaces=namespaces)
  157 + if type(match[0]) == lxml.etree._ElementStringResult:
  158 + return True
  159 + return False
  160 +
  161 +def xpath_matches(tree, xpath, namespaces):
  162 + """ Test if a node exists """
  163 + if tree.xpath(xpath, namespaces=namespaces):
  164 + return True
  165 + else:
  166 + return False
  167 +
  168 +def delete_xpath_target(module, tree, xpath, namespaces):
  169 + """ Delete an attribute or element from a tree """
  170 + try:
  171 + for result in tree.xpath(xpath, namespaces=namespaces):
  172 + if not module.check_mode:
  173 + # Get the xpath for this result
  174 + if is_attribute(tree, xpath, namespaces):
  175 + # Delete an attribute
  176 + parent = result.getparent()
  177 + # Pop this attribute match out of the parent
  178 + # node's 'attrib' dict by using this match's
  179 + # 'attrname' attribute for the key
  180 + parent.attrib.pop(result.attrname)
  181 + elif is_node(tree, xpath, namespaces):
  182 + # Delete an element
  183 + result.getparent().remove(result)
  184 + except Exception, e:
  185 + abort(module, "Couldn't delete xpath target: %s (%s)" % (xpath, str(e)))
  186 + else:
  187 + finish(module, tree, xpath, namespaces, changed=True)
  188 +
  189 +def set_target_children_inner(module, tree, xpath, namespaces, children, type):
  190 + matches = tree.xpath(xpath, namespaces=namespaces)
  191 +
  192 + # Create a list of our new children
  193 + children = children_to_nodes(module, children, type)
  194 + children_as_string = [lxml.etree.tostring(c) for c in children]
  195 +
  196 + changed = False
  197 +
  198 + def replace_children_of(match):
  199 + if not module.check_mode:
  200 + for element in match.getchildren():
  201 + match.remove(element)
  202 + match.extend(children)
  203 +
  204 + # xpaths always return matches as a list, so....
  205 + for match in matches:
  206 + # Check if elements differ
  207 + if len(match.getchildren()) == len(children):
  208 + for idx, element in enumerate(match.getchildren()):
  209 + if lxml.etree.tostring(element) != children_as_string[idx]:
  210 + replace_children_of(match)
  211 + changed = True
  212 + break
  213 + else:
  214 + replace_children_of(match)
  215 + changed = True
  216 +
  217 + return changed
  218 +
  219 +def set_target_children(module, tree, xpath, namespaces, children, type):
  220 + changed = set_target_children_inner(module, tree, xpath, namespaces, children, type)
  221 + # Write it out
  222 + finish(module, tree, xpath, namespaces, changed=changed)
  223 +
  224 +def add_target_children(module, tree, xpath, namespaces, children, type):
  225 + if is_node(tree, xpath, namespaces):
  226 + new_kids = children_to_nodes(module, children, type)
  227 + for node in tree.xpath(xpath, namespaces=namespaces):
  228 + if not module.check_mode: node.extend(new_kids)
  229 + finish(module, tree, xpath, namespaces, changed=True)
  230 + else:
  231 + finish(module, tree, xpath, namespaces)
  232 +
  233 +_ident = "[a-zA-Z-][a-zA-Z0-9_-]*"
  234 +_nsIdent = _ident +"|"+_ident+":"+_ident
  235 +_xpstr = "('(?:.*)'|\"(?:.*)\")" # Note: we can't reasonably support the 'if you need to put both ' and " in a string, concatenate
  236 + # strings wrapped by the other delimiter' XPath trick, especially as simple XPath.
  237 +
  238 +_re_splitSimpleLast = re.compile("^(.*)/("+_nsIdent+")$")
  239 +_re_splitSimpleLastEqValue = re.compile("^(.*)/("+_nsIdent+")/text\\(\\)="+_xpstr+"$")
  240 +_re_splitSimpleAttrLast = re.compile("^(.*)/(@(?:"+_nsIdent+"))$")
  241 +_re_splitSimpleAttrLastEqValue = re.compile("^(.*)/(@(?:"+_nsIdent+"))="+_xpstr+"$")
  242 +
  243 +_re_splitSubLast = re.compile("^(.*)/("+_nsIdent+")\\[(.*)\\]$")
  244 +
  245 +_re_splitOnlyEqValue = re.compile("^(.*)/text\\(\\)="+_xpstr+"$")
  246 +
  247 +def _extract_xpstr(g):
  248 + return g[1:-1]
  249 +
  250 +def split_xpath_last(xpath):
  251 + """split an XPath of the form /foo/bar/baz into /foo/bar and baz"""
  252 + xpath = xpath.strip()
  253 + m = _re_splitSimpleLast.match(xpath)
  254 + if m:
  255 + return (m.group(1), [(m.group(2), None)]) # requesting an element to exist
  256 + m = _re_splitSimpleLastEqValue.match(xpath)
  257 + if m:
  258 + return (m.group(1), [(m.group(2), _extract_xpstr(m.group(3)))]) # requesting an element to exist with an inner text
  259 +
  260 + m = _re_splitSimpleAttrLast.match(xpath)
  261 + if m:
  262 + return (m.group(1), [(m.group(2), None)]) # requesting an attribute to exist
  263 + m = _re_splitSimpleAttrLastEqValue.match(xpath)
  264 + if m:
  265 + return (m.group(1), [(m.group(2), _extract_xpstr(m.group(3)))]) # requesting an attribute to exist with a value
  266 +
  267 + m = _re_splitSubLast.match(xpath)
  268 + if m:
  269 + content = map(lambda x: x.strip(), m.group(3).split(" and "))
  270 +
  271 + return (m.group(1), [('/'+m.group(2), content )] )
  272 +
  273 + m = _re_splitOnlyEqValue.match(xpath)
  274 + if m:
  275 + return (m.group(1), [("", _extract_xpstr(m.group(2)))]) # requesting a change of inner text
  276 + return (xpath, [])
  277 +
  278 +def nsnameToClark(name, namespaces):
  279 + if ":" in name:
  280 + (nsname, rawname) = name.split(":")
  281 + return "{{{0}}}{1}".format(namespaces[nsname], rawname)
  282 + else:
  283 + # no namespace name here
  284 + return name
  285 +
  286 +def check_or_make_target(module, tree, xpath, namespaces):
  287 + (inner_xpath, changes) = split_xpath_last(xpath)
  288 + if (inner_xpath == xpath) or (changes == None):
  289 + abort(module, "Can't process Xpath " + xpath + " in order to spawn nodes! tree is " + etree.tostring(tree, pretty_print=True))
  290 + return False
  291 +
  292 + changed = False
  293 +
  294 + if not is_node(tree, inner_xpath, namespaces):
  295 + changed = check_or_make_target(module, tree, inner_xpath, namespaces)
  296 +
  297 + if is_node(tree, inner_xpath, namespaces) and changes: # we test again after calling check_or_make_target
  298 + for (eoa, eoa_value) in changes:
  299 + if eoa and eoa[0] != '@' and eoa[0] != '/':
  300 + # implicitly creating an element
  301 + new_kids = children_to_nodes(module, [nsnameToClark(eoa, namespaces)], "yaml")
  302 + if eoa_value:
  303 + for nk in new_kids:
  304 + nk.text = eoa_value
  305 +
  306 + for node in tree.xpath(inner_xpath, namespaces=namespaces):
  307 + if not module.check_mode: node.extend(new_kids)
  308 + changed = True
  309 + #abort(module, "now tree=" + etree.tostring(tree, pretty_print=True))
  310 + elif eoa and eoa[0] == '/':
  311 + element = eoa[1:]
  312 + new_kids = children_to_nodes(module, [nsnameToClark(element, namespaces)], "yaml")
  313 +
  314 + for node in tree.xpath(inner_xpath, namespaces=namespaces):
  315 + if not module.check_mode: node.extend(new_kids)
  316 + for nk in new_kids:
  317 + for subexpr in eoa_value:
  318 + #abort(module, "element="+element+" subexpr="+str(subexpr)+" node="+etree.tostring(node, pretty_print=True)+
  319 + # " now tree=" + etree.tostring(tree, pretty_print=True))
  320 + check_or_make_target(module, nk, "./"+subexpr, namespaces)
  321 + changed = True
  322 +
  323 + #abort(module, "now tree=" + etree.tostring(tree, pretty_print=True))
  324 + elif eoa == "":
  325 + for node in tree.xpath(inner_xpath, namespaces=namespaces):
  326 + if (node.text != eoa_value):
  327 + node.text = eoa_value
  328 + changed = True
  329 +
  330 + elif eoa and eoa[0] == '@':
  331 + attribute = nsnameToClark(eoa[1:], namespaces)
  332 +
  333 + for element in tree.xpath(inner_xpath, namespaces=namespaces):
  334 + changing = (not element.attrib.has_key(attribute)) or (element.attrib.get(attribute) != eoa_value)
  335 +
  336 + if not module.check_mode and changing:
  337 + changed = changed or changing
  338 + if eoa_value is None:
  339 + value = ""
  340 + else:
  341 + value = eoa_value
  342 + element.attrib[attribute] = value
  343 +
  344 + #abort(module, "arf "+xpath+" changing="+str(changing)+" as curval="+str(element.get(attribute))+" changed tree=" + etree.tostring(tree, pretty_print=True))
  345 +
  346 +
  347 + else:
  348 + abort(module, "unknown tree transformation=" + etree.tostring(tree, pretty_print=True))
  349 +
  350 + return changed
  351 +
  352 +
  353 +def ensure_xpath_exists(module, tree, xpath, namespaces):
  354 + changed = False
  355 +
  356 + if not is_node(tree, xpath, namespaces):
  357 + changed = check_or_make_target(module, tree, xpath, namespaces)
  358 +
  359 + finish(module, tree, xpath, namespaces, changed)
  360 +
  361 +def set_target_inner(module, tree, xpath, namespaces, attribute, value):
  362 + changed = False
  363 +
  364 + try:
  365 + if not is_node(tree, xpath, namespaces):
  366 + changed = check_or_make_target(module, tree, xpath, namespaces)
  367 + except Exception, e:
  368 + abort(module, "Xpath " + xpath + " causes a failure: "+str(e)+ "\n" + traceback.format_exc(e)+"\n -- tree is " + etree.tostring(tree, pretty_print=True))
  369 +
  370 + if not is_node(tree, xpath, namespaces):
  371 + abort(module, "Xpath " + xpath + " does not reference a node! tree is " + etree.tostring(tree, pretty_print=True))
  372 +
  373 + for element in tree.xpath(xpath, namespaces=namespaces):
  374 + if not attribute:
  375 + changed = changed or (element.text != value)
  376 + if not module.check_mode and (element.text != value): element.text = value
  377 + else:
  378 + changed = changed or (element.get(attribute) != value)
  379 + if ":" in attribute:
  380 + attr_ns, attr_name = attribute.split(":")
  381 + attribute = "{{{0}}}{1}".format(namespaces[attr_ns], attr_name)
  382 + if not module.check_mode and (element.get(attribute) != value): element.set(attribute, value)
  383 +
  384 + return changed
  385 +
  386 +def set_target(module, tree, xpath, namespaces, attribute, value):
  387 + changed = set_target_inner(module, tree, xpath, namespaces, attribute, value)
  388 + finish(module, tree, xpath, namespaces, changed)
  389 +
  390 +def pretty(module, tree):
  391 + xml_string = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
  392 + changed = False
  393 +
  394 + if module.params['path']:
  395 + xml_file = os.path.expanduser(module.params['path'])
  396 +
  397 + xml_content = open(xml_file)
  398 + try:
  399 + if xml_string != xml_content.read():
  400 + changed = True
  401 + tree.write(xml_file, xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
  402 + finally:
  403 + xml_content.close()
  404 +
  405 + module.exit_json(changed=changed)
  406 +
  407 + if module.params['xmlstring']:
  408 + if xml_string != module.params['xmlstring']:
  409 + changed = True
  410 +
  411 + module.exit_json(changed=changed, xmlstring=xml_string)
  412 +
  413 +def get_element_text(module, tree, xpath, namespaces):
  414 + if not is_node(tree, xpath, namespaces):
  415 + abort(module, "Xpath " + xpath + " does not reference a node!")
  416 +
  417 + elements = []
  418 + for element in tree.xpath(xpath, namespaces=namespaces):
  419 + elements.append({element.tag: element.text})
  420 +
  421 + finish(module, tree, xpath, namespaces, changed=False, msg=len(elements), hitcount=len(elements), matches=elements)
  422 +
  423 +def get_element_attr(module, tree, xpath, namespaces):
  424 + if not is_node(tree, xpath, namespaces):
  425 + abort(module, "Xpath " + xpath + " does not reference a node!")
  426 +
  427 + elements = []
  428 + for element in tree.xpath(xpath, namespaces=namespaces):
  429 + child = {}
  430 + for key in element.keys():
  431 + value = element.get(key)
  432 + child.update({key: value})
  433 + elements.append({element.tag: child})
  434 +
  435 + finish(module, tree, xpath, namespaces, changed=False, msg=len(elements), hitcount=len(elements), matches=elements)
  436 +
  437 +def child_to_element(module, child, in_type):
  438 + if in_type == 'xml':
  439 + infile = BytesIO(child.encode('utf-8'))
  440 +
  441 + try:
  442 + parser = etree.XMLParser()
  443 + node = etree.parse(infile, parser)
  444 + return node.getroot()
  445 + except etree.XMLSyntaxError, e:
  446 + module.fail_json(
  447 + msg="Error while parsing child element: %s" %
  448 + str(e))
  449 + elif in_type == 'yaml':
  450 + ch_type = type(child)
  451 + if ch_type == str or ch_type == unicode:
  452 + return etree.Element(child)
  453 + elif ch_type == dict:
  454 + if len(child) > 1:
  455 + abort(module, "Can only create children from hashes with one key")
  456 +
  457 + (key, value) = child.items()[0]
  458 + if type(value) == dict:
  459 + children = value.pop('_', None)
  460 +
  461 + node = etree.Element(key, value)
  462 +
  463 + if children is not None:
  464 + if type(children) != list:
  465 + abort(module, "Invalid children type: %s, must be list." % str(type(children)))
  466 +
  467 + subnodes = children_to_nodes(module, children)
  468 + node.extend(subnodes)
  469 + else:
  470 + node = etree.Element(key)
  471 + node.text = value
  472 + return node
  473 + else:
  474 + abort(module, "Invalid child type: %s. Children must be either strings or hashes." % str(ch_type))
  475 + else:
  476 + abort(module, "Invalid child input type: %s. Type must be either xml or yaml." % in_type)
  477 +
  478 +def children_to_nodes(module=None, children=[], type='yaml'):
  479 + """turn a str/hash/list of str&hash into a list of elements"""
  480 + return [child_to_element(module, child, type) for child in children]
  481 +
  482 +def abort(m, msg):
  483 + m.fail_json(msg=msg)
  484 +
  485 +def finish(m, tree, xpath, namespaces, changed=False, msg="", hitcount=0, matches=[]):
  486 + if not changed:
  487 + m.exit_json(changed=changed,actions={"xpath": xpath, "namespaces": namespaces, "ensure": m.params['ensure']}, msg=msg, count=hitcount, matches=matches)
  488 +
  489 + if m.params['path']:
  490 + xml_file = os.path.expanduser(m.params['path'])
  491 + tree.write(xml_file, xml_declaration=True, encoding='UTF-8', pretty_print=m.params['pretty_print'])
  492 + m.exit_json(changed=changed,actions={"xpath": xpath, "namespaces": namespaces, "ensure": m.params['ensure']}, msg=msg, count=hitcount, matches=matches)
  493 +
  494 + if m.params['xmlstring']:
  495 + xml_string = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', pretty_print=m.params['pretty_print'])
  496 + m.exit_json(changed=changed,actions={"xpath": xpath, "namespaces": namespaces, "ensure": m.params['ensure']}, msg=msg, count=hitcount, matches=matches, xmlstring=xml_string)
  497 +
  498 +def decode(value):
  499 + # Convert value to unicode to use with lxml
  500 + if not value or isinstance(value, unicode):
  501 + return value
  502 + elif isinstance(value, str):
  503 + return value.decode('utf-8')
  504 + elif isinstance(value, list):
  505 + return [decode(v) for v in value]
  506 + elif isinstance(value, dict):
  507 + return dict((key, decode(val)) for key, val in value.iteritems())
  508 + else:
  509 + raise AttributeError('Undecodable value: type=%s, value=%s' %
  510 + (type(value), value))
  511 +
  512 +def main():
  513 + module = AnsibleModule(
  514 + argument_spec=dict(
  515 + path=dict(required=False, default='', aliases=['dest', 'file']),
  516 + xmlstring=dict(required=False, default=''),
  517 + xpath=dict(required=False, default='/'),
  518 + namespaces=dict(required=False, default={}, type='dict'),
  519 + ensure=dict(required=False, default='present', choices=['absent', 'present']),
  520 + value=dict(required=False, default=None),
  521 + attribute=dict(required=False, default=None),
  522 + add_children=dict(required=False, default=None, type='list'),
  523 + set_children=dict(required=False, default=None, type='list'),
  524 + count=dict(required=False, default=None, type='bool'),
  525 + print_match=dict(required=False, default=None, type='bool'),
  526 + pretty_print=dict(required=False, default=False, type='bool'),
  527 + content=dict(required=False, default=None, choices=['attribute', 'text']),
  528 + input_type=dict(required=False, default='yaml', choices=['yaml', 'xml'])
  529 + ),
  530 + supports_check_mode=True,
  531 + mutually_exclusive = [
  532 + ['value','set_children'],
  533 + ['value','add_children'],
  534 + ['set_children', 'add_children'],
  535 + ['path', 'xmlstring'],
  536 + ['content','set_children'],
  537 + ['content','add_children'],
  538 + ['content','value'],
  539 + ]
  540 + )
  541 +
  542 + xml_file = os.path.expanduser(module.params['path'])
  543 + xml_string = module.params['xmlstring']
  544 + xpath = module.params['xpath']
  545 + namespaces = module.params['namespaces']
  546 + ensure = module.params['ensure']
  547 + value = decode(module.params['value'])
  548 + attribute = module.params['attribute']
  549 + set_children = decode(module.params['set_children'])
  550 + add_children = decode(module.params['add_children'])
  551 + pretty_print = module.params['pretty_print']
  552 + content = module.params['content']
  553 + input_type = module.params['input_type']
  554 +
  555 + ##################################################################
  556 + # Check if the file exists
  557 + # No: abort
  558 + if xml_string:
  559 + infile = BytesIO(xml_string.encode('utf-8'))
  560 + elif os.path.isfile(xml_file):
  561 + infile = file(xml_file, 'r')
  562 + else:
  563 + module.fail_json(
  564 + msg="The target XML source does not exist: %s" %
  565 + xml_file)
  566 +
  567 + # Try to parse in the target XML file
  568 + try:
  569 + parser = etree.XMLParser(remove_blank_text=pretty_print)
  570 + x = etree.parse(infile, parser)
  571 + except etree.XMLSyntaxError, e:
  572 + module.fail_json(
  573 + msg="Error while parsing file: %s" %
  574 + str(e))
  575 +
  576 + if module.params['print_match']:
  577 + print_match(module, x, xpath, namespaces)
  578 +
  579 + if module.params['count']:
  580 + count(module, x, xpath, namespaces)
  581 +
  582 + if content == 'attribute':
  583 + get_element_attr(module, x, xpath, namespaces)
  584 + elif module.params['content'] == 'text':
  585 + get_element_text(module, x, xpath, namespaces)
  586 +
  587 + # module.fail_json(msg="OK. Well, etree parsed the xml file...")
  588 +
  589 + # module.exit_json(what_did={"foo": "bar"}, changed=True)
  590 +
  591 + ##################################################################
  592 + # File exists:
  593 + # Ensure:
  594 + if ensure == 'absent':
  595 + # - absent: delete xpath target
  596 + delete_xpath_target(module, x, xpath, namespaces)
  597 + # Exit
  598 + # - present: carry on
  599 +
  600 + ##################################################################
  601 + # children && value both set?: should have already aborted by now
  602 + ##################################################################
  603 +
  604 + ##################################################################
  605 + # add_children && set_children both set?: should have already aborted by now
  606 + ##################################################################
  607 +
  608 + ##################################################################
  609 + # set_children set?
  610 + # Yes: Set children of target
  611 + if module.params['set_children']:
  612 + set_target_children(module, x, xpath, namespaces, set_children, input_type)
  613 +
  614 + ##################################################################
  615 + # add_children set?
  616 + # Yes: Add children to target
  617 + if module.params['add_children']:
  618 + add_target_children(module, x, xpath, namespaces, add_children, input_type)
  619 +
  620 + # No?: Carry on
  621 +
  622 + ##################################################################
  623 + # Is the xpath target an attribute selector?
  624 + # Yes: Set the attribute, exit
  625 + if module.params['value'] is not None:
  626 + set_target(module, x, xpath, namespaces, attribute, value)
  627 +
  628 + ##################################################################
  629 + # Format the xml only?
  630 + if module.params['pretty_print']:
  631 + pretty(module, x)
  632 +
  633 + ensure_xpath_exists(module, x, xpath, namespaces)
  634 + #abort(module, "don't know what to do")
  635 +
  636 +######################################################################
  637 +from ansible.module_utils.basic import *
  638 +main()
... ...
... ... @@ -0,0 +1,6 @@
  1 +---
  2 +- hosts: all
  3 + vars_files:
  4 + - ../config.yml
  5 + roles:
  6 + - common
... ...
yml/bm-server.yml
... ... @@ -0,0 +1,8 @@
  1 +---
  2 +- hosts: bm-server
  3 + vars_files:
  4 + - ../config.yml
  5 + roles:
  6 + - tomcat
  7 + - bm-server
  8 +
... ...
yml/host_vars/alfmbm
... ... @@ -0,0 +1,2 @@
  1 +tomcat_port: 9080
  2 +
... ...
yml/load-driver.yml
... ... @@ -0,0 +1,8 @@
  1 +---
  2 +- hosts: load-driver
  3 + vars_files:
  4 + - ../config.yml
  5 + roles:
  6 + - { role: tomcat, when: "inventory_hostname not in groups['bm-server']" }
  7 + - load-driver
  8 +
... ...
... ... @@ -0,0 +1,7 @@
  1 +---
  2 +- hosts: mongodb
  3 + vars_files:
  4 + - ../config.yml
  5 + roles:
  6 + - mongodb
  7 +
... ...
yml/roles/bm-server/tasks/main.yml
... ... @@ -0,0 +1,16 @@
  1 +---
  2 +- name: Remove all Tomcat webapps
  3 + file: name="{{ catalina_home }}/webapps/{{ item }}" state=absent
  4 + with_items:
  5 + - alfresco-benchmark-server
  6 + - alfresco-benchmark-server.war
  7 + when: tomcat_clean == True
  8 +
  9 +- name: Get Alfresco benchmark server artifact
  10 + get_url:
  11 + url: "{{ artifact_repo }}/{{ bm_server_path }}"
  12 + dest: "{{ catalina_home }}/webapps/alfresco-benchmark-server.war"
  13 + owner: "{{ tomcat_user }}"
  14 + group: "{{ tomcat_group }}"
  15 + mode: 0644
  16 +
... ...
yml/roles/common/tasks/dist.yml
... ... @@ -0,0 +1,23 @@
  1 +---
  2 +- name: Load OS specific vars
  3 + include_vars: "{{ item }}"
  4 + with_first_found:
  5 + - "{{ ansible_os_family|lower }}.yml"
  6 + ignore_errors: true
  7 +
  8 +- name: Load distribution vars
  9 + include_vars: "{{ item }}"
  10 + with_first_found:
  11 + - "../vars/{{ ansible_distribution|lower }}.yml"
  12 + ignore_errors: true
  13 +
  14 +- name: Load major version vars
  15 + include_vars: "{{ item }}"
  16 + with_first_found:
  17 + - "../vars/{{ ansible_distribution|lower }}_{{ ansible_distribution_major_version|lower }}.yml"
  18 + ignore_errors: true
  19 +
  20 +- name: WARNING
  21 + debug: msg="your Linux distribution is quite outdated! Software version specified in your configuration may not be the one deployed"
  22 + when: warn_old
  23 +
... ...
yml/roles/common/tasks/main.yml
... ... @@ -0,0 +1,4 @@
  1 +---
  2 +- include: dist.yml
  3 +- include: packages.yml
  4 +
... ...
yml/roles/common/tasks/packages.yml
... ... @@ -0,0 +1,16 @@
  1 +---
  2 +- name: Setup required repositories (Debian-like systems)
  3 + apt_repository: repo="{{ item }}" filename=alfbm
  4 + when: ansible_pkg_mgr == "apt" and repo_list is defined
  5 + with_items: "{{ repo_list }}"
  6 +
  7 +- name: Install common packages (RedHat like systems)
  8 + yum: pkg={{ item }}
  9 + when: ansible_pkg_mgr == "yum"
  10 + with_items: "{{ common_pkg }}"
  11 +
  12 +- name: Install common packages (Debian like systems)
  13 + apt: name={{ item }}
  14 + when: ansible_pkg_mgr == "apt"
  15 + with_items: "{{ common_pkg }}"
  16 +
... ...
yml/roles/common/vars/centos.yml
yml/roles/common/vars/debian.yml
... ... @@ -0,0 +1,3 @@
  1 +common_pkg:
  2 + - python-lxml
  3 +
... ...
yml/roles/common/vars/debian_7.yml
... ... @@ -0,0 +1,5 @@
  1 +repo_list:
  2 + - http://ftp.fr.debian.org/debian jessie-backport main
  3 +
  4 +warn_old: True
  5 +
... ...
yml/roles/common/vars/debian_8.yml
... ... @@ -0,0 +1,3 @@
  1 +repo_list:
  2 + - http://ftp.fr.debian.org/debian jessie-backport main
  3 +
... ...
yml/roles/common/vars/redhat.yml
... ... @@ -0,0 +1,3 @@
  1 +common_pkg:
  2 + - python-lxml
  3 +
... ...
yml/roles/common/vars/ubuntu.yml
yml/roles/common/vars/ubuntu_14.yml
... ... @@ -0,0 +1,5 @@
  1 +repo_list:
  2 + - ppa:openjdk-r/ppa
  3 +
  4 +warn_old: True
  5 +
... ...
yml/roles/load-driver/tasks/main.yml
... ... @@ -0,0 +1,33 @@
  1 +---
  2 +- name: Remove all Tomcat webapps
  3 + file: name="{{ catalina_home }}/webapps/{{ item }}" state=absent
  4 + with_items:
  5 + - alfresco-benchmark-tests-signup
  6 + - alfresco-benchmark-tests-signup.war
  7 + - alfresco-benchmark-tests-cmis
  8 + - alfresco-benchmark-tests-cmis.war
  9 + - alfresco-benchmark-tests-dataload
  10 + - alfresco-benchmark-tests-dataload.war
  11 + - alfresco-benchmark-tests-share
  12 + - alfresco-benchmark-tests-share.war
  13 + - alfresco-benchmark-tests-workflow
  14 + - alfresco-benchmark-tests-workflow.war
  15 + - alfresco-benchmark-tests-desktopsync
  16 + - alfresco-benchmark-tests-desktopsync.war
  17 + when: tomcat_clean == True
  18 +
  19 +- name: Get Alfresco benchmark tests artifacts
  20 + get_url:
  21 + url: "{{ artifact_repo }}/{{ item.artifact }}"
  22 + dest: "{{ catalina_home }}/webapps/{{ item.warfile }}.war"
  23 + owner: "{{ tomcat_user }}"
  24 + group: "{{ tomcat_group }}"
  25 + mode: 0644
  26 + with_items:
  27 + - { artifact: "{{ tests_signup_path }}", warfile: 'alfresco-benchmark-tests-signup' }
  28 + - { artifact: "{{ tests_cmis_path }}", warfile: 'alfresco-benchmark-tests-cmis' }
  29 + - { artifact: "{{ tests_dataload_path }}", warfile: 'alfresco-benchmark-tests-dataload' }
  30 + - { artifact: "{{ tests_share_path }}", warfile: 'alfresco-benchmark-tests-share' }
  31 + - { artifact: "{{ tests_workflow_path }}", warfile: 'alfresco-benchmark-tests-workflow' }
  32 + - { artifact: "{{ tests_desktopsync_path }}", warfile: 'alfresco-benchmark-tests-desktopsync' }
  33 +
... ...
yml/roles/mongodb/handlers/main.yml
... ... @@ -0,0 +1,6 @@
  1 +---
  2 +- name: Restart Mongo
  3 + service:
  4 + name: mongod
  5 + state: restarted
  6 +
... ...
yml/roles/mongodb/tasks/config.yml
... ... @@ -0,0 +1,8 @@
  1 +---
  2 +- name: bind MongoDB to the external network
  3 + lineinfile:
  4 + line: " bindIp: 0.0.0.0"
  5 + regexp: ' bindIp: .*'
  6 + dest: /etc/mongod.conf
  7 + notify: Restart Mongo
  8 +
... ...
yml/roles/mongodb/tasks/dist.yml
... ... @@ -0,0 +1,23 @@
  1 +---
  2 +- name: Load OS specific vars
  3 + include_vars: "{{ item }}"
  4 + with_first_found:
  5 + - "{{ ansible_os_family|lower }}.yml"
  6 + ignore_errors: true
  7 +
  8 +- name: Load distribution vars
  9 + include_vars: "{{ item }}"
  10 + with_first_found:
  11 + - "../vars/{{ ansible_distribution|lower }}.yml"
  12 + ignore_errors: true
  13 +
  14 +- name: Load major version vars
  15 + include_vars: "{{ item }}"
  16 + with_first_found:
  17 + - "../vars/{{ ansible_distribution|lower }}_{{ ansible_distribution_major_version|lower }}.yml"
  18 + ignore_errors: true
  19 +
  20 +- name: WARNING
  21 + debug: msg="your Linux distribution is quite outdated! Software version specified in your configuration may not be the one deployed"
  22 + when: warn_old
  23 +
... ...
yml/roles/mongodb/tasks/main.yml
... ... @@ -0,0 +1,5 @@
  1 +---
  2 +- include: dist.yml
  3 +- include: packages.yml
  4 +- include: config.yml
  5 +
... ...
yml/roles/mongodb/tasks/packages.yml
... ... @@ -0,0 +1,17 @@
  1 +---
  2 +- name: Add MongoDB repo key
  3 + apt_key:
  4 + keyserver: hkp://keyserver.ubuntu.com:80
  5 + id: 0C49F3730359A14518585931BC711F9BA15703C6
  6 + when: ansible_pkg_mgr == 'apt'
  7 +
  8 +- name: Set up MongoDB reposirtory
  9 + apt_repository:
  10 + repo: "{{ mongo_repo_url }}"
  11 + when: ansible_pkg_mgr == 'apt'
  12 +
  13 +- name: Install MongoDB
  14 + apt:
  15 + name: mongodb-org
  16 + when: ansible_pkg_mgr == 'apt'
  17 +
... ...
yml/roles/mongodb/vars/centos_7.yml
yml/roles/mongodb/vars/debian.yml
... ... @@ -0,0 +1,2 @@
  1 +mongo_repo_url: deb http://repo.mongodb.org/apt/debian "{{ ansible_distribution_release }}"/mongodb-org/"{{ mongodb_version }}" main
  2 +
... ...
yml/roles/mongodb/vars/debian_7.yml
yml/roles/mongodb/vars/debian_8.yml
yml/roles/mongodb/vars/ubuntu.yml
... ... @@ -0,0 +1,2 @@
  1 +mongo_repo_url: deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu "{{ ansible_distribution_release }}"/mongodb-org/"{{ mongodb_version }}" multiverse
  2 +
... ...
yml/roles/mongodb/vars/ubuntu_14.yml
yml/roles/selenium/tasks/main.yml
... ... @@ -0,0 +1,43 @@
  1 +---
  2 +- name: Create selenium user
  3 + user:
  4 + name: selenium
  5 + system: yes
  6 + shell: /bin/false
  7 + home: /usr/local/share/selenium
  8 + comment: "Selenium Browser autamation user"
  9 +
  10 +- name: Get Selenium server
  11 + get_url:
  12 + url: "{{ selenium_url }}"
  13 + dest: "/usr/local/share/selenium/selenium-server-standalone-{{ selenium_version }}.jar"
  14 + mode: 0644
  15 +
  16 +- name: Install Pseudo X server
  17 + apt:
  18 + name: xvfb
  19 + when: ansible_pkg_mgr == "apt"
  20 +
  21 +- name: Add Selenium Hub init script
  22 + template:
  23 + src: sysvinit-hub
  24 + dest: /etc/init.d/selenium-hub
  25 + mode: 0755
  26 + when: inventory_hostname in groups['selenium-hub']
  27 +
  28 +- name: Add Selenium Node init script
  29 + template:
  30 + src: sysvinit-node
  31 + dest: /etc/init.d/selenium-node
  32 + mode: 0755
  33 + when: inventory_hostname in groups['load-driver']
  34 +
  35 +- name: Enable Selenium service
  36 + service:
  37 + name: "{{ item }}"
  38 + state: started
  39 + enabled: yes
  40 + with_items:
  41 + - selenium-hub
  42 + - selenium-node
  43 +
... ...
yml/roles/selenium/templates/sysvinit-hub
... ... @@ -0,0 +1,50 @@
  1 +#!/bin/bash
  2 +
  3 +DESC="Selenium server"
  4 +RUN_AS=selenium
  5 +JAVA_BIN=/usr/bin/java
  6 +
  7 +SELENIUM_DIR=/usr/local/share/selenium
  8 +PID_FILE="$SELENIUM_DIR/selenium-hub.pid"
  9 +JAR_FILE="$SELENIUM_DIR/selenium-server-standalone-{{ selenium_version }}.jar"
  10 +LOG_FILE="$SELENIUM_DIR/selenium-hub.log"
  11 +SELENIUM_PORT={{ selenium_hub_portnumber }}
  12 +
  13 +MAX_MEMORY="-Xmx1G"
  14 +STACK_SIZE="-Xss1024k"
  15 +
  16 +DAEMON_OPTS="$MAX_MEMORY -jar $JAR_FILE -log $LOG_FILE -role hub -newSessionWaitTimeout 5000 -timeout 300 -port $SELENIUM_PORT"
  17 +