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:
parent
00e5d7d798
commit
2319e768a6
237 changed files with 7741 additions and 0 deletions
24
content/2011-01-02-a-homegrown-python-bread-crumb-module.rst
Normal file
24
content/2011-01-02-a-homegrown-python-bread-crumb-module.rst
Normal 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>`_
|
||||
|
|
@ -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>`__
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
103
content/2011-04-03-voice-over-ip-with-teamspeak.rst
Normal file
103
content/2011-04-03-voice-over-ip-with-teamspeak.rst
Normal 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
|
||||
59
content/2011-04-16-dropbox-encryption-with-truecrypt.rst
Normal file
59
content/2011-04-16-dropbox-encryption-with-truecrypt.rst
Normal 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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
45
content/2011-08-07-the-great-gist-heist.rst
Normal file
45
content/2011-08-07-the-great-gist-heist.rst
Normal 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).
|
||||
56
content/2011-08-08-programming-is-like-alchemy.rst
Normal file
56
content/2011-08-08-programming-is-like-alchemy.rst
Normal 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
|
||||
110
content/2011-08-10-virt-back-restoring-from-backups.rst
Normal file
110
content/2011-08-10-virt-back-restoring-from-backups.rst
Normal 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.
|
||||
|
||||
|
|
||||
|
|
@ -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
|
||||
46
content/2011-08-22-python-image-grabber-pig-py.rst
Normal file
46
content/2011-08-22-python-image-grabber-pig-py.rst
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.**
|
||||
|
|
@ -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
|
||||
12
content/2011-09-27-new-baby-homecoming.rst
Normal file
12
content/2011-09-27-new-baby-homecoming.rst
Normal 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
|
||||
|
|
@ -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!
|
||||
|
|
@ -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>`__
|
||||
|
|
@ -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
|
||||
|
|
@ -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.**
|
||||
|
|
@ -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
|
||||
|
|
@ -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!**
|
||||
31
content/2011-10-21-occupy-wall-street-stack-vs-queue.rst
Normal file
31
content/2011-10-21-occupy-wall-street-stack-vs-queue.rst
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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**
|
||||
|
|
@ -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!**
|
||||
|
||||
|
|
@ -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**
|
||||
|
|
@ -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
|
||||
16
content/2011-12-12-flash-mob-office-meeting-definition.rst
Normal file
16
content/2011-12-12-flash-mob-office-meeting-definition.rst
Normal 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*
|
||||
37
content/2012-01-18-today-i-lost-a-customer.rst
Normal file
37
content/2012-01-18-today-i-lost-a-customer.rst
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
22
content/2012-02-13-attributes-of-an-8-block-cipher.rst
Normal file
22
content/2012-02-13-attributes-of-an-8-block-cipher.rst
Normal 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.
|
||||
|
|
@ -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``
|
||||
|
|
@ -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.
|
||||
|
|
@ -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>`__.**
|
||||
73
content/2012-02-14-block-cipher-lab.rst
Normal file
73
content/2012-02-14-block-cipher-lab.rst
Normal 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
|
||||
25
content/2012-02-16-my-4-month-olds-15-minutes-of-fame.rst
Normal file
25
content/2012-02-16-my-4-month-olds-15-minutes-of-fame.rst
Normal 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/
|
||||
52
content/2012-02-18-jake.rst
Normal file
52
content/2012-02-18-jake.rst
Normal 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
|
||||
|
|
@ -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 )
|
||||
|
|
@ -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
|
||||
|
|
@ -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/
|
||||
|
|
@ -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.
|
||||
|
|
@ -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".
|
||||
|
|
@ -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
|
||||
|
|
@ -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``
|
||||
|
|
@ -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.**
|
||||
|
|
@ -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>`__.**
|
||||
|
|
@ -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.
|
||||
|
|
@ -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/
|
||||
|
|
@ -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/
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
124
content/2012-10-02-explaining-cache-with-python.rst
Normal file
124
content/2012-10-02-explaining-cache-with-python.rst
Normal 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
|
||||
|
|
@ -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.
|
||||
118
content/2012-12-12-tips-for-getting-pull-requests-approved.rst
Normal file
118
content/2012-12-12-tips-for-getting-pull-requests-approved.rst
Normal 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
|
||||
|
|
@ -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!
|
||||
|
|
@ -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!'
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
40
content/2013-04-09-automatic-event-hangout-with-cron.rst
Normal file
40
content/2013-04-09-automatic-event-hangout-with-cron.rst
Normal 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>
|
||||
13
content/2013-04-26-survey-baby-monkey.rst
Normal file
13
content/2013-04-26-survey-baby-monkey.rst
Normal 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
|
||||
53
content/2013-05-26-honey-i-just-deleted-linkpeek-com.rst
Normal file
53
content/2013-05-26-honey-i-just-deleted-linkpeek-com.rst
Normal 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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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!
|
||||
|
|
@ -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 %}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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!
|
||||
180
content/2013-10-17-postfix-salt-state-formula.rst
Normal file
180
content/2013-10-17-postfix-salt-state-formula.rst
Normal 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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
490
content/2013-12-13-hackathon-2013-virtualization.rst
Normal file
490
content/2013-12-13-hackathon-2013-virtualization.rst
Normal 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>
|
||||
107
content/2013-12-17-sensu-salt.rst
Normal file
107
content/2013-12-17-sensu-salt.rst
Normal 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>
|
||||
82
content/2013-12-20-heka-world.rst
Normal file
82
content/2013-12-20-heka-world.rst
Normal 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!
|
||||
30
content/2014-01-02-test-game-engine-with-python-and-sfml.rst
Normal file
30
content/2014-01-02-test-game-engine-with-python-and-sfml.rst
Normal 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>`__
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
29
content/2014-02-07-automatic-backups.rst
Normal file
29
content/2014-02-07-automatic-backups.rst
Normal 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.
|
||||
63
content/2014-02-07-mysql-back.rst
Normal file
63
content/2014-02-07-mysql-back.rst
Normal 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>
|
||||
122
content/2014-02-07-tar-back.rst
Normal file
122
content/2014-02-07-tar-back.rst
Normal 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>
|
||||
|
|
@ -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)
|
||||
|
|
@ -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!
|
||||
101
content/2014-03-13-filter-salt-stack-return-data-output.rst
Normal file
101
content/2014-03-13-filter-salt-stack-return-data-output.rst
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
53
content/2014-05-15-you-can-hack-on-freenas-9.rst
Normal file
53
content/2014-05-15-you-can-hack-on-freenas-9.rst
Normal 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!
|
||||
|
|
@ -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>
|
||||
108
content/2014-08-23-heka-world2.rst
Normal file
108
content/2014-08-23-heka-world2.rst
Normal 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
Loading…
Add table
Add a link
Reference in a new issue