tag:blogger.com,1999:blog-2479641745081578592024-03-18T05:47:53.297-04:00Ed's tech cornerTalking about design & implementation of solutions using modern OSS frameworks, tools, open standards and cutting-edge technologyEduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.comBlogger39125tag:blogger.com,1999:blog-247964174508157859.post-39492406629372505172016-10-07T08:49:00.003-04:002016-10-07T08:49:23.926-04:00Spring Sandbox Welcome<h2>
Spring Sandbox Welcome</h2>
<br />
Just a set of examples of the state-of-the-art Spring ecosystem. Mostly Spring Boot and Spring Platform IO.<br />
<br />
Check it out @ <a href="https://github.com/eduardo-lago-aguilar/spring-sandbox">https://github.com/eduardo-lago-aguilar/spring-sandbox</a>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com10tag:blogger.com,1999:blog-247964174508157859.post-13182816723180197162016-07-28T14:03:00.003-04:002016-07-28T14:03:55.336-04:00<h2>
JAngular launched</h2>
<div>
This is set of matchers for TDD practicing developers using AngularJS + UI-Router. </div>
<div>
<br /></div>
<div>
<a href="https://github.com/aleph-engineering/jangular" target="_blank">JAngular</a></div>
<div>
<br /></div>
<div>
<br /></div>
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com3tag:blogger.com,1999:blog-247964174508157859.post-90458156378859606042014-04-11T08:55:00.000-04:002014-04-11T08:55:11.822-04:00How to install collectd & graphite on Ubuntu and CentOSThis post is a direct approach to install collectd & graphite on Ubuntu, by direct I mean using the available repositories:<br />
<br />
<b>Requeriments</b><br />
<br />
- Ubuntu 13.10 installed and configured with standard repositories<br />
- CentOS 6.3 + EPEL repository configured<br />
- Login as root<br />
<br />
<b>Ubuntu guide</b><br />
<br />
(i) Install collectd daemon:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># apt-get install collectd</span><br />
<br />
<i>(here you may modify the collectd config file and select the metrics to harvest)</i><br />
<br />
(ii) Install apache2 & graphite, disable default site and enable graphite site:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># apt-get install apache2 libapache2-mod-wsgi</span><br />
<span style="font-family: Courier New, Courier, monospace;"># apt-get install graphite-carbon graphite-web</span><br />
<br />
(iii) Config graphite site, disable default site and enable graphite site:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># cd /etc/apache2/sites-available/</span><br />
<span style="font-family: Courier New, Courier, monospace;"># ln -s /usr/share/graphite-web/apache2-graphite.conf</span><br />
<span style="font-family: Courier New, Courier, monospace;"># a2dissite 000-default</span><br />
<span style="font-family: Courier New, Courier, monospace;"># a2ensite apache2-graphite</span><br />
<br />
(iv) Initial graphite DB creation:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># cd /usr/share/pyshared/graphite</span><br />
<span style="font-family: Courier New, Courier, monospace;"># python manage.py syncdb</span><br />
<br />
(v) Configure carbon cache:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># nano /etc/default/graphite-carbon</span><br />
<br />
<i>(and change flag to true)</i><br />
<br />
<span style="font-family: Courier New, Courier, monospace;">CARBON_CACHE_ENABLED=true</span><br />
<br />
(vi) Setup carbon-cache to autostart, then start it:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># chkconfig --levels 235 carbon-cache on</span><br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/carbon-cache start</span><br />
<br />
(vii) Setup collectd to autostart:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># chkconfig --levels 235 collectd on</span><br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/collectd start</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
(viii) Restart apache2:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># service apache2 restart</span><br />
<div>
<br /></div>
<b>CentOS guide</b><br />
<br />
(i) Install & config collectd daemon:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># yum install collectd pyhton-bucky httpd</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"># cp /etc/collectd.conf /etc/collectd.conf.org</span><br />
<br />
<i>(here you may modify the collectd config file and select the sts to harvest)</i><br />
<br />
(ii) Install apache2 & graphite, config apache to autostart:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># yum install graphite-web python-carbon</span><br />
<span style="font-family: Courier New, Courier, monospace;"># chkconfig --levels 235 httpd on</span><br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/httpd start</span><br />
<br />
(iii) Initial graphite DB creation:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># cd /usr/lib/python2.6/site-packages/graphite</span><br />
<span style="font-family: Courier New, Courier, monospace;"># python manage.py syncdb</span><br />
<br />
(iv) configure carbon cache:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># chkconfig --levels 235 carbon-cache on</span><br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/carbon-cache start</span><br />
<br />
(v) restart apache:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/httpd restart</span><br />
<br />
(vii) Setup collectd to autostart:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"># chkconfig --levels 235 collectd on</span><br />
<span style="font-family: Courier New, Courier, monospace;"># /etc/init.d/collectd start</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<br /></div>
<div>
At this point you are already collecting metrics!</div>
<div>
Enjoy it!</div>
<div>
<br /></div>
<br />
<br />
<br />Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com1tag:blogger.com,1999:blog-247964174508157859.post-56980100692224890132013-08-09T07:50:00.004-04:002016-08-28T14:47:11.051-04:00Spring CDI Mocks: Separating Unit Tests and Integration Tests. The right things in the right place<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
<b>INTRO</b></div>
<div style="margin-bottom: 0in;">
In the last post I clearly became more
independently of Spring, not because I hate him, in fact I like
Spring a lot. But to postpone the choice of the <i>wiring/</i><i>infrastructure</i>
technology until the production stage, and to encourage
standardization and simplicity.
</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
Just for that? No!
</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
In many domains, all bleeding-edge Java
framework/toolkit seems to honor the JSR specifications, at the same
time JSR specifications very often receive continuous feedback from
concrete implementations, some examples are: JPA (JSR-220) with
Hibernate, Bean Validation (JSR-303) with Hibernate Validator and
Apache Beans Validator, and our main focus CDI (JSR-299) and Spring.
Spring actually honors CDI annotations, and if the leading framework
on DI (Spring) honors the standard, then there is a big chance for
both to be aligned with the future.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
As you may noticed I used Spring
[before] on the testing phase to wire up the SUT and dependencies
with a relative small complexity. Nevertheless simple and small is
better, that's why I'll show you how achieve a very similar wiring
scenario w/o Spring (in testing phase) with a more simple approach
IMO.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
There's an open question: <i>Do we
still need Spring? </i><i>Where does </i><i>it</i><i> fit?</i>
</div>
<div style="margin-bottom: 0in;">
The trivial answer is: Yes there's no a
DI framework as mature as Spring. It fits in the Integration-Test
(IT) phase very well.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<b>NOTE</b>: Take these things into
account when you read this post: 1) it's a simplified scenario, 2)
there MANY other phases besides of compilation, test, integration
tests, etc. where Spring also shines, 3) Spring is much more than an
DI framework.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<b>HANDS ON LAB</b></div>
<div style="margin-bottom: 0in;">
So we need to replace the wiring
benefits of Spring in the early testing stage (don't confuse with IT)
showed on the [last post] with a worthy substitute. What we mean with
worthy? Two things: 1) it solves the wiring at this stage, and 2)
it's easy to use and reproduce.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<b>Testing Phase</b></div>
<div style="margin-bottom: 0in;">
In the Java world many mocking
frameworks exists, I performed a research on this topic and found up
a mighty combination in the mocking area: PowerMock w/ Mockito.
</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
PowerMock is the nearest similar thing
to a silver bullet I have ever seen in the mocking area, you can
combine it with Mockito. By the other hand Mockito is very user
friendly, simple and easy to learn. I recommend both in combination
for more elaborated tests, and only Mockito for simple scenarios like
this.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<br /></div>
<i><b>pom.xml</b></i>
<br />
<pre class="xml" name="code"><dependency>
<groupid>org.mockito</groupid>
<artifactid>mockito-all</artifactid>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
To achieve the same goal simplifying
things we need to inject the SUT's dependencies:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<br /></div>
<i><b>EditorTest.java</b></i>
<br />
<pre class="java" name="code">package demos.sf.editor.test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import demos.sf.editor.SpellChecker;
import demos.sf.editor.impl.Editor;
@RunWith(MockitoJUnitRunner.class)
public class EditorTest {
@InjectMocks
private Editor editor;
@Mock
private SpellChecker spellChecker;
@Test
public void testPasteSuccess() {
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
}
@Test
public void testAddParagraphSpellIsChecked() {
editor.paste("Hello everybody!");
verify(spellChecker, only()).check("Hello everybody!");
}
}
</pre>
<br />
Observe the introduction of:
<br />
<br />
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
<i>@Mock</i>: annotates a dependency to be
mocked.</div>
<div style="margin-bottom: 0in;">
<i>@InjectMocks</i>: annotates the SUT to be
instantiated and its <i>@Mocks</i> dependencies to get injected.</div>
<div style="margin-bottom: 0in;">
<i>@RunWith(MockitoJUnitRunner.class)</i>:
uses the Mockito runner for tests.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
Therefore, there's no need for DI
framework (neither application context configuration file) in test
phase at least for scenarios like this. Of course the injection and
mocking capacities of Mockito are limited compared to Spring.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
</div>
<div style="margin-bottom: 0in;">
<b>IT Phase</b></div>
<div style="margin-bottom: 0in;">
In the IT phase you replace mock
objects with real ones, followed by a verification of the real
objects' combination performing as you expect. Let's show one of such
possible object graph where I use an English implementation for spell
checking, dictionary and tokenizer:
</div>
<div style="margin-bottom: 0in;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkH7a9INnmYkayoip8gWBVrYypjAM3VK8YzJ7zdBBM8u-qNkNSMe7fZ-cterC3PMriFs2Z6Ap7d2Lch9JELzMqjXmu4fGKG_S6Ua-s8_RfPzwX-FIVY-rQQ5My8_U5tPx7grCWPQx8egk_/s1600/texteditor_en.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkH7a9INnmYkayoip8gWBVrYypjAM3VK8YzJ7zdBBM8u-qNkNSMe7fZ-cterC3PMriFs2Z6Ap7d2Lch9JELzMqjXmu4fGKG_S6Ua-s8_RfPzwX-FIVY-rQQ5My8_U5tPx7grCWPQx8egk_/s320/texteditor_en.png" width="320" /></a></div>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
By replacing every mock for a real
object at time, you gradually introduce a full IT. You can achieve a
complete mock-replacement using a top-down approach, t<span style="font-style: normal;">here
may be other ways of course </span><span style="font-style: normal;">but
I took this one:</span></div>
<br />
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
(1) test <i>Editor</i> using
<i>SpellChecker</i>'s mock</div>
<div style="margin-bottom: 0in;">
(2) replace <i>SpellChecker</i>'s mock
for <i>EnglishSpellChecker </i> and use <i>Dictionary</i> and
<i>Tokenizer</i> mocks, then test <i>Editor</i></div>
<div style="margin-bottom: 0in;">
(3) replace <i>Dictionary</i>'s mock
for <i>OxfordDictionary, </i><span style="font-style: normal;">then
test </span><i>Editor</i></div>
<div style="margin-bottom: 0in;">
(4) replace <i>Tokenizer</i>'s mock for
<i>EnglishTokenizer, </i><span style="font-style: normal;">then test
</span><i>Editor</i></div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="font-style: normal; margin-bottom: 0in;">
Two new interfaces
appear in the <i>demos.sf.editor</i> package, they represent the
abstractions mentioned above:</div>
<br />
<br />
<div style="margin-bottom: 0in;">
<br /></div>
<pre class="java" name="code">public interface Dictionary {
String lookup(String word);
}
public interface Tokenizer {
Collection<string> tokenize(String text);
}
</string></pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="font-style: normal; margin-bottom: 0in;">
Three new classes
appear in the <i>demos.sf.editor.english</i> package, they represent
and English wiring combination:</div>
<pre class="java" name="code">@Named
public class EnglishSpellChecker implements SpellChecker {
@Resource
private Tokenizer tokenizer;
@Resource
private Dictionary dictionary;
public String check(String text) {
...
}
}
@Named
public class EnglishTokenizer implements Tokenizer {
public Collection<string> tokenize(String text) {
// TODO Auto-generated method stub
return null;
}
}
@Named
public class OxfordDictionary implements Dictionary {
public String lookup(String word) {
// TODO Auto-generated method stub
return null;
}
}
</string></pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="font-style: normal; margin-bottom: 0in;">
The beauty behind
is that out Editor remains the same:</div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<pre class="java" name="code">@Named
public class Editor {
@Getter
protected String text;
@Resource
private SpellChecker spellChecker;
public void paste(String cut) {
if (text == null) {
text = "";
}
text += cut;
spellChecker.check(cut);
}
}
</pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="font-style: normal; margin-bottom: 0in;">
And there is a
change for introducing and test for integration by only changing the
IT application context, observe the <i>component-scan</i> with
<i>demos.sf.editor.english</i> package which instantiates the desired
combination of beans:</div>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
it-applicationContext.xml
<br />
<pre class="xml" name="code"><beans>
<context:component-scan base-package="demos.sf.editor.impl"></context:component-scan>
<context:component-scan base-package="demos.sf.editor.english"></context:component-scan>
<alias alias="spellChecker" name="englishSpellChecker"></alias>
<alias alias="tokenizer" name="englishTokenizer"></alias>
<alias alias="dictionary" name="oxfordDictionary"></alias>
</beans>
</pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="font-style: normal; margin-bottom: 0in;">
There are
additional aliasing entries due to the fact that the injection is
performed on-resource-basis (by bean name), remember <i>@Resource</i> and
@Named annotations:</div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<pre class="java" name="code">@Named
public class Editor {
// the following injects a bean named spellChecker
@Resource
private SpellChecker spellChecker;
...
}
// the following produces a bean named englishSpellChecker
@Named
public class EnglishSpellChecker implements SpellChecker {
// the following injects a bean named tokenizer
@Resource
private Tokenizer tokenizer;
// the following injects a bean named dictionary
@Resource
private Dictionary dictionary;
...
}
// the following produces a bean named englishTokenizer
@Named
public class EnglishTokenizer implements Tokenizer {
...
}
// the following produces a bean named oxfordDictionary
@Named
public class OxfordDictionary implements Dictionary {
...
}
</pre>
You may want to inject based on type instead of name, it's easy to achieve by replacing the @Resource annotations with @Inject. Lets resume again the meaning of these annotations:<br />
<br />
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="margin-bottom: 0in;">
<i> * @Resource</i>:
</div>
<ul><ul>
<li><div style="margin-bottom: 0in;">
<b>Matching</b>: First matches by
name, then matches by type and filters by qualifiers if not
name-based match gets found.
</div>
</li>
<li><div style="margin-bottom: 0in;">
<b>Applicable to</b>: Fields,
Classes and Methods.
</div>
</li>
<li><div style="margin-bottom: 0in;">
<b>Package</b>: <i>javax.annotation</i>
P { margin-bottom: 0.08in; }A:link { }
</div>
</li>
</ul>
</ul>
<ul>
<li><div style="margin-bottom: 0in;">
<i>@Inject</i>:
</div>
<ul>
<li><div style="margin-bottom: 0in;">
<b>Matching</b>: First
matches by type, filters by qualifiers, then matches be name.
</div>
</li>
<li><div style="margin-bottom: 0in;">
<b>Applicable to</b>: Methods,
Constructors and Fields.
</div>
</li>
<li><div style="margin-bottom: 0in;">
<b>Package</b>: <i>javax.inject</i>
</div>
</li>
</ul>
</li>
</ul>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<ul>
<li><div style="margin-bottom: 0in;">
<i>@Named</i>:
</div>
<ul>
<li><div style="margin-bottom: 0in;">
<b>Description</b>: Its a
qualifier that denotes a named bean. Annotated classes are
considered for auto-detection on classpath scanning. It's similar
to Spring's <i>@Component</i>
</div>
</li>
<li><div style="margin-bottom: 0in;">
<b>Applicable to</b>: Classes and
Fields.
</div>
</li>
<li><b>Package</b>: <i>javax.inject</i>
<br />
</li>
</ul>
</li>
</ul>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div style="font-style: normal; margin-bottom: 0in;">
<b>How to change it
to Spanish?</b></div>
<div style="font-style: normal; margin-bottom: 0in;">
1- First implement
the three corresponding classes in <i>demos.sf.editor.</i><i>spanish</i>
package, they will represent the desired wiring combination:</div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<pre class="java" name="code">@Named
public class SpanishSpellChecker implements SpellChecker {
@Resource
private Tokenizer tokenizer;
@Resource
private Dictionary dictionary;
public String check(String text) {
...
}
}
@Named
public class SpanishTokenizer implements Tokenizer {
public Collection<string> tokenize(String text) {
// TODO Auto-generated method stub
return null;
}
}
@Named
public class RAEDictionary implements Dictionary {
public String lookup(String word) {
// TODO Auto-generated method stub
return null;
}
}
</string></pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;">Create
a new </span><span style="color: black;">IT application context, </span><span style="color: black;">or
change the existing one with a</span><span style="color: black;">
</span><span style="color: black;"><i>component-scan</i></span><span style="color: black;">
with </span><span style="color: black;"><i>demos.sf.editor.</i></span><span style="color: black;"><i>spanish</i></span><span style="color: black;">
package which instantiates the desired combination of </span><span style="color: black;">Spanish
</span><span style="color: black;">beans :</span><span style="color: black;">)</span></span></span></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<pre class="xml" name="code"><beans>
<context:component-scan base-package="demos.sf.editor.impl"></context:component-scan>
<context:component-scan base-package="demos.sf.editor.spanish"></context:component-scan>
<alias alias="spellChecker" name="spanishSpellChecker"></alias>
<alias alias="tokenizer" name="spanishTokenizer"></alias>
<alias alias="dictionary" name="rAEDictionary"></alias>
</beans>
</pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="color: black;"><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><b>A
final step (really the first one)</b></span></span></span></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="color: black;"><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"></span></span></span></div>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;">Create
the integration test in the package </span><span style="color: black;"><i>demos.sf.editor.it</i></span><span style="color: black;">,
it's very similar to the unit test case in the [previous post], the
main differences are the use of </span><span style="color: #2a00ff;"><span style="font-family: monospace;"><span style="font-size: x-small;">it-applicationContext.xml
</span></span></span><span style="color: black;"><span style="font-family: monospace;"><span style="font-size: x-small;">as
context configuration, and the </span></span></span><span style="color: black;"><span style="font-family: monospace;"><span style="font-size: x-small;"><i>IT</i></span></span></span><span style="color: black;"><span style="font-family: monospace;"><span style="font-size: x-small;">
suffix on the test case name:</span></span></span></span></span></div>
<br />
<br />
<br />
<pre class="java" name="code">@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/it-applicationContext.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class EditorIT {
@Resource
private Editor editor;
@Test
public void testPasteWorks() {
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
}
}
</pre>
To honor the IT test case only in the integration test phase the maven-failsafe-plugin may be used, hence the mvn test execution only runs the traditional JUnit test cases and ignores the IT test case classes suffixed with IT:
<br />
<b>pom.xml</b>
<br />
<pre class="xml" name="code"><plugin>
<artifactid>maven-failsafe-plugin</artifactid>
<version>2.13</version>
<executions>
<execution>
<id>failsafe-integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</pre>
To run IT test then execute:<br />
<pre class="bash" name="code">$ mvn verify
</pre>
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
<br />
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;">The
source code can be found here
[<a href="https://bitbucket.org/eduardo_lago_aguilar/spring-demos">https://bitbucket.org/eduardo_lago_aguilar/spring-demos</a>] under the
directory: </span><span style="color: black;"><b>spring-hello-word-cdi-mocks</b></span><span style="color: black;">.</span></span></span></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;">
<style type="text/css">P { margin-bottom: 0.08in; }A:link { }</style>
</span></span></span></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="color: black;"><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><b>REFERENCES</b></span></span></span></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<span style="color: black;"><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;">CDI
/ JSR-299: </span></span></span>
</div>
<ul>
<li><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;"> </span><a href="http://jcp.org/en/jsr/detail?id=299">http://jcp.org/en/jsr/detail?id=299</a></span></span></li>
<li><span style="font-family: "liberation" serif , serif;"><span style="font-size: small;"><span style="color: black;"> </span><a href="http://docs.jboss.org/cdi/spec/1.0/html/">http://docs.jboss.org/cdi/spec/1.0/html/</a></span></span></li>
</ul>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
<div align="LEFT" style="font-style: normal; margin-bottom: 0in;">
<br /></div>
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com2tag:blogger.com,1999:blog-247964174508157859.post-5089757914895988002013-05-07T20:00:00.000-04:002013-05-07T20:12:59.503-04:00Creating a REST web service using webtoolkit (aka witty)I <a href="http://eduardo-lago.blogspot.de/2012/12/running-webtoolkit-application-as-nginx.html" target="_blank">previously </a>introduced witty as a powerful web framework for C++ developers, mostly those one that don't want to re-invent the wheel and become productive. Witty uses a resource-based approach to register services that listen on specific paths (resource paths), these resources are registered on the witty server first and the server is started later. <br />
<br />
So the steps of an standalone witty w/ custom resource server:<br />
<br />
<ol>
<li>create a server</li>
<li>register a custom resource on a desired path</li>
<li>start the server, hence its resources</li>
<li>wait for server shutdown signal</li>
<li>stop the server</li>
</ol>
<br />
Here is the snippet of the <b>main.cpp</b>, ignore all log-related plumbing:
<br />
<br />
<br />
<pre class="cpp" name="code">#include <Wt/WServer>
#include <iostream>
#include "MyResource.h"
#include "log.h"
using namespace std;
using namespace Wt;
int main(int argc, char **argv) {
WLogger logger;
configLogger(logger);
try {
WServer server(argv[0], "");
try {
server.setServerConfiguration(argc, argv);
MyResource dr;
server.addResource(&dr, "/resource");
info(logger, "Starting resource server.");
if (server.start()) {
WServer::waitForShutdown();
server.stop();
} else {
fatal(logger, "Fatal error starting resource server.");
return 1;
}
return 0;
} catch (std::exception& e) {
fatal(logger, "Fatal error starting resource server.", e.what());
return 1;
}
} catch (WServer::Exception& e) {
fatal(logger, "Fatal error creating WServer.", e.what());
return 1;
} catch (exception& e) {
fatal(logger, "Fatal error occurred.", e.what());
return 1;
}
}
</pre>
<br />
You may notice the creation and registration of MyResource, this is place where you provide the service implementation, specifically on the <i>handleRequest()</i> method, take a look at <b>MyResource.h</b>:<br />
<br />
<pre class="cpp" name="code">#ifndef MYRESOURCE_H_
#define MYRESOURCE_H_
#include <Wt/WResource>
using namespace Wt;
using namespace Wt::Http;
class MyResource: public WResource {
public:
MyResource();
virtual ~MyResource();
protected:
virtual void handleRequest(const Request &request, Response &response);
};
#endif /* MYPRESOURCE_H_ */
</pre>
<br />
The implemantation is very trivial, it's just an echo of the request content also including some extra data like content type, content length and request method: GET, POST, etc. Enjoy <b>MyResource.cpp</b>:
<br />
<pre class="cpp" name="code">#include <iostream>
#include <Wt/Http/Response>
#include "MyResource.h"
using namespace std;
using namespace Wt::Http;
MyResource::MyResource() {
}
MyResource::~MyResource() {
}
void MyResource::handleRequest(const Request& request, Response& response) {
string method = request.method();
string contentType = request.contentType();
int contentLength = request.contentLength();
char* buffer = new char[contentLength + 1];
request.in().read(buffer, contentLength);
buffer[contentLength] = 0;
response.setMimeType("application/xml");
ostream& out = response.out();
out << "<?xml version='1.0' encoding='utf-8' ?>" << endl;
out << "<reply>" << endl;
out << "<method>" << method << "</method>" << endl;
out << "<contentType>" << contentType << "</contentType>" << endl;
out << "<contentLenght>" << contentLength << "</contentLenght>" << endl;
out << "<body>" << buffer << "</body>" << endl;
out << "</reply>";
delete[] buffer;
}
</pre>
<br />
The reamining stuff is just plumbing as I said before, here you got <b>log.h</b>:
<br />
<pre class="cpp" name="code">#ifndef LOG_H_
#define LOG_H_
#include <Wt/WLogger>
using namespace std;
using namespace Wt;
void info(WLogger& logger, const string& message);
void fatal(WLogger& logger, const string& message, const char* what);
void fatal(WLogger& logger, const string& message);
void configLogger(WLogger& logger);
#endif /* LOG_H_ */
</pre>
<br />
And <b>log.cpp</b>:
<br />
<pre class="cpp" name="code">#include "log.h"
#include <iostream>
void info(WLogger& logger, const string& message) {
WLogEntry entry = logger.entry("info");
entry << WLogger::timestamp << WLogger::sep << WLogger::sep << '[' << "info"
<< ']' << WLogger::sep << message;
}
void fatal(WLogger& logger, const string& message, const char* what) {
WLogEntry entry = logger.entry("fatal");
entry << WLogger::timestamp << WLogger::sep << WLogger::sep << '['
<< "fatal" << ']' << WLogger::sep << message << what;
}
void fatal(WLogger& logger, const string& message) {
fatal(logger, message, "");
}
void configLogger(WLogger& logger) {
logger.addField("datetime", false);
logger.addField("type", false);
logger.addField("message", true);
logger.setFile("/var/log/resource.log");
}
</pre>
<br />
Now compile an run the server, here in this case using standalone server mode instead of fast cgi mode, but also works w/ fast cgi variant:
<br />
<pre class="bash" name="code">$ g++ log.cpp MyResource.cpp main.cpp -lwthttp -oresource
</pre>
<br />
Run the server:
<br />
<pre class="bash" name="code">$ ./resource --http-address 0.0.0.0 --http-port 80 --docroot=.
INFO: Opened log file (/var/log/resource.log).
[2013-May-07 19:52:11.046985] 9658 - [info] "config: reading Wt config file: /etc/wt/wt_config.xml (location = './resource')"
[2013-May-07 19:52:11.047757] 9658 - [info] "WServer/wthttp: initializing built-in wthttpd"
[2013-May-07 19:52:11.048027] 9658 - [info] "wthttp: started server: http://0.0.0.0:80"
</pre>
<br />
Finally test the service using a simple call:
<br />
<pre class="bash" name="code">$ curl -X POST -H "Content-Type: application/xml" -d"<payload>PAYLOAD GOES HERE!</payload>" http://localhost/resource
</pre>
<pre class="xml" name="code">
<?xml version='1.0' encoding='utf-8' ?>
<reply>
<method>POST</method>
<contentType>application/xml</contentType>
<contentLenght>37</contentLenght>
<body><payload>PAYLOAD GOES HERE!</payload></body>
</reply>
</pre>
Very simple as you can see!
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com2tag:blogger.com,1999:blog-247964174508157859.post-27491668711839712172013-05-02T21:45:00.000-04:002013-05-07T20:14:46.593-04:00ImageMagick convert to raw RGB format in C++This is a code snippet to convert from any <a href="http://www.imagemagick.org/script/formats.php" target="_blank">supported ImageMagick format</a> to raw RGB format, it's just an example on how to use the conversion features of ImageMagick libraries. The example was tested on both: Windows and Linux.<br />
<br />
<pre class="cpp" name="code">
#include <Magick++.h>
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
using namespace Magick;
using namespace boost::filesystem;
int main(int argc, char **argv) {
Image img;
string src_path;
string tgt_path;
Blob blob;
FILE* tgt_file;
InitializeMagick(*argv);
// read image
src_path = argv[1];
if(exists(src_path)) {
img.read(src_path);
cout << "Detected format: " << img.format() << endl;
// set raw RGBS output format & convert it into a Blob
img.magick("RGB");
img.write(&blob);
// dump blob to disk
tgt_path = src_path + ".RGB.raw";
tgt_file = fopen(tgt_path.c_str(), "wb");
fwrite(blob.data(), blob.length(), 1, tgt_file);
fclose(tgt_file);
cout << "Converted to raw RGB: " << tgt_path << endl;
exit(0);
} else {
cout << "Could not load image, file not found " << src_path << endl;
exit(1);
}
}
</pre>
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com1tag:blogger.com,1999:blog-247964174508157859.post-26629276127359362792013-05-02T21:30:00.001-04:002013-05-02T21:30:11.599-04:00Recording dialog-based interactive user input using expect on LinuxThis is a how to successfully combine the shell script <a href="http://linux.die.net/man/1/dialog" target="_blank">dialog</a> tool with <a href="http://linux.die.net/man/1/expect" target="_blank">expect</a> in order to record all user interactions and repeat them later. You can apply expect to record almost anything on a terminal, I applied it on a very complex dialog-based installation wizard with 100% success. There's nothing special here, nor a magic spell. The solution is pretty straightforward: start recording, launch dialogs + interact, stop record and finally replay it on a hit.<br />
<br />
Show the example dialogs below:<br />
<br />
<pre class="code" name="bash">#!/bin/bash
#
# dlgs.sh : all dialogs in one script, 1- a radio list, 2- a Yes/No dialog, 3-An input box
radiolist_result=$(dialog --stdout --clear --backtitle "Combining dialog w/ expect" \
--radiolist "Select distro:" 10 40 3 1 "CentOS" off 2 "Ubuntu" on 3 "Debian" off)
dialog --stdout --clear --title "Simple question" --backtitle "Combining dialog w/ expect" \
--yesno "Are you having fun?" 6 25 && yesno_result=Yes || yesno_result=No
inputbox_result=$(dialog --stdout --clear --backtitle "Combining dialog w/ expect" \
--inputbox "Enter your name:" 8 40)
</pre>
<br />
I use CentOS 6 for this demo, but it seems to work on other distros:
<br />
<br />
<pre class="code" name="bash">$ yum -y install expect
</pre>
<br />
Assign execution permissions to <em>dlgs.sh</em>:
<br />
<br />
<pre class="code" name="bash">$ chmod +x dlgs.sh
</pre>
<br />
Start recording using <em><a href="http://linux.die.net/man/1/autoexpect" target="_blank">autoexpect</a></em>:
<br />
<br />
<pre class="code" name="bash">$ autoexpect
autoexpect started, file is script.exp
</pre>
<br />
Call <em>dlgs.sh</em> and interact:
<br />
<br />
<pre class="code" name="bash">$ ./dlgs.sh
...
3
No
Eduardo Lago Aguilar
</pre>
By pressing<em> Ctrl+D</em> end the session watch. Then verify the recording by calling the generated script:
<br />
<br />
<pre class="code" name="bash">$ ./script.exp
...
3
No
Eduardo Lago Aguilar
</pre>
<br />
Very simple isn't?
See <a href="http://www.admin-magazine.com/Articles/Automating-with-Expect-Scripts" target="_blank">more</a>.Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-70593584591569360202013-02-21T16:51:00.001-05:002018-04-17T16:56:50.367-04:00Becoming unaware of the Dependency Injection Framework. Encouraging standarization<h2>
Intro</h2>
The <a href="http://eduardo-lago.blogspot.com/2012/12/a-tdd-approach-using-spring-framework.html" target="_blank">previous post</a> showed up how to combine Spring Framework, Mockito and Lombok in TDD fashion. But it's far from be the simplest and portable solution if such thing even exists. Let's try to make the whole example more unaware of the DI Framework, in this case Spring, but still use it for testing purposes.<br />
<br />
Java 6 brought us several improvements, CDI among them. <a href="http://refcardz.dzone.com/refcardz/contexts-and-depencency" target="_blank">This refcardz</a> is self-explanatory. IMO the main advantage of CDI is standardization, but CDI is pure specification, not an implementation, and its RI is Weld.<br />
<br />
Results that Spring honors CDI since version 3.0, at least in Java Injection Standard (<a href="http://jcp.org/en/jsr/summary?id=330" target="_blank">JSR-330</a>). There is a bunch of posts and articles talking about the Spring support for CDI specifications, and which one is better to use. But this is not the goal of this post.<br />
<h2>
Goals</h2>
<ul>
<li>Become unaware of the DI Framework at <i>design time</i></li>
<li>Support Spring at <i>test time</i></li>
<li>Support Spring at <i>runtime</i></li>
<li>Support others DI Framework at <i>runtime </i></li>
</ul>
<h2>
How to</h2>
<ul>
<li>By replacing <i>@Autowired</i> and using injection by name with <i>@Resource</i></li>
<li>By naming beans using <i>@Name</i> and scanning packages for candidates instead of explicitly create the bean in application context configuration file.
</li>
</ul>
<h2>
Let's do it</h2>
As I said <a href="http://eduardo-lago.blogspot.com/2012/12/a-tdd-approach-using-spring-framework.html" target="_blank">before</a>, it's just an example. Real world scenarios are more complex, and you will end for sure combining many APIs from different sources.<br />
<br />
<ol>
<li>Use the example from the <a href="http://eduardo-lago.blogspot.com/2012/12/a-tdd-approach-using-spring-framework.html" target="_blank">previous post</a>.</li>
<li>Include a couple of dependencies in your <i>pom.xml</i>.</li>
<pre class="xml" name="code"><dependency>
<groupid>javax.annotation</groupid>
<artifactid>jsr250-api</artifactid>
<version>1.0</version>
</dependency>
<dependency>
<groupid>javax.inject</groupid>
<artifactid>javax.inject</artifactid>
<version>1</version>
</dependency>
</pre>
<b>JSR-250</b> provide us various standard annotations: <i>@Resource, @PostConstruct, @PreDestroy </i>, etc; these annotations are honored by Spring. In the other hand with have <b>JSR-333</b> (javax.inject) that give us <i>@Inject, @Named, @Provider, @Qualifier, @Scope and @Singleton</i>.
<br />
Lets explain some of these annotations:
<br />
<ul>
<li><i>@Resource</i>: </li>
<ul>
<li><b>Matching</b>: First matches by name, then matches by type and filters by qualifiers if not name-based match gets found.</li>
<li><b>Applicable to</b>: Fields, Classes and Methods.</li>
<li><b>Package</b>: <i>javax.annotation</i> </li>
</ul>
<li><i>@PostConstruct</i>: </li>
<ul>
<li><b>Runs</b>: After the bean construction and wiring </li>
<li><b>Applicable to</b>: Methods.</li>
<li><b>Package</b>: <i>javax.annotation</i> </li>
</ul>
<li><i>@PreDestroy</i>: </li>
<ul>
<li><b>Runs</b>: Before the release of the bean</li>
<li><b>Applicable to</b>: Methods.</li>
<li><b>Package</b>: <i>javax.annotation</i> </li>
</ul>
<li><i>@Inject</i>: </li>
<ul>
<li><b>Matching</b>: First matches by type, filters by qualifiers, then matches be name.</li>
<li><b>Applicable to</b>: Methods, Constructors and Fields.</li>
<li><b>Package</b>: <i>javax.inject</i> </li>
</ul>
<li><i>@Named</i>: </li>
<ul>
<li><b>Description</b>: Its a qualifier that denotes a named bean. Annotated classes are considered for auto-detection on classpath scanning. It's similar to Spring's <i>@Component</i></li>
<li><b>Applicable to</b>: Classes and Fields.</li>
<li><b>Package</b>: <i>javax.inject</i> </li>
</ul>
</ul>
<li>Spring dependencies are now on test scope, since they are only required on test time. But in a real world scenario you will need a DI Framework, so don't forget to include Spring or Weld dependencies for production stage.</li>
<pre class="xml" name="code"><dependencies>
...
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-test</artifactid>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-beans</artifactid>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
...
</dependencies>
</pre>
<li>The test is an hybrid between Spring and more standard stuff, that is:</li>
<pre class="java" name="code">package demos.sf.editor.test;
import ... ;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-applicationContext.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class EditorTest {
@Resource
private Editor editor;
@Resource
private SpellChecker spellChecker;
@Test
public void testPasteSuccess() {
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
}
@Test
public void testAddParagraphSpellIsChecked() {
editor.paste("Hello everybody!");
verify(spellChecker, only()).check("Hello everybody!");
}
}
</pre>
<li>The SUT (Editor) is now DI Framework unaware:</li>
<pre class="java" name="code">package demos.sf.editor.impl;
import ...;
@Named
public class Editor {
@Getter
protected String text;
@Resource
private SpellChecker spellChecker;
public void paste(String cut) {
if (text == null) {
text = "";
}
text += cut;
spellChecker.check(cut);
}
}
</pre>
<li>The application context configuration file for testing stage file is now simplified. Only package scanning is needed to create the SUT (Editor):</li>
<pre class="xml" name="code"><beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="demos.sf.editor.impl">
<bean class="org.mockito.Mockito" factory-method="mock" id="spellChecker" primary="true">
<constructor-arg value="demos.sf.editor.SpellChecker">
</constructor-arg></bean>
</context:component-scan></beans>
</pre>
<b>NOTE</b>:Observe that some minor package relocation were made to organize tests, interfaces and classes.
</ol>
The final snapshot can be found <a href="https://bitbucket.org/eduardo_lago_aguilar/spring-demos" target="_blank">here</a> under the spring-hello-word-cdi directory. Enjoy it!Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-51030924635672453122012-12-19T10:39:00.000-05:002012-12-20T08:37:57.765-05:00A TDD approach using Spring Framework + Mockito + Lombok<h2>
Intro</h2>
This is simple Hello World Java application that successfully combines Spring Framework with <a href="http://code.google.com/p/mockito/" target="_blank">Mockito</a> and <a href="http://projectlombok.org/" target="_blank">Lombok</a>. It's just for demonstrative purposes, so the presented example is very simple and used many times before in the literature. <br />
<br />
The final goal is to show up how simple is to combine some of the best features of each involved technology with Test-Driven-Development in mind:<br />
<br />
<ul>
<li>Spring Framework: <i>wiring</i></li>
<li>Mockito: <i>isolation</i></li>
<li>Lombok: <i>simplicity</i></li>
</ul>
<h2>
Example</h2>
A text editor uses and spell checker among other things to make the life of user easier. Depending on the target language, an spell editor may change is behavior and its dependencies as well, for example dictionaries and tokenizers. Simplifying the whole idea you may encounter a contract-driven (interface based) solution like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitFhig9zgwMBQ-ytpSVTrbWE_tlH1nHvndwSuCGndyQjqJg0iHYtejmJpfgGsvJB8fjwAKtQRtbbpMQk6P28uX-_-72dXm-nBw8eq2TkXo5HXUe4bzfz4FqkQnUiHm2Zl3PxPLIlqnOITm/s1600/texteditor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="159" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitFhig9zgwMBQ-ytpSVTrbWE_tlH1nHvndwSuCGndyQjqJg0iHYtejmJpfgGsvJB8fjwAKtQRtbbpMQk6P28uX-_-72dXm-nBw8eq2TkXo5HXUe4bzfz4FqkQnUiHm2Zl3PxPLIlqnOITm/s320/texteditor.png" width="320" /></a></div>
with many possible combinations of spell checkers, dictionaries and tokenizers. For example this one:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkH7a9INnmYkayoip8gWBVrYypjAM3VK8YzJ7zdBBM8u-qNkNSMe7fZ-cterC3PMriFs2Z6Ap7d2Lch9JELzMqjXmu4fGKG_S6Ua-s8_RfPzwX-FIVY-rQQ5My8_U5tPx7grCWPQx8egk_/s1600/texteditor_en.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkH7a9INnmYkayoip8gWBVrYypjAM3VK8YzJ7zdBBM8u-qNkNSMe7fZ-cterC3PMriFs2Z6Ap7d2Lch9JELzMqjXmu4fGKG_S6Ua-s8_RfPzwX-FIVY-rQQ5My8_U5tPx7grCWPQx8egk_/s320/texteditor_en.png" width="320" /></a></div>
where every contract was replaced by a concrete instance. Combinations are multiple, and sometimes you cannot predict all them in a classes/interfaces design.<br />
<br />
You want to start engaging TDD for sure, and you want to do it in a <i>top-down</i> fashion. That means, start testing the<i> text editor first</i>, then the spell checker, then the remaining pieces. You also want to do it in isolation, so spell checker and the remaining implementations aren't involved in editor tests, and it's your desire to obtain zero infrastructure code (no constructor calls). As a plus no getter nor setter nor trivial constructor implementations.<br />
<br />
<h3>
What to test?</h3>
Our test will be very simple as well, here is the <i>top-down</i> ordered list (remember it's a simplification of the reality):<br />
<br />
1- Given an Editor, when a new text is pasted it should be added.<br />
2- Given an Editor, when a new text is pasted the spell should be checked against the appended text.<br />
3- Given an English spell checker, when a word is not found in a dictionary during the spell check then it must be signaled as misspelled and returned back. <br />
<br />
<br />
You may be wondering, why should I test these obvious use cases? Trust
me, it's very important to cover all possible scenarios if you really
want to delivery software with built-in quality. This is the hidden synergy behind TDD. <br />
<br />
<br />
<h2>
Hands on <a href="http://www.springsource.org/sts" target="_blank">Spring Tool Suite</a></h2>
<ol>
<li>Open STS and create a new Maven project.</li>
<li>Check 'Create a simple project (skip archetype selection)'.</li>
<li>Enter a Group Id = <b><i>demos.sf.editor</i></b>, Artifact Id = <b><i>spring-hello-world</i> </b>and Version = <b><i>1.0</i></b>.</li>
<li>Edit your <i>pom.xml</i> and append all the required dependencies. It should ends like: </li>
<pre class="xml" name="code"><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>demos.sf.editor</groupid>
<artifactid>spring-hello-word</artifactid>
<version>1.0</version>
<name>Spring Framework Hello World</name>
<description>Spring Framework Hello World demo w/ Unit Tests</description>
<properties>
<project .build.sourceencoding=".build.sourceencoding">UTF-8</project>
<spring .version=".version">3.2.0.RELEASE</spring>
</properties>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.10</version>
</dependency>
<!-- Support for testing Spring applications with tools such
as JUnit and TestNG This artifact is generally always
defined with a 'test' scope for the integration testing
framework and unit testing stubs -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-test</artifactid>
<version>${spring.version}</version>
</dependency>
<!-- Bean Factory and JavaBeans utilities (depends on
spring-core) Define this if you use Spring Bean APIs
(org.springframework.beans.*) -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-beans</artifactid>
<version>${spring.version}</version>
</dependency>
<!-- Application Context (depends on spring-core,
spring-expression, spring-aop, spring-beans) This is the
central artifact for Spring's Dependency Injection Container
and is generally always defined -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupid>org.mockito</groupid>
<artifactid>mockito-all</artifactid>
<version>1.9.5</version>
</dependency>
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<version>0.11.6</version>
</dependency>
</dependencies>
</project>
</pre>
<li>Start testing! A golden rule, don't forget it. Create a new JUnit 4 Test Case at <i>src/test/java</i>. Name it <b><i>EditorTest</i></b> in package <i><b>demos.sf.editor</b></i>.<b><i> </i></b></li>
<li>Delete the existing test, called <b><i>test </i></b>and append a new failing test for the first use case. Its name <i><b>testPasteSuccess</b></i>:</li>
<pre class="java" name="code">package demos.sf.editor;
import static org.junit.Assert.fail;
import org.junit.Test;
public class EditorTest {
@Test
public void testPasteSuccess() {
fail();
}
}
</pre>
Making the test fail it's very important, if you start failing you won't forget it until its fixed and the test pass. So the "last thing" to do is to remove the failing statement.
<li>Write the assertion first. How's that? See use case 1: "... the text should be added":</li>
<pre class="java" name="code">...
import static org.junit.Assert.assertEquals;
public class EditorTest {
@Test
public void testPasteSuccess() {
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
fail();
}
}</pre>
The code above will fail to compile, because there isn't and variable/field called editor. This is perfectly normal in TDD, guide your design only by needs (the tests).
<li>Right-click on <i><b>editor</b></i> and choose "Create local variable...". Change its type from Object to <b><i>Editor</i></b>, a non-existing class:</li>
<pre class="java" name="code">public class EditorTest {
@Test
public void testPasteSuccess() {
Editor editor;
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
fail();
}
}
</pre>
The code above, still without compiling due to the non-existing class <b><i>Editor</i></b>. Right click on <b><i>Editor</i></b> and "Create a new class...".
<li>Annotate the Editor class for auto-implementing the getter using Lombok annotations:</li>
<pre class="java" name="code">package demos.sf.editor;
import lombok.Getter;
public class Editor {
@Getter
private String text;
}
</pre>
<b>NOTE</b>: Lombok must be attached to Spring Tool Suite (or Eclipse) for completion at development time. Copy your lombok.jar to STS installation folder and append the following settings to your STS.ini (eclipse.ini):
<pre>-javaagent:/home/lago/Soft/springsource2.9.2/sts-2.9.2.RELEASE/lombok.jar
-Xbootclasspath/a:/home/lago/Soft/springsource2.9.2/sts-2.9.2.RELEASE/lombok.jar
</pre>
Alternative open a terminal/command prompt and run:
<pre>$ java -jar ~/.m2/repository/org/projectlombok/lombok/0.11.6/lombok-0.11.6.jar
</pre>
An install wizard gets launched. Choose your STS.ini (eclipse.ini) and press Install/Update. Finally restart your IDE.
<li>Go back to the test, and perform the call to a non-existing <b><i>paste()</i></b> method:</li>
<pre class="java" name="code">public class EditorTest {
@Test
public void testPasteSuccess() {
Editor editor;
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
fail();
}
}
</pre>
<li>Ctrl+1 on top the <b>editor.paste("...")</b> call and choose "Create method paste()....". A new method is generated:</li>
<pre class="java" name="code">public class Editor {
@Getter
private String text;
public void paste(String cut) {
}
}
</pre>
Go back to the test, don't waste your time implementing anything at this moment, the test will drive you to that point later.
<li>In the test, the only missing thing is the editor initialization. Let's inject the <b>Editor</b> at test time. Convert the <b><i>editor</i></b> local variable to a field declaration and annotate it as <i><b>@Autowired</b></i>:</li>
<pre class="java" name="code">...
import org.springframework.beans.factory.annotation.Autowired;
public class EditorTest {
@Autowired
private Editor editor;
@Test
public void testPasteSuccess() {
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
fail();
}
}
</pre>
<li>Create a new Spring Bean Configuration file at <i>src/test/resources/test-applicationContext.xml</i> and declare the editor bean on it:</li>
<pre class="xml" name="code"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean class="demos.sf.editor.Editor" id="editor">
</bean>
</beans>
</pre>
<li>Use the Spring test runner and load the application context via annotations:</li>
<pre class="java" name="code">...
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-applicationContext.xml" })
public class EditorTest {
...
}
</pre>
<li>Right click on test case and Run As JUnit test. The test will fail but corner stones are ready to support more agile tests. Make the test pass by implementing the <b><i>paste()</i></b> method. Oops! remember to remove the <b><i>fail()</i></b> from the test.
<pre class="java" name="code">public class Editor {
@Getter
private String text;
public void paste(String cut) {
if(text == null) {
text = "";
}
text += cut;
}
}
</pre>
</li>
<li>Re-run the test, this time also using a Maven run configuration like this:</li>
<pre>mvn test
</pre>
<li>The spring default behavior is to cache the application context between different tests in the same test case. They also introduced the annotations @TestExecutionListeners and @DirtiesContext to mark the application context as dirty after each test:</li>
<pre class="java" name="code">...
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-applicationContext.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class EditorTest {
...
}
</pre>
<li>Now starts the use case 2. First append the test in failing mode:</li>
<pre class="java" name="code">...
public class EditorTest {
...
@Test
public void testAddParagraphSpellIsChecked() {
fail();
}
}
</pre>
<li>Using Mockito style, we need to verify that the spell is checked against the pasted text. The syntax is straightforward it means that <b><i>check()</i></b> method must be called only once with <i>"Hello everybody!"</i>:</li>
<pre class="java" name="code">...
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-applicationContext.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class EditorTest {
...
@Autowired
private SpellChecker spellChecker;
@Test
public void testAddParagraphSpellIsChecked() {
editor.paste("Hello everybody!");
verify(spellChecker, only()).check("Hello everybody!");
fail();
}
}
</pre>
<li>At this time we need to create a new SpellChecker interface:</li>
<pre class="java" name="code">package demos.sf.editor;
public interface SpellChecker {
void check(String text);
}
</pre>
<li>The next step is to provide an spell checker mock to the application context:</li>
<pre class="xml" name="code"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean class="demos.sf.editor.Editor" id="editor">
</bean>
<bean class="org.mockito.Mockito" factory-method="mock" id="spellChecker">
<constructor-arg value="demos.sf.editor.SpellChecker">
</constructor-arg></bean>
</beans>
</pre>
<li>Remove the <b>fail()</b> from the test and run all tests and you will get a test failure saying: '<i>Wanted but no invoked: spellChecker.check("Hello everybody!")</i>'. That makes sense since we are not invoking the <i><b>SpellChecker.check()</b></i> from <i><b>Editor.paste()</b></i>. In fact haven't create any kind of dependency <i><b>Editor</b></i> -> <b><i>SpellChecker</i></b>. Let's do it at class level and force its injection at construction time using Lombok's <b>@RequiredArgsConstructor<i></i></b>:</li>
<pre class="java" name="code">package demos.sf.editor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Editor {
@Getter
private String text;
private final SpellChecker spellChecker;
public void paste(String cut) {
if (text == null) {
text = "";
}
text += cut;
}
}
</pre>
<li>Declare the constructor injection in the application context as well and re-run the tests:</li>
<pre class="java" name="code"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean class="demos.sf.editor.Editor" id="editor">
<constructor-arg ref="spellChecker">
</constructor-arg></bean>
<bean class="org.mockito.Mockito" factory-method="mock" id="spellChecker">
<constructor-arg value="demos.sf.editor.SpellChecker">
</constructor-arg></bean>
</beans>
</pre>
<li>Oops! The same failure: '<i>Wanted but no invoked: spellChecker.check("Hello everybody!")</i>'. Implement the <b>SpellChecker.check()<i></i></b> call and run the tests again:</li>
<pre class="java" name="code">package demos.sf.editor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Editor {
@Getter
private String text;
private final SpellChecker spellChecker;
public void paste(String cut) {
if (text == null) {
text = "";
}
text += cut;
spellChecker.check(cut);
}
}
</pre>
All tests should now pass:<br />
</ol>
<h2>
Final Snapshot</h2>
<ul>
<li>The test case at : <i>src/test/java/demos/sf/editor/EditorTest.java</i>:</li>
<pre class="java" name="code">package demos.sf.editor;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-applicationContext.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class EditorTest {
@Autowired
private Editor editor;
@Autowired
private SpellChecker spellChecker;
@Test
public void testPasteSuccess() {
editor.paste("Hello everybody!");
String expected = "Hello everybody!";
String actual = editor.getText();
assertEquals(expected, actual);
}
@Test
public void testAddParagraphSpellIsChecked() {
editor.paste("Hello everybody!");
verify(spellChecker, only()).check("Hello everybody!");
}
}
</pre>
<li>The test case at : <i>src/main/java/demos/sf/editor/Editor.java</i>:</li>
<pre class="java" name="code">package demos.sf.editor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Editor {
@Getter
private String text;
private final SpellChecker spellChecker;
public void paste(String cut) {
if (text == null) {
text = "";
}
text += cut;
spellChecker.check(cut);
}
}
</pre>
<li>The test case at : <i>src/main/java/demos/sf/editor/SpellChecker.java</i>:</li>
<pre class="java" name="code">package demos.sf.editor;
public interface SpellChecker {
void check(String text);
}
</pre>
<li>The test case at : <i>src/main/resources/test-applicationContext.xml</i>:</li>
<pre class="xml" name="code"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean class="demos.sf.editor.Editor" id="editor">
<constructor-arg ref="spellChecker">
</constructor-arg></bean>
<bean class="org.mockito.Mockito" factory-method="mock" id="spellChecker">
<constructor-arg value="demos.sf.editor.SpellChecker">
</constructor-arg></bean>
</beans>
</pre>
</ul>
You can obtain the source code from <a href="https://bitbucket.org/eduardo_lago_aguilar/spring-demos" target="_blank">here</a>. Enjoy it!Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com4tag:blogger.com,1999:blog-247964174508157859.post-57850874618582442052012-12-11T16:36:00.000-05:002012-12-12T08:15:11.126-05:00Running webtoolkit application as nginx FastCGI on CentOS 6.x<a href="http://www.webtoolkit.eu/wt" target="_blank">Wt</a> (pronounced as <i>witty</i>) is a powerful C++ library for developing
web applications. Witty-based apps can be integrated as <a href="http://en.wikipedia.org/wiki/FastCGI" target="_blank">FastCGI</a> with <a href="http://nginx.org/" target="_blank">nginx</a> and other web servers. This guide is about the integration of witty w/ nginx on CentOS 6.x OS.<br />
<br />
On this how to, we'll be using the simplest witty example: <b>hello</b> <br />
<br />
I assumed you already had installed a CentOS 6.x minimal x86_64. <br />
<br />
<h2>
The Steps</h2>
<ol>
<li>Login as a <i>sudoer</i> user.</li>
<li>Append EPEL repository by creating the file <i>/etc/yum.repos.d/epel.repo</i> with the following content:</li>
<pre class="bash" name="code">[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=0
</pre>
<li>Install required packages: <i>nginx</i>, <i>witty</i> and CentOS development kit:</li>
<pre class="bash" name="code">$ sudo yum install nginx
$ sudo yum install wt
$ sudo yum install fcgi
$ sudo yum install spawn-fcgi
$ sudo yum install wt-devel wt-examples # Only for development env
$ sudo yum groupinstall "Development Tools" # Only for development env
$ sudo yum install nano # Only for development env
</pre>
<li>Go to Wt's examples directory and edit the CMakeLists.txt archive:</li>
<pre class="bash" name="code">$ cd /usr/lib64/Wt/examples/hello
$ sudo nano CMakeLists.txt
</pre>
and replace
<pre class="bash" name="code">WT_ADD_EXAMPLE(hello.wt hello.C)</pre>
by:
<pre class="bash" name="code">ADD_EXECUTABLE(hello.wt hello.C)
TARGET_LINK_LIBRARIES(hello.wt ${EXAMPLES_CONNECTOR})
</pre>
<li>Run CMake specifying FastCGI support & copy resulting binary to nginx document root:</li>
<pre class="bash" name="code">$ sudo rm -rf target
$ sudo mkdir -p target
$ cd target
$ sudo cmake ../ -DEXAMPLES_CONNECTOR=wtfcgi -DCONNECTOR_FCGI=yes -DCONNECTOR_HTTP=no
$ sudo make
$ sudo cp -a hello.wt /usr/share/nginx/html/
</pre>
<li>Create a new archive <i>/etc/sysconfig/spawn-fcgi-hello.wt</i> with the following content:</li>
<pre class="bash" name="code">FCGI_SOCKET=/var/run/hello.wt.socket
FCGI_PROGRAM=/usr/share/nginx/html/hello.wt
FCGI_USER=nginx
FCGI_GROUP=nginx
FCGI_EXTRA_OPTIONS="-M 0700"
OPTIONS="-u $FCGI_USER -g $FCGI_GROUP -s $FCGI_SOCKET -S $FCGI_EXTRA_OPTIONS -F 1 -P /var/run/hello.wt.socket.pid -- $FCGI_PROGRAM"</pre>
<li>Allow nginx to write at /var/spool/wt/run/:</li>
<pre class="bash" name="code">$ sudo chgrp nginx /var/spool/wt/run/
$ sudo chmod g+w /var/spool/wt/run/</pre>
<li>Launch <b>hello</b> app via <i>spawn-fcgi</i>:</li>
<pre class="bash" name="code">$ source /etc/sysconfig/spawn-fcgi-hello.wt
$ spawn-fcgi $OPTIONS
</pre>
now the FastCGI is running and waiting.
<li>Create a new file /etc/nginx/conf.d/wt.conf w/ the following content:</li>
<pre class="bash" name="code">server {
listen 9091;
server_name _;
# by default relative to /usr/share/nginx/html
location / {
access_log /var/log/nginx/nginx-fastcgi-access.log;
gzip off;
# the full path /usr/share/nginx/html/hello.wt
if ($uri !~ "^/hello.wt/$") {
fastcgi_pass unix:/var/run/hello.wt.socket;
}
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
</pre>
<li>Restart <i>nginx</i>: </li>
<pre class="bash" name="code">$ service ngnix restart
</pre>
<li>Visit <a href="http://host:9091/">http://HOST:9091</a> and enjoy it!</li>
</ol>
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com2tag:blogger.com,1999:blog-247964174508157859.post-58710523890719906752012-08-08T21:27:00.001-04:002012-08-08T21:29:42.943-04:00Packaging Csync2 in RPM on CentOS 6.XThis is a how to package in RPM format, the <a href="http://oss.linbit.com/csync2/" target="_blank">Csync2</a> synchronization tool on CentOS 6.x.<br />
<br />
<h2>
Steps</h2>
<ol>
<li>Append RPM Forge repository by creating the file /etc/yum.repos.d/rpmforge.repo with the following content:</li>
<pre class="bash" name="code">$ sudo dd of=/etc/yum.repos.d/rpmforge.repo << EOT
[rpmforge]
name=CentOS-$releasever - Rpmforge
baseurl=http://apt.sw.be/redhat/el6/en/$basearch/rpmforge
gpgcheck=0
enabled=1
EOT
</pre>
<li>Install building dependencies:</li>
<pre class="bash" name="code">$ sudo yum -y groupinstall "Development Tools"
$ sudo yum -y install openssl-devel
$ sudo yum -y install librsync librsync-devel
$ sudo yum install gnutls openssl libtasn1 gnutls-devel
</pre>
<li>Create a temporal cache and define a downloader command:</li>
<pre class="bash" name="code">$ mkdir -p /tmp/cache
#/**
# * Downloads a file to the cache if doesn't exists
# *
# * @param $1 the file to download
# * @param $2 the url where the file is located
# */
$ get() {
[ -f /tmp/cache/$1 ] || wget -t inf -w 5 -c $2/$1 -O /tmp/cache/$1
}
</pre>
<li>Download latest csync2 tarball with sources:</li>
<pre class="bash" name="code">$ lastver=1.34
$ cs=csync2-$lastver.tar.gz
$ get $cs http://oss.linbit.com/csync2/$cs
</pre>
<li>Download an sqlite compatible version:</li>
<pre class="bash" name="code">$ sqver=2.8.16
$ sq=sqlite-$sqver.tar.gz
get $sq http://pkgs.fedoraproject.org/repo/pkgs/sqlite/$sq/9c79b461ff30240a6f9d70dd67f8faea/$sq
</pre>
<li>Create a clean RPM build environment:</li>
<pre class="bash" name="code">$ rm -rf ~/rpmbuild ~/.rpmmacros
$ mkdir -p ~/rpmbuild/{BUILD,RPMS,S{OURCE,PEC,RPM}S}
$ cat > ~/.rpmmacros <<< "%_topdir $HOME/rpmbuild"
</pre>
<li>Copy the sources tar balls to SOURCES and BUILD:</li>
<pre class="bash" name="code">$ cd ~/rpmbuild
$ cp /tmp/cache/$cs SOURCES/
$ cp /tmp/cache/$sq BUILD/
</pre>
<li>Extract the csync2.spec archive from tar ball and modify it to user sqlite sources and include some missing files into the final RPM:</li>
<pre class="bash" name="code">$ cd SPECS
$ tar --strip-components=1 -x csync2-$lastver/csync2.spec -vzf ../SOURCES/$cs
$ sed -i \
-e 's/^%changelog/%files\n%defattr(-,root,root,-)\n\/usr\/sbin\/csync2-compare\n\/etc\/csync2.cfg\n\/etc\/xinetd.d\/csync2\n\/usr\/sbin\/csync2\n\/usr\/share\/man\/man1\/csync2.1.gz\n\n&/' \
-e 's/\(^%configure\)/\1 --with-libsqlite-source=..\/sqlite-2.8.16.tar.gz --disable-gnutls/' csync2.spec
$ cd ..
</pre>
<li>Build and install the csync2 RPM packages: </li>
<pre class="bash" name="code">$ rpmbuild -bb SPECS/csync2.spec
$ sudo rpm -ivh RPMS/x86_64/csync2-$lastver-1.x86_64.rpm
</pre>
</ol>
<br />
<b>Enjoy it!
</b>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-47169057037378830042012-07-05T11:07:00.000-04:002012-07-05T13:00:13.225-04:00Building and packaging the latest Gearman server in CentOS 6.2This is a <i>how to</i> package, install and test the latest version of <a href="http://gearman.org/" target="_blank">Gearman</a> server in RPM format using CentOS 6.2.<br />
<br />
<h2>
Motivation</h2>
The latest version available of Gearman in the Fedora repository is very outdated, CentOS is even more. So, if you plan to use the latest Gearman features, you have two choices 1) compile it using the tarball; 2) package it (hence compile it) in RPM format. <br />
This guide uses the second approach, but not from scratch. Instead using the latest available SRPM German's package and performing some minor changes.<br />
<br />
At this moment, the latest Gearman version is 0.33 and the latest Fedora-based SRPM is 0.23. <br />
<br />
<h2>
Hands on bash</h2>
<ol>
<li>Append EPEL repository by creating the file /etc/yum.repos.d/epel.repo with the following content:</li>
<pre class="bash" name="code">[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=0
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 6 - $basearch - Debug
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch
failovermethod=priority
enabled=0
gpgcheck=0
[epel-source]
name=Extra Packages for Enterprise Linux 6 - $basearch - Source
#baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch
failovermethod=priority
enabled=0
gpgcheck=0
</pre>
<li>Install building dependencies:</li>
<pre class="bash" name="code">$ sudo yum groupinstall "Development Tools"
$ sudo yum install libevent-devel libuuid-devel
$ sudo yum install boost-devel
$ sudo yum install libmemcached-devel memcached google-perftools-devel
</pre>
<li>Create a temporal cache and define a downloader command:</li>
<pre class="bash" name="code">$ mkdir -p /tmp/cache
#/**
# * Downloads a file to the cache if doesn't exists
# *
# * @param $1 the file to download
# * @param $2 the url where the file is located
# */
$ get() {
[ -f /tmp/cache/$1 ] || wget -t inf -w 5 -c $2/$1 -O /tmp/cache/$1
}
</pre>
<li>Download latest Gearman tarball with sources and the latest SRPM package provided by Fedora</li>
<pre class="bash" name="code">$ lastver=0.33
$ gm=gearmand-$lastver.tar.gz
$ get $gm https://launchpadlibrarian.net/104788829/$gm
$ srcver=0.23
$ srpm=gearmand-$srcver-1.fc16.src.rpm
$ get $srpm http://www.muug.mb.ca/mirror/fedora/linux/releases/16/Everything/source/SRPMS/$srpm
</pre>
<li>Create a clean RPM build environment and install the SRPM package on it:</li>
<pre class="bash" name="code">$ rm -rf ~/rpmbuild ~/.rpmmacros
$ mkdir -p ~/rpmbuild/{BUILD,RPMS,S{OURCE,PEC,RPM}S}
$ cat > ~/.rpmmacros <<< "%_topdir $HOME/rpmbuild"
$ rpm -ivh /tmp/cache/$srpm
</pre>
<li>Remove the unpacked old Gearman tarball and copy the new one to SOURCES:</li>
<pre class="bash" name="code">$ cd ~/rpmbuild
$ cp /tmp/cache/gearmand-$lastver.tar.gz SOURCES/
$ rm SOURCES/gearmand-$srcver.tar.gz
</pre>
<li>Now the trick. The sed command below performs some changes on gearmand.spec file: 1) replaces the old version number with the new one; 2) Comments dependencies from systemd packages not available yet on CentOS; and 3) Adds various file/directory entries only available on the latest version of Gearman. </li>
<pre class="bash" name="code">$ sed -i \
-e 's/\(Version:[[:space:]]\+\)'${srcver}'/\1'${lastver}'/' \
-e 's/^BuildRequires:[[:space:]]\+systemd-units/#&/' \
-e 's/^Requires(post):[[:space:]]\+systemd-sysv/#&/' \
-e 's/^Requires(post):[[:space:]]\+systemd-units/#&/' \
-e 's/^Requires(preun):[[:space:]]\+systemd-units/#&/' \
-e 's/^Requires(postun):[[:space:]]\+systemd-units/#&/' \
-e 's/install -m 0644 %{SOURCE3} %{buildroot}%{_unitdir}\/%{name}.service/#&/' \
-e 's/^%changelog/%files\n%defattr(-,root,root,-)\n\/usr\/include\/libgearman-1.0\n\/etc\/rc.d\/init.d\/gearmand\n\/etc\/sysconfig\/gearmand\n\/usr\/bin\/gearadmin\n\/usr\/bin\/gearman\n\/usr\/sbin\/gearmand\n\/usr\/share\/man\n\n&/' \
SPECS/gearmand.spec
</pre>
<li>Build and install the Gearman RPM packages:</li>
<pre class="bash" name="code">$ rpmbuild -bb SPECS/gearmand.spec
$ sudo rpm -ivh RPMS/x86_64/libgearman-$lastver-1.el6.x86_64.rpm RPMS/x86_64/gearmand-$lastver-1.el6.x86_64.rpm
</pre>
<li>Register gearmand as a daemon on standard runleves:</li>
<pre class="bash" name="code">$ sudo chkconfig --add gearmand
$ sudo chkconfig gearmand on
</pre>
<li>Create an emtpy log archive with permissions for gearmand user amd group (created by the RPM installer), crate pid directory and start the daemon:</li>
<pre class="bash" name="code">$ sudo mkdir -p /usr/local/var
$ sudo ln -s /var/log /usr/local/var/log
$ sudo touch /var/log/gearmand.log
$ sudo chown gearmand:gearmand /var/log/gearmand.log
$ sudo mkdir -p /var/run/gearmand/
$ sudo /etc/init.d/gearmand start
</pre>
</ol>
<br />
<h2>
Testing</h2>
<ol>
<li>Use the examples provided by Gearman to test. Enter examples directory and run the reverse_worker example:</li>
<pre class="bash" name="code">$ cd BUILD/gearmand-$lastver/examples
$ ./reverse_worker
</pre>
<li>Open a new terminal, enter examples directory and run the reverse_client:</li>
<pre class="bash" name="code">cd ~/rpmbuild/BUILD/gearmand-$lastver/examples
$ ./reverse_client "Hello Gearman"
</pre>
<br /> in the reverse_worker terminal you should see:
<pre class="bash" name="code">Recieved 12 bytes
Job=H:centosarq62.localdomain:1 Reversed=HelloGearman
</pre>
</ol>
<br />
Now your Gearman with the latest features is ready for battle.<br />
<h3>
Enjoy it!</h3>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com2tag:blogger.com,1999:blog-247964174508157859.post-65214519829180068732012-07-04T15:36:00.001-04:002012-07-04T15:36:09.051-04:00The NGINX's RAM-only Jail. Adding nginx to RAM-only diskless Linux boxWithout any doubt, <a href="http://wiki.nginx.org/" target="_blank">nginx</a> is one of the best ever created HTTP servers, it solves the <a href="http://www.kegel.com/c10k.html" target="_blank">C10K</a> problem with an elegant event-drive architecture. Many big sites around the WWW use it: <a href="http://wikitech.wikimedia.org/view/Https#SSL_termination" target="_blank">Wikipedia</a>, <a href="http://hyves.nl/" target="_blank">Hyves</a>, etc.<br />
<br />
This is a<i> how to</i> prepare your <a href="http://eduardo-lago.blogspot.com/2012/06/ram-only-pxe-boot-smallest-diskless.html" target="_blank">rambox</a> to run nginx smoothly. It makes sense only after the preparation of the <a href="http://eduardo-lago.blogspot.com/2012/06/ram-only-pxe-boot-smallest-diskless.html" target="_blank">RAM-only PXE boot in my previous post</a>. This post isn't about any kind of nginx optimization stuff beside of its compilation in a single binary rock. At the end of this guide, you will be running the nginx very much like in a jail.<br />
<br />
Run these steps BEFORE the <i>step "Change ownership to everything" </i>at <a href="http://eduardo-lago.blogspot.com/2012/06/ram-only-pxe-boot-smallest-diskless.html" target="_blank">RAM-only PXE boot in my previous post</a><span style="background-color: white;">.</span><br />
<ol>
<li>Download <a href="http://nginx.org/download/nginx-1.2.1.tar.gz" target="_blank">latest stable version</a> nginx and upack it.
</li>
<pre class="bash" name="code">$ pushd /tmp/wrk
$ ngx=nginx-1.2.1.tar.gz
$ cache $ngx http://nginx.org/download/$ngx
$ tar -xvzf /tmp/cache/$ngx -C .
$ mv $ngx nginx
</pre>
<li>Add options depending on the features that you want to support. Beside of the default options I only add static compilation options: </li>
<pre class="bash" name="code">$ pushd nginx
$ ./configure --with-ld-opt="-static -static-libgcc" \
--with-cc-opt="-static -static-libgcc"
</pre>
make it (jN means N threads devoted to compilation and linking): <br />
<pre class="bash" name="code">$ make -j2
</pre>
ensure that it's not a dynamic executable:<br />
<pre class="bash" name="code">$ ldd objs/nginx
not a dynamic executable
$ popd
</pre>
<li>Copy the nginx executable to rambox's <i>/sbin </i>:
<pre class="bash" name="code">$ popd
$ pushd sbin
$ chmod +w .
$ cp ../../nginx/objs/nginx .
$ chmod -w .
$ popd
</pre>
</li>
<li>Create /usr/local/nginx directories:</li>
<pre class="bash" name="code">$ mkdir -p -m 0755 usr/local/nginx/{conf,logs}
</pre>
<li>Copy some needed libs: </li>
<pre class="bash" name="code">$ chmod +w lib
$ cp /lib64/{ld-2.12.so,ld-linux-x86-64.so.2} lib/
$ cp /lib64/{libc-2.12.so,libc.so.6} lib/
$ cp /lib64/{libnsl-2.12.so,libnsl.so.1} lib/
$ cp /lib64/{libnss_compat-2.12.so,libnss_compat.so.2} lib/
$ chmod -w lib
</pre>
<li>Copy mime conf file: </li>
<pre class="bash" name="code">$ cp ../nginx/conf/mime.types usr/local/nginx/conf/
</pre>
<li>Create the nginx.conf file with some basic settings:</li>
<pre class="bash" name="code">$ dd of=usr/local/nginx/conf/nginx.conf << EOT
# user and group to run nginx
user www www;
# numbers of dedicated CPUs
worker_processes 1;
# pid archive
pid /var/run/nginx.pid;
events {
# max connections in WAIT
worker_connections 128;
# accept & enqueue NEW connections, put them in WAIT
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
autoindex on;
location / {
root /var/www;
index index.php index.html index.htm;
}
}
}
EOT
</pre>
<li>Resume the rambox creation process and once it gets started run:</li>
<pre class="bash" name="code">$ nginx</pre>
</ol>
Enjoy it by browsing at http://HOSTADDRESS<br />
<br />
<h2>
Post install steps</h2>
Daemonize nginx by running it at <i>/init</i> script!Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com1tag:blogger.com,1999:blog-247964174508157859.post-19919898251414045932012-07-03T16:07:00.001-04:002012-07-03T16:16:56.870-04:00The Parking Permit Demo. README and Sources for Season 01<span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;">Here I let you the README and Sources for the Season 01 of The Parking Permit Demo with Oracle BPM/SOA suite.</span><br />
<br />
<span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"> </span>
<br />
<ul>
<li><span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"><a href="https://docs.google.com/file/d/0BwdYbdsQ8bOYcktGVXk3cW50Rk0/edit" target="_blank">The README</a>.</span><span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"> </span></li>
<li><span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"><a href="https://docs.google.com/file/d/0BwdYbdsQ8bOYRU9JT1pGODhUS2c/edit" target="_blank">The Sources for Season 01</a> </span></li>
</ul>
<br />
<span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"> Enjoy it!</span><br />
<span style="color: #222222; font-family: Arial,Tahoma,Helvetica,FreeSans,sans-serif; font-size: 13px; line-height: 18px;"><br /></span>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com1tag:blogger.com,1999:blog-247964174508157859.post-69958325349189800922012-06-07T13:06:00.001-04:002012-07-04T15:11:26.607-04:00RAM-only PXE boot & the "smallest" diskless Linux boxThis is a <i>how to</i> easily create a very small Linux box purely running on RAM memory that boots using <a href="http://en.wikipedia.org/wiki/Preboot_Execution_Environment" target="_blank">PXE</a>. This is an introductory topic (not new at all), for further posts about <i>scalability</i>, <i>load balancing</i> and <i>high availability</i>. That's why I mention clustering very often and also start simple & small by preparing a single node that boots smoothly in a controlled environment.<br />
<br />
<h2>
Why RAM-only & diskless?</h2>
I must say that such configuration of a Linux box can bring many advantages if you plan to assemble a cheap cluster without persistence in mind and with low maintenance costs. Consider the following:<br />
<ul>
<li><b>ram availability</b>: RAM memory is cheaper & fast wrt. the pass years</li>
<li><b>modern hardware, big ram</b>: A lot of hardware support a great amount RAM memory installed, from 1 GB to 128 GB, and go on </li>
<li><b>Linux rocks</b>: 64-bits Linux systems are able to manage a lot of RAM memory efficiently</li>
<li><b>HDD & the planet</b>: Electromechanical HDD have serious implications in energy consumption, recycling, NOISE and are more susceptible to failures</li>
<li><b>SSD & your wallet</b>: <a href="http://en.wikipedia.org/wiki/SSD" target="_blank">SSD</a> are more advanced wrt. their electromechanical counterparts (less susceptible to physical shock, are silent, and have lower access time and latency) but at present market prices, more expensive per unit of storage. So if you problem is not the storage, just processing, caching and networking, you are in the right place!</li>
<li><b>less is sometimes cheaper</b>: It's not a bad idea, if you have a good chance to buy cheaper nodes by parts/complete w/o HDD </li>
<li><b>crashing doesn't matter</b>: if a misbehaving node crashes you just need to restart it and it'll wake up again in a healthy state. A single node state doesn't wander in time</li>
<li><b>scaling better</b>: adding a node to the cluster is easy, just connect it, enable PXE boot and add an entry in DHCP config</li>
<li><b>network congestion is reduced</b>: the RAM filesystem is copied once per boot to the target node </li>
</ul>
Life is easy and cluster's maintenance costs are reduced, but remember that this is only if you don't need persistence in every single node, just CPU power, networking and RAM memory.<br />
<br />
<h2>
When don't I need persistence?</h2>
I don't have a full inventory of persistence-less & memory-network-only scenarios, but a practical and discrete list, I'm sure you can see the benefits:<br />
<ul>
<li><b>cryptographic stuff, privacy</b>: you need to run a cryptographic algorithm and ensure a full cleanup of private keys after the execution is complete, a HDD formating is not enough sometimes, and recovery data from a RAM memory after a full power of is very difficult if not impossible. Also an encrypted filesystem on top of the RAM shall be challenging for hackers</li>
<li><b>caching efficiently</b>: if your RAM is enough and your backend cluster is under a constantly growing demand for static content. You can delegate all your caching needs to a dedicated frontend cluster running purely in RAM and release the load of backend servers by processing only dynamic content on this physical layer</li>
<li><b>time only algorithms</b>: many algorithms have only processing power needs and low/medium memory foot print, some of them even only need volatile (non-persistence) memory for allocating data structures </li>
<li><b>display only apps</b>: some software solutions only need for displaying incoming data via graphs, <a href="http://en.wikipedia.org/wiki/Cctv" target="_blank">video streaming</a>, etc.. So a good display, a RAM-only system and a network is enough </li>
</ul>
<br />
<b>What will I obtain at the end of this guide?</b>
A Linux box, named it <b><i>rambox</i></b>, purely running in RAM memory, that means a root (/) filesystem mounted in RAM, that's why memory preservation is a priority as well as avoidance of a filesystem full of never-used archives which also increase the memory usage.<br />
<br />
We'll also make a customized Kernel compilation to shrink it, with a "minimal" set of features incorporated. Keeping it simple small! At this point you should be careful about omitting mandatory kernel features, there's another set of features that are not mandatory but useful to obtain the best performance. They mainly depend on your hardware, so take care of them.<br />
<br />
<b>What's a RAM filesystem? </b>A filesystem mounted on RAM isn't a new invention, is a awesome Kernel feature mostly used to load firmware/modules before starting the normal boot process. It's called <i>initrd</i> or <i>initramfs</i>, there are differences between both (see references) and we'll be using <i>initramfs</i>.<br />
<br />
<h2>
What do I need?</h2>
For this guide I use two <a href="http://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine" target="_blank">KVM</a>-virtualized computers, running in a <a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-kvm-and-libvirt-on.html" target="_blank">CentOS 6.2 host with bridged networking</a>. For simplicity, the host and the two guests are in the same subnetwork<br />
<ol>
<li><b>pxe</b>: a server computer with CentOS 6.2 amd64 installed, w/ 16 GB HDD, 1 GB RAM, no GUI, networking. With DHCP and <a href="http://en.wikipedia.org/wiki/Tftp" target="_blank">TFTP</a> role. Static IP = 192.168.24.202, subnet =192.168.24.0/24 </li>
<li><b>rambox</b>: a RAM-only computer, <b>w/o HDD installed</b>, w/ networking. With cluster node role. Dynamic DHCP-designated IP = 192.168.24.203, subnet =192.168.24.0/24 </li>
</ol>
<br />
<b>NOTE</b>: Notice that BIOS used for <a href="http://en.wikipedia.org/wiki/QEMU" title="QEMU">QEMU</a> and <a href="http://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine" title="Kernel-based Virtual Machine">KVM</a> virtual machines (the <a href="http://en.wikipedia.org/wiki/SeaBIOS" target="_blank">SeaBIOS</a>) supports an open source implementation of PXE, named <a href="http://en.wikipedia.org/wiki/GPXE" target="_blank">gPXE</a>. So KVM-based virtual machine is able to boot via network. Now days almost any motherboard should have a BIOS with PXE support. Ensure that your rambox support it by checking the BIOS setup.<br />
<br />
<h2>
How does it work?</h2>
In summary, when the <i>rambox</i> with PXE boot activated wake ups:<br />
<ol>
<li>the BIOS PXE boot loader <i>requests an address</i> to DHCP server</li>
<li>the DHCP server <i>offers an IP address</i>, a <a href="http://en.wikipedia.org/wiki/Tftp" target="_blank">TFPT</a> server IP address (himself), and the Linux PXE boot loader's location on the TFTP server</li>
<li>the BIOS PXE boot loader <i>downloads</i> <i>the Linux PXE boot loader</i> from the TFPT server</li>
<li>the Linux PXE boot loader <i>takes control</i> and uses the same IP configuration to connect to TFTP server <i>and fetch</i> two archives: the <i>kernel</i> and the <i>ramdisk</i></li>
<li>the Kernel <i>takes control and configures</i> its network interface, statically or by performing a second round of DHCP request, it depends </li>
<li>the Kernel <i>uncompress the ramdisk in memory</i></li>
<li>the RAM disk is <i>mounted on / </i>and the <i>/init</i> script gets invoked</li>
</ol>
<b>What do we have to configure and where? </b>In <i><b>pxe</b></i> server computer is where everything takes place:<br />
<ol>
<li>Install and configure a DHCP server with support for PXE extensions</li>
<li>Install and configure a TFTP server</li>
<li>Create a reduced ramdisk with a minimal set of utils and programs</li>
<li>Compile and optionally shrink the Kernel to include support for Kernel-level IP configuration, including NIC drivers</li>
<li>Locate all the stuff in the correct place and wake up the rambox! </li>
</ol>
There are several detailed explanations of the Linux boot process, some of them are outdated but still useful. At the moment, I won't make a full description of every single step of the boot process, ramdisk, PXE, Kernel-level IP, etc. (see references) <br />
<br />
<h2>
Hands on Bash</h2>
Login in<i> pxe</i> as a sudoer user (named <i>bozz</i> on this guide)<br />
<br />
<h3>
Installing phase</h3>
<ol>
<li>Install the <i>dhcp</i>, <i>tftp-server</i> and <i>syslinux</i> packages, <a href="http://en.wikipedia.org/wiki/Syslinux" target="_blank">syslinux</a> contains the Linux PXE boot loader:</li>
<blockquote>
<pre>$ sudo yum install dhcp tftp-server syslinux
</pre>
</blockquote>
<li>Additionally, install some tools:</li>
<blockquote>
<pre>$ sudo yum install bc wget
</pre>
</blockquote>
<li>Finally install kernel packages for kernel compilation. These packages ensure that you have all the required tools for the build:</li>
<blockquote>
<pre>$ sudo yum install kernel-devel
$ sudo yum groupinstall "Development Tools"
# This is required to enable a make *config command to execute correctly.
$ sudo yum install ncurses-devel
# These are required when building a CentOS-6 kernel.
$ sudo yum install hmaccalc zlib-devel binutils-devel elfutils-libelf-devel
# These are required when working with the full Kernel source
$ sudo yum install rpm-build redhat-rpm-config unifdef
# These are needed by kernel-2.6.32-220.el6
$ sudo yun install xmlto asciidoc newt-devel python-devel perl-ExtUtils-Embed
</pre>
</blockquote>
</ol>
<h3>
Configure DHCP</h3>
<ol>
<li>Ensure that the <i>dhcpd</i> starts at boot time:</li>
<blockquote>
<pre>$ sudo chkconfig --level 35 dhcpd on
$ chkconfig --list dhcpd
dhcpd 0:off 1:off 2:off 3:<b>on</b> 4:off 5:<b>on</b> 6:off
</pre>
</blockquote>
<li>Edit <i>dhcp.conf</i> adding PXE specific options:</li>
<blockquote>
<pre>$ sudo nano /etc/dhcp/dhcpd.conf</pre>
</blockquote>
it should finally look like this:
<blockquote>
<pre># dhcpd.conf
#
# DHCP configuration file for ISC dhcpd
#
# Use this to enble / disable dynamic dns updates globally.
ddns-update-style none;
# Definition of PXE-specific options
# Code 1: Multicast IP address of boot file server
# Code 2: UDP port that client should monitor for MTFTP responses
# Code 3: UDP port that MTFTP servers are using to listen for MTFTP requests
# Code 4: Number of seconds a client must listen for activity before trying
# to start a new MTFTP transfer
# Code 5: Number of seconds a client must listen before trying to restart
# a MTFTP transfer
option space PXE;
option PXE.mtftp-ip code 1 = ip-address;
option PXE.mtftp-cport code 2 = unsigned integer 16;
option PXE.mtftp-sport code 3 = unsigned integer 16;
option PXE.mtftp-tmout code 4 = unsigned integer 8;
option PXE.mtftp-delay code 5 = unsigned integer 8;
option PXE.discovery-control code 6 = unsigned integer 8;
option PXE.discovery-mcast-addr code 7 = ip-address;
subnet 192.168.24.0 netmask 255.255.255.0 {
class "pxeclients" {
match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
option vendor-class-identifier "PXEClient";
vendor-option-space PXE;
# At least one of the vendor-specific PXE options must be set in
# order for the client boot ROMs to realize that we are a PXE-compliant
# server. We set the MCAST IP address to 0.0.0.0 to tell the boot ROM
# that we can't provide multicast TFTP (address 0.0.0.0 means no
# address).
option PXE.mtftp-ip 0.0.0.0;
# This is the name of the file the boot ROMs should download.
filename "pxelinux.0";
# This is the name of the server they should get it from.
next-server 192.168.24.202;
}
pool {
max-lease-time 86400;
default-lease-time 86400;
range 192.168.24.203 192.168.24.203;
deny unknown clients;
}
host rambox {
hardware ethernet 08:00:07:26:c0:a5;
fixed-address 192.168.24.203;
hostname rambox01.home.dev;
}
}
</pre>
</blockquote>
<b>NOTE</b>: In this configuration the nodes will always use the same IP addresses leased by their MAC and the nodes with an unknown hardware address will be rejected. You can easily change this behavior by replacing <i>"deny unknown clients"</i> directive with <i>"allow unknown clients"</i> and deleting all the hosts entries. </ol>
<h3>
Configuring TFPT</h3>
<ol>
<li>To enable the TFTP server, edit <i>/etc/xinetd.d/tftp</i> replacing the word <i>yes</i> on the <i>disable</i> line with the word <i>no</i>. Then save the file and exit the editor:</li>
<blockquote>
<pre>$ sudo nano /etc/xinetd.d/tftp</pre>
</blockquote>
it should finally look like:
<blockquote>
<pre># default: off
# description: The tftp server serves files using the trivial file transfer \
# protocol. The tftp protocol is often used to boot diskless \
# workstations, download configuration files to network-aware printers, \
# and to start the installation process for some operating systems.
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s <b>/var/lib/tftpboot</b>
disable = <b>no</b>
per_source = 11
cps = 100 2
flags = IPv4
}
</pre>
</blockquote>
<li>Restart the <i>xinetd</i> daemon to reload configuration files:</li>
<blockquote>
<pre>$ sudo service xinetd restart</pre>
</blockquote>
<li>Verify if <i>xinetd</i> is started at boot time, it should be, if not then use <i>chkconfig</i> like the previous step:</li>
<blockquote>
<pre>$ chkconfig --list xinetd
xinetd 0:off 1:off 2:off 3:<b>on</b> 4:<b>on</b> 5:<b>on</b> 6:off
</pre>
</blockquote>
</ol>
<h3>
Concerning the firewall</h3>
<ul>
<li>Allow access to TFTP via standard ports:</li>
<blockquote>
<pre>$ sudo iptables -I INPUT -p udp --dport 69 -j ACCEPT
$ sudo iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 21 -j ACCEPT
$ sudo service iptables save
$ sudo service iptables restart
</pre>
</blockquote>
</ul>
<h3>
Configuring the PXE environment</h3>
<ol>
<li>Copy the Linux PXE boot loader <i>pxelinux.0</i> to <i>tftpboot</i> published root directory:</li>
<blockquote>
<pre>$ sudo cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot</pre>
</blockquote>
<li>Create PXE config directory on TFP root, this directory will contains a single configuration file per node or per subnet:</li>
<blockquote>
<pre>$ sudo mkdir -p /var/lib/tftpboot/pxelinux.cfg</pre>
</blockquote>
<li>The Linux PXE boot loader uses its own IP address in hexadecimal format to look for a single configuration file under <i>pxelinux.cfg</i> directory, if its not found it will remove the last octet and
try again, repeating until it runs out of octets. That's why I define a helper function to convert IPv4 decimal to an hexadecimal string:</li>
<blockquote>
<pre>#/**
# * converts an IPv4 address to hexadecimal format completing the missing
# * leading zero
# *
# * @example:
# * $ hxip 10.10.24.203
# * 0A0A18CB
# *
# * @param $1: the IPv4 address
# */
hxip() {
( bc | sed 's/^\([[:digit:]]\|[A-F]\)$/0\1/' | tr -d '\n' ) <<< "obase=16; ${1//./;}"
}
</pre>
</blockquote>
test the function via command line:
<blockquote>
<pre>$ hxip 192.168.24.203
C0A818CB</pre>
</blockquote>
<li>Create PXE Linux config file using the designated IPv4 address in hexadecimal format:
<blockquote>
<pre>$ sudo nano /var/lib/tftpboot/pxelinux.cfg/$(hxip 192.168.24.203)</pre>
</blockquote>
with the following content:
<blockquote>
<pre>DEFAULT bzImage
APPEND initrd=initramfs.cpio.gz rw ip=dhcp shell
</pre>
</blockquote>
or if you prefer to avoid the second round of DHCP issued by the Kernel:
<blockquote>
<pre>DEFAULT bzImage
APPEND initrd=initramfs.cpio.gz rw ip=192.168.24.203:192.168.24.202:192.168.24.1:255.255.252.0:rambox:eth0:off shell
</pre>
</blockquote>
where <i>DEFAULT</i> provides the Kernel archive and <i>APPEND</i> the Kernel parameters passed on boot:
<ul>
<li><b>bzImage</b>: is the name of the compressed Kernel image</li>
<li><b>initrd=initramfs.cpio.gz</b>: tells to Linux PXE boot loader to download this file and pass it to the Kernel later which will interpret it to be a compressed ramdisk filesystem image</li>
<li><b>rw</b>: Kernel mounts the ramdisk filesystem in read-write mode</li>
<li><b>ip=dhcp</b>: a Kernel-level IP parameter indicating to perform a DHCP request to obtain a valid network parameters, or alternative you can used a fixed network configuration</li>
<li><b>ip=192.168.24.203:192.168.24.202:192.168.24.1:255.255.252.0:rambox:eth0:off</b></li>
<ul>
<li>node IP address = 192.168.24.203</li>
<li>server IP address = 192.168.24.202</li>
<li>default gateway IP address = 192.168.24.1</li>
<li>network mask = 255.255.252.0</li>
<li>node hostname = rambox</li>
<li>device = eth0</li>
<li>auto configuration protocol = off</li>
</ul>
<li><b>shell</b>: a custom parameter added by me to run a shell </li>
</ul>
<br />
</li>
</ol>
<h3>
Creating a compressed root filesystem</h3>
The Kernel support for initramfs allow us to create a customizable boot process to load modules and provide a minimalistic shell that runs on RAM memory. An initramfs disk is nothing else than a compressed <a href="http://en.wikipedia.org/wiki/Cpio" target="_blank"><i>cpio</i></a> archive, that is then either embedded directly into your kernel image, or stored as a separate file which can be loaded by the Linux PXE boot loader. Embedded or not, it should always contains at least:<br />
<ul>
<li>a minimum set of directories:
<ul>
<li>/<i>sbin</i> -- Critical system binaries</li>
<li>/<i>bin</i> -- Essential binaries considered part of the system</li>
<li>/<i>dev</i> -- Device files, required to perform I/O</li>
<li>/<i>etc</i> -- System configuration files </li>
<li>/<i>lib</i>, /<i>lib32</i>, /<i>lib64</i> -- Shared libraries to provide run-time support </li>
<li>/<i>mnt</i> -- A mount point for maintenance and use after the boot/root system is running</li>
<li>/<i>proc</i> -- Directory stub required by the proc filesystem. The /proc directory is a stub under which the proc filesystem is placed</li>
<li>/<i>root</i> -- the root's home directory </li>
<li>/<i>sys</i> -- </li>
<li>/<i>tmp</i> -- Temporal directory </li>
<li>/<i>usr</i> -- Additional utilities and applications </li>
<li>/<i>var</i> -- Variable files whose content is expected to continually change during normal operation of the system—such as logs, spool files, and temporary e-mail files.
</li>
</ul>
</li>
<li>basic set of utilities: <tt class="FILENAME">sh</tt>, <tt class="FILENAME">ls</tt>, <tt class="FILENAME">cp</tt>, <tt class="FILENAME">mv</tt>, etc</li>
<li>minimum set of config files: <tt class="FILENAME">rc, inittab, fstab</tt>, etc</li>
<li>devices: <tt class="FILENAME">/dev/hd*, /dev/tty*, </tt>etc </li>
<li>runtime libraries to provide basic functions used by utilities</li>
</ul>
<ol>
</ol>
<b>Is there any other simple method to create the RAM disk?</b> Creating an initramfs can be also achieved by copying the content of an already installed Linux distro into an empty directory then package it, but you must be aware of carrying undesired and/or useless archives. There other methods, some of them simple, some of them not, but they are outside of the scopte of this guide which aims to show you a handy approch to obtain a lightweight RAM disk and Kernel <br />
<br />
Use the following steps to create the initramfs:
<br />
<ol><br />
<li>Creating a download cache & working zone. Also defining a helper command to download and cache archives:</li>
<pre class="bash" name="code">$ mkdir -p /tmp/cache
$ mkdir /tmp/wrk
$ pushd /tmp/wrk
#/**
# * Downloads a file to the cache if doesn't exists
# *
# * @param $1 the file to download
# * @param $2 the url where the file is located
# */
$ get() {
[ -f /tmp/cache/$1 ] || wget -t inf -w 5 -c $2/$1 -O /tmp/cache/$1
}
</pre>
<li>Creating and entering to initramfs root directory:</li>
<pre class="bash" name="code">$ mkdir initramfs
$ pushd initramfs
</pre>
<li>Creating filesystem's base directories:</li>
<pre class="bash" name="code">$ mkdir -p -m 0755 dev etc/{,init,sysconfig} mnt sys usr/{,local} var/{,www,log,lib,cache} run
$ mkdir -p -m 0555 {,s}bin lib{,32,64} proc usr/{,s}bin
$ mkdir -p -m 0700 root
$ mkdir -p -m 1777 tmp
$ pushd var
$ ln -s ../run run
$ popd
</pre>
<li>Creating /etc/profile to exports environment variables:</li>
<pre>$ dd of=etc/profile << EOT
## /etc/profile
export PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin"
EOT</pre>
<li>Creating /etc/fstab with various mount points:</li>
<pre class="bash" name="code">$ dd of=etc/fstab << EOT
devpts /dev/pts devpts nosuid,noexec,gid=5,mode=0620 0 0
tmpfs /dev/shm tmpfs nosuid,nodev,mode=0755 0 0
sysfs /sys sysfs nosuid,nodev,noexec 0 0
proc /proc proc nosuid,nodev,noexec 0 0
EOT
$ chmod 0644 etc/fstab
</pre>
<li>Configure passwd & group settings: </li>
<pre class="bash" name="code">$ dd of=etc/passwd << EOT
root:x:0:0:root:/root:/bin/sh
nobody:x:99:99:NoBody:/none:/bin/false
www:x:33:33:HTTP Server:/var/www:/bin/false
EOT
$ dd of=etc/group << EOT
root:x:0:
nobody:x:99:
www:x:33:
EOT
</pre>
<li>Configure some host related settings: </li>
<pre class="bash" name="code">$ dd of=etc/host.conf <<< "multi on"
$ dd of=etc/hostname <<< "rambox"
$ dd of=etc/hosts << EOT
127.0.0.1 localhost.localdomain localhost
127.0.1.1 $(cat etc/hostname)
EOT
</pre>
<li>Configure timezone: </li>
<pre class="bash" name="code">$ dd of=etc/timezone <<< "America/New_York"
$ cp /usr/share/zoneinfo/$(cat etc/timezone) etc/localtime
</pre>
<li><a href="http://en.wikipedia.org/wiki/Busybox" target="_blank">Busybox</a> is a handful tool used very often in ramdisks and small devices with very limited resources, providing a self-contained and minimal set of POSIX compatible unix tools in a single executable archive. I'll be using busybox on this guide. Getting busybox and create sh symbolic link: </li>
<pre class="bash" name="code">$ pushd bin
$ chmod +w .
$ bb=busybox-x86_64 && get $bb http://www.busybox.net/downloads/binaries/latest/busybox-x86_64
$ cp /tmp/cache/$bb busybox && chmod +x busybox
$ ln -s busybox sh
$ chmod -w .
$ popd
</pre>
<li>Additionally we MAY need an DHCP configuration script, so we'll use busybox's udhcp and simple.script. The we'll create an script named renew_ip that performs all the job: </li>
<pre class="bash" name="code">$ pushd bin
$ chmod +w .
$ ss=simple.script
$ get $ss http://git.busybox.net/busybox/plain/examples/udhcp/$ss
$ cp /tmp/cache/$ss . && chmod +x $ss
$ dd of=renew_ip << EOT
#!/bin/sh
ifconfig eth0 up
udhcpc -t 5 -q -s /bin/simple.script
EOT
$ chmod +x renew_ip
$ chmod -w .
$ popd
</pre>
<li>One of the most important phases is <i>/init</i> script execution, this is a simple shell script file that performs all initialization process on the ramdisk. It usually mounts all filesystems listed on fstab, creates device nodes (like <a href="http://en.wikipedia.org/wiki/Udev" target="_blank">udev</a> device manager), loads device firmware and finally remounts another root (<i>/</i>) directory in other device and relaunches the new mounted /sbin/init. This is the point where we intervened, by just launching the shell or by executing our own<i> /sbin/init</i> w/o remounting the root (<i>/</i>). So edit init script and add the following content:</li>
<pre class="bash" name="code">$ nano init
</pre>
w/ this content:
<pre class="bash" name="code">#!/bin/sh
# Make all core utils reachable
. /etc/profile
# Create all busybox's symb links
/bin/busybox --install -s
# Create some devices statically
# pts: pseudoterminal slave
mkdir dev/pts
# shm
mkdir dev/shm
chmod 1777 dev/shm
# Mount the fstab's filesystems.
mount -av
# Some things don't work properly without /etc/mtab.
ln -sf /proc/mounts /etc/mtab
# mdev is a suitable replacement of the udev device node creator for loading
# firmware
touch /etc/mdev.conf
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
# Only renew the IP address via DHCP if you need it. Not needed if Kernel-level 'ip=...'
# was used.
#renew_ip
# set hostname
hostname $(cat /etc/hostname)
# shell launcher
shell() {
echo "${1}Launching shell..." && exec /bin/sh
}
# launch the shell if the 'shell' parameter was supplied
grep -q 'shell' /proc/cmdline && shell
# parse kernel command and obtain the init & root parameters
# if not then use default values
for i in $(cat /proc/cmdline); do
par=$(echo $i | cut -d "=" -f 1)
val=$(echo $i | cut -d "=" -f 2)
case $par in
root)
root=$val
;;
init)
init=$val
;;
esac
done
init=${init:-/sbin/init}
root=${root:-/dev/hda1}
# if rambox parameter is supplied then keep the ramdisk mounted, ignore root parameter
# and run the other init script. Located at /sbin/init by default
if grep -q 'rambox' /proc/cmdline ; then
[ -e ${init} ] || shell "Not init found on ramdisk at '${init}'... "
echo "Keeping the ramdisk since rambox param was supplied & executing init... "
exec ${init}
#This will only be run if the exec above failed
shell "Failed keeping the ramdisk and executing '${init}'... "
fi
# Neither shell nor rambox parameters were supplied then, try to switch to the new
# root and launch init
mkdir /newroot
mount ${root} /newroot || shell "An error ocurred mounting '${root}' at /newroot... "
[ -e /newroot${init} ] || shell "Not init found at '${init}'... "
echo "Resetting kernel hotplugging... "
:> /proc/sys/kernel/hotplug
echo "Umounting all... "
umount -a
echo "Switching to the new root and executing init... "
exec switch_root /newroot ${init}
#This will only be run if the exec above failed
mount -av
mdev -s
shell "Failed to switch_root... "
</pre>
as you may notice, every single step is commented, however this is an overall explanation of the process:
<ol>
<li><i>/etc/profile</i> is sourced to export <i>PATH</i> variable and make all executables reachable </li>
<li>All busybox's symbolic links are created</li>
<li>Some special devices are created by hand</li>
<li>All <i>/etc/fstab</i> filesystems are mounted</li>
<li>The rest of the devices are discovery and created by busybox's <i>mdev</i></li>
<li>The Kernel command line located at<i> /proc/cmdline</i> is parsed to see if the <b>shell</b> parameter was supplied, is so the shell is immediately launched replacing the current process instance, hence everything else is ignored</li>
<li>The Kernel command line is checked again to see if <b>rambox</b> parameter was supplied, indicating that we want to keep the ramdisk mounted at / and launch the normal /sbin/init process</li>
<li>If neither <b>shell</b> nor <b>rambox</b> parameters were supplied then, try to mount the new root (/) and launch the<i> /sbin/init </i>on this new location</li>
<li>Finally if neither the new root cannot be mounted nor the <i>/sbin/init</i> script cannot be executed, then a shell is launched indicating this situation</li>
<li>If on any of these shell launching steps an error is produced, then a Kernel panic is issued </li>
</ol>
<li>Append execution permissions to /init:</li>
<pre class="bash" name="code">$ chmod +x init
</pre>
<li>Change ownership to everything:</li>
<pre class="bash" name="code">$ sudo chown -R root:root *
</pre>
<li>Create the <i>initramfs.cpio.gz </i>compressed archive and copy it to tftp's root directory:</li>
<pre class="bash" name="code">$ sudo find . -print0 | sudo cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
$ sudo cp ../initramfs.cpio.gz /var/lib/tftpboot
</pre>
<li>Go back to working directory:</li>
<pre class="bash" name="code">$ popd
</pre>
</ol>
<h3>
Now the Kernel stuff:</h3>
What I am about to do with the Kernel is very simple, compile it using a minimal set of features that makes it boot and recognize MY hardware, mainly the NIC device. Hence, depending on your hardware you should probably use a different selection of features for Kernel compiling. So I recommend first to do a once-time installation of any modern Linux distribution (like I did) like CentOS, Gentoo, Fedora, Debian or Ubuntu with a modern Kernel version and check the modules loaded on boot using <i>/sbin/lsmod</i>. Then using this modules list, look for the corresponding Kernel options and INCLUDE them all in the Kernel, making it a solid rock!. That's what I did.<br />
<br />
<b>NOTE</b>: In our journey for making the Kernel simple and small, we should be careful in omitting some Kernel critical features and lost the hardware advantages, for example <a href="http://en.wikipedia.org/wiki/Symmetric_Multiprocessor" target="_blank">SMP</a> features. So if we really want to use it in a production environment, then a deep research and customization must be done before.<br />
<br />
Start Kerneling... <br />
<ol>
<li>Download the Kernel sources from the sky:</li>
<pre class="bash" name="code">$ krn=linux-2.6.39
$ get $krn.tar.xz http://www.kernel.org/pub/linux/kernel/v2.6/$krn.tar.xz
</pre>
<li>Uncompress it into a working directory, named it linux:</li>
<pre class="bash" name="code">$ tar xvf /tmp/cache/$krn.tar.xz -C .
$ mv $krn linux
$ pushd linux
</pre>
<li>Clean all configuration settings and enter the menu:</li>
<pre class="bash" name="code">$ make clean
$ make allnoconfig
$ make menuconfig
</pre>
<li>An <a href="http://en.wikipedia.org/wiki/Ncurses" target="_blank">ncurses</a> menu dialog should be opened. Now check a "minimal" set of features, and uncheck the unneeded ones, I'll only list what changes wrt the clean configuration settings. So [*] means explicitly checked to be EMBEDDED it into the Kernel, and [ ] means explicitly unchecked to be not included</li>
<ol>
<li><b>General Setup</b> (here the RAM filesystem is the most important feature)</li>
<blockquote>
<pre>[*] Prompt for development and/or incomplete code/drivers
(-minimal) Local version - append to kernel release
<b>[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support</b>
</pre>
</blockquote>
<li><b>Bus options (PCI etc.) ---></b> (Enable support for PCI devices, you may add support for your PCI hardware here)</li>
<blockquote>
<pre>[*] PCI support </pre>
</blockquote>
<li><b>Executable file formats / Emulations ---></b> (An important piece!, you won't be able to execute almost anything if you don't check it)</li>
<blockquote>
<pre><b>[*] Kernel support for ELF binaries</b>
</pre>
</blockquote>
<li><b>[*] Networking support</b> (Beside of enabling TCP/IP and disable Wireless, IPSec, etc. The most important feature to check here is the IP-Kernel level auto configuration with DHCP support)</li>
<blockquote>
<pre> [ ] Wireless --->
Networking options
[*] Packet socket
[*] Packet socket: mmapped IO
[*] Unix domain sockets
[*] Transformation sub policy support (EXPERIMENTAL)
[*] Transformation migrate database (EXPERIMENTAL)
[*] PF_KEY sockets
[*] PF_KEY MIGRATE (EXPERIMENTAL)
[*] TCP/IP networking
[*] IP: multicasting
[*] IP: advanced router
Choose IP: FIB lookup algorithm (choose FIB_HASH if unsure) (FIB_HASH
[*] IP: policy routing
[*] IP: equal cost multipath
[*] IP: verbose route monitoring
<b>[*] IP: kernel level autoconfiguration
[*] IP: DHCP support</b>
[*] IP: tunneling
[*] IP: GRE tunnels over IP
[*] IP: broadcast GRE over IP
[*] IP: multicast routing
[*] IP: PIM-SM version 1 support
[*] IP: PIM-SM version 2 support
[*] IP: ARP daemon support
[*] IP: TCP syncookie support (disabled per default)
[*] IP: AH transformation
[*] IP: ESP transformation
[*] IP: IPComp transformation
[ ] IP: IPsec transport mode
[ ] IP: IPsec tunnel mode
[ ] IP: IPsec BEET mode
[*] TCP: advanced congestion control --->
[*] CUBIC TCP (NEW) (only cubic)
[*] TCP: MD5 Signature Option support (RFC2385) (EXPERIMENTAL)
[ ] The IPv6 protocol --->
</pre>
</blockquote>
<li><b>Device Drivers ---></b> (RAM block device support and Network device support + Ethernet are the most important things, the remaining stuff is related to my current hardware)</li>
<blockquote>
<pre> [*] Block devices ---><b>
[*] RAM block device support</b>
[*] Multiple devices driver support (RAID and LVM) --->
[*] Device mapper support
[*] Network device support --->
<b>[*] Ethernet (10 or 100Mbit) ---></b>
[ ] Wireless LAN --->
Character devices --->
[*] /dev/kmem virtual device support
[*] Hardware Random Number Generator Core support
[*] I2C support --->
[*] I2C device interface
I2C Hardware Bus support --->
[*] Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)
Serial ATA (prod) and Parallel ATA (experimental) drivers (ATA [=n])
[*] ATA SFF support (NEW)
[*] Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support
[*] Generic ATA support
</pre>
</blockquote>
<li><b>File systems ---> </b> (File systems are very important, they support depend on what's your final goal: mount an NFS remotely for a shared storage? use a <a href="http://en.wikipedia.org/wiki/GlusterFS" target="_blank">GlusterFS</a> / <a href="http://en.wikipedia.org/wiki/Ceph" target="_blank">Ceph</a> filesystem in top of a <a href="http://en.wikipedia.org/wiki/Network-attached_storage" target="_blank">NAS</a>? The configuration I used is the simplest one, only support for initramfs and other pseudo filesystem. I recommend to start with this one, then gradually embed your filesystems) </li>
<blockquote>
<pre> [ ] Network File Systems --->
Pseudo filesystems --->
[*] Virtual memory file system support (former shm fs)
[*] Tmpfs POSIX Access Control Lists
</pre>
</blockquote>
<li><b>[*] Virtualization ---></b> (As I mentioned earlier, I'm using a KVM-virtualized hardware with a wide usage of <a href="http://www.linux-kvm.org/page/Virtio" target="_blank">Virtio</a> <a href="http://en.wikipedia.org/wiki/Paravirtualization" target="_blank">paravirtualization</a> technology. Virtio adds supports for a paravirtual Ethernet card, a paravirtual disk I/O controller, a balloon device for adjusting guest memory usage, and a VGA graphics interface using SPICE drivers. Virtio drivers for guest machines are included in the Kernel >= 2.6.25, see details <a href="http://www.linux-kvm.org/page/Virtio" target="_blank">here</a>) </li>
<blockquote>
<pre> [*] PCI driver for virtio devices (EXPERIMENTAL)
[*] Virtio balloon driver (EXPERIMENTAL)
</pre>
</blockquote>
<li><b>Device Drivers ---></b> [for virtualization]</li>
<blockquote>
<pre>
[*] Block devices --->
[*] Virtio block driver (EXPERIMENTAL)
[*] Network device support --->
<b>[*] Virtio network driver (EXPERIMENTAL)</b>
Character devices --->
[*] Virtio console
[*] Hardware Random Number Generator Core support
[*] VirtIO Random Number Generator support
</pre>
</blockquote>
<li>Exit the Kernel configuration menu and don't forget to save the settings file. </li>
</ol>
<li>Compile the Kernel (-j4 means 4 threads devoted to compilation), copy it to TFPT's root directory:</li>
<pre class="bash" name="code">$ make -j4 bzImage
$ sudo cp arch/x86/boot/bzImage /var/lib/tftpboot/
</pre>
<li>Do cleanup: </li>
<pre class="bash" name="code">$ popd
$ sudo rm -rf /tmp/wrk
</pre>
<li>Power on the rambox and enjoy it! It should boot smoothly and launch the busybox's shell.</li>
</ol>
You will find the basic tools at <i>/bin, /</i><i>sbin, /</i><i>usr/bin, /</i><i>usr/sbin, /usr/local/sbin</i>, all these tools are indeed in the PATH environment variable. To renew your IP address just run <i>renew_ip</i>. Finally notice that any Kernel module is loaded since all that you need is embedded.<br />
<br />
Enjoy it!<br />
<br />
<h2>
Post install</h2>
Perform some checks after install to ensure that everything is OK and measure for resource consumption:<br />
<br />
<ul>
<li>Free memory, as you may notice approximately only 12Mb are used:</li>
<pre class="bash" name="code">$ free -m
total used free shared buffers
Mem: 1255 <b>12</b> 1243 0 0
-/+ buffers: 12 1243
Swap: 0 0 0
</pre>
<li>Network configuration/connectivity, both interfaces should be listed, <i>eth0</i> and <i>lo</i>:</li>
<pre class="bash" name="code">$ ifconfig
<b>eth0</b> Link encap:Ethernet HWaddr 08:00:07:26:c0:a5
inet addr:192.168.24.203 Bcast:192.168.24.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:378 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:43745 (42.7 KiB) TX bytes:1180 (1.1 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0 B) TX bytes:0 (0 B)
$ ping -c 3 192.168.24.202
PING 192.168.24.202 (172.26.24.202) 56(84) bytes of data.
64 bytes from 192.168.24.202: icmp_req=1 ttl=63 time=0.774 ms
64 bytes from 192.168.24.202: icmp_req=2 ttl=63 time=0.639 ms
64 bytes from 192.168.24.202: icmp_req=3 ttl=63 time=0.574 ms
--- 192.168.24.202 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.574/0.662/0.774/0.085 ms
</pre>
<li>Mounted partitions: </li>
<pre class="bash" name="code">$ mount
<b>rootfs on / type rootfs (rw)</b>
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,nosuid,nodev,relatime,mode=0755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
</pre>
<li>Disk usage, about 1.1Mb: </li>
<pre class="bash" name="code ">$ du -chs /*
1.1M /bin
0 /dev
28.0K /etc
4.0K /init
0 /lib
0 /lib32
0 /lib64
0 /linuxrc
0 /mnt
0 /proc
0 /root
36.0K /sbin
0 /sys
0 /tmp
20.0K /usr
0 /var
<b>1.1M total</b>
</pre>
<li>Loaded modules, since no module was loaded an empty list or a 'No such a file or directory' message is issued</li>
<pre class="bash" name="code">$ lsmod
lsmod: can't open '/proc/modules': No such file or directory
</pre>
<li>Device nodes created by device manager(mdev/udev). The result depends on many factors:</li>
<pre class="bash" name="code">$ find /dev | wc -l
<b>106</b>
</pre>
</ul>
<h2>
References</h2>
<ul>
<li><a href="http://www.linuxfordevices.com/c/a/Linux-For-Devices-Articles/Introducing-initramfs-a-new-model-for-initial-RAM-disks/">http://www.linuxfordevices.com/c/a/Linux-For-Devices-Articles/Introducing-initramfs-a-new-model-for-initial-RAM-disks/</a> </li>
<li><a href="http://processors.wiki.ti.com/index.php/Initrd">http://processors.wiki.ti.com/index.php/Initrd</a></li>
<li><a href="http://frank.harvard.edu/%7Ecoldwell/diskless/" target="_blank">Diskless Linux </a></li>
<li><a href="http://www.kernel.org/doc/Documentation/blockdev/ramdisk.txt" target="_blank">Ramdisk</a></li>
<li><a href="http://www.kernel.org/doc/Documentation/kernel-parameters.txt" target="_blank">Kernel parameters</a></li>
<li><a href="http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/buildroot.html" target="_blank">Building a root filesystem</a> </li>
<li><a href="http://lists.ozlabs.org/pipermail/linuxppc-dev/2007-April/034254.html" target="_blank">Cannot open ramdisk</a></li>
<li><a href="http://kerneltrap.org/node/17029" target="_blank">Failed to boot using ramfs</a></li>
<li><a href="http://wiki.centos.org/HowTos/Custom_Kernel" target="_blank">I need to build a custom kernel</a> </li>
<li><a href="http://wiki.centos.org/HowTos/I_need_the_Kernel_Source" target="_blank">I need the Kernel source</a> </li>
<li><a href="http://cateee.net/lkddb/web-lkddb/BLK_DEV_RAM_BLOCKSIZE.html" target="_blank">Default RAM disk block size (bytes) </a></li>
<li><a href="http://wiki.linuxquestions.org/wiki/Configuring_linux_kernel" target="_blank">Configuring Linux Kernel</a></li>
<li><a href="http://wiki.linuxquestions.org/wiki/Compiling_a_Linux_kernel" target="_blank">Compiling Linux Kernel</a></li>
<li><a href="http://www.fogonacaixadagua.com.br/2009/09/how-to-compile-a-new-kernel-in-linux-centos-red-hat/" target="_blank">How to compile a new kernel (2.6.30.5) in Linux CentOS 5.3 / Red Hat 5.3</a> </li>
<li><a href="http://cooker.techsnail.com/index.php/PXEClusterInstall" target="_blank">PXEClusterInstall</a> </li>
<li><a href="http://forums.opensuse.org/english/get-technical-help-here/install-boot-login/471730-pxe-boot-unable-mount-device-ram0.html" target="_blank">PXE boot unable to mount device ram0</a> </li>
<li><a href="http://www.linuxquestions.org/questions/gentoo-87/diskless-kernel-panic-not-syncing-no-init-found-try-passing-init%3D-option-to-kernel-798110/" target="_blank">Diskless: Kernel panic-not syncing: No init found. Try passing init= option to kernel</a></li>
<li><a href="http://www.ibm.com/developerworks/linux/library/l-initrd/index.html%20" target="_blank">http://www.ibm.com/developerworks/linux/library/l-initrd/index.html </a></li>
<li><a href="http://www.linux-kvm.org/page/Virtio">http://www.linux-kvm.org/page/Virtio</a></li>
<li><a href="http://linuxnewbie.internet.com/forum/showthread.php?p=859689">http://linuxnewbie.internet.com/forum/showthread.php?p=859689</a></li>
<li><a href="http://jootamam.net/howto-initramfs-image.htm">http://jootamam.net/howto-initramfs-image.htm</a> </li>
</ul>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com43tag:blogger.com,1999:blog-247964174508157859.post-42040763172079381122012-04-25T14:50:00.003-04:002012-04-25T14:50:27.570-04:00Matching IP address on BashI use bash very often, this is a tip on <i>how to </i>match an IPv4 address and address list using bash. It makes use of the Bash's matching operator ~=. <a href="https://pzt.me/683p#" target="_blank">Enjoy it!</a><br />
<br />Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-24358561604512553742012-04-17T16:37:00.003-04:002012-04-17T16:44:35.037-04:00A WSDL-first asynchronous JAX-WS webservice. Correlating the messages using WS-Addressing and a callback interface<div class="separator" style="clear: both; text-align: center;">
</div>
This <i>how to</i> is a WSDL-first (top-down) example of implementing a pure JAX-WS asynchronous web service with the following features:
<br />
<ul>
<li>the service, named <i> JobProcessor </i>is designed top-down, starting from WSDL and generating
the interface and classes</li>
<li>the service support <a href="http://en.wikipedia.org/wiki/WS-Addressing" target="_blank">WS-Addressing</a> for correlating the asynchronous request message with another request message targeted to callback endpoint</li>
<li>the service convention is
document/literal</li>
<li>the service supports
a single asynchronous operation: <i>JobProcessor.processJob()</i></li>
<li>the desired message exchange pattern is simple:</li>
<ul>
<li>a caller invokes the
<i> JobProcessor.processJob()</i> operation in asynchronous manner</li>
<li>a <b><wsa:MessageID/></b>'s WS-Addressing header is supplied in the request</li>
<li>a callback endpoint
is also supplied with the call using <a href="http://en.wikipedia.org/wiki/WS-Addressing" target="_blank">WS-Addressing</a> headers, specifically <b><i><wsa:ReplyTo/></i></b> </li>
<li>the service simulates
some time-consuming processing (hence the need for asynchronous WS)</li>
<li>once the processing
is finished, the service callbacks the caller issuing
correlation via <a href="http://en.wikipedia.org/wiki/WS-Addressing" target="_blank">WS-Addressing</a> headers, that is using <b><i><wsa:RelatesTo/></i></b> </li>
</ul>
<li>the service also acts
like a client when invokes the callback endpoint, so Messages,
PortTypes, Bindings, etc.. are also needed</li>
</ul>
<br />
<b>NOTE</b>: the term caller
denotes an external entity/user invoking the <i>JobProcessor</i> service,
the term client denotes the role of the service when acts like a
client
<br />
<br />
<h2>
Hands on</h2>
<br />
I use JDeveloper on this
<i>how to</i>, it can be easily done using another IDE.
<br />
<ol>
<li>Create an empty generic
application and project, named them <i>JobProcessorApp</i> and <i>JobProcessorProj</i>
respectively, add only <i>Java</i> and <i>Web Service</i> technologies. Choose a default package name like <i>dev.home.examples.jobprocessor</i> </li>
<li>Create an new folder,
named it <i>wsdl.</i> </li>
<li>Create a new XML Schema
archive into the <i>wsdl</i> folder, name it <i>jobprocessor.xsd</i> and use a
custom namespace like <i>http://examples.home.dev/jobprocessor/types</i> and a
significant prefix like <i>jpt</i> </li>
<li>Create a new WSDL Document into the <i>wsdl</i> folder, named it
<i>jobprocessor.wsdl</i>, use a custom namespace like:
<i>http://examples.home.dev/jobprocessor</i> </li>
<li>Edit the WSDL an replace
the default namespace prefix from <i>tns</i> to a significant one
like <i>jp</i>, and declares the XML
Schema namespace using the <i>jpt</i> prefix:
<i>xmlns:jpt="<a href="http://examples.home.dev/jp/types">http://examples.home.dev/jp/types</a>"</i> </li>
<li>Edit
the WSDL an add the namespace WS-Addressing for WSDL:
<i>xmlns:wsaw="<a href="http://www.w3.org/2006/05/addressing/wsdl">http://www.w3.org/2006/05/addressing/wsdl</a>"</i> </li>
<li>Include the XSD archive
into the schema section of WSDL:
<i>definitions/types/xsd:schema/xsd:include</i>, at this point the WSDL should look like:</li>
<pre class="xml" name="code"><?xml version="1.0" encoding="UTF-8" ?>
<definitions targetNamespace="http://examples.home.dev/jobprocessor"
xmlns:jpt="http://examples.home.dev/jobprocessor/types"
xmlns:jp="http://examples.home.dev/jobprocessor"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl">
<types>
<xsd:schema targetNamespace="http://examples.home.dev/jobprocessor/types"
elementFormDefault="qualified">
<xsd:include schemaLocation="jobprocessor.xsd"/>
</xsd:schema>
</types>
</definitions>
</pre>
<li>Define schema types and
elements for message payloads: <i>Job</i>, <i>JobType</i>, <i>JobReply</i> and
<i>JobReplyType</i>, the final XSD should look
like:</li>
<pre class="xml" name="code"><?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jpt="http://examples.home.dev/jobprocessor/types"
targetNamespace="http://examples.home.dev/jobprocessor/types"
elementFormDefault="qualified">
<xsd:element name="Job" type="jpt:JobType"/>
<xsd:complexType name="JobType">
<xsd:sequence>
<xsd:element name="jobId" type="xsd:string"/>
<xsd:element name="payload" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="JobReply" type="jpt:JobReplyType"/>
<xsd:complexType name="JobReplyType">
<xsd:sequence>
<xsd:element name="jobId" type="xsd:string"/>
<xsd:element name="result" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema></pre>
<li>Define WSDL messages: <i>Job</i>
and <i>JobReply</i> either with their corresponding element-based parts <i>job</i>
and <i>jobReply</i> </li>
<li>Define WSDL port types:
<i>JobProcessor</i> for the service interface and <i>JobProcessorNotify</i> for
callback interface with their corresponding operations <i>procesJob()
</i>and <i>replyFinishedJob(), </i>also specify the corresponding WS-Addressing actions for each input message, at this point the WSDL looks like:
</li>
<pre class="xml" name="code"><?xml version="1.0" encoding="UTF-8" ?>
<definitions targetNamespace="http://examples.home.dev/jobprocessor"
xmlns:jpt="http://examples.home.dev/jobprocessor/types"
xmlns:jp="http://examples.home.dev/jobprocessor"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl">
<types>
<xsd:schema targetNamespace="http://examples.home.dev/jobprocessor/types"
elementFormDefault="qualified">
<xsd:include schemaLocation="jobprocessor.xsd"/>
</xsd:schema>
</types>
<message name="Job">
<part name="job" element="jpt:Job"/>
</message>
<message name="JobReply">
<part name="jobReply" element="jpt:JobReply"/>
</message>
<portType name="JobProcessor">
<operation name="processJob">
<input message="jp:Job" wsaw:Action="http://examples.home.dev/jobprocessor/processJob"/>
</operation>
</portType>
<portType name="JobProcessorNotify">
<operation name="replyFinishedJob">
<input message="jp:JobReply" wsaw:Action="http://examples.home.dev/jobprocessor/replyFinishedJob"/>
</operation>
</portType>
</definitions>
</pre>
<li>Define document-literal bindings with HTTP transport for each port type <i>JobProcessor</i> and
<i>JobProcessorNotify</i>, enforce WS-Addressing using <b><i><wsaw:UsingAddressing required="true"/></i></b></li>
<li>Define the service <i>JobProcessor</i> and the callback service <i>JobProcessorNotify</i>, the final WSDL should look
like: </li>
<pre class="xml" name="code"><?xml version="1.0" encoding="UTF-8" ?>
<definitions targetNamespace="http://examples.home.dev/jobprocessor"
xmlns:jpt="http://examples.home.dev/jobprocessor/types"
xmlns:jp="http://examples.home.dev/jobprocessor"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl">
<types>
<xsd:schema targetNamespace="http://examples.home.dev/jobprocessor/types"
elementFormDefault="qualified">
<xsd:include schemaLocation="jobprocessor.xsd"/>
</xsd:schema>
</types>
<message name="Job">
<part name="job" element="jpt:Job"/>
</message>
<message name="JobReply">
<part name="jobReply" element="jpt:JobReply"/>
</message>
<portType name="JobProcessor">
<operation name="processJob">
<input message="jp:Job" wsaw:Action="http://examples.home.dev/jobprocessor/processJob"/>
</operation>
</portType>
<portType name="JobProcessorNotify">
<operation name="replyFinishedJob">
<input message="jp:JobReply" wsaw:Action="http://examples.home.dev/jobprocessor/replyFinishedJob"/>
</operation>
</portType>
<binding name="JobProcessor" type="jp:JobProcessor">
<wsaw:UsingAddressing wsdl:required="true"/>
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="processJob">
<soap:operation style="document"
soapAction="http://examples.home.dev/jobprocessor/processJob"/>
<input>
<soap:body use="literal" parts="job"/>
</input>
</operation>
</binding>
<binding name="JobProcessorNotify" type="jp:JobProcessorNotify">
<wsaw:UsingAddressing required="true"/>
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="replyFinishedJob">
<soap:operation style="document"
soapAction="http://examples.home.dev/jobprocessor/replyFinishedJob"/>
<input>
<soap:body use="literal" parts="jobReply"/>
</input>
</operation>
</binding>
<service name="JobProcessor">
<port name="jobProcessor" binding="jp:JobProcessor">
<soap:address location="http://localhost/JobProcessor"/>
</port>
</service>
<service name="JobProcessorNotify">
<port name="jobProcessorNotify" binding="jp:JobProcessorNotify">
<soap:address location="http://localhost/JobProcessorNotify"/>
</port>
</service>
</definitions>
</pre>
<li>Once finished the WSDL design, start coding. Go to <b>File</b> > <b>New</b> > <b>Java Web Service from WSDL</b> </li>
<li>Choose Java EE 1.5 with support for JAX-WS RI </li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPAQDN9HBfoVANQ_yP8KrP2No4AHmQdl0yihV6BJvzXJexqvTlGOJ8yiX_G4fiKKO8yl_VO1KiyDWv7_qVMNCrskjext8UymMFSRSOXj9MiYcjz14hzwQtUBT2Cr91ph6zPmDw6UefJ4Zn/s1600/1_deployment_platform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPAQDN9HBfoVANQ_yP8KrP2No4AHmQdl0yihV6BJvzXJexqvTlGOJ8yiX_G4fiKKO8yl_VO1KiyDWv7_qVMNCrskjext8UymMFSRSOXj9MiYcjz14hzwQtUBT2Cr91ph6zPmDw6UefJ4Zn/s320/1_deployment_platform.png" width="320" /></a></div>
<br />
<li>Choose the <i>jobprocessor.wsdl</i> archive, ensure that the interface is also generated by marking Add Service Endpoint Interface, also uncheck copy WSDL locally:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUga41aYmo_sF03oLIhHJ-13t4QOUDOziWPjTYlzlACsVbu_hjRFlr_Ay0Mv1aSf51x0innhbNNbB3mcxs6IROC6XUDVAc_8fJ0wN89XYx4sU8oSNu7wfd_6pfgTA3z7t0vX6ivmdMCju3/s1600/2_wsdl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUga41aYmo_sF03oLIhHJ-13t4QOUDOziWPjTYlzlACsVbu_hjRFlr_Ay0Mv1aSf51x0innhbNNbB3mcxs6IROC6XUDVAc_8fJ0wN89XYx4sU8oSNu7wfd_6pfgTA3z7t0vX6ivmdMCju3/s320/2_wsdl.png" width="320" /></a></div>
<br />
<li>Choose <i>JobProcessor</i> service</li>
<li>Enter <i>dev.home.examples.jobprocessor.ws</i> for the Package Name and <i>dev.home.examples.jobprocessor.types</i> for Root Package for Generated types (SDO types). </li>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix00KJIx4d8RMsYve2HBIWvHhZ4AK7spJjmT74KAPfvm9mBYzHzRZ5RXR_mV4mCcASxJcyMRBt_N7Jcq2QyjkuM1YcR2552kEVWT0f3CKIE0pF-AxebTfa_ti9BBhB9ePTgnXn-dnq1epK/s1600/3_packs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix00KJIx4d8RMsYve2HBIWvHhZ4AK7spJjmT74KAPfvm9mBYzHzRZ5RXR_mV4mCcASxJcyMRBt_N7Jcq2QyjkuM1YcR2552kEVWT0f3CKIE0pF-AxebTfa_ti9BBhB9ePTgnXn-dnq1epK/s320/3_packs.png" width="320" /></a></div>
<li>Choose <i>jobProcessor</i> port then click Finish</li>
<li>You will end with several classes and interfaces generated from the WSDL. But we won't implement the <i>JobProcessorNotify</i> service in Java, so you can delete the <i>JobProcessorNotify.java</i> and <i>JobProcessorNotifyImpl.java</i> </li>
<li>Annotate the <i>JobProcessorImpl</i> class to enforce Addressing with <i>javax.xml.ws.soap.Addressing</i>:</li>
<pre class="java" name="code">@Addressing(required = true)
public class JobProcessorImpl {
...
}
</pre>
<li>It's necessary to create a Java-based WS client to invoke the caller via the <i>JobProcessorNotify</i> PortType. Unfortunately JDeveloper 11.1.1.5.0 cannot do that in pure JAX-WS RI manner. Instead I use JDK's wsimport tool in this way, open a terminal/command prompt and type: </li>
<pre name="code">cd [PROJECT HOME DIRECTORY]
wsimport -p dev.home.examples.jobprocessor.client -d classes/ -s src/ \
-verbose wsdl/jobprocessor.wsdl
</pre>
<li>Then delete the unnecessary class <i>dev.home.examples.jobprocessor.client.JobProcessor_Service</i> and the interface <i>dev.home.examples.jobprocessor.client.JobProcessor </i>since we won't call ourselves</li>
<li>At this point you will notice that the SDO types <i>JobType</i> and <i>JobReplyType</i> were generated twice, the first time in the <i>dev.home.examples.jobprocessor.types</i> package and the second one on<i> dev.home.examples.jobprocessor.client</i> package, if you compare them you'll notice they are almost identical except for the package name. So, delete <i> dev.home.examples.jobprocessor.client.JobType </i>and <i> dev.home.examples.jobprocessor.client.JobReply</i> leaving the SDO only on the <i> dev.home.examples.jobprocessor.types</i> package. </li>
<li>Now the magic behind, the correlation is very simple: 1) grab request.MessageID and request.ReplyTo headers from the request message, and 2) set on the reply (a new request) message the reply.RelatesTo = MessageID and reply.To = ReplyTo. To achieve this I create a helper class named <i>CorrelationHelper</i>, it takes a <i>javax.xml.ws.Service</i> subtype as a template parameter, and pass a service of this type as constructor argument, a WebServiceContext is also needed:</li>
<pre class="java" name="code">/**
* Performs correlation between incoming one-way message and an outgoing
* one-way message by mapping the header wsa:To with wsa:ReplyTo and
* wsa:RelatesTo with wsa:MessageID
*
* @param <S> the service type
*/
public final class CorrelationHelper<S extends Service> {
private WebServiceContext wsc;
private S service;
public CorrelationHelper(S service, WebServiceContext wsc) {
this.service = service;
this.wsc = wsc;
}
/**
* Retrieves the headers
* @return
*/
private HeaderList getHeaders() {
return (HeaderList)wsc.getMessageContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
}
/**
* Creates the correlated port, by appending a WS-Addressing relatesTo
* header and assignind the MessageID
* @param <P> the port type, castable to WSBindingProvider
* @return the correlated port
*/
public <P> P getCorrelatedPort(Class<P> portType) {
P port = service.getPort(getReplyTo(), portType);
((WSBindingProvider)port).setOutboundHeaders(Headers.create(AddressingVersion.W3C.relatesToTag,
getMessageId()));
return port;
}
/**
* Grab WS-Addressing ReplyTo/Address header
* @return
*/
private EndpointReference getReplyTo() {
return getHeaders().getReplyTo(AddressingVersion.W3C,
SOAPVersion.SOAP_11).toSpec();
}
/**
* Grab WS-Addressing MessageID header
* @return
*/
private String getMessageId() {
return getHeaders().getMessageID(AddressingVersion.W3C,
SOAPVersion.SOAP_11);
}
}
</pre>
<li>The <i>processJob()</i> implementation is trivial:</li>
<pre class="java" name="code">@WebService(serviceName = "JobProcessor",
targetNamespace = "http://examples.home.dev/jobprocessor",
portName = "jobProcessor",
endpointInterface = "dev.home.examples.jobprocessor.ws.JobProcessor")
@HandlerChain(file = "JobProcessor-HandlerChain.xml")
@Addressing(required = true)
public class JobProcessorImpl {
@Resource
private WebServiceContext wsc;
private CorrelationHelper<JobProcessorNotify_Service> correlationHelper;
private Random random;
public void processJob(JobType job) {
// do processing
int seconds = doJob();
// prepare reply message
JobReplyType jobReply = new JobReplyType();
jobReply.setJobId(job.getJobId());
jobReply.setResult(String.format("Job payload %s processed in %d seconds!",
job.getPayload(), seconds));
// do correlation and perform the callback
JobProcessorNotify jobProcessorNotify =
correlationHelper.getCorrelatedPort(JobProcessorNotify.class);
jobProcessorNotify.replyFinishedJob(jobReply);
}
/**
* Sleeps random time between 5 and 10 seconds to simulate processing
* @return
*/
private int doJob() {
int seconds = random.nextInt(6) + 5;
try {
Thread.currentThread().sleep(1000 * seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
return seconds;
}
@PostConstruct
public void doPostConstruct() {
correlationHelper =
new CorrelationHelper<JobProcessorNotify_Service>(new JobProcessorNotify_Service(),
wsc);
random = new Random(System.nanoTime());
}
}
</pre>
<li>As you noticed, all the magic occurs in the <i>getCorrelatedPort()</i> template method, which takes a generic parameter <i>Class<P></i> and supplies the <b>wsa:ReplyTo</b> header as the endpoint argument in the <i>javax.xml.ws.Service.getPort() </i>invocation, then relates to the outgoing message with the incoming one via <b>wsa:RelatesTo = wsaMessageID </b> <i> </i></li>
<li>I also changed some initialization routines<i> </i>in the generated code<i>, </i>specially in <i>JobProcessorNotify_Service</i> to able to load the WSDL from the classpath, notice I changed the <i>wsdlLocation</i>'s <i>@WebServiceClient</i> parameter and <i>url</i> assigment statement:</li>
<pre class="java" name="code">@WebServiceClient(name = "JobProcessorNotify",
targetNamespace = "http://examples.home.dev/jobprocessor",
wsdlLocation = "classpath:wsdl/jobprocessor.wsdl")
public class JobProcessorNotify_Service extends Service {
private final static URL JOBPROCESSORNOTIFY_WSDL_LOCATION;
private final static WebServiceException JOBPROCESSORNOTIFY_EXCEPTION;
private final static QName JOBPROCESSORNOTIFY_QNAME =
new QName("http://examples.home.dev/jobprocessor",
"JobProcessorNotify");
static {
URL url =
ClassLoader.getSystemClassLoader().getResource("classpath:wsdl/jobprocessor.wsdl");
WebServiceException e = null;
JOBPROCESSORNOTIFY_WSDL_LOCATION = url;
JOBPROCESSORNOTIFY_EXCEPTION = e;
}
...
}
</pre>
<li>Finally notice that the generic class <i>CorrelationHelper<S extends Service></i> can be used in any kind of JAX-WS web service (hence the generic) that engages this message exchange pattern. </li>
</ol>
<br />
<br />
<h2>
What's next?</h2>
The testing of course:
<br />
<ol>
<li>Use <a href="http://www.soapui.org/" target="_blank">soapUI</a> to make your life easier. </li>
<li>Deploy the WS to a testing / staging weblogic/tomcat/... server, once deployed you will end up with an arbitrary endpoint address, e.g.: <u>http://HOST/JobProcessor</u></li>
<li>Create a new soapUI project by supplying the WSDL address, it should be <u>http://HOST/JobProcessor?WSDL</u> or simply use the system path's WSDL </li>
<li>Check "Create a Web Service Simulation from the imported WSDL" and uncheck everything else:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC5QyLEiiru1VyMBvSU8ZHcoz5ztbKrmtmwOH6ooX6kzTgPycLfzqG8eOn9Uw0vfbGAa4oqAh5xZuT8Rq6PL6GIbxeJZ1wN-T-hcE_47JzN4DG7rkLuOKvIn_GShVjTIkWT_NzBM95T8dx/s1600/soapui_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC5QyLEiiru1VyMBvSU8ZHcoz5ztbKrmtmwOH6ooX6kzTgPycLfzqG8eOn9Uw0vfbGAa4oqAh5xZuT8Rq6PL6GIbxeJZ1wN-T-hcE_47JzN4DG7rkLuOKvIn_GShVjTIkWT_NzBM95T8dx/s320/soapui_1.png" width="320" /></a></div>
<li>Generate the Mock service ONLY for <i>replyFinishedJob</i> operation, check Starts MockService immediately. </li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7LIMb5j3JgpQsL1zzajd6oHxA_QxAu0C4_hbp1wkkJ_po3QVQvJq1ez5wMai6ac8yD-S9YyrtHpMXT4_B7ZvQ4W1_ScUFqEIzL1oHIzFrFX_SK6FhToWRPxBAH14yNh0OTU6nZDpZkZEk/s1600/soapui_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7LIMb5j3JgpQsL1zzajd6oHxA_QxAu0C4_hbp1wkkJ_po3QVQvJq1ez5wMai6ac8yD-S9YyrtHpMXT4_B7ZvQ4W1_ScUFqEIzL1oHIzFrFX_SK6FhToWRPxBAH14yNh0OTU6nZDpZkZEk/s320/soapui_2.png" width="320" /></a></div>
<li>Name the mock JobProcessorNotify MockService, your mock will end up listening on the address <u>http://YOURPC:8080/mockJobProcessNotify</u></li>
<li>Go to JobProcessor / processJob, create a new request and fill it:</li>
<pre class="xml" name="code"><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:typ="http://examples.home.dev/jobprocessor/types">
<soapenv:Header/>
<soapenv:Body>
<typ:Job>
<typ:jobId>1</typ:jobId>
<typ:payload>Euardo Lago Aguilar</typ:payload>
</typ:Job>
</soapenv:Body>
</soapenv:Envelope>
</pre>
<li>In the request window's bottom, open WS-Addressing related settings, check Enable/Disable WS-A Addressing </li>
<li>The Action should be already filled up with: http://examples.home.dev/jobprocessor/processJob</li>
<li>Set To equal to: <u>http://HOST/JobProcessor </u>and also set the endpoint address (above) to the same value</li>
<li>Set ReplyTo equal to your mock listening address: <u>http://YOURPC:8088/mockJobProcessorNotify</u></li>
<li>Check Randomly generate MessageId, at the end the request should look like:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOLdkYxvqCw0fxl2HBlaXu993Xh_TL4qGyMiyO86rA23OAXEi5jT9C2E5s0u1qPWpyWtmlxQRkGlz5WYaWuMw_PzjckYzA6QTGFjHGuOO3BweayDloWNkaHvKNapRCLPuPfMUrc-hhjVIb/s1600/soapui_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOLdkYxvqCw0fxl2HBlaXu993Xh_TL4qGyMiyO86rA23OAXEi5jT9C2E5s0u1qPWpyWtmlxQRkGlz5WYaWuMw_PzjckYzA6QTGFjHGuOO3BweayDloWNkaHvKNapRCLPuPfMUrc-hhjVIb/s320/soapui_3.png" width="320" /></a></div>
<li>Send the request and you should receive a reply message within 5 and 10 seconds later, see the mock log in the picture below:</li>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjenydoF3RWTZoamiYKuqJkil6_m_QmUnY636NYF80FQpRb6ncm743STatJjk43cO55sTL3xyqb9A-Gk2wA-oWFhdjHfkfX797j9sMF84Jv_6mYGkB3KIR8Su6zTbSh8boTQfnAKIiv5wCw/s1600/soapui_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjenydoF3RWTZoamiYKuqJkil6_m_QmUnY636NYF80FQpRb6ncm743STatJjk43cO55sTL3xyqb9A-Gk2wA-oWFhdjHfkfX797j9sMF84Jv_6mYGkB3KIR8Su6zTbSh8boTQfnAKIiv5wCw/s320/soapui_4.png" width="320" /></a></div>
<br />
open the log entry by double clicking, the content should be:
<pre class="xml" name="code"><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<wsa:RelatesTo xmlns:wsa="http://www.w3.org/2005/08/addressing">uuid:732cf156-6de4-4701-9ae8-1aaa1c7a3bd9</wsa:RelatesTo>
<wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">http://YOURPC:8088/mockJobProcessorNotify</wsa:To>
<wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">http://examples.home.dev/jobprocessor/replyFinishedJob</wsa:Action>
<work:WorkContext xmlns:work="http://oracle.com/weblogic/soap/workarea/">rO0ABXoAA...AAAA=</work:WorkContext>
</S:Header>
<S:Body>
<JobReply xmlns="http://examples.home.dev/jobprocessor/types">
<jobId>1</jobId>
<result>Job payload Euardo Lago Aguilar processed in 8 seconds!</result>
</JobReply>
</S:Body>
</S:Envelope>
</pre>
</ol>
That's all folks! Thanks for be patient! Get the code here: <a href="https://docs.google.com/open?id=0BwdYbdsQ8bOYbHNsdTVTbUZ0Sk0">JobProcessorApp.tar.gz</a>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com6tag:blogger.com,1999:blog-247964174508157859.post-198896808578343642012-02-20T14:47:00.001-05:002012-02-20T14:47:37.386-05:00Installing NFS on CentOS 6.2This is a <i>how to</i> install the NFS service on a Linux CentOS 6.2 box and making it accessible to others. The scenario is the following:<br />
<ul>
<li>Grant <b>read-only</b> access to the <i>/home/public</i> directory to all networks</li>
<li>Grant <b>read/write</b> access to the <i>/home/common</i> directory to all networks </li>
</ul>
At the end of this guide you will get:
<br />
<ul>
<li>A running NFS server with various LAN shared directories</li>
<li>A active set of firewall rules allowing the access to NFS ports</li>
<li>A permanently mounted NFS shared on a CentOS / Ubuntu client </li>
</ul>
I assume you already have:<br />
<br />
<ul>
<li>a fresh running Linux CentOS 6.2 server </li>
<li>a <i>sudoer</i> user, named <i>bozz</i> on this guide</li>
<li>an accessible RPM repository / mirror</li>
<li>a Linux client with CentOS / Ubuntu</li>
</ul>
<br />
<h2>
Steps</h2>
<ol>
<li>Login as <i>bozz</i> user on the server</li>
<li>Check if <i>rpcbind</i> is installed:</li>
<blockquote>
<pre>$ rpm -q rpcbind
rpcbind-0.2.0-8.el6.x86_64
</pre>
</blockquote>
if not, install it:
<blockquote>
<pre>$ sudo yum install rpcbind
</pre>
</blockquote>
<li>Install NFS-related packages:</li>
<blockquote>
<pre>$ sudo yum install nfs-utils nfs-utils-lib
</pre>
</blockquote>
<li>Once installed, configure the <i>nfs</i>, <i>nfslock</i> and <i>rpcbind</i> to run as daemons:</li>
<blockquote>
<pre>$ sudo chkconfig --level 35 nfs on
$ sudo chkconfig --level 35 nfslock on
$ sudo chkconfig --level 35 rpcbind on
</pre>
</blockquote>
then start the <i>rpcbind</i> and <i>nfs</i> daemons:
<blockquote>
<pre>$ sudo service rpcbind start
$ sudo service nfslock start
$ sudo service nfs start
</pre>
</blockquote>
<b>NFS daemons</b>
<ul>
<li><b>rpcbind</b>: (<b>portmap</b> in older versions of Linux) the primary daemon upon which all the others rely, <i>rpcbind</i> manages connections for applications that use the RPC specification. By default, <i>rpcbind</i> listens to TCP port 111 on which an initial connection is made. This is then used to negotiate a range of TCP ports, usually above port 1024, to be used for subsequent data transfers. You need to run <i>rpcbind</i> on both the NFS server and client. </li>
<li><b>nfs</b>: starts the RPC processes needed to serve shared NFS file systems. The <i>nfs</i> daemon needs to be run on the NFS server only. </li>
<li><b>nfslock</b>: Used to allow NFS clients to lock files on the server via RPC processes. The <i>nfslock</i> daemon needs to be run on both the NFS server and client.</li>
</ul>
<br />
<li>Test whether NFS is running correctly with the <i>rpcinfo</i> command. You
should get a listing of running RPC programs that must include <i>mountd</i>, <i>
portmapper</i>, <i>nfs</i>, and <i>nlockmgr</i>:</li>
<br />
<blockquote>
<pre>$ rpcinfo -p localhost
program vers proto port service
100000 4 tcp 111 portmapper
100000 3 tcp 111 portmapper
100000 2 tcp 111 portmapper
100000 4 udp 111 portmapper
100000 3 udp 111 portmapper
100000 2 udp 111 portmapper
100024 1 udp 40481 status
100024 1 tcp 49796 status
100011 1 udp 875 rquotad
100011 2 udp 875 rquotad
100011 1 tcp 875 rquotad
100011 2 tcp 875 rquotad
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
100227 2 tcp 2049 nfs_acl
100227 3 tcp 2049 nfs_acl
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
100227 2 udp 2049 nfs_acl
100227 3 udp 2049 nfs_acl
100021 1 udp 32769 nlockmgr
100021 3 udp 32769 nlockmgr
100021 4 udp 32769 nlockmgr
100021 1 tcp 32803 nlockmgr
100021 3 tcp 32803 nlockmgr
100021 4 tcp 32803 nlockmgr
100005 1 udp 892 mountd
100005 1 tcp 892 mountd
100005 2 udp 892 mountd
100005 2 tcp 892 mountd
100005 3 udp 892 mountd
100005 3 tcp 892 mountd
</pre>
</blockquote>
<br />
<li><h3>
<span class="mw-headline" id="The_.2Fetc.2Fexports_File"></span></h3>
The <i>/etc/exports</i> file is the main NFS configuration file, and it
consists of two columns. The first column lists the directories you want
to make available to the network. The second column has two parts. The
first part lists the networks or DNS domains that can get access to the
directory, and the second part lists NFS options in brackets. Edit <i>/etc/exports</i> and append the desired shares:</li>
<blockquote>
<pre>$ sudo nano /etc/exports
</pre>
</blockquote>
then append:
<br />
<blockquote>
<pre>/home/public *(ro,sync,all_squash)
/home/common *(rw,sync,all_squash)
</pre>
</blockquote>
<ul>
<li>
<b>/home/public</b>: directory to share with read-only access to all networks</li>
<li><b>/home/common</b>: directory to share with read/write access to all networks </li>
<li><b>*</b>: allow access from all networks</li>
<li><b>ro</b>: read-only access</li>
<li><b>rw</b>: read/write access </li>
<li><b>sync</b>: synchronous access </li>
<li><b>root_squash</b>: prevents root users connected remotely from having root privileges and assigns them the user ID for the user <i>nfsnobody</i>. This effectively "squashes" the power of the remote root user to the lowest local user, preventing unauthorized alteration of files on the remote server. Alternatively, the <b>no_root_squash</b> option turns off root squashing. To squash every remote user, including root, use the <b>all_squash option</b>. To specify the user and group IDs to use with remote users from a particular host, use the <b>anonuid</b> and <b>anongid</b> options, respectively. In this case, a special user account can be created for remote NFS users to share and specify (<b>anonuid</b>=<uid-value>,<b>anongid</b>=<gid-value>), where <uid-value> is the user ID number and <gid-value> is the group ID number. </gid-value></uid-value></gid-value></uid-value></li>
</ul>
<br />
<li>Create the directories to be published with the correct permissions:</li>
<blockquote>
<pre>$ sudo mkdir -p /home/public
$ sudo chown nfsnobody:nfsnobody /home/public
$ sudo mkdir -p /home/common
$ sudo chown nfsnobody:nfsnobody /home/common
</pre>
</blockquote>
it should end like this:
<blockquote>
<pre>$ ls -l /home/
...
drwxr-xr-x. 2 nfsnobody nfsnobody 4096 Feb 20 12:55 common
drwxr-xr-x. 7 nfsnobody nfsnobody 4096 Feb 17 14:44 public
</pre>
</blockquote>
<li>[OPTIONAL] Allow <i>bozz</i> user to locally write on the created directories by appending it to nfsnobody group and granting write permissions to the group:</li>
<blockquote>
<pre>$ sudo usermod -a -G nfsnobody bozz
$ sudo chmod g+w /home/public
$ sudo chmod g+w /home/common
</pre>
</blockquote>
it should end like this:
<blockquote>
<pre>$ ls -l /home/
...
drwxrwxr-x. 2 nfsnobody nfsnobody 4096 Feb 20 12:40 common
drwxrwxr-x. 7 nfsnobody nfsnobody 4096 Feb 17 14:44 public
</pre>
</blockquote>
<li> <b>Security issues</b>. To allow remote access some firewall rules and other NFS settings must be changed. You need to open the following ports:</li>
<ul>
<li><b>TCP/UDP 111</b> - RPC 4.0 portmapper</li>
</ul>
<ul>
<li><b>TCP/UDP 2049</b> - NFSD (nfs server)</li>
</ul>
<ul>
<li><b>Portmap static ports</b>, Various TCP/UDP ports defined in <i>/etc/sysconfig/nfs</i> file.</li>
</ul>
the portmapper assigns each NFS service to a port dynamically at service startup time, but dynamic ports cannot be protected by <i>iptables</i>. First, you need to configure NFS services to use fixed ports. Edit <i>/etc/sysconfig/nfs</i>, enter:
<blockquote>
<pre>$ sudo nano /etc/sysconfig/nfs
</pre>
</blockquote>
and set:
<blockquote>
<pre>LOCKD_TCPPORT=32803
LOCKD_UDPPORT=32769
MOUNTD_PORT=892
RQUOTAD_PORT=875
STATD_PORT=662
STATD_OUTGOING_PORT=2020
</pre>
</blockquote>
then restart nfs daemons:
<blockquote>
<pre>$ sudo service rpcbind restart
$ sudo service nfs restart
</pre>
</blockquote>
update iptables rules by editing <i>/etc/sysconfig/iptables</i>, enter:
<blockquote>
<pre>$ sudo nano /etc/sysconfig/iptables</pre>
</blockquote>
and append the following rules:
<blockquote>
<pre>-A INPUT -s 0.0.0.0/0 -m state --state NEW -p udp --dport 111 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 111 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 2049 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 32803 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p udp --dport 32769 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 892 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p udp --dport 892 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 875 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p udp --dport 875 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p tcp --dport 662 -j ACCEPT
-A INPUT -s 0.0.0.0/0 -m state --state NEW -p udp --dport 662 -j ACCEPT
</pre>
</blockquote>
restart iptables daemon:
<blockquote>
<pre>$ sudo service iptables restart
</pre>
</blockquote>
<li><b>Mount NFS shared directories</b>: Install client NFS packages first:</li>
on Ubuntu client:
<blockquote>
<pre>$ sudo apt-get install nfs-common
</pre>
</blockquote>
on CentOS client:
<blockquote>
<pre>$ sudo yum install nfs-utils nfs-utils-lib
</pre>
</blockquote>
inquiry for the list of all shared directories:
<blockquote>
<pre>$ showmount -e SERVERADDRESS
</pre>
</blockquote>
mount server's <i>/home/public</i> on client's <i>/public</i>:
<blockquote>
<pre>$ sudo mkdir -p /public
$ sudo mount SERVERADDRESS:/home/public /public
$ df -h
</pre>
</blockquote>
mount server's <i>/home/common</i> on client's <i>/common</i>:
<blockquote>
<pre>$ sudo mkdir -p /common
$ sudo mount SERVERADDRESS:/home/common /common
$ df -h
</pre>
</blockquote>
<li><b>Mount NFS automatically after reboot</b> on the client. Edit <i>/etc/fstab</i>, enter:</li>
<blockquote>
<pre>$ sudo nano /etc/fstab
</pre>
</blockquote>
append the following line:
<blockquote>
<pre>#Directory Mount Point Type Options Dump FSCK
SERVER_IP_ADDRESS:/home/public /public nfs hard 0 0
SERVER_IP_ADDRESS:/home/common /common nfs hard 0 0
</pre>
</blockquote>
to test the correctness of /etc/fstab before restarting, you can try to manually mount /public and /common:
<blockquote>
<pre>$ sudo mount /public
$ sudo mount /common</pre>
</blockquote>
</ol>
<h2>
References</h2>
<ul>
<li><a href="http://www.cyberciti.biz/faq/centos-fedora-rhel-iptables-open-nfs-server-ports/" target="_blank">Linux Iptables Allow NFS Clients to Access the NFS Server</a> </li>
<li><a href="http://www.linuxhomenetworking.com/wiki/index.php/Quick_HOWTO_:_Ch29_:_Remote_Disk_Access_with_NFS" target="_blank">Remote disk access with NFS</a></li>
<li><a href="http://www.centos.org/docs/5/html/Deployment_Guide-en-US/s1-nfs-client-config.html" target="_blank">NFS Client configuration</a></li>
</ul>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com23tag:blogger.com,1999:blog-247964174508157859.post-44139172025381141082012-02-13T11:49:00.000-05:002012-02-13T11:49:10.904-05:00The Parking Permit Demo (5)Here I let you the episodes #7 - #10 of The Parking Permit Demo with Oracle BPM/SOA suite.<br />
<br />
<a href="http://www.youtube.com/watch?v=sleMwwpQ6MA" target="_blank">pp-07_Defining_Schema_Type_n_Element_for_ParkingPermitReply</a><br />
<ul>
<li>Defining XSD complexType & element for replies from verification services, naming it ParkingPermitReply</li>
</ul>
<br />
<a href="http://www.youtube.com/watch?v=Ar4LJMpjLP8" target="_blank">pp-08_Defining_WS_Messages_based_on_existing_Schema_Elements</a><br />
<ul>
<li>Creating two WSDL messages shared among the verification services, naming them ParkingPermitApplicationMsg & ParkingPermitReplyMsg</li>
</ul>
<ul>
<li>Basing the message parts on existing XSD elements ParkingPermitApplication & ParkingPermitReply respectively</li>
</ul>
<br />
<a href="http://www.youtube.com/watch?v=rBo7cY0qD8E" target="_blank">pp-09_Defining_Verification_Services_Interfaces_via_PortTypes__Part1</a><br />
<ul>
<li>Creating a portType for the Vehicle Verification Service, naming it VehicleOwnershipVerificationPortType, defining a single operation & calling it verifyVehicleOwnership </li>
</ul>
<ul>
<li>Setting the operation type (pattern) to Request/Response with input message ParkingPermitApplicationMsg and output message ParkingPermitReplyMsg</li>
</ul>
<br />
<a href="http://www.youtube.com/watch?v=nXds7nVl9AI" target="_blank">pp-10_Defining_Verification_Services_Interfaces_via_PortTypes__Part2</a><br />
<ul>
<li>Creating a portType for the Electoral Register Verification Service, naming it ElectoralRegisterVerificationPortType, verifyRegisterEligibility.</li>
</ul>
<ul>
<li>Defining a single operation & calling it </li>
</ul>
<ul>
<li>Setting the operation type (pattern) to Request/Response, reusing the input message ParkingPermitApplicationMsg and the output message ParkingPermitReplyMsg</li>
</ul>
<ul>
<li>Creating a portType for the Residence Verification Service, naming it ResidenceVerificationPortType, verifyResidenceElegibility.</li>
</ul>
<ul>
<li>Defining a single operation </li>
</ul>
<ul>
<li>Setting the operation type (pattern) to Request/Response, reusing the input message ParkingPermitApplicationMsg and the output message ParkingPermitReplyMsg</li>
</ul>
<br />
<br />Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-16563918790179720912012-02-09T12:55:00.000-05:002012-02-10T08:30:15.827-05:00What is the size of CentOS repository?How do we know the size of CentOS repository before download it all? I'm sure there are many ways to do that by using yum / yast2 or other tool.<br />
I'll try to achieve the same using "simple" commands presented in almost any Linux distribution. I let you <a href="https://pzt.me/8plv#" target="_blank">here the scrip</a>t. It's not 100% perfect, in fact isn't, but I think is a nice starting point, feel free to improve it.<br />
The script can be possible applied to RHEL, Oracle Linux and Fedora but I haven't test it against such distros. <br />
<br />
Enjoy it!<br />
<br />
<br />
I got the following results: <br />
<br />
<b>Summary</b> [Feb, 9 2012] (<i>Considering only the x86_64 architecture</i>)<br />
<br />
DISTRO SUMMARY http://mirror.centos.org/centos 5.7 : ~ 11 GB<br />
DISTRO SUMMARY http://mirror.centos.org/centos 6.2 : ~ 10 GB<br />
<br />
<br />
<br />
<b>TOTAL SIZE: ~22 GB</b><br />
<br />
<br />Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com0tag:blogger.com,1999:blog-247964174508157859.post-4969471440991226702012-02-08T18:14:00.002-05:002012-02-10T08:31:45.157-05:00What are the size of Ubuntu and Debian repositories?Before mirroring a complete Ubuntu / Debian repository on your hard drive, you want for sure have an estimation of the "weight" of all packages.<br />
<br />
<a href="https://pzt.me/6xbd#" target="_blank">This script </a>computes the total size of various Debian-based repositories by means of downloading the <i>Packages.gz</i> and making a sum of the size of every package. Of course, there are many shared packages between different versions of the same Linux distro. So this script will compute the max possible size.<br />
<br />
<i>Enjoy it!</i><br />
<br />
I got the following results:..<br />
<br />
<b>Summary</b> [Feb 9, 2012] <br />
<br />
<span style="font-size: x-small;">UBUNTU TOTAL SIZE = 466993 MB [456 GB] *</span><br />
<br />
<span style="font-size: x-small;">DEBIAN TOTAL SIZE = 184712 MB [180 GB] **</span><span style="font-size: x-small;"> </span><br />
<span style="font-size: x-small;">DEBIAN-SECURITY TOTAL SIZE = 22148 MB [21 GB] **</span><br />
<span style="font-size: x-small;">DEBIAN-VOLATILE TOTAL SIZE = 489 MB [0 GB]</span><span style="font-size: x-small;"> **</span><br />
<br />
<b><span style="font-size: x-small;">TOTAL SIZE: ~658 GB</span> </b>
<br />
<br />
<i>* Considering <b>lucid</b>, <b>maverick</b>, <b>natty</b>, <b>oneiric</b> and <b>precise</b>, with <span style="color: orange;">i386</span> and <span style="color: orange;">amd64</span> architectures, and the components <span style="color: blue;">main</span>, <span style="color: blue;">multiverse</span>, <span style="color: blue;">restricted</span> and <span style="color: blue;">universe</span></i><br />
<i>** Considering <b>lenny</b> and <b>squeeze</b> with <span style="color: orange;">i386</span>, <span style="color: orange;">amd64</span>, <span style="color: orange;">armel</span> and <span style="color: orange;">arm</span> architectures, and the components <span style="color: blue;">main</span>, <span style="color: blue;">contrib <span style="color: black;">and</span></span><span style="color: black;"> </span><span style="color: blue;">non-free</span></i>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com5tag:blogger.com,1999:blog-247964174508157859.post-53224615608794110402012-02-08T17:24:00.001-05:002012-02-08T17:24:55.428-05:00Booting Oracle 11g R2 Database Server on CentOS 6.2This is a<i> how to</i> autostart the Oracle 11g R2 Database Server on CentOS 6.2. I assume you have done the following:<br />
<br />
<ol>
<li><a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">Install an Oracle-ready CentOS 6.2 Linux box</a></li>
<li><a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-oracle-11g-database.html" target="_blank">Install the Oracle 11g R2 Database Server on the CentOS 6.2 Linux box</a></li>
<li><a href="http://eduardo-lago.blogspot.com/2012/02/configuring-network-listener-for-oracle.html" target="_blank">Configure a network listener for Oracle 11g R2 Database Server on the CentOS 6.2 Linux box</a></li>
<li><a href="http://eduardo-lago.blogspot.com/2012/02/creating-new-oracle-11g-r2-database-on.html" target="_blank">Create a fresh Oracle 11g R2 database on the CentOS 6.2 Linux box</a> </li>
</ol>
<br />
So, it's time to run the three installed components as Linux daemons. These components are: <i>listener</i>, <i>database</i> and <i>enterprise manager</i>. The main programs concerning the start and stop tasks for these components can be found at <i>$ORACLE_HOME/bin</i>, and they are:<br />
<br />
<ul>
<li><i>listener</i>: <b>lsnrctl</b> {start|stop|...}</li>
<li><i>database</i>: <b>dbstart</b> / <b>dbshut</b></li>
<li><i>ent. manager</i>: <b>emctl</b> {start|stop|...}</li>
</ul>
<br />
<br />
<ol>
<li>Login as the <i>bozz </i>user (a <i>sudoer</i>) in the server and create the archive<i> /etc/init.d/oracle</i> with the following content:</li>
<blockquote>
<pre>#!/bin/bash
# oracle: Start/Stop Oracle Database 11g R2
#
# chkconfig: 345 90 10
# description: The Oracle Database Server is an RDBMS created by Oracle Corporation
#
# processname: oracle
. /etc/rc.d/init.d/functions
LOCKFILE=/var/lock/subsys/oracle
ORACLE_HOME=/opt/app/oracle/product/11.2.0/db_1/
ORACLE_USER=oracle
case "$1" in
'start')
if [ -f $LOCKFILE ]; then
echo $0 already running.
exit 1
fi
echo -n $"Starting Oracle Database:"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/lsnrctl start"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/dbstart $ORACLE_HOME"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/emctl start dbconsole"
touch $LOCKFILE
;;
'stop')
if [ ! -f $LOCKFILE ]; then
echo $0 already stopping.
exit 1
fi
echo -n $"Stopping Oracle Database:"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/lsnrctl stop"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/dbshut $ORACLE_HOME"
su - $ORACLE_USER -c "$ORACLE_HOME/bin/emctl stop dbconsole"
rm -f $LOCKFILE
;;
'restart')
$0 stop
$0 start
;;
'status')
if [ -f $LOCKFILE ]; then
echo $0 started.
else
echo $0 stopped.
fi
;;
*)
echo "Usage: $0 [start|stop|status]"
exit 1
esac
exit 0
</pre>
</blockquote>
<li>First, ensure that the<i> init.d</i> script can be executed manually:</li>
<blockquote>
<pre>$ sudo chmod +x /etc/init.d/oracle
$ sudo /etc/init.d/oracle start
</pre>
</blockquote>
if so, then stop it:
<blockquote>
<pre>$ sudo /etc/init.d/oracle stop
</pre>
</blockquote>
<li>Use <i>chkconfig</i> to register the init.d script on runlevels 3, 4 and 5:
<blockquote>
<pre>$ sudo chkconfig --add oracle
</pre>
</blockquote>
then verify if is marked as <b>on</b> in the runleves 3, 4 and 5:
<blockquote>
<pre>$ chkconfig --list oracle
oracle 0:off 1:off 2:off <b> 3:on 4:on 5:on</b> 6:off
</pre>
</blockquote>
</li>
<li>Reboot and enjoy!</li>
</ol>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com95tag:blogger.com,1999:blog-247964174508157859.post-77488633499790660832012-02-06T16:13:00.001-05:002018-08-28T15:49:54.834-04:00Configuring a network listener for Oracle on CentOS 6.2You require to <i>configure</i> and <i>run</i> a network listener before creating a database with the Oracle's Database Configuration Assistant (<i>dbca</i>) tool. This is a <i>how to</i> configure such a listener.<br />
<br />
I assume you already install a <a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-oracle-11g-database.html" target="_blank">Oracle 11g Database Server on CentOS 6.2</a> with <a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">SSH X11 Forwarding</a> and you are using a Linux client with a Desktop Environment like Gnome or KDE.<br />
<ol>
<li>Login on the server using <i>oracle</i> user with <a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">SSH X11 Forwarding</a>:</li>
<blockquote>
<pre>$ ssh -Y oracle@SERVER
</pre>
</blockquote>
<li>You already had the <i>$ORACLE_HOME/bin</i> on the PATH environment variable (see <i>~/.bash_profile</i> <a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-oracle-11g-database.html" target="_blank">here</a>) so you can issue:</li>
<blockquote>
<pre>$ netca</pre>
</blockquote>
<li>Choose Listener Configuration on the Welcome panel:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ2VnVG-Sao63uw5QFnRv5FyXLzIyW1MjJBwlHKIlwUGPP-16WrEto5xHWYZfme2olV-aYfZTXOR4GvW2uDr3S8CnDfj6r_caf4trAKBykF-DnTw9q-43X62cdj60dl8eEumoiASHQtL5n/s1600/lst1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ2VnVG-Sao63uw5QFnRv5FyXLzIyW1MjJBwlHKIlwUGPP-16WrEto5xHWYZfme2olV-aYfZTXOR4GvW2uDr3S8CnDfj6r_caf4trAKBykF-DnTw9q-43X62cdj60dl8eEumoiASHQtL5n/s320/lst1.png" width="320" /></a></div>
<br />
<li>Select Add as the action:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjI49JI1t0SJiiKoJvuXOJ44bokXSfM_vhnp1BRPPAtWIUiHTdVUdpoc0e9GzmgSqKUiQ8JfbaHbl542muvV2ft2M-Z_IVd6jlTLuycukknX03OfDvtwq3Jz_BxGG3Y-Y3U_u4HeH7RULH/s1600/lst2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjI49JI1t0SJiiKoJvuXOJ44bokXSfM_vhnp1BRPPAtWIUiHTdVUdpoc0e9GzmgSqKUiQ8JfbaHbl542muvV2ft2M-Z_IVd6jlTLuycukknX03OfDvtwq3Jz_BxGG3Y-Y3U_u4HeH7RULH/s320/lst2.png" width="320" /></a></div>
<br />
<li>Type a listener name, I recommend the default "LISTENER":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6qO2QTDgQhSN3KK3SSF1FXA4SuOIwQmoyBpv67_lFv_2rcyDC2Z_7K2SKh3EduuRAkSIeBk3YLWFfnvTSYY9IvlfBZHXDbTmDsvgmXKStVUQxOczhsY1kVCjbZ8_URpSXxeaAYZNKHrlK/s1600/lst3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6qO2QTDgQhSN3KK3SSF1FXA4SuOIwQmoyBpv67_lFv_2rcyDC2Z_7K2SKh3EduuRAkSIeBk3YLWFfnvTSYY9IvlfBZHXDbTmDsvgmXKStVUQxOczhsY1kVCjbZ8_URpSXxeaAYZNKHrlK/s320/lst3.png" width="320" /></a></div>
<br />
<li>Select protocols among the available, I choose TCP only:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTSjbyEmPAh04KW_Z4Mb698wI-cflKQ7Q4YQvI-5VpcsAfqwNrRJnB-sox-y-ZrpfY0tm8FjCzLU5UtY5fkl3Xn5XmkdpPCCKB6Jyi-Mh157bri9TvxNRU1Z7DjsDKWoftx_CJDYtV4bHO/s1600/lst4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTSjbyEmPAh04KW_Z4Mb698wI-cflKQ7Q4YQvI-5VpcsAfqwNrRJnB-sox-y-ZrpfY0tm8FjCzLU5UtY5fkl3Xn5XmkdpPCCKB6Jyi-Mh157bri9TvxNRU1Z7DjsDKWoftx_CJDYtV4bHO/s320/lst4.png" width="320" /></a></div>
<br />
<li>Choose a listening port, I recommend the default "1521" but you can change it if you want. <b>NOTE</b>: Remember to open this port on <a href="http://www.cyberciti.biz/tips/linux-iptables-examples.html" target="_blank"><i>iptables</i></a> if you want remote database access, otherwise you won't be able to use it:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXYGoZEoWx1aErSw9O8a6NiLC0cyd4QU62ca4swuzFnH7yC0lg8-mbGZKEMbYshzNkYlALGm3CpHaEo3LhmnQ0yUduiVAHdbF-uJejpbxO6APMbvjEk3cJURBmiavD6NFceQCbB3J5WRfr/s1600/lst5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXYGoZEoWx1aErSw9O8a6NiLC0cyd4QU62ca4swuzFnH7yC0lg8-mbGZKEMbYshzNkYlALGm3CpHaEo3LhmnQ0yUduiVAHdbF-uJejpbxO6APMbvjEk3cJURBmiavD6NFceQCbB3J5WRfr/s320/lst5.png" width="320" /></a></div>
<br />
<li>On More Listeners panels, choose No.</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgrZZX53SpdQ-zLiA2eSav27Cxvv9iqp316VgYWsFsrmOr239Y_eExvVq_ZcsEBx2wiAzfIHGPO1yIh8nVkIeiwD-2OxVlxQvZN4HEXCkF0vSupQIJSiqBkKXAztalvPBTF21ZTdkDJnmj/s1600/lst6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgrZZX53SpdQ-zLiA2eSav27Cxvv9iqp316VgYWsFsrmOr239Y_eExvVq_ZcsEBx2wiAzfIHGPO1yIh8nVkIeiwD-2OxVlxQvZN4HEXCkF0vSupQIJSiqBkKXAztalvPBTF21ZTdkDJnmj/s320/lst6.png" width="320" /></a></div>
<li>If you finish gracefully the wizard, you should be able to see this on the standard output. Observe that the listener is running.</li>
<blockquote>
<pre>$ netca
Oracle Net Services Configuration:
Configuring Listener:LISTENER
Listener configuration complete.
Oracle Net Listener Startup:
Running Listener Control:
<b>/opt/app/oracle/product/11.2.0/db_1/bin/lsnrctl start LISTENER</b>
Listener Control complete.
Listener started successfully.
Oracle Net Services configuration successful. The exit code is 0
</pre>
</blockquote>
you can manually stop the listener by issuing: <blockquote>
<pre>$ <b>lsnrctl stop LISTENER</b>
</pre>
</blockquote>
then start the listener by: <blockquote>
<pre>$ <b>lsnrctl start LISTENER</b>
</pre>
</blockquote>
and check the listener status: <blockquote>
<pre>$ <b>lsnrctl status LISTENER</b>
LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 06-FEB-2012 16:53:04
Copyright (c) 1991, 2009, Oracle. All rights reserved.
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=myserver)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for Linux: Version 11.2.0.1.0 - Production
Start Date 06-FEB-2012 16:49:47
Uptime 0 days 0 hr. 3 min. 17 sec
Trace Level off
Security ON: Local OS Authentication
SNMP OFF
Listener Parameter File /opt/app/oracle/product/11.2.0/db_1/network/admin/listener.ora
Listener Log File /opt/app/oracle/diag/tnslsnr/soabpm/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=soabpm)(PORT=1521)))
The listener supports no services
The command completed successfully
</pre>
</blockquote>
or verify if the listener is alive and locally reachable via TNS ping: <blockquote>
<pre>$ tnsping localhost 1521
TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 06-FEB-2012 16:55:34
Copyright (c) 1997, 2009, Oracle. All rights reserved.
Used parameter files:
Used HOSTNAME adapter to resolve the alias
Attempting to contact (DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=))(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)))
OK (0 msec)
...
OK (0 msec)
</pre>
</blockquote>
to ensure the same thing remotely you must issue. <b>NOTE</b>: Remember to configure <a href="http://www.cyberciti.biz/tips/linux-iptables-examples.html" target="_blank"><i>iptables</i></a> to allow remote access. <blockquote>
<pre>$ tnsping myserver 1521
</pre>
</blockquote>
<b>NOTE</b>: Remember you must a have a running listener before <a href="http://eduardo-lago.blogspot.com/2012/02/creating-new-oracle-11g-r2-database-on.html" target="_blank">creating a database</a>.
<li>One final step is to add the <i>ORACLE_HOME_LISTNER</i> environment variable to <i>oracle</i>'s <i>~/.bash_profile</i>. Login to the server as oracle user and edit:</li>
<blockquote>
<pre>$ nano ~/.bash_profile
</pre>
</blockquote>
then append: <blockquote>
<pre>export ORACLE_HOME_LISTNER=LISTENER
</pre>
</blockquote>
at the end the <i>~/.bash_profile</i> archive should look like: <blockquote>
<pre># .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
umask 022
export TMPDIR=$TMP
export ORACLE_BASE=/opt/app/oracle
export ORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
export PATH=$ORACLE_HOME/bin:$PATH
# The SID for a database, See http://eduardo-lago.blogspot.com/2012/02/creating-new-oracle-11g-r2-database-on.html
export ORACLE_SID=demo
export ORACLE_HOME_LISTNER=LISTENER
</pre>
</blockquote>
</ol>
<h2>
What to do next?</h2>
<ul>
<li><a href="http://eduardo-lago.blogspot.com/2012/02/creating-new-oracle-11g-r2-database-on.html" target="_blank">Creating a new Oracle 11g R2 Database on CentOS 6.2</a></li>
<li><a href="http://eduardo-lago.blogspot.com/2012/02/booting-oracle-11g-r2-database-server.html" target="_blank">Run Oracle 11g R2 Database as a Linux daemon on CentOS 6.2</a></li>
<li>An excellent article about IPTABLES by <span style="background-color: white; color: #212121; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; white-space: nowrap;">Mokhtar Ebrahim</span><span style="background-color: white; color: #212121; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: 700; white-space: nowrap;">, </span> <a href="https://likegeeks.com/linux-iptables-firewall-examples/" target="_blank">Linux iptables Firewall Simplified Examples</a> </li>
</ul>
Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com4tag:blogger.com,1999:blog-247964174508157859.post-1531145242451770672012-02-06T15:17:00.001-05:002012-02-08T17:33:57.580-05:00Creating a new Oracle 11g R2 Database on CentOS 6.2Once you installed an <a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-oracle-11g-database.html" target="_blank">Oracle 11g R2 Database Server on CentOS 6.2</a> the next step is to create a new empty and fresh database for your own use. You must have a <a href="http://eduardo-lago.blogspot.com/2012/02/configuring-network-listener-for-oracle.html" target="_blank">running listener </a>before creating a database.<br />
<br />
I assume you are using a Linux client (CentOS, Ubuntu, ...) with a desktop environment installed (Gnome, KDE, ...).<br />
<br />
The steps are straightforward and I gonna choose a simple deployment configuration.<br />
<br />
<ol>
<li>Login on the server with <i>oracle</i> user with <a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">SSH X11 Forwarding</a>:</li>
<blockquote>
<pre>$ ssh -Y oracle@SERVER
</pre>
</blockquote>
<li>You already had the <i>$ORACLE_HOME/bin</i> on the PATH environment variable (see <i>~/.bash_profile</i> <a href="http://eduardo-lago.blogspot.com/2012/01/how-to-install-oracle-11g-database.html" target="_blank">here</a>) so you can issue:</li>
<blockquote>
<pre>$ dbca
</pre>
</blockquote>
<li>An X11 windows should be launched on your Linux desktop, on the Operations panel choose "Create a database":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo_O30bgKiC_KNV_ZNeMtkcuiq4as5FIEb_gC0aO7o6Dyvkx-RpWBBtNSgtpaSHkeRKMphhtfYM3iAmYOpll6TxbJzYtHv1lVpT5yI9rkwfZ9YpgkMIDiuMexw3UH4qLuTwELmcxIHtJW-/s1600/db1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo_O30bgKiC_KNV_ZNeMtkcuiq4as5FIEb_gC0aO7o6Dyvkx-RpWBBtNSgtpaSHkeRKMphhtfYM3iAmYOpll6TxbJzYtHv1lVpT5yI9rkwfZ9YpgkMIDiuMexw3UH4qLuTwELmcxIHtJW-/s320/db1.png" width="320" /></a></div>
<li>Select "General Purpose or transaction Processing" on the Database Templates panel:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyYXaEztyWNCC5ca6SQ5a6M51oq80RAqlL16U-7GSYkM83MmhQkDATCK7kfL6goC8SpXHdocQ4HI6bVOkplAOT34PQ_2xoVzpvyxI7weowQPPYEDYMMfLN5f0sh0I7Vje-qJ15056vRHu0/s1600/db2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyYXaEztyWNCC5ca6SQ5a6M51oq80RAqlL16U-7GSYkM83MmhQkDATCK7kfL6goC8SpXHdocQ4HI6bVOkplAOT34PQ_2xoVzpvyxI7weowQPPYEDYMMfLN5f0sh0I7Vje-qJ15056vRHu0/s320/db2.png" width="320" /></a></div>
<li>On Database Identification Panel put a Global Database Name "<i>demo.home.dev</i>" and Oracle System Identifier (SID) "<i>demo</i>". <b>NOTE</b>: on this example, I used "<i>demo.home.dev</i>" as the global database name and "demo" for the SID, but I recommend to let it as simple as possible and choose "<i>demo</i>" for both:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjKcaN8WDGH38dOp4HGJNewxzqrPBBlLrCbB0_V9oGSTmpbprvGeI97si1CXsSBp4kDYtsagHymEvTNzrm9vWVofJOzukL9SdISjRWkNGsw3zhDpDaEChf0dfpXAKBZqF2GMLSQiRhw5xU/s1600/db3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjKcaN8WDGH38dOp4HGJNewxzqrPBBlLrCbB0_V9oGSTmpbprvGeI97si1CXsSBp4kDYtsagHymEvTNzrm9vWVofJOzukL9SdISjRWkNGsw3zhDpDaEChf0dfpXAKBZqF2GMLSQiRhw5xU/s320/db3.png" width="320" /></a></div>
<li>On Management Options panel check "Configure Enterprise Manager" and "Configure Database Control for local management"</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3rISgfXR0ppgeikz0lmhj119-K8F9amsGgtWSGwxFk7cuRfXzDNC5lc8RuALfr_QRnwSt9JlLH0-UHfj9T6cacfbCE2f11hylI37U-xBp-sCOpH3fWuUXNdS70O7hIOvMVPA-57QlrMYu/s1600/db4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3rISgfXR0ppgeikz0lmhj119-K8F9amsGgtWSGwxFk7cuRfXzDNC5lc8RuALfr_QRnwSt9JlLH0-UHfj9T6cacfbCE2f11hylI37U-xBp-sCOpH3fWuUXNdS70O7hIOvMVPA-57QlrMYu/s320/db4.png" width="320" /></a></div>
<li>On Database Credentials panel I choose "Use the Same Administrative Password for All Accounts" but you can make a different choice and set each password individually:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_IU13XWDKRrvhDs_aIJuH04nwqD7hSqr3xrACVO3CzF0jEeLq9iI_bP4CR2bjXP0NgH74TVQChxaVNphIbv4g6Z72_-1rhn2WGgA7dolBNmtCrTvQoYDmM4hHhMMGzGktxXWg8dq8tYhg/s1600/db5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_IU13XWDKRrvhDs_aIJuH04nwqD7hSqr3xrACVO3CzF0jEeLq9iI_bP4CR2bjXP0NgH74TVQChxaVNphIbv4g6Z72_-1rhn2WGgA7dolBNmtCrTvQoYDmM4hHhMMGzGktxXWg8dq8tYhg/s320/db5.png" width="320" /></a></div>
<li>On the Storage Options panel I choose "File System":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFFfcgMsm3Qgb4gxoKAZM8RHf_M9hoZSFQWH2FDVDRy2onTf2lDwqHotis8V7_vhBV1ZR8xTTZqNDnMAaKUcLl5d82uR1Sie5hvjJTmUEPEUzU64wbgHZVaBfX2sJEpEl_3le7LE8SHcDL/s1600/db6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFFfcgMsm3Qgb4gxoKAZM8RHf_M9hoZSFQWH2FDVDRy2onTf2lDwqHotis8V7_vhBV1ZR8xTTZqNDnMAaKUcLl5d82uR1Sie5hvjJTmUEPEUzU64wbgHZVaBfX2sJEpEl_3le7LE8SHcDL/s320/db6.png" width="320" /></a></div>
<li>I let the Database File Locations with the defaults:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCOktZ3dTFbSAQRn1P6bAlm8X7UzhA4qfPgemXwQZG6DK-dNEnXYpsKZnF6kxgTDPC51Hrz4kLCu38ewPaj6U2qhyphenhyphenowXzv0SdlRt9A5l3gog67dJlsbVIDOOt7AVqnwPZcrW4OmHIREKAN/s1600/db7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCOktZ3dTFbSAQRn1P6bAlm8X7UzhA4qfPgemXwQZG6DK-dNEnXYpsKZnF6kxgTDPC51Hrz4kLCu38ewPaj6U2qhyphenhyphenowXzv0SdlRt9A5l3gog67dJlsbVIDOOt7AVqnwPZcrW4OmHIREKAN/s320/db7.png" width="320" /></a></div>
<li>Also the default settings on Recovery Configuration:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifZjAL4_dVV3Jr8OTYqa2QJLW_vYEa_NGkubbqX_OiU0pEUVjg5j_WplLF_kNFTg6XnKiShNoJ0QJKUap0E4mYkoAxkU6fYvB7dPKSYQRMBmPbDJ9lte5rUYw8Fn-f-lsA38MLuhtf-UOX/s1600/db8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifZjAL4_dVV3Jr8OTYqa2QJLW_vYEa_NGkubbqX_OiU0pEUVjg5j_WplLF_kNFTg6XnKiShNoJ0QJKUap0E4mYkoAxkU6fYvB7dPKSYQRMBmPbDJ9lte5rUYw8Fn-f-lsA38MLuhtf-UOX/s320/db8.png" width="320" /></a></div>
<li> On database content I didn't touch anything:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeHHD6lyPC7dfeC6ie1pkoAtpJz7GpwFBvXfY1rTY-IrSYWBlO3bwRXJwnGguKqwhl4spZH_v99ODTdrMr8HmVkht-7clnpyWHNebXuuOhTaWr1abnl3dIMnuPGQ61e_wl_hWqzbe_iSUy/s1600/db9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeHHD6lyPC7dfeC6ie1pkoAtpJz7GpwFBvXfY1rTY-IrSYWBlO3bwRXJwnGguKqwhl4spZH_v99ODTdrMr8HmVkht-7clnpyWHNebXuuOhTaWr1abnl3dIMnuPGQ61e_wl_hWqzbe_iSUy/s320/db9.png" width="320" /></a></div>
<li>You can tweak the database Initialization Parameters according to your system configuration and usage scenario:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx4-erOA1Nf7cbFYK0v-gV5stI1Fxu-2ExgBk_LW3AOFmytIPEZqm_0IGmvFOTon0UIVYtAq0BVgcq_YwhuP-32NXcRvN0LzaUC8-zfSQvkYv31e6KESCn2aOQMO260qup4elV42iLavHG/s1600/db10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx4-erOA1Nf7cbFYK0v-gV5stI1Fxu-2ExgBk_LW3AOFmytIPEZqm_0IGmvFOTon0UIVYtAq0BVgcq_YwhuP-32NXcRvN0LzaUC8-zfSQvkYv31e6KESCn2aOQMO260qup4elV42iLavHG/s320/db10.png" width="320" /></a></div>
<li>On Security Settings panel I recommend to use the enhanced 11g default security as well:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8SZmn_lk_SK1BTKYv7FlC8dpla_UUlQnqhsPJH6T7BvkBMm6qOsh4NA6J72Ew9YbyakRs5BEeiQ5qEY1sB3hnqVn5oflLb4zNCXvsgopzjEb4EKYEfDCcGFdVcvcxHGoXfETV0Qnv96l9/s1600/db11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8SZmn_lk_SK1BTKYv7FlC8dpla_UUlQnqhsPJH6T7BvkBMm6qOsh4NA6J72Ew9YbyakRs5BEeiQ5qEY1sB3hnqVn5oflLb4zNCXvsgopzjEb4EKYEfDCcGFdVcvcxHGoXfETV0Qnv96l9/s320/db11.png" width="320" /></a></div>
<li>I also enabled the Automatic Maintenance Tasks at certain schedule:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT7yqdNCVGif0CsPDC5mrFl8CfGWTW71ZGhmYiJKnw7XJJALa3d4zzSVbEAEAFOwlP7qGwBBk-bwj7cq3nhwrIwBEfuQiOmiUd_25dGEnUt9_W-RptGaWbNxB9AlDBiaW0MkD7MbWxp92Z/s1600/db12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT7yqdNCVGif0CsPDC5mrFl8CfGWTW71ZGhmYiJKnw7XJJALa3d4zzSVbEAEAFOwlP7qGwBBk-bwj7cq3nhwrIwBEfuQiOmiUd_25dGEnUt9_W-RptGaWbNxB9AlDBiaW0MkD7MbWxp92Z/s320/db12.png" width="320" /></a></div>
<li>I used defaults Database Storage options:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-WPMJE_D6Ae4xAjSnOaWUesUbp0MdpS6rbCmj31Avsu1hlB6sCIcvXAI7KBOWB_O0D4zdUKmlK5n1N-SE2UtQR89L4cu-wLp9m09vbX_NPomMDckKUQwlCUj5TvR_YeJrXN6ZVmNXCKbD/s1600/db13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-WPMJE_D6Ae4xAjSnOaWUesUbp0MdpS6rbCmj31Avsu1hlB6sCIcvXAI7KBOWB_O0D4zdUKmlK5n1N-SE2UtQR89L4cu-wLp9m09vbX_NPomMDckKUQwlCUj5TvR_YeJrXN6ZVmNXCKbD/s320/db13.png" width="320" /></a></div>
<li>On Creation Options panel don't forget to check the Create Database and optionally check Save as Database Template if you want to reuse this settings further:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgprpDTbtp4MqvNxaqVYcmBwdx9VceDTMOWAny81HEVaAnlHC73llrysJaO-eQmZQ08sDcicJwPhWqoN3FF-rgdjsxYMuDJptuUN17qi4zbPf2Jsc1DwEm03eR9KZbOHjAs1zA4f0DmABJU/s1600/db14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgprpDTbtp4MqvNxaqVYcmBwdx9VceDTMOWAny81HEVaAnlHC73llrysJaO-eQmZQ08sDcicJwPhWqoN3FF-rgdjsxYMuDJptuUN17qi4zbPf2Jsc1DwEm03eR9KZbOHjAs1zA4f0DmABJU/s320/db14.png" width="320" /></a></div>
<li>You should be able to see an install progress like this:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisI-jL7v7hzP-aScfMogIwHMm3hHk1CU_XRud09RAYyMDtqNpUPAujDveIE0HzaYp6TXKUDu9TvL7DLuFrNTxfkUjdgKMriEc-vgjZvsrOpm-mfnHTGIPaQm6KvYf6SehjRgiY0_yNzeEC/s1600/db15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisI-jL7v7hzP-aScfMogIwHMm3hHk1CU_XRud09RAYyMDtqNpUPAujDveIE0HzaYp6TXKUDu9TvL7DLuFrNTxfkUjdgKMriEc-vgjZvsrOpm-mfnHTGIPaQm6KvYf6SehjRgiY0_yNzeEC/s320/db15.png" width="320" /></a></div>
<li>An finally a summary screen like this:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHbpR_SQw-xIqlHdiAADul2bgarv2pOATmIsOeAk0AaS1TcZ5oWW6PykOBLrO6-bcmZdtIjYpO-W3Du-fRX17XcghrJO7nnAQgD5ns64nlXQL5wRrI-1CwiWZPEfk8y35AWC0KJQVssvdl/s1600/db16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHbpR_SQw-xIqlHdiAADul2bgarv2pOATmIsOeAk0AaS1TcZ5oWW6PykOBLrO6-bcmZdtIjYpO-W3Du-fRX17XcghrJO7nnAQgD5ns64nlXQL5wRrI-1CwiWZPEfk8y35AWC0KJQVssvdl/s320/db16.png" width="320" /></a></div>
Once finished you should had a fresh empty database.<br />
<br />
<li>Finnally add the <i>ORACLE_SID</i> environment variable to <i>oracle</i>'s <i>~/.bash_profile</i>. Login to the server as <i>oracle</i> user and edit: </li>
<blockquote>
<pre>$ nano ~/.bash_profile
</pre>
</blockquote>
then append:
<blockquote>
<pre>export ORACLE_SID=demo
</pre>
</blockquote>
at the end the ~/.bash_profile archive should look like:
<blockquote>
<pre># .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
umask 022
export TMPDIR=$TMP
export ORACLE_BASE=/opt/app/oracle
export ORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_SID=demo
export ORACLE_HOME_LISTNER=LISTENER
</pre>
</blockquote>
<li><b><span style="color: red;">DON'T FORGET</span></b> to set the recently created database in <i>autostart mode</i> at <i>/etc/oratab</i>. Edit with <i>oracle</i> user: </li>
<blockquote>
<pre>$ nano /etc/oratab
</pre>
</blockquote>
and it should look like. Notice the red Y:
<blockquote>
<pre># This file is used by ORACLE utilities. It is created by root.sh
# and updated by the Database Configuration Assistant when creating
# a database.
# A colon, ':', is used as the field terminator. A new line terminates
# the entry. Lines beginning with a pound sign, '#', are comments.
#
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<n|y>:
#
# The first and second fields are the system identifier and home
# directory of the database respectively. The third filed indicates
# to the dbstart utility that the database should , "Y", or should not,
# "N", be brought up at system boot time.
#
# Multiple entries with the same $ORACLE_SID are not allowed.
#
#
fresh:/opt/app/oracle/product/11.2.0/db_1:<b><span style="color: red;">Y</span></b>
</n|y></pre>
</blockquote>
<li>One final thing to do is to test the 3 main components installed: <i>listener</i>, <i>database</i> and <i>enterprise manager</i>. Login as <i>oracle</i> user and issue:</li>
<blockquote>
<pre>$ <b>lsnrctl start</b>
LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 08-FEB-2012 16:44:03
...
<span style="color: red;">The command completed successfully</span>
</pre>
</blockquote>
then run. (<b>NOTE</b>: you must have the autostart mode for the database. See previous step):
<blockquote>
<pre>$ <b>dbstart $ORACLE_HOME</b>
Processing Database instance "fresh": log file /opt/app/oracle/product/11.2.0/db_1/startup.log
...
</pre>
</blockquote>
and finally the Enterprise Manager:
<blockquote>
<pre>$ <b>emctl start dbconsole</b>
Oracle Enterprise Manager 11g Database Control Release 11.2.0.1.0
Copyright (c) 1996, 2009 Oracle Corporation. All rights reserved.
https://SERVERNAME:1158/em/console/aboutApplication
Starting Oracle Enterprise Manager 11g Database Control ......... started.
...
</pre>
</blockquote>
<li>
<b>To verify if you can connect to the database</b>, on the server issue the following command and enter the password supplied on the Database Credentials panel:</li>
<br />
<blockquote>
<pre>$ sqlplus sys@demo AS SYSDBA
SQL*Plus: Release 11.2.0.1.0 Production on Mon Feb 6 17:36:28 2012
Copyright (c) 1982, 2009, Oracle. All rights reserved.
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL>
</pre>
</blockquote>
then issue the following query:
<br />
<blockquote>
<pre>SQL> select * from dual;
D
-
X
</pre>
</blockquote>
<li><b>To check if the listener, database and enterprise manager are running</b>, open a browser at the URL:<span style="color: blue;"> </span><i><span style="color: blue;">https://SERVERNAME:1158/em/console/aboutApplication</span>. </i><b>NOTE</b>: Remember to open this port on <a href="http://www.cyberciti.biz/tips/linux-iptables-examples.html" target="_blank"><i>iptables</i></a> if you want remote database access, otherwise you won't be able to use it.
</li>
</ol>
<h2>
What to do next?</h2>
<ul>
<li><a href="http://eduardo-lago.blogspot.com/2012/02/booting-oracle-11g-r2-database-server.html" target="_blank">Create a init script to make Oracle database start automatically on system bootin</a> </li>
</ul>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com7tag:blogger.com,1999:blog-247964174508157859.post-72989473412258022952012-01-24T16:43:00.000-05:002012-02-10T09:36:34.878-05:00How to install KVM and libvirt on CentOS 6.2 with bridged networkingThis is a <i>how to</i> install the <a href="http://www.linux-kvm.org/" target="_blank">KVM hypervisor</a> and <a href="http://libvirt.org/" target="_blank">libvirt</a> virtualization library on Linux CentOS 6.2. At the end of this guide you will have a CentOS box (name it host) with the following capabilities:<br />
<ul>
<li><i>virtualization capacity</i>: chance for multiple guests (Linux/Windows) running and sharing the host's hardware </li>
<li><i>bridge network configuration</i>: you'll be able to directly access to the guests as if they were physical machines on the same LAN and viceversa.</li>
<li><i>visual management</i>: using <i>virt-manager</i> (I use Ubuntu 11.10 on this <i>how to</i>) you will be able to remotely admin the virtual machines on the CentOS host.</li>
</ul>
<h2>
What's what?</h2>
<h3>
KVM:</h3>
<blockquote class="tr_bq">
<i>"KVM (for Kernel-based Virtual Machine) is a full virtualization solution
for Linux on x86 hardware containing virtualization extensions (Intel
VT or AMD-V). It consists of a loadable kernel module, kvm.ko, that
provides the core virtualization infrastructure and a processor specific
module, kvm-intel.ko or kvm-amd.ko..."
<br />
"Using KVM, one can run multiple virtual machines running unmodified Linux or Windows images. Each virtual machine has private virtualized hardware: a network card, disk, graphics adapter, etc."</i></blockquote>
<h3>
libvirt:</h3>
<blockquote class="tr_bq">
<i>
</i><br />
<ul><i>
<li>"A toolkit to interact with the virtualization capabilities of recent versions of Linux... "</li>
<li>"A Free software available under the GNU Lesser General Public License."</li>
<li>"A long term stable C API"</li>
<li>"A set of bindings for common languages"</li>
</i></ul>
<i>
</i></blockquote>
<h3>
Intel-VT and AMD-V</h3>
<ul>
<li><a href="http://www.linux-kvm.org/page/FAQ#What_is_Intel_VT_.2F_AMD-V_.2F_hvm.3F" target="_blank">What is Intel VT / AMD-V / hvm?</a>. </li>
<li><a href="http://www.linux-kvm.org/page/FAQ#How_can_I_tell_if_I_have_Intel_VT_or_AMD-V.3F" target="_blank">How can I tell if I have Intel VT or AMD-V?</a> </li>
</ul>
<h2>
Requirements
</h2>
<ul>
<li><b>Hardware</b>: </li>
<ul>
<li>Processor with support for <a href="http://ark.intel.com/VTList.aspx" target="_blank">Intel-VT</a> or <a href="http://www.amd.com/us-en/Processors/ProductInformation/0,,30_118_8826_14287,00.html" target="_blank">AMD-V</a> technology</li>
<li>Plenty RAM memory depending on the number of guests</li>
<li>Network connectivity </li>
</ul>
<li><b>Software</b>: </li>
<ul>
<li>A <a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">previosly installed Linux CentOS 6.2</a> with a recent Linux kernel. On this <i>how to</i>:</li>
<blockquote>
<pre>$ uname -r
2.6.32-220.2.1.el6.x86_64
$ lsb_release -a
LSB Version: :core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
Distributor ID: CentOS
Description: CentOS release 6.2 (Final)
Release: 6.2
Codename: Final
</pre>
</blockquote>
<li>A Linux client with a Desktop environment installed</li>
<li>An available CentOS mirror/repository</li>
</ul>
</ul>
<h2>
Installation Steps</h2>
<ol>
<li>Login into the CentOS as <a href="http://eduardo-lago.blogspot.com/2012/01/step-by-step-installing-oracle-ready.html" target="_blank">bozz</a> user (a sudoer user) and check if your hardware support for virtualization extensions, on my hardware it was:</li>
<blockquote>
<pre>$ egrep '^flags.*(vmx|svm)' /proc/cpuinfo
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ida dts tpr_shadow vnmi flexpriority ept vpid
</pre>
</blockquote>
<li>Install <i>kvm</i> and <i>libvirt</i> packages:</li>
<blockquote>
<pre>$ sudo yum install kvm libvirt</pre>
</blockquote>
<li>Update both packages to the latest version available on repositories/mirrors: </li>
<blockquote>
<pre>$ sudo yum update libvirt kvm
...
$ yum info libvirt
Installed Packages
Name : libvirt
Arch : x86_64
Version : 0.9.4
Release : 23.el6_2.4
...
$ yum info qemu-kvm
Installed Packages
Name : qemu-kvm
Arch : x86_64
Epoch : 2
Version : 0.12.1.2
Release : 2.209.el6_2.1
...
</pre>
</blockquote>
<li>Restart the <i>libvirtd</i> daemon:</li>
<blockquote>
<pre>$ sudo service libvirtd restart</pre>
</blockquote>
<li>Verify if the <i>kvm</i> module is loaded, you should see <i>amd</i> or <i>intel</i> depending on the hardware:</li>
<blockquote>
<pre>$ lsmod | grep kvm
kvm_intel 50380 0
kvm 305113 1 kvm_intel
</pre>
</blockquote>
<li>Issue a <i>virsh</i> command to ensure local <i>root</i> connectivity first:</li>
<blockquote>
<pre>$ sudo virsh sysinfo
<sysinfo type="smbios">
...
</pre>
</blockquote>
<li>[OPTIONAL] To use KVM by a non-<i>root</i> user verify if <i>kvm</i> group was created on installation:</li>
<blockquote>
<pre>$ cat /etc/group | grep kvm
kvm:x:36:qemu
</pre>
</blockquote>
then add the <i>bozz</i> user to <i>kvm</i> group, so it can gain access to hypervisor:
<blockquote>
<pre>$ sudo usermod -a -G kvm bozz
$ logout</pre>
</blockquote>
login again as the <i>bozz</i> user and verify <i>kvm</i>'s membership:
<blockquote>
<pre>$ id
uid=500(bozz) gid=500(bozz) groups=500(bozz),10(wheel),36(kvm) context=...</pre>
</blockquote>
and verify if <i>/dev/kvm</i> is owned by group <i>kvm</i>:
<blockquote>
<pre>$ ls -l /dev/kvm
crw-rw-rw-. 1 root kvm 10, 232 Jan 17 14:50 /dev/kvm
</pre>
</blockquote>
on a system that runs udev, you will probably need to add the following line in your udev configuration so it will automatically give the right group to the newly created device:
<blockquote>
<pre>$ cat /etc/udev/rules.d/80-kvm.rules
KERNEL=="kvm", GROUP="kvm", MODE="0666"
</pre>
</blockquote>
<li>To manage <i>libvirt</i> with a non-<i>root</i> account you should use <a href="http://wiki.libvirt.org/page/SSHPolicyKitSetup" target="_blank">PolicyKit</a>. Define access control for a <i>libvirt</i> group:</li>
<blockquote>
<pre>$ sudo groupadd libvirt
$ sudo usermod -a -G libvirt bozz
$ logout
</pre>
</blockquote>
login again as <i>bozz</i> user and edit a new archive:
<blockquote>
<pre>$ sudo nano /etc/polkit-1/localauthority/50-local.d/50-libvirt-remote-access.pkla</pre>
</blockquote>
with this content:
<blockquote>
<pre>[libvirt Management Access]
# For allowing access to specific user only:
#Identity=unix-user:bozz
# For allowing access to a group (like this guide):
Identity=unix-group:libvirt
Action=org.libvirt.unix.manage
ResultAny=yes
ResultInactive=yes
ResultActive=yes
</pre>
</blockquote>
restart <i>libvirtd</i> daemon:
<blockquote>
<pre>$ sudo service libvirtd restart</pre>
</blockquote>
verify if <i>bozz</i> user can locally access to <i>qemu:///system</i> (<b>NOTE</b>: the use of <a href="http://wiki.libvirt.org/page/FAQ#What_is_the_difference_between_qemu:.2F.2F.2Fsystem_and_qemu:.2F.2F.2Fsession.3F_Which_one_should_I_use.3F" target="_blank"><i>qemu:///session</i> is discouraged</a>):<blockquote>
<pre>$ virsh -c qemu:///system sysinfo
<sysinfo type="smbios">
...</pre>
</blockquote>
verify if <i>bozz</i> user can remotely access to <i>qemu+ssh://bozz@SERVER/system</i> too. So on the Linux client issue:
<br /> <br />
for Ubuntu client (like in this guide):
<blockquote>
<pre>$ sudo apt-get install libvirt-bin</pre>
</blockquote>
for CentOS client:
<blockquote>
<pre>$ sudo yum install libvirt</pre>
</blockquote>
then:
<blockquote>
<pre>$ virsh -c qemu+ssh://bozz@SERVER/system sysinfo
<sysinfo type="smbios">
...</pre>
</blockquote>
change group ownership and permissions on the default images directory:
<blockquote>
<pre>$ sudo chown root:libvirt /var/lib/libvirt/images
$ sudo chmod g+rw /var/lib/libvirt/images</pre>
</blockquote>
<li>[OPTIONAL] When <i>libvirtd</i> (<i>>= 0.9.3</i>) is running as non-<i>root</i> it tries to read
<i>~/.libvirt/libvirtd.conf </i>(see <a href="http://comments.gmane.org/gmane.comp.emulators.libvirt.user/1539">here</a>). A workaround is: </li>
<blockquote>
<pre>$ mkdir -p ~/.libvirt
$ touch ~/.libvirt/libvirtd.conf
</pre>
</blockquote>
then issue a <i>virsh</i> command as <i>bozz</i> user:
<blockquote>
<pre>$ virsh list
Id Name State
----------------------------------
</pre>
</blockquote>
<li>Configure Bridged Network by creating a new network script at <i>/etc/sysconfig/network-scripts/ifcfg-br0</i>:</li>
<blockquote>
<pre>$ sudo nano /etc/sysconfig/network-scripts/ifcfg-br0
</pre>
</blockquote>
and configuring the parameters according to your LAN settings (<b>NOTE</b>: options are case sensitive i.e. Bridge and bridge are two different options):
<blockquote>
<pre>DEVICE="br0"
TYPE=Bridge
DELAY=0
ONBOOT="yes"
BOOTPROTO=static
IPADDR=192.168.11.12
NETMASK=255.255.255.0
NETWORK=192.168.11.0
GATEWAY=192.168.11.1
DNS1=192.168.11.2
PEERDNS="yes"
NM_CONTROLLED=no
</pre>
</blockquote>
then edit the Ethernet network script <i>/etc/sysconfig/network-scripts/ifcfg-eth0</i>:
<blockquote>
<pre>$ sudo nano /etc/sysconfig/network-scripts/ifcfg-eth0</pre>
</blockquote>
with the following content (<b>NOTE</b>: the hardware address depends on your NIC, an arbitrary MAC address is used here):
<blockquote>
<pre>DEVICE="eth0"
HWADDR="00:2C:C2:85:29:A3"
ONBOOT="yes"
BRIDGE=br0
NM_CONTROLLED=no
</pre>
</blockquote>
restart the networking service:
<blockquote>
<pre>$ sudo service network restart</pre>
</blockquote>
and verify the bridge config:
<blockquote>
<pre>$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.002cc28529a3 no eth0
...
</pre>
</blockquote>
configure iptables to allow all traffic to be forwarded across the bridge:
<blockquote>
<pre>$ sudo iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
$ sudo service iptables save
$ sudo service iptables restart</pre>
</blockquote>
prevent bridged traffic from being processed by iptables rules, this improves the bridge’s performance. In <i>/etc/sysctl.conf</i> append the following lines:
<blockquote>
<pre>net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0</pre>
</blockquote>
reload the kernel parameters configured with sysctl:
<blockquote>
<pre>$ sudo sysctl -p /etc/sysctl.conf</pre>
</blockquote>
restart the libvirt daemon.
<blockquote>
<pre>$ sudo service libvirtd reload</pre>
</blockquote>
</ol>
<h2>
Post-install Steps</h2>
<ul>
<li>Creating a virtual machine / guest by means of Virtual Machine Manager (<i>virt-manager</i>) from the Linux client machine. The guest OS will be an Ubuntu Server 11.10 Oneiric Ocelot:</li>
<br /> <br />
for Ubuntu client (like in this guide):
<blockquote>
<pre>$ sudo apt-get install virt-manager</pre>
</blockquote>
for CentOS client:
<blockquote>
<pre>$ sudo yum install virt-manager</pre>
</blockquote>
then download <a href="http://releases.ubuntu.com/11.10/ubuntu-11.10-server-amd64.iso"><i>oneiric-server-amd64.iso</i></a> from the Ubuntu site on the Linux client:
<blockquote>
<pre>$ wget http://releases.ubuntu.com/11.10/ubuntu-11.10-server-amd64.iso</pre>
</blockquote>
copy the downloaded ISO to SERVER<i></i>:
<blockquote>
<pre>$ scp ubuntu-11.10-server-amd64.iso bozz@SERVER:/var/lib/libvirt/images/</pre>
</blockquote>
on the Linux client run <i>virt-manager</i>:
<blockquote>
<pre>$ virt-manager &
</pre>
</blockquote>
goto "File" / "Add Connection..." with this settings:
<blockquote>
<ul>
<li><b>Hypervisor</b>: QEMU/KVM</li>
<li><b>Method</b>: ssh</li>
<li><b>Username</b>: bozz</li>
<li><b>Host</b>: SERVER </li>
</ul>
</blockquote>
once connected click on the "Create a new virtual machine" button.<br />
<ol>
<li>Set the name to ubuntu-oneiric and choose "Local install media (ISO image or CDROM)":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh46yThnVE2xAMMaxRTSJ-xcTZj4WDvFnpCSkPXhj3RK3ca4QmXYjYFuYsIF8tG_hFAAP5BOCHB0NxvmHhfr2iGDuKNRlA5te_1OQCjq8WHHjwSJW5WrcfH4Mgr-g04VNWEP5OudylZFkAy/s1600/oneiric_vm_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh46yThnVE2xAMMaxRTSJ-xcTZj4WDvFnpCSkPXhj3RK3ca4QmXYjYFuYsIF8tG_hFAAP5BOCHB0NxvmHhfr2iGDuKNRlA5te_1OQCjq8WHHjwSJW5WrcfH4Mgr-g04VNWEP5OudylZFkAy/s320/oneiric_vm_01.png" width="274" /></a></div>
<li>Browse and select the ISO located at <i>/var/lib/libvirt/images/ubuntu-11.10-server-amd64.iso</i>, OS type "Linux" and Version "Ubuntu 11.10 (Oneiric Ocelot)":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixd7IiflO86KDREAlSFT-lmcxYEK__lU-syf4zHnkYgwW11xyp4p1lqKFmJfkhSDdeaRreGi0pHrxWWcW2Mmwff2STgbFIDzyKJ2BfBDOhGjW0kC7GGghC-1QnFQN3HLtV6JhgENeXC_-P/s1600/oneiric_vm_02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixd7IiflO86KDREAlSFT-lmcxYEK__lU-syf4zHnkYgwW11xyp4p1lqKFmJfkhSDdeaRreGi0pHrxWWcW2Mmwff2STgbFIDzyKJ2BfBDOhGjW0kC7GGghC-1QnFQN3HLtV6JhgENeXC_-P/s320/oneiric_vm_02.png" width="302" /></a></div>
<li>Choose memory and CPU setting as you wish:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf2zKj2EKn5Bc40QvngI4zHUEpbC6gFVm7rccqOTQiuKHlyX_aQJhoi_eviz0u0pasEAB-hqERlkaDoRzsiemfZIBF43ObQGCIDQcslBLKIicJEb5a-F_lT7DYsVyUGCIN1DdtGvYMho_O/s1600/oneiric_vm_03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf2zKj2EKn5Bc40QvngI4zHUEpbC6gFVm7rccqOTQiuKHlyX_aQJhoi_eviz0u0pasEAB-hqERlkaDoRzsiemfZIBF43ObQGCIDQcslBLKIicJEb5a-F_lT7DYsVyUGCIN1DdtGvYMho_O/s320/oneiric_vm_03.png" width="269" /></a></div>
<li>Choose storage settings:</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmb_jJSv72UO1MTAqVKZF-ULLXCgdieiwU98I07SQCHjESMEr6yDDV7WxEDIpijF3FQTglYpjNuxibbaFkTWRFWWLPwef2ZcaXA-fJL9hDAI_ZtgJgC9IjRuy2D0upl86z0JxMlOLf_dUg/s1600/oneiric_vm_04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmb_jJSv72UO1MTAqVKZF-ULLXCgdieiwU98I07SQCHjESMEr6yDDV7WxEDIpijF3FQTglYpjNuxibbaFkTWRFWWLPwef2ZcaXA-fJL9hDAI_ZtgJgC9IjRuy2D0upl86z0JxMlOLf_dUg/s320/oneiric_vm_04.png" width="269" /></a></div>
<li>Choose the previously created bridge network device "Host device eth0 (Bridge 'br0')", Virt type "kvm" and Architecture "x86_64":</li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj37diyrQQu8Uxla0uAhSlHVHIbUbzdufFKsvzgVz1d38KN-za2p8te8cWU7UFYdjuOyw0MUbKgiTn5v71_mUcIFhiP6vPP4GqAoJlpjOmPkGfIBDpt0jxSYmuHn0CBhdIpWcqF-BSc_O8d/s1600/oneiric_vm_05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj37diyrQQu8Uxla0uAhSlHVHIbUbzdufFKsvzgVz1d38KN-za2p8te8cWU7UFYdjuOyw0MUbKgiTn5v71_mUcIFhiP6vPP4GqAoJlpjOmPkGfIBDpt0jxSYmuHn0CBhdIpWcqF-BSc_O8d/s320/oneiric_vm_05.png" width="269" /></a></div>
press "Finish" button and install the guest OS.
</ol>
<br />
<h2>
References</h2>
<ul>
<li><a href="http://www.linux-kvm.org/" target="_blank">KVM</a> </li>
<li><a href="http://www.linux-kvm.org/page/FAQ" target="_blank">KVM FAQ</a></li>
<li><a href="http://libvirt.org/" target="_blank">libvirt</a></li>
<li><a href="http://www.cyberciti.biz/faq/rhel-linux-kvm-virtualization-bridged-networking-with-libvirt/" target="_blank">CentOS / Redhat: KVM Bridged Network Configuration</a></li>
<li><a href="http://wiki.centos.org/HowTos/KVM" target="_blank">HowTos/KVM</a></li>
<li><a href="http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/5/html/Virtualization/sect-Virtualization-Network_Configuration-Bridged_networking_with_libvirt.html" target="_blank">Bridged networking with libvirt </a><br /><pre><i><i>
</i></i></pre>
</li>
</ul>
</ul>Eduardo Lago Aguilarhttp://www.blogger.com/profile/03779472227942300526noreply@blogger.com2