def issue_cert(self, hostname=system.fqdn, hash="1024", key_prv=None, key_pub=None): """Issues a cert. hostname: CN value. key_prv: Alternate path to store the certificate's private key. key_pub: Alternate path to store the certificate's public key. """ with fabric.context_managers.lcd(self.workspace): subject = "/DC=%s/DC=%s/CN=%s" % (self.domain_comp_country, self.domain_comp, hostname) utils.runcmd(("openssl req -newkey rsa:%s -nodes -sha1 -keyout " "cert.key -keyform PEM -out cert.req -outform PEM " "-subj '%s' -config openssl.cnf" % (hash, subject)), chdir=self.workspace) utils.runcmd(("openssl x509 -req -in cert.req -CA ca.pem -CAkey " "ca.key -CAcreateserial -extensions v3_req -extfile " "openssl.cnf -out cert.crt -days 1"), chdir=self.workspace) if key_prv: utils.runcmd("chmod 400 cert.key", chdir=self.workspace) utils.runcmd("cp cert.key %s" % key_prv, chdir=self.workspace) api.info("Private key stored in '%s' (with 400 perms)." % key_prv) if key_pub: utils.runcmd("cp cert.crt %s" % key_pub, chdir=self.workspace) api.info("Public key stored in '%s'." % key_pub) return OwnCACert(subject, key_prv, key_pub)
def _run(self): logfile = os.path.join(config.CFG["log_path"], "qc_conf.stderr") module_path = utils.runcmd("puppet config print modulepath", envvars=[( "PATH", "$PATH:/opt/puppetlabs/bin")], nosudo=True, stop_on_error=False) if module_path: self.module_path = ':'.join([self.module_path, module_path]) cmd = ("%s apply --verbose --debug --modulepath %s %s " "--detail-exitcodes") % (self.puppet_bin, self.module_path, self.manifest) r = utils.runcmd(cmd, os.getcwd(), log_to_file="qc_conf", stop_on_error=False, nosudo=True) if r.return_code == 0: api.info("Puppet execution ended successfully.") elif r.return_code == 2: api.info(("Puppet execution ended successfully (changes were " "applied)")) r.failed = False else: api.fail("Puppet execution failed. More information on %s log" % logfile, stop_on_error=True) r.failed = True return r
def pre_validate(self): # 1. LDAP utils installation if system.distname in ["redhat", "centos"]: utils.install("openldap-clients") # 2. Decrease BDII_BREATHE_TIMEOUT (for validation tests) utils.runcmd(("sed -i 's/BDII_BREATHE_TIME=.*/BDII_BREATHE_TIME=10/g' " "/etc/bdii/bdii.conf && /etc/init.d/bdii restart"))
def _set_hiera(self): if self.hiera_data: with open("/etc/puppet/hiera.yaml", 'w') as f: f.write(hiera_config) if not os.path.exists("/etc/puppet/hieradata"): utils.runcmd("mkdir /etc/puppet/hieradata") utils.runcmd("cp %s /etc/puppet/hieradata/global.yaml" % self.hiera_data)
def pre_config(self): api.info("PRE-config actions.") utils.install("ntp") api.info("<ntp> installed.") utils.runcmd("mount -o remount,acl,user_xattr /") api.info("Enabled ACLs and Extended Attribute Support in /") api.info("END of PRE-config actions.")
def pre_install(self): api.info("PRE-install actions.") try: pwd.getpwnam("storm") except KeyError: utils.runcmd("/usr/sbin/adduser -M storm") api.info("users storm and gridhttps added") api.info("END of PRE-install actions.")
def pre_validate(self): voms.client_install() utils.runcmd("pip install voms-auth-system-openstack") # fake proxy config.CFG["x509_user_proxy"] = product_utils.create_fake_proxy() # fake voms server - lsc product_utils.add_fake_lsc() # If Ubuntu, token must be retrieved using curl calls if system.distro_version == "ubuntu16": config.CFG["qc_specific_id"] = "keystone-voms-ubuntu"
def _deploy(self): # Install release package if not (utils.is_pkg_installed("puppetlabs-release") or utils.is_pkg_installed("puppetlabs-release-pc1")): utils.install_remote(config.CFG["puppet_release"]) # Install puppet client r = utils.install("puppet") if r.failed: api.fail("Puppet installation failed", stop_on_error=True) # Set hiera environment - required before pre_config() method if not os.path.exists(self.hiera_data_dir): utils.runcmd("mkdir -p %s" % self.hiera_data_dir)
def _set_hiera_params(self): """Sets hiera parameter files (repository deploy and custom params).""" # umd file common.set_umd_params( "umd_puppet.yaml", os.path.join(self.hiera_data_dir, "umd.yaml")) self._add_hiera_param_file("umd.yaml") # custom (static) files if self.hiera_data: for f in self.hiera_data: target = os.path.join(self.hiera_data_dir, f) utils.runcmd("cp etc/puppet/%s %s" % (f, target)) self._add_hiera_param_file(f) # runtime file if config.CFG.get("params_file", None): self._add_hiera_param_file(config.CFG["params_file"])
def runcmd(self, cmd, chdir=None, fail_check=True, stop_on_error=True, log_to_file=True, get_error_msg=False, stderr_to_stdout=False): logfile = None if log_to_file: logfile = self.logfile r = utils.runcmd(cmd, chdir=chdir, fail_check=fail_check, stop_on_error=stop_on_error, logfile=logfile, get_error_msg=get_error_msg, stderr_to_stdout=stderr_to_stdout) try: self.logs = r.logfile except AttributeError: pass return r
def _run(self): if self.role.find("://") != -1: repo_location = os.path.join( "/etc/ansible/roles", os.path.basename(self.role)) cmd = "ansible-pull -vvv -C %s -d %s -i %s -U %s" % ( self.checkout, repo_location, os.path.join(repo_location, "hosts"), self.role) # extra vars cmd += " --extra-vars '@%s'" % UMD_VARS_FILE if self.extra_vars: _extra_vars_file = self._add_extra_vars() cmd += " --extra-vars '@%s'" % _extra_vars_file # extra vars runtime file if config.CFG.get("params_file", None): cmd += " --extra-vars '@%s'" % config.CFG["params_file"] # tags if self.tags: cmd += " --tags '%s'" % ','.join(self.tags) r = utils.runcmd(cmd, log_to_file="qc_conf", stop_on_error=False, nosudo=True) return r
def create(self, trusted_ca_dir=None): """Creates the CA public and private key. trusted_ca_dir: if set, it will copy the CA public key and the signing policy file under the trusted CA directory. """ utils.runcmd("mkdir -p %s" % self.workspace) with fabric.context_managers.lcd(self.workspace): subject = self.subject utils.runcmd(("openssl req -x509 -nodes -days 1 -newkey rsa:2048 " "-out ca.pem -outform PEM -keyout ca.key -subj " "'%s'" % subject)) if trusted_ca_dir: hash = utils.runcmd("openssl x509 -noout -hash -in ca.pem") utils.runcmd("cp ca.pem %s" % os.path.join(trusted_ca_dir, '.'.join([hash, '0']))) with open(os.path.join( trusted_ca_dir, '.'.join([hash, "signing_policy"])), 'w') as f: f.writelines([ "access_id_CA\tX509\t'%s'\n" % subject, "pos_rights\tglobus\tCA:sign\n", "cond_subjects\tglobus\t'\"/DC=%s/DC=%s/*\"'\n" % (self.domain_comp_country, self.domain_comp)])
def _install_modules(self): """Installs required Puppet modules through librarian-puppet.""" if utils.runcmd("librarian-puppet", os.getcwd(), envvars=[( "PATH", "$PATH:/usr/local/bin:/opt/puppetlabs/bin")], nosudo=True, stop_on_error=False).failed: utils.runcmd("gem install librarian-puppet") puppetfile = self._set_puppetfile() utils.runcmd( "librarian-puppet install --clean --path=%s --verbose" % self.module_path, os.path.dirname(puppetfile), envvars=[("PATH", "$PATH:/usr/local/bin:/opt/puppetlabs/bin")], log_to_file="qc_conf", nosudo=True)
def _run_validator(self, glue_version, logfile): # NOTE(orviz): within a QCStep? utils.install("glue-validator") if system.distro_version == "redhat5": utils.install("openldap-clients") port = config.CFG.get("info_port", "2170") if glue_version == "glue1": # cmd = ("glue-validator -H localhost -p %s -b " # "mds-vo-name=resource,o=grid -g glue1 -s " # "general -v 3" % port) cmd = ("glue-validator -h localhost -p %s -b " "mds-vo-name=resource,o=grid -t glue1" % port) version = "1.3" elif glue_version == "glue2": # cmd = ("glue-validator -H localhost -p %s -b " # "GLUE2GroupID=resource,o=glue -g glue2 -s general -v 3" # % port) cmd = ("glue-validator -h localhost -p %s -b " "GLUE2GroupID=resource,o=glue -t glue2" % port) version = "2.0" time.sleep(self.attempt_sleep) breathe_time_set = False slapd_working = False summary = None for attempt in xrange(self.attempt_no): r = utils.runcmd(cmd, log_to_file=logfile) if not r.failed: summary = {} if r: summary = info_utils.get_gluevalidator_summary(r) slapd_working = True break else: if not breathe_time_set: self._set_breathe_time() breathe_time_set = True time.sleep(self.attempt_sleep) if not slapd_working: api.fail("Could not connect to LDAP service.", stop_on_error=True) if summary: if summary["errors"] != '0': api.fail(("Found %s errors while validating GlueSchema " "v%s support" % (summary["errors"], version)), logfile=r.logfile) elif summary["warnings"] != '0': api.warn(("Found %s warnings while validating GlueSchema " "v%s support" % (summary["warnings"], version))) else: api.ok(("Found no errors or warnings while validating " "GlueSchema v%s support" % version))
def pre_install(self): if not config.CFG["repository_url"]: api.fail("No CA verification URL was given.", stop_on_error=True) if system.distname in ["debian", "ubuntu"]: repo = "egi-igtf" utils.runcmd("wget -q -O - %s | apt-key add -" % os.path.join(config.CFG["repository_url"][0], "GPG-KEY-EUGridPMA-RPM-3")) elif system.distname in ["redhat"]: repo = ["EGI-trustanchors", "LCG-trustanchors"] utils.remove_repo(repo) # FIXME(orviz) workaround CA release with no Debian '.list' repofile if system.distname in ["debian", "ubuntu"]: # Just one repository is expected repo = config.CFG["repository_url"][0] ca_version = urlparse.urlparse(repo).path.split("cas/")[-1] ca_version = ''.join(ca_version.replace('/', '.', 1).replace('/', '-')) repo = os.path.join(repo, '-'.join(["ca-policy-egi-core", ca_version])) repodeb = "deb %s egi-igtf core" % repo if system.distro_version == "debian6": source = "/etc/apt/sources.list.d/egi-igtf.list" utils.runcmd("echo '%s' > %s" % (repodeb, source)) else: utils.runcmd("apt-add-repository '%s'" % repodeb)
def config(self, logfile=None): # Install ansible if it does not exist if utils.runcmd("ansible --help", stop_on_error=False).failed: utils.install("ansible") common.set_umd_params( "umd_ansible.yaml", UMD_VARS_FILE) # Run ansible r = self._run() self.has_run = True return r
def create_fake_proxy(vo="dteam", out="/tmp/umd_proxy"): """Creates a fake proxy for further testing. :vo: VO used for the proxy creation. :out: Output path to store the proxy being created. """ fqdn = system.fqdn keypath = "/tmp/userkey.crt" certpath = "/tmp/usercert.crt" config.CFG["ca"].issue_cert(hostname="perico-palotes", hash="2048", key_prv=keypath, key_pub=certpath) utils.runcmd(("voms-proxy-fake -rfc -cert %s -key %s " "-hours 44000 -voms %s -hostcert " "/etc/grid-security/hostcert.pem -hostkey " "/etc/grid-security/hostkey.pem " "-fqan /%s/Role=NULL/Capability=NULL " "-uri %s:15000 -out %s") % (certpath, keypath, vo, vo, fqdn, out)) api.info("Fake proxy created under '%s'" % out) return out
def trust_ca(ca_location): """Add the given CA to the system's CA trust database.""" if system.distname == "ubuntu": trust_dir = "/usr/share/ca-certificates/" trust_cmd = "update-ca-certificates" elif system.distname == "centos": trust_dir = "/etc/pki/ca-trust/source/anchors/" trust_cmd = "update-ca-trust" ca_location_basename = os.path.basename(ca_location) ca_location_basename_crt = '.'.join([ ca_location_basename.split('.')[0], "crt"]) utils.runcmd("cp %s %s" % ( ca_location, os.path.join(trust_dir, ca_location_basename_crt))) utils.runcmd("echo '%s' >> /etc/ca-certificates.conf" % ca_location_basename_crt) r = utils.runcmd(trust_cmd) if r.failed: api.fail("Could not add CA '%s' to the system's trust DB" % ca_location) else: api.info("CA '%s' added to system's trust DB" % ca_location)
def pre_config(self): ip = utils.runcmd("hostname -I | cut -d' ' -f1") # extra vars extra_vars = [ "site_name: UMD", "projects: demo", "extractor: nova", "auth_type: password", "auth_url: http://%s/identity" % ip, "username: admin", "user_domain_name: default", "password: secret", "output_path: /var/spool/apel/outgoing/openstack", "vos: { \"demo\": [\"demo\"]}"] self.cfgtool.extra_vars = extra_vars
def qc_sec_5(self): """World Writable Files check.""" _logfile = "qc_sec_5" r = utils.runcmd(("find / -not \\( -path \"/proc\" -prune \\) " "-not \\( -path \"/sys\" -prune \\) " "-type f -perm -002 -exec ls -l {} \;"), log_to_file=_logfile) if r: ww_filelist = sec_utils.get_filelist_from_find(r) try: known_ww_filelist = self.exceptions[ "known_worldwritable_filelist"] except KeyError: known_ww_filelist = [] if set(ww_filelist).difference(set(known_ww_filelist)): api.fail("Found %s world-writable file/s." % len(ww_filelist), logfile=r.logfile) else: api.warn("Found world-writable file/s required for operation.", logfile=r.logfile) else: api.ok("Found no world-writable file.")
def post_install(self): s_groups = [t[0] for t in grp.getgrall()] s_users = [t[0] for t in pwd.getpwall()] api.info("Configuration actions.") if "iberops" not in s_groups: utils.runcmd("groupadd -g 23200 iberops") for i in xrange(5): user = "******" % i if user not in s_users: utils.runcmd("useradd -m -u 2320%s -g 23200 %s" % (i, user)) api.info("Group 'iberops' and user accounts created.") if "iberopses" not in s_groups: utils.runcmd("groupadd -g 23210 iberopses") for i in xrange(5): user = "******" % i if user not in s_users: utils.runcmd("useradd -m -u 2321%s -g 23210 %s" % (i, user)) api.info("Group 'iberopses' and user accounts created.") utils.runcmd("cp /etc/glexec.conf /etc/glexec.conf.0") api.info("Backup /etc/glexec.conf file.") utils.runcmd(("sed -i 's/.*user_white_list.*/user_white_list = umd/g' " "/etc/glexec.conf")) api.info("User 'umd' white-listed in /etc/glexec.conf") utils.runcmd(("cp /etc/lcmaps/lcmaps-glexec.db " "/etc/lcmaps/lcmaps-glexec.db.0")) api.info("Backup /etc/lcmaps/lcmaps-glexec.db file.") lcmaps_db = """ # where to look for modules path = /usr/lib64/lcmaps # module definitions verify_proxy = "lcmaps_verify_proxy.mod" " -certdir /etc/grid-security/certificates/" " --allow-limited-proxy" pepc = "lcmaps_c_pep.mod" "--pep-daemon-endpoint-url https://amargus02.ifca.es:8154/authz" " -resourceid http://authz-interop.org/xacml/resource/resource-type/wn" " -actionid http://glite.org/xacml/action/execute" " -capath /etc/grid-security/certificates/" " -pep-certificate-mode implicit" " --use-pilot-proxy-as-cafile" # Add this on RHEL 6 based systems glexec_get_account: verify_proxy -> pepc """ with open("/etc/lcmaps/lcmaps-glexec.db", 'w') as f: f.write(lcmaps_db) api.info("GLExec lcmaps configured.")
def pre_config(self): os_release = config.CFG["openstack_release"] self.cfgtool.module.append(( "git://github.com/egi-qc/puppet-keystone.git", "umd_stable_%s" % os_release)) keystone_voms_params = [ "keystone_voms::openstack_version: %s" % os_release, "cacert: %s" % config.CFG["ca"].location ] keystone_voms_conf = os.path.join( config.CFG["cfgtool"].hiera_data_dir, "keystone_voms.yaml") if utils.to_yaml(keystone_voms_conf, keystone_voms_params): api.info(("keystone-voms hiera parameters " "set: %s" % keystone_voms_conf)) # Add it to hiera.yaml config.CFG["cfgtool"]._add_hiera_param_file("keystone_voms.yaml") pki.trust_ca(config.CFG["ca"].location) # Apache2 if system.distro_version == "ubuntu16": utils.runcmd(("sed -e '/ServerName*/c\ServerName %s' " "/etc/apache2/apache2.conf") % system.fqdn) utils.runcmd("/etc/init.d/apache2 restart") elif system.distro_version == "centos7": utils.runcmd(("sed -e '/ServerName*/c\ServerName %s' " "/etc/httpd/conf/httpd.conf") % system.fqdn) utils.runcmd("systemctl restart httpd") # mysql - set current hostname utils.runcmd(("mysql -e 'UPDATE keystone.endpoint SET " "url=\"https://%s:5000/v2.0\" WHERE url " "like \"%%5000%%\";'" % system.fqdn)) utils.runcmd(("mysql -e 'UPDATE keystone.endpoint SET " "url=\"https://%s:35357/v2.0\" WHERE url " "like \"%%35357%%\";'" % system.fqdn)) # FIXME Create tenant VO:dteam utils.runcmd(("/bin/bash -c 'source /root/.nova/admin-novarc ; " "openstack --os-password $OS_PASSWORD " "--os-username $OS_USERNAME " "--os-project-name $OS_PROJECT_NAME " "--os-auth-url $OS_AUTH_URL " "--os-cacert $OS_CACERT " "project create --enable VO:dteam --or-show'"))
def _set_breathe_time(self): # FIXME(orviz) This should be handled by the Puppet module breathe_time = 30 utils.runcmd(("sed -i 's/^BDII_BREATHE_TIME.*/BDII_BREATHE_TIME=%s/g' " "/etc/bdii/bdii.conf" % breathe_time)) utils.runcmd("/etc/init.d/bdii restart")
def pre_validate(self): utils.runcmd("pip install IM-client")
def get_subject(hostcert): return utils.runcmd(("openssl x509 -in %s -noout " "-subject" % hostcert)).split()[1]
def _v3_workaround(self): # Include hiera functions in Puppet environment utils.install("rubygems") utils.runcmd(("gem install hiera-puppet --install-dir " "/etc/puppet/modules")) utils.runcmd("mv /etc/puppet/modules/gems/* /etc/puppet/modules/")
def create(self, trusted_ca_dir=None): """Creates the CA public and private key. trusted_ca_dir: if set, it will copy the CA public key and the signing policy file under the trusted CA directory. """ utils.runcmd("mkdir -p %s" % self.workspace) with fabric.context_managers.lcd(self.workspace): subject = self.subject utils.runcmd(("openssl req -x509 -nodes -days 1 -newkey rsa:2048 " "-out ca.pem -outform PEM -keyout ca.key -subj " "'%s'" % subject), chdir=self.workspace) if trusted_ca_dir: hash = utils.runcmd("openssl x509 -noout -hash -in ca.pem", chdir=self.workspace) # CA cert (.0) ca_dest = os.path.join(trusted_ca_dir, '.'.join([hash, '0'])) utils.runcmd("cp ca.pem %s" % ca_dest, chdir=self.workspace) self.location = ca_dest # CRL cert (.r0) # FIXME(orviz) check why absolute path is needed here with open(os.path.join(self.workspace, "openssl.cnf"), 'w') as f: f.write(openssl_cnf) utils.runcmd("echo \"01\" > crlnumber", chdir=self.workspace) utils.runcmd("touch index.txt", chdir=self.workspace) utils.runcmd(("openssl ca -config openssl.cnf -gencrl " "-keyfile ca.key -cert ca.pem -out crl.pem"), chdir=self.workspace) crl_dest = os.path.join(trusted_ca_dir, '.'.join([hash, 'r0'])) utils.runcmd("cp crl.pem %s" % crl_dest, chdir=self.workspace) # signing_policy signing_policy_dest = os.path.join( trusted_ca_dir, '.'.join([hash, "signing_policy"])) with open(signing_policy_dest, 'w') as f: f.writelines([ "access_id_CA\tX509\t'%s'\n" % subject, "pos_rights\tglobus\tCA:sign\n", "cond_subjects\tglobus\t'\"/DC=%s/DC=%s/*\"'\n" % (self.domain_comp_country, self.domain_comp)])
def config(self): self.manifest = os.path.join(config.CFG["puppet_path"], self.manifest) if self.hiera_data: self.hiera_data = os.path.join(config.CFG["puppet_path"], self.hiera_data) utils.install("puppet") # Puppet versions <3 workarounds puppet_version = utils.runcmd("facter -p puppetversion") if puppet_version and (version.StrictVersion(puppet_version) < version.StrictVersion("3.0")): # self._v3_workaround() pkg_url = config.CFG["puppet_release"] pkg_loc = "/tmp/puppet-release.rpm" r = utils.runcmd("wget %s -O %s" % (pkg_url, pkg_loc)) if r.failed: api.fail("Could not fetch Puppet package from '%s'" % pkg_url, stop_on_error=True) else: api.info("Fetched Puppet release package from '%s'." % pkg_url) utils.install(pkg_loc) utils.runcmd(("sed '/enabled=1/a\priority=1' " "/etc/yum.repos.d/puppet*")) # FIXME (orviz) Remove this check when dropping redhat5 support if system.distro_version == "redhat5": pkg = ("ftp://rpmfind.net/linux/centos/5.11/os/x86_64/CentOS/" "virt-what-1.11-2.el5.x86_64.rpm") utils.runcmd(("wget %s -O /tmp/virt-what.rpm && yum -y " "install /tmp/virt-what.rpm") % pkg) utils.install("puppet") # Install modules from puppetforge/local for mod in self.module_from_puppetforge: r = utils.runcmd("puppet module install %s --force" % mod) if r.failed: api.fail("Error while installing module '%s'" % mod) self.module_path.append(*["/etc/puppet/modules"]) module_loc = [] for mod in self.module_from_repository: dirname = utils.clone_repo(*mod) if dirname: module_loc.append(dirname) if module_loc: self.module_path.append(*module_loc) # Hiera environment self._set_hiera() logfile = os.path.join(config.CFG["log_path"], "puppet.log") r = utils.runcmd(("puppet apply -l %s --modulepath %s %s " "--detail-exitcodes") % (logfile, ':'.join(self.module_path), self.manifest)) if r.return_code in [0, 2]: api.info("Puppet execution ended successfully.") else: api.fail("Puppet execution failed. More information in logs: %s" % logfile, stop_on_error=True)
def certify(): """Create host certificate and private key.""" cert_path = "/etc/grid-security/hostcert.pem" key_path = "/etc/grid-security/hostkey.pem" do_cert = True utils.runcmd("mkdir -p /etc/grid-security/certificates") utils.runcmd("chown root:root /etc/grid-security") utils.runcmd("chmod 0755 /etc/grid-security") if os.path.isfile(cert_path) and os.path.isfile(key_path): if not config.CFG.get("dont_ask_cert_renewal", False): r = prompt(("Certificate already exists under " "'/etc/grid-security'. Do you want to " "overwrite them? (y/N)")) if r.lower() == "y": api.info("Overwriting already existant certificate") else: do_cert = False api.info("Using already existant certificate") cert_for_subject = None if do_cert: hostcert = config.CFG.get("hostcert", None) hostkey = config.CFG.get("hostkey", None) if hostkey and hostcert: api.info("Using provided host certificates") utils.runcmd("cp %s %s" % (hostkey, key_path)) utils.runcmd("chmod 400 %s" % key_path) utils.runcmd("cp %s %s" % (hostcert, cert_path)) cert_for_subject = hostcert else: api.info("Generating own certificates") config.CFG["ca"] = OwnCA( domain_comp_country="es", domain_comp="UMDverification", common_name="UMDVerificationOwnCA") config.CFG["ca"].create( trusted_ca_dir="/etc/grid-security/certificates") config.CFG["cert"] = config.CFG["ca"].issue_cert( hash="2048", key_prv=key_path, key_pub=cert_path) else: cert_for_subject = cert_path if cert_for_subject: subject = get_subject(cert_for_subject) config.CFG["cert"] = OwnCACert(subject)
def pre_validate(self): utils.runcmd("useradd -r ees") utils.runcmd("/etc/init.d/ees start") utils.install("nc")