new file: content/2011-01-02-a-homegrown-python-bread-crumb-module.rst

new file:   content/2011-01-05-how-did-stack-overflow-get-initial-traction.rst
	new file:   content/2011-01-09-graphic-design-powerhouse-wacom-bamboo-ubuntu-and-mypaint.rst
	new file:   content/2011-01-10-i-just-used-google-to-purchase-chinese-food-takeout-for-two.rst
	new file:   content/2011-01-11-response-to-l-theanine-a-4000-year-old-mind-hack.rst
	new file:   content/2011-03-20-virt-back-a-python-libvirt-backup-utility-for-kvm-xen-virtualbox.rst
	new file:   content/2011-03-26-porting-the-chaostheory-wordpress-theme-to-pylowiki.rst
	new file:   content/2011-04-03-voice-over-ip-with-teamspeak.rst
	new file:   content/2011-04-16-dropbox-encryption-with-truecrypt.rst
	new file:   content/2011-06-11-connecticut-killed-affliate-marketing-with-amazon-com.rst
	new file:   content/2011-06-18-att-uverse-residential-gateway-broadband-led-flashes-red-intermittently.rst
	new file:   content/2011-06-25-google-bot-attempts-to-crawl-shortest-urls-first.rst
	new file:   content/2011-07-02-add-a-breadcrumb-subscriber-to-a-pyramid-project-using-4-simple-steps.rst
	new file:   content/2011-07-11-a-hack-to-gain-80-percent-efficiency-when-creating-github-projects.rst
	new file:   content/2011-08-07-the-great-gist-heist.rst
	new file:   content/2011-08-08-programming-is-like-alchemy.rst
	new file:   content/2011-08-10-virt-back-restoring-from-backups.rst
	new file:   content/2011-08-18-security-professionals-yes-we-appear-vulnerable-but-that-attack-vector-will-never-happen.rst
	new file:   content/2011-08-22-python-image-grabber-pig-py.rst
	new file:   content/2011-09-06-deliberating-the-viewers-vs-doers-concept.rst
	new file:   content/2011-09-08-a-system-administrators-guide-to-installing-and-maintaining-multiple-python-environments.rst
	new file:   content/2011-09-09-hacker-olive-oil-lamp-crafted-from-home-materials.rst
	new file:   content/2011-09-27-new-baby-homecoming.rst
	new file:   content/2011-10-11-css-frameworks-not-rendering-properly-on-all-browsers.rst
	new file:   content/2011-10-12-a-better-way-to-show-website-backlinks.rst
	new file:   content/2011-10-15-i-cancelled-my-xbox-live-automatic-renewal.rst
	new file:   content/2011-10-15-r8168-driver-issues-after-ubuntu-11-10-upgrade-kernel-linux-3-0.rst
	new file:   content/2011-10-16-cell-shading-practice-a-sketched-skull-and-wind-mill-photograph.rst
	new file:   content/2011-10-20-adding-inline-image-support-to-a-gmail-messages.rst
	new file:   content/2011-10-21-occupy-wall-street-stack-vs-queue.rst
	new file:   content/2011-10-29-webmaster-tools-alerted-issue-turned-out-pylon-session-files-flooded-inodes.rst
	new file:   content/2011-11-03-im-petrified-of-launching-my-web-application.rst
	new file:   content/2011-11-29-career-development-is-a-game-of-chutes-and-ladders.rst
	new file:   content/2011-11-30-how-to-incorporate-custom-configuration-in-a-pyramid-application.rst
	new file:   content/2011-12-05-linkpeek-com-number-one-on-hacker-news.rst
	new file:   content/2011-12-12-flash-mob-office-meeting-definition.rst
	new file:   content/2012-01-18-today-i-lost-a-customer.rst
	new file:   content/2012-02-08-reasons-why-some-internet-entities-might-want-secure-communication.rst
	new file:   content/2012-02-08-what-are-the-differences-between-message-confidentiality-and-message-integrity.rst
	new file:   content/2012-02-13-attributes-of-an-8-block-cipher.rst
	new file:   content/2012-02-13-monoalphabetic-cipher-and-inverse-written-in-python.rst
	new file:   content/2012-02-13-symmetric-encryption-vs-public-key-encryption.rst
	new file:   content/2012-02-13-why-does-a-hash-provide-better-message-integrity-then-an-internet-checksum.rst
	new file:   content/2012-02-14-block-cipher-lab.rst
	new file:   content/2012-02-16-my-4-month-olds-15-minutes-of-fame.rst
	new file:   content/2012-02-18-jake.rst
	new file:   content/2012-02-26-zenoss-or-nagios-monitoring-of-https-using-client-certificate-authentication.rst
	new file:   content/2012-02-29-how-to-capture-https-ssl-tls-packets-with-wireshark.rst
	new file:   content/2012-03-03-how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons.rst
	new file:   content/2012-03-07-what-do-you-name-your-python-virtualenv.rst
	new file:   content/2012-03-23-i-see-said-the-blind-man-to-the-deaf-dog-as-he-walked-off-the-cliff.rst
	new file:   content/2012-03-26-nosslsearch-cname-is-a-bad-idea-and-solution.rst
	new file:   content/2012-03-27-trouble-mounting-filesystem-on-kvm-guest-after-reboot.rst
	new file:   content/2012-04-09-i-just-purchased-instagram-for-1b-and-all-i-got-was-this-lousy-image-filter.rst
	new file:   content/2012-04-26-the-most-valuable-registration-field-how-did-you-hear-about-us.rst
	new file:   content/2012-05-11-how-to-rescue-logs-and-config-from-a-failed-citrix-netscaler-app-gateway.rst
	new file:   content/2012-05-19-high-load-on-web-server-after-updating-from-ubuntu-10-04-to-ubuntu-12-04-lts.rst
	new file:   content/2012-06-10-always-attempt-to-scale-vertically-first.rst
	new file:   content/2012-06-13-prevent-a-certain-program-from-running-to-long-in-bash.rst
	new file:   content/2012-06-25-my-top-five-suggestions-for-an-independent-developer-creating-a-new-product-or-service.rst
	new file:   content/2012-06-29-miniuri-parser-and-ago-human-timedelta.rst
	new file:   content/2012-09-22-the-pyramid-community-taught-me-the-importance-of-test-driven-development.rst
	new file:   content/2012-10-02-explaining-cache-with-python.rst
	new file:   content/2012-11-03-how-to-drain-an-iphone-battery-without-needing-passcode.rst
	new file:   content/2012-12-12-tips-for-getting-pull-requests-approved.rst
	new file:   content/2013-01-09-ago-py-human-readable-timedelta0-0-4-release.rst
	new file:   content/2013-01-12-how-to-overload-default-function-arguments-in-python-using-lambda.rst
	new file:   content/2013-03-02-virt-backs-domfetcher-class-returns-doms-from-libvirt-api.rst
	new file:   content/2013-03-22-guido-name-dropped-tornado-python-tulip-and-pep-3156.rst
	new file:   content/2013-04-09-automatic-event-hangout-with-cron.rst
	new file:   content/2013-04-26-survey-baby-monkey.rst
	new file:   content/2013-05-26-honey-i-just-deleted-linkpeek-com.rst
	new file:   content/2013-06-20-high-load-and-cpu-usage-craftbukkit-compared-to-vanilla-minecraft.rst
	new file:   content/2013-07-01-add-a-custom-header-to-all-salt-managed-files-using-pillar-and-jinja-templates.rst
	new file:   content/2013-07-02-create-your-own-fleet-of-servers-with-digital-ocean-and-salt-cloud.rst
	new file:   content/2013-07-08-understanding-salt-stack-user-and-group-management.rst
	new file:   content/2013-07-11-configuration-management-vs-remote-execution.rst
	new file:   content/2013-10-14-control-a-mongodb-collection-in-configuration-management.rst
	new file:   content/2013-10-17-postfix-salt-state-formula.rst
	new file:   content/2013-10-19-simplify-deployments-with-upstart-and-uwsgi.rst
	new file:   content/2013-10-27-backup-all-virtual-machines-on-a-smartos-hypervisor-with-smart-back-sh.rst
	new file:   content/2013-12-13-hackathon-2013-virtualization.rst
	new file:   content/2013-12-17-sensu-salt.rst
	new file:   content/2013-12-20-heka-world.rst
	new file:   content/2014-01-02-test-game-engine-with-python-and-sfml.rst
	new file:   content/2014-01-05-how-i-added-two-seagate-240g-ssds-as-smartos-l2arc.rst
	new file:   content/2014-01-25-how-to-reset-hp-ilo-lights-out-user-and-password-settings-with-ipmitools.rst
	new file:   content/2014-02-03-the-three-deployment-management-strategies.rst
	new file:   content/2014-02-07-automatic-backups.rst
	new file:   content/2014-02-07-mysql-back.rst
	new file:   content/2014-02-07-tar-back.rst
	new file:   content/2014-02-21-configuration-management-and-the-golden-image.rst
	new file:   content/2014-03-08-replace-the-nagios-scheduler-and-nrpe-with-salt-stack.rst
	new file:   content/2014-03-13-filter-salt-stack-return-data-output.rst
	new file:   content/2014-03-15-irc-bot-foxbot-runs-canned-remote-executions-using-salt-stack.rst
	new file:   content/2014-04-08-how-to-patch-heartbleed-openssl-defect-libssl-on-ubuntu.rst
	new file:   content/2014-05-08-nginx-with-ssl-and-mixed-content-errors-with-upstream-wsgi-servers.rst
	new file:   content/2014-05-15-you-can-hack-on-freenas-9.rst
	new file:   content/2014-08-15-mailpile-salt-states-for-ubuntu-or-debian.rst
	new file:   content/2014-08-23-heka-world2.rst
	new file:   content/2014-09-18-rabbits.rst
	new file:   content/2014-09-18-reality-shattered.rst
	new file:   content/2014-10-11-migrating-mongodb-from-ubuntu-to-smartos.rst
	new file:   content/2014-10-11-set-root-password-smartos-percona-mysql-zone.rst
	new file:   content/2014-11-09-turn-python-dict-into-a-keyvalue-string.rst
	new file:   content/2014-11-13-dealing-with-pagination-in-python.rst
	new file:   content/2014-11-24-build-release-pipelines-on-s3-with-s3p.rst
	new file:   content/2014-12-03-custom-rundeck-hipchat-notification-templates.rst
	new file:   content/2014-12-21-autofs-net-automount-stopped-working.rst
	new file:   content/2015-01-10-risk-process-and-balance.rst
	new file:   content/2015-01-21-set-postgres-user-password-on-postgresql-smartos-zone.rst
	new file:   content/2015-02-22-securely-publish-jenkins-build-artifacts-on-salt-master.rst
	new file:   content/2015-04-24-setting-region-programmatically-in-boto3.rst
	new file:   content/2015-04-28-migrating-libvirt-kvm-guest-to-smartos-kvm-guest.rst
	new file:   content/2015-05-08-a-python-script-which-searches-for-available-interpreters.rst
	new file:   content/2015-05-09-smartos-ubuntu-guest-apt-get-not-working-because-ipv6.rst
	new file:   content/2015-06-01-change-default-gateway-on-all-smartos-zones-and-kvm-guests.rst
	new file:   content/uploads/2010/11/thebarrymores.png
	new file:   content/uploads/2010/12/board.png
	new file:   content/uploads/2010/12/create.png
	new file:   content/uploads/2010/12/four2go.png
	new file:   content/uploads/2010/12/gumyumgameslogo.png
	new file:   content/uploads/2010/12/history.png
	new file:   content/uploads/2010/12/stats.png
	new file:   content/uploads/2010/12/usermenu.png
	new file:   content/uploads/2011/01/MyPaint.png
	new file:   content/uploads/2011/01/bamboo.jpg
	new file:   content/uploads/2011/01/dream.jpg
	new file:   content/uploads/2011/01/food1.jpg
	new file:   content/uploads/2011/01/pot.png
	new file:   content/uploads/2011/01/stack.png
	new file:   content/uploads/2011/01/stack1.png
	new file:   content/uploads/2011/01/treeman1.png
	new file:   content/uploads/2011/03/kvmbanner-logo2.png
	new file:   content/uploads/2011/03/virt-back.png
	new file:   content/uploads/2011/04/onion.png
	new file:   content/uploads/2011/04/truecrypt.png
	new file:   content/uploads/2011/06/access-school.yohdah.log_.zip
	new file:   content/uploads/2011/06/new-splitter.jpg
	new file:   content/uploads/2011/06/old-splitter.jpg
	new file:   content/uploads/2011/06/school.yohdah.com_.graph_.png
	new file:   content/uploads/2011/06/us-public-schools.png
	new file:   content/uploads/2011/06/us-public-schools1.png
	new file:   content/uploads/2011/06/uverse-dsl-link-errors.jpg
	new file:   content/uploads/2011/07/80-percent-github-hack.png
	new file:   content/uploads/2011/08/Alchemy_2.jpg
	new file:   content/uploads/2011/08/Golem-soul-caliber.jpg
	new file:   content/uploads/2011/08/Golem-soul-caliber1.jpg
	new file:   content/uploads/2011/08/password-strength.png
	new file:   content/uploads/2011/08/pig.png
	new file:   content/uploads/2011/08/pig1-color1.png
	new file:   content/uploads/2011/08/pig1.png
	new file:   content/uploads/2011/08/pig2.png
	new file:   content/uploads/2011/08/pig3.png
	new file:   content/uploads/2011/08/pig4.png
	new file:   content/uploads/2011/08/pig5.png
	new file:   content/uploads/2011/08/pig6.png
	new file:   content/uploads/2011/08/pig7.png
	new file:   content/uploads/2011/08/pigpy.xcf
	new file:   content/uploads/2011/08/the-great-gist-heist.torrent
	new file:   content/uploads/2011/08/time-travelling-wizard-alchemists.jpg
	new file:   content/uploads/2011/09/RJ-and-Carter.jpg
	new file:   content/uploads/2011/09/eye.png
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-10-birds-eye.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-11-profile.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-12-birds-eye.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-2.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-3.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-4-materials.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-5-wick-and-holder.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-6-pickle-jar.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-7.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-8-bending-with-plyers.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp-9-completed-holder.jpg
	new file:   content/uploads/2011/09/hacker-olive-oil-lamp.jpg
	new file:   content/uploads/2011/09/lecture-viewers-vs-doers.jpg
	new file:   content/uploads/2011/10/df_inode-day.png
	new file:   content/uploads/2011/10/df_inode-year.png
	new file:   content/uploads/2011/10/microsoft-resells-free-internet-services.png
	new file:   content/uploads/2011/10/skull.jpg
	new file:   content/uploads/2011/10/smile-guy.jpg
	new file:   content/uploads/2011/10/stack-vs-queue.png
	new file:   content/uploads/2011/10/wind.jpg
	new file:   content/uploads/2011/11/job-networking-chutes-and-ladders.gif
	new file:   content/uploads/2011/12/linkpeek-number-1-on-hacker-news.png
	new file:   content/uploads/2012/02/Screenshot-at-2012-02-16-172630.png
	new file:   content/uploads/2012/02/cutest-redsox-fan-ever.jpg
	new file:   content/uploads/2012/02/ice-king-adventure-time.png
	new file:   content/uploads/2012/02/jake-standing.png
	new file:   content/uploads/2012/02/lump-princess.png
	new file:   content/uploads/2012/02/mypaint-finn-adventure-time.png
	new file:   content/uploads/2012/02/mypaint-jake-adventure-time.png
	new file:   content/uploads/2012/02/sad-jake.png
	new file:   content/uploads/2012/02/wireshark-filter.png
	new file:   content/uploads/2012/02/wireshark-packets.png
	new file:   content/uploads/2012/02/wireshark-sniff.png
	new file:   content/uploads/2012/02/wireshark-start-capture.png
	new file:   content/uploads/2012/02/wireshark-stop-capture.png
	new file:   content/uploads/2012/02/wireshark.png
	new file:   content/uploads/2012/03/3247325203_6108897833_o.jpg
	new file:   content/uploads/2012/03/6876068965_db8a5d12b2_o.jpg
	new file:   content/uploads/2012/03/6876069657_5a2f4ae487_o.jpg
	new file:   content/uploads/2012/03/6876071849_b82bca1076_o.jpg
	new file:   content/uploads/2012/05/high-load-after-updating-ubuntu-from-10.04-LTS-to-12.04-LTS.png
	new file:   content/uploads/2012/05/ubuntu.12.04.swap_.year_.png
	new file:   content/uploads/2012/06/dedication-eye-chemical-burn.jpg
	new file:   content/uploads/2012/06/vertical-scale-marked.png
	new file:   content/uploads/2012/06/vertical-scale.png
	new file:   content/uploads/2012/11/virt-back-kvm-xen-virtualbox.png
	new file:   content/uploads/2012/12/comments-docs-tests-oh-my.xcf_.png
	new file:   content/uploads/2012/12/commit-working-code.xcf_.png
	new file:   content/uploads/2012/12/follow-the-leader.xcf_.png
	new file:   content/uploads/2012/12/holiday-postal-receipt-virus.png
	new file:   content/uploads/2012/12/ideas-blog-get-heard.xcf_.png
	new file:   content/uploads/2012/12/less-is-more.xcf_.png
	new file:   content/uploads/2012/12/pull-request-stay-focused.xcf_.png
	new file:   content/uploads/2012/12/spot-the-diff.xcf_.png
	new file:   content/uploads/2012/12/start-small.xcf_.png
	new file:   content/uploads/2013/03/2013-03-02-scale-virt-back-domfetcher-scaled.png
	new file:   content/uploads/2013/03/2013-03-03-explaining-cache-scaled.png
	new file:   content/uploads/2013/03/2013-03-22-tornado-callback-long-polling-event-loop-scaled.png
	new file:   content/uploads/2013/04/baby-monkey-fxhp.jpg
	new file:   content/uploads/2013/06/image.jpeg
	new file:   content/uploads/2013/06/minecraft-vs-craftbukkit.png
	new file:   content/uploads/2013/07/config-mangement-vs-remote-execution-2.png
	new file:   content/uploads/2013/07/config-mangement-vs-remote-execution.png
	new file:   content/uploads/2013/10/mysza.gif
	new file:   content/uploads/2013/11/rss-icon-25-x-25.png
	new file:   content/uploads/2014/03/download_icon-40px.png
	new file:   content/uploads/2014/09/2014-09-17-reality-shattered-150x150.png
	new file:   content/uploads/2014/09/2014-09-17-reality-shattered-212x300.png
	new file:   content/uploads/2014/09/2014-09-17-reality-shattered-726x1024.png
	new file:   content/uploads/2014/09/2014-09-17-reality-shattered.png
	new file:   content/uploads/2014/09/2014-09-18-rabbits-150x150.png
	new file:   content/uploads/2014/09/2014-09-18-rabbits-177x300.png
	new file:   content/uploads/2014/09/2014-09-18-rabbits.png
	new file:   content/uploads/hotlink.jpg
This commit is contained in:
russellballestrini 2016-06-03 11:22:15 -04:00
parent 00e5d7d798
commit 2319e768a6
237 changed files with 7741 additions and 0 deletions

View file

@ -0,0 +1,24 @@
A homegrown python bread crumb module
#####################################
:date: 2011-01-02 14:14
:author: Russell Ballestrini
:tags: Code, Project
:tags: bread, code, crumb, python
:slug: a-homegrown-python-bread-crumb-module
:status: published
I have placed
`bread.py <https://bitbucket.org/russellballestrini/bread/raw/tip/bread.py>`__, a python breadcrumb module, into the public domain.
The bread object accepts a url string and grants access to the url
crumbs (parts) or url links (list of hrefs to each crumb).
**Tutorial:** `Add a Breadcrumb Subscriber to a Pyramid project using 4 simple
steps </add-a-breadcrumb-subscriber-to-a-pyramid-project-using-4-simple-steps/>`__.
**Demo:** http://school.yohdah.com/
You should follow me on twitter `here <http://twitter.com/russellbal>`_

View file

@ -0,0 +1,38 @@
How did Stack Overflow get initial traction?
############################################
:date: 2011-01-05 02:57
:author: Russell Ballestrini
:tags: Opinion
:tags: Opinion, social network
:slug: how-did-stack-overflow-get-initial-traction
:status: published
**Stack Overflow was a progressive and natural evolution of the standard
clunky forum.**
Using ajax it created a more fun and clean user experience.
Using badges and karma to gain responsibility allow forums post to
become a game. People naturally like to see progression and growth,
being able to watch my karma go up was a rush, similar to a video game.
(some people plan elaborate flights to rack up frequent flyer miles,
planning a trip properly could cost as little as 50 dollars to fly
around the world in 24 hours, and gaining thousands of miles using point
multipliers)
.. image:: /uploads/2011/01/stack1.png
:class: wordwrap-left
:alt: StackOverflow Logo
Stack Overflow was search-able! Here is a thought,
create a forum that is easily index by search engines...
One question per page, on topic with the most relevant posts at the top of the page.
Who cares about user bounce backs, if they see your URL enough (StackOverflow.com) they will end up creating an account and becoming a user who creates even more quality content!
The site was geared toward geeks, and programmers! They adopt new tech
the quickest.
Did I miss any reasons? Feel free to comment below.
You should follow me on twitter `here <http://twitter.com/russellbal>`__

View file

@ -0,0 +1,57 @@
Graphic Design Powerhouse: Wacom Bamboo, Ubuntu, and MyPaint
############################################################
:date: 2011-01-09 00:14
:author: Russell Ballestrini
:tags: Art
:tags: Art, Bamboo, MyPaint, Ubuntu, Wacom
:slug: graphic-design-powerhouse-wacom-bamboo-ubuntu-and-mypaint
:status: published
:summary:
|pot|
For Christmas this year my wife gifted me a Wacom Bamboo pen tablet.
She claimed to have researched many products and looked
for a model that worked well with Ubuntu and had positive user reviews.
She ultimately chose the Wacom Bamboo model and I was so excited when I
unwrapped it!
The driver install was flawless and I was up and running after about 5
mins and the first application I attempted to use was Gimp. Gimp worked
but the flow felt "clunky".
|treeman|
I wanted an experience that better emulated
a pencil or brush and canvas. I went to bed that night feeling a little
unsatisfied.
The next day I decided to give the tablet another try, but instead of
using Gimp I decided to try my luck with other software called
`MyPaint <http://mypaint.intilinux.com/>`__. MyPaint's interface feels
perfect and it is loaded with brushes, pencils, and inks. MyPaint
implements brush stroke pressure with unmatched precision!
The art on this page was created with my wacom BAMBOO tablet and
MyPaint.
You should follow me on twitter `here <http://twitter.com/russellbal>`_.
Thank you so much MyPaint developers for such a stellar piece of
software!
**MyPaint is a fast and easy open-source graphics application for
digital painters. It lets you focus on the art instead of the
program. You work on your canvas with minimum distractions, bringing
up the interface only when you need it.**
.. |pot| image:: /uploads/2011/01/pot.png
:target: /uploads/2011/01/pot.png
:class: wordwrap-left
:width: 150
.. |treeman| image:: /uploads/2011/01/treeman1.png
:target: /uploads/2011/01/treeman1.png
:class: wordwrap-right
:width: 300

View file

@ -0,0 +1,46 @@
I just used Google to purchase Chinese food takeout for two
###########################################################
:date: 2011-01-10 20:03
:author: Russell Ballestrini
:tags: Guide, Opinion
:tags: Chinese, Google, hack
:slug: i-just-used-google-to-purchase-chinese-food-takeout-for-two
:status: published
:summary:
|china-food|
Yes, it has been done. Using my Google Chromium browser I searched
Google Maps for the closest local Chinese restaurant. The small town
venue doesn't have a website or menu posted online.
So I used Google Web to search for the restaurant name. I found
http://www.takeouttonight.com which had the entire menu and VALID prices
for the restaurant hosted online! I choose 'Sweet and Sour chicken', my
wife selected Chicken and Broccoli.
I then logged into my Google Gmail account and clicked Call phone. Using
the mouse I dialed the number I previously researched and after 3 rings
an Asian speaking fellow answered the phone.
I said, "Hello, I'd like to place a delivery order." His reply, "Ok."
resonated out of my 5.1 speaker setup.
I read off our selections and was super excited that it was WORKING! In
my titillated state, I ended up ordering an extra side of egg rolls just
to keep the conversation going.
He asked for my phone number to complete the order. I obliged by reading
my Google phone number.
Google might be spamy these days, but it's still useful.
Who would have thought a lazy hacker could combine six different Google
services to order Chinese food without leaving the desk?
I know it's possible now, I've seen me do it.
You should follow me on twitter
`here <http://twitter.com/russellbal>`__.
.. |china-food| image:: /uploads/2011/01/food1.jpg

View file

@ -0,0 +1,43 @@
Response to L-Theanine: a 4000 Year Old Mind-Hack
#################################################
:date: 2011-01-11 17:06
:author: Russell Ballestrini
:tags: Code, Greatest Hits, Opinion
:tags: code, L-Theanine, mind hack, tea
:slug: response-to-l-theanine-a-4000-year-old-mind-hack
:status: published
:summary:
Solve problems and write code in your sleep.
**RE: http://worldoftea.org/caffeine-and-l-theanine**
|dream-solving|
As the original article speculates, the combination and amount of
L-Theanine and caffeine present in tea, appears to have notable affects
on my programming and problem solving abilities. It's difficult to show
conclusive evidence to this claim, but I generally feel most alert and
organized when coding under the influence of 2 - 3 cups. In a typical
day I drink about 4 cups, dosed an hour apart. I feel compelled to
introduce another "mind hack" for problem solving or programming, which
I call "Dream coding"
"Dream coding" is just that, coding at night during sleep. One can
successfully invoke this "mind hack" by provoking thought about the
problem while drifting to sleep. Once sleeping I'm able to see my code
and my brain seems to iteratively problem solve. Most often I'm lucid
during these events; aware of the problem I'm trying to solve and of the
possible solutions. Other times I don't recall performing the solutions,
but when I wake and after my first cup of tea, I'm able to solve my
problem with a optimal and beautiful solution.
PBS NOVA published an episode about this phenomenon: `What are
dreams? <http://www.pbs.org/wgbh/nova/dreams/>`__
Does this happen to you? Have you experienced this?
You should follow me on twitter `here <http://twitter.com/russellbal>`__.
.. |dream-solving| image:: /uploads/2011/01/dream.jpg
:target: /uploads/2011/01/dream.jpg
:class: wordwrap-left

View file

@ -0,0 +1,101 @@
virt-back: a python libvirt backup utility for kvm xen virtualbox
#################################################################
:date: 2011-03-20 11:32
:author: Russell Ballestrini
:tags: Code, DevOps, Greatest Hits, Guide, Project
:slug: virt-back-a-python-libvirt-backup-utility-for-kvm-xen-virtualbox
:status: published
:summary:
backup your virtual maching guests.
|image0|
**Over the weekend I wrote virt-back, a backup utility for QEMU, KVM,
XEN, or Virtualbox guests.**
virt-back is a python application that uses the libvirt API to safely
shutdown, gzip, and restart guests.
The backup process logs to syslog for auditing and virt-back works great
with cron for scheduling outages. Virt-back is in active development so
feel free to give suggestions or branch the source.
virt-back has been placed in the public domain and the latest version
may be downloaded here:
* `https://bitbucket.org/russellballestrini/virt-back <https://bitbucket.org/russellballestrini/virt-back>`_
Installation:
=============
The fastest way to install virt-back is to use pip or setuptools.
Try `sudo pip install virt-back` or `sudo easy_install virt-back`
Otherwise you may manually install virt-back
::
sudo wget https://bitbucket.org/russellballestrini/virt-back/raw/tip/virt-back \
-O /usr/local/bin/virt-back
::
sudo chmod 755 /usr/local/bin/virt-back
Test installation:
==================
::
virt-back --help
Example cronjob:
================
::
15 2 * * 1 /usr/local/bin/virt-back --quiet --backup sagat
15 23 * * 5 /usr/local/bin/virt-back --quiet --backup mbison
Manual:
=======
::
russell@host:~$ virt-back --help
Usage: virt-back [options]
Options:
-h, --help show this help message and exit
-q, --quiet prevent output to stdout
-d, --date append date to tar filename [default: no date]
-g, --no-gzip do not gzip or tar the resulting files
-a amount, --retention=amount
backups to retain [default: 3]
-p 'PATH', --path='PATH'
backup path [default: '/KVMBACK']
-u 'URI', --uri='URI'
optional hypervisor uri
Actions for info testing:
These options display info or test a list of guests.
-i, --info info/test a list of guests (space delimited dom names)
--info-all attempt to show info on ALL guests
Actions for a list of dom names:
WARNING: These options WILL bring down guests!
-b, --backup backup a list of guests (space delimited dom names)
-r, --reboot reboot a list of guests (space delimited dom names)
-s, --shutdown shutdown a list of guests (space delimited dom names)
-c, --create start a list of guests (space delimited dom names)
Actions for all doms:
WARNING: These options WILL bring down ALL guests!
--backup-all attempt to shutdown, backup, and start ALL guests
--reboot-all attempt to shutdown and then start ALL guests
--shutdown-all attempt to shutdown ALL guests
--create-all attempt to start ALL guests
.. |image0| image:: /uploads/2011/03/virt-back.png

View file

@ -0,0 +1,25 @@
Porting the ChaosTheory Wordpress theme to Pylowiki
###################################################
:date: 2011-03-26 22:00
:author: Russell Ballestrini
:tags: Code, Project
:slug: porting-the-chaostheory-wordpress-theme-to-pylowiki
:status: published
:summary:
**As you may have noticed, I recently switched this blog over to the
ChaosTheory Wordpress theme.**
I had to tweak some of my pre-existing articles to make them look
'right' but upon completion I was very pleased with the aesthetics of
each page.
Later on I reviewed the theme of foxhop.net, my pylowiki demo site.
Pylowiki felt bland in comparison to this wordpress blog.
So, I made up my mind to build themes for Pylowik, and I started by
porting ChaosTheory. You can view the finished product at
`http://www.foxhop.net <%20http://www.foxhop.net%20>`__.
You should also give Pylowiki a try. Pylowiki has futuristic, live
preview, same page, section edit functionality.

View file

@ -0,0 +1,103 @@
Voice Over IP with TeamSpeak
############################
:date: 2011-04-03 12:11
:author: Russell Ballestrini
:tags: Guide
:slug: voice-over-ip-with-teamspeak
:status: published
**This article will cover running a Voice Over IP service like TeamSpeak
on a VPS.**
Voice Over IP allows users to communicate using audio over the Internet.
When planning for this article I originally was going to cover ventrilo,
but their download link was obfuscated behind a heinous php session
script. Ventrilo also does not have a Linux client although they have
been promising one for quite some time.
Instead we will cover how to install and configure
`TeamSpeak <http://www.TeamSpeak.com>`__.
**Installing a teamspeak server on your VPS**
Create a user to run teamspeak.
::
sudo adduser teamspeak
*follow the prompts*
Personally I don't want the teamspeak user to have ssh access so I added
the following to /etc/ssh/sshd\_config:
::
DenyUsers teamspeak
Then reload ssh server config:
::
sudo service ssh reload
Setup the installation environment.
The following 4 commands will create a directory to hold your
installation, change the ownership of the directory to the teamspeak
user, change the working directory to the new folder and then become the
teamspeak user:
::
sudo mkdir /opt/teamspeak
sudo chown teamspeak:teamspeak /opt/teamspeak
cd /opt/teamspeak
sudo su teamspeak
Download and extract TeamSpeak server software.
Find the proper package for your VPS and download it, in my case I ran:
::
wget http://teamspeak.gameserver.gamed.de/ts3/releases/beta-30/teamspeak3-server_linux-amd64-3.0.0-beta30.tar.gz
*For best results download the latest version of teamspeak.*
A teamspeak tarball should now exist in your present working directory.
We can extract the files from the tarball by issuing the following
command:
::
tar xvf teamspeak3-server_linux-amd64-3.0.0-beta30.tar.gz --strip-components=1
*No you don't have to type that file name out! The bash shell has tab
completion, type 'tar xvf teamsp' and then press tab. : )*
Install the TeamSpeak server software.
I had success running:
::
./ts3server_startscript.sh start
Write down the auto generated serveradmin password.
Configure TeamSpeak to start at system bootup.
Create a cronjob under the teamspeak user:
::
crontab -e
Place the following into teamspeak's crontab:
::
@reboot /opt/teamspeak/ts3server_startscript.sh start

View file

@ -0,0 +1,59 @@
Dropbox Encryption with TrueCrypt
#################################
:date: 2011-04-16 12:50
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion, Security
:slug: dropbox-encryption-with-truecrypt
:status: published
:summary:
The best security acts like an onion.
`Derek Newton <http://dereknewton.com/2011/04/dropbox-authentication-static-host-ids/>`_
recently invoked discussion about insecurities in Dropbox authentication.
In his article he describes how an attacker could exploit Dropbox and gain access to unshared files.
The concerns he raised do appear accurate however we must remember that security is an onion.
|onion|
An onion, like security, has layers to protect its vital parts.
The vital parts are more vulnerable when its security model only possess one layer.
As we add layers to our security model, our system's protection grows exponentially.
In the case of Dropbox, the username and password act as the first
layer. Experts agree that a simple authentication layer will provide
enough protection for nonsensitive data. However when attempting to
protect sensitive data we must pair authorization with encryption.
Generally speaking file systems have maintained a sense of insecurity,
which makes them useful. Not encrypting files on Dropbox is akin to not
encrypting files on a shared PC. Sensitive data should always be
encrypted *regardless* of its location or media. We should treat
sensitive data-at-rest on Dropbox the same way we treat sensitive data
on local, optical or flash disk. *We should encrypt it!*
**So how does a user encrypt their Dropbox?**
My strongly opinionated solution uses TrueCrypt to create an encrypted
volume in the Dropbox directory. Simply treat the Dropbox like a normal
directory, follow the TrueCrypt documentation to build a volume, and
give Dropbox a chance to sync the data. When the sync completes, the
TrueCrypt volume will be mountable on each of your Dropbox enabled
computers.
I have to admit at first I was skeptical, but the software cooperates
surprisingly well and after the initial sync proceeding syncs occur
quickly! I prefer TrueCrypt because it is open source, cross platform,
and free (both in freedom and cost). TrueCrypt also functions and
performs better then any other solution including commercial products
like GuardianEdge or PGP both recently acquired by Symantec.
All security and encryption software should remain open sourced and peer
reviewed to prevent harmful tampering. Commercial software, written in a
black-box vacuum, prevents customers from viewing its code and
procedures. We cannot trust software for security when we cannot view
its source code.
You should follow me on twitter
`here <http://twitter.com/russellbal>`_
.. |onion| image:: /uploads/2011/04/onion.png

View file

@ -0,0 +1,83 @@
Connecticut killed affliate marketing with Amazon.com
######################################################
:date: 2011-06-11 14:10
:author: Russell Ballestrini
:tags: Opinion
:slug: connecticut-killed-affliate-marketing-with-amazon-com
:status: published
:summary:
**Connecticut killed affliate marketing with Amazon.com**
I just opened my amazon account and read this:
| Account Closed
| This account is closed and will not generate referrals. Access to
this site is for historical purposes only.
When I checked my email, I had this letter from Amazon.com:
Hello,
For well over a decade, the Amazon Associates Program has worked
with thousands of Connecticut residents. **Unfortunately, the budget
signed by Governor Malloy contains a sales tax provision that
compels us to terminate this program for Connecticut-based
participants effective immediately.** It specifically imposes the
collection of taxes from consumers on sales by online retailers -
including but not limited to those referred by Connecticut-based
affiliates like you - even if those retailers have no physical
presence in the state.
**We opposed this new tax law because it is unconstitutional and
counterproductive. It was supported by big-box retailers, most of
which are based outside Connecticut, that seek to harm the affiliate
advertising programs of their competitors.** Similar legislation in
other states has led to job and income losses, and little, if any,
new tax revenue. We deeply regret that we must take this action.
**As a result of the new law, contracts with all Connecticut
residents participating in the Amazon Associates Program will be
terminated today, June 10, 2011.** Those Connecticut residents will
no longer receive advertising fees for sales referred to Amazon.com
, Endless.com , MYHABIT.COM or SmallParts.com . Please be assured
that all qualifying advertising fees earned on or before today, June
10, 2011, will be processed and paid in full in accordance with the
regular payment schedule.
You are receiving this email because our records indicate that you
are a resident of Connecticut. If you are not currently a resident
of Connecticut, or if you are relocating to another state in the
near future, you can manage the details of your Associates account
here . And if you relocate to another state after June 10, 2011,
please contact us for reinstatement into the Amazon Associates
Program.
To avoid confusion, we would like to clarify that this development
will only impact our ability to offer the Associates Program to
Connecticut residents and will not affect their ability to purchase
from www.amazon.com .
| We have enjoyed working with you and other Connecticut-based
participants in the Amazon Associates Program and, if this
situation is rectified, would very much welcome the opportunity to
re-open our Associates Program to Connecticut residents.
| Regards,
The Amazon Associates Team
**Summary**
- Amazon.com does not have a facility in CT.
- CT passed a new bill.
- The bill declares: if a customer from any state clicks on a CT
resident's affiliate link and purchases an item, that
customer/Amazon.com should have to pay CT tax.
**My Opinion**
- This seems bad.
- I have less money to spend in Connecticut.
- This smells like double taxation.
- One more reason to move out of Connecticut when looking for Tech or
IT jobs.

View file

@ -0,0 +1,66 @@
ATT Uverse Residential Gateway Broadband LED flashes red intermittently
########################################################################
:date: 2011-06-18 10:08
:author: Russell Ballestrini
:tags: Opinion
:slug: att-uverse-residential-gateway-broadband-led-flashes-red-intermittently
:status: published
:summary:
**ATT Uverse Residential Gateway Broadband LED flashes red
intermittently**
I have struggled with my uverse Internet and TV service randomly and
intermittently shutting off for the last 3 months. When this occurs the
Residential Gateway's broadband LED flashes red.
Also I get Uverse DSL link errors:
.. raw:: html
<p>
<center>
|uverse link errors|
.. raw:: html
</center>
</p>
When I 'live support chatted' with the uverse technical support they
claimed I had bridge tap on my lines, and that my lines had other
'issues'.
They had to dispatch 3 technicians before one of them figured out the
problem. It turns out I had a faulty 'voice/data' splitter.
I snapped a picture of the demarcation box before and after he replaced
the faulty splitter.
.. raw:: html
<p>
<center>
|old uverse splitter|\ |new uverse splitter|
.. raw:: html
</center>
</p>
<p>
<center>
*Old Uverse filter on the left, New filter on the right*
.. raw:: html
<center>
</p>
**So far I have had no Uverse DSL Link Errors!**
.. |uverse link errors| image:: /uploads/2011/06/uverse-dsl-link-errors.jpg
.. |old uverse splitter| image:: /uploads/2011/06/old-splitter.jpg
.. |new uverse splitter| image:: /uploads/2011/06/new-splitter.jpg

View file

@ -0,0 +1,48 @@
Google Bot Attempts to Crawl Shortest Urls First
################################################
:date: 2011-06-25 09:37
:author: Russell Ballestrini
:tags: Code, Greatest Hits, Opinion, Project
:slug: google-bot-attempts-to-crawl-shortest-urls-first
:status: published
:summary:
**Recently I built http://school.yohdah.com a Python, Pyramid, and
mongoDB project during the last couple weekends.**
|image0|
The site features a directory style navigation of nearly every public
school in the US. We have 61 state pages, approximately 19,000 city
pages, and over 103,000 school pages.
It seems the Google Bots have noticed
`school.yohdah.com <http://school.yohdah.com>`__ and started crawling
the site. Since the initial crawl I started reviewing a sample of the
sites apache logs in an attempt to track the bot's activity. After a few
minutes of viewing the logs, I locked onto a pattern; Google Bot's
algorithm appears to crawl the short URLs first!
PersonalCompute (a user) attached a graph of the fetched URL lengths
here:
|school.yohdah.com.graph|\
I have attached a zip containing the apache google bot crawl logs
here:
`access-school.yohdah.log.zip </uploads/2011/06/access-school.yohdah.log_.zip>`_
I found the pattern by opening the file in vim and scrolling very
quickly down. You will notice the log lines will grow slowly to the
right, as the urls being fetched increase by one character.
**Why does Google do this? Does anyone have speculation as to what this
means?**
.. |image0| image:: /uploads/2011/06/us-public-schools1.png
:target: http://school.yohdah.com
:alt: school.yohdah.com
:class: wordwrap-left
.. |school.yohdah.com.graph| image:: /uploads/2011/06/school.yohdah.com_.graph_.png
:target: /uploads/2011/06/school.yohdah.com_.graph_.png

View file

@ -0,0 +1,53 @@
Add a Breadcrumb Subscriber to a Pyramid project using 4 simple steps
#####################################################################
:date: 2011-07-02 17:25
:author: Russell Ballestrini
:tags: Code, Guide
:slug: add-a-breadcrumb-subscriber-to-a-pyramid-project-using-4-simple-steps
:status: published
:summary:
**This article will explain how to add a breadcrumb subscriber to a
Pyramid project using 4 simple steps.**
While programming http://school.yohdah.com/ I needed the ability to easily create
breadcrumb links from the current url. You may view the `bread.py source
code
here <https://bitbucket.org/russellballestrini/bread/src/tip/bread.py>`__.
The following guide describes the process I took to add this
functionality.
1. Download and include
`bread.py <https://bitbucket.org/russellballestrini/bread/raw/tip/bread.py>`__
at the top of your Pyramid project's \_\_init\_\_.py file:
::
from yourproject.bread import Bread
from pyramid.events import subscriber, NewRequest
2. At the bottom of the projects \_\_init\_\_.py file create the
following subscriber function as follows:
::
def bread_subscriber( event ):
""" Build Bread object and add to request """
event.request.bread = Bread( event.request.url )
3. Now we can add our new subscriber to our Pyramid config inside
main and above the routes:
::
config.add_subscriber( base_subscriber, NewRequest )
4. You are done! Now you should have the ability to use the bread
object from your template. Below I have provided a mako template
snippet:
::
% for link in request.bread.links:
${ link | n } /
% endfor

View file

@ -0,0 +1,50 @@
a hack to gain 80 percent efficiency when creating github projects
##################################################################
:date: 2011-07-11 21:47
:author: Russell Ballestrini
:tags: Code, Guide
:slug: a-hack-to-gain-80-percent-efficiency-when-creating-github-projects
:status: published
:summary:
|image0|
**Last night I decided to learn how to use git for its popularity and
github to code more socially.**
I have to admit early on that I enjoy hg and bitbucket so it came to a
surprise that github would have me jump through hoops to create a new
repository...
Below I have copied the seemingly bloated github instructions for
creating a new project.
**Github:**
#. create project/repo using the form
#. mkdir scratch
#. cd scratch
#. git init
#. touch README
#. git add README
#. git commit -m 'first commit'
#. git remote add origin
#. git@github.com:russellballestrini/scratch.git
#. git push -u origin master
Wow...
I like my method better.
**My Github clone hack:**
#. create project/repo using the form
#. git clone
https://russellballestrini@github.com/russellballestrini/scratch.git
Awesome, 2 steps versus 10. That hack appears 80% more efficient! Now if
only my name was shorter... LOL
You should follow me on twitter `here <http://twitter.com/russellbal>`_
.. |image0| image:: /uploads/2011/07/80-percent-github-hack.png

View file

@ -0,0 +1,45 @@
The Great Gist Heist
####################
:date: 2011-08-07 02:30
:author: Russell Ballestrini
:tags: Code, Project
:slug: the-great-gist-heist
:status: published
:summary:
Please listen to my story before jumping to conclusions.
**The Great Gist Heist**
I have crawled, downloaded, and archived all of gist.github.com. Please
hear my story before jumping to conclusions.
**Why?**
I'm currently building software that requires a large corpus of source
code.
I began to search for a collection of source code documents but my
pursuit appeared fruitless. Feeling displeased I attempted to gather all
of my own source code. My collection lacked fidelity perhaps because of
my revere for the python language.
Regardless of the reasoning, I needed a higher quantity of samples. I
needed unbiased samples from all programming languages. I needed, most
importantly, samples in a variation of quality that only the most
popular paste sites have... sites like gist.
**Why are you sharing it?**
I feel a little bad about using Github's bandwidth.
Sharing this collection should reduce the chances that others will crawl
for the same data. If you need a large collection of source code,
`download this torrent </uploads/2011/08/the-great-gist-heist.torrent>`_.
**How did you do it?**
I wrote a short, 30 line, python script. The script is part of the
torrent.
At the peak of the scrape I had 14 threads running of the script, using
approximately 580Kbps (I used iftop).

View file

@ -0,0 +1,56 @@
Programming is like Alchemy
###########################
:date: 2011-08-08 21:52
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion
:slug: programming-is-like-alchemy
:status: published
:summary:
except instead of exchanging matter, we programmers exchange time.
|alchemy|
**Programming is like Alchemy** except instead of exchanging matter, we
programmers exchange time. Also depending on the program the exchange of
time worked (coding) increases the productivity (time) of its users.
On second thought, perhaps programmers correlate less with Alchemists
and more with Time Travelers; Or at the very least time manipulators.
For example, on a good day a programmer can easily complete a task that
would take one thousand men. See, time created! On a bad week we can
procrastinate and do nothing at all. Time lost!
|Golem-soul-caliber|
**Programming embodies other magic like wizardry.** For example,
our programs typically live as golems performing one task,
repeatedly, over and over. Golem programs, without a soul, stuck in a
loop of servitude.
Recently we have started coding creations with artificial intelligence.
These smart programs act like familiar spirits (Wikipedia) and assist
their creator in conjuring even more magic.
So we settled it! Programmers are like bad ass, time travelling, wizard
alchemists!
**Or maybe not ...**
It is more accurate to group programs with technology then magic, but
less fun. Programs are leveraged tools born to save people time and energy.
Thanks for reading, you should follow me on twitter `here <http://twitter.com/russellbal>`_
|time-travelling-wizard-alchemists|
.. |alchemy| image:: /uploads/2011/08/Alchemy_2.jpg
:alt: This image comes from Michael Maier's 1618 treatise on alchemy. It combines music, image and text to communicate alchemical knowledge to adepts. Note the combination of Pythagorean imagery with alchemical practice.
:class: wordwrap-right
:width: 270
.. |Golem-soul-caliber| image:: /uploads/2011/08/Golem-soul-caliber1.jpg
:class: wordwrap-left
:width: 220
.. |time-travelling-wizard-alchemists| image:: /uploads/2011/08/time-travelling-wizard-alchemists.jpg
:alt: time traveling wizard alchemist

View file

@ -0,0 +1,110 @@
virt-back: restoring from backups
#################################
:date: 2011-08-10 23:20
:author: Russell Ballestrini
:tags: Code, DevOps, Guide, Project
:slug: virt-back-restoring-from-backups
:status: published
**In a perfect world we should create backups but never need them.**
Although this statement holds truth, creating guest backups provides
many more benefits.
The most common reasons system administrators restore from a virt-back
guest backup:
- recovering from data corruption
- recovering deleted files
- recovering from a virus infection
- recovering from a compromised server
- backing out a failed change
- rolling back to a previous state
- testing disaster recovery plans
- cloning a server
- building test environments
During this article we will cover how to restore a system from a
virt-back guest backup. This article will not cover how to restore a VM
host server.
**Virt-back guest restore procedure**
In this guide our guest mbison has failed with a major corruption and we
would like to restore from our backups. We have our running production
guest images in /KVMROOT and our virt-back guest backups in /KVMBACK. We
will be restoring the backup on the same hypervisor.
**Overview:**
#. Ensure the guest is shut off.
#. move the bad image file out of the way
#. untar the virt-back backup into place
#. power up the guest
**Detailed Procedure:**
#. Verify the guest is shut off by running:
::
virt-back --info-all
#. We noticed that mbison was still running so we invoked:
::
virt-back --shutdown mbison
#. Move the corrupted image file out of the way:
::
mv /KVMROOT/mbison.img /KVMROOT/mbison.img.NFG
#. Unzip and unarchive the backup using the following command:
::
sudo tar -xzvf /KVMBACK/mbison.tar.gz -C /KVMROOT --strip 1
#. When the untar completes, start the guest:
::
virt-back --create mbison
#. Connect to the guest over SSH and verify that all required services
and applications start. Determine if the restore was successful.
|
**Restore guest backup on new hypervisor:**
|
The details in this section were adapted from a tutorial given by
`Fabian Rodriguez <http://fabianrodriguez.com/>`_.
|
#. Re-create any bridge network interfaces on new hypervisor
(/etc/network/interfaces for Debian)
#. Adjust mbison.xml if needed (for example if you are changing paths)
::
sudo mkdir /KVMROOT
sudo tar -xvzf mbison.tar.gz -C /KVMROOT --strip 1
virsh create /KVMROOT/mbison.xml
**Note:** We use virsh create instead of virt-back create. While both
commands start guest DOMs, virsh create will also register the DOM into
the hypervisor.
|

View file

@ -0,0 +1,67 @@
Security Professionals: Yes we appear vulnerable but that attack vector will never happen
#########################################################################################
:date: 2011-08-18 21:36
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion, Security
:slug: security-professionals-yes-we-appear-vulnerable-but-that-attack-vector-will-never-happen
:status: published
In loom of recent internet attacks many institutions have started
scrambling in attempt to "strengthen" their security stance. I agree
that auditing our systems and networks for potential flaws seems
appropriate at this time to prevent getting "caught with our pants
down". Incidentally, I have recently witnessed the introduction of silly
and at times ineffective security adjustments. Many of these new
procedures, rules, and requirements do not make us more secure and worse
instill a false sense of security.
I have previously addressed the fallacy of absolute security. No system
is perfect. A successful security model accomplishes fortitude by
implementing layers like an onion. Through the use of security
layers we can significantly hamper attack vectors and create a safer
complex.
When analyzing a potential attack vector we must first determine our
current location in the security layers. This step serves two purposes:
- to prevent wasting time and energy on vulnerabilities that don't
matter at that point in our matrix.
- to prevent causing outages and unneeded administrator and customer
heartache.
If a vulnerability requires root or elevated privileges to occur, don't
waste your time resolving it. If the attacker already has root, you have
bigger problems on your hands.
**Some real life examples:**
#. Firewall denying a large range of IP addresses (like entire
countries). This truly does not increase security, it just creates
headaches for users. An attacker could just proxy to an open range
(like a VPS based in a more trusted zone) and gain access from there.
Also if you decide to ignore this advice and create blanket IP range
deny rules, DON'T also block services intended to be internet-facing.
For example, don't block your Internet-facing DNS server if it is
authoritative for public domains. This will cause countless
intermittent issues and will be a nightmare to diagnose.
#. Weekly Scanning for Windows viruses on network shares or data at
rest. This hammers the servers for no reason. If all the desktops run
antivirus then the file was already scanned when it was downloaded.
That same file will be scanned again when retrieved on the share. If
you want the warm and fuzzys of virus scanning network file shares,
do it once a year. These scans waste time and resources. I feel even
more outraged when asked to virus scan network shares hosted on UNIX
servers or NAS.
I speculate that most of these arbitrary ideas come about because the
people in charge make uninformed decisions out of fear without first
consulting the appropriate subject matter experts.
Unfortunately, once a security mandate occurs it seems difficult to
expunge. People are just not willing to put their neck on the chopping
block to banish a legacy or silly mandates; So we end up living with
nonsensical rules and procedures.
|http://xkcd.com/936/|
.. |http://xkcd.com/936/| image:: /uploads/2011/08/password-strength.png

View file

@ -0,0 +1,46 @@
Python Image Grabber pig.py
###########################
:date: 2011-08-22 18:48
:author: Russell Ballestrini
:tags: Code, Guide, Project
:slug: python-image-grabber-pig-py
:status: published
:summary:
Download all the images from a URI with this simple tool!
|pig.py|
**Python Image Grabber pig.py**
Python Image Grabber or pig.py is a *very* simple python command line
tool to download all the images from a given uri.
Enjoy!
**Download and Installation:**
https://bitbucket.org/russellballestrini/pig/raw/tip/pig.py
**Usage:**
``python pig.py``
|
**Example:**
``python pig.py http://www.foxhop.net``
|
**Open Source:**
`pig.py <https://bitbucket.org/russellballestrini/pig/raw/tip/pig.py>`_ has been placed in the public domain and its sourcecode may be viewed or branched here here: https://bitbucket.org/russellballestrini/pig
|
**Pigpy Gimp Project**
`pigpy.xcf </uploads/2011/08/pigpy.xcf>`__
|
.. |pig.py| image:: /uploads/2011/08/pig1-color1.png
:class: wordwrap-left

View file

@ -0,0 +1,67 @@
Deliberating the Viewers vs. Doers concept
###########################################
:date: 2011-09-06 10:20
:author: Russell Ballestrini
:tags: Opinion
:slug: deliberating-the-viewers-vs-doers-concept
:status: published
:summary:
America has succumbed to "spectatoritis".
|Viewers vs Doers Lecture|
`Brett & Kate McKay <http://artofmanliness.com/2011/08/28/viewers-vs-doers-the-rise-of-spectatoritis/>`_ from artofmanliness.com recently subscribed to an idea that America has succumbed to
"spectatoritis, a blanket description to cover all kinds of passive
amusement, an entering into the handiest activity merely to escape
boredom." -Jay B. Nash, 1938
The basic concept discussed how people can fall into one of two groups
when interacting in life, the viewers and the doers. Viewers or
spectators watch an activity while doers perform the said activity. For
example:
**Viewers**
- Sports Fans
- Audience
- Couch Potatoes
**Doers**
- Athletes
- Musicians
- Actors
Although the above patterns persists in the physical world, intellectual
situations operate differently. For instance on the surface reading
seems like a viewer or spectator task and writing appears to behave like
a doer task. But what happens when a reader learns or responds to a blog
post? We seem to need a stronger definition what constitutes doing a
task.
We should not regard spectating as bad, however we should try to
stay lucid of our current rolls when engaging in activities.
Spectating has some important purposes in our society and culture.
Viewing a performance grants power to the actor, whether that person
plays football or plays guitar. For this power and influence to occur a
viewer must witness and pay attention to the event. What if the next
great political leader took the podium and no one bothered to view his
speech?
The truth is our American society has conditioned us to spend much of
our lives spectating. During school a good student will view and listen
to the lecture. While driving to work one radio DJ broadcasts his
thoughts to many.
Spectating also plays and integral roll in learning. Human infants learn
by first watching and then imitating. Experts also agree that
inspiration for a creative works often occur after observation of prior
productions.
**tl;dr** We should not battle the viewers vs the doers because both
hold importance and need each other.
If you liked this article, you should follow me `here <https://plus.google.com/101342467879466559261/posts>`_
.. |Viewers vs Doers Lecture| image:: /uploads/2011/09/lecture-viewers-vs-doers.jpg

View file

@ -0,0 +1,74 @@
A system administrators guide to installing and maintaining multiple python environments
########################################################################################
:date: 2011-09-08 19:28
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: a-system-administrators-guide-to-installing-and-maintaining-multiple-python-environments
:status: published
Some operating systems depend on a specific version of python to
function properly. For example, Yum on Redhat Enterprise Linux 5 (RHEL5)
depends on python 2.4.3. This version of python lacks support from many
utilities and 3rd party libraries. This guide will cover installing an
alternative python instance while leaving the system's python alone.
*This guide supports the following operating systems: Redhat, CentOS,
and Fedora. As of this publication the latest Python version was 2.7.2;
You might want to determine if a newer version exists.*
Compile
=======
#. Gather the dependencies::
yum install gcc zlib-devel python-setuptools readline-devel
- **gcc** is a compiler used to build python
- **zlib-devel** allows the python zlib module to be built
- **python-setuptools** provides the easy_install application
- **readline-devel** arrows readline and history handling in python shell
#. Download and untar the python sourcecode::
wget http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz
tar -xzvf Python-2.7.2.tgz
cd Python-2.7.2
#. Compile the sourcecode::
./configure
make altinstall
#. Test new alternative python::
python2.7 --version
#. Now we can install third party libraries into our alternative python::
python2.7 -m easy_install
virtualenv
==========
Optionally we can create a virtualenv (for development) based on the
python 2.7 install. Virtual environments appear useful for testing
packages and libraries without installing them to the system owned
python site-packages directory.
#. Install virtualenv using easy\_install::
easy_install virtualenv
#. Create a new virtual python environment named virtpy::
virtualenv --no-site-packages -p /usr/local/bin/python2.7 virtpy
This will create a virtual python 2.7.2 environment named virtpy in your present working directory.
To invoke this environment run source virtpy/bin/activate and your prompt should change to reflect the active virtualenv.
Now you can run easy_install to install packages into virtpy/lib/python2.7/site-packages.
**Thanks for reading, that's all for now.**

View file

@ -0,0 +1,56 @@
Hacker Olive Oil Lamp Crafted From Home Materials
#################################################
:date: 2011-09-09 19:01
:author: Russell Ballestrini
:tags: Art, Guide, Project
:slug: hacker-olive-oil-lamp-crafted-from-home-materials
:status: published
:summary:
Really fast and clean design.
|image0|
In loom of the recent and persisting hurricane season I present a
**"Hacker's Olive Oil Lamp"!**
**YES, this lamp is ...**
- **Fun and simple to build**
- **Fun and simple to use**
- **Easy to Cleanup**
- **Gentle on environment**
- **Energy efficient** (1 cup ~= 16 hours)
- **Safe** (flame smothers if lamp tips over)
**NO**
- **Odor**
- **Smoke**
- **Heat near base**
- **Cost** (household materials)
- **Waste** (Store and seal oil in lamp with jar top)
**Materials**
- 1x Pickle jar with lid
- 1x Washcloth 100% cotton
- 1x Steel wire (salvaged from paint can handle)
- 2x Cups of olive oil
**Tools**
- Needle nose plyers
- Scissors
.. raw:: html
<p>
<center>
<iframe width="420" height="345" src="http://www.youtube.com/embed/3I1W2ddJaAs" frameborder="0" allowfullscreen>
</iframe>
</center>
</p>
[gallery link="file"]
.. |image0| image:: /uploads/2011/09/hacker-olive-oil-lamp.jpg

View file

@ -0,0 +1,12 @@
New Baby Homecoming
####################
:date: 2011-09-27 16:53
:author: Russell Ballestrini
:tags: Uncategorized
:slug: new-baby-homecoming
:status: published
:summary: family += 1
|image0|
.. |image0| image:: /uploads/2011/09/RJ-and-Carter.jpg

View file

@ -0,0 +1,45 @@
CSS frameworks not rendering properly on all browsers
#####################################################
:date: 2011-10-11 00:07
:author: Russell Ballestrini
:tags: Code, Guide
:slug: css-frameworks-not-rendering-properly-on-all-browsers
:status: published
:summary:
Read on for the one line fix.
I ran into an issue when testing skeleton css framework and twitter
bootstrap where some browsers were not rendering the pages properly.
After some research I determined that I forgot the DOCTYPE declaration
tag.
The DOCTYPE tag tells the browser what "rules" or standards to use when
rendering markup.
The following code block displays the minimal approach to setting the
document type:
.. raw:: html
<p>
|
|
|
.. raw:: html
</p>
<p>
After placing the doctype above the
tag the pages render properly because we forced the browser into
standards mode.
.. raw:: html
</p>
Thanks!

View file

@ -0,0 +1,21 @@
A better way to show website backlinks
######################################
:date: 2011-10-12 20:05
:author: Russell Ballestrini
:tags: Guide, Opinion
:slug: a-better-way-to-show-website-backlinks
:status: published
| Early web pilgrims of the Internet fashioned search queries like
| ``link:russell.ballestrini.net`` to gather backlinks for a domain.
I however advocate a revolutionary search pattern like -
::
"russell.ballestrini.net" -site:russell.ballestrini.net
to gather an improved representation of backlinks.
`Click here to try
now! <http://www.google.com/#sclient=psy-ab&hl=en&source=hp&q=%22school.yohdah.com%22+-site:school.yohdah.com&pbx=1&oq=%22school.yohdah.com%22+-site:school.yohdah.com&aq=f&aqi=&aql=1&gs_sm=e&gs_upl=1836l15311l0l17007l43l35l0l0l0l8l274l5507l4.23.8l35l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=18aa9f7b4fee5b6d&biw=1485&bih=912>`__

View file

@ -0,0 +1,53 @@
I cancelled my xbox live automatic renewal
###########################################
:date: 2011-10-15 19:26
:author: Russell Ballestrini
:tags: Opinion
:slug: i-cancelled-my-xbox-live-automatic-renewal
:status: published
:summary:
**I cancelled my xbox live automatic renewal today because I no longer
use the service.**
I find humor in Microsoft's list of reasons to keep Xbox live:
|image0|
**6 out of 8 services above are FREE Internet services!**
If I want to use those free Internet services on my TV,
I'd rather use my tiny media center PC (`nT330i <http://www.foxhop.net/nT330i>`__)
which is amazing and near silent.
Microsoft prevents subscribers from cancelling their service over the
web, and thus they forced me to call their service center. After waiting
5 minutes I finally spoke to a sales representative who seemed friendly
and understanding.
She asked me why I wanted to cancel automatic payments. I explained "I
want to pay month to month". After listening to my position the sales
representative offered **1 month of Xbox Live for $1.00.** So I'll have
Xbox live until December 2011 after all.
Microsoft, give your users a web browser on the Xbox and stop trying to
sell subscriptions to Free Internet services.
***PlayStation 3 launched with a web browser and a FREE network.***
**UPDATE:** If you opt into the $1 for one month deal, you are also
opting back into the automatic renewal program... Even if the person on
the phone tells you otherwise.
Call us directly by dialing:
::
Toll free:
(800) 4MY-XBOX or (800) 469-9269
Hearing impaired (TDD device):
(866) 740-9269 or (425) 635-7102
9 am to 1 am Eastern Time
6 am to 10 pm Pacific Time
.. |image0| image:: /uploads/2011/10/microsoft-resells-free-internet-services.png

View file

@ -0,0 +1,23 @@
r8168 driver issues after Ubuntu 11.10 upgrade kernel linux 3.0
###############################################################
:date: 2011-10-15 11:15
:author: Russell Ballestrini
:tags: Guide
:tags: guide
:slug: r8168-driver-issues-after-ubuntu-11-10-upgrade-kernel-linux-3-0
:status: published
**I had network issues after upgrading to Ubuntu 11.10 which has the
linux 3.0 kernel.**
I used `this guide <http://www.foxhop.net/realtek-dropping-packets-on-linux-ubuntu-and-fedora>`__
to compile the r8168 driver however, I needed to alter the Makefile to support for linux 3.0 kernel.
Edit *src/Makefile*:
.. code-block:: bash
#KEXT := $(shell echo $(KVER) | sed -ne 's/^2\.[567]\..*/k/p')o
KEXT := $(shell echo $(KVER) | sed -ne 's/^[23]\.[1-9]\..*/k/p')o-
**Now you should follow the rest of the guide.**

View file

@ -0,0 +1,39 @@
Cell shading practice, A sketched skull, and an old wind mill photograph
########################################################################
:date: 2011-10-16 12:56
:author: Russell Ballestrini
:tags: Art
:slug: cell-shading-practice-a-sketched-skull-and-wind-mill-photograph
:status: published
:summary:
**Some of my resent works using my Ubuntu + Wacom Bamboo + MyPaint +
Gimp Workflow!**
First some cell shading practice. Inside MyPaint I only used two colors
in my palate but to gain shadow and depth I used variations of opacity.
This technique produces a neat cartoon effect.
|image0|
Next I sketched a skull. I attempted to keep all my strokes in the same
direction. This piece is monotone. I used only one color and deviated
each layer with different opacity settings.
|image1|
Last is a photograph I snapped during a family outing. I had to lower
the quality of this image to prepare for use on the web, but I still
adore the colors spectrum of this shot. The white birch tree frames the
right side while the grass and brush frame the bottom. The wind mill was
intentionally placed in the center with the apex extending upward. The
ivy creeps up the structure and the cloud filled sky appears to stretch
forever. The foliage emits a euphoric glow under the sun saturated rays.
|image2|
**Come back soon!**
.. |image0| image:: /uploads/2011/10/smile-guy.jpg
.. |image1| image:: /uploads/2011/10/skull.jpg
.. |image2| image:: /uploads/2011/10/wind.jpg

View file

@ -0,0 +1,18 @@
Adding inline image support to a gmail messages
###############################################
:date: 2011-10-20 17:46
:author: Russell Ballestrini
:tags: Guide
:slug: adding-inline-image-support-to-a-gmail-messages
:status: published
Enable ability to insert images into a message body. You can upload and
insert image files in your computer, or insert images by URLs. This lab
will not work if you have offline enabled.
#. Go to google labs: https://mail.google.com/mail/#settings/labs
#. Search "images".
#. *Enable* "Inserting images - by Kent T"
**Welcome!**

View file

@ -0,0 +1,31 @@
Occupy Wall Street Stack vs Queue
#################################
:date: 2011-10-21 13:31
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion
:slug: occupy-wall-street-stack-vs-queue
:status: published
**Occupy Wall Street contributors claim to use a "stack" to determine
speaking arrangements.**
I plan to explain how the term "stack" used in this scenario does not
align itself with the mathematical or computer science definition.
The term stack means First In Last Out or "FILO". For example: a person
placed on the stack in the morning would be the last to speak at the end
of the day. This isn't happening like that...
Occupy Wall Street compatriots really use a technique called "queue".
Mathematicians and Computer Scientists define a queue as First In First
Out or "FIFO". A real world example of a queue would be a line at the
grocery store. The first person in line is the first person to leave the
line.
- An object enters a stack from the rear and exits the rear.
- An object enters a queue from the rear and exits from the front.
|
| |image0|
.. |image0| image:: /uploads/2011/10/stack-vs-queue.png

View file

@ -0,0 +1,50 @@
Webmaster tools alerted issue turned out Pylon session files flooded inodes
###########################################################################
:date: 2011-10-29 15:30
:author: Russell Ballestrini
:tags: DevOps, Opinion
:slug: webmaster-tools-alerted-issue-turned-out-pylon-session-files-flooded-inodes
:status: published
:summary:
**This graph could happen to you if you ever forget to configure munin
email alerting:**
|image0|
It only took approximately 1 hour to diagnoses and resolve this issue
however most of my web applications hosted on this server were down for
about 11 hours. I was lucky that this outage fell on a weekend otherwise
I would not have known about the problem till around 6:30pm!
|image1|
**Diagnosis:**
Two of my pylons apps had session files that slowly ran away on me. The
session files don't consume much capacity however the shear quantity of
them caused my inode usage to hit 100%.
Had I properly configured Munin's email alerting this issue would have
been identified well before it was a problem.
Want to know what alerted me to the problem? G Webmaster's tools claimed
it could not read my robots.txt on a few of my sites... After
investigating I learned the site was down. Checking the Apache error
logs pointed me to disk space issues. ``df -ha`` reported everything was
fine, however ``df -hi`` reported 100% inode usage! At this point I
started looking to cache and log locations to find lots of files, which
lead me to my pylons web applications data/sessions directories.
**Resolution:**
Delete the session cache tree directories and allow the applications to
rebuild them.
Todo: move /www off the root disk partition. This issue could have been
much worse if I was unable to boot or login to remedy. Moving /www off
root should prevent the web server from effecting the systems ability to
boot.
.. |image0| image:: /uploads/2011/10/df_inode-year.png
.. |image1| image:: /uploads/2011/10/df_inode-day.png

View file

@ -0,0 +1,22 @@
I'm petrified of launching my web application
#############################################
:date: 2011-11-03 21:35
:author: Russell Ballestrini
:tags: LinkPeek, Opinion
:slug: im-petrified-of-launching-my-web-application
:status: published
|
| **I'm petrified of launching my web application because I'm fearful
that I won't ...**
- acquire users
- support my users well
- scale in a timely manner
- react quickly to feedback
- monetize the application
But most of all I'm scared that nobody will like me. I'm scared of
failure.
**Now that I got that out of my system please check out https://linkpeek.com**

View file

@ -0,0 +1,46 @@
Career development is a game of chutes and ladders
##################################################
:date: 2011-11-29 20:42
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion
:slug: career-development-is-a-game-of-chutes-and-ladders
:status: published
:summary: find out what is the ladder.
If career development was a game of chutes and ladders, job networking
would be the ladder. They provide a shortcut to the top, a direct route
to win your dream job.
.. image:: /uploads/2011/11/job-networking-chutes-and-ladders.gif
:align: right
:alt: chutes and ladders
At work today, a colleague was reviewing resumes for an open requisition
within the unix group. I decided later that night to clean up `my
resume <http://russell.ballestrini.net/uploads/2011/10/Russell-Ballestrini-Resume.pdf>`__
to make it more relevant. I felt like I did something positive for my
career. After coming down from the high of resume writing I began to
question rational. I then came to the contradictory conclusion that a
great resume holds less importance than a mediocre recommendation.
It is better for an employer to learn about you from a recommendation
then your resume. Why? Hiring new people holds risk. A hiring manager
will reduce risk by promoting from within or using personal
recommendations. In both cases the resume becomes a document of
formality instead of a document of credentials.
"Its not what you know, its who you know."
What can we assume about the position they are extending to the public?
We can safely assume that the manager has already hired somebody for the
high risk, enjoyable position and is now looking for a bottom feeder
with a resume to fill the newly vacant position. Keep this in mind the
next time a head hunter sends you a job proposition email.
Normally taking shortcuts goes against conventional wisdom for success.
Job networking however, allows candidates to skip ahead and arrive at
their dream occupation more directly. Meeting new people seems like the
single best way to land a job doing what you love.
**Get out there and meet people with similar interests!**

View file

@ -0,0 +1,110 @@
How to Incorporate Custom Configuration in a Pyramid Application
################################################################
:date: 2011-11-30 22:47
:author: Russell Ballestrini
:tags: Code, Guide
:slug: how-to-incorporate-custom-configuration-in-a-pyramid-application
:status: published
Imagine that you have just built a wiki, blog, or cms web application
that will be deployed multiple times by different people. You would like
to provide the ability to configure some aspects of the program without
the end user altering your python or template code. This guide explains
how to incorporate custom configuration from the project's .ini file
with the rest of the application.
To explain this process we will add a customizable Google Analytics key
to our project.
#. Make a configuration key/value pair for Google Analytics
#. Add the Google Analytics javascript code to the template
**Make a configuration key/value pair for Google Analytics**
Inside production.ini place the following in the ``[app:main]`` section:
::
#google_analytics_key = UA-55555555-1
google_analytics_key =
**Add the Google Analytics javascript code to the template**
In this case I will show an example in mako. Other template solutions
should look similar.
.. raw:: html
<p>
|
| <%def name="google\_analytics()">
| % if request.registry.settings['google\_analytics\_key']:
.. raw:: html
<script type="text/javascript"></p>
<p>var _gaq = _gaq || [];<br />
_gaq.push(['_setAccount', "${request.registry.settings['google_analytics_key']}"]);<br />
_gaq.push(['_trackPageview']);</p>
<p>(function() {<br />
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;<br />
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';<br />
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);<br />
})();</p>
<p></script>
|
| % endif
|
|
.. raw:: html
</p>
Then in the head section call the function:
::
${ google_analytics() }
That was easy because the configuration string just needed to be
substituted into the JavaScript in the template. What if you needed to
do something with the provided key/value before using it? Next I will
show you a method for building renderer globals again showing a
different way to configure Google Analytics:
**Make an inject\_renderer\_globals subscriber**
Define ``inject_renderer_globals(event)`` function in the project's
``__init__.py`` file.
I normally place it at the bottom of the file and it looks like this:
::
def inject_renderer_globals(event):
"""Inject some renderer globals before passing to template"""
request = event['request']
# Build ${google_analytics_key} from the configuration file
event['google_analytics_key'] = request.registry.settings[ 'google_analytics_key' ]
Import BeforeRender at the top of the ``__init__.py``:
::
from pyramid.events import BeforeRender
In the main function, add the ``inject_renderer_globals`` to the
subscribers:
::
config.add_subscriber(inject_renderer_globals, BeforeRender)
Now you can use ``${google_analytics_key}`` anywhere in your template.
**Thank you for reading, and feel free to leave comments**

View file

@ -0,0 +1,15 @@
LinkPeek.com Number One on Hacker News
######################################
:date: 2011-12-05 00:39
:author: Russell Ballestrini
:tags: LinkPeek, Opinion
:slug: linkpeek-com-number-one-on-hacker-news
:status: published
:summary:
**We made the number one spot on Hacker News.**
| LinkPeek was used to take a snapshot of the event:
| |image0|
.. |image0| image:: /uploads/2011/12/linkpeek-number-1-on-hacker-news.png

View file

@ -0,0 +1,16 @@
flash mob office meeting definition
####################################
:date: 2011-12-12 15:34
:author: Russell Ballestrini
:tags: Uncategorized
:slug: flash-mob-office-meeting-definition
:status: published
**Flash Meeting**
In a office or cubicle environment a group of uninvited people gather
and hover around your desk to talk to you. A meeting forms in immaculate
conception as you sit bewildered at your desk.
**Other names:** *Flash Meeting*, *Flash Mob Meeting*, *Flash Office
Meeting*

View file

@ -0,0 +1,37 @@
Today I lost a customer
#######################
:date: 2012-01-18 23:30
:author: Russell Ballestrini
:tags: LinkPeek, Opinion
:slug: today-i-lost-a-customer
:status: published
Today I lost a customer.
I added some new code to `LinkPeek <http://linkpeek.com/website-thumbnail-generator>`__ to accept coupons and I didn't think of an edge case.
This ended up creating an uncaught exception in my server side code which ultimatly served the newly subscribing customer an HTTP 500 error page.
The damage was done.
This error was catastrophic and ultimately killed the conversion. Here
is an excerpt of how the user felt after the experience:
At this point the website has failed spectacularly enough that I can
no longer trust you with my business. Please void the charge on my
American Express card before it's processed. I need to hear from you
ASAP.
Customers and prospects are forgiving for normal bugs in software.
However, customers are intolerant to bugs in the sign up or payment
flow. An error in payment flow will cause friction and friction will
kill the sale.
Running a start up is hard work, and negative (but justified) feedback
hurts more then I thought it would. This email made me feel awful.
Fortunately I learned from this mistake and the user's card was never
charged.
Always make sure to extensively test payment and sign up code. Try all
the edge cases. Try to make your software break. Don't inflict friction
on your potential customers.

View file

@ -0,0 +1,28 @@
Reasons why some Internet entities might want secure communication
##################################################################
:date: 2012-02-08 18:23
:author: Russell Ballestrini
:tags: Security
:slug: reasons-why-some-internet-entities-might-want-secure-communication
:status: published
:summary:
Internet entities often desire to communicate securely, some reasons include:
#. **Web Servers:** Communication on the Internet, or any network for that
matter, should be encrypted before transmitting sensitive data. This
will help prevent snooping from unauthorized parties. Most often SSL or
HTTPS may be used to create a secure communication “tunnel” between a
web server and a web client (browser).
#. **Server Administration:**
Operators should always use a secure protocal when working on a server or remote computer.
SSH (Secure Shell) wins the popularity contest with server admins.
#. **DNS Servers:**
Using DNSSEC could help prevent DNS poisoning and certifies DNS data.
DNS was first conceived as a distributed and highly scalable address lookup system.
Security was not its top priority.
Since then we have new DNSSEC extensions which allow for origin authentication of DNS data,
authenticated denial of existence, and data integrity.
DNSSEC does not attempt to solve availability and confidentiality.

View file

@ -0,0 +1,35 @@
What are the differences between message confidentiality and message integrity
##############################################################################
:date: 2012-02-08 17:47
:author: Russell Ballestrini
:tags: Security
:slug: what-are-the-differences-between-message-confidentiality-and-message-integrity
:status: published
:summary:
|
**What are the differences between message confidentiality and message
integrity? Can you have confidentiality without integrity? Can you have
integrity without confidentiality?**
|
message confidentiality
Two or more hosts communicate securely, typically using encryption.
The communication cannot be monitored (sniffed) by untrusted hosts.
The communication between trusted parties is confidential.
message integrity
The message transported has not been tampered with or altered. A
message has integrity when the payload sent is the same as the
payload received.
Sending a message confidentially does not guarantee data integrity. Even
when two nodes have authenticated each other, the integrity of a message
could be compromised during the transmission of a message.
Yes, you can have integrity of a message without confidentiality. One
can take a hash or sum of the message on both sides to compare. Often we
share downloadable files and provide data integrity using md5 hash sums.

View file

@ -0,0 +1,22 @@
Attributes of an 8-block cipher
###############################
:date: 2012-02-13 00:50
:author: Russell Ballestrini
:tags: Security
:slug: attributes-of-an-8-block-cipher
:status: published
**Consider an 8-block cipher and answer the following:**
How many possible input blocks does this cipher have?
How many possible mappings are there?
If we view each mapping as a key, then how many possible keys does this cipher have?
To find the input blocks of this cipher we raise 2 to the 8th power. 2^8 = 256 possible inputs.
To find the number of possible mappings we take the 256 input blocks and
find it's factorial. There are 256! possible mappings.
We can view each of these mappings as a key, so this cipher has 256! keys.

View file

@ -0,0 +1,73 @@
Monoalphabetic Cipher and Inverse Written in Python
####################################################
:date: 2012-02-13 22:39
:author: Russell Ballestrini
:tags: Code, Security
:slug: monoalphabetic-cipher-and-inverse-written-in-python
:status: published
|
| **Here is my implementation of a Monoalphabetic Cipher written with a
python dictionary:**
::
monoalpha = {
'a': 'm',
'b': 'n',
'c': 'b',
'd': 'v',
'e': 'c',
'f': 'x',
'g': 'z',
'h': 'a',
'i': 's',
'j': 'd',
'k': 'f',
'l': 'g',
'm': 'h',
'n': 'j',
'o': 'k',
'p': 'l',
'q': 'p',
'r': 'o',
's': 'i',
't': 'u',
'u': 'y',
'v': 't',
'w': 'r',
'x': 'e',
'y': 'w',
'z': 'q',
' ': ' ',
}
inverse_monoalpha = {}
for key, value in monoalpha.iteritems():
inverse_monoalpha[value] = key
message = "This is an easy problem"
encrypted_message = []
for letter in message:
encrypted_message.append( monoalpha[letter.lower()] )
print ''.join( encrypted_message )
**The encrypted output:** ``uasi si mj cmiw lokngch``
**Now we may use the inverse cipher to decrypt a message, "rmij'u uamu
xyj"**
::
encrypted_message = "rmij'u uamu xyj"
decrypted_message = []
for letter in encrypted_message:
try:
decrypted_message.append( inverse_monoalpha[letter] )
except KeyError:
decrypted_message.append( letter )
print ''.join( decrypted_message )
**Decrypted message:** ``wasn't that fun``

View file

@ -0,0 +1,66 @@
Symmetric Encryption vs Public Key Encryption
#############################################
:date: 2012-02-13 17:25
:author: Russell Ballestrini
:tags: Security
:slug: symmetric-encryption-vs-public-key-encryption
:status: published
**How many keys are involved for symmetric key encryption? How about
public key encryption?**
Suppose you have N people who want to communicate with each other using
symmetric keys. All communication between any two people, i and j, is
visible to group N. Only person i and person j can decrypt each others
messages.
**How many keys would Symmetric Encryption require to protect group N?**
I solved this with the following python function:
::
def count_symmetric_keys( N=2 ):
"""Provide the number of entities in group N.
return the number of symmetric keys needed for this group"""
keys = 0
for i in range( 0, N ): keys += i
return keys
A reader suggested the following optimized formula:
::
def calc_symmetric_keys( N=2 ):
"""Provide the number of entities in group N.
return the number of symmetric keys needed for this group"""
return N*(N-1)/2
.. raw:: html
</p>
If group N had 10 members, it would need to generate and maintain 45
Symmetric Keys.
If group N had 50 members, it would need to generate and maintain 1225
Symmetric Keys.
Symmetric keys are also susceptible to man-in-the-middle attacks. This
attack occurs when an entity poses as a trusted entity. Let i and j be
trusted entities. Let k be an untrusted attacker. If k determined the
Symmetric key it could send or receive messages posing as i or j.
**How many keys would Public-key Encryption require to protect group
N?**
Public Key Encryption requires 2n keys or two keys per person in group
N. Public key encryption also does not require 'pre sharing' the secret
key before communication may start. Each member would need 1 public key
and 1 private key.
If group N had 10 members, it would need to generate and maintain 20
Public/Private Keys.
If group N had 50 members, it would need to generate and maintain 100
Public/Private Keys.

View file

@ -0,0 +1,140 @@
Why does a Hash provide better message integrity then an Internet checksum?
###########################################################################
:date: 2012-02-13 19:19
:author: Russell Ballestrini
:tags: Code, Security
:slug: why-does-a-hash-provide-better-message-integrity-then-an-internet-checksum
:status: published
**Why does a Hash provide better message integrity then an Internet
checksum?**
Hash function and checksum function both return a value which cannot be
reversed.
| An Internet checksum (TCP checksum or IP checksum) is designed to
detect common errors quickly and efficiently. An Internet checksum
does not attempt to prevent collisions.
| *Man cksum for more info.*
A Hash provides better message integrity because it has less collisions
then an Internet checksum. A collision means there is more then one way
to produce the same sum. A great hash function aims to reduce the
occurrence of collisions. *Man md5 and sha for more info.*
**What is a collision**
| Let H() be a hash function. Let x and y be two differing messages.
| *H(x) = H(y)* would be a collision.
**I like to use python to show examples of hash functions**
In this example I pass a message into the MD5 hash function to produce a
resulting hash of the message. You can think of this hashed output as a
finger print of the message.
::
import hashlib as h
message = "This message will be placed into an MD5 hash function to authenticate its integrity."
print h.md5(message).hexdigest()
.. raw:: html
</p>
| *Hash Output:*
::
18f189f94b245ad8566206c199b4f60a
.. raw:: html
</p>
Now If I passed that message to you along with its MD5 hash hex
representation, you could put the message into your own MD5 hash
function and compare the resulting hash. This method is used to validate
the message or verify data integrity.
**Can you "decrypt" a hash of a message to get the original message**
No! A hash may *not* be reversed, which means it cannot be decrypted.
.. raw:: html
</p>
By design a hash algorithm has no inverse, there is no way to get the
original message from the hash. This is good news, turns out we have
some really great applications for this type of function. We can
validate messages, we can securely store passwords, and we can quickly
determine if a message or file has been tampered with.
When using a publicly known hash function for storing password hashes,
make sure to always use a salt or shared secret. Failure to do so will
make your storage scheme susceptible to a rainbow table attack. A
rainbow table allows a cracker to quickly match a list of hashes with a
table of previously computed hash values and correlated passwords.
**What is salt or a shared secret?**
You can use salt or a shared secret to add extra data to a message
before hashing with a publicly known algorithm. Below I will document
how to properly add salt to a message before generating a SHA256 hash.
::
import hashlib as h
message = "This message and some salt will be hashed with SHA 256."
salt = "This is some secret salt data"
print h.sha256(message+salt).hexdigest()
.. raw:: html
</p>
| *Hash Output:*
::
5e8d86bab9604620f19cfbc5f836f47feb9e8c9e74264fff1f4938bdaab1eeaa
.. raw:: html
</p>
Adding a salt to the message allows us to use a publicly know algorithm
in a more protected manner.
**Can you spot the error in the python code below?**
::
import hashlib as h
message = "This message and some salt will be hashed with SHA 256."
salt = "This is some secret salt data"
print h.sha256(message).hexdigest()+salt
.. raw:: html
</p>
If you guessed that the message and salt BOTH need to be hashed together
then you are correct!
The above code would have produced the following invalid hash:
| *Hash Output:*
::
79cd4bfa1bcb71a7a1b5bfd5e8cfc8368a6cc6cb836d24bf04f2ef2bd0e81261This is some secret salt data
.. raw:: html
</p>
**You should follow me on twitter
`here <https://twitter.com/#!/RussellBal>`__.**

View file

@ -0,0 +1,73 @@
Block cipher lab
################
:date: 2012-02-14 01:36
:author: Russell Ballestrini
:tags: Code, Security
:slug: block-cipher-lab
:status: published
|
| **Consider the following block cipher.** Suppose that each block
cipher T simply reverses the order of the eight input bits (so that,
for example 11110000 becomes 00001111).
| Further suppose that the 64-bit scrambler does not modify any bits.
With n = 3 iterations and the original 64-bit input equal to 10100000
repeated eight times, what is the value of the output?
| Now change the last bit of the original 64-bit input from 0 to a 1.
Now suppose that the 64-bit scrambler inverses the order of the 64
bits.
| **Solution in python:**
::
def chunks( l, n ):
"""accept a list and chuck size, return chunks"""
return [ l[ i:i+n ] for i in range( 0, len(l), n ) ]
def T( blocks ):
"""for each block, reverse block, return blocks"""
result = []
for block in blocks:
result.append( ''.join( [bit for bit in reversed( block )] ) )
return result
def scrambler( input ):
"""inverse the order of input"""
return ''.join( [i for i in reversed( input ) ] )
def cipher1( input, n = 3, chunk_length = 8 ):
"""make chucks out of input, reverse each chunk return result"""
blocks = chunks( input, chunk_length )
for i in range( 0, n ): blocks = T( blocks )
return ''.join( blocks )
def cipher2( input, n = 3, chunk_length = 8 ):
"""same as cipher1 but with scrambler"""
blocks = chunks( input, chunk_length )
for i in range( 0, n ):
blocks = T( blocks )
blocks = chunks( scrambler( ''.join( blocks ) ), chunk_length )
return ''.join( blocks )
if __name__ == "__main__":
input = "1010000010100000101000001010000010100000101000001010000010100000"
print cipher1( input )
# output: 0000010100000101000001010000010100000101000001010000010100000101
input = "1010000010100000101000001010000010100000101000001010000010100001"
print cipher1( input )
# output: 0000010100000101000001010000010100000101000001010000010110000101
input = "1010000010100000101000001010000010100000101000001010000010100000"
print cipher2( input )
# output: 1010000010100000101000001010000010100000101000001010000010100000
input = "1010000010100000101000001010000010100000101000001010000010100001"
print cipher2( input )
# output: 1010000110100000101000001010000010100000101000001010000010100000

View file

@ -0,0 +1,25 @@
My 4 month old's 15 minutes of fame
###################################
:date: 2012-02-16 18:42
:author: Russell Ballestrini
:tags: Opinion
:slug: my-4-month-olds-15-minutes-of-fame
:status: published
:summary:
|image0|
My sister `@VeronicaBal <https://twitter.com/#!/VeronicaBal>`__
was watching my son (`@RussellBal <https://twitter.com/#!/RussellBal>`__)
tweeted a picture of him.
The official `@RedSox <https://twitter.com/#!/RedSox>`__ re-tweeted the image to all 197,035 followers!
**I'm a proud daddy!**
|image1|
.. |image0| image:: /uploads/2012/02/cutest-redsox-fan-ever.jpg
:target: http://russell.ballestrini.net/my-4-month-olds-15-minutes-of-fame/cutest-redsox-fan-ever/
.. |image1| image:: /uploads/2012/02/Screenshot-at-2012-02-16-172630.png
:target: http://russell.ballestrini.net/my-4-month-olds-15-minutes-of-fame/screenshot-at-2012-02-16-172630/

View file

@ -0,0 +1,52 @@
Jake, Finn, and Ice King
########################
:date: 2012-02-18 13:34
:author: Russell Ballestrini
:tags: Art
:slug: jake
:status: published
:summary:
My Adventure Time art, painted with MyPaint and wacom tablet.
**Jake from Adventure Time, painted with MyPaint and wacom tablet.**
I did this because my 4 month old "cracks-up" laughing whenever this
character is on screen. I think the combination of colors and Jake's
voice, played by John William DiMaggio, get him going.
|image0|
**Finn from Adventure Time.**
Took a shot at painting Finn.
|image1|
I'm on a roll! Here is Ice King.
|image2|
Jake standing up, painted this this morning
|image3|
Lumpy princess
|image4|
I'm getting faster, this was completed in 20 minutes
|image5|
.. |image0| image:: /uploads/2012/02/mypaint-jake-adventure-time.png
:alt: I drew Jake from Adventure Time
.. |image1| image:: /uploads/2012/02/mypaint-finn-adventure-time.png
:alt: I drew Finn from Adventure Time
.. |image2| image:: /uploads/2012/02/ice-king-adventure-time.png
:alt: I drew Ice King from Adventure Time
.. |image3| image:: /uploads/2012/02/jake-standing.png
:alt: I drew Jake again from Adventure Time
.. |image4| image:: /uploads/2012/02/lump-princess.png
:alt: I drew lumpy princess from Adventure Time
.. |image5| image:: /uploads/2012/02/sad-jake.png
:alt: I drew sad Jake from Adventure Time

View file

@ -0,0 +1,62 @@
Zenoss or Nagios monitoring of HTTPS using client certificate authentication
############################################################################
:date: 2012-02-26 19:21
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: zenoss-or-nagios-monitoring-of-https-using-client-certificate-authentication
:status: published
I recently needed to monitor an HTTPS API for response time and
availability. At first I planned to just use the Nagios check\_http
command.
After gathering more requirements I learned that the API was protected
by client certificate authentication. After some research I quickly
found that no solution existed to monitor HTTP protected by client
certs. I needed to write my own plugin.
This is the python plugin I came up with:
**check\_http\_client\_cert.py**
::
#!/usr/bin/python
"""Nagios/Zenoss client cert https checker"""
import httplib
from optparse import OptionParser
from time import time
from sys import exit
def request( hostname, port, cert_file, path ):
"""request a resource and return response object"""
try:
c = httplib.HTTPSConnection( hostname, port, cert_file=cert_file )
c.request( "GET", path )
return c.getresponse()
except:
return False
if __name__ == '__main__':
parser = OptionParser()
parser.add_option('-H', '--hostname', dest='hostname')
parser.add_option('-p', '--port', dest='port')
parser.add_option('-c', '--cert_file', dest='cert_file')
parser.add_option('-P', '--path', dest='path',
help="Path relative to root, like /image/search")
o, args = parser.parse_args()
#print o
start = time()
r = request( o.hostname, o.port, o.cert_file, o.path )
elapse = time() - start
if r:
if r.status >= 200 and r.status < 400:
print "HTTP OK:", r.status, r.reason, "|time=" + str(elapse) + "s;;;"
exit( 0 )
print "HTTP Critical:", r.status, r.reason
exit( 2 )

View file

@ -0,0 +1,141 @@
How to capture HTTPS SSL TLS packets with wireshark
###################################################
:date: 2012-02-29 19:14
:author: Russell Ballestrini
:tags: Guide, Security
:slug: how-to-capture-https-ssl-tls-packets-with-wireshark
:status: published
This article will explain how to use wireshark to capture TCP/IP
packets. Specifically I will show how to capture encrypted (HTTPS)
packets and attempt to document the "dance" a client and server do to
build an SSL tunnel.
**What is Wireshark?**
Wireshark is a network protocol analyzer for Windows, OSX, and Linux. It
lets you capture and interactively browse the traffic running on a
computer network. Similar software includes tcpdump on Linux.
**Install Wireshark**
First step, acquire Wireshark for your operating system.
*Ubuntu Linux:* ``sudo apt-get install wireshark``
*Windows or Mac OSX:* search for wireshark and download the binary.
**How to capture packets**
This is Wireshark's main menu:
|image0|
To start a capture, click the following icon:
|image1|
A new dialog box should have appeared. Click start on your preferred
interface:
|image2|
You are now capturing packets. The packet information is displayed in
the table below the main menu:
|image3|
Now browse to an HTTPS website with your browser. I went to
https://linkpeek.com and after the page completely loaded, I stopped the
Wireshark capture:
|image4|
Depending on your network, you could have just captured MANY packets. To
limit our view to only interesting packets you may apply a filter.
Filter the captured packets by ssl and hit Apply:
|image5|
Now we should be only looking at SSL packets.
**Next we will analyze the SSL packets and answer a few questions**
**1.** For each of the first 8 Ethernet frames, specify the source of
the frame (client or server), determine the number of SSL records that
are included in the frame, and list the SSL record types that are
included in the frame. Draw a timing diagram between client and server,
with one arrow for each SSL record.
| Frame 1 client \| 1 record \| Arrival Time: Feb 15, 2012
15:38:55.601588000
| Frame 2 server \| 1 record \| Arrival Time: Feb 15, 2012
15:38:55.688170000
| Frame 3 server \| 2 record \| Arrival Time: Feb 15, 2012
15:38:55.688628000
| Frame 4 client \| 3 record \| Arrival Time: Feb 15, 2012
15:38:55.697705000
| frame 5 server \| 2 record \| Arrival Time: Feb 15, 2012
15:38:55.713139000
| frame 6 client \| 1 record \| Arrival Time: Feb 15, 2012
15:38:55.713347000
| frame 7 server \| 0 record \| Arrival Time: Feb 15, 2012
15:38:55.713753000
| frame 8 server \| 1 record \| Arrival Time: Feb 15, 2012
15:38:55.715003000
**2.** Each of the SSL records begins with the same three fields (with
possibly different values). One of these fields is “content type” and
has length of one byte. List all three fields and their lengths.
| Each hexadecimal digit (also called a "nibble") represents four binary
digits (bits) so each pair of hexadecimal digits equals 1 byte.
| a. Destination mac address \| 6 btyes \| 00 21 9b 31 99 51
| b. Source mac address \| 6 bytes \| 00 10 db ff 20
| c. Type: IP \| 2 byte \| 08 00
**ClientHello Records**
| **3.**\ Expand the ClientHello record. (If your trace contains
multiple ClientHello
| records, expand the frame that contains the first one.) What is the
value of the
| content type?
| hex: 16 (16+6=22) Handshake
| **4.** Does the ClientHello record advertise the cipher suites it
supports? If so, in the first listed suite, what are the public-key
algorithm, the symmetric-key algorithm, and the hash algorithm?
| MD5, SHA, RSA, DSS, DES, AES
**ServertHello Records**
| **5.** Look to the ServerHello packet. What cipher suite does it
choose?
| Cipher Suite: TLS\_RSA\_WITH\_AES\_128\_CBC\_SHA (0x002f)
| **6.** Does this record include a nonce? If so, how long is it? What
is the purpose of the
| client and server nonces in SSL?
| Yes, 28 bytes. The ClientHello packet also generated a nonces. They
are used to make the session communication between the two nodes
unique. It "salts" the communication to prevent replay attacks. A
replay attack happens when data from old communications is used to
"crack" a current communication.
| **7.**\ Does this record include a session ID? What is the purpose of
the session ID?
| Yes, This is to make things efficient, in case the client has any
plans of closing the current connection and reconnect in the near
future.
| **8.**\ How many frames does the SSL certificate take to send?
| In this case it took 4 frames
.. |image0| image:: /uploads/2012/02/wireshark.png
.. |image1| image:: /uploads/2012/02/wireshark-start-capture.png
.. |image2| image:: /uploads/2012/02/wireshark-sniff.png
.. |image3| image:: /uploads/2012/02/wireshark-packets.png
.. |image4| image:: /uploads/2012/02/wireshark-stop-capture.png
.. |image5| image:: /uploads/2012/02/wireshark-filter.png

View file

@ -0,0 +1,110 @@
How to save hundreds of dollars on groceries without clipping coupons
#####################################################################
:date: 2012-03-03 19:54
:author: Russell Ballestrini
:tags: Greatest Hits, Opinion
:slug: how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons
:status: published
:summary:
Jenn explains how she drastically decreased our grocery bill.
**This is the first time I've had a guest blogger on my site. It may
sound campy but my wife Jenn wrote this article after explaining how our
grocery bill decreased so drastically.**
|image0|
**How to save hundreds of dollars on groceries without clipping
coupons**
I was recently re-working the cabinets of the kitchen, anticipating a
new and organized year. With a growing family and limited space, I
decided to do some consolidating.
I allocated a whole section of cabinet space to arts and craft supplies
for the kids (play-doh, paints, etc), and another to bottles and
formula. I suddenly realized that my food products remained scattered
across the counter and there was only one lonely cabinet left to fill.
|image1|
Among the food items on the counter, many were far expired, forgotten in
the dusty back corners of the cabinets. I thought to myself “Hey! I
worked hard to find coupons and sales for all these things.” and I
regretfully filled a garbage bag with old food.
I had been clipping coupons and striving to match the savings of super
couponers. These couponers, featured on popular reality shows, often
recommend stock-piling when food items go on sale and you have coupons
to purchase them.
|image2|
I opened the refrigerator to find a similar and familiar situation: a
head of lettuce that had seen better days, a tomato that had lost it's
freshness, and other food items that were past the date of recommended
consumption.
Looking into the garbage bag, I realized that... every week I was
wastefully throwing food and money away. I decided to make a commitment
that has cut my grocery bill in half! Do I still use coupons? YES, if
they fit into my weekly plan. Do I still look at the sale flyers?
ABSOLUTELY! But...
**These two strategies have saved me much more money:**
| **1. Cut down food storage spaces**
| If you currently use 4 food cabinets try designating only 2. In my
case I consolidated my food items to 1 cabinet. This consolidation
saved money in two distinct ways:
- *First,* I am easily able to assess which food items I have and when
they expire. This helps prevent re-buying a product we already have
at home (Remember the time you were at the grocery store buying all
the ingredients for those chocolate chip cookies and couldn't
remember if you had any brown sugar? Undoubtebly you re-bought,
probably to discover you had a brand new, unopened package in the
cabinet).
- *Secondly,* it prevents stock piling. I can buy only what I need for
the week ahead, after all, it is all I have room for. If you generate
the sense that “my kitchen is full of food”, it eliminates the need
to buy for the sake of filling the cabinets.
| **2. Let your menu dictate your shopping list**
| My son complains “There are no more crackers." My husband sighs,
“We're all out of bananas." My answer used to be probably a lot like
yours “OK, I'll put it on my list." I had the perception that I
constantly needed to replenish. It is unnecessary and wasteful to have
three different brands of crackers, five types of cereal, and every
kind of fruit known to the produce section. Without fail, I was
throwing away money each week.
Instead, I sit down each week with two sheets of paper. One serves as
dinner menu for the week and the other as my shopping list. When
building my menu, I consider the sales and coupons I have on hand. If I
notice a great deal on pasta sauce, we might have spaghetti one night
and meatball subs another. I immediately begin a shopping list writing
ONLY items used for the companion menu. Additionally, I add two
breakfast choices and two lunch choices for the week if needed (some
breakfast items such as pancake batter or a family size box of cereal
often last longer). I also include 2 choices of fresh fruit or
vegetable. To reduce waste and cost I ONLY add the staples (eggs, milk,
and bread) when it is on the menu. If none of my breakfast, lunch, or
dinner choices call for bread, I skip that aisle for this trip.
**It took me only a few short weeks to notice that by cutting my food
storage space in half, and adopting a disciplined approach to menu and
list making, I had saved hundreds of dollars. I challenge you to cut
down your food storage spaces and let your menu dictate your shopping
list!**
|image3|
.. |image0| image:: /uploads/2012/03/3247325203_6108897833_o.jpg
:target: http://russell.ballestrini.net/how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons/3247325203_6108897833_o/
.. |image1| image:: /uploads/2012/03/6876071849_b82bca1076_o.jpg
:target: http://russell.ballestrini.net/how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons/6876071849_b82bca1076_o/
.. |image2| image:: /uploads/2012/03/6876069657_5a2f4ae487_o.jpg
:target: http://russell.ballestrini.net/how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons/6876069657_5a2f4ae487_o/
.. |image3| image:: /uploads/2012/03/6876068965_db8a5d12b2_o.jpg
:target: http://russell.ballestrini.net/how-to-save-hundreds-of-dollars-on-groceries-without-clipping-coupons/6876068965_db8a5d12b2_o/

View file

@ -0,0 +1,16 @@
What do you name your python virtualenv?
########################################
:date: 2012-03-07 21:42
:author: Russell Ballestrini
:tags: Opinion
:slug: what-do-you-name-your-python-virtualenv
:status: published
|
| **What do you name your python virtualenv?**
I name my virtualenv 'virtpy'. Is there a standard name being used out
there?
Maybe we can come to a consensus as a standard name? Please feel free to
post your virtualenv names here as a sort of poll.

View file

@ -0,0 +1,19 @@
"I see" said the blind man, to the deaf dog, as he walked off the cliff.
########################################################################
:date: 2012-03-23 22:04
:author: Russell Ballestrini
:tags: Opinion
:slug: i-see-said-the-blind-man-to-the-deaf-dog-as-he-walked-off-the-cliff
:status: published
|
**"I see" said the blind man, to the deaf dog, as he walked off the
cliff.**
|
As far as I can tell, I am the originator of this version of this quote.
**EDIT:** changed "originator of the quote" to "originator of this
version of this quote".

View file

@ -0,0 +1,35 @@
nosslsearch cname is a bad idea and solution
############################################
:date: 2012-03-26 11:51
:author: Russell Ballestrini
:tags: Opinion, Security
:slug: nosslsearch-cname-is-a-bad-idea-and-solution
:status: published
|
| **Google SafeSearch and SSL Search for Schools suggests implementing
the following changes to the network:**
To utilize the no SSL option for your network, configure the DNS
entry for www.google.com to be a CNAME for nosslsearch.google.com.
|
| **
Here are the reasons why this is a bad idea and solution:**
- In order to create a CNAME record for www.google.com we need to
become an authoritative master of that zone.
- If you become an authoritative master you need to host all of
Google's DNS resource records for the domain.
- Google is asking us to DNS poison it's flag ship product on our
networks.
- If other companies follow suit the internet will quickly become
unmanageable. DNS was not ment to work this way.
- Not all networks have a local DNS server
|
| This is a bad idea. Please change your stance on this matter.
|
| Reference:
http://support.google.com/websearch/bin/answer.py?hl=en&answer=186669

View file

@ -0,0 +1,25 @@
Trouble mounting filesystem on KVM guest after reboot
#####################################################
:date: 2012-03-27 12:04
:author: Russell Ballestrini
:tags: DevOps
:slug: trouble-mounting-filesystem-on-kvm-guest-after-reboot
:status: published
**Just found this out the hard way...**
It looks like the attachment of ``/KVMROOT/guest-dev-app.img`` on
guest-dev did not persist when the KVM host rebooted for patching.
As it appears the ``virsh attach-disk`` command works a lot like the
``mount`` command.
In order to have a disk attachment persist after a reboot, I think we still need to do a ``virsh edit``.
the ``virsh attach-disk`` command is useful because it allows us to
attach disk images to guests without restarting.
**tldr;**
| ``virsh attach-disk`` is to ``mount`` as
| ``virsh edit`` is to ``vim /etc/fstab``

View file

@ -0,0 +1,28 @@
I just purchased Instagram for 1B and all I got was this lousy image filter
###########################################################################
:date: 2012-04-09 17:47
:author: Russell Ballestrini
:tags: Opinion
:slug: i-just-purchased-instagram-for-1b-and-all-i-got-was-this-lousy-image-filter
:status: published
**Facebook purchased Instagram on Monday, April 9th 2012 for
$1,000,000,000 US.** Instagram, a free to use image sharing iPhone
application, has absolutely NO REVENUE STREAM. The application is free
to download, free to use, and has no monetization support from
advertisements.
$1000000000.00 / 30000000 users = **33.33 per user**
At the time of the sale Instagram had 30 million users in total
(30,000,000). That means Facebook just paid heavy **$33.33** per user...
This is obviously a bad deal for Facebook.
Instagram did not have a method to monetize its application. I hedge my
bets that Facebook will never recoup the one billion dollars spent on
this deal.
Mark Zuckerburg, I just thought up the perfect T-Shirt for you:
**I just purchased Instagram for 1B and all I got was this lousy
image filter.**

View file

@ -0,0 +1,70 @@
The most valuable registration field: How did you hear about us?
################################################################
:date: 2012-04-26 22:21
:author: Russell Ballestrini
:tags: Greatest Hits, LinkPeek, Opinion
:slug: the-most-valuable-registration-field-how-did-you-hear-about-us
:status: published
|
**How did you hear about us?**
|
I first answered this question when joining Linode.
I remember thinking "Wow, this is a great time to ask me!" because the real answer was still in my short term memory.
|
When I launched `LinkPeek <https://linkpeek.com/signup?plan=better>`__ I applied this technique.
After an amazing launch (thank you colleagues from HackerNews) the true significance of this field was exposed.
I would argue that the answer to this simple question holds more value than collecting a username.
|
**Here is why:**
|
It may seem obvious after reading this post, but this is what I learned
and I am sharing to help other startups.
|
Asking "How did you hear about us?" will help you determine:
- how your customers found you
- which of your marketing efforts are working
- where to spend money or time marketing (and where to stop)
- your true target market
- how your customers will use your product
Here are some other benefits to this technique. It opens a dialog or
conversation with the customer, which in return should help lower
friction during the sale. You already have the the customer engaged
during the signup process. I am firmly against the traditional method of
surveying customers. I believe the same data can be collected without
being abrasive or wasting the customers time.
"How did you hear about us?" is the gift that keeps on giving.
It is commonly accepted that shorter registration forms lead to better
conversions. I totally agree, but in this case the sacrifice is worth
learning more about my target market and customer needs.
|
**How to ask this question properly:**
#. **Just ask!** I second guessed my decision to add this field right
before launch but that anxiety promptly faded after reading the first
answer.
#. **Don't use pre-filled answers!** Having pre-filled answers is
counter productive because you will not learn anything new. You are
forcing the user into choosing one of your answers...
#. **Allow the user to type as much as they like!** A few of my
customers nearly wrote a book in the text field.
**If you liked this post, you should follow me on twitter
`here <https://twitter.com/RussellBal>`__.**

View file

@ -0,0 +1,27 @@
How to rescue logs and config from a failed Citrix NetScaler App Gateway
########################################################################
:date: 2012-05-11 00:05
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: how-to-rescue-logs-and-config-from-a-failed-citrix-netscaler-app-gateway
:status: published
Today our production Citrix NetScaler broke. The box wouldn't boot and
our only backup copy of the config was on the NetScaler itself.
Being the only Unix guy around I attempted to help out the admins
working the outage. I SSH'd into the development NetScaler and noticed
it runs on FreeBSD.
I suggested fetching the Hard drive and mounting it on a Linux computer.
The NetScaler has one SATA (not SAS) disk so my desktop was compatible.
I installed the disk in the Linux tower and mounted the filesystem using
the following command:
``mount --read-only --type=ufs --test-opts ufstype=44bsd /dev/sda5 /mnt``
Once mounted I was able to SCP interesting files to a safe location.
Warning, this procedure might void your warranty. If in doubt, call
support first.

View file

@ -0,0 +1,39 @@
High load on web server after updating from Ubuntu 10.04 to Ubuntu 12.04 LTS
############################################################################
:date: 2012-05-19 18:40
:author: Russell Ballestrini
:tags: DevOps
:slug: high-load-on-web-server-after-updating-from-ubuntu-10-04-to-ubuntu-12-04-lts
:status: published
:summary:
Charts that show the load difference.
|
| **High load on web server after updating from Ubuntu 10.04 to Ubuntu
12.04 LTS**
| Check out charts which lineup to when I upgraded:
| |image0|
| I couldn't determine the cause of the load average increase...
**Update:** The issue might be memory bound. Check out this graph that
show much higher swap.
|image1|
**After much research this appears to be a load calculation and display
problem with the newer Linux kernels. The community has found Commit-ID:
c308b56b5398779cd3da0f62ab26b0453494c3d4 to be the problem. The commit
causes incorrect high reported load averages can be reported under
conditions of light load and high enter/exit idle frequency conditions
(greater then 25 hertz).**
A nice fellow at
http://www.smythies.com/~doug/network/load\_average/new.html researched
the topic between tick and tickless linux kernels and the effect they
had on load averages. You should check it out.
.. |image0| image:: /uploads/2012/05/high-load-after-updating-ubuntu-from-10.04-LTS-to-12.04-LTS.png
:target: http://russell.ballestrini.net/high-load-on-web-server-after-updating-from-ubuntu-10-04-to-ubuntu-12-04-lts/high-load-after-updating-ubuntu-from-10-04-lts-to-12-04-lts/
.. |image1| image:: /uploads/2012/05/ubuntu.12.04.swap_.year_.png
:target: http://russell.ballestrini.net/high-load-on-web-server-after-updating-from-ubuntu-10-04-to-ubuntu-12-04-lts/ubuntu-12-04-swap-year/

View file

@ -0,0 +1,36 @@
Always attempt to scale vertically first
########################################
:date: 2012-06-10 21:52
:author: Russell Ballestrini
:tags: DevOps, LinkPeek
:slug: always-attempt-to-scale-vertically-first
:status: published
I spent the weekend fretting because one of my servers was basically
being DOS'd by paying customers. During the outage I started thinking
about the best way to scale and how I could make the code-base more
efficient.
Linux top reported high load, in the 20's. Eventually I figured out that
the server was having IO performance issues.
I wasted a bunch of time attempting to fight fires. After about an hour
of that I decided to scale my VPS vertically by giving it an extra 256mb
of memory and a larger swap file (256mb to 1024mb).
These two changes were surprisingly effective and the IO issues
resolved. Apparently the server was starving for memory which caused the
host to swap which brought things to a crawl waiting for IO.
Crisis averted for the moment. Now I am free to think clearly and
engineer a proper solution instead of attempting to put out fires.
If you ever encounter a similar situation, attempt the simplest fix.
There is no shame in throwing more money at a problem if it will buy you
time. In this case, an extra $10.00 a month relieved the performance
issues and bought myself some time, for the moment.
|image0|
.. |image0| image:: /uploads/2012/06/vertical-scale-marked.png
:target: http://russell.ballestrini.net/always-attempt-to-scale-vertically-first/vertical-scale-marked/

View file

@ -0,0 +1,40 @@
Prevent a certain program from running too long in bash
#######################################################
:date: 2012-06-13 09:45
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: prevent-a-certain-program-from-running-to-long-in-bash
:status: published
**Update** - I opensourced this script here: `bash kira <https://github.com/russellballestrini/bash-kira>`__
I came up this this script to kill certain programs after they run for
too long. This works like similar to a timeout. Warning this script is
pretty harsh and kills the program.
::
#!/bin/bash
PROGRAM=replace-with-program-name
PIDSFILE=/tmp/kill-these.pids
for pid in `pidof $PROGRAM`
do
if grep -q $pid $PIDSFILE
then
kill $pid
fi
done
> $PIDSFILE
for pid in `pidof $PROGRAM`
do
echo $pid >> $PIDSFILE
done
Then I wrote a cronjob to kill hung programs:
::
* * * * * /usr/local/sbin/killprogs.sh

View file

@ -0,0 +1,73 @@
My top five suggestions for an independent developer creating a new product or service
######################################################################################
:date: 2012-06-25 19:50
:author: Russell Ballestrini
:tags: Greatest Hits, LinkPeek, Opinion
:slug: my-top-five-suggestions-for-an-independent-developer-creating-a-new-product-or-service
:status: published
**1. Write everyday.** Build a blog for the project and write about
milestones, progress, and hurdles. Also keep a personal blog and write
about hobbies. Read some theory about "copy writing" and search engine
optimization. Write personalized email responses to customers. Great
communication skills will have the most impact on the success of your
company. The best way to increase communication skills is to do it.
**2. Spend at least 15 minutes on the company everyday.** Even small or
petty tasks will lift your company to the next goal. Over time the
company will grow strong and people will give you attention. Building a
company seems similar to building a character in an RPG. Instead however
you will progress your company to its next level. Also, always look
ahead when working, don't look back at prior achievements. Keep your
eyes on the next milestone and keep moving forward. This type of
behavior will promote growth and prevent getting stuck in the daily
grind.
**3. Do not fear manual processes.** In the early stages of a company
you should only build automation which will enhance customer experience.
For everything else, build a repeatable manual process.
Coding automation will typically cost more time to build than it will save.
For example on `LinkPeek <https://linkpeek.com>`__ I have not automated customer account de-activation because fortunately I don't have to do many of them.
This manual process only takes a couple minutes to complete and gives me a chance to write a "thank you, and sorry to see you go" email to the fleeting customer.
Manually de-activating (loosing) a customer is also a humbling experience.
Keep a list of nice-to-have automations and review them in the far future.
How far in the future? Far, like when your idea is validated and your company appears successful.
Don't waste valuable time building automation for a service nobody will pay for or use.
Time is your most scarce resource.
**4. Always attempt to increase your luck.** This might sound funny but
the the following suggestions should reduce risk and also increase luck:
- Meet fellow lucky people
- Write on a blog
- Advertise your company on side-projects that lack monetization
- Think before wasting money using adsense or similar networks
- Do market research before committing to an idea
- Figure out how to make money before building product
- Build the smallest version of the product that could still be sold
- Try to stay focused on the things your customers will see
- Don't build an admin dashboard until successful (profitable?)
- Choose tasks that require the least effort but have the biggest
impact
- Don't be afraid to do things in the early stages that won't scale in
the long run (example: personal email responses)
- Listen to how early adopters describe your product; then on your
website, marketing and emails reuse their words.
- Attempt to keep marketing, newsletter, and support emails only few
sentences long.
- Start maintaining an opt-in email list
You might have the best mouse-trap but without luck it will never gain
traction.
**5. Have an unhealthy passion to support your customers and build your
company.** Six days ago I suffered a major chemical burn to my right
eye. With my wife's help I continued to respond to support emails. Since
I launched `LinkPeek <https://linkpeek.com>`__ my
focus and attention to my customers and company have never faltered.
Dedication plays a key role to success.
|image0|
.. |image0| image:: /uploads/2012/06/dedication-eye-chemical-burn.jpg
:target: /uploads/2012/06/dedication-eye-chemical-burn.jpg

View file

@ -0,0 +1,27 @@
miniuri parser and ago human timedelta
######################################
:date: 2012-06-29 21:30
:author: Russell Ballestrini
:tags: Code, Project
:slug: miniuri-parser-and-ago-human-timedelta
:status: published
I just packaged and published a couple of python modules to pypi:
* `ago <http://pypi.python.org/pypi/ago>`__
* `miniuri <http://pypi.python.org/pypi/miniuri>`__
To install them, run:
*Install:*
.. code-block:: bash
pip install --upgrade ago miniuri
If you want to view their source code, look here:
* `miniuri <https://bitbucket.org/russellballestrini/miniuri>`__
* `ago <https://bitbucket.org/russellballestrini/ago>`__
I hope you enjoy them.

View file

@ -0,0 +1,41 @@
The Pyramid community taught me the importance of test driven development
#########################################################################
:date: 2012-09-22 14:03
:author: Russell Ballestrini
:tags: Opinion
:slug: the-pyramid-community-taught-me-the-importance-of-test-driven-development
:status: published
`Sontek's patch <https://github.com/Pylons/pyramid/commit/72561a213ccc456738582551e85fab0f0c8d09ab>`__
I greeted the UPS man in the middle of the street to sign for my new
Lenovo ThinkPad T430. Because this was My first *brand-new* laptop
purchase I rationalized the time I spent tracking the package from the
factory in China to my hands in Connecticut. Once inside, I opened the
box and started installing Fedora 17. I couldn't help but to take in the
new-electronics smell.
I've been without a laptop for more than a month so I was eager to get
my development environment configured. Most of my tools ship with
vanilla Linux, vim, python, hg mercurial, chromium browser, etc. My
first goal was to get a development copy of linkpeek.com running
locally. This took about 5 minutes and it seemed to be working fine
until I noticed a few pages had errors. The errors seemed to be caused
by a difference between Pyramid 1.3 and 1.4a1. But what was failing?
I posted a short message in #pyramid about the bug and minutes later I
had multiple developers prodding for hints. "Could you post the whole
traceback?", "What does your view look like?". I answered quickly and
attempted to explain what I thought was going on. Turns out I was close
but before I could finish explaining the problem, sontek had a working
one-character-fix and was in the process finishing the tests to prove
the patch. He also explained what I should do in the interim to patch
locally.
In the next 4 hours a pull request was submitted to the upstream master
and the patch was peer reviewed, accepted, and integrate by mcdonc. That
impressed me, a lot. All Pylon Projects have strict policies about test
coverage and now I understand why. Tests not only help produce better
bug-free software but also act as a powerful tool when proving the
validity of a patch. I plan to devote the next couple weeks to making
test-driven-development a habit.

View file

@ -0,0 +1,124 @@
Explaining cache with python
############################
:date: 2012-10-02 15:22
:author: Russell Ballestrini
:tags: Code, Greatest Hits
:slug: explaining-cache-with-python
:status: published
**What is cache?** I define cache as "a saved answer to a question".
Caching can speed up an application if a computationally complex
question is asked frequently. Instead of the computing the answer over
and over, we can use the previously cached answer. This post will
present one method of adding cache to a python program. Specifically we
will write a program that computes prime numbers and saves the answers
into cache for quick retrieval.
**EDIT:** The kind people of the Internet have expressed concern with my
loose use of the term cache; the techniques that follow are most
accurately described as memoization.
**One algorithm for determining if a number is prime follows:**
::
prime_flag = True # default state
x = 5 # number to test
if x == 1:
prime_flag = False
else:
for i in range( 2, x ):
if x % i == 0:
prime_flag = False
break
print prime_flag
*Create a prime\_flag variable to hold the answer and default it to
true. Let x be the number being tested and if x is equal to 1, the x is
not prime. Otherwise iterate over each number in the range of 2 to x.
Let i be the current number to be tested. if x is divided by i without
any remainder, x is not prime. Set the prime\_flag to False and break
out of the loop. Print the result.*
**Next we will move the algorithm into a function which will allow for
code reuse:**
::
def is_prime( x ):
"""Determine if a number is prime, return Boolean"""
prime_flag = True
if x == 1:
prime_flag = False
else:
for i in range( 2, x ):
if x % i == 0:
prime_flag = False
break
return prime_flag
# invoke function:
print is_prime( 5 ) # True
print is_prime( 4 ) # False
This function saves us a lot of typing and enables the ability to
quickly determine if a given number is prime.
**Next we will use a python dictionary to implement a result cache.**
Also by circumstance we introduce objects and classes.
::
class Primer( object ):
def __init__( self ):
"""create a cache dictionary"""
self.cache = {}
def is_prime( self, x ):
"""Determine if x is prime, cache and return result"""
if x in self.cache:
return self.cache[x] # lookup result
prime_flag = True
if x == 1:
prime_flag = False
else:
for i in range( 2, x ):
if x % i == 0:
prime_flag = False
break
self.cache[x] = prime_flag # cache result
return prime_flag
p = Primer() # create a new primer object
p.is_prime( 5 ) # True
p.is_prime( 4 ) # False
p.is_prime( 5 ) # True and fetched from cache
What is great about this solution is that we can avoid looping and
computation if an answer is already in cache. Looking up a cached result
is much more efficient and will ultimately make a program feel more
responsive.
**Determining if 97352613 is prime takes my laptop nearly 18 seconds.**
Fetching the cached result seems to happen instantly.
::
>>> s1 = time();p.is_prime( 97352613 );e1 = time()
False # not prime
>>> s2 = time();p.is_prime( 97352613 );e2 = time()
False # not prime from cache
>>> e1 - s1
17.970067977905273 # seconds
>>> e2 - s2
2.5987625122070312e-05 # or approx .000026 seconds
A look-up will always beat a computation. Anything that can be cached,
should be cached. I hope this helps clear things up.
|2013-03-03-explaining-cache-scaled|
.. |2013-03-03-explaining-cache-scaled| image:: /uploads/2013/03/2013-03-03-explaining-cache-scaled.png

View file

@ -0,0 +1,17 @@
how to drain an iPhone battery without needing passcode
#######################################################
:date: 2012-11-03 19:27
:author: Russell Ballestrini
:tags: Guide, Security
:slug: how-to-drain-an-iphone-battery-without-needing-passcode
:status: published
#. Press home button [ ]
#. Slide camera button up
#. Slide mode to video
#. Turn on flash
#. Put iPhone on table with light pointed down
#. Walk away inconspicuously
Extra points if you set the camera to record (just don't record yourself
or sounds) which will fill up the iPhone capacity.

View file

@ -0,0 +1,118 @@
Tips for getting pull requests approved
#######################################
:date: 2012-12-12 12:50
:author: Russell Ballestrini
:tags: Code, Greatest Hits, Opinion
:slug: tips-for-getting-pull-requests-approved
:status: published
**Pull rejection sucks!**
You have just coded, implemented, and submitted a pull request. A short
while later the request is declined by an upstream maintainer and you
feel crushed. We have all been there. Today I'm going to show you a
better way. This article will teach you how to create pull requests that
get approved.
**Start small**
You need to earn trust with the maintainers. Your first commit should be
a small change which they *cannot* reject. Try to write a missing test
or re-factor duplicate code. Correct a comment's accuracy or rename a
variable to better reflect its purpose. Your first pull request should
not alter how the program works.
|start-small.xcf|
**Don't add leading or trailing white space**
Additional whitespace will alter the diff output. This causes version
control systems to flag lines as changed which is irritating and
sometimes misleading.
|spot-the-diff.xcf|
**Less is more**
Minimize the number of changes to accomplish your goal. People are busy
and at times lazy. Reduce the work the maintainers must do to perform a
merge. Lowering the amount of lines to review should increase the chance
of approval.
|less-is-more.xcf|
**Only commit working code**
Do not break the program, only commit working code. If the project has
tests make sure they work before you commit.
|commit-working-code.xcf|
**Follow the leader**
Try to mimic the maintainers. Follow the coding style and project layout
even if it seems wrong. This is not your playground yet. Before you
commit, review the VCS logs to learn how verbose or terse your commit
messages should be. When in Rome do as the romans do. This silly game of
follow the leader reduces friction of an outsider committing to the
project.
|follow-the-leader.xcf|
**Comments, docs, and tests oh my!**
Your first pull request should clarify or add to the existing
documentation. Fix the README, adjust a comment, or write a test. These
tasks might appear small but they serve to prove that you possess
comprehension of the source code. They also do not alter the program's
functionality. Having a few of these pull requests under-your-belt will
earn you trust which will eventuality translate to more responsibly in
the future. You will also differentiate yourself from the rest because
most people do not enjoy working on documentation.
|comments-docs-tests-oh-my.xcf|
**Blog about the change**
Write about the change, give reasons and examples. Include a link to
your blog post in the pull request.
|ideas-blog-get-heard.xcf|
**Pull requests are like paragraphs**
If you were writing an essay, you would split up your ideas into
separate paragraphs. A pull request has many qualities similar to a
paragraph. Each commit should be related to the pull request's main
objective. Commits of a pull request should stay focused and on topic.
| A pull request is to a program as a paragraph is to an essay.
| A commit is to a pull request as a sentence is to a paragraph.
In an essay, if you have more then one topic you should have more then
one paragraph. Likewise when coding, only one idea or change per pull
request.
Separating your ideas into different pull requests will grant the
maintainers greater flexibility when they begin to integrate. They will
have the ability to pick-and-choose which requests to merge and
everybody wins!
|pull-request-stay-focused.xcf|
.. |start-small.xcf| image:: /uploads/2012/12/start-small.xcf_.png
:target: /uploads/2012/12/start-small.xcf_.png
.. |spot-the-diff.xcf| image:: /uploads/2012/12/spot-the-diff.xcf_.png
:target: /uploads/2012/12/spot-the-diff.xcf_.png
.. |less-is-more.xcf| image:: /uploads/2012/12/less-is-more.xcf_.png
:target: /uploads/2012/12/less-is-more.xcf_.png
.. |commit-working-code.xcf| image:: /uploads/2012/12/commit-working-code.xcf_.png
:target: /uploads/2012/12/commit-working-code.xcf_.png
.. |follow-the-leader.xcf| image:: /uploads/2012/12/follow-the-leader.xcf_.png
:target: /uploads/2012/12/follow-the-leader.xcf_.png
.. |comments-docs-tests-oh-my.xcf| image:: /uploads/2012/12/comments-docs-tests-oh-my.xcf_.png
:target: /uploads/2012/12/comments-docs-tests-oh-my.xcf_.png
.. |ideas-blog-get-heard.xcf| image:: /uploads/2012/12/ideas-blog-get-heard.xcf_.png
:target: /uploads/2012/12/ideas-blog-get-heard.xcf_.png
.. |pull-request-stay-focused.xcf| image:: /uploads/2012/12/pull-request-stay-focused.xcf_.png
:target: /uploads/2012/12/pull-request-stay-focused.xcf_.png

View file

@ -0,0 +1,30 @@
ago.py human readable timedelta 0.0.4 release
#############################################
:date: 2013-01-09 00:04
:author: Russell Ballestrini
:tags: Code
:slug: ago-py-human-readable-timedelta0-0-4-release
:status: published
|
| **We have released ago.py 0.0.4**
| pypi: http://pypi.python.org/pypi/ago
| repo: https://bitbucket.org/russellballestrini/ago
Special thanks to David Beitey for supplying `ideas and python
code <http://davidjb.com/>`__ for this update!
All changes are backward compatible.
Change log:
- added support for future dates
- added optional past\_tense keyword argument string for custom output
- added optional future\_tense keyword argument string for custom
output
- added 13 tests to help prevent regressions
- Updated the README.rst documentation for better coverage
We were just shy of 800 downloads on pypi for ago-0.0.3, I hope
ago-0.0.4 will perform even better!

View file

@ -0,0 +1,90 @@
How to overload default function arguments in python using lambda
#################################################################
:date: 2013-01-12 11:22
:author: Russell Ballestrini
:tags: Code, Greatest Hits, Guide
:slug: how-to-overload-default-function-arguments-in-python-using-lambda
:status: published
Python Lambda functions are very powerful but I often forget how they
work or the fun things they do. This post will document how to use a
lambda to provide different default arguments to a function.
We will use the `human function found in
ago.py <https://bitbucket.org/russellballestrini/ago/overview>`__ as an
example - because I'm the module author and I really like it. Lets use
the interactive python interpreter to run help on the human function.
::
>>> from ago import human
>>> help( human )
Help on function human in module ago:
human(dt, precision=2, past_tense='{} ago', future_tense='in {}')
Accept a datetime or timedelta, return a human readable delta string
.. raw:: html
</p>
Shown above the human function accepts 1 argument and 3 named keyword
arguments. The dt argument must be a datetime or timedelta object, the
precision must be an integer, and the other two must be strings. If we
didn't like the default arguments, we would need to specify (or pass in)
new values each time we invoked the function.
Example: ``human(dt, 3, 'this happened {} ago!', 'in {} from now!')``.
If we know we will always want different default arguments we can create
a lambda function to shorten the invocation length.
::
>>> h = lambda dt : human(dt, 3, 'this happened {} ago!', 'in {} from now!')
>>> print h( dt ) # h is much shorter then human, and still reusable!
| Above creates a new function h who only accepts one argument dt. This
function calls human with our default arguments.
| This lambda is equivalent to this regular python function:
::
>>> def h( dt ):
... return human(d, 3, 'this happened {} ago!', 'in {} from now!')
>>> print h( dt ) # h is much shorter then human, and still reusable!
Here is a working example to show the new lambda function h in action:
::
>>> from datetime import datetime
>>> from datetime import timedelta
>>> from ago import human
>>>
>>> h = lambda dt : human(dt, 3, 'this happened {} ago!', 'in {} from now!')
>>>
>>> present = datetime.now()
>>>
>>> for i in range( 1, 15 ):
... if i % 2 == 0:
... new_date = present - timedelta( i, i * i, i * i * i )
... else:
... new_date = present + timedelta( i, i * i, i * i * i )
... h( new_date )
...
'in 22 hours, 9 minutes, 30 seconds from now!'
'this happened 2 days, 1 hour, 50 minutes ago!'
'in 2 days, 22 hours, 9 minutes from now!'
'this happened 4 days, 1 hour, 50 minutes ago!'
'in 4 days, 22 hours, 9 minutes from now!'
'this happened 6 days, 1 hour, 51 minutes ago!'
'in 6 days, 22 hours, 10 minutes from now!'
'this happened 8 days, 1 hour, 51 minutes ago!'
'in 8 days, 22 hours, 10 minutes from now!'
'this happened 10 days, 1 hour, 52 minutes ago!'
'in 10 days, 22 hours, 11 minutes from now!'
'this happened 12 days, 1 hour, 52 minutes ago!'
'in 12 days, 22 hours, 12 minutes from now!'
'this happened 14 days, 1 hour, 53 minutes ago!'

View file

@ -0,0 +1,67 @@
virt-back's Domfetcher class returns doms from libvirt API
##########################################################
:date: 2013-03-02 15:04
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: virt-backs-domfetcher-class-returns-doms-from-libvirt-api
:status: published
:summary:
|2013-03-02-scale-virt-back-domfetcher-scaled|
**I'm hooked...**
I attended SCaLE 11x, my first technical conference, and had an amazing
time. My favorite talk was Michael Day's "`Advancements with Open
Virtualization &
KVM <http://code.ncultra.org/2013/02/scale-11x-open-virtualization/>`__\ "
(link to slides). Michael's presentation inspired me to continue my work
on virt-back.
During my trip home I used the in-flight wifi to push this
`commit <https://bitbucket.org/russellballestrini/virt-back/commits/d6dff27323650bf784cc284f676299ffe07953cb>`__
into the cloud from the clouds! This particular commit re-factored the
dom object list generation into a simple-to-use class called Domfetcher.
Domfetcher abstracts the libvirt API and grants access to the following
helper methods:
| **get\_all\_doms( )**
| Return a list of all dom objects
| **get\_doms\_by\_names( guest\_names=[] )**
| Accept a list of guest\_names, return a list of related dom objects
| **get\_running\_doms( )**
| Return a list of running dom objects
| **get\_shutoff\_doms( )**
| Return a list of shutoff but defined dom objects
**This is an example of how to use Domfetcher:**
::
import virtback
# optionally supply hypervisor uri
domfetcher = virtback.Domfetcher()
doms = domfetcher.get_running_doms()
for dom in doms:
print dom.name()
for dom in doms:
print dom.info()
for dom in doms:
print dom.shutdown()
.. raw:: html
</p>
As always thanks for reading!
.. |2013-03-02-scale-virt-back-domfetcher-scaled| image:: /uploads/2013/03/2013-03-02-scale-virt-back-domfetcher-scaled.png
:target: /uploads/2013/03/2013-03-02-scale-virt-back-domfetcher-scaled.png

View file

@ -0,0 +1,36 @@
Guido name dropped tornado python tulip and pep-3156
#####################################################
:date: 2013-03-22 22:07
:author: Russell Ballestrini
:tags: Uncategorized
:slug: guido-name-dropped-tornado-python-tulip-and-pep-3156
:status: published
Pycon 2013 was excellent, in fact it was my first one I have attended.
I found it odd that django and Pyramid had plenty of talks but nobody
mentioned tornado.
The only person that brought up tornado was Guido himself, who has been
researching and developing async python since December 12th, 2012. Guido
wants to add async API libraries to python core, and has been comparing
his work to twisted and more importantly tornado. He is leveraging the
existing solutions to stay on the correct track.
Async API's in Python core! This news was extra exciting for me, because
I have already learned the power of async by messing around with
tornado. Since the talk many people have approached me and asked for
more information about async API's.
My favorite use for async is long polling for web applications. Here is
a diagram that shows how great tornado is:
|2013-03-22-tornado-callback-long-polling-event-loop-scaled|
This type of polling can support thousands of concurrent users, all
connected and waiting for a callback event to occur. I used tornado to
build `four2go <http://four2go.gumyum.com>`__, a real-time and
multi-player browser-based-game.
.. |2013-03-22-tornado-callback-long-polling-event-loop-scaled| image:: /uploads/2013/03/2013-03-22-tornado-callback-long-polling-event-loop-scaled.png
:target: /uploads/2013/03/2013-03-22-tornado-callback-long-polling-event-loop-scaled.png

View file

@ -0,0 +1,40 @@
Automatic event hangout with cron
#################################
:date: 2013-04-09 09:17
:author: Russell Ballestrini
:tags: Guide
:slug: automatic-event-hangout-with-cron
:status: published
**Create an online only, hangout event**
Create a new event with a date far into the future, like the year 2015.
Go to the event's options > advanced and enable 'this event is online
only' which will create a unique Hangout URI.
**Create a cronjob**
Create a cronjob on each device to open the web browser Monday-Friday at
12:49pm and open the unique Hangout URI.
Firefox cron example:
::
# Run Firefox at 12:49 EST each weekday and open hangout URI
49 12 * * 1-5 export DISPLAY=:0 && /usr/bin/firefox 'hangout-uri'
.. raw:: html
</p>
Chromium-browser cron example:
::
# Run Chromium-browser at 12:49 EST each weekday and open hangout URI
49 12 * * 1-5 export DISPLAY=:0 && /usr/bin/chromium-browser 'hangout-uri'
.. raw:: html
</p>

View file

@ -0,0 +1,13 @@
Survey Baby Monkey
###################
:date: 2013-04-26 13:47
:author: Russell Ballestrini
:tags: Art
:slug: survey-baby-monkey
:status: published
:summary:
baby monkey fxhp cartoon
|baby-monkey-fxhp|
.. |baby-monkey-fxhp| image:: /uploads/2013/04/baby-monkey-fxhp.jpg

View file

@ -0,0 +1,53 @@
Honey! I just DELETED LinkPeek.com
###################################
:date: 2013-05-26 13:19
:author: Russell Ballestrini
:tags: DevOps, LinkPeek, Opinion
:slug: honey-i-just-deleted-linkpeek-com
:status: published
During the day I am an ops sys-admin. During the night I am a husband,
father of two, and a CEO of a bootstrapped start-up. After launch, my
first project was to schedule regular backups of user data and archive
off-site. My goal was to `create backups but never need
them <http://russell.ballestrini.net/virt-back-restoring-from-backups/>`__.
Boy was I lucky ...
Yes, leave it to me to inadvertently delete the VPS root disk. One of
the major cloud providers places the "rename" and "remove" disk buttons
right next to each other and I learned a nasty habit of clearing pop-ups
without reading them (thanks Windows).
"Honey! I just deleted LinkPeek.com"
The horror... My stomach felt like I took a tumble in a roller-coaster.
Instantly I tossed off my developer hat and put on my operations hat. I
checked the off-site backups. I had nightly dumps of MongoDB and weekly
tar backups of the /etc partition. The user data was in MongoDB and most
of the system configuration information was in the tar. I used the tar
to recover 2 upstart scripts, 2 supervisord scripts, 2 complex nginx
confs, an ssl cert, and the pyramid production.ini.
I set out to stand up a new server, re-install the needed packages,
recover the user data, and restore the service. After 1.5 hours of
feverish typing, https://linkpeek.com/ was back online.
**What I learned and my plan going forward**
If you are a small team or a start-up, you must have somebody dedicated
to operations. Without backups I would not have been able to gracefully
recover. Most likely I would have reimbursed the existing members and
shuttered the doors.
This experience was eye-opening. In my next couple of posts I will
explain how I `create and maintain backups </automatic-backups/>`__ and
my next project will implement a configuration management and
provisioning system.
This system will allow me to:
#. take out the human element of recovery
#. significantly reduce the time-to-recover from a catastrophic failure
#. test disaster recovery procedures before needing them
#. provision development and production environments without effort
#. have a reproducible blueprint of "how to build a LinkPeek server"

View file

@ -0,0 +1,25 @@
High load and CPU usage craftbukkit compared to vanilla minecraft
#################################################################
:date: 2013-06-20 23:35
:author: Russell Ballestrini
:tags: Opinion, Project
:slug: high-load-and-cpu-usage-craftbukkit-compared-to-vanilla-minecraft
:status: published
I started researching the best ways to use `salt to provision minecraft
servers <http://bobbylikeslinux.net/salt-minecraft-fun.html>`__. I wrote
a salt state formula for the vanilla minecraft server deployment. The
deployment worked out great so I decided to try my luck with plugins.
In order to use plugins and mods we need to use a customized server
package. I decided to try provisioning a craftbukkit server and quickly
noticed something was very wrong. Vanilla Craftbukkit (with no plugins)
produces very high load and CPU usage.
Here is a chart for proof. The spike is when I stopped the vanilla
minecraft server and started the craftbukkit minecraft server:
|minecraft-vs-craftbukkit|
.. |minecraft-vs-craftbukkit| image:: /uploads/2013/06/minecraft-vs-craftbukkit.png
:target: /uploads/2013/06/minecraft-vs-craftbukkit.png

View file

@ -0,0 +1,69 @@
Add a custom header to all Salt managed files using pillar and jinja templates
##############################################################################
:date: 2013-07-01 14:11
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: add-a-custom-header-to-all-salt-managed-files-using-pillar-and-jinja-templates
:status: published
Salt-stack (salt) provides a solution for centralized configuration
management and remote execution. One of the most basic things Salt
provides is the ability to manage the contents of a file or a directory
of files. Using Salt we can dictate the state of our minions and as a
result we also gain auto-healing of configuration files.
Salt will clobber local changes to managed files and force the state to
reflect the version in configuration management. In an effort to avoid
confusing the uninformed, I place a header on each managed file which
announces "THIS FILE IS MANAGED BY SALT".
To avoid repeating myself in each managed file, I came up with the
following centralized solution -
First, I give all minions access to the headers pillar tree in top.sls:
::
base:
'*':
- headers
Next, I create a couple headers in in headers/init.sls:
::
headers:
salt:
file: |
################################
# THIS FILE IS MANAGED BY SALT #
################################
directory: |
#####################################
# THIS DIRECTORY IS MANAGED BY SALT #
#####################################
Then, I parse each managed file in the state tree with jinja, for
example hosts/init.sls:
::
/etc/hosts:
file.managed:
source: salt://hosts/hosts
user: root
group: group
mode: 644
template: jinja
Finally, in each file served by salt I add the jinja header
substitution, for example hosts/hosts:
::
{{pillar['headers']['salt']['file']}}
127.0.0.1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
I use this same technique to declare most configuration options as
parameters, like hostnames, IP addresses, ports, and more.

View file

@ -0,0 +1,125 @@
Create your own fleet of servers with Digital Ocean and salt-cloud
##################################################################
:date: 2013-07-02 22:20
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: create-your-own-fleet-of-servers-with-digital-ocean-and-salt-cloud
:status: published
Have you heard about Digital Ocean? They offer a polished user
interface, KVM guests with SSD storage, and an API to interact with a
cloud of hypervisors. API integration got you down? Don't worry,
salt-cloud has already integrated Digital Ocean among it's list of
providers! The rest of this post illustrates the steps I took to
configure salt-cloud to work with Digital Ocean.
This guide assumes the following: You already have a salt-master. You
already have a `Digital
Ocean <https://www.digitalocean.com/?refcode=27e015299dc7%20>`__
account.
**Step one,** install the most recent version of salt-cloud
(salt-cloud==2015.5.0) on the salt-master:
::
sudo apt-get install salt-cloud
# or if you prefer ...
pip install salt-cloud==2015.5.0
# last verify it was successfully installed
salt-cloud --version
.. raw:: html
</p>
**Step two,** configure salt-cloud. Salt-cloud uses the following files
YAML files for configuration:
| */etc/salt/cloud.conf.d/main.conf*
|     This is the main configuration file. I have the following
statements:
::
minion:
master: master.foxhop.net
append_domain: foxhop.net
| */etc/salt/cloud.providers/do.conf*
|     This is a provider configuration file for Digital Ocean (do).
Collect your client\_key and personal\_access\_token (api\_key) from
the Digital Ocean user dashboard. Also create an SSH key and add the
public key using the dashboard:
::
# For Digital Ocean
do:
provider: digital_ocean
client_key: MyClientKeyLiftedFromDashboard
personal_access_token: MyAPIKeyLiftedFromDashboard
ssh_key_file: /keys/digital-ocean-salt-cloud
ssh_key_name: digital-ocean-salt-cloud.pub
| */etc/salt/cloud.profiles/do.conf*
|     This is the Digital Ocean profiles configuration file. We will
create just two profiles for now, but you can create unlimited named
combinations.
::
ubuntu-12-04-do-512:
provider: do
image: ubuntu-12-04-x64
size: 512mb
location: nyc1
ubuntu-14-04-do-512:
provider: do
image: ubuntu-14-04-x64
size: 512mb
location: nyc1
| *ssh\_key\_file*
|     This is your private SSH key located on your salt-master
| *ssh\_key\_name*
|     This is the name of the public key you added in your Digital
Ocean dashboard
| *size*
|     The size or plan you would like to provision,
| 512mb is the smallest plan
| *location*
|     The geographical region, location, and/or data center
| *image*
|     The operating system image
After you configure the do provider in /etc/salt/cloud.providers you
gain access to the following commands:
::
salt-cloud --list-sizes do
salt-cloud --list-locations do
salt-cloud --list-images do
salt-cloud --help
.. raw:: html
</p>
**Lets provision a new cloud server!**
::
salt-cloud --profile ubuntu-14-04-do-512 deejay
If all goes well you should have a newly provisioned server bootstrapped
with salt-minion. The new minion's keys are already added to the
salt-master. Now you just need to run highstate!

View file

@ -0,0 +1,100 @@
Understanding Salt Stack user and group management
##################################################
:date: 2013-07-08 13:51
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: understanding-salt-stack-user-and-group-management
:status: published
This state will create a user:
::
russell:
user:
- present
This state will create a user and a group. This also makes the user part
of the group, and handles creating the group first:
::
russell:
group:
- present
user:
- present
- groups:
- russell
- require:
- group: russell
This state handles user and group generation along with password and
ssh-key maintenance. This is all done securely using pillar to
parameterize arguments:
::
# This state will create users accounts
#
# This state requires a pillar named 'users' with data formatted like:
#
# users:
#
# tusername:
# fullname: Test Username
# uid: 1007
# gid: 1007
# groups:
# - sudo
# - ops
# crypt: $password-hash-sha512-prefered
# pub_ssh_keys:
# - ssh-rsa list-of-public-keys tusername-sm
#
# anotheruser: ... snipped ...
# loop over all users presented by pillar:
# create user's group, create user, then add pub keys
{% for username, details in pillar.get('users', {}).items() %}
{{ username }}:
group:
- present
- name: {{ username }}
- gid: {{ details.get('gid', '') }}
user:
- present
- fullname: {{ details.get('fullname','') }}
- name: {{ username }}
- shell: /bin/bash
- home: /home/{{ username }}
- uid: {{ details.get('uid', '') }}
- gid: {{ details.get('gid', '') }}
- crypt: {{ details.get('crypt','') }}
{% if 'groups' in details %}
- groups:
{% for group in details.get('groups', []) %}
- {{ group }}
{% endfor %}
- require:
{% for group in details.get('groups', []) %}
- group: {{ group }}
{% endfor %}
{% endif %}
{% if 'pub_ssh_keys' in details %}
ssh_auth:
- present
- user: {{ username }}
- names:
{% for pub_ssh_key in details.get('pub_ssh_keys', []) %}
- {{ pub_ssh_key }}
{% endfor %}
- require:
- user: {{ username }}
{% endif %}
{% endfor %}

View file

@ -0,0 +1,74 @@
Configuration Management vs Remote Execution
############################################
:date: 2013-07-11 23:15
:author: Russell Ballestrini
:tags: DevOps, Greatest Hits, Opinion
:slug: configuration-management-vs-remote-execution
:status: published
:summary:
It's about time to learn the difference.
|config-mangement-vs-remote-execution|
|
**What is configuration management?**
In a perfect world configuration management provides a centralized,
revision controlled, self-documented, change management location for
manifests and formulas which both define how to build a complete system
and organize a means of knowledge transfer. An infrastructure perfectly
described in configuration management allows any single part of the
system to be created, reproduced, multiplied, self-healed or even
re-purposed.
**When should I use configuration management?**
Use configuration management whenever you intend to permanently alter
system state or infrastructure data.
*System state - the current state of the system:*
- operating system (distro,kernel,patches,hot-fixes)
- software installed (base,role-specific)
- services running (base,role-specific)
- user access
- etc
*Infrastructure data - the information that describes the
infrastructure:*
- asset information (models,specs,IP,DNS,rack-elevation,etc)
- roles (app,web,db,proxy,load-balancer,etc)
- current allocations (allocated,unallocated,number of servers in each
role,etc)
- etc
**What is remote execution?**
Remote execution is the act of issuing commands to one or more remote
systems. The most popular remote execution system in use today is SSH.
SSH is great for maintaining a small group of dissimilar systems. Once
the system count grows and similar roles present themselves, start
looking at something like Fabric. Fabric is a python framework which
builds on top of the SSH protocol and makes it possible to invoke the
same command on hundreds of servers sequentially. Once the fleet count
reaches thousands of servers and commands must run in parallel, look at
Salt-stack's remote execution layer written with python and ZeroMQ.
**When should I use remote execution?**
Only use remote execution for ad hoc reports, data collection, or for
temporary tests which have no expectation of persistence after research
is complete. Frequent data collection jobs work best when implemented in
a metrics collection or monitoring system. (I'll save that for another
post)
**When should I not use remote execution?**
Do not use remote execution to change the state of the system or the
data which describes the infrastructure! If a remote execution job
changes either state or data it should be placed into the configuration
management for the reasons mentioned above.
.. |config-mangement-vs-remote-execution| image:: /uploads/2013/07/config-mangement-vs-remote-execution.png

View file

@ -0,0 +1,80 @@
Control a MongoDB collection in configuration management
########################################################
:date: 2013-10-14 21:39
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: control-a-mongodb-collection-in-configuration-management
:status: published
This post explains how to use configuration management (Salt Stack) to
completely control a MongoDB collection. In our example we want to
control a store's collection of plans.
First we create a JSON representation of the collection.
**mongodb/plan.json:**
::
{
"_id" : { "$oid" : "4ef8b9e2be329f491d98f74b" },
"cost" : 20, "description" : "development",
"name" : "good", "count" : 6000
}
{
"_id" : { "$oid" : "4ef8b9e8be329f491d98f74c" },
"cost" : 60, "description" : "freelancers",
"name" : "better", "count" : 36000
}
{
"_id" : { "$oid" : "4ef8b9f0be329f491d98f74d" },
"cost" : 180, "description" : "production",
"name" : "best", "count" : 162000
}
.. raw:: html
</p>
Next we configure a salt state formula to manage the JSON file and watch
it for changes.
**mongodb/init.sls:**
::
# install mongodb server
mongodb-server:
pkg:
- installed
# manage the store's plan.json
/tmp/plan.json:
file.managed:
- source: salt://mongodb/plan.json
- user: root
- group: root
- mode: 644
# import the plan collection if it changes
import-plan-collection:
cmd.wait:
- name: mongoimport --db=store --collection=plan --upsert /tmp/plan.json
- require:
- pkg: mongodb-server
- watch:
- file: /tmp/plan.json
.. raw:: html
</p>
*Now whenever plan.json is altered in configuration management, the file
on the minion will update which will trigger a mongoimport with upsert
to occur.*
Optionally, we could replace ``--upsert`` with ``--drop`` which will
drop the collection before re-importing thus removing stale records.
We now have a version controlled JSON file in configuration management
and the power of MongoDB Document Objects in our application code!

View file

@ -0,0 +1,180 @@
Postfix Salt State Formula
##########################
:date: 2013-10-17 21:28
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: postfix-salt-state-formula
:status: published
:summary:
|postfix-config-management|
The following formula was tested on Ubuntu and Debian however it would
not take much work to test on other operating systems.
This state formula will install postfix and mutt. The postfix service
will watch various configuration files for changes and restart
accordingly.
This formula will also manage and watch the /etc/aliases file and invoke
the *newaliases* command to initialize or re-initialize the alias
database. This formula will also manage and watch the
/etc/postfix/virtual file and invoke the *postmap* command to create or
update a managed Postfix lookup table.
**postfix/init.sls:**
::
# Install mutt and postfix mutt packages.
#
# This formula supports setting an optional:
#
# * 'aliases' file
# * 'virtual' map file
#
# Both aliases and virtual use a pillar data schema
# which takes the following form:
#
# postfix:
# aliases: |
# postmaster: root
# root: testuser
# testuser: russell@example.com
# virtual: |
# example.com this is a comment
# test1@example.com me@example.com
# test2@example.com me@example.com
#
# install mutt
mutt:
pkg:
- installed
# install postfix have service watch main.cf
postfix:
pkg:
- installed
service:
- running
- enable: True
- watch:
- pkg: postfix
- file: /etc/postfix/main.cf
# postfix main configuration file
/etc/postfix/main.cf:
file.managed:
- source: salt://postfix/main.cf
- user: root
- group: root
- mode: 644
- template: jinja
- require:
- pkg: postfix
# manage /etc/aliases if data found in pillar
{% if 'aliases' in pillar.get('postfix', '') %}
/etc/aliases:
file.managed:
- source: salt://postfix/aliases
- user: root
- group: root
- mode: 644
- template: jinja
- require:
- pkg: postfix
run-newaliases:
cmd.wait:
- name: newaliases
- cwd: /
- watch:
- file: /etc/aliases
{% endif %}
# manage /etc/postfix/virtual if data found in pillar
{% if 'virtual' in pillar.get('postfix', '') %}
/etc/postfix/virtual:
file.managed:
- source: salt://postfix/virtual
- user: root
- group: root
- mode: 644
- template: jinja
- require:
- pkg: postfix
run-postmap:
cmd.wait:
- name: /usr/sbin/postmap /etc/postfix/virtual
- cwd: /
- watch:
- file: /etc/postfix/virtual
{% endif %}
**postfix/aliases:**
::
# Managed by config management
# See man 5 aliases for format
{{pillar['postfix']['aliases']}}
**postfix/virtual:**
::
# Managed by config management
{{pillar['postfix']['virtual']}}
**postfix/main.cf:**
::
# Managed by config management
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
readme_directory = no
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
myhostname = {{ grains['fqdn'] }}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = {{ grains['fqdn'] }}, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
{% if 'virtual' in pillar.get('postfix','') %}
virtual_alias_maps = hash:/etc/postfix/virtual
{% endif %}
.. |postfix-config-management| image:: /uploads/2013/10/mysza.gif

View file

@ -0,0 +1,77 @@
Simplify deployments with Upstart and uWSGI
###########################################
:date: 2013-10-19 22:35
:author: Russell Ballestrini
:tags: DevOps, Opinion
:slug: simplify-deployments-with-upstart-and-uwsgi
:status: published
As you know from my previous post, I recently `deleted
LinkPeek.com <http://russell.ballestrini.net/honey-i-just-deleted-linkpeek-com/>`__
and after struggling to get it back online, I vowed to start utilizing
configuration management. During this exercise, I noticed that the
architecture I use in production seems overly complicated.
**The current production deployment stack:**
- Nginx listen on 80/443 proxy upstream 9901/9902
- Upstart => Supervisord => Cherrypy/Paste listen on 9901/9902
*Each of these services and processes have their own configuration files
which must work together. Upstart needs to know the location of
Supervisord's configuration files. Supervisord needs to know the
location of Cherrypy/PasteDeploy's configuration files. Supervisord also
must bring up a specified number of worker processes who listen on a
pool of ports. Nginx needs to proxy upstream to that pool of worker
ports.*
This architecture seems difficult to automate because of the numerous
places errors may sneak in. I started researching an alternative
architecture and stumbled upon Nicholas Piel's `Benchmark of Python Web
Servers <http://nichol.as/benchmark-of-python-web-servers>`__. The
graphs Nicholas compiled allowed me to narrow down a list of potential
replacements.
I chose to review uWSGI first because it was a top contender on every
chart. After briefly testing uWSGI, I halted my explorations and
selected it as the winner. I know you probably feel that was a premature
decision but uWSGI fit what I was looking for.
**The new simplified stack:**
- Nginx listen on 80/443 proxy upstream 5200
- Upstart => uWSGI listen on 5200
The uWSGI server possesses great performance out-of-the-box but also
presents many options for fine tuning. These options may be specified
either directly in the Upstart script using flags or in a configuration
file (like a Pyramid .ini). Either way, the stack uses less files, less
processes, and less complexity then the original architecture. For
`LinkPeek <https://linkpeek.com>`__ deployments I decided to place all
uWSGI related configuration directly in the Upstart script which I
embedded below:
**linkpeek/weblinkpeek.conf \| Upstart for starting uWSGI**
::
description "Start the uWSGI master for the LinkPeek WEB"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
# run service as linkpeek user
setuid linkpeek
setgid linkpeek
# uWSGI command to execute and treat as a daemon
exec /path/to/linkpeek/web/env/bin/uwsgi --master --processes=4 --http=127.0.0.1:5200 --die-on-term --logto2=/path/to/linkpeek/web/uwsgi.log --virtualenv=/path/to/linkpeek/web/env --ini-paste=/path/to/linkpeek/web/linkpeek/production.ini
.. raw:: html
</p>
The web application is now daemonized and listening on port 5200 with 4
workers. I leave implementing the Nginx front end to the reader.

View file

@ -0,0 +1,90 @@
Backup all virtual machines on a SmartOS hypervisor with smart-back.sh
######################################################################
:date: 2013-10-27 20:45
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: backup-all-virtual-machines-on-a-smartos-hypervisor-with-smart-back-sh
:status: published
This post will explain how to create a cronjob to backup of every
virtual machine on a SmartOS hypervisor.
**Create** the following bash script in /opt/smart-back.sh:
::
#!/usr/bin/bash
# Backup all virtual machines on a SmartOS hypervisor
# Author: russell@ballestrini.net
# Website: http://russell.ballestrini.net/
# Backup directory without trailing slash
backupdir=/opt/backups
# temp dir where we ZFS send and gzip before moving to backupdir
tmpdir=/opt
svcadm enable autofs
for VM in `vmadm list -p -o alias,uuid`
do
# create an array called VM_PARTS splitting on ':'
IFS=':' VM_PARTS=($VM)
# create some helper varibles for alias and uuid
alias=${VM_PARTS[0]}
uuid=${VM_PARTS[1]}
# echo "Backup started for $VM"
vmadm send $uuid > $tmpdir/$alias
# echo "Starting $VM"
vmadm start $uuid
gzip $tmpdir/$alias
mv $tmpdir/$alias.gz $backupdir/$alias.gz
done
.. raw:: html
</p>
**Create** a cronjob entry to schedule the backups:
::
crontab -e
::
2 15 * * * /usr/bin/bash /opt/smart-back.sh
.. raw:: html
</p>
If I expand on this script much more, I plan to stick it into revision
control.
If you look closely, I have also added a hack to enable autofs (svcadm
enable autofs) which lets me automount an NFS share on my remote FreeNAS
by setting
``backupdir=/net/[ip-or-fqdn-of-freenas]/mnt/zfs-mirror/backup/vms``.
We have scheduled a backup of each virtual machine on your SmartOS
hypervisor!
If or when the time comes to restore a VM from a backup, use the
following:
::
vmadm receive -f /path/to/backup-file
Just make sure the VM doesn't currently exist on the hypervisor.
This strategy is great for complete backups of machines which could be
used during a manual migration, or if corruption happened to the VM and
we wanted to restore to a previous version.

View file

@ -0,0 +1,490 @@
Hackathon 2013 Virtualization
#############################
:date: 2013-12-13 20:56
:author: Russell Ballestrini
:tags: Project
:slug: hackathon-2013-virtualization
:status: published
As a warning before we dive into things, this post is less of a formal
publication and more of a stream of conscience.
My employer `newcars.com <http://newcars.com/jobs/>`__ has allowed the
technical staff to host hackathon! Over the past couple weeks I have had
quite a few ideas tumbling around in my head:
- Standup a central logging server
- Standup Sensu for monitoring
- Bake-off and document some KVM virtualization hypervisors (Ubuntu or
SmartOS)
- Test Docker and document findings
Ultimately I have chosen to dedicate my time to testing out
virtualization on the new Cisco UCS Blade servers. I plan to test Ubuntu
KVM first.
**KVM (Ubuntu 12.04)**
I decided to use a Salt-stack configuration management formulas to
document how I transformed ``kvmtest02`` (a regular Ubuntu 12.04 server)
into a KVM hypervisor. I use ``kvmtest0a`` and ``kvmtest02b`` when
refering to the virtual machines living on the hypervisor. Here is the
formula:
**kvm/init.sls:**
::
# https://help.ubuntu.com/community/KVM
# Ubuntu server a KVM - Kernel Virtual Machine hypervisor
# the official ubuntu document suggests we install the following
kvm-hypervisor:
pkg.installed:
- names:
- qemu-kvm
- libvirt-bin
- ubuntu-vm-builder
- bridge-utils
# we need to make all of our ops people part of the libvirtd group
# so that they may create and manipulate guests. we skip this for now
# and assume all virtual machines will be owned by root.
# these are optional packages which gives us a GUI for the hypervisor
virt-manager-and-viewer:
pkg.installed:
- names:
- virt-manager
- virt-viewer
- require:
- pkg: kvm-hypervisor
# create a directory to hold virtual machine image files
kvm-image-dir:
file.directory:
- name: /cars/vms
- user: root
- group: root
- mode: 775
.. raw:: html
</p>
I used the following to install the formula to the test hypervisor:
::
salt 'kvmtest02.example.com' state.highstate
.. raw:: html
</p>
The salt highstate reported everything was good (green), so I moved on
to setting up the bridge networking interface. At this point I don't
want to figure out the logistics setting up the network bridge in
configuration management, so I simply manually edited
``/etc/network/interfaces`` to look like this (substitute your own
network values):
::
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
#auto eth0
#iface eth0 inet manual
# https://help.ubuntu.com/community/KVM/Networking
# create a bridge so guest VMs may have their own identities
auto br0
iface br0 inet static
address XXX.XX.89.42
netmask 255.255.255.0
network XXX.XX.89.0
broadcast XXX.XX.89.255
gateway XXX.XX.89.1
# dns-* options are implemented by the resolvconf package
dns-nameservers XXX.XX.254.225 XXX.XX.254.225
dns-search example.com
# bridge_* options are implemented by bridge-utils package
bridge_ports eth0
bridge_stp off
bridge_fd 0
bridge_maxwait 0
Then, I crossed my fingers and reloaded the network stack using this
command:
::
/etc/init.d/networking restart
I also used this to "bounce" the bridge network interface:
::
ifdown br0 && ifup br0
I verified with ``ifconfig``.
I'm ready to create my first VM. There are many different ways to boot
the VM and install the operating system. KVM is fully virtualized so
nearly any operating system may be install on the VM.
If you have not already, please get familiarized with the following two
commands:
#. virsh
#. virt-install
The ``virsh`` command is an unified tool / API for working with
hypervisors that support the libvirt library. Currently ``virsh``
supports Xen, QEmu, KVM, LXC, OpenVZ, VirtualBox and VMware ESX. For
more information run ``virsh help``.
The ``virt-install`` command line tool is used to create new KVM, Xen,
or Linux container guests using the "libvirt" hypervisor management
library. For more information run ``man virt-install``.
Woah, virsh and virt-install both support LXC?
We have decided to only support Ubuntu 12.04 at this time, so obviously
we will choose that for our guest's OS. Now we need to decide on an
installation strategy. We may use the following techniques to perform an
install:
- boot from local CD-rom
- boot from local ISO
- boot from PXE server on our local vLAN
- boot from netboot image from anywhere in the world
We will choose the PXE boot strategy because our vLAN environment
already uses that for physical hosts.
We will use the ``virt-install`` helper tool to create the virtual
machine's "hardware" with various flags. Lets document the creation of
this guest in a simple bash script so we may reference it again in the
future.
/tmp/create-kvmtest02-a.sh:
::
HOSTNAME=kvmtest02-a
DOMAIN=example.com
sudo virt-install \
--connect qemu:///system \
--virt-type kvm \
--name $HOSTNAME \
--vcpu 2 \
--ram 4096 \
--disk /cars/vms/$HOSTNAME.qcow2,size=20 \
--os-type linux \
--graphics vnc \
--network bridge=br0,mac=RANDOM \
--autostart \
--pxe
.. raw:: html
</p>
This was not used, but shows the flags to perform a netboot from
Internet:
::
--location=http://archive.ubuntu.com/ubuntu/dists/raring/main/installer-amd64/ \
--extra-args="auto=true priority=critical keymap=us locale=en_US hostname=$HOSTNAME domain=$DOMAIN url=http://192.168.1.22/my-debconf-preseed.txt"
.. raw:: html
</p>
I created the vm:
::
bash /tmp/create-kvmtest02-a.sh
.. raw:: html
</p>
``virt-install`` drops you into the "console" of the VM, but this will
not work yet, so we use ctrl+] to break out and get back to our
hypervisor. Use ``virsh list`` to list all the currently running VMs.
Lets use ``virt-viewer`` to view the VMs display. For this we need to
SSH to the hypervisor and forward our display to our workstation, we do
this with the ``-X`` flag. For example:
::
ssh -X kvmtest02
Now we can launch ``virt-viewer`` on the remote hypervisor, and the GUI
will be drawn on our local X display!
::
virt-viewer kvmtest02-a
Once I got that to work, I also tested ``virt-manager`` which gives a
GUI to control all guests on the remote hypervisor.
::
virt-manager
.. raw:: html
</p>
Now we need to determine the auto-generated MAC Address of the new
virtual machine.
::
virsh dumpxml kvmtest02-a | grep -i "mac "
mac address='52:54:00:47:86:8e'
We need to add this MAC address to our PXE server's DHCP configuration
to allocate the IP and tell it where to PXE-boot from.
During a real deployment we would get an IP address allocated and an A
record and PTR setup for new servers. This is a test and I will be
destroying all traces of this virtual machine after presenting during
the hackathon, so for now I'm going to skip the DNS entries and "steal"
an IP address. I must be VERY careful not to use an IP address already
in production. First use dig to find an IP without a record, then
attempt to ping and use NMAP on the IP.
::
dig -x XXX.XX.89.240 +short
ping XXX.XX.89.240
nmap XXX.XX.89.240 -PN
The IP address checked out, it didn't have a PTR, it didn't respond to
pings, and using nmap proved there were no open ports. I'm very
confident this IP address is not in use.
I added a record to our DHCP / PXE server for this Virtual Machine. I
attempted multiple times to pxe boot the VM, but the network stack was
never automatically configured... The DHCP server was discovering the
new VMs MAC and offering the proper IP address, as noted by these log
lines:
::
Dec 13 07:57:43 pxeserver60 dhcpd: DHCPDISCOVER from 52:54:00:47:86:8e via eth0
Dec 13 07:57:43 pxeserver60 dhcpd: DHCPOFFER on xxx.xx.89.240 to 52:54:00:47:86:8e via eth0
Dec 13 07:57:44 pxeserver60 dhcpd: DHCPDISCOVER from 52:54:00:47:86:8e via eth0
Dec 13 07:57:44 pxeserver60 dhcpd: DHCPOFFER on xxx.xx.89.240 to 52:54:00:47:86:8e via eth0
Dec 13 07:57:48 pxeserver60 dhcpd: DHCPDISCOVER from 52:54:00:47:86:8e via eth0
Dec 13 07:57:48 pxeserver60 dhcpd: DHCPOFFER on xxx.xx.89.240 to 52:54:00:47:86:8e via eth0
I wasted about 4 hours attempting to troubleshoot and diagnose why the
VM wouldn't work with DHCP. I ended the night without any guests
online...
**The Next DAY!**
So today I decided to stop trying to get DHCP and PXE working. I
downloaded an Ubuntu server ISO to the hypervisor, and used
``virt-manager`` to mount the ISO on the guest and booted for a manual
operating system install.
This did two things, it proved that the hypervisor's network bridge
``br0`` worked for static network assigned settings and that something
between the DHCP server and the hypervisor was preventing the
``DHCPOFFER`` answer from getting back to the VM. I looked into iptables
firewall, removed apparmor, removed SELINUX and reviewed countless logs
looking for hints... then moved on...
I was able to get salt-minion installed on the vm using our
post-install-salt-minion.sh script, which I manually downloaded from the
salt master. But to keep this test self contained, I pointed the VM's
salt-minion to ``kvmtest02`` which we already had setup as a test
salt-master.
The salt-master saw the salt-minion's key right away, so I decided to
target an install. This what I applied to the VM:
salt/top.sls:
::
'kvmtest02.example.com':
- kvm
'kvmtest02b.example.com':
- virtualenv
- python-ldap
- nginx
- the-gateway
salt-pillar/top.sls
::
# kvmtest02b gateway in a VM experiment
'kvmtest02b.example.com':
- nginx
- the-gateway.alpha
- deployment-keys.the-gateway-alpha
.. raw:: html
</p>
The stack was successfully deployed to the VM and proved that virtual
machines are a viable solution for stage or production. It also gave me
the change to test out this particular deployment again and found a few
gotchas we need to create maintenance tickets for.
Without configuration management, it would have taken weeks to deploy
this custom application stack. The install with configuration management
took less then 10 minutes!
One of the KVM related snags I ran into was that Nginx does some fun
calculations with cpu cache to determine hash table sizes. As a
temporary work around, until I can devote more research time, I raised
up the following three hash table directives in the http section of
nginx.conf:
::
server_names_hash_bucket_size 512;
types_hash_bucket_size 512;
types_hash_max_size 4096;
.. raw:: html
</p>
**SmartOS**
**snippet from /etc/dhcp/dhcpd.conf**
::
# SmartOS hypervisor group to boot image
group "smartos-hypervisors" {
next-server xxx.xx.89.71;
host smrtest01-eth0 {
hardware ethernet 00:25:B5:02:07:DF;
option host-name "ncstest01";
fixed-address smrtest01.example.com;
if exists user-class and option user-class = "iPXE" {
filename = "smartos/menu.ipxe";
} else {
filename = "smartos/undionly.kpxe";
}
}
}
::
mkdir /cars/tftp/smartos
cd /cars/tftp/smartos
wget http://boot.ipxe.org/undionly.kpxe
wget https://download.joyent.com/pub/iso/platform-latest.tgz
tar -xzvf platform-latest.tgz
mv platform-20130629T040542Z 20130629T040542Z
mkdir platform
mv i86pc/ platform/
create boot menu that we referenced,
``vim /cars/tftp/smartos/menu.ipxe``
::
#!ipxe
kernel /smartos/20130629T040542Z/platform/i86pc/kernel/amd64/unix
initrd /smartos/20130629T040542Z/platform/i86pc/amd64/boot_archive
boot
Make sure to replace platform version with current.
I was able to get the blade server to PXE boot the image, but it seems
SmartOS doesn't really support the SANs. SmartOS really expects to see
local disks, and to build a ZFS pool on top of that. Basically SmartOS
could be used to build a SAN, so they didn't put much effort in
supporting SANs. After I figured this out I abandoned this test. We
could revist this again, using one of the Dell servers, or use it to
stand up a really powerful Alpha server environment.
**LXC**
Run /usr/bin/httpd in a linux container guest (LXC). Resource usage is
capped at 512 MB of ram and 2 host cpus:
::
virt-install \
--connect lxc:/// \
--name lxctest02-a \
--ram 512 \
--vcpus 2 \
--init /usr/bin/httpd
.. raw:: html
</p>
**Discussion points**
- Why doesn't DHCP work on bridge?
- If we use virtualization, we need to come up with a plan for IP
addresses, like possibly allocate ~5 IP addresses to a hypervisor
host
- We need to come up with a naming convention for guests, in testing I
appended a letter to the hypervisor name ``kvmtest02`` so the guests
names were ``kvmtest02a`` and ``kvmtest02b``, is this plausible going
forward?
**If I had more time ...**
.. raw:: html
<ul>
<li>
I would have liked to test out LXC
.. raw:: html
</li>
<li>
I would have liked to test out Docker
.. raw:: html
</li>
<li>
I would have liked to test out physical to virtual migrations
.. raw:: html
</li>
</p>

View file

@ -0,0 +1,107 @@
sensu-salt
###########
:date: 2013-12-17 11:35
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: sensu-salt
:status: published
A while back I explained how to `Create your own fleet of servers with
Digital Ocean and
salt-cloud <http://russell.ballestrini.net/create-your-own-fleet-of-servers-with-digital-ocean-and-salt-cloud/>`__.
Today I will extend that post and show how I deployed a test environment
for `Sensu, an open source monitoring
framework <http://sensuapp.org/>`__.
Before I test out new infrastructure software, I always attempt to write
deployment manifests. I subject myself to this exercise for the
following reasons:
- to get better at configuration management (self growth)
- to enable quick setup and tear down of test environments (save money)
- to make sure the new software may be deployed and maintained in
configuration management (a must)
- to document the install process and leave comments (my notes double
as automation scripts!)
- to allow knowledge transfer (sharing is caring, you're welcome!)
The state formulas in this post were tested on Ubuntu 12.04.3 x64bit.
**Clone or fork the Salt-State tree**
https://github.com/sensu/sensu-salt
::
git clone https://github.com/sensu/sensu-salt.git
.. raw:: html
</p>
**Declare deployment targets**
Before deployment, we must declare some targets in the top.sls file:
::
'*':
- git
'sensu-client*':
- sensu.client
'sensu-server*':
- rabbitmq.server
- redis.server
- sensu.server
- sensu.client
.. raw:: html
</p>
**Stand up Sensu test environment**
I used the following ``salt-cloud`` command to create the sensu monitor
server:
::
salt-cloud --profile ubuntu_do sensu-server && salt 'sensu-server' state.highstate
.. raw:: html
</p>
Once the sensu-server was live, I altered the ``client-config.json`` and
modified the RabbitMQ host with the new ``sensu-server``'s IP or DNS
record.
I then spun up 4 sensu-clients using the following command:
::
salt-cloud -P --profile ubuntu_do sensu-client1 sensu-client2 sensu-client3 sensu-client4 && salt 'sensu-client*' state.highstate
.. raw:: html
</p>
This caused 4 cloud servers to be spawned in parallel, and when the
provisioning finished, they instantly appeared in the
``sensu-dashboard`` which runs on the ``sensu-server``.
**Tear down Sensu test environment**
Later when I was done messing with writing checks, I used the following
``salt-cloud`` commands to destroy the sensu server and clients:
::
salt-cloud --destroy sensu-server
salt-cloud --destroy sensu-client1 sensu-client2 sensu-client3 sensu-client4
.. raw:: html
</p>

View file

@ -0,0 +1,82 @@
Heka, World!
############
:date: 2013-12-20 18:25
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: heka-world
:status: published
This post serves as a "Hello World" for the `data collection and
processing software called
Heka <https://github.com/mozilla-services/heka>`__. Heka is written in
Go and was open sourced by Mozilla, the same fabulous group that brings
us Firefox!
I intend to use Heka to replace Logstash agents by sending logs directly
to ElasticSearch and continuing to use Kibana3 for visualizations. Also
I aim to start collecting metrics and sending to a central Whisper
back-end to fuel Graphite charts. All that we need to make Heka take on
these responsibilities is one binary and some custom configuration.
**Heka:** Hello, World!
This mostly contrived example will show how to use Heka to watch
``/tmp/input.log`` and write to ``/tmp/output.log``.
**Step 1:** install Heka
Compile from source or install the `Heka
package <https://github.com/mozilla-services/heka/releases>`__ for your
operating system.
**Step 2:** create a Heka TOML configuration file
``/tmp/hello-heka.conf:``
::
[hello_heka_input_log]
type = "LogstreamerInput"
log_directory = "/tmp"
file_match = 'input\.log'
[hello_heka_output_log]
type = "FileOutput"
message_matcher = "TRUE"
path = "/tmp/output.log"
perm = "664"
encoder = "hello_heka_output_encoder"
[hello_heka_output_encoder]
type = "PayloadEncoder"
append_newlines = false
.. raw:: html
</p>
**Step 3:** start Hekad and test!
#. in terminal 1:
::
sudo hekad -config=/tmp/hello-heka.conf
#. in terminal 2:
::
tail -f /tmp/output.log
#. in terminal 3:
::
echo 'Heka, World!' >> /tmp/input.log
Like magic the data appended to ``input.log`` will appear in
``output.log``
Heka also has great docs and a number of input and output plugins, but
don't take my word for it, try it yourself!

View file

@ -0,0 +1,30 @@
Test Game Engine with Python and SFML
#####################################
:date: 2014-01-02 00:30
:author: Russell Ballestrini
:tags: Art, Code
:slug: test-game-engine-with-python-and-sfml
:status: published
Over this holiday season, Christmas and New Years, I took the time to
mess with some Game Development. I wrote a demo game engine using Python
and SFML. I plan to use this post to track my progress.
**Video Evolution Playlist**
- `All
videos <http://www.youtube.com/watch?v=ytEG21b2es4&list=PLPqlh_ebFYDFPv_tIdUb26Bit2Q2nIVrm>`__
**2014-01-01**
- `User Input, Simple world,
Scrolling <http://www.youtube.com/watch?v=ytEG21b2es4>`__
- `Simple Collision
detection <http://www.youtube.com/watch?v=LFxfepBSgEc>`__
**2014-01-02**
- `Simple Collision bump vs
push <http://www.youtube.com/watch?v=SsAi00wv0vU>`__
- `Power ups and grow and
shrink <http://www.youtube.com/watch?v=2-FFMP67atU>`__

View file

@ -0,0 +1,98 @@
How I added two Seagate 240G SSDs as SmartOS L2ARC
##################################################
:date: 2014-01-05 14:56
:author: Russell Ballestrini
:tags: Guide
:slug: how-i-added-two-seagate-240g-ssds-as-smartos-l2arc
:status: published
**How I added two Seagate 240G SSDs as SmartOS L2ARC**
#. removed icepacks from two western digital velociraptors
#. installed ssds into icepacks
#. installed icepacks into HP hotswap trays
#. installed trays into HP prolaient g6 server
**How to list all drive installed in Solaris, Open Solaris, or SmartOS**
::
iostat -eE
::
format
.. raw:: html
</p>
::
AVAILABLE DISK SELECTIONS:
0. c1t0d0
/pci@0,0/pci103c,330b@1f,2/disk@0,0
1. c1t1d0
/pci@0,0/pci103c,330b@1f,2/disk@1,0
2. c1t2d0
/pci@0,0/pci103c,330b@1f,2/disk@2,0
3. c1t3d0
/pci@0,0/pci103c,330b@1f,2/disk@3,0
.. raw:: html
</p>
**list zpools:**
::
zpool list
::
NAME SIZE ALLOC FREE EXPANDSZ CAP DEDUP HEALTH ALTROOT
zones 1.81T 41.4G 1.77T - 2% 1.00x ONLINE -
.. raw:: html
</p>
**Add the two 240G SSDs as L2ARC devices:**
::
zpool add zones cache c1t2d0 c1t3d0
.. raw:: html
</p>
**Look at the iostats of the drives in the zpool**
::
zpool iostat -v
::
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
zones 41.4G 1.77T 5 46 78.7K 730K
mirror 41.4G 1.77T 5 46 78.7K 730K
c1t0d0 - - 0 15 41.5K 733K
c1t1d0 - - 0 15 41.6K 733K
cache - - - - - -
c1t2d0 14.6M 224G 0 3 2.48K 273K
c1t3d0 45.5M 224G 0 6 2.48K 852K
---------- ----- ----- ----- ----- ----- -----
.. raw:: html
</p>
This machine is a test hypervisor and after reviewing the output from
``zpool iostat -v`` over the last couple days, I'm pretty sure adding
L2ARC to this box was not needed, but it was a great learning
experience.

View file

@ -0,0 +1,176 @@
How to reset HP iLO Lights-Out User and Password Settings with IPMItool
#######################################################################
:date: 2014-01-25 09:18
:author: Russell Ballestrini
:tags: Guide
:slug: how-to-reset-hp-ilo-lights-out-user-and-password-settings-with-ipmitools
:status: published
Do NOT follow guides that suggest to make a DOS boot disk, this is over
complicated.
Use the ``ipmitool`` which ships with most Unix based operating systems.
I tested on SmartOS and Ubuntu Linux. Use a live boot disk if you must.
#. Run ``ipmitool user list`` to list all users installed on the iLO.
#. Choose a user ID from the list and run ``ipmitool user set password [ID]``.
#. Last, make sure the user is enabled with ``ipmitool user enable [ID]``.
**Here is a complete set of commands I used to reset user ID 6:**
::
[root@1c-c1-de-f0-ad-36 /opt]# ipmitool user list
ID Name Callin Link Auth IPMI Msg Channel Priv Limit
2 ROUSER true false false Unknown (0x00)
3 USERID true false false Unknown (0x00)
4 OEM true false false Unknown (0x00)
5 Operator true false false Unknown (0x00)
6 admin true false false Unknown (0x00)
... truncated ...
15 admin true false false Unknown (0x00)
16 OEM true false false Unknown (0x00)
[root@1c-c1-de-f0-ad-36 /opt]# ipmitool user set password 6 mynew-password
[root@1c-c1-de-f0-ad-36 /opt]# ipmitool user enable 6
.. raw:: html
</p>
Next I logged into the web GUI and cleaned up this crazy list of users.
You may also need to change the privileges on a user ID to grant admin
level access. Run ``ipmitool user`` for a list of possible sub-commands.
After I finished poking around with the web GUI, I decided to learn a
bit more about IPMI and the ``ipmitool`` command.
I figured out how to get sensor information over the LAN using this
command:
::
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin sensor
.. raw:: html
</p>
I was even able to log into the server OS via Serial-over-LAN (SOL)
using this command:
::
sudo ipmitool -I lanplus -H guy-ilo.foxhop.net -U admin sol activate
.. raw:: html
</p>
Serial-over-LAN completely resolves the need for a complicated JAVA/KVM
(keyboard video mouse) setup, I was able to reboot the server and watch
the machine POST over the serial connection! I uploaded a video showing
`Serial-Over-LAN with IPMItools to an HP Proliant DL160 G6 running
SmartOS <http://www.youtube.com/watch?v=xAFjbKAzB4s>`__. Now you don't
need to shell out $229+ on an Advanced 1 yr single server Licence for HP
Lights-Out 100i (LO100i)!
**Update:** just documenting a few other useful commands for myself
here.
Power on and off the server chassis:
::
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin chassis power status
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin chassis power on
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin chassis power soft
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin chassis power off
sudo ipmitool -I lan -H guy-ilo.foxhop.net -U admin chassis power cycle
|
| Also check this out for firmware upgrades: (SPP 2014.09.0)
SPP2014090.2014\_0827.10.iso
|
|
ftp://ftp.hp.com/pub/softlib2/software1/cd-generic/p1513074441/v101117/SPP2014090.2014\_0827.10.iso
|
**Update:**
I ended up flashing the my P410 smart array raid card with
CP019316.scexe [6.00] (SPP 2013.02.0) because the newer patches wouldn't
run properly. This was enough however to get my 3TB disks to show up in
the raid configuration!
CP023869.scexe [6.60] (SPP 2014.09.0) is the next version of the
firmware. I restarted the machine which I believe is important between
firmware flashing. I was able to use the scexe to flash from 6.00 to
6.60!
I used CP021014.scexe [4.26] to upgrade my DL180 G6 ILO from 4.22 to
4.26. Cool but I don't notice any changes besides the version number...
**Update:** apparently after you know the ILO username and password you
may also use SSH to connect and manage the server:
::
ssh admin@guy-ilo.foxhop.net
admin@guile-ilo.foxhop.net's password:
Lights-Out 100 Management
Copyright 2005-2007 ServerEngines Corporation
Copyright 2006-2007 Hewlett-Packard Development Company, L.P.
/./-> help
Root Directory
/./-> show
/./
Targets
system1
map1
Properties
Verbs
cd
version
exit
show
help
/./-> cd system1
/./system1/-> show
/./system1/
Targets
oemhp_sensors
oemhp_frus
console1
led1
Properties
name=DL180(Aspen) _R
enabledstate=enabled
Verbs
cd
version
exit
show
reset
start
stop
help
You can even trigger the server OS to stop change run levels or mess
with chassis power for more extreme measures.
::
/./system1/-> stop
System1 stopped.

View file

@ -0,0 +1,36 @@
The Three Deployment Management Strategies
##########################################
:date: 2014-02-03 15:06
:author: Russell Ballestrini
:tags: DevOps, Opinion
:slug: the-three-deployment-management-strategies
:status: published
There are three deployment management strategies that could be used to
maintain a system. Each has pros and cons which I outline in this
document.
**run once**
A proceedure that is run once and only once to setup a system's
configuration values and settings. A semaphore or flag generally
blocks repeated executions to prevent an undesirable outcome.
Development Difficulty: LOW - binary state aware (has run/has not
run)
**run always**
A proceedure that is safe to run multiple times but uses a brute
force method to setup a system's configuration and settings. It will
self-heal at the expense of clobbering existing state.
Development Difficulty: MEDIUM - not state aware - attention must be
spent to allow multiple executions
**run as needed**
A proceedure that is safe to run multiple times and checks a
system's configuration and settings before it sets a system's
configuration or settings. "checks before it steps". It will
self-heal only the values it needs to.
Development Difficulty: HIGH - state aware - must know how to both
get and set values and make desicions based on what it finds.

View file

@ -0,0 +1,29 @@
Automatic Backups
#################
:date: 2014-02-07 17:14
:author: Russell Ballestrini
:tags: Code, DevOps, Greatest Hits
:slug: automatic-backups
:status: published
:summary: My tools for creating automatic backups for various systems
I maintain many tools for creating automatic backups for various systems.
This page will act as a hub to each of those solutions.
**`tar-back </tar-back>`__**
tar-back is a backup utility to tar and gzip target filesystems. It
supports a custom retention, filter exclusions, and backup
directory.
**`virt-back </virt-back-a-python-libvirt-backup-utility-for-kvm-xen-virtualbox/>`__**
virt-back virt-back is a python application that uses the libvirt
API to safely shutdown, gzip, and restart guests. Supports KVM,
QEMU, XEN, Virtualbox, and more.
**`mysql-back </mysql-back>`__**
mysql-back is a backup utility script to dump (backup) and gzip
every MySQL database on a host.
**`smart-back </backup-all-virtual-machines-on-a-smartos-hypervisor-with-smart-back-sh/>`__**
smart-back is a utility used to backup of every virtual machine on a
SmartOS hypervisor.

View file

@ -0,0 +1,63 @@
mysql-back
##########
:date: 2014-02-07 16:39
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: mysql-back
:status: published
``mysql-back``\ is a backup utility script to dump (backup) and gzip
every MySQL database on a host.
I use ``mysql-back`` in combination with cron to perform regular
database dumps of MySQL servers to the ``/archive/db`` partition on
localhost. I then have a central long term storage server that collects
the ``/archive`` partition from every host.
**mysql-back:**
::
#!/bin/bash
USER="root"
PASSWORD=""
TODAY=`date +%Y-%m-%d`
OUTPUTDIR="/archive/db/$TODAY"
MYSQLDUMP=`which mysqldump`
MYSQL=`which mysql`
GZIP=`which gzip`
mkdir $OUTPUTDIR
# get a list of databases
databases=`$MYSQL --user=$USER --password=$PASSWORD \
-e "SHOW DATABASES;" | tr -d "| " | grep -v Database`
# dump each database in turn
for db in $databases; do
$MYSQLDUMP --force --opt --user=$USER --password=$PASSWORD \
--databases $db > "$OUTPUTDIR/$db.sql"
done
# compress all of the files
$GZIP $OUTPUTDIR/*
# clean up all dumps 30 days old
find /archive/db -ctime +30 -type d | xargs rm -rf
.. raw:: html
</p>
If you ever need to restore, like in my case when I moved all my
databases over to a brand new Percona MySQL SmartOS zone, I used the
following command:
::
zcat *sql.gz | mysql -u root -p
.. raw:: html
</p>

View file

@ -0,0 +1,122 @@
tar-back
########
:date: 2014-02-07 16:49
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: tar-back
:status: published
| ``tar-back`` is a backup utility to tar and gzip target filesystems.
| It supports a custom retention, filter exclusions, and backup
directory.
I use ``tar-back`` in combination with cron to perform regular backups
of all localhost filesystems into ``/archive/fs``. I then have a central
long term storage server that collects the ``/archive`` partition from
every host.
**tar-back:**
::
#!/usr/bin/env python
DESCRIPTION = """A backup utility to tar and gzip
target filesystems. Custom retention, filter exclusions and
backup directory.
"""
from os import path
from sys import exit
from shutil import move
from optparse import OptionParser
import tarfile
def filter_exclusions( tarinfo ):
"""Accept tarinfo, return tarinfo or None"""
if o.filters:
for filtr in o.filters:
if filtr in tarinfo.name:
return None
return tarinfo
def exclude_exclusions( filename ):
"""Accept filename, return True or False"""
"""support for python 2.6 or lower"""
if o.filters:
for filtr in o.filters:
if filtr in filename:
return True
return False
def file_rotate( target, retention = 3 ):
"""file rotation routine"""
for i in range( retention-2, 0, -1 ): # count backwards
old_name = "%s.%s" % ( target, i )
new_name = "%s.%s" % ( target, i + 1 )
try: move( old_name, new_name )
except IOError: pass
move( target, target + '.1' )
if __name__ == '__main__':
p = OptionParser() # create an option parser object
p.set_description( DESCRIPTION )
p.add_option( '-t', '--targets',
help='list of target filesystems to backup (coma delimited)',
default=None, dest='targets', metavar='"/home"',
)
p.add_option( '-b', '--backup-dir',
help='path to backup directory',
dest='backdir', metavar='"PATH"',
)
p.add_option( '-f', '--filters',
help='list of filter patterns to exclude (coma delimited)',
default=None, dest='filters', metavar='".mp3"',
)
p.add_option( '-r', '--retention',
help='backups to retain [default: 3]',
default=3, type='int', dest='retention', metavar='amount',
)
o, args = p.parse_args() # parse options and args
extension = '.tar.gz'
if not o.targets:
exit( "missing filesystem target, run --help" )
if not o.backdir:
exit( "missing backup directory, run --help" )
if not path.isdir( o.backdir ):
exit( "backup-dir %s does not exist" % o.backdir )
if o.filters:
o.filters = o.filters.split(',')
o.targets = o.targets.split(',')
for target in o.targets:
slug = target.replace( '/', '-' ).lstrip( '-' ) # change / to -
if slug == '': slug = 'r' # / slug is empty (root)
tarpath = path.join( o.backdir, slug + extension )
if path.isfile( tarpath ):
file_rotate( tarpath, o.retention )
tar = tarfile.open( tarpath, 'w:gz' )
try:
tar.add( target, filter=filter_exclusions )
except TypeError:
tar.add( target, exclude=exclude_exclusions )
tar.close()
.. raw:: html
</p>

View file

@ -0,0 +1,66 @@
Configuration Management and the Golden Image
#############################################
:date: 2014-02-21 17:19
:author: Russell Ballestrini
:tags: DevOps, Greatest Hits, Opinion
:slug: configuration-management-and-the-golden-image
:status: published
When operations first became a thing, system administrators stood up
servers using a base image from their favourite distribution. Things
were done manually. Some administrators created their own distros, some
wrote customised shell scripts to be run once-and-only-once to provision
software and settings. This method worked, but it was slow, manual, and
the human element caused defects. Then the request came in to stand up
100 servers the exact same way.
| System admins resolved this large request by coming up with a Standard
Operating Environment (SOE) image which would end up becoming the
"golden image". This golden image was the source of truth, and we
built hundreds, thousands of machines this way. All systems were the
same, or rather started out the same, but it didn't take long for
deviations occur.
| (Norton Ghost, DD, ISOs)
| http://en.wikipedia.org/wiki/Standard\_Operating\_Environment
The golden image by itself was a flawed idea. The task of creating a
golden image was difficult and required a lot of work. Not only was it
technical, but there was a lot of politics involved in what was worthy
of inclusion. We didn't want to add cruft to every machine. Also the
golden image was only updated every couple of years, so it would quickly
become outdated and it still required provisioning scripts. Systems
already in production didn't get configuration updates that were
recently added to the golden image. There had to be a better way, and
there was...
| Some novel and smart system administrators who also knew how to
program decided that they didn't want to maintain a golden image and
deal with all the headaches involved and opted to use the light weight
distribution image and then build sophisticated remote execution
software to manage and maintain each server's configuration. Later on
they built configuration management systems on top of this remote
execution layer and were finally able to keep each system up-to-date,
regardless of when it was deployed. They were geniuses and everyone
who knew anything quickly rushed to implement remote execution and
configuration management. It was the right way to manage servers,
until the cloud drifted in.
| (SaltStack, Ansible, Puppet, Chef, CFEngine)
In the day of cloud computing, we needed to scale up and down servers in
seconds. A complex configuration manifest could take hours to run from
start to finish. Configuration management was too slow. Each server
needed to download, install, and configure the software stack, in
real-time. Sure we could spin up multiple machines in parallel, but it
was still slow. We started to look back at the golden age of the golden
image, when a server was built and booted in moments. How could we pair
the speed of the golden image with the flexibility of configuration
management?
| In the future could we use configuration management manifests and
revision control to document how to build the golden image and then
take a snapshot? Could we overlay multiple layers or dataset of images
on top of each other? Perhaps we take a step way back and create
golden images with a combination of a highly customizable distribution
like Gentoo and configuration management.
| (Joyant SmartOS zone datasets, Docker container images, Vagrant box
files, AWS AMI, Digital Ocean Snapshots, etc)

View file

@ -0,0 +1,267 @@
Replace the Nagios Scheduler and NRPE with Salt Stack
#####################################################
:date: 2014-03-08 15:53
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: replace-the-nagios-scheduler-and-nrpe-with-salt-stack
:status: published
Note: I will update this post as I progress.
So the idea is to use Salt Stack's remote execution to communicate with
all nodes and run the Nagios checks and collect the return output
instead of using the NRPE client/service protocol. This reduces the
number of agents running on each host and appears significantly more
secure. Salt Stack uses public/private crypto on top of the ZMQ
publisher/subscriber model. This means the communication transport is
very fast, very secure, and all nodes will run checks in parallel!
| First, we need to install the Nagios checks and plugins on each host
or minion.
| I used the following State Formula on my Ubuntu and Debian hosts:
**salt://nagios/plugins.sls**
::
nagios-plugins:
pkg:
- installed
nagios-plugins-extra:
pkg:
- installed
**salt://top.sls**
::
base:
'*'
- nagios.plugins
I kicked off a highstate (``salt '*' state.highstate``) on all minions
and eventually they all returned in the affirmative. We now have all the
Nagios plugins and checks installed on each host.
This next part is FUN, We run a check on every host concurrently.
This particular check counts the number of processes running on each
host and warns at 150 and criticals at 200.
::
salt '*' --out=json --static cmd.run_all '/usr/lib/nagios/plugins/check_procs -w 150 -c 200'
.. raw:: html
</p>
Output static JSON - Wait for all minions to return and then
generate a single JSON object.
::
{
"graphite.foxhop.net": {
"pid": 24356,
"retcode": 0,
"stderr": "",
"stdout": "PROCS OK: 78 processes"
},
"akuma.foxhop.net": {
"pid": 4610,
"retcode": 1,
"stderr": "",
"stdout": "PROCS WARNING: 158 processes"
},
"ken.foxhop.net": {
"pid": 31254,
"retcode": 2,
"stderr": "",
"stdout": "PROCS CRITICAL: 392 processes"
}
}
.. raw:: html
</p>
Review the `Nagios Plugin
API <http://nagios.sourceforge.net/docs/3_0/pluginapi.html>`__ for
more information about return codes and STDOUT formats.
Last, we take this JSON data perform further processing or display it in
a meaningful way.
For example, we could generate an HTML/JavaScript dashboard from the raw
JSON objects. We could cut metrics out of the STDOUT and persist
historic values in a data store. We could push the data into another
system like Graphite or even send an email alerts.
This solution has all the perks of Nagios without ANY of the cons!
Returning the check output data to the CLI isn't super useful for
further processing, so I hacked in a way to return data back to the
master using the encrypted zeromq bus.
**\_returners/zeromq\_return.py**
::
# -*- coding: utf-8 -*-
'''
The zeromq returner will send return data back to the Salt Master over the
Encrypted 0MQ event bus with a custom tag for filtering on the other end.
Basically after the remote execution finishes, the ret data is "packaged" into
a special "envelope" which triggers the local Salt Minion Daemon to
forward the ret data to the Salt Master's event bus.
The "package" basically wraps the ret data and uses the tag 'fire_master'.
For example, a ret data object from the execution of test.ping
would be "packaged" like this::
ret = {
'graphite.foxhop.net': true
}
ret['tag'] = 'third-party'
package = {
'events': [ ret ],
'tag': None,
'pretag': None,
'data': None
}
The Salt Minion Daemon will forward this package to the Salt Master
where a 3rd party script may be filtering on the specified internal event tag.
To use the zeromq returner, append '--return zeromq' to the salt command. ex::
salt --return zeromq '*' test.ping
TODO:
figure out a way for user to define custom tag for filtering ...
Most returners use the Salt Minion config file to supply returner
details... that is not optimal, it would be ideal if the custom tag
could be supplied on the CLI when the remote execution is run, like::
--return=zeromq --tag=mytag
'''
# needed to log to log file
import logging
# needed for config to opts processing
import os
import salt.syspaths as syspaths
from salt.config import minion_config
# needed to send events over ZMQ
import salt.utils.event
log = logging.getLogger(__name__)
# needed to define the module's virtual name
__virtualname__ = 'zeromq'
def __virtual__():
return __virtualname__
def returner(ret):
'''
Send the return data to the Salt Master over the encrypted
0MQ bus with custom tag for 3rd party script filtering.
'''
# get opts from minion config file, supports minion.d drop dir!
opts = minion_config(os.path.join(syspaths.CONFIG_DIR, 'minion'))
# TODO: this needs to be customizable!
tag = 'third-party'
# add custom tag to return data for filtering
ret['tag'] = tag
# multi event example, supports a list of event ret objects.
# single event does not currently expand/filter properly on Master side.
package = {
#'id': opts['id'],
'events': [ ret ],
'tag': None,
'pretag': None,
'data': None
}
# opts must contain valid minion ID else it binds to invalid 0MQ socket.
event = salt.utils.event.SaltEvent('minion', **opts)
# Fire event payload with 'fire_master' tag which triggers the
# salt-minion daemon to forward payload to the master event bus!
event.fire_event(package, 'fire_master')
.. raw:: html
</p>
Next we run a third party application on the Salt Master which
subscribes to our events by filtering on the special tag
('third-party').
**listen\_to\_master\_bus.py**
::
# event libary for events over ZMQ
import salt.utils.event
# create event object, attach to master socket ...
event = salt.utils.event.MasterEvent('/var/run/salt/master')
tag = 'third-party'
print('Listening for events tagged \'{}\' on Salt Master bus.'.format(tag))
# generator iterator yields events forever, we filter on tag
for data in event.iter_events(tag=tag):
print(data)
This small application just prints the incoming return data, but it
could easily be expanded to process the incoming return data and persist
it somewhere.
Open two terminals on the Salt Master host.
::
# on terminal 1 run:
python listen_to_master_bus.py
::
# on terminal 2 run:
salt '*' --return zeromq cmd.run_all '/usr/lib/nagios/plugins/check_procs -w 150 -c 200'
Same remote execution check as before but now our new returner will
make data appear in terminal 1!
This zeromq returner is more of a proof-of-concept. I think the salt
remote execution command line tool should allow end-users to provide a
``--tag`` so that data may be feed directly back to a third-party script
listening to the Salt Master's event bus which filters on the particular
tag. My next step is to look into what it would take to build in this
functionality.
In the future I want to rig up the Salt scheduler to invoke these remote
execution checks on a steady and predictable cadence. Nagios
historically runs checks every 5 minutes. The Salt scheduler will allow
us schedule different checks with different frequencies. For example, I
might want my load checks and metrics to be collected every 10 secs, but
my disk capacity usage checked every 2 minutes. This fine-grain control
is super powerful!

View file

@ -0,0 +1,101 @@
Filter Salt Stack Return Data Output
####################################
:date: 2014-03-13 18:21
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: filter-salt-stack-return-data-output
:status: published
Sometimes you only want to see what has changed, and that is OK.
Create a file like this:
**filter.py**
::
#!/usr/bin/python
from json import loads
from json import dumps
import fileinput
stdin_lines = [line for line in fileinput.input()]
ret = loads(''.join(stdin_lines))
for minion_id, data in ret.items():
print(minion_id)
print('='*len(minion_id))
for key, value in ret[minion_id].items():
if value['changes'] or value['result'] == False:
print('')
print(dumps(value, indent=4))
print('')
Make the file executable:
::
chmod 755 filter.py
Execute your remote execution like this:
::
sudo salt-call --out=json state.highstate | ./filter.py
.. raw:: html
</p>
::
sudo salt '*' --out=json --timeout=60 --static state.highstate | ./filter.py
.. raw:: html
</p>
The flags ``--timeout=60`` and ``--static`` will cause the Salt
command to block until the specified seconds for each minion to
return results. We then pipe the returned JSON into our
``filter.py`` script to filter out only the changes and failures!
Profit!
Change the conditional depending on what you want. For example, for just
failures do this:
::
if value['result'] == False:
.. raw:: html
</p>
Example output:
::
# sudo salt 'graphite.foxhop.net' --out=json --static --timeout=60 state.highstate | ./filter.py
graphite.foxhop.net
===================
{
"comment": "File /tmp/taco updated",
"__run_num__": 15,
"changes": {
"diff": "New file",
"mode": "0640"
},
"name": "/tmp/taco",
"result": true
}
.. raw:: html
</p>

View file

@ -0,0 +1,29 @@
IRC Bot (Foxbot) runs canned remote executions using Salt Stack
###############################################################
:date: 2014-03-15 16:33
:author: Russell Ballestrini
:tags: Code, DevOps
:slug: irc-bot-foxbot-runs-canned-remote-executions-using-salt-stack
:status: published
I extended my IRC Bot Foxbot today to allow it to run canned remote
executions on behalf of users in an IRC channel. This is only a
prototype or proof-of-concept. Be very careful not to allow users to
inject their own commands. Foxbot must be running on the Salt Master and
must be running as the same user that runs the salt-master daemon.
The code lives here:
`foxbot/plugins/checks.py <https://bitbucket.org/russellballestrini/foxbot/src/tip/plugins/checks.py>`__
Example usage:
::
16:18:25 * | russell checks uptime minion2.foxhop.net
16:18:25 foxbot | minion2.foxhop.net: 16:18:25 up 496 days, 22:36, 17 users, load average: 0.33, 0.70, 0.87
::
16:29:10 * | russell checks procs *
16:29:17 foxbot | minion2.foxhop.net: PROCS WARNING: 165 processes
16:29:17 foxbot | minion5.foxhop.net: PROCS CRITICAL: 309 processes

View file

@ -0,0 +1,74 @@
How to patch Heartbleed OpenSSL defect (libssl) on Ubuntu
##########################################################
:date: 2014-04-08 22:42
:author: Russell Ballestrini
:tags: Guide, Security
:slug: how-to-patch-heartbleed-openssl-defect-libssl-on-ubuntu
:status: published
Lots of people claim that you need to upgrade openssl package, but this
will not fix the issue.
| The issue is not the openssl package, it is one of the libraries that
the package relies on (libssl).
| http://www.ubuntu.com/usn/usn-2165-1/
The output of ``openssl version -a`` command should have a ``built on``
date older then ``Mon Apr 7 20:33:29 UTC 2014``. After patching openssl
we still see the vulnerable date:
::
openssl version -a | egrep "OpenSSL|built"
OpenSSL 1.0.1 14 Mar 2012
built on: Tue Aug 21 05:18:48 UTC 2012
Now we patch ``libssl1.0.0``:
::
sudo apt-get update
sudo apt-get install libssl1.0.0
Notice the patched ``built on`` date:
::
openssl version -a | egrep "OpenSSL|built"
OpenSSL 1.0.1 14 Mar 2012
built on: Mon Apr 7 20:33:29 UTC 2014
In my case I used a Salt remote execution to patch, verify, and restart
nginx on all of my 14 hosts:
::
sudo salt '*' cmd.run 'apt-get update'
::
sudo salt '*' cmd.run 'apt-get -y install libssl1.0.0'
::
sudo salt '*' cmd.run 'openssl version -a | egrep "OpenSSL|built"'
::
sudo salt '*' service.restart nginx
.. raw:: html
<p>
<center>
|
| |image0|
| xkcd 1353
.. raw:: html
</center>
</p>
.. |image0| image:: http://imgs.xkcd.com/comics/heartbleed.png

View file

@ -0,0 +1,46 @@
Nginx with SSL and mixed content errors with upstream WSGI servers
##################################################################
:date: 2014-05-08 16:28
:author: Russell Ballestrini
:tags: Code, Guide
:slug: nginx-with-ssl-and-mixed-content-errors-with-upstream-wsgi-servers
:status: published
Mixed content errors occur because Nginx (the front-end server)
communicates to the upstream WSGI server using http. WSGI does not know
(or care) about the SSL session between Nginx and the user. The WSGI
server will naively generate URIs and serve assets as http.
To fix mixed content errors, we need to communicate the inbound request
scheme or configure the WSGI server to always use https.
**waitress**
| Waitress is meant to be a production-quality pure-Python WSGI server
with very acceptable performance.
| It commonly powers Pyramid and Substance D deployments.
To configure waitress to always use https in code:
::
from waitress import serve
serve(wsgiapp, host='0.0.0.0', port=8080, url_scheme='https')
.. raw:: html
</p>
configure waitress to always use https in paste deploy compatible
configuration file:
::
[server:main]
host = 127.0.0.1
port = 6543
url_scheme = https
.. raw:: html
</p>

View file

@ -0,0 +1,53 @@
You can hack on FreeNAS 9
#########################
:date: 2014-05-15 01:06
:author: Russell Ballestrini
:tags: Code
:slug: you-can-hack-on-freenas-9
:status: published
This post analyses the FreeNAS 9 code base and discusses the various
places users may feel confident to hack on.
FreeNAS uses the following software stack:
Django
A Python Web Application Framework which complies with WSGI
Nginx
A very fast web server which may act as a reverse proxy server for
HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load
balancer and HTTP cache.
Dojo Toolkit
The Javascript toolkit used to create widgets and handle client side
processing.
FreeBSD
FreeBSD is an advanced computer operating system used to power
modern servers.
Want to hack on the frontend web application?
Start here if you enjoy Python, or if you really enjoy coding on
Django applications:
https://github.com/freenas/freenas/tree/master/gui
Want to hack on the GUI?
Start here if you are a front end developer and enjoy writing HTML,
CSS, and working with Javascript:
https://github.com/freenas/freenas/tree/master/gui/templates
Want to change Nginx?
Take a look here if you would like to review, change, or tune Nginx
on FreeNAS:
https://github.com/freenas/freenas/tree/master/nanobsd/Files/usr/local/etc/nginx
This directory holds the nginx "vhost" config files and CGI parameters.
Want to hack on the OS?
Start here, if you know about ``FreeBSD`` or operating systems in general:
https://github.com/freenas/freenas/tree/master/nanobsd
This directory seems like a customized and completely version controlled nanoBSD install!

View file

@ -0,0 +1,81 @@
Mailpile Salt States for Ubuntu or Debian
#########################################
:date: 2014-08-15 20:32
:author: Russell Ballestrini
:tags: DevOps
:slug: mailpile-salt-states-for-ubuntu-or-debian
:status: published
I wrote these Salt States to install Mailpile on an Ubuntu host. Fun
fact, it took me 20 minutes to write these states and they worked the
first time I ran them. Disclaimer - I used a throw away server and
wasn't concerned that buckets of packages were installed to the system
instead of using a virtualenv.
::
cd /opt/Mailpile
./mp --set sys.http_host=0.0.0.0
./mp
.. raw:: html
</p>
Then open a web browser the IP address of the host running the mp
command and follow the prompts to setup the server/client/app.
**mailpile/init.sls:**
::
# Clone the source repository
mailpile-git-latest:
git.latest:
- name: https://github.com/pagekite/Mailpile.git
- target: /opt/Mailpile
# install the system requirements
mailpile-system-packages:
pkg.installed:
- names:
- make
- python-imaging
- python-lxml
- python-jinja2
- pep8
- ruby-dev
- yui-compressor
- python-nose
- spambayes
- phantomjs
- python-pip
- python-mock
- python-pexpect
{% if grains['lsb_distrib_release']|float >= 14.04 %}
- rubygems-integration
{% else %}
- rubygems
{% endif %}
# install some python requirements with pip
mailpile-pip-packages:
pip.installed:
- names:
- pgpdump
- selenium >= 2.40.0
- require:
- pkg: mailpile-system-packages
# install some ruby requirements with gem
mailpile-gem-packages:
gem.installed:
- names:
- therubyracer
- less
- require:
- pkg: mailpile-system-packages
.. raw:: html
</p>

View file

@ -0,0 +1,108 @@
Heka, World2!
#############
:date: 2014-08-23 22:54
:author: Russell Ballestrini
:tags: DevOps, Guide
:slug: heka-world2
:status: published
This article expands on my `“Hello World” for Heka </heka-world/>`_ blog post.
Check that one out first if you are new to Heka.
In this guide we introduce using Heka over the network by utilizing two
Hekad processes on localhost. For discussion purposes we name one of the
Hekad processes "sender" and the other "receiver".
- The "sender" will watch a log file and emit messages to localhost TCP
port 9612.
- The "receiver" will listen on localhost TCP port 9612 and emit
message payloads to file.
**hello-heka-file-in-tcp-out.toml (sender):**
::
# watch /tmp/input.log and output to TCP port 9612 on localhost
[hello_heka_input_log]
type = "LogstreamerInput"
log_directory = "/tmp"
file_match = 'input\.log'
[tcp_out:9612]
type = "TcpOutput"
message_matcher = "TRUE"
address = "127.0.0.1:9612"
#encoder = "hello_heka_output_encoder"
#
#[hello_heka_output_encoder]
#type = "PayloadEncoder"
#append_newlines = false
**hello-heka-tcp-in-file-out.toml (receiver):**
::
# listen to TCP port 9612 and emit to /tmp/output.log
[tcp_in:9612]
type = "TcpInput"
parser_type = "message.proto"
decoder = "ProtobufDecoder"
address = ":9612"
[hello_heka_output_log]
type = "FileOutput"
message_matcher = "TRUE"
path = "/tmp/output.log"
perm = "664"
encoder = "hello_heka_output_encoder"
[hello_heka_output_encoder]
type = "PayloadEncoder"
append_newlines = false
Now that we have config files, let us start our hekad processes, Open
three terminals.
#. in terminal 1:
::
sudo hekad -config=hello-heka-file-in-tcp-out.toml
#. in terminal 2:
::
sudo hekad -config=/tmp/hello-heka-tcp-in-file-out.toml
#. in terminal 3:
::
echo 'Heka, World2!' >> /tmp/input.log
#. in terminal 3:
::
cat /tmp/output.log
Again, like magic, the data echoed into input.log shows up in
output.log. This time the data traveled over TCP between to separate
Hekad processes. I leave changing the configuration to support separate
hosts to the reader.
By default TCP sender encodes the message with Protobuf
(ProtobufEncoder) and the TCP reciever decodes the message with Protobuf
(ProtobufDecoder).
In my testing I decided to make the TCP sender use the PayloadEncoder
and then instead of using a second hekad process, I used ``nc -l 9612``
to listen on the port. When data was added to ``/tmp/input.log`` it
showed up in the netcat terminal because hekad was watching the file and
emiting just payload portion of the message to TCP 9612 which netcat was
listening on. I left this configuration in the examples above, simply
uncomment to reproduce.
Read this for `more fun with
netcat <http://www.foxhop.net/linux-nc-and-python-sockets>`__.

Some files were not shown because too many files have changed in this diff Show more