Repository: bigtop Updated Branches: refs/heads/master f1f5619a3 -> f5e89f4e1
http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/icon.svg ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/icon.svg b/bigtop-packages/src/charm/spark/layer-spark/icon.svg new file mode 100644 index 0000000..a0da80d --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/icon.svg @@ -0,0 +1,843 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="96" + height="96" + id="svg6517" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="icon.svg"> + <defs + id="defs6519"> + <linearGradient + inkscape:collect="always" + xlink:href="#Background" + id="linearGradient6461" + gradientUnits="userSpaceOnUse" + x1="0" + y1="970.29498" + x2="144" + y2="970.29498" + gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" /> + <linearGradient + id="Background"> + <stop + id="stop4178" + offset="0" + style="stop-color:#b8b8b8;stop-opacity:1" /> + <stop + id="stop4180" + offset="1" + style="stop-color:#c9c9c9;stop-opacity:1" /> + </linearGradient> + <filter + style="color-interpolation-filters:sRGB;" + inkscape:label="Inner Shadow" + id="filter1121"> + <feFlood + flood-opacity="0.59999999999999998" + flood-color="rgb(0,0,0)" + result="flood" + id="feFlood1123" /> + <feComposite + in="flood" + in2="SourceGraphic" + operator="out" + result="composite1" + id="feComposite1125" /> + <feGaussianBlur + in="composite1" + stdDeviation="1" + result="blur" + id="feGaussianBlur1127" /> + <feOffset + dx="0" + dy="2" + result="offset" + id="feOffset1129" /> + <feComposite + in="offset" + in2="SourceGraphic" + operator="atop" + result="composite2" + id="feComposite1131" /> + </filter> + <filter + style="color-interpolation-filters:sRGB;" + inkscape:label="Drop Shadow" + id="filter950"> + <feFlood + flood-opacity="0.25" + flood-color="rgb(0,0,0)" + result="flood" + id="feFlood952" /> + <feComposite + in="flood" + in2="SourceGraphic" + operator="in" + result="composite1" + id="feComposite954" /> + <feGaussianBlur + in="composite1" + stdDeviation="1" + result="blur" + id="feGaussianBlur956" /> + <feOffset + dx="0" + dy="1" + result="offset" + id="feOffset958" /> + <feComposite + in="SourceGraphic" + in2="offset" + operator="over" + result="composite2" + id="feComposite960" /> + </filter> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath873"> + <g + transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)" + id="g875" + inkscape:label="Layer 1" + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"> + <path + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" + d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z" + id="path877" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssssssss" /> + </g> + </clipPath> + <filter + inkscape:collect="always" + id="filter891" + inkscape:label="Badge Shadow"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.71999962" + id="feGaussianBlur893" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="4.0745362" + inkscape:cx="18.514671" + inkscape:cy="-0.067172296" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1056" + inkscape:window-x="2160" + inkscape:window-y="340" + inkscape:window-maximized="1" + showborder="true" + showguides="true" + inkscape:guide-bbox="true" + inkscape:showpageshadow="false"> + <inkscape:grid + type="xygrid" + id="grid821" /> + <sodipodi:guide + orientation="1,0" + position="16,48" + id="guide823" /> + <sodipodi:guide + orientation="0,1" + position="64,80" + id="guide825" /> + <sodipodi:guide + orientation="1,0" + position="80,40" + id="guide827" /> + <sodipodi:guide + orientation="0,1" + position="64,16" + id="guide829" /> + </sodipodi:namedview> + <metadata + id="metadata6522"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="BACKGROUND" + inkscape:groupmode="layer" + id="layer1" + transform="translate(268,-635.29076)" + style="display:inline"> + <path + style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)" + d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z" + id="path6455" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssssssss" /> + <image + y="636.29077" + x="-260.2941" + id="image3115" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAIABJREFU +eJzs3XeYVNX5B/Dve869M7O7sCKg9Coo2JC6uxTFroklajQxGjUmUaMmFmyJGmvsMcWoMTGJmqiJ +SUz9RaOiRulgb4g0AQUBkbZl5t5z3t8fswuIgLuzs7Pt+3mefYTdmXPfmcU57z3lPQARERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +EREREREREREREREREREREREREdG2SXMHQERELcecu84assvymYeaeO3+Lo46wAAJsRvS4c5TN3bd +c+o+F/9xTnPHSPnBBICIiLDgjmNOSS6ffQa8Hxgg7mVFk04CWDhAFc5rBNiVGVv038rOezw29Mpn +/tvcMVPjMAEgImrHfq5a+qXLBt+SiD8+WW1RJ1EH6yN4AE4CKAQqAqNAgBhQhYesUVvyjw/O//2V +o/rvv7y5XwPlhgkAEVE79Qf9uPSAScPuDv3Gk42xNtu5G8QSQKCwiKEQCBSAwMNCAYRw8Op9ZIqf +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 +npYDbiNE9BSJ2QISUA0rAYQIBjCORXcAKKWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWU +UkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJK +KaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSiml +lFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRS +SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop +pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWU +UkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJK +KaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSiml +lFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRS +SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop +pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWU +UkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJK +KaWUUkoppZRSSimllFJKKaWUqsn/Bw1WfWPJuUdqAAAAAElFTkSuQmCC +" + height="89.999992" + width="87.294098" /> + </g> + <g + inkscape:groupmode="layer" + id="layer3" + inkscape:label="PLACE YOUR PICTOGRAM HERE" + style="display:inline" /> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="BADGE" + style="display:none" + sodipodi:insensitive="true"> + <g + style="display:inline" + transform="translate(-340.00001,-581)" + id="g4394" + clip-path="none"> + <g + id="g855"> + <g + inkscape:groupmode="maskhelper" + id="g870" + clip-path="url(#clipPath873)" + style="opacity:0.6;filter:url(#filter891)"> + <path + transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)" + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" + sodipodi:ry="12" + sodipodi:rx="12" + sodipodi:cy="552.36218" + sodipodi:cx="252" + id="path844" + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + sodipodi:type="arc" /> + </g> + <g + id="g862"> + <path + sodipodi:type="arc" + style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path4398" + sodipodi:cx="252" + sodipodi:cy="552.36218" + sodipodi:rx="12" + sodipodi:ry="12" + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" + transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" /> + <path + transform="matrix(1.25,0,0,1.25,33,-100.45273)" + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" + sodipodi:ry="12" + sodipodi:rx="12" + sodipodi:cy="552.36218" + sodipodi:cx="252" + id="path4400" + style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + sodipodi:type="arc" /> + <path + sodipodi:type="star" + style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path4459" + sodipodi:sides="5" + sodipodi:cx="666.19574" + sodipodi:cy="589.50385" + sodipodi:r1="7.2431178" + sodipodi:r2="4.3458705" + sodipodi:arg1="1.0471976" + sodipodi:arg2="1.6755161" + inkscape:flatsided="false" + inkscape:rounded="0.1" + inkscape:randomized="0" + d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 -0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z" + transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" /> + </g> + </g> + </g> + </g> +</svg> http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/layer.yaml ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/layer.yaml b/bigtop-packages/src/charm/spark/layer-spark/layer.yaml new file mode 100644 index 0000000..b67a34e --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/layer.yaml @@ -0,0 +1,28 @@ +repo: [email protected]:juju-solutions/layer-apache-bigtop-spark.git +includes: + - 'layer:apache-bigtop-base' + - 'layer:hadoop-client' + - 'layer:leadership' + - 'interface:benchmark' + - 'interface:spark' + - 'interface:zookeeper' + - 'interface:spark-quorum' +options: + hadoop-client: + groups: + - 'hadoop' + dirs: + spark_events: + path: '/var/log/spark/apps' + ports: + # Ports that need to be exposed, overridden, or manually specified. + # Only expose ports serving a UI or external API (i.e., namenode and + # resourcemanager). Communication among units within the cluster does + # not need ports to be explicitly opened. + spark-history: + port: 18080 + exposed_on: 'spark' + spark-webui: + port: 8080 + exposed_on: 'spark' + silent: True http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/lib/charms/layer/bigtop_spark.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/lib/charms/layer/bigtop_spark.py b/bigtop-packages/src/charm/spark/layer-spark/lib/charms/layer/bigtop_spark.py new file mode 100755 index 0000000..52f5e97 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/lib/charms/layer/bigtop_spark.py @@ -0,0 +1,255 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from jujubigdata import utils +from path import Path + +from charms.layer.apache_bigtop_base import Bigtop +from charms.reactive import is_state +from charms import layer +from charmhelpers.core import hookenv, host, unitdata +from charmhelpers.fetch.archiveurl import ArchiveUrlFetchHandler + + +class Spark(object): + """ + This class manages Spark. + """ + def __init__(self): + self.dist_config = utils.DistConfig( + data=layer.options('hadoop-client')) + + # translate our execution_mode into the appropriate --master value + def get_master_url(self, spark_master_host): + mode = hookenv.config()['spark_execution_mode'] + zk_units = unitdata.kv().get('zookeeper.units', []) + master = None + if mode.startswith('local') or mode == 'yarn-cluster': + master = mode + elif mode == 'standalone' and not zk_units: + master = 'spark://{}:7077'.format(spark_master_host) + elif mode == 'standalone' and zk_units: + master_ips = [p[1] for p in unitdata.kv().get('sparkpeer.units')] + nodes = [] + for ip in master_ips: + nodes.append('{}:7077'.format(ip)) + nodes_str = ','.join(nodes) + master = 'spark://{}'.format(nodes_str) + elif mode.startswith('yarn'): + master = 'yarn-client' + return master + + def get_roles(self): + roles = ['spark-worker', 'spark-client'] + zk_units = unitdata.kv().get('zookeeper.units', []) + if is_state('leadership.is_leader') or zk_units: + roles.append('spark-master') + roles.append('spark-history-server') + return roles + + def install_benchmark(self): + install_sb = hookenv.config()['spark_bench_enabled'] + sb_dir = '/home/ubuntu/spark-bench' + if install_sb: + if not unitdata.kv().get('spark_bench.installed', False): + if utils.cpu_arch() == 'ppc64le': + sb_url = hookenv.config()['spark_bench_ppc64le'] + else: + # TODO: may need more arch cases (go with x86 sb for now) + sb_url = hookenv.config()['spark_bench_x86_64'] + + Path(sb_dir).rmtree_p() + au = ArchiveUrlFetchHandler() + au.install(sb_url, '/home/ubuntu') + + # ##### + # Handle glob if we use a .tgz that doesn't expand to sb_dir + # sb_archive_dir = glob('/home/ubuntu/spark-bench-*')[0] + # SparkBench expects to live in ~/spark-bench, so put it there + # Path(sb_archive_dir).rename(sb_dir) + # ##### + + unitdata.kv().set('spark_bench.installed', True) + unitdata.kv().flush(True) + else: + Path(sb_dir).rmtree_p() + unitdata.kv().set('spark_bench.installed', False) + unitdata.kv().flush(True) + + def setup(self): + self.dist_config.add_users() + self.dist_config.add_dirs() + self.install_demo() + self.open_ports() + + def setup_hdfs_logs(self): + # create hdfs storage space for history server + dc = self.dist_config + events_dir = dc.path('spark_events') + events_dir = 'hdfs://{}'.format(events_dir) + utils.run_as('hdfs', 'hdfs', 'dfs', '-mkdir', '-p', events_dir) + utils.run_as('hdfs', 'hdfs', 'dfs', '-chown', '-R', 'ubuntu:spark', + events_dir) + return events_dir + + def configure(self, available_hosts, zk_units, peers): + """ + This is the core logic of setting up spark. + + Two flags are needed: + + * Namenode exists aka HDFS is there + * Resource manager exists aka YARN is ready + + both flags are infered from the available hosts. + + :param dict available_hosts: Hosts that Spark should know about. + """ + unitdata.kv().set('zookeeper.units', zk_units) + unitdata.kv().set('sparkpeer.units', peers) + unitdata.kv().flush(True) + + if not unitdata.kv().get('spark.bootstrapped', False): + self.setup() + unitdata.kv().set('spark.bootstrapped', True) + + self.install_benchmark() + + master_ip = utils.resolve_private_address(available_hosts['spark-master']) + hosts = { + 'spark': master_ip, + } + + dc = self.dist_config + events_log_dir = 'file://{}'.format(dc.path('spark_events')) + if 'namenode' in available_hosts: + hosts['namenode'] = available_hosts['namenode'] + events_log_dir = self.setup_hdfs_logs() + + if 'resourcemanager' in available_hosts: + hosts['resourcemanager'] = available_hosts['resourcemanager'] + + roles = self.get_roles() + + override = { + 'spark::common::master_url': self.get_master_url(master_ip), + 'spark::common::event_log_dir': events_log_dir, + 'spark::common::history_log_dir': events_log_dir, + } + + if zk_units: + zks = [] + for unit in zk_units: + ip = utils.resolve_private_address(unit['host']) + zks.append("%s:%s" % (ip, unit['port'])) + + zk_connect = ",".join(zks) + override['spark::common::zookeeper_connection_string'] = zk_connect + else: + override['spark::common::zookeeper_connection_string'] = "" + + bigtop = Bigtop() + bigtop.render_site_yaml(hosts, roles, override) + bigtop.trigger_puppet() + # There is a race condition here. + # The work role will not start the first time we trigger puppet apply. + # The exception in /var/logs/spark: + # Exception in thread "main" org.apache.spark.SparkException: Invalid master URL: spark://:7077 + # The master url is not set at the time the worker start the first time. + # TODO(kjackal): ...do the needed... (investiate,debug,submit patch) + bigtop.trigger_puppet() + if 'namenode' not in available_hosts: + # Local event dir (not in HDFS) needs to be 777 so non-spark + # users can write job history there. It needs to be g+s so + # spark (in the spark group) can read non-spark user entries. + dc.path('spark_events').chmod(0o2777) + + self.patch_worker_master_url(master_ip) + + def patch_worker_master_url(self, master_ip): + ''' + Patch the worker startup script to use the full master url istead of contracting it. + The master url is placed in the spark-env.sh so that the startup script will use it. + In HA mode the master_ip is set to be the local_ip instead of the one the leader + elects. This requires a restart of the master service. + ''' + master_url = self.get_master_url(master_ip) + zk_units = unitdata.kv().get('zookeeper.units', []) + if master_url.startswith('spark://'): + if zk_units: + master_ip = hookenv.unit_private_ip() + spark_env = '/etc/spark/conf/spark-env.sh' + utils.re_edit_in_place(spark_env, { + r'.*SPARK_MASTER_URL.*': "export SPARK_MASTER_URL={}".format(master_url), + r'.*SPARK_MASTER_IP.*': "export SPARK_MASTER_IP={}".format(master_ip), + }, append_non_matches=True) + + self.inplace_change('/etc/init.d/spark-worker', + 'spark://$SPARK_MASTER_IP:$SPARK_MASTER_PORT', + '$SPARK_MASTER_URL') + host.service_restart('spark-master') + host.service_restart('spark-worker') + + def inplace_change(self, filename, old_string, new_string): + # Safely read the input filename using 'with' + with open(filename) as f: + s = f.read() + if old_string not in s: + return + + # Safely write the changed content, if found in the file + with open(filename, 'w') as f: + s = s.replace(old_string, new_string) + f.write(s) + + def install_demo(self): + ''' + Install sparkpi.sh to /home/ubuntu (executes SparkPI example app) + ''' + demo_source = 'scripts/sparkpi.sh' + demo_target = '/home/ubuntu/sparkpi.sh' + Path(demo_source).copy(demo_target) + Path(demo_target).chmod(0o755) + Path(demo_target).chown('ubuntu', 'hadoop') + + def start(self): + if unitdata.kv().get('spark.uprading', False): + return + + # stop services (if they're running) to pick up any config change + self.stop() + # always start the history server, start master/worker if we're standalone + host.service_start('spark-history-server') + if hookenv.config()['spark_execution_mode'] == 'standalone': + host.service_start('spark-master') + host.service_start('spark-worker') + + def stop(self): + if not unitdata.kv().get('spark.installed', False): + return + # Only stop services if they're running + if utils.jps("HistoryServer"): + host.service_stop('spark-history-server') + if utils.jps("Master"): + host.service_stop('spark-master') + if utils.jps("Worker"): + host.service_stop('spark-worker') + + def open_ports(self): + for port in self.dist_config.exposed_ports('spark'): + hookenv.open_port(port) + + def close_ports(self): + for port in self.dist_config.exposed_ports('spark'): + hookenv.close_port(port) http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/metadata.yaml ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/metadata.yaml b/bigtop-packages/src/charm/spark/layer-spark/metadata.yaml new file mode 100644 index 0000000..efec6f0 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/metadata.yaml @@ -0,0 +1,19 @@ +name: spark +summary: Apache Spark from Apache Bigtop platform +maintainer: Juju Big Data <[email protected]> +description: > + Apache Spark is a fast and general engine for large-scale data processing. + + Learn more at http://spark.apache.org. +tags: ["applications"] +provides: + benchmark: + interface: benchmark + client: + interface: spark +requires: + zookeeper: + interface: zookeeper +peers: + sparkpeers: + interface: spark-quorum http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/reactive/spark.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/reactive/spark.py b/bigtop-packages/src/charm/spark/layer-spark/reactive/spark.py new file mode 100644 index 0000000..bb36401 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/reactive/spark.py @@ -0,0 +1,171 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from charms.reactive import RelationBase, when, when_not, is_state, set_state, remove_state, when_any +from charms.layer.apache_bigtop_base import get_fqdn +from charms.layer.bigtop_spark import Spark +from charmhelpers.core import hookenv +from charms import leadership +from charms.reactive.helpers import data_changed +from jujubigdata import utils +from charms import layer + + +def set_deployment_mode_state(state): + if is_state('spark.yarn.installed'): + remove_state('spark.yarn.installed') + if is_state('spark.standalone.installed'): + remove_state('spark.standalone.installed') + remove_state('spark.ready.to.install') + set_state('spark.started') + set_state(state) + + +def report_status(): + mode = hookenv.config()['spark_execution_mode'] + if (not is_state('spark.yarn.installed')) and mode.startswith('yarn'): + hookenv.status_set('blocked', + 'yarn execution mode not available') + return + + if mode == 'standalone' and is_state('zookeeper.ready'): + mode = mode + " - HA" + elif mode == 'standalone' and is_state('leadership.is_leader'): + mode = mode + " - master" + + hookenv.status_set('active', 'ready ({})'.format(mode)) + + +def install_spark(hadoop=None, zks=None): + spark_master_host = leadership.leader_get('master-fqdn') + if not spark_master_host: + hookenv.status_set('waiting', 'master not elected yet') + return False + + hosts = { + 'spark-master': spark_master_host, + } + + if is_state('hadoop.yarn.ready'): + rms = hadoop.resourcemanagers() + hosts['resourcemanager'] = rms[0] + + if is_state('hadoop.hdfs.ready'): + nns = hadoop.namenodes() + hosts['namenode'] = nns[0] + + spark = Spark() + spark.configure(hosts, zks, get_spark_peers()) + return True + + +@when('config.changed', 'spark.started') +def reconfigure_spark(): + config = hookenv.config() + mode = config['spark_execution_mode'] + hookenv.status_set('maintenance', + 'changing default execution mode to {}'.format(mode)) + + hadoop = (RelationBase.from_state('hadoop.yarn.ready') or + RelationBase.from_state('hadoop.hdfs.ready')) + + zks = None + if is_state('zookeeper.ready'): + zk = RelationBase.from_state('zookeeper.ready') + zks = zk.zookeepers() + + if install_spark(hadoop, zks): + report_status() + + +# This is a triky call. We want to fire when the leader changes, yarn and hdfs become ready or +# depart. In the future this should fire when Cassandra or any other storage +# becomes ready or departs. Since hdfs and yarn do not have a departed state we make sure +# we fire this method always ('spark.started'). We then build a deployment-matrix +# and if anything has changed we re-install. +# 'hadoop.yarn.ready', 'hadoop.hdfs.ready' can be ommited but I like them here for clarity +@when_any('hadoop.yarn.ready', + 'hadoop.hdfs.ready', 'master.elected', 'sparkpeers.joined', 'zookeeper.ready') +@when('bigtop.available', 'master.elected') +def reinstall_spark(): + spark_master_host = leadership.leader_get('master-fqdn') + peers = [] + zks = [] + if is_state('zookeeper.ready'): + # if ZK is availuable we are in HA. We do not want reconfigurations if a leader fails + # HA takes care of this + spark_master_host = '' + zk = RelationBase.from_state('zookeeper.ready') + zks = zk.zookeepers() + # We need reconfigure Spark when in HA and peers change ignore otherwise + peers = get_spark_peers() + + deployment_matrix = { + 'spark_master': spark_master_host, + 'yarn_ready': is_state('hadoop.yarn.ready'), + 'hdfs_ready': is_state('hadoop.hdfs.ready'), + 'zookeepers': zks, + 'peers': peers, + } + + if not data_changed('deployment_matrix', deployment_matrix): + return + + hookenv.status_set('maintenance', 'configuring spark') + hadoop = (RelationBase.from_state('hadoop.yarn.ready') or + RelationBase.from_state('hadoop.hdfs.ready')) + if install_spark(hadoop, zks): + if is_state('hadoop.yarn.ready'): + set_deployment_mode_state('spark.yarn.installed') + else: + set_deployment_mode_state('spark.standalone.installed') + + report_status() + + +def get_spark_peers(): + nodes = [(hookenv.local_unit(), hookenv.unit_private_ip())] + sparkpeer = RelationBase.from_state('sparkpeers.joined') + if sparkpeer: + nodes.extend(sorted(sparkpeer.get_nodes())) + return nodes + + +@when('leadership.is_leader', 'bigtop.available') +def send_fqdn(): + spark_master_host = get_fqdn() + leadership.leader_set({'master-fqdn': spark_master_host}) + hookenv.log("Setting leader to {}".format(spark_master_host)) + + +@when('leadership.changed.master-fqdn') +def leader_elected(): + set_state("master.elected") + + +@when('spark.started', 'client.joined') +def client_present(client): + if is_state('leadership.is_leader'): + client.set_spark_started() + spark = Spark() + master_ip = utils.resolve_private_address(hookenv.unit_private_ip()) + master_url = spark.get_master_url(master_ip) + client.send_master_info(master_url, master_ip) + + +@when('client.joined') +@when_not('spark.started') +def client_should_stop(client): + if is_state('leadership.is_leader'): + client.clear_spark_started() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/scripts/sparkpi.sh ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/scripts/sparkpi.sh b/bigtop-packages/src/charm/spark/layer-spark/scripts/sparkpi.sh new file mode 100755 index 0000000..3a34549 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/scripts/sparkpi.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -eu + +echo "Running SparkPi" +spark-submit --class org.apache.spark.examples.SparkPi /usr/lib/spark/lib/spark-examples-*.jar 10 +echo "" http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/tests/01-basic-deployment.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/tests/01-basic-deployment.py b/bigtop-packages/src/charm/spark/layer-spark/tests/01-basic-deployment.py new file mode 100755 index 0000000..f4022e1 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/tests/01-basic-deployment.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import amulet + + +class TestDeploy(unittest.TestCase): + """ + Trivial deployment test for Apache Spark. + """ + + def test_deploy(self): + self.d = amulet.Deployment(series='trusty') + self.d.add('spark', 'spark') + self.d.setup(timeout=900) + self.d.sentry.wait(timeout=1800) + self.unit = self.d.sentry['spark'][0] + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/tests/02-smoke-test.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/tests/02-smoke-test.py b/bigtop-packages/src/charm/spark/layer-spark/tests/02-smoke-test.py new file mode 100755 index 0000000..bb179a2 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/tests/02-smoke-test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import amulet + + +class TestDeploy(unittest.TestCase): + """ + Deployment and smoke-test of Apache Spark. + """ + @classmethod + def setUpClass(cls): + cls.d = amulet.Deployment(series='trusty') + cls.d.add('openjdk', 'openjdk') + cls.d.add('spark', 'spark') + cls.d.relate('spark:java', 'openjdk:java') + cls.d.setup(timeout=900) + cls.d.sentry.wait(timeout=1800) + + def test_deploy(self): + self.d.sentry.wait_for_messages({ + "spark": "ready (standalone - master)", + }) + spark = self.d.sentry['spark'][0] + smk_uuid = spark.action_do("smoke-test") + output = self.d.action_fetch(smk_uuid, full_output=True) + assert "completed" in output['status'] + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/tests/03-scale-standalone.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/tests/03-scale-standalone.py b/bigtop-packages/src/charm/spark/layer-spark/tests/03-scale-standalone.py new file mode 100755 index 0000000..38f1290 --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/tests/03-scale-standalone.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import amulet +import time + + +class TestScaleStandalone(unittest.TestCase): + """ + Test scaling of Apache Spark in standalone mode. + """ + @classmethod + def setUpClass(cls): + cls.d = amulet.Deployment(series='trusty') + cls.d.add('sparkscale', 'spark', units=3) + cls.d.add('openjdk', 'openjdk') + cls.d.relate('openjdk:java', 'sparkscale:java') + cls.d.setup(timeout=1800) + cls.d.sentry.wait(timeout=1800) + + @classmethod + def tearDownClass(cls): + cls.d.remove_service('sparkscale') + + def test_scaleup(self): + """ + Wait for all three spark units to agree on a master. + Remove the master. + Check that all units agree on the same new master. + """ + print("Waiting for units to become ready.") + self.d.sentry.wait_for_messages({"sparkscale": ["ready (standalone - master)", + "ready (standalone)", + "ready (standalone)"]}, timeout=900) + + print("Waiting for units to agree on master.") + time.sleep(60) + + spark0_unit = self.d.sentry['sparkscale'][0] + spark1_unit = self.d.sentry['sparkscale'][1] + spark2_unit = self.d.sentry['sparkscale'][2] + (stdout0, errcode0) = spark0_unit.run('grep spark.master /etc/spark/conf/spark-defaults.conf') + (stdout1, errcode1) = spark1_unit.run('grep spark.master /etc/spark/conf/spark-defaults.conf') + (stdout2, errcode2) = spark2_unit.run('grep spark.master /etc/spark/conf/spark-defaults.conf') + # ensure units agree on the master + assert stdout0 == stdout2 + assert stdout1 == stdout2 + + master_name = '' + for unit in self.d.sentry['sparkscale']: + (stdout, stderr) = unit.run("pgrep -f \"[M]aster\"") + lines = len(stdout.split('\n')) + if lines > 0: + master_name = unit.info['unit_name'] + print("Killin master {}".format(master_name)) + self.d.remove_unit(master_name) + break + + print("Waiting for the cluster to select a new master.") + time.sleep(60) + self.d.sentry.wait_for_messages({"sparkscale": ["ready (standalone - master)", + "ready (standalone)"]}, timeout=900) + + spark1_unit = self.d.sentry['sparkscale'][0] + spark2_unit = self.d.sentry['sparkscale'][1] + (stdout1, errcode1) = spark1_unit.run('grep spark.master /etc/spark/conf/spark-defaults.conf') + (stdout2, errcode2) = spark2_unit.run('grep spark.master /etc/spark/conf/spark-defaults.conf') + # ensure units agree on the master + assert stdout1 == stdout2 + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/tests/10-test-ha.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/tests/10-test-ha.py b/bigtop-packages/src/charm/spark/layer-spark/tests/10-test-ha.py new file mode 100755 index 0000000..6f5c6ff --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/tests/10-test-ha.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import amulet +import time +import unittest +import requests + + +class TestDeployment(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.d = amulet.Deployment(series='trusty') + cls.d.add('sparkha', 'spark', units=3) + cls.d.add('openjdk', 'openjdk') + cls.d.add('zk', 'zookeeper') + cls.d.expose('sparkha') + cls.d.relate('openjdk:java', 'sparkha:java') + cls.d.relate('zk:zookeeper', 'sparkha:zookeeper') + try: + cls.d.relate('zk:java', 'openjdk:java') + except ValueError: + # No need to related older versions of the zookeeper charm + # to java. + pass + cls.d.setup(timeout=1800) + cls.d.sentry.wait(timeout=1800) + + @classmethod + def tearDownClass(cls): + cls.d.remove_service('sparkha') + + def test_master_selected(self): + ''' + Wait for all three spark units to agree on a master leader. + Remove the leader unit. + Check that a new leader is elected. + ''' + self.d.sentry.wait_for_messages({"sparkha": ["ready (standalone - HA)", + "ready (standalone - HA)", + "ready (standalone - HA)"]}, timeout=900) + # Give some slack for the spark units to elect a master + time.sleep(60) + master = '' + masters_count = 0 + for unit in self.d.sentry['sparkha']: + ip = unit.info['public-address'] + url = 'http://{}:8080'.format(ip) + homepage = requests.get(url) + if 'ALIVE' in homepage.text: + masters_count += 1 + master = unit.info['unit_name'] + else: + assert 'STANDBY' in homepage.text + + assert masters_count == 1 + + print("Removing master: {} ".format(master)) + self.d.remove_unit(master) + time.sleep(120) + + self.d.sentry.wait_for_messages({"sparkha": ["ready (standalone - HA)", + "ready (standalone - HA)"]}, timeout=900) + + masters_count = 0 + for unit in self.d.sentry['sparkha']: + ip = unit.info['public-address'] + url = 'http://{}:8080'.format(ip) + homepage = requests.get(url) + if 'ALIVE' in homepage.text: + print("New master is {}".format(unit.info['unit_name'])) + masters_count += 1 + else: + assert 'STANDBY' in homepage.text + + assert masters_count == 1 + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/tests/tests.yaml ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/tests/tests.yaml b/bigtop-packages/src/charm/spark/layer-spark/tests/tests.yaml new file mode 100644 index 0000000..3b6ce3e --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/tests/tests.yaml @@ -0,0 +1,3 @@ +reset: false +packages: + - amulet http://git-wip-us.apache.org/repos/asf/bigtop/blob/f5e89f4e/bigtop-packages/src/charm/spark/layer-spark/wheelhouse.txt ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/spark/layer-spark/wheelhouse.txt b/bigtop-packages/src/charm/spark/layer-spark/wheelhouse.txt new file mode 100644 index 0000000..183242f --- /dev/null +++ b/bigtop-packages/src/charm/spark/layer-spark/wheelhouse.txt @@ -0,0 +1 @@ +charms.benchmark>=1.0.0,<2.0.0
