publications/index.html

1056 lines
30 KiB
HTML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-US">
<head>
<title>Scaling OpenStack Development: Continuous Integration Overview</title>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 1st November 2003), see www.w3.org" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="copyright" content=
"Copyright &#169; 2005-2010 W3C (MIT, ERCIM, Keio)" />
<meta name="duration" content="60" />
<meta name="font-size-adjustment" content="0" />
<link rel="stylesheet" href="styles/slidy.css" type="text/css" />
<link rel="stylesheet" href="styles/openstack.css" type="text/css" />
<script src="scripts/slidy.js" charset="utf-8" type="text/javascript"></script>
<script src="scripts/jquery-1.7.2.min.js" charset="utf-8" type="text/javascript">
</script>
<script src="scripts/raphael-min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div class="background"><img alt="" id="head-icon"
src="graphics/openstack-cloud-software-horizontal-small.png" /></div>
<div class="slide cover titlepage">
<!-- hidden style graphics to ensure they are saved with other content -->
<img class="hidden" src="graphics/bullet.png" alt="" />
<img class="hidden" src="graphics/fold.gif" alt="" />
<img class="hidden" src="graphics/unfold.gif" alt="" />
<img class="hidden" src="graphics/fold-dim.gif" alt="" />
<img class="hidden" src="graphics/nofold-dim.gif" alt="" />
<img class="hidden" src="graphics/unfold-dim.gif" alt="" />
<img class="hidden" src="graphics/bullet-fold.gif" alt="" />
<img class="hidden" src="graphics/bullet-unfold.gif" alt="" />
<img class="hidden" src="graphics/bullet-fold-dim.gif" alt="" />
<img class="hidden" src="graphics/bullet-nofold-dim.gif" alt="" />
<img class="hidden" src="graphics/bullet-unfold-dim.gif" alt="" />
<img src="graphics/openstack-cloud-software-vertical-large.png" alt="OpenStack logo" class="cover" />
<h1>Scaling OpenStack Development<br />
<span class="smaller">Continuous Integration Overview</span></h1>
<hr />
<div class="smaller">Monty Taylor
&lt;<a href="mailto:mordred@inaugust.com">mordred@inaugust.com</a>&gt;</div>
<div class="smaller">James E. Blair
&lt;<a href="mailto:jeblair@openstack.org">jeblair@openstack.org</a>&gt;</div>
<div class="smaller">Jeremy Stanley
&lt;<a href="mailto:fungi@yuggoth.org">fungi@yuggoth.org</a>&gt;</div>
<div class="smaller">Clark Boylan
&lt;<a href="mailto:cboylan@sapwetik.org">cboylan@sapwetik.org</a>&gt;</div>
<div class="smaller">Elizabeth K. Joseph
&lt;<a href="mailto:lyz@princessleia.com">lyz@princessleia.com</a>&gt;</div>
</div>
<div class="slide">
<h1>OpenStack</h1>
<p>Is open source software for building private and public clouds.</p>
<center>
<img src="images/openstack-software-diagram.png"/>
</center>
</div>
<div class="slide">
<h1>Projects</h1>
<div style="float:left;width:50%"><ul>
<li>Servers<ul>
<li>nova (compute)</li>
<li>swift (object storage)</li>
<li>glance (image service)</li>
<li>keystone (identity service)</li>
<li>neutron (network service)</li>
<li>cinder (volume service)</li>
<li>heat (orchestration)</li>
<li>ceilometer (measurement)</li>
<li>horizon (dashboard)</li>
<li>trove (databases)</li>
<li>sahara (hadoop)</li>
<li class="gray">ironic (bare metal)</li>
<li class="gray">marconi (message queueing)</li>
</ul></li>
</ul></div>
<div style="float:left;width:50%"><ul>
<li>Client libraries<ul>
<li>python-novaclient</li>
<li>python-swiftclient</li>
<li>python-glanceclient</li>
<li>python-keystoneclient</li>
<li>python-neutronclient</li>
<li>python-cinderclient</li>
<li>python-heatclient</li>
<li>python-ceilometerclient</li>
<li>python-openstackclient</li>
<li>python-troveclient</li>
<li>python-saharaclient</li>
<li class="gray">python-ironicclient</li>
<li class="gray">python-marconiclient</li>
</ul></li>
</ul></div>
<div class="indent"><a href="https://wiki.openstack.org/wiki/Projects">https://wiki.openstack.org/wiki/Projects</a></div>
</div>
<div class="slide">
<h1>Programs/Horizontal Efforts</h1>
<div style="float:left;width:50%"><ul>
<li>Documentation<ul>
<li>installation guides</li>
<li>operations manuals</li>
<li>API references</li>
<li>howtos and tutorials</li>
</ul></li>
<li>Infrastructure<ul>
<li>continuous integration systems</li>
<li>developer community resources</li>
</ul></li>
</ul></div>
<div style="float:left;width:50%"><ul>
<li>Oslo<ul>
<li>common libraries</li>
<li>copied code incubation</li>
</ul></li>
<li>Quality Assurance<ul>
<li>integration testing</li>
<li>upgrade testing</li>
</ul></li>
<li>Release Management</li>
<li>Translation/Internationalization</li>
<li>Vulnerability Management</li>
</ul></div>
</div>
<div class="slide">
<h1>Release Management</h1>
<ul>
<li>Time Based Releases</li>
<li>Six Month Cadence<ul>
<li>Similar to Ubuntu and Fedora Release Schedules</li>
</ul>
<li>Design summits each cycle</li>
<li>Continuously Open Trunk<ul>
<li>Develop directly on master</li>
</ul>
<li>Intra-Cycle Milestone Releases</li>
<li>Post-Release Stable Branches</li>
</ul>
</div>
<div class="slide">
<h1>Contributors</h1>
<img style="float:right; margin-right:24pt" src="images/contributor-pie-chart.png"/>
<ul>
<li>Unaffiliated individuals</li>
<li>Commercial entities</li>
<li>Nonprofit organizations</li>
<li>National and local governments</li>
<li>Number, quality, and area of contributions can change daily</li>
</ul>
</div>
<div class="slide">
<h1>Consistent Tooling</h1>
<ul>
<li>Minimize meta-development</li>
<li>Process divergence == wasted developer time</li>
<li>Lowers onboarding time</li>
<li>Consolidate tool development</li>
<li>Minimize project-specific weird build crud</li>
</ul>
</div>
<div class="slide">
<h1>Developer Infrastructure</h1>
<small>
<div style="float:left;width:33%"><ul>
<li>Code review and VCS<ul>
<li>Gerrit</li>
<li>Git</li>
<li>git-review</li>
<li>reviewday</li>
</ul></li>
<li>Test/build automation<ul>
<li>devstack-gate</li>
<li>Nodepool</li>
<li>Jenkins</li>
<li>JJB</li>
<li>Zuul</li>
</ul></li>
<li>Repository mirrors<ul>
<li>Apache</li>
<li>Cgit</li>
<li>GitHub</li>
<li>PyPI packages</li>
</ul></li>
</ul></div>
<div style="float:left;width:33%"><ul>
<li>Job logs, artifacts<ul>
<li>logs site</li>
<li>Logstash</li>
</ul></li>
<li>Documentation/publication<ul>
<li>Sphinx</li>
<li>Wiki</li>
</ul></li>
<li>Releases/pre-releases<ul>
<li>PyPI uploads</li>
<li>tarballs</li>
</ul></li>
<li>IRC<ul>
<li>eavesdrop</li>
<li>gerritbot</li>
<li>meetbot</li>
<li>statusbot</li>
</ul></li>
</ul></div>
<div style="float:left;width:33%"><ul>
<li>Project blogs<ul>
<li>Foundation</li>
<li>Planet</li>
</ul></li>
<li>Collaboration<ul>
<li>Mailing lists</li>
<li>Lodgeit paste</li>
<li>Etherpad</li>
</ul></li>
<li>Authentication<ul>
<li>CLA</li>
<li>membership</li>
<li>Launchpad SSO</li>
</ul></li>
<li>Feature/bug tracking<ul>
<li>LP blueprints</li>
<li>LP bugs</li>
</ul></li>
<!-- TODO: add transifex? -->
</ul></div>
</small>
</div>
<div class="slide">
<h1>System Administration is in Git</h1>
<ul>
<li>All system administration performed via code review</li>
<li>Anyone on the internet can look at our changes</li>
<li>Anyone can sign up for a code review account</li>
<li>Anyone can write and propose changes</li>
<li>https://git.openstack.org/cgit/openstack-infra/config/tree/</li>
</ul>
</div>
<div class="slide">
<h1>Development Environment</h1>
<ul>
<li>Python<ul>
<li>CentOS (2.6), Ubuntu LTS (2.7, 3.3, pypy)</li>
<li>PEP-8 standards</li>
<li>Oslo (common libraries)</li>
<li>virtualenv/pip/tox</li>
</ul></li>
<li>Freenode IRC (#openstack-dev, #openstack-meeting)</li>
<li>DevStack</li>
<li>Tests run on all newly submitted changes</li>
<li>Code merges are gated on tests</li>
</ul>
</div>
<div class="slide">
<h1>Project Gating</h1>
<img style="float:right; margin-right:24pt" src="images/jobs-launched.png"/>
<!--
http://graphite.openstack.org/render/?from=00%3A00_20130601&fgcolor=000000&title=Jobs%20Launched%20%28per%20Day%29&_t=0.7862690862083328&height=300&bgcolor=ffffff&width=400&showTarget=alias%28summarize%28sumSeries%28stats_counts.zuul.job.*%29%2C%271h%27%29%2C%27All%20Jobs%27%29&_salt=1373041766.855&target=alias%28summarize%28sumSeries%28stats_counts.zuul.job.*%29%2C%271d%27%29%2C%27All%20Jobs%27%29&until=23%3A59_20130630
-->
<ul>
<li>Ensures Code Quality</li>
<li>Protects developers<ul>
<li>Devs always start from working code</li>
</ul><li>Protects tree<ul>
<li>Bad code doesn't land</li>
</ul><li>Egalitarian<ul>
<li>Process is the same for everyone</li>
<li>Process is transparent</li>
<li>Process is automated</li>
</ul></ul>
</ul>
</div>
<div class="slide">
<h1>Everything Is Automated</h1>
<center>
<img src="images/status.png"/>
</center>
</div>
</div>
<div class="slide">
<h1>CI Workflow</h1>
<center>
<img src="images/openstack-ci-workflow.png"/>
</center>
</div>
<div class="slide">
<h1>Process Flow</h1>
<center>
<img src="images/contribution-path.png"/>
</center>
</div>
<div class="slide">
<h1>Gerrit</h1>
<ul>
<li>Developed by Google for Android</li>
<li>Stand-alone patch review system</li>
<li>Integration points: hooks, JSON queries, event-stream</li>
<li>Extensible review categories, default: Verified, Code-Review</li>
<li>REST API</li>
</ul>
</div>
<div class="slide">
<h1>Gerrit</h1>
<center>
<img style="margin-top:2em" src="images/gerrit.png"/>
</center>
</div>
<div class="slide">
<h1>Gerrit Diff View</h1>
<center>
<img style="margin-top:2em" src="images/gerrit-diff.png"/>
</center>
</div>
<div class="slide">
<h1>Bug Integration - Launchpad</h1>
<center>
<img style="margin-top:2em" src="images/lp-bug.png"/>
</center>
</div>
<div class="slide">
<h1>Approved Reviews</h1>
<center>
<img style="margin-top:2em"src="images/gerrit-approved.png"/>
</center>
</div>
<div class="slide">
<h1>Git Review</h1>
<ul>
<li>External Git subcommand</li>
<li>Developers can easily incorporate code review into git workflow</li>
<li>Zero-configuration</li>
<li>Can be used for any project, being adopted by other projects</li>
</ul>
<pre>
corvus@shiprock:~/git/neutron$ git commit -a
[new-versionpy ddf1dce] Base version.py on glance.
3 files changed, 28 insertions(+), 107 deletions(-)
delete mode 100644 version.py
corvus@shiprock:~/git/neutron$ git review
remote: Resolving deltas: 0% (0/3)
remote:
remote: New Changes:
remote: https://review.openstack.org/3072
remote:
To ssh://corvus@review.openstack.org:29418/openstack/neutron.git
* [new branch] HEAD -> refs/for/master/bug/916018
</pre>
</div>
<div class="slide">
<h1>Types of Tests</h1>
<ul>
<li>Unit tests<ul>
<li>Should be run in virtual environments, may run in operating systems</li>
<li>Fairly quick and easy for a developer to run</li>
</ul></li>
<li>Integration tests<ul>
<li>May be able to run on virtual servers, should run on real servers</li>
<li>Difficult and time-consuming for a developer to run</li>
</ul></li>
</li>
</ul>
</div>
<div class="slide">
<h1>Specific Challenges/Solutions</h1>
<ul>
<li>Testing effect of merging change</li>
<li>Using cloud builders</li>
<li>Large numbers of similar projects</li>
<li>Disparate hardware configurations</li>
</ul>
</div>
<div class="slide">
<h1>Gerrit Git Prep</h1>
<ul>
<li>Test the result of the change, not the change</li>
<li>openstack-infra/config:modules/openstack_project/files/slave_scripts/gerrit-git-prep.sh</li>
<li>For each gerrit change:
<ul>
<li>grabs target branch</li>
<li>cleans tree</li>
<li>merges change to be tested</li>
</ul>
</li>
</ul>
</div>
<div class="slide">
<h1>Interrelated Integration Testing</h1>
<ul>
<li>devstack-gate &amp; Nodepool<ul>
<li>boot fresh servers</li>
<li>run DevStack</li>
<li>run integration tests</li>
</ul></li>
<li>Gate proposed change against current state of other projects</li>
</ul>
</div>
<div class="slide">
<h1>Devstack-Gate Problems</h1>
<ul>
<li>Tests are slow</li>
<li>Cloud API calls can fail</li>
<li>External services are unreliable</li>
</ul>
</div>
<div class="slide">
<h1>Devstack-Gate Solutions</h1>
<ul>
<li>Create a new node</li>
<li>Pre-fetch all needed packages, repos</li>
<li>Snapshot to cloud image</li>
<li>Maintain a pool of cloud nodes</li>
<li>Slave can only be used for one test run</li>
<li>Python and shell scripts run as jobs</li>
<li>Partition tests and run them in parallel</li>
</ul>
</div>
<div class="slide">
<h1>Zuul</h1>
<ul>
<li>A general purpose trunk gating system</li>
<li>Interfaces with Gerrit and Jenkins</li>
<li>Flexible configuration allows for many kinds of project automation</li>
<li>Allows parallel testing of serialized changes</li>
</ul>
</div>
<div class="slide">
<h1>Bottlenecking</h1>
<ul>
<li>Serialize changes across all projects</li>
<li>Speculative execution of tests</li>
<li>Run in parallel in order triggered</li>
<li>Assume success</li>
<li>Start over on failure</li>
</ul>
</div>
<div class="slide" id="zuul">
<h1>Zuul Simulation</h1>
<script>
var green = "#00bb44";
var red = "#bb0000";
var light_green = "#8dbb9e";
var light_red = "#bb7777";
function Change(paper, name, number, x, y, delay)
{
this.set = paper.set();
this.x = x;
this.y = y;
this.text = paper.text(x, y, name+'\n#'+number);
this.set.push(this.text);
this.text.attr({"font-size": 16});
width = this.text.getBBox().width+16;
height = this.text.getBBox().height+12;
this.left = x-width/2;
this.right = x+width/2;
this.top = y-height/2;
this.bottom = y+height/2;
this.rect = paper.rect(this.left, this.top, width, height, 2);
this.set.push(this.rect);
this.rect.attr({fill: "#ccc", stroke: "#bbb"});
this.rect.toBack();
this.set.attr({opacity: 0});
this.set.animate(Raphael.animation({opacity: 1}, 500, 'linear').delay(delay));
this.hide = function ()
{
var set = this.set;
var remove = function () { set.remove(); };
this.set.animate({opacity: 0}, 1000, 'linear', remove);
}
this.merge = function(x, y)
{
this.set.toFront();
this.text.animate(Raphael.animation({opacity: 0}, 500, 'linear'));
this.rect.animate(Raphael.animation(
{width: 18, height: 18, r: 9, x: x, y: y, stroke: green},
1000).delay(500));
}
this.move = function(x, y, delay)
{
var width = this.text.getBBox().width+16;
var height = this.text.getBBox().height+12;
this.x = x;
this.y = y;
this.left = x-width/2;
this.right = x+width/2;
this.top = y-height/2;
this.bottom = y+height/2;
this.text.animate(Raphael.animation(
{x: x, y: y}, 500, 'linear').delay(delay));
this.rect.animate(Raphael.animation(
{x: this.left, y: this.top},
500, 'linear').delay(delay));
}
this.provisionalResult = function(success)
{
if (success)
{
color = light_green;
} else {
color = light_red;
}
this.rect.animate({fill: color}, 500);
}
this.result = function(success)
{
if (success)
{
color = green;
} else {
color = red;
}
this.rect.animate({fill: color}, 500);
}
this.reset = function(success)
{
this.rect.animate({fill: "#ccc"}, 500);
}
}
function linePath(x1, y1, x2, y2) {
return "M"+x1+","+y1+"L"+x2+","+y2;
}
function Dependency(paper, source, target, delay)
{
this.set = paper.set();
this.source = source;
this.target = target;
startx = this.source.right + 8;
starty = this.source.y;
tipx = this.target.left - 8;
tipy = this.target.y;
path = "M" + (startx) + "," + starty;
path = path + "L" + (tipx-15) + "," + tipy;
this.line = paper.path(path);
this.set.push(this.line);
this.line.attr("stroke-width", "2");
this.line.attr("fill", "#000");
this.line.attr("stroke", "#000");
path = "M" + (tipx - 20) + "," + (tipy - 10);
path = path + "L" + (tipx) + "," + (tipy);
path = path + "L" + (tipx - 20) + "," + (tipy + 10);
path = path + "Q" + (tipx - 12) + "," + (tipy);
path = path + "," + (tipx - 20) + "," + (tipy - 10);
path = path + "Z";
this.arrow = paper.path(path);
this.set.push(this.arrow);
this.arrow.attr("fill", "#000");
this.arrow.attr("stroke", "#000");
this.set.attr({opacity: 0});
this.set.animate(Raphael.animation({opacity: 1}, 500, 'linear').delay(delay));
this.hide = function ()
{
var set = this.set;
var remove = function () { set.remove(); };
this.set.animate({opacity: 0}, 1000, 'linear', remove);
}
}
function TestSeries(paper, change, tests)
{
this.x = change.x - 20;
this.y = change.bottom + 6;
offset = 40; // the "merge" test length
height = 0;
for (test in tests)
{
len = tests[test][0];
if (len > height)
{
height = len;
}
}
this.width = (1+10*tests.length)
this.height = offset + height + 10;
this.set = paper.set();
startpath = "M" + this.x + "," + this.y;
line = paper.path(startpath+"l0," + offset);
this.set.push(line);
startpath = "M" + this.x + "," + (this.y + offset);
line = paper.path(startpath+"l0," + (this.height - offset));
this.set.push(line);
line.attr("stroke-dasharray", "-");
line.attr("stroke", green);
line = paper.path(startpath + "l" + (1+10*tests.length) + ",0");
this.set.push(line);
var global_success = true;
curx = this.x;
cury = this.y + offset;
for (var test in tests)
{
len = tests[test][0];
success = tests[test][1];
curx = curx + 10;
line = paper.path("M" + curx + "," + cury + "l0," + len);
this.set.push(line);
line = paper.path("M" + curx + "," + cury + "m0," + len +
"l0," + (this.height-offset-len));
this.set.push(line);
line.attr("stroke-dasharray", "-");
if (success) {
line.attr("stroke", green);
} else {
line.attr("stroke", red);
global_success = false;
}
}
var complete = function ()
{
change.provisionalResult(global_success);
}
this.rect = paper.rect(this.x-2, this.y-2, this.width+4, this.height+2);
this.rect.attr({fill: "#fff", stroke: "#fff"})
this.rect.animate({y: this.y+this.height+2, height: 0}, 36*this.height, //36
'linear', complete);
this.hide = function ()
{
var set = this.set;
var remove = function () { set.remove(); };
this.set.animate({opacity: 0}, 2000, 'linear', remove);
}
this.move = function (delay)
{
var old_x = this.x;
var old_y = this.y;
this.x = change.x - 20;
this.y = change.bottom + 6;
var dX = this.x - old_x;
var dY = this.y - old_y;
this.set.forEach(function (line) {
line.animate(Raphael.animation(
{transform: 't' + dX + ',' + dY}, 500, 'linear').delay(delay));
});
}
}
var zuul_simulation = {
paper: null,
state: 0,
finished: false,
running: false,
setup: function()
{
this.paper = Raphael(200, 200, 760, 400);
this.running = true;
this.finished = false;
this.state = 0;
},
teardown: function()
{
if (this.paper != null) {
this.paper.remove();
this.paper = null;
}
this.finished = true;
this.running = false;
},
next: function()
{
this.state = this.state + 1;
if (this.state == 1)
{
head_label = this.paper.text(520, 385, "HEAD");
head_label.attr({"font-size": 16});
nova_label = this.paper.text(520, 300, "nova");
nova_label.attr({"font-size": 16});
nova_label.attr({x:this.paper.width-(nova_label.getBBox().width/2)-10});
nova_end = [(this.paper.width-nova_label.getBBox().width-16), 300];
nova_line = this.paper.path(linePath(520, 300, nova_end[0], nova_end[1]))
nova_line.attr({"stroke-width": 2, fill: "#000", stroke: "#000"});
nova_head = this.paper.circle(520, 300, 9);
nova_head.attr({fill: "#5069c9", stroke:"#5069c9"});
keystone_label = this.paper.text(520, 360, "keystone");
keystone_label.attr({"font-size": 16});
keystone_label.attr({x:this.paper.width-(keystone_label.getBBox().width/2)-10});
keystone_end = [(this.paper.width-keystone_label.getBBox().width-16), 360];
keystone_line = this.paper.path(linePath(520, 360, keystone_end[0], keystone_end[1]));
keystone_line.attr({"stroke-width": 2, fill: "#000", stroke: "#000"});
keystone_head = this.paper.circle(520, 360, 9);
keystone_head.attr({fill: "#eead5f", stroke:"#eead5f"});
}
if (this.state == 2)
{
change1 = new Change(this.paper, 'nova', '1', 480, 40, 0);
change2 = new Change(this.paper, 'nova', '2', 340, 40, 1000);
dep21 = new Dependency(this.paper, change2, change1, 1800);
change3 = new Change(this.paper, 'keystone', '3', 180, 40, 2000);
dep32 = new Dependency(this.paper, change3, change2, 2800);
change4 = new Change(this.paper, 'nova', '4', 40, 40, 3000);
dep43 = new Dependency(this.paper, change4, change3, 3800);
}
if (this.state == 3) {
change1_test = new TestSeries(this.paper, change1, [[80, 1], [60, 1], [95, 1]]);
change2_test = new TestSeries(this.paper, change2, [[80, 1], [60, 1], [90, 1]]);
change3_test = new TestSeries(this.paper, change3, [[50, 1], [30, 0]]);
change4_test = new TestSeries(this.paper, change4, [[80, 1], [60, 1], [85, 0]]);
}
if (this.state == 4) {
change4_test.hide();
dep43.hide();
}
if (this.state == 5) {
dep32.hide();
change3.move(180, 80, 200);
change3_test.move(change3, 200);
dep32 = new Dependency(this.paper, change3, change2, 1000);
}
if (this.state == 6) {
change4.hide();
change4 = new Change(this.paper, 'nova', '4', 40, 40, 1000);
dep42 = new Dependency(this.paper, change4, change2, 1000);
}
if (this.state == 7) {
change4_test = new TestSeries(this.paper, change4, [[80, 1], [60, 1], [85, 1]]);
}
if (this.state == 8) {
change1_test.hide();
change1.result(1);
}
if (this.state == 9) {
change1.merge(480, 290);
dep21.hide();
setTimeout(function() {
nova_line.attr({path: linePath(490, 300, nova_end[0], nova_end[1])})
} , 2000);
}
if (this.state == 10) {
change2_test.hide();
change2.result(1);
}
if (this.state == 11) {
change2.merge(450, 290);
dep32.hide();
dep42.hide();
setTimeout(function() {
nova_line.attr({path: linePath(460, 300, nova_end[0], nova_end[1])})
} , 2000);
}
if (this.state == 12) {
change3_test.hide();
change3.result(0);
}
if (this.state == 13) {
change3.hide();
}
if (this.state == 14) {
change4_test.hide();
change4.result(1);
}
if (this.state == 15) {
change4.merge(420, 290);
setTimeout(function() {
nova_line.attr({path: linePath(430, 300, nova_end[0], nova_end[1])})
} , 2000);
}
if (this.state == 16) {
this.finished = true;
}
},
}; /* zuul_simulation */
$(document).ready(function() {
w3c_slidy.zuul_orig_show_slide = w3c_slidy.show_slide;
var advance = function() {
zuul_simulation.next();
if (zuul_simulation.finished) {
$("#zuul").unbind('click');
$(document).unbind('keypress');
w3c_slidy.mouse_click_enabled = true;
}
};
var show_slide = function (slide) {
w3c_slidy.zuul_orig_show_slide(slide);
if ($("#zuul")[0] === slide) {
if (!zuul_simulation.running) {
zuul_simulation.setup();
zuul_simulation.next();
w3c_slidy.mouse_click_enabled = false;
$("#zuul").bind('click', advance);
$(document).bind('keypress', function(event) {
var key = null;
if (window.event) {
key = window.event.keyCode;
} else if (event.which) {
key = event.which;
}
// 'x' key
if (key == 120) {
advance();
}
});
}
} else {
if (zuul_simulation.running) {
$("#zuul").unbind('click');
$(document).unbind('keypress');
w3c_slidy.mouse_click_enabled = true;
zuul_simulation.teardown();
}
}
};
w3c_slidy.show_slide = show_slide;
}); /* document ready */
</script>
</div>
<div class="slide">
<h1>Zuul Gate Pipeline</h1>
<pre>
pipelines:
- name: gate
manager: DependentPipelineManager
precedence: high
trigger:
gerrit:
- event: comment-added
approval:
- approved: 1
start:
gerrit:
verified: 0
success:
gerrit:
verified: 2
submit: true
failure:
gerrit:
verified: -2
</pre>
</div>
<div class="slide">
<h1>Zuul Project Configuration</h1>
<pre>
projects:
- name: openstack/nova
check:
- gate-nova-pep8
- gate-nova-python26
- gate-nova-python27
- gate-tempest-devstack-vm
gate:
- gate-nova-pep8
- gate-nova-python26
- gate-nova-python27
- gate-tempest-devstack-vm
post:
- nova-branch-tarball
- nova-docs
- nova-upstream-translation-update
release:
- nova-tarball
- nova-docs
</pre>
</div>
<div class="slide">
<h1>Templated Jobs</h1>
<ul>
<li>TONS of consistent projects</li>
<li>Manage everything through Git and code review</li>
<li><a href="http://ci.openstack.org/jenkins-job-builder/">Jenkins Job Builder</a></li>
<li>Manage jobs as YAML files in Git</li>
<li>Jobs updated via Jenkins API</li>
<li>groupings of jobs supported</li>
</ul>
</div>
<div class="slide">
<h1>Example Job Template</h1>
<pre>
- job-template:
name: 'gate-{name}-pep8'
builders:
- gerrit-git-prep
- pep8:
github-org: '{github-org}'
project: '{name}'
publishers:
- console-log
</pre>
</div>
<div class="slide">
<h1>Apply Template</h1>
<pre>
- project:
name: nova
github-org: openstack
tarball-site: tarballs.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- gate-{name}-pep8
- translation-jobs
</pre>
</div>
<div class="slide">
<h1>Logs</h1>
<img style="float:right; margin-right:24pt" src="images/logstash-diagram-small.png"/>
<ul style="width: 20em">
<li>Massive amount of logs generated<ul>
<li>Terabytes of compressed logs per development cycle</li>
</ul></li>
<li>Filtered and normalized by LogStash</li>
<li>Recent logs indexed by ElasticSearch<ul>
<li>Searchable through Kibana and ElasticSearch API</li>
</ul></li>
<li>Driving automatic failure classification</li>
</ul>
</div>
<div class="slide">
<h1>Scaling Hardware Needs</h1>
<ul>
<li>Different vendors have different combinations of configurations</li>
<li><a
href="http://ci.openstack.org/third_party.html#the-jenkins-gerrit-trigger-plugin-way">http://ci.openstack.org/third_party.html#the-jenkins-gerrit-trigger-plugin-way</a></li><!-- TODO: update this URL -->
<li>Run their own Jenkins</li>
<li>Consume Gerrit event stream</li>
<li>Their Jenkins votes VRFY +1/-1</li>
<li>Our Zuul votes VRFY +2/-2</li>
<li>They can run their lab - we don't have to know anything about it</li>
</ul>
</div>
<div class="slide">
<h1>Multi-Master Jenkins</h1>
<ul>
<li>Horizontal Scaling</li>
<li>Redundancy</li>
<li>Uses the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Gearman+Plugin">Jenkins Gearman Plugin</a></li>
<li>Masters: jenkins01.openstack.org, jenkins02.o.o, jenkins03.o.o, etc..</li>
<li><a href="http://graphite.openstack.org/render/?from=-24hours&height=680&until=now&width=1034&bgcolor=ffffff&fgcolor=000000&target=color(alias(sumSeries(stats.gauges.nodepool.target.*.*.*.used),%20%27In%20Use%27),%20%276464ff%27)&title=Test%20Nodes&_t=0.605142645072192#1395182490817">600+ test nodes</a></div></li>
</ul>
</div>
<div class="slide">
<h1>Jenkins Slaves</h1>
<center>
<img style="margin-top:2em;" src="images/jenkins_in_use_slaves.png"/>
</center>
</div>
<div class="slide">
<h1>Elastic test nodes</h1>
<ul>
<li><a href="http://git.openstack.org/cgit/openstack-infra/nodepool/tree/">Nodepool</a></li>
<li>Automate management of test nodes</li>
<li>Registers nodes with Jenkins masters</li>
<li>Uses Jenkins ZeroMQ events for build status</li>
<li>Manages multiple openstack clouds</li>
</ul>
</div>
<div class="slide">
<h1>Elastic test nodes</h1>o
<center>
<img style="margin-top:2em;" src="images/elastic_test_nodes.png"/>
</center>
</div>
<div class="slide">
<h1>Thanks!</h1>
<p><img src="images/stack-o-pancakes-150x150.png"/>
<p>
These slides available at: <a href="http://docs.openstack.org/infra/publications/">http://docs.openstack.org/infra/publications/</a>
</p>
</div>
</body>
</html>