Visit https://ebookultra.com to download the full version and
explore more ebooks
Python for DevOps Learn Ruthlessly Effective
Automation 1st Edition Noah Gift
_____ Click the link below to download _____
https://ebookultra.com/download/python-for-devops-
learn-ruthlessly-effective-automation-1st-edition-noah-
gift/
Explore and download more ebooks at ebookultra.com
Here are some suggested products you might be interested in.
Click the link to download
Devops Automation Cookbook 1st Edition Michael Duffy
https://ebookultra.com/download/devops-automation-cookbook-1st-
edition-michael-duffy/
Learn Python the Hard Way 5th Edition Zed A. Shaw
https://ebookultra.com/download/learn-python-the-hard-way-5th-edition-
zed-a-shaw/
A Smarter Way to Learn Python Learn it faster Remember it
longer First Edition Mark Myers
https://ebookultra.com/download/a-smarter-way-to-learn-python-learn-
it-faster-remember-it-longer-first-edition-mark-myers/
Python Testing with pytest Simple Rapid Effective and
Scalable 1st Edition Brian Okken
https://ebookultra.com/download/python-testing-with-pytest-simple-
rapid-effective-and-scalable-1st-edition-brian-okken/
Developing on AWS with C A Comprehensive Guide on Using C
to Build Solutions on the AWS Platform 1st Edition Noah
Gift
https://ebookultra.com/download/developing-on-aws-with-c-a-
comprehensive-guide-on-using-c-to-build-solutions-on-the-aws-
platform-1st-edition-noah-gift/
Losing Afghanistan An Obituary for the Intervention 1st
Edition Noah Coburn
https://ebookultra.com/download/losing-afghanistan-an-obituary-for-
the-intervention-1st-edition-noah-coburn/
PowerShell for Sysadmins Workflow Automation Made Easy 1st
Edition Adam Bertram
https://ebookultra.com/download/powershell-for-sysadmins-workflow-
automation-made-easy-1st-edition-adam-bertram/
Practical GitLab Services A Complete DevOps Guide for
Developers and Administrators 1st Edition Jeffrey Painter
https://ebookultra.com/download/practical-gitlab-services-a-complete-
devops-guide-for-developers-and-administrators-1st-edition-jeffrey-
painter/
Dead Simple Python Idiomatic Python for the Impatient
Programmer Jason C. Mcdonald
https://ebookultra.com/download/dead-simple-python-idiomatic-python-
for-the-impatient-programmer-jason-c-mcdonald/
Python for DevOps Learn Ruthlessly Effective
Automation 1st Edition Noah Gift Digital Instant
Download
Author(s): Noah Gift, Kennedy Behrman, Alfredo Deza, Robert Jordan, Grig
Gheorghiu
ISBN(s): 9781492057697, 149205769X
Edition: 1
File Details: PDF, 10.79 MB
Year: 2020
Language: english
Noah Gift, Kennedy Behrman,
Alfredo Deza & Grig Gheorghiu
Python
for DevOps
Learn Ruthlessly Effective Automation
Noah Gift, Kennedy Behrman,
Alfredo Deza, and Grig Gheorghiu
Python for DevOps
Learn Ruthlessly Effective Automation
Boston Farnham Sebastopol Tokyo
Beijing Boston Farnham Sebastopol Tokyo
Beijing
978-1-492-05769-7
[LSI]
Python for DevOps
by Noah Gift, Kennedy Behrman, Alfredo Deza, and Grig Gheorghiu
Copyright © 2020 Noah Gift, Kennedy Behrman, Alfredo Deza, Grig Gheorghiu. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are
also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional
sales department: 800-998-9938 or corporate@oreilly.com.
Acquisitions Editor: Rachel Roumeliotis
Development Editor: Corbin Collins
Production Editor: Christopher Faucher
Copyeditor: nSight, Inc.
Proofreader: Sonia Saruba
Indexer: WordCo Indexing Services, Inc.
Interior Designer: David Futato
Cover Designer: Karen Montgomery
Illustrator: Rebecca Demarest
December 2019: First Edition
Revision History for the First Release
2019-12-11: First Release
2020-06-19: Second Release
See http://oreilly.com/catalog/errata.csp?isbn=9781492057697 for release details.
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Python for DevOps, the cover image,
and related trade dress are trademarks of O’Reilly Media, Inc.
The views expressed in this work are those of the authors, and do not represent the publisher’s views.
While the publisher and the authors have used good faith efforts to ensure that the information and
instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility
for errors or omissions, including without limitation responsibility for damages resulting from the use of
or reliance on this work. Use of the information and instructions contained in this work is at your own
risk. If any code samples or other technology this work contains or describes is subject to open source
licenses or the intellectual property rights of others, it is your responsibility to ensure that your use
thereof complies with such licenses and/or rights.
Table of Contents
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
1. Python Essentials for DevOps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Installing and Running Python 2
The Python Shell 2
Jupyter Notebooks 3
Procedural Programming 3
Variables 4
Basic Math 4
Comments 5
Built-in Functions 5
Print 5
Range 6
Execution Control 6
if/elif/else 7
for Loops 8
while Loops 9
Handling Exceptions 10
Built-in Objects 10
What Is an Object? 11
Object Methods and Attributes 11
Sequences 12
Functions 23
Anatomy of a Function 24
Functions as Objects 25
iii
Anonymous Functions 26
Using Regular Expressions 26
Searching 27
Character Sets 27
Character Classes 28
Groups 29
Named Groups 29
Find All 29
Find Iterator 30
Substitution 30
Compiling 30
Lazy Evaluation 31
Generators 31
Generator Comprehensions 32
More IPython Features 33
Using IPython to Run Unix Shell Commands 33
Exercises 34
2. Automating Files and the Filesystem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Reading and Writing Files 35
Using Regular Expressions to Search Text 44
Dealing with Large Files 46
Encrypting Text 47
Hashing with Hashlib 47
Encryption with Cryptography 47
The os Module 49
Managing Files and Directories Using os.path 51
Walking Directory Trees Using os.walk 54
Paths as Objects with Pathlib 55
3. Working with the Command Line. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Working with the Shell 57
Talking to the Interpreter with the sys Module 57
Dealing with the Operating System Using the os Module 58
Spawn Processes with the subprocess Module 59
Creating Command-Line Tools 61
Using sys.argv 62
Using argparse 65
Using click 69
iv | Table of Contents
fire 73
Implementing Plug-ins 78
Case Study: Turbocharging Python with Command-Line Tools 79
Using the Numba Just-in-Time (JIT) Compiler 80
Using the GPU with CUDA Python 82
Running True Multicore Multithreaded Python Using Numba 83
KMeans Clustering 84
Exercises 85
4. Useful Linux Utilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Disk Utilities 88
Measuring Performance 88
Partitions 90
Retrieving Specific Device Information 91
Network Utilities 93
SSH Tunneling 93
Benchmarking HTTP with Apache Benchmark (ab) 93
Load Testing with molotov 95
CPU Utilities 97
Viewing Processes with htop 98
Working with Bash and ZSH 99
Customizing the Python Shell 100
Recursive Globbing 101
Searching and Replacing with Confirmation Prompts 101
Removing Temporary Python Files 103
Listing and Filtering Processes 103
Unix Timestamp 104
Mixing Python with Bash and ZSH 104
Random Password Generator 104
Does My Module Exist? 105
Changing Directories to a Module’s Path 106
Converting a CSV File to JSON 107
Python One-Liners 107
Debuggers 108
How Fast Is this Snippet? 108
strace 109
Exercises 112
Case Study Question 112
Table of Contents | v
5. Package Management. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Why Is Packaging Important? 114
When Packaging Might Not Be Needed 114
Packaging Guidelines 114
Descriptive Versioning 115
The changelog 116
Choosing a Strategy 117
Packaging Solutions 118
Native Python Packaging 118
Debian Packaging 124
RPM Packaging 131
Management with systemd 137
Long-Running Processes 138
Setting It Up 138
The systemd Unit File 140
Installing the Unit 141
Log Handling 143
Exercises 144
Case Study Question 144
6. Continuous Integration and Continuous Deployment. . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Real-World Case Study: Converting a Poorly Maintained WordPress Site to
Hugo 145
Setting Up Hugo 147
Converting WordPress to Hugo Posts 148
Creating an Algolia Index and Updating It 150
Orchestrating with a Makefile 151
Deploying with AWS CodePipeline 152
Real-World Case Study: Deploying a Python App Engine Application with
Google Cloud Build 153
Real-World Case Study: NFSOPS 160
7. Monitoring and Logging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Key Concepts in Building Reliable Systems 163
Immutable DevOps Principles 164
Centralized Logging 164
Case Study: Production Database Kills Hard Drives 165
Did You Build It or Buy It? 166
Fault Tolerance 166
vi | Table of Contents
Monitoring 168
Graphite 168
StatsD 169
Prometheus 169
Instrumentation 173
Naming Conventions 176
Logging 177
Why Is It Hard? 177
The basicconfig 178
Deeper Configuration 179
Common Patterns 183
The ELK Stack 184
Logstash 185
Elasticsearch and Kibana 187
Exercises 190
Case Study Question 191
8. Pytest for DevOps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Testing Superpowers with pytest 193
Getting Started with pytest 194
Testing with pytest 194
Differences with unittest 196
pytest Features 198
conftest.py 198
The Amazing assert 199
Parametrization 200
Fixtures 202
Getting Started 202
Built-in Fixtures 204
Infrastructure Testing 206
What Is System Validation? 207
Introduction to Testinfra 208
Connecting to Remote Nodes 209
Features and Special Fixtures 212
Examples 213
Testing Jupyter Notebooks with pytest 216
Exercises 216
Case Study Question 217
Table of Contents | vii
9. Cloud Computing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Cloud Computing Foundations 220
Types of Cloud Computing 222
Types of Cloud Services 223
Infrastructure as a Service 223
Metal as a Service 227
Platform as a Service 227
Serverless Computing 228
Software as a Service 231
Infrastructure as Code 232
Continuous Delivery 232
Virtualization and Containers 232
Hardware Virtualization 232
Software Defined Networks 233
Software Defined Storage 233
Containers 234
Challenges and Opportunities in Distributed Computing 235
Python Concurrency, Performance, and Process Management in the Cloud
Era 237
Process Management 237
Manage Processes with Subprocess 237
Using Multiprocessing to Solve Problems 240
Forking Processes with Pool() 241
Function as a Service and Serverless 243
High Performance Python with Numba 243
Using Numba Just in Time Compiler 243
Using High-Performance Servers 244
Conclusion 245
Exercises 245
Case Study Questions 246
10. Infrastructure as Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
A Classification of Infrastructure Automation Tools 249
Manual Provisioning 250
Automated Infrastructure Provisioning with Terraform 251
Provisioning an S3 Bucket 252
Provisioning an SSL Certificate with AWS ACM 255
Provisioning an Amazon CloudFront Distribution 256
Provisioning a Route 53 DNS Record 258
viii | Table of Contents
Copying Static Files to S3 259
Deleting All AWS Resources Provisioned with Terraform 260
Automated Infrastructure Provisioning with Pulumi 260
Creating a New Pulumi Python Project for AWS 261
Creating Configuration Values for the Staging Stack 265
Provisioning an ACM SSL Certificate 266
Provisioning a Route 53 Zone and DNS Records 266
Provisioning a CloudFront Distribution 269
Provisioning a Route 53 DNS Record for the Site URL 270
Creating and Deploying a New Stack 271
Exercises 273
11. Container Technologies: Docker and Docker Compose. . . . . . . . . . . . . . . . . . . . . . . . . . . 275
What Is a Docker Container? 276
Creating, Building, Running, and Removing Docker Images and Containers 276
Publishing Docker Images to a Docker Registry 280
Running a Docker Container with the Same Image on a Different Host 281
Running Multiple Docker Containers with Docker Compose 283
Porting the docker-compose Services to a New Host and Operating System 295
Exercises 298
12. Container Orchestration: Kubernetes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Short Overview of Kubernetes Concepts 300
Using Kompose to Create Kubernetes Manifests from docker-compose.yaml 301
Deploying Kubernetes Manifests to a Local Kubernetes Cluster Based on
minikube 302
Launching a GKE Kubernetes Cluster in GCP with Pulumi 316
Deploying the Flask Example Application to GKE 319
Installing Prometheus and Grafana Helm Charts 325
Destroying the GKE Cluster 330
Exercises 331
13. Serverless Technologies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Deploying the Same Python Function to the “Big Three” Cloud Providers 336
Installing Serverless Framework 336
Deploying Python Function to AWS Lambda 336
Deploying Python Function to Google Cloud Functions 339
Deploying Python Function to Azure 344
Deploying a Python Function to Self-Hosted FaaS Platforms 348
Table of Contents | ix
Deploying Python Function to OpenFaaS 349
Provisioning DynamoDB Table, Lambda Functions, and API Gateway
Methods Using the AWS CDK 356
Exercises 375
14. MLOps and Machine learning Engineering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
What Is Machine Learning? 377
Supervised Machine Learning 377
Modeling 380
Python Machine learning Ecosystem 382
Deep Learning with PyTorch 383
Cloud Machine learning Platforms 387
Machine learning Maturity Model 388
Machine Learning Key Terminology 388
Level 1: Framing, Scope Identification, and Problem Definition 389
Level 2: Continuous Delivery of Data 390
Level 3: Continuous Delivery of Clean Data 391
Level 4: Continuous Delivery of Exploratory Data Analysis 393
Level 5: Continuous Delivery of Traditional ML and AutoML 393
Level 6: ML Operational Feedback Loop 394
Sklearn Flask with Kubernetes and Docker 395
Sklearn Flask with Kubernetes and Docker 398
EDA 399
Modeling 400
Tune Scaled GBM 401
Fit Model 402
Evaluate 402
adhoc_predict 403
JSON Workflow 404
Scale Input 404
adhoc_predict from Pickle 405
Scale Input 406
Exercises 406
Case Study Question 406
Learning Assessments 407
15. Data Engineering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Small Data 410
Dealing with Small Data Files 410
x | Table of Contents
Write a File 411
Read a File 411
Generator Pipeline to Read and Process Lines 411
Using YAML 412
Big Data 413
Big Data Tools, Components, and Platforms 415
Data Sources 415
Filesystems 416
Data Storage 417
Real-Time Streaming Ingestion 418
Case Study: Building a Homegrown Data Pipeline 419
Serverless Data Engineering 420
Using AWS Lambda with CloudWatch Events 421
Using Amazon CloudWatch Logging with AWS Lambda 421
Using AWS Lambda to Populate Amazon Simple Queue Service 422
Wiring Up CloudWatch Event Trigger 427
Creating Event-Driven Lambdas 428
Reading Amazon SQS Events from AWS Lambda 428
Conclusion 432
Exercises 433
Case Study Question 433
16. DevOps War Stories and Interviews. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
Film Studio Can’t Make Film 436
Game Studio Can’t Ship Game 438
Python Scripts Take 60 Seconds to Launch 440
Putting Out a Fire with a Cache and Intelligent Instrumentation 441
You’ll Automate Yourself Out of a Job! 442
DevOps Antipatterns 443
No Automated Build Server Antipattern 443
Flying Blind 444
Difficulties in Coordination as an Ongoing Accomplishment 444
No Teamwork 445
Interviews 451
Glenn Solomon 451
Andrew Nguyen 451
Gabriella Roman 453
Rigoberto Roche 454
Jonathan LaCour 456
Table of Contents | xi
Ville Tuulos 458
Joseph Reis 460
Teijo Holzer 461
Matt Harrison 463
Michael Foord 464
Recommendations 467
Exercises 468
Challenges 468
Capstone Project 469
Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
xii | Table of Contents
Preface
One time Noah was in the ocean, and a wave crashed on top of him and took his
breath away as it pulled him deeper into the sea. Just as he started to recover his
breath, another wave dropped on top. It extracted much of his remaining energy. It
pulled him even deeper into the ocean. Just as he started to recover, yet another wave
crashed down on top. The more he would fight the waves and the sea, the more
energy was drained. He seriously wondered if he would die at that moment. He
couldn’t breathe, his body ached, and he was terrified he was going to drown. Being
close to death helped him focus on the only thing that could save him, which was
conserving his energy and using the waves—not fighting them.
Being in a startup that doesn’t practice DevOps is a lot like that day at the beach.
There are production fires that burn for months; everything is manual, alerts wake
you up for days on end damaging your health. The only escape from this death spiral
is the DevOps way.
Do one right thing, then another, until you find clarity. First, set up a build server,
start testing your code, and automate manual tasks. Do something; it can be anything,
but have a “bias for action.” Do that first thing right and make sure it is automated.
A common trap in startups or any company is the search for superheroes. “We need a
performance engineer” because they will fix our performance problems. “We need a
Chief Revenue Officer” because they will fix all sales problems. “We need DevOps
engineers” because they will fix our deployment process.
At one company, Noah had a project that was over a year late, and the web applica‐
tion had been rewritten three times in multiple languages. This next release only
needed a “performance engineer” to get it finished. I remember being the only one
brave or stupid enough to say, “What is a performance engineer?” This engineer
made everything work at scale. He realized at that point that they were looking for a
superhero to save them. Superhero hiring syndrome is the best way to pick up on
something being very wrong on a new product or a new startup. No employee will
save a company unless they first save themselves.
xiii
At other companies, Noah heard similar things: “If we could only hire a senior Erlang
engineer,” or “If we could only hire someone to make us revenue,” or “If we could
only hire someone to teach us to be financially disciplined,” or “If we could only hire
a Swift developer,” etc. This hire is the last thing your startup or new product needs—
it needs to understand what it is doing wrong that only a superhero can save the day.
In the case of the company that wanted to hire a performance engineer, it turned out
that the real issue was inadequate technical supervision. The wrong people were in
charge (and verbally shouting down the people who could fix it). By removing a poor
performer, listening to an existing team member who knew how to fix the problem all
along, deleting that job listing, doing one right thing at a time, and inserting qualified
engineering management, the issue resolved itself without a superhero hire.
No one will save you at your startup; you and your team have to protect yourselves by
creating great teamwork, a great process, and believing in your organization. The sol‐
ution to the problem isn’t a new hire; it is being honest and mindful about the situa‐
tion you are in, how you got there, and doing one right thing at a time until you work
your way out. There is no superhero unless it is you.
Just like being in the ocean in a storm and slowly drowning, no one is going to save
you or the company unless it is you. You are the superhero your company needs, and
you might discover your coworkers are too.
There is a way out of the chaos, and this book can be your guide. Let’s get started.
What Does DevOps Mean to the Authors?
Many abstract concepts in the software industry are hard to define precisely. Cloud
Computing, Agile, and Big Data are good examples of topics that can have many defi‐
nitions depending on whom you talk to. Instead of strictly defining what DevOps is,
let’s use some phrases that show evidence DevOps is occurring:
• Two-way collaboration between Development and Operation teams.
• Turnaround of Ops tasks in minutes to hours, not days to weeks.
• Strong involvement from developers; otherwise, it’s back to Devs versus Ops.
• Operations people need development skills—at least Bash and Python.
• Developer people need operational skills—their responsibilities don’t end with
writing the code, but with deploying the system to production and monitoring
alerts.
• Automation, automation, automation: you can’t accurately automate without Dev
skills, and you can’t correctly automate without Ops skills
• Ideally: self-service for developers, at least in terms of deploying code.
xiv | Preface
• Can be achieved via CI/CD pipelines.
• GitOps.
• Bidirectional everything between Development and Operations (tooling, knowl‐
edge, etc.).
• Constant collaboration in design, implementation, deployment—and yes, auto‐
mation—can’t be successful without cooperation.
• If it isn’t automated, it’s broken.
• Cultural: Hierarchy < Process.
• Microservices > Monolithic.
• The continuous deployment system is the heart and soul of the software team.
• There are no superheroes.
• Continuous delivery isn’t an option; it is a mandate.
How to Use This Book
This book is useful in any order. You can randomly open any chapter you like, and
you should be able to find something helpful to apply to your job. If you are an expe‐
rienced Python programmer, you may want to skim Chapter 1. Likewise, if you are
interested in war stories, case studies, and interviews, you may want to read the
Chapter 16 first.
Conceptual Topics
The content is broken up into several conceptual topics. The first group is Python
Foundations, and it covers a brief introduction to the language as well as automating
text, writing command-line tools, and automating the file system.
Next up is Operations, which includes useful Linux utilities, package management,
build systems, monitoring and instrumentation, and automated testing. These are all
essential topics to master to become a competent DevOps practitioner.
Cloud Foundations are in the next section, and there are chapters on Cloud Comput‐
ing, Infrastructure as Code, Kubernetes, and Serverless. There is currently a crisis in
the software industry around finding enough talent trained in the Cloud. Mastering
this section will pay immediate dividends to both your salary and your career.
Next up is the Data section. Machine Learning Operations and Data Engineering are
both covered from the perspective of DevOps. There is also a full soup to nuts
machine learning project walkthrough that takes you through the building,
deploying, and operationalizing of a machine learning model using Flask, Sklearn,
Docker, and Kubernetes.
Preface | xv
The last section is Chapter 16 on case studies, interviews, and DevOps war stories.
This chapter makes for good bed time reading.
Python Foundations
• Chapter 1, Python Essentials for DevOps
• Chapter 2, Automating Files and the Filesystem
• Chapter 3, Working with the Command Line
Operations
• Chapter 4, Useful Linux Utilities
• Chapter 5, Package Management
• Chapter 6, Continuous Integration and Continuous Deployment
• Chapter 7, Monitoring and Logging
• Chapter 8, Pytest for DevOps
Cloud Foundations
• Chapter 9, Cloud Computing
• Chapter 10, Infrastructure as Code
• Chapter 11, Container Technologies: Docker and Docker Compose
• Chapter 12, Container Orchestration: Kubernetes
• Chapter 13, Serverless Technologies
Data
• Chapter 14, MLOps and Machine learning Engineering
• Chapter 15, Data Engineering
Case Studies
• Chapter 16, DevOps War Stories and Interviews
xvi | Preface
Conventions Used in This Book
The following typographical conventions are used in this book:
Italic
Indicates new terms, URLs, email addresses, filenames, and file extensions.
Constant width
Used for program listings, as well as within paragraphs to refer to program ele‐
ments such as variable or function names, databases, data types, environment
variables, statements, and keywords.
Constant width bold
Shows commands or other text that should be typed literally by the user.
Constant width italic
Shows text that should be replaced with user-supplied values or by values deter‐
mined by context.
This element signifies a tip or suggestion.
This element signifies a general note.
This element indicates a warning or caution.
Using Code Examples
Supplemental material (code examples, exercises, etc.) is available for download at
https://pythondevops.com. You can also view DevOps content related to the code in
the book at the Pragmatic AI Labs YouTube channel.
If you have a technical question for the authors or a problem using the code exam‐
ples, please email technical@pythondevops.com.
Preface | xvii
This book is here to help you get your job done. In general, if example code is offered
with this book, you may use it in your programs and documentation. You do not
need to contact us for permission unless you’re reproducing a significant portion of
the code. For example, writing a program that uses several chunks of code from this
book does not require permission. Selling or distributing examples from O’Reilly
books does require permission. Answering a question by citing this book and quoting
example code does not require permission. Incorporating a significant amount of
example code from this book into your product’s documentation does require
permission.
We appreciate, but generally do not require, attribution. An attribution usually
includes the title, author, publisher, and ISBN. For example: “Python for DevOps by
Noah Gift, Kennedy Behrman, Alfredo Deza, and Grig Gheorghiu. (O’Reilly). Copy‐
right 2020 Noah Gift, Kennedy Behrman, Alfredo Deza, Grig Gheorghiu,
978-1-492-05769-7.”
If you feel your use of code examples falls outside fair use or the permission given
above, feel free to contact us at permissions@oreilly.com.
O’Reilly Online Learning
For more than 40 years, O’Reilly Media has provided technol‐
ogy and business training, knowledge, and insight to help
companies succeed.
Our unique network of experts and innovators share their knowledge and expertise
through books, articles, and our online learning platform. O’Reilly’s online learning
platform gives you on-demand access to live training courses, in-depth learning
paths, interactive coding environments, and a vast collection of text and video from
O’Reilly and 200+ other publishers. For more information, please visit http://
oreilly.com.
How to Contact Us
Please address comments and questions concerning this book to the publisher:
O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)
xviii | Preface
We have a web page for this book, where we list errata, examples, and any additional
information. You can access this page at oreil.ly/python-for-devops.
Email bookquestions@oreilly.com to comment or ask technical questions about this
book.
For news and more information about our books and courses, see our website at
http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Acknowledgments
To start off, the authors would like to thank the two main technical reviewers of the
book:
Wes Novack is an architect and engineer specializing in public cloud systems and
web-scale SaaS applications. He designs, builds, and manages complex systems that
enable highly available infrastructure, continuous delivery pipelines, and rapid relea‐
ses within large, polyglot microservice ecosystems hosted on AWS and GCP. Wes
makes extensive use of languages, frameworks, and tools to define Infrastructure as
Code, drive automation, and eliminate toil. He is vocal in the tech community by par‐
ticipating in mentorship, workshops, and conferences, and he is also a Pluralsight
video course author. Wes is an advocate for the CALMS of DevOps; Culture, Auto‐
mation, Lean, Measurement, and Sharing. You can find him on Twitter @WesleyTech
or visit his personal blog.
Brad Andersen is a software engineer and architect. He has designed and developed
software professionally for 30 years. He works as a catalyst for change and innova‐
tion; he has assumed leadership and development roles across a spectrum from enter‐
prise organizations to startups. Brad is currently pursuing a master’s degree in data
science at the University of California, Berkeley. You can find more information on
Brad’s LinkedIn profile.
We would also like to thank Jeremy Yabrow and Colin B. Erdman for chipping in
with many great ideas and bits of feedback.
Noah
I would like to thank the coauthors of the book: Grig, Kennedy, and Alfredo. It was
incredible working with a team that was this effective.
Preface | xix
Kennedy
Thanks to my coauthors, it has been a pleasure to work with you. And thanks for the
patience and understanding of my family.
Alfredo
In 2010—nine years ago as of this writing—I landed my first software engineering
job. I was 31 years old with no college education and no previous engineering experi‐
ence. That job meant accepting a reduced salary and no health insurance. I learned a
lot, met amazing people, and gained expertise through relentless determination.
Throughout those years, it would’ve been impossible to get here without people
opening opportunities and pointing me in the right direction.
Thanks to Chris Benson, who saw that I was hungry for learning and kept finding
opportunities to have me around.
Thanks to Alejandro Cadavid, who realized that I could fix things nobody else
wanted to fix. You helped me get work when no one (including myself) thought I
could be useful.
Carlos Coll got me into programming and didn’t let me quit even when I asked him
to. Learning to program changed my life, and Carlos had the patience to push me to
learn and land my first program in production.
To Joni Benton, for believing in me and helping me land my first full-time job.
Thanks to Jonathan LaCour, an inspiring boss who continues to help me get to a bet‐
ter place. Your advice has always been invaluable to me.
Noah, thanks for your friendship and guidance you are a tremendous source of moti‐
vation to me. I always enjoy working together, like that one time when we rebuilt
infrastructure from scratch. Your patience and guidance when I had no idea about
Python was life-changing.
Lastly, a tremendous thanks to my family. My wife Claudia, who never doubts my
ability to learn and improve, and so generous and understanding of the time I spent
working toward this book. My children, Efrain, Ignacio, and Alana: I love you all.
Grig
My thanks to all creators of open source software. Without them, our jobs would be
so much more bleak and unfulfilling. Also thank you to all who blog and share your
knowledge freely. Lastly, I also wish to thank the coauthors of this book. It’s been a
really fun ride.
xx | Preface
CHAPTER 1
Python Essentials for DevOps
DevOps, the combination of software development with information technology
operations, has been a hot field during the last decade. Traditional boundaries among
software development, deployment, maintenance, and quality assurance have been
broken, enabling more integrated teams. Python has been a popular language both in
traditional IT operations and in DevOps due to its combination of flexibility, power,
and ease of use.
The Python programming language was publicly released in the early 1990s for use in
system administration. It has been a great success in this area and has gained wide
adoption. Python is a general-purpose programming language used in just about
every domain. The visual effects and the motion picture industries embraced it. More
recently, it has become the de facto language of data science and machine learning
(ML). It has been used across industries from aviation to bioinformatics. Python has
an extensive arsenal of tools to cover the wide-ranging needs of its users. Learning
the whole Python Standard Library (the capabilities that come with any Python
installation) would be a daunting task. Trying to learn all the third-party packages
that enliven the Python ecosystem would be an immense undertaking. The good
news is that you don’t need to do those things. You can become a powerful DevOps
practitioner by learning only a small subset of Python.
In this chapter, we draw on our decades of Python DevOps experience to teach only
the elements of the language that you need. These are the parts of Python DevOps
that are used daily. They form the essential toolbox to get things done. Once you have
these core concepts down, you can add more complicated tools, as you’ll see in later
chapters.
1
Installing and Running Python
If you want to try the code in this overview, you need Python 3.7 or later installed
(the latest release is 3.8.0 as of this writing) and access to a shell. In macOS X, Win‐
dows, and most Linux distributions, you can open the terminal application to access a
shell. To see what version of Python you are using, open a shell, and type python
--version:
$ python --version
Python 3.8.0
Python installers can be downloaded directly from the Python.org website. Alterna‐
tively, you can use a package manager such as Apt, RPM, MacPorts, Homebrew,
Chocolatey, or many others.
The Python Shell
The simplest way to run Python is to use the built-in interactive interpreter. Just type
python in a shell. You can then interactively run Python statements. Type exit() to
exit the shell.
$ python
Python 3.8.0 (default, Sep 23 2018, 09:47:03)
[Clang 9.0.0 (clang-900.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 + 2
3
>>> exit()
Python scripts
Python code runs from a file with the .py extension:
# This is my first Python script
print('Hello world!')
Save this code to a file named hello.py. To invoke the script, in a shell run python
followed by the filename:
$ python hello.py
Hello world!
Python scripts are how most production Python code runs.
IPython
Besides the built-in interactive shell, several third-party interactive shells run Python
code. One of the most popular is IPython. IPython offers introspection (the ability to
dynamically get information about objects), syntax highlighting, special magic com‐
mands (which we touch on later in this chapter), and many more features, making it
2 | Chapter 1: Python Essentials for DevOps
a pleasure to use for exploring Python. To install IPython, use the Python package
manager, pip:
$ pip install ipython
Running is similar to running the built-in interactive shell described in the previous
section:
$ ipython
Python 3.8.0 (default, Sep 23 2018, 09:47:03)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: print('Hello')
Hello
In [2]: exit()
Jupyter Notebooks
A spin-off from the iPython project, the Jupyter project allows documents containing
text, code, and visualizations. These documents are powerful tools for combining
running code, output, and formatted text. Jupyter enables the delivery of documenta‐
tion along with the code. It has achieved widespread popularity, especially in the data
science world. Here is how to install and run Jupyter notebooks:
$ pip install jupyter
$ jupyter notebook
This command opens a web browser tab showing the current working directory.
From here, you can open existing notebooks in the current project or create new
ones.
Procedural Programming
If you’ve been around programming at all, you’ve probably heard terms like object-
oriented programming (OOP) and functional programming. These are different
architectural paradigms used to organize programs. One of the most basic paradigms,
procedural programming, is an excellent place to start. Procedural programming is the
issuing of instructions to a computer in an ordered sequence:
>>> i = 3
>>> j = i +1
>>> i + j
7
As you can see in this example, there are three statements that are executed in order
from the first line to the last. Each statement uses the state produced by the previous
ones. In this case, the first statement assigns the value 3 to a variable named i. In the
second statement, this variable’s value is used to assign a value to a variable named j,
Procedural Programming | 3
and in the third statement, the values from both variables are added together. Don’t
worry about the details of these statements yet; notice that they are executed in order
and rely on the state created by the previous statements.
Variables
A variable is a name that points to some value. In the previous example, the variables
are i and j . Variables in Python can be assigned to new values:
>>> dog_name = 'spot'
>>> dog_name
'spot'
>>> dog_name = 'rex'
>>> dog_name
'rex'
>>> dog_name = 't-' + dog_name
>>> dog_name
't-rex'
>>>
Python variables use dynamic typing. In practice, this means that they can be reas‐
signed to values of different types or classes:
>>> big = 'large'
>>> big
'large'
>>> big = 1000*1000
>>> big
1000000
>>> big = {}
>>> big
{}
>>>
Here the same variable is set to a string, a number, and a dictionary. Variables can be
reassigned to values of any type.
Basic Math
Basic math operations such as addition, subtraction, multiplication, and division can
all be performed using built-in math operators:
>>> 1 + 1
2
>>> 3 - 4
–1
>>> 2*5
10
>>> 2/3
0.6666666666666666
4 | Chapter 1: Python Essentials for DevOps
Note that a // symbol is for integer division. The symbol ** creates an exponent, and
% is the modulo operator:
>>> 5/2
2.5
>>> 5//2
2
>>> 3**2
9
>>> 5%2
1
Comments
Comments are text ignored by the Python interpreter. They are useful for documen‐
tation of code and can be mined by some services to provide standalone documenta‐
tion. Single-line comments are delineated by prepending with #. A single-line
comment can start at the beginning of a line, or at any point thereafter. Everything
after the # is part of the comment until a new line break occurs:
# This is a comment
1 + 1 # This comment follows a statement
Multiline comments are enclosed themselves in blocks beginning and ending with
either """ or ''':
"""
This statement is a block comment.
It can run for multiple lines
"""
'''
This statement is also a block comment
'''
Built-in Functions
Functions are statements grouped as a unit. You invoke a function by typing the func‐
tion name, followed by parentheses. If the function takes arguments, the arguments
appear within the parentheses. Python has many built-in functions. Two of the most
widely used built-in functions are print and range.
Print
The print function produces output that a user of a program can view. It is less rele‐
vant in interactive environments but is a fundamental tool when writing Python
scripts. In the previous example, the argument to the print function is written as
output when the script runs:
Procedural Programming | 5
# This is my first Python script
print("Hello world!")
$ python hello.py
Hello world!
print can be used to see the value of a variable or to give feedback as to the state of a
program. print generally outputs the standard output stream and is visible as pro‐
gram output in a shell.
Range
Though range is a built-in function, it is technically not a function at all. It is a type
representing a sequence of numbers. When calling the range() constructor, an object
representing a sequence of numbers is returned. Range objects count through a
sequence of numbers. The range function takes up to three integer arguments. If only
one argument appears, then the sequence is represented by the numbers from zero up
to, but not including, that number. If a second argument appears, it represents the
starting point, rather than the default of starting from 0. The third argument can be
used to specify the step distance, and it defaults to 1.
>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5, 10))
[5, 6, 7, 8, 9]
>>> list(range(5, 10, 3))
[5, 8]
>>>
range maintains a small memory footprint, even over extended sequences, as it only
stores the start, stop, and step values. The range function can iterate through long
sequences of numbers without performance constraints.
Execution Control
Python has many constructs to control the flow of statement execution. You can
group statements you wish to run together as a block of code. These blocks can be
run multiple times using for and while loops or only run under certain conditions
using if statements, while loops, or try-except blocks. Using these constructs is the
first step to taking advantage of the power of programming. Different languages
demarcate blocks of code using different conventions. Many languages with syntax
similar to the C language (a very influential language used in writing Unix) use curly
brackets around a group of statements to define a block. In Python, indentation is
used to indicate a block. Statements are grouped by indentation into blocks that exe‐
cute as a unit.
6 | Chapter 1: Python Essentials for DevOps
The Python interpreter does not care if you use tabs or spaces to
indent, as long as you are consistent. The Python style guide,
PEP-8, however, recommends using four whitespaces for each level
of indentation.
if/elif/else
if/elif/else statements are common ways to branch between decisions in code. A
block directly after an if statement runs if that statement evaluates to True:
>>> i = 45
>>> if i == 45:
... print('i is 45')
...
...
i is 45
>>>
Here we used the == operator, which returns True if items are equal and False if not.
Optionally, this block can follow an elif or else statement with an accompanying
block. In the case of an elif statement, this block only executes if the elif evaluates
to True:
>>> i = 35
>>> if i == 45:
... print('i is 45')
... elif i == 35:
... print('i is 35')
...
...
i is 35
>>>
Multiple elif loops can append together. If you are familiar with switch statements
in other languages, this simulates that same behavior of choosing from multiple
choices. Adding an else statement at the end runs a block if none of the other condi‐
tions evaluate as True:
>>> i = 0
>>> if i == 45:
... print('i is 45')
... elif i == 35:
... print('i is 35')
... elif i > 10:
... print('i is greater than 10')
... elif i%3 == 0:
... print('i is a multiple of 3')
... else:
... print('I don't know much about i...')
...
Execution Control | 7
...
i is a multiple of 3
>>>
You can nest if statements, creating blocks containing if statements that only exe‐
cute if an outer if statement is True:
>>> cat = 'spot'
>>> if 's' in cat:
... print("Found an 's' in a cat")
... if cat == 'Sheba':
... print("I found Sheba")
... else:
... print("Some other cat")
... else:
... print(" a cat without 's'")
...
...
Found an 's' in a cat
Some other cat
>>>
for Loops
for loops allow you to repeat a block of statements (a code block) once for each
member of a sequence (ordered group of items). As you iterate through the sequence,
the current item can be accessed by the code block. One of most common uses of
loops is to iterate through a range object to do a task a set number of times:
>>> for i in range(10):
... x = i*2
... print(x)
...
...
0
2
4
6
8
10
12
14
16
18
>>>
In this example, our block of code is as follows:
... x = i*2
... print(x)
8 | Chapter 1: Python Essentials for DevOps
We repeat this code 10 times, each time assigning the variable i to the next number in
the sequence of integers from 0–9. for loops can be used to iterate through any of the
Python sequence types. You will see these later in this chapter.
continue
The continue statement skips a step in a loop, jumping to the next item in the
sequence:
>>> for i in range(6):
... if i == 3:
... continue
... print(i)
...
...
0
1
2
4
5
>>>
while Loops
while loops repeat a block as long as a condition evaluates to True:
>>> count = 0
>>> while count < 3:
... print(f"The count is {count}")
... count += 1
...
...
The count is 0
The count is 1
The count is 2
>>>
It is essential to define a way for your loop to end. Otherwise, you will be stuck in the
loop until your program crashes. One way to handle this is to define your conditional
statement such that it eventually evaluates to False. An alternative pattern uses the
break statement to exit a loop using a nested conditional:
>>> count = 0
>>> while True:
... print(f"The count is {count}")
... if count > 5:
... break
... count += 1
...
...
The count is 0
while Loops | 9
The count is 1
The count is 2
The count is 3
The count is 4
The count is 5
The count is 6
>>>
Handling Exceptions
Exceptions are a type of error causing your program to crash if not handled (caught).
Catching them with a try-except block allows the program to continue. These
blocks are created by indenting the block in which the exception might be raised,
putting a try statement before it and an except statement after it, followed by a code
block that should run when the error occurs:
>>> thinkers = ['Plato', 'PlayDo', 'Gumby']
>>> while True:
... try:
... thinker = thinkers.pop()
... print(thinker)
... except IndexError as e:
... print("We tried to pop too many thinkers")
... print(e)
... break
...
...
...
Gumby
PlayDo
Plato
We tried to pop too many thinkers
pop from empty list
>>>
There are many built-in exceptions, such as IOError, KeyError, and ImportError.
Many third-party packages also define their own exception classes. They indicate that
something has gone very wrong, so it only pays to catch them if you are confident
that the problem won’t be fatal to your software. You can specify explicitly which
exception type you will catch. Ideally, you should catch the exact exception type (in
our example, this was the exception IndexError).
Built-in Objects
In this overview, we will not be covering OOP. The Python language, however, comes
with quite a few built-in classes.
10 | Chapter 1: Python Essentials for DevOps
What Is an Object?
In OOP, data or state and functionality appear together. The essential concepts to
understand when working with objects are class instantiation (creating objects from
classes) and dot syntax (the syntax for accessing an object’s attributes and methods).
A class defines attributes and methods shared by its objects. Think of it as the techni‐
cal drawing of a car model. The class can then be instantiated to create an instance.
The instance, or object, is a single car built based on those drawings.
>>> # Define a class for fancy defining fancy cars
>>> class FancyCar():
... pass
...
>>> type(FancyCar)
<class 'type'>
>>> # Instantiate a fancy car
>>> my_car = FancyCar()
>>> type(my_car)
<class '__main__.FancyCar'>
You don’t need to worry about creating your own classes at this point. Just under‐
stand that each object is an instantiation of a class.
Object Methods and Attributes
Objects store data in attributes. These attributes are variables attached to the object or
object class. Objects define functionality in object methods (methods defined for all
objects in a class) and class methods (methods attached to a class and shared by all
objects in the class), which are functions attached to the object.
In Python documentation, functions attached to objects and classes
are referred to as methods.
These functions have access to the object’s attributes and can modify and use the
object’s data. To call an object’s method or access one of its attributes, we use dot
syntax:
>>> # Define a class for fancy defining fancy cars
>>> class FancyCar():
... # Add a class variable
... wheels = 4
... # Add a method
... def driveFast(self):
... print("Driving so fast")
...
...
Built-in Objects | 11
...
>>> # Instantiate a fancy car
>>> my_car = FancyCar()
>>> # Access the class attribute
>>> my_car.wheels
4
>>> # Invoke the method
>>> my_car.driveFast()
Driving so fast
>>>
So here our FancyCar class defines a method called driveFast and an attribute
wheels. When you instantiate an instance of FancyCar named my_car, you can access
the attribute and invoke the method using the dot syntax.
Sequences
Sequences are a family of built-in types, including the list, tuple, range, string, and
binary types. Sequences represent ordered and finite collections of items.
Sequence operations
There are many operations that work across all of the types of sequences. We cover
some of the most commonly used operations here.
You can use the in and not in operators to test whether or not an item exists in a
sequence:
>>> 2 in [1,2,3]
True
>>> 'a' not in 'cat'
False
>>> 10 in range(12)
True
>>> 10 not in range(2, 4)
True
You can reference the contents of a sequence by using its index number. To access the
item at some index, use square brackets with the index number as an argument. The
first item indexed is at position 0, the second at 1, and so forth up to the number one
less than the number of items:
>>> my_sequence = 'Bill Cheatham'
>>> my_sequence[0]
'B'
>>> my_sequence[2]
'l'
>>> my_sequence[12]
'm'
12 | Chapter 1: Python Essentials for DevOps
Indexing can appear from the end of a sequence rather than from the front using neg‐
ative numbers. The last item has the index of –1, the second to last has the index of –
2, and so forth:
>>> my_sequence = "Bill Cheatham"
>>> my_sequence[–1]
'm'
>>> my_sequence[–2]
'a'
>>> my_sequence[–13]
'B'
The index of an item results from the index method. By default, it returns the index
of the first occurrence of the item, but optional arguments can define a subrange in
which to search:
>>> my_sequence = "Bill Cheatham"
>>> my_sequence.index('C')
5
>>> my_sequence.index('a')
8
>>> my_sequence.index('a',9, 12)
11
>>> my_sequence[11]
'a'
>>>
You can produce a new sequence from a sequence using slicing. A slice appears by
invoking a sequence with brackets containing optional start, stop, and step
arguments:
my_sequence[start:stop:step]
start is the index of the first item to use in the new sequence, stop the first index
beyond that point, and step, the distance between items. These arguments are all
optional and are replaced with default values if omitted. This statement produces a
copy of the original sequence. The default value for start is 0, for stop is the length
of the sequence, and for step is 1. Note that if the step does not appear, the corre‐
sponding : can also be dropped:
>>> my_sequence = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_sequence[2:5]
['c', 'd', 'e']
>>> my_sequence[:5]
['a', 'b', 'c', 'd', 'e']
>>> my_sequence[3:]
['d', 'e', 'f', 'g']
>>>
Built-in Objects | 13
Negative numbers can be used to index backward:
>>> my_sequence[–6:]
['b', 'c', 'd', 'e', 'f', 'g']
>>> my_sequence[3:–1]
['d', 'e', 'f']
>>>
Sequences share many operations for getting information about them and their con‐
tents. len returns the length of the sequence, min the smallest member, max the larg‐
est, and count the number of a particular item. min and max work only on sequences
with items that are comparable. Remember that these work with any sequence type:
>>> my_sequence = [0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4]
>>> len(my_sequence)
12
>>> min(my_sequence)
0
>>> max(my_sequence)
4
>>> my_sequence.count(1)
3
>>>
Lists
Lists, one of the most commonly used Python data structures, represent an ordered
collection of items of any type. The use of square brackets indicates a list syntax.
The function list() can be used to create an empty list or a list based on another
finite iterable object (such as another sequence):
>>> list()
[]
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list("Henry Miller")
['H', 'e', 'n', 'r', 'y', ' ', 'M', 'i', 'l', 'l', 'e', 'r']
>>>
Lists created by using square brackets directly are the most common form. Items in
the list need to be enumerated explicitly in this case. Remember that the items in a list
can be of different types:
>>> empty = []
>>> empty
[]
>>> nine = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> nine
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> mixed = [0, 'a', empty, 'WheelHoss']
>>> mixed
14 | Chapter 1: Python Essentials for DevOps
[0, 'a', [], 'WheelHoss']
>>>
The most efficient way to add a single item to a list is to append the item to the end of
the list. A less efficient method, insert, allows you to insert an item at the index posi‐
tion of your choice:
>>> pies = ['cherry', 'apple']
>>> pies
['cherry', 'apple']
>>> pies.append('rhubarb')
>>> pies
['cherry', 'apple', 'rhubarb']
>>> pies.insert(1, 'cream')
>>> pies
['cherry', 'cream', 'apple', 'rhubarb']
>>>
The contents of one list can be added to another using the extend method:
>>> pies
['cherry', 'cream', 'apple', 'rhubarb']
>>> desserts = ['cookies', 'paste']
>>> desserts
['cookies', 'paste']
>>> desserts.extend(pies)
>>> desserts
['cookies', 'paste', 'cherry', 'cream', 'apple', 'rhubarb']
>>>
The most efficient and common way of removing the last item from a list and return‐
ing its value is to pop it. An index argument can be supplied to this method, removing
and returning the item at that index. This technique is less efficient, as the list needs
to be re-indexed:
>>> pies
['cherry', 'cream', 'apple', 'rhubarb']
>>> pies.pop()
'rhubarb'
>>> pies
['cherry', 'cream', 'apple']
>>> pies.pop(1)
'cream'
>>> pies
['cherry', 'apple']
There is also a remove method, which removes the first occurrence of an item.
>>> pies.remove('apple')
>>> pies
['cherry']
>>>
Built-in Objects | 15
One of the most potent and idiomatic Python features, list comprehensions, allows
you to use the functionality of a for loop in a single line. Let’s look at a simple exam‐
ple, starting with a for loop squaring all of the numbers from 0–9 and appending
them to a list:
>>> squares = []
>>> for i in range(10):
... squared = i*i
... squares.append(squared)
...
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
In order to replace this with a list comprehension, we do the following:
>>> squares = [i*i for i in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
Note that the functionality of the inner block is put first, followed by the for state‐
ment. You can also add conditionals to list comprehensions, filtering the results:
>>> squares = [i*i for i in range(10) if i%2==0]
>>> squares
[0, 4, 16, 36, 64]
>>>
Other techniques for list comprehensions include nesting them and using multiple
variables, but the more straightforward form shown here is the most common.
Strings
The string sequence type is a collection of ordered characters surrounded by quota‐
tion marks. As of Python 3, strings default to using UTF-8 encoding.
You can create strings either by using the string constructor method, str(), or by
directly enclosing the text in quotation marks:
>>> str()
''
>>> "some new string!"
'some new string!'
>>> 'or with single quotes'
'or with single quotes'
The string constructor can be used to make strings from other objects:
>>> my_list = list()
>>> str(my_list)
'[]'
16 | Chapter 1: Python Essentials for DevOps
You can create multiline strings by using triple quotes around the content:
>>> multi_line = """This is a
... multi-line string,
... which includes linebreaks.
... """
>>> print(multi_line)
This is a
multi-line string,
which includes linebreaks.
>>>
In addition to the methods shared by all sequences, strings have quite a few methods
distinct to their class.
It is relatively common for user text to have trailing or leading whitespace. If some‐
one types " yes " in a form instead of “yes” you usually want to treat them the same.
Python strings have a strip method just for this case. It returns a string with the
whitespace removed from the beginning and end. There are also methods to remove
the whitespace from only the right or left side of the string:
>>> input = " I want more "
>>> input.strip()
'I want more'
>>> input.rstrip()
' I want more'
>>> input.lstrip()
'I want more '
On the other hand, if you want to add padding to a string, you can use the ljust or
rjust methods. Either one pads with whitespace by default, or takes a character
argument:
>>> output = 'Barry'
>>> output.ljust(10)
'Barry '
>>> output.rjust(10, '*')
'*****Barry'
Sometimes you want to break a string up into a list of substrings. Perhaps you have a
sentence you want to turn into a list of words, or a string of words separated by com‐
mas. The split method breaks a string into a list of strings. By default, it uses white‐
space as the token to make the breaks. An optional argument can be used to add in
another character where the split can break:
>>> text = "Mary had a little lamb"
>>> text.split()
['Mary', 'had', 'a', 'little', 'lamb']
>>> url = "gt.motomomo.io/v2/api/asset/143"
>>> url.split('/')
['gt.motomomo.io', 'v2', 'api', 'asset', '143']
Built-in Objects | 17
You can easily create a new string from a sequence of strings and join them into a
single string. This method inserts a string as a separator between a list of other
strings:
>>> items = ['cow', 'milk', 'bread', 'butter']
>>> " and ".join(items)
'cow and milk and bread and butter'
Changing the case of text is a common occurrence, whether it is making the case uni‐
form for comparison or changing in preparation for user consumption. Python
strings have several methods to make this an easy process:
>>> name = "bill monroe"
>>> name.capitalize()
'Bill monroe'
>>> name.upper()
'BILL MONROE'
>>> name.title()
'Bill Monroe'
>>> name.swapcase()
'BILL MONROE'
>>> name = "BILL MONROE"
>>> name.lower()
'bill monroe'
Python also provides methods to understand a string’s content. Whether it’s checking
the case of the text, or seeing if it represents a number, there are quite a few built-in
methods for interrogation. Here are just a few of the most commonly used methods:
>>> "William".startswith('W')
True
>>> "William".startswith('Bill')
False
>>> "Molly".endswith('olly')
True
>>> "abc123".isalnum()
True
>>> "abc123".isalpha()
False
>>> "abc".isalnum()
True
>>> "123".isnumeric()
True
>>> "Sandy".istitle()
True
>>> "Sandy".islower()
False
>>> "SANDY".isupper()
True
18 | Chapter 1: Python Essentials for DevOps
You can insert content into a string and control its format at runtime. Your program
can use the values of variables or other calculated content in strings. This approach is
used in both creating user-consumed text and for writing software logs.
The older form of string formatting in Python comes from the C language printf
function. You can use the modulus operator, %, to insert formatted values into a
string. This technique applies to the form string % values, where values can be a
single nontuple or a tuple of multiple values. The string itself must have a conversion
specifier for each value. The conversion specifier, at a minimum, starts with a % and is
followed by a character representing the type of value inserted:
>>> "%s + %s = %s" % (1, 2, "Three")
'1 + 2 = Three'
>>>
Additional format arguments include the conversion specifier. For example, you can
control the number of places a float, %f, prints:
>>> "%.3f" % 1.234567
'1.235'
This mechanism for string formatting was the dominant one in Python for years, and
you encounter it in legacy code. This approach offers some compelling features, such
as sharing syntax with other languages. It also has some pitfalls. In particular, due to
the use of a sequence to hold the arguments, errors related to displaying tuple and
dict objects are common. We recommend adopting newer formatting options, such
as the string format method, template strings, and f-strings, to both avoid these
errors and increase the simplicity and readability of your code.
Python 3 introduced a new way of formatting strings using the string method format.
This way of formatting has been backported to Python 2 as well. This specification
uses curly brackets in the string to indicate replacement fields rather than the
modulus-based conversion specifiers of the old-style formatting. The insert values
become arguments to the string format method. The order of the arguments deter‐
mines their placement order in the target string:
>>> '{} comes before {}'.format('first', 'second')
'first comes before second'
>>>
You can specify index numbers in the brackets to insert values in an order different
than that in the argument list. You can also repeat a value by specifying the same
index number in multiple replacement fields:
>>> '{1} comes after {0}, but {1} comes before {2}'.format('first',
'second',
'third')
'second comes after first, but second comes before third'
>>>
Built-in Objects | 19
An even more powerful feature is that the insert values can be specified by name:
>>> '''{country} is an island.
... {country} is off of the coast of
... {continent} in the {ocean}'''.format(ocean='Indian Ocean',
... continent='Africa',
... country='Madagascar')
'Madagascar is an island.
Madagascar is off of the coast of
Africa in the Indian Ocean'
Here a dict works to supply the key values for name-based replacement fields:
>>> values = {'first': 'Bill', 'last': 'Bailey'}
>>> "Won't you come home {first} {last}?".format(**values)
"Won't you come home Bill Bailey?"
You can also specify format specification arguments. Here they add left and right pad‐
ding using > and <. In the second example, we specify a character to use in the
padding:
>>> text = "|{0:>22}||{0:<22}|"
>>> text.format('O','O')
'| O||O |'
>>> text = "|{0:<>22}||{0:><22}|"
>>> text.format('O','O')
'|<<<<<<<<<<<<<<<<<<<<<O||O>>>>>>>>>>>>>>>>>>>>>|'
Format specifications are done using the format specification mini-language. Our
topic also uses another type of language called f-strings.
Python f-strings use the same formatting language as the format method, but offer a
more straightforward and intuitive mechanism for using them. f-strings are prepen‐
ded with either f or F before the first quotation mark. Like the format string previ‐
ously described, f-strings use curly braces to demarcate replacement fields. In an f-
string, however, the content of the replacement field is an expression. This approach
means it can refer to variables defined in the current scope or involve calculations:
>>> a = 1
>>> b = 2
>>> f"a is {a}, b is {b}. Adding them results in {a + b}"
'a is 1, b is 2. Adding them results in 3'
As in format strings, format specifications in f-strings happen within the curly brack‐
ets after the value expression and start with a ::
>>> count = 43
>>> f"|{count:5d}"
'| 43'
The value expression can contain nested expressions, referencing variables, and
expressions in the construction of the parent expression:
20 | Chapter 1: Python Essentials for DevOps
>>> padding = 10
>>> f"|{count:{padding}d}"
'| 43'
We highly recommend using f-strings for the majority of your
string formatting. They combine the power of the specification
mini-language with a simple and intuitive syntax.
Template strings are designed to offer a straightforward string substitution mecha‐
nism. These built-in methods work for tasks such as internationalization, where sim‐
ple word substitutions are necessary. They use $ as a substitution character, with
optional curly braces surrounding them. The characters directly following the $ iden‐
tify the value to be inserted. When the substitute method of the string template exe‐
cutes, these names are used to assign values.
Built-in types and functions are available whenever you run Python
code, but to access the broader world of functionality available in
the Python ecosystem, you need to use the import statement. This
approach lets you add functionality from the Python Standard
Library or third-party services into your environment. You can
selectively import parts of a package by using the from keyword:
>>> from string import Template
>>> greeting = Template("$hello Mark Anthony")
>>> greeting.substitute(hello="Bonjour")
'Bonjour Mark Anthony'
>>> greeting.substitute(hello="Zdravstvuyte")
'Zdravstvuyte Mark Anthony'
>>> greeting.substitute(hello="Nǐn hǎo")
'Nǐn hǎo Mark Anthony'
Dicts
Aside from strings and lists, dicts may be the most used of the Python built-in classes.
A dict is a mapping of keys to values. The lookup of any particular value using a key is
highly efficient and fast. The keys can be strings, numbers, custom objects, or any
other nonmutable type.
A mutable object is one whose contents can change in place. Lists
are a primary example; the contents of the list can change without
the list’s identity changing. Strings are not mutable. You create a
new string each time you change the contents of an existing one.
Built-in Objects | 21
Dicts are represented as comma–separated key/value pairs surrounded by curly
braces. The key/value pairs consist of a key, a colon (:), and then a value.
You can create a dict object using the dict() constructor. With no arguments, it cre‐
ates an empty dict. It takes a sequence of key/value pairs as an argument as well:
>>> map = dict()
>>> type(map)
<class 'dict'>
>>> map
{}
>>> kv_list = [['key-1', 'value-1'], ['key-2', 'value-2']]
>>> dict(kv_list)
{'key-1': 'value-1', 'key-2': 'value-2'}
You can also create a dict directly using curly braces:
>>> map = {'key-1': 'value-1', 'key-2': 'value-2'}
>>> map
{'key-1': 'value-1', 'key-2': 'value-2'}
You can access the value associated with a key using square bracket syntax:
>>> map['key-1']
'value-1'
>>> map['key-2']
'value-2'
You can use the same syntax to set a value. If the key is not in the dict, it adds as a
new entry. If it already exists, the value changes to the new value:
>>> map
{'key-1': 'value-1', 'key-2': 'value-2'}
>>> map['key-3'] = 'value-3'
>>> map
{'key-1': 'value-1', 'key-2': 'value-2', 'key-3': 'value-3'}
>>> map['key-1'] = 13
>>> map
{'key-1': 13, 'key-2': 'value-2', 'key-3': 'value-3'}
If you try to access a key that has not been defined in a dict, a KeyError exception will
be thrown:
>>> map['key-4']
Traceback (most recent call last):
File "<input>", line 1, in <module>
map['key-4']
KeyError: 'key-4'
You can check to see if the key exists in a dict using the in syntax we saw with
sequences. In the case of dicts, it checks for the existence of keys:
>>> if 'key-4' in map:
... print(map['key-4'])
22 | Chapter 1: Python Essentials for DevOps
... else:
... print('key-4 not there')
...
...
key-4 not there
A more intuitive solution is to use the get() method. If you have not defined a key in
a dict, it returns a supplied default value. If you have not supplied a default value, it
returns None:
>>> map.get('key-4', 'default-value')
'default-value'
Use del to remove a key-value pair from a dict:
>>> del(map['key-1'])
>>> map
{'key-2': 'value-2', 'key-3': 'value-3'}
The keys() method returns a dict_keys object with the dict’s keys. The values()
method returns an dict_values object, and the items() method returns key-value
pairs. This last method is useful for iterating through the contents of a dict:
>>> map.keys()
dict_keys(['key-1', 'key-2'])
>>> map.values()
dict_values(['value-1', 'value-2'])
>>> for key, value in map.items():
... print(f"{key}: {value}")
...
...
key-1: value-1
key-2: value-2
Similar to list comprehensions, dict comprehensions are one-line statements return‐
ing a dict by iterating through a sequence:
>>> letters = 'abcde'
>>> # mapping individual letters to their upper-case representations
>>> cap_map = {x: x.upper() for x in letters}
>>> cap_map['b']
'B'
Functions
You have seen some Python built-in functions already. Now move on to writing your
own. Remember, a function is a mechanism for encapsulating a block of code. You
can repeat the behavior of this block in multiple spots without having to duplicate the
code. Your code will be better organized, more testable, maintainable, and easier to
understand.
Functions | 23
Another Random Scribd Document
with Unrelated Content
The offer of mercy was made a fourth time. A young Indian stepped
out and received additional assurance that no harm should come if
they surrendered. He went back into the cave and presently
reappeared with another young warrior, supporting between them
the tall, splendid figure of brave old American Horse. He had been
shot through the bowels, and his intestines protruded from the
wound. He was suffering frightful agony, and was biting hard upon a
piece of wood to control himself. He handed his gun to Crook and
gave up the contest. The surgeons with the command did everything
they possibly could for him, but his wound was beyond human skill.
That night, surrounded by his wives and children, he died, as
stoically and as bravely as he had lived.
Inside the cave the rocky walls were cut and scored by the rain of
bullets which had been poured into it, and lying on the floor were
the bodies of the two Indian warriors, together with a woman and a
child, who had been killed. The soldiers had not known, until the
squaws came out, that there were any women or children there. The
little band had sold their lives dearly. Even the women had used
guns, and had displayed all the bravery and courage of the Sioux.
Too late Crazy Horse, with some six hundred warriors, appeared on
the scene. Imagining he had only to deal with Mills’ small force, he
galloped gallantly forward to the attack at about five o’clock. He was
greatly astonished at the number of antagonists developed thereby.
He retired to the top of the buttes, and the soldiers in gallant style
dashed after him. They scaled the cliffs, finally gaining the level
plateau. Crazy Horse made one or two attempts to break through
the line, but it was impossible, and seeing himself greatly
outnumbered, he wisely retired, having sustained some loss.
The battle was one of the most picturesque ever fought in the West.
Crook and his officers stood in the camp, the center of a vast
amphitheater ringed with fire, up the sides of which the soldiers
steadily climbed to get at the Indians, silhouetted in all their war
finery against the sky. The loss of life on either side was not great,
but the capture of the village and the provisions which had been
accumulated for the winter was a serious one.
In the camp were discovered many articles that had belonged to the
Seventh Cavalry—a guidon, money, one of Captain Keogh’s
gauntlets, marked with his name, orderly books, saddles, etc.
Among other things, were letters written by officers and soldiers to
friends in the East, some of them still sealed and ready for mailing.
They must have come like voices from the dead when they reached
those to whom they had been written.
99. One of the scouts killed in this battle was a great admirer of
Buffalo Bill, whose manners, methods, and appearance he
aped as well as he could. He rejoiced in an unfortunate
sobriquet, which was received in this wise: General Sheridan,
seeking Buffalo Bill to lead a hunting expedition on one
occasion, was met by this swaggerer, with the remark that
Buffalo Bill was gone away, and when Buffalo Bill was gone he
was Buffalo Bill himself. “The h—l you are!” said Sheridan
contemptuously. “Buffalo Chip, you mean!” The poor braggart
never got away from the name of “Buffalo Chip Charlie.” He
was a brave man for all his vanity, and the soldiers were sorry
enough for their mockery when they buried him that night at
the foot of the buttes, where he had fallen in the attack on the
cave.
C
CHAPTER TEN
A Decisive Blow
I. Mackenzie’s Winter Battle
rook now gave over the pursuit, and returned to Fort
Fetterman to organize a winter campaign. This expedition
was one of the best equipped that ever started on an Indian
campaign. It contained all arms of the service, with an abundance of
everything necessary to success. To follow its marches to the Big
Horn Range would reveal little of interest; but late in November it
was learned, from a captured Cheyenne, that the principal Cheyenne
village was located in a cañon through which flowed one of the main
sources of Crazy Woman’s Fork of the Powder River. Colonel Ranald
S. Mackenzie was ordered, with the Indian scouts and ten troops of
cavalry from the Second, Fourth, and Fifth regiments, to find and
destroy the village.
The Cheyennes were not so numerous as the Sioux, and the greater
number of their allies has sometimes caused people to minimize the
quality of the Cheyennes; but no braver, more magnificent fighters
ever lived than this same tribe. They had some of the Homeric
qualities of the ancient Greeks. I believe it will generally be admitted
that they were the finest of the Plains Indians. They were foemen
worthy Mackenzie’s or anybody else’s steel. The battle which ensued
was in some respects one of the most terrible in Western history,
and in its results exemplified, as few others have done, the horrible
character of the war. It was, perhaps, as great a contribution to the
downfall of the Sioux as any single incident that occurred.
Mackenzie’s men left the main encampment on the 23d of
November. The ground was covered with snow. The weather was
arctic in its severity. The scouts and friendly Indians—Pawnees,
Crows, Shoshones, the hereditary enemies of the Cheyennes,
including certain Cheyennes also who had entered the service of the
United States[100]
—had located the camp in Willow Creek Cañon.
Some of the Indians had kept the camp under observation while
Mackenzie brought up his troops. He had seven hundred and fifty
cavalrymen and three hundred and fifty Indians. Halting at the
mouth of the cañon, which he reached on the night of the 24th, he
resolved to await the still hours before the break of day the next
morning before delivering his attack.
The cañon was a gloomy gorge in the Big Horn Mountains. A swift,
ice-bound river rushed over the rocks between precipitous walls,
which soared into the sky for perhaps a thousand feet on either side.
Numberless icy brooks poured their contents into the main stream
through lateral cañons scarcely less forbidding in their appearance
than the main one, and which made the trail of the creek almost
impossible. Here and there the cañon widened, and in one of these
open places the Cheyennes, under the leadership of Dull Knife, had
pitched their camp. They fondly believed the place impregnable—as,
indeed, with careful guarding it would have been. The greatest
precaution was taken by Mackenzie to prevent his men from making
any noise. They stood in ranks by their horses in the snow in that
polar cold, waiting for the order for the advance. Presently the moon
rose, flooding the recesses of the ravine with silvery light, which
sparkled with dazzling brilliancy upon patches of snow here and
there on the dark walls.
Mackenzie, calculating that day would be breaking just about the
time he would reach the camp from his present position, at last gave
order to take up the march. With what relief the benumbed troopers
sprang to their saddles and urged their shivering horses forward, can
scarcely be imagined by dwellers in peaceful lands around warm
firesides. As they struggled up the cañon they could hear the sound
of dancing and revelry in the Indian camp, faintly blown back to
them by the night wind. They learned afterward that the Cheyennes
had just returned from a successful raid on the Shoshones, and that
the dance was in celebration of an important victory they had
gained. They halted again, therefore, until all was silence, before
they once more advanced. Day was beginning to break as they
reached the village.
The sleeping Indians in the camp had not the slightest suspicion that
the enemy was within a hundred miles. The troops, cheering and
shouting, burst upon them like a winter storm. Indians, when not
apprehensive of attack, invariably sleep naked. The Cheyennes had
just time to seize rifles and cartridge belts, while the women caught
hasty blankets about the children, when the soldiers were upon
them. Indeed, so quick and sudden was the attack that some of the
warriors could not get out of the tepees. With their knives they
slashed the wigwams, and from these openings fired upon the
soldiers as they galloped through the village. Many were shot dead
where a few moments before they had slept in peace.
Most of the pony herd was captured, and the village in a short time
was in possession of Mackenzie. The Cheyennes, though
overwhelmed, were undismayed. They had retreated headlong up
the cañon, but were soon rallied by their subchiefs. Dull Knife, their
leader, was found in the village with half a dozen bullets in him. He
had fought gallantly in the open until he died.
Presently the Indians came swarming back along the side of the
cañon. They occupied points of vantage, and, naked though they
were in the frightful weather, with the thermometer ranging from ten
to twenty degrees below zero during this campaign, they opened fire
upon their opponents. Unless they could be dislodged, Mackenzie’s
position was untenable. He sent his Shoshone and other Indian
scouts, who, animated with bitter hatred of the Cheyennes, were
eager to obey his commands, to the summits of the cliffs to clear the
Indians from them.
Meanwhile he directed Lieutenant John A. McKinney, with his troop,
to charge and drive the Indians from a rocky eminence where they
were concentrating and from which they were pouring a hot fire
upon the soldiers. McKinney’s charge was entirely successful, for he
drove the Cheyennes back until he was stopped by a ravine.
Wheeling his men, he attempted to find a crossing, when he was
fired upon by a flanking party of Indians and instantly killed, being
hit no less than six times. Six of his troopers were wounded, and a
number of horses were shot. The troop was thrown into confusion,
and some of the men started to retreat. Mackenzie, observing the
situation, immediately ordered Captain John M. Hamilton and Major
G. A. Gordon to charge to the rescue. The charge was gallantly
made and stubbornly resisted.
The fighting was hand to hand, of the fiercest description; and the
Cheyennes, while keeping the rest of Mackenzie’s forces engaged,
began concentrating on these two troops, which had been joined by
Captain Davis, with his men. There was no reserve; the cavalry were
all in, and this detachment might have been wiped out had it not
been for the success of the Shoshones and other Indians, who
cleared the key to the position on the summit of the plateau above
the cañon, and then came to the assistance of the sorely beset
soldiers. Twenty Cheyennes were killed here and several of the
soldiers.
Relieved in a measure by these two movements, although not
altogether, for the Cheyennes with their superior knowledge of the
topography of the country could not be entirely dislodged from their
position, and kept up a fierce fire upon the soldiers all day long, to
which he could make little reply, Mackenzie sent back word to Crook
of his success, and meanwhile began the destruction of the village.
All the winter supplies for over a thousand Indians were there. The
Cheyennes were a forehanded, prosperous tribe of Indians, as
Indians go, and the property destroyed was enormous.
II. The Sufferings of the Cheyennes
What must have been the despair of the surprised warriors, with
their women and children, naked, shivering in the hills, as they saw
their belongings consumed by the flames! It was simply impossible
for them to maintain their position during the night. They had to
move away or die of cold. As it was, twelve little Indian babies froze
to death that awful night. Many of the older men and women were
kept alive only by having their hands and feet, and in the case of the
children, their whole bodies, thrust into the warm bodies of the few
ponies not captured by the soldiers, which had been disemboweled
for the purpose.
Courtesy of The Century Co.
MACKENZIE’S MEN IN DULL KNIFE’S VILLAGE
Drawing by Frederic Remington
There was no fighting on the 26th. The Cheyennes took up a strong
position six miles farther up the cañon, from which Mackenzie could
not dislodge them, and on the 27th he started on his return to the
camp. Crook, who made a forced march night and day, with Colonel
Dodge and the infantry, who came forward with astonishing speed in
spite of storm and cold, met Mackenzie retiring just after he left the
cañon, and the whole army returned to the encampment.
The subsequent sufferings of the Indians were frightful. Naturally,
they repaired to Crazy Horse, expecting that he would succor them,
feed them, and clothe them. The Sioux and the Cheyennes had been
warm friends and allies, and had fought together on many a field.
Had they come in their prosperity, Crazy Horse would have given
them a warm welcome. As it was, he had little with which to support
his own band during the winter, owing to Crook’s pursuit of him, and
with short-sighted, yet natural—from an Indian point of view—policy,
he refused to receive these Cheyennes, or to share anything with
them.
Exasperated beyond measure by their treatment by the Sioux, and
swearing eternal vengeance upon Crazy Horse, the wretched band
struggled into the nearest agency and surrendered, and in the
following spring moved out with the soldiers against Crazy Horse and
his men.
It is appalling to think of that night attack in that awful weather
upon that sleeping camp—to read of those wretched women and
children, wandering naked in that bitter cold; to learn of those little
ones frozen to death; of the old men and women abandoned by the
road to die—yet there is another side to the picture, scarcely less
horrible.
In this Indian camp also were found many relics of the Custer battle.
So far as that is in question, I may say that I consider that action to
have been a fair and square stand-up fight, in which one side was
defeated and its members all died fighting.[101]
Naturally, the Indians
despoiled the slain for trophies. White soldiers have done the same
when conditions have been reversed, as has been noted in the
preceding chapters of this book. Of course, the Indians mutilated the
dead and tortured the living, but some instances of both practices
are found among white men, and we cannot judge the Indian by our
standards, anyway.
But in the camp there were other evidences of savage ferocity, from
which the soul shrinks in horror, and which showed that these
Indians were among the most cruel and ruthless on the continent,
and that they were only getting what they had given. Two instances
will suffice. The troops took from the body of a dead warrior an
unique necklace of human forefingers, which had been displayed
with pride upon his barbaric breast;[102]
and a bag was found which
contained the right hands of twelve little Shoshone babies and
children, which had been recently cut from little arms to give some
ruthless warrior a ghastly trophy.
100.
It is a singular thing to note the looseness of the tie with which
the members of the various tribes were bound. Frequently we
find bands of the same tribe fighting for and against the United
States on the same field. One of the most fruitful causes of the
success of our arms has been this willingness on the part of
the Indians to fight against their own people, of which the
government has been quick to avail itself.
101.
See Preface for discussion of the term “Massacre.”
102.
A picture of a similar necklace may be seen in Captain J. Lee
Humfreville’s interesting book, “Twenty Years Among Our
Hostile Indians.”
N
CHAPTER ELEVEN
Miles’ Great Campaigning
I. Miles and His Foot Cavalry Defeat Sitting
Bull
ow let us turn to Miles and his men.
General Miles was ordered to march his command up the
Yellowstone to the mouth of the Tongue River, and establish
a temporary post or cantonment there for the winter. He was an
officer in whom great confidence was reposed, and from whom
much was to be expected. He had as brilliant a record in the Civil
War as Custer, and had practically fought one decisive battle in the
closing campaign on his own responsibility, with splendidly
successful results. He was a natural-born soldier, and he never
showed his talents to better advantage than in the operations which
followed. His career before and after this period is still fresh in the
minds of a grateful people.
While Crook and his men were hammering away in one portion of
the field, Miles was doing splendid service in the other. The original
intention had been to place under his command some fifteen
hundred men, but the force he really received amounted only to
about five hundred. With these he was not expected to do more
than maintain his position, and acquire such information as he could
in preparing for the spring and summer campaign of the following
year. That was not, from his point of view, a satisfactory program.
Veteran Indian fighters in the Northwest informed him that it would
be useless to try to reach the Indians in the winter; but Miles was
not that kind of a soldier. If the Indians could live in tepees in that
season, he saw no reason why white soldiers should not move
against them in spite of the weather. He had one of the finest
regiments of infantry in the service—the Fifth. Based upon the report
of courts-martial, discipline, etc., no regiment surpassed or even
equaled its record. Miles himself proved to be the most successful
commander against Indians that the war produced, and his success
was not due to what envious people called good luck. It was well
merited and thoroughly earned.
The government, upon the representations of Sheridan and
Sherman, which were based upon Miles’ previous successful fighting
with the Southwestern Indians, allowed the young colonel
everything he asked for. If his troops were not completely equipped
for the work in which their commander designed to employ them, it
would be his fault. With wise forethought, he provided the soldiers
as if for an arctic expedition. They cut up blankets for underwear.
They were furnished with fur boots and the heaviest kind of leggings
and overshoes. Every man had a buffalo overcoat and a woolen or
fur mask to go over his face under his fur cap. Their hands were
protected by fur gloves. It was well for them that they were thus
provided, for the winter of 1876–7 was one of the most severe that
had ever visited that section of the country. The mercury frequently
froze in the thermometer, and on one occasion a temperature of
sixty degrees below zero was recorded by the spirit thermometer.
Busying themselves during the late fall, which was, in effect, winter,
in the erection of the cantonment on the Tongue and Yellowstone,
the first important touch they got with the Indians was on the 18th
of October, when Lieutenant-Colonel Elwell S. Otis, commanding a
battalion of four companies of the Twenty-third Infantry, escorting a
wagon-load of supplies from Glendive, Montana, to the cantonment,
was attacked by a large force of hostiles. The attack was not
delivered with any great degree of force at first, but it grew in power
until the troops had to corral the train. The soldiers had a hard fight
to keep the animals from being stampeded and the train captured.
Having beaten off the Indians, the train advanced, fighting, until
Clear Creek was reached. During a temporary cessation of the
attacks a messenger rode out from the Indian lines, waving a paper,
which he left upon a hill in line with the advance of the train. When
it was picked up, Colonel Otis found it to be an imperious message—
probably written by some half-breed—from the chief whom he had
been fighting. It ran as follows:
“Yellowstone.
“I want to know what you are doing traveling on this road. You scare all the
buffalo away. I want to hunt in this place. I want you to turn back from here. If
you don’t, I’ll fight you again. I want you to leave what you have got here, and
turn back from here.
I am your friend,
Sitting Bull.
“I mean all the rations you have got and some powder. Wish you would write me
as soon as you can.”
I consider this document unique in the history of Indian warfare, and
it well illustrates not only the spirit, but the naïveté of the great
chief. Otis despatched a scout to Sitting Bull with the information
that he intended to take the train through to the cantonment in spite
of all the Indians on earth, and if Sitting Bull wanted to have a fight,
he (Otis) would be glad to accommodate him at any time and on any
terms. The train thereupon moved out, and the Indians promptly
recommenced the fight. But the engagement was soon terminated
by a flag of truce. A messenger appeared, who stated that the
Indians were tired and hungry and wanted to treat for peace. Otis
asked Sitting Bull to come into his lines, but that wily old chief
refused, although he sent three chiefs to represent him.
Otis had no authority to treat for peace or anything else, but he
gave the Indians a small quantity of hardtack and a couple of sides
of bacon, and advised them to go to the Tongue River and
communicate with General Miles. The train then moved on, and after
following a short distance, with threatening movements, the Indians
withdrew.
On the same night Otis fell in with Miles and his whole force. Miles,
being alarmed for Otis’ safety, had marched out to meet him. The
train was sent down to the cantonment, and the troops, numbering
three hundred and ninety-eight, with one gun, started out in pursuit
of Sitting Bull. They overtook him on the 21st of October at Cedar
Creek. With Sitting Bull were Gall and other celebrated chiefs, and
one thousand warriors of the Miniconjous, San Arcs, Brulés, and
Unkpapas, together with their wives and children, in all over three
thousand Indians. Crazy Horse, with the Oglalas and Two Moon’s
band of the Northern Cheyennes, were not with Sitting Bull, while
Dull Knife’s band, as we have seen, had gone to Wyoming for the
winter.
The reason for this separation is obvious. They could better support
the hardships of the winter, more easily find shelter, and with less
difficulty escape from the pursuing soldiers, if they were broken up
in smaller parties.
Sitting Bull asked Miles for an interview, which was arranged. He
was attended by a subchief and six warriors, Miles by an aide and six
troopers. The meeting took place between the lines, all parties being
on horseback.
Sitting Bull wanted peace on the old basis. The Indians demanded
permission to retain their arms, with liberty to hunt and roam at will
over the plains and through the mountains, with no responsibility to
any one, while the government required them to surrender their
arms and come into the agencies. The demands were irreconcilable
therefore. The interview was an interesting one, and although it
began calmly enough, it grew exciting toward the end.
Sitting Bull, whom Miles describes as a fine, powerful, intelligent,
determined looking man, was evidently full of bitter and persistent
animosity toward the white race. He said no Indian that ever lived
loved the white man, and that no white man that ever lived loved
the Indian; that God Almighty had made him an Indian, but He
didn’t make him an Agency Indian, and he didn’t intend to be one.
The manner of the famous chief had been cold, but dignified and
courteous. As the conversation progressed, he became angry—so
enraged, in fact, that in Miles’ words “he finally gave an exhibition of
wild frenzy. His whole manner seemed more like that of a wild beast
than a human being. His face assumed a furious expression. His
jaws were tightly closed, his lips were compressed, and you could
see his eyes glisten with the fire of savage hatred.”[103]
One cannot help admiring the picture presented by the splendid, if
ferocious, savage. I have no doubt that General Miles himself
admired him.
At the height of the conference a young warrior stole out from the
Indian lines and slipped a carbine under Sitting Bull’s blanket. He
was followed by several other Indians to the number of a dozen,
who joined the band, evidently meditating treachery. Miles, who,
with his aide, was armed with revolver only, promptly required these
new auxiliaries to retire, else the conference would be terminated
immediately. His demand was reluctantly obeyed. After some further
talk, a second meeting was appointed for the morrow, and the
conference broke up.
During the night Miles moved his command in position to be able to
intercept the movement of the Indians the next day. There was
another interview with the picturesque and imperious savage, whose
conditions of peace were found to be absolutely impossible, since
they involved the abandonment of all the military posts, the
withdrawal of all settlers, garrisons, etc., from the country. He
wanted everything and would give nothing. He spoke like a
conqueror, and he looked like one, although his subsequent actions
were not in keeping with the part. Miles, seeing the futility of further
discussion, peremptorily broke up the conference. He told Sitting Bull
that he would take no advantage of the flag of truce, but that he
would give him just fifteen minutes to get back to his people to
prepare for fighting. Shouting defiance, the chiefs rode back to the
Indian lines.
There was “mounting in hot haste” surely, and hurried preparations
were made for immediate battle on both sides. Watch in hand, Miles
checked off the minutes, and exactly at the time appointed he
ordered an advance. The Indians set fire to the dry grass, which was
not yet covered with snow, and the battle was joined amid clouds of
flame and smoke. Although outnumbered nearly three to one, the
attack of the soldiers was pressed home so relentlessly that the
Indians were driven back from their camp, which fell into the
possession of Miles.
The Sioux were not beaten, however, for the discomfited warriors
rallied a force to protect their flying women and children, under the
leadership of Gall and others, Sitting Bull not being as much of a
fighter as a talker. They were led to the attack again and again by
their intrepid chiefs. On one occasion, so impetuous was their
gallantry that the troops were forced to form square to repel their
wild charges. Before the battle was over—and it continued into the
next day—the Indians had been driven headlong for over forty miles.
They had suffered a serious loss in warriors, but a greater in the
destruction of their camp equipage, winter supplies, and other
property. Two thousand of them came in on the third day and
surrendered, under promises of good treatment. Several hundred
broke into small parties and scattered. Miles’ little force was too
small to be divided to form a guard for the Indians who had been
captured; and besides, he had other things to do, so he detained a
number of the principal chiefs as hostages, and exacted promises
from the rest that they would surrender at the Spotted Tail or Red
Cloud Agency—a promise which, by the way, the great majority of
them kept. Sitting Bull, Gall, and about four hundred others refused
to surrender, and made for the boundary line, escaping pursuit for
the time being.
This was the first and most serious defection from the Indian
Confederacy. It was followed by others. In a subsequent campaign,
in the depth of winter, a battalion under Lieutenant Baldwin struck
Sitting Bull’s depleted and starving camp on two separate occasions,
inflicting further loss upon that implacable chieftain.[104]
II. Miles’ Crushing Defeat of Crazy Horse at
Wolf Mountain
Late in December Miles, having practically eliminated Sitting Bull
from the game, moved out against Crazy Horse. He had with him
five companies of the Fifth Infantry and two of the Twenty-second,
in all four hundred and thirty-six officers and men and two Napoleon
guns. These guns were fitted with canvas wagon-tops, and were so
disguised as exactly to resemble the supply wagons of the train. The
men left the cantonment on the 29th of December, 1876. It had
been learned that Crazy Horse was in the valley of the Tongue River,
south of the Yellowstone. There were sharp skirmishes on the first
and third of January between the advance and war parties of
Indians, who were moving gradually up the Tongue toward the
mountains. On the evening of the 7th of January, 1877, a young
warrior and a woman were captured, belonging to those Cheyennes
who were still with Crazy Horse and the Unkpapas, and were related
to some of the principal members of the band. From them much was
learned of the situation of the Indian position.
From the collection of
J. Robert Coster
GEN. JOHN GIBBON GEN. NELSON A. MILES
GEN. WESLEY MERRITT GEN. ALFRED H. TERRY
SOME FAMOUS INDIAN FIGHTERS
The next morning, the weather being bitterly cold, the men moved
out to attack the Indian camp. Crazy Horse’s warriors numbered
between eight and nine hundred. He had posted his men on the
cliffs surmounting a valley in the Wolf Mountains, a spur of the Big
Horn Range, not far from Crook’s battle-ground on the Rosebud. The
troops entered the valley in full view of the Indians occupying the
heights. The position was well chosen; for in order to make the
attack, the soldiers would have to climb straight up the walls to get
at the Indians, who were enabled, by the configuration of the
ground and by their numbers, almost to surround the soldiers. One
reason why Crazy Horse was willing to fight was because of his great
desire to get possession of the Indians recently captured.
Seeing that Crazy Horse was willing to accept battle, Miles made his
preparations deliberately. The troops, out of range of the Indians,
calmly had breakfast and made their camp secure. Having done
everything at his leisure, Miles moved out to the attack.
The Sioux were plainly visible on the cliffs. They could be seen
shaking their fists and brandishing their rifles as the soldiers slowly
advanced through the deep snow which covered the ground. The
Indians seemed absolutely confident that Miles was marching into a
trap, that when he got into the cañon he would be unable to scale
the slopes, and they would have him at their mercy. There was no
ambush about it. The whole thing was open and plain. They had
chosen their position and had invited the soldiers to make at them.
There was, indeed, no other way for Miles to get to them, so
cunningly had they taken advantage of the ground, except the way
which lay open before them. As the troops drew nearer, the gestures
of defiance and contempt were accompanied by yells and jeers.
Among the things they shouted in their confident assurance of
success were these significant words:
“You have had your last breakfast!”
Indeed, the grim prophecy did not seem unlikely of fulfilment.
It might have been supposed that men, encumbered as were the
soldiers with their heavy, winter clothing, could never have scaled
those heights, especially in the face of such opposition as the
redoubtable warriors of Crazy Horse would offer. If they did not
succeed in clearing the cliffs of the Indians, they would probably be
shot down in scores in the valley. They would then be forced to
retreat to their train, if any of them were left alive to do so, and
stand a siege; and as they were three or four hundred miles from
any possible relieving force, and in the depth of a Dakota winter,
that would mean a speedy annihilation. It was a serious risk to take,
but no battle was ever won without taking risks, and the nice art of
the soldier consists in knowing what risks to take and when to take
them. Not the least of Miles’ claims to admiration as a commander
was his determination, under all circumstances, to fight then and
there.
Undaunted by the threatening prospect and unmoved by the savage
shouts and jeers, although some of the scouts who knew the Sioux
language retorted in kind, the troops deployed, and at as rapid a
pace as they could manage, started for the hills. The artillery was
exposed and unlimbered, and the shells thrown into the Indian
position caused great surprise and consternation. The key to the
position was a high elevation upon the left. The Indians who held it
were led by Big Crow, the chief medicine man. As the battle began
he exposed himself freely between the lines, dressed in a
magnificent Indian war shirt and bonnet, running up and down and
yelling like a fiend.
Miles massed a little column against Big Crow and the warriors
defending the eminence. At the same time he ordered a general
escalade of the cliff along the whole line. Under a heavy fire, which,
however, like most plunging fires down the sides of mountains or
slopes, did but little damage, the troops slowly toiled up the icy,
snow-covered bluffs.[105]
Led by Major Casey and Captains McDonald
and Baldwin, the charge was delivered with the utmost resolution. It
was not a dash. No men, encumbered as were those soldiers, could
move rapidly up icy cliffs, covered, wherever the sharpness of the
acclivity permitted, with from one to three feet of snow. It was
rather a slow, dogged, determined crawl, with a stop every few
moments to fire at some Indian silhouetted above them on the gray
sky-line of that winter morning.
The fighting for the high cliff on the left of the line was spirited and
desperate. Finally, the men came to a hand-to-hand struggle. The
Indians clung tenaciously to the post until Big Crow was shot, when
the soldiers succeeded in dislodging them. This bluff commanded
the lines. It was occupied by the troops, who poured an enfilading
fire upon the army of Crazy Horse. The Indian position, therefore,
became untenable, and fighting sullenly and stubbornly, they
withdrew in good order, though closely pursued by the troops. In the
latter part of the advance snow began to fall, and before the battle
was closed the combatants were fighting in the midst of a blinding
storm. Miles says that the moment at which the Indians turned their
backs and began the retreat was one in which he felt relief scarcely
to be expressed, so desperate had been the fighting, so difficult the
ascent, and so doubtful the result.
The Indians were pursued for some distance, and a large portion of
their camp equipage, with supplies, was captured. On the whole,
they had suffered a most disheartening and disorganizing defeat.
Their ammunition was about gone, their confederates in other tribes
had been captured, the main body of the redoubtable Cheyennes
had been crushed and were starving, the Unkpapas, the
Miniconjous, the Sans Arcs, and the Brulés had surrendered. The
game was up. There was nothing for Crazy Horse and the exhausted
remnant which remained faithful to him to do but to surrender,
which they accordingly did in the early spring.
III. The Capture of Lame Deer’s Village
There remained, then, in the field practically but one band of sixty
lodges,[106]
under Lame Deer and Iron Star, who refused positively to
surrender. The indefatigable and brilliantly successful Miles pursued
this band, overtook it, surprised it one morning in May, captured the
village, dispersed the greater portion of the Indians, and succeeded
in isolating and surrounding Lame Deer and Iron Star, with half a
dozen principal warriors. Miles was very desirous of taking them
alive. He advanced with some of his officers toward the desperate
little body of Indians who had been cut off from the fleeing mass of
savages, making peace signs and crying peace words.
The Indians were tremendously excited and remained on guard, but
committed no act of hostility. Miles rode up, and leaning over the
saddle, extended his hand to Lame Deer. The intrepid chieftain, who
was quivering with emotion under his Indian stoicism, grasped the
general’s hand and clung to it tightly. Iron Star took Baldwin’s hand.
The other Indians came forward, reluctantly, with hands extended,
and all was going well.
At this juncture one of the white scouts, not knowing what was
going on, dashed up to the group, and possibly under a
misapprehension that the life of the commanding officer was
threatened, covered Lame Deer with his rifle. The Indian, probably
thinking that he was to be killed in any event, resolved to die
fighting. Miles strove to hold him and to reassure him, but by a
powerful wrench he freed himself, lifting his rifle as he did so, and
pointing it straight at the general.
Miles had been in many battles, but he was never nearer death than
at that moment. His quickness and resource did not desert him. Just
as the Indian’s finger pressed the trigger he dug his spurs into his
horse and swung the animal aside in a powerful swerve. Lame
Deer’s bullet, which missed him by a hair’s breadth, struck one of
the escort and instantly killed him. Iron Star also drew away from
Baldwin and raised his rifle, as the other Indian had done. None of
them were so quick, however, as Lame Deer had been. The soldiers
closing in had seen Lame Deer’s motion, and before any further
damage was done by the Indians they were overwhelmed by a rapid
fire, which stretched them all dead upon the ground. The fighting
had been short, but exceedingly sharp. The troops lost four killed
and seven wounded, the Sioux fourteen killed and a large number
wounded. The band was completely broken up, and most of the
Indians surrendered soon after.[107]
Of all the Indians who had borne prominent parts in this greatest of
our Indian wars with the savage tribes, there remained at large only
the indomitable Sitting Bull, and he had escaped capture because,
with a wretched band of starving but resolute followers, he
succeeded in crossing the British Columbia boundary line.
Crook’s persistence, Mills’ bold stroke, Mackenzie’s desperate dash
up Willow Creek Cañon, Miles’ splendid campaigning, his hard
fighting at Cedar Creek and Wolf Mountain, his pursuit of Lame Deer,
his policy and skill in dealing with the critical situations which had
arisen, at last brought peace to the blood-drenched land. The most
important work ever done by the United States Army outside of the
greater wars of the nation had been successfully and brilliantly
accomplished.
IV. Farewell to a Great Chief and His Hopes
A note of the fate of the two chief antagonists of the United States
may fittingly close this chapter. Sitting Bull returned to the United
States, and surrendered to the army a few years later. Ever a
malcontent, he was one of the moving spirits in the Ghost Dance
uprising, which culminated in the battle of Wounded Knee in 1890,
and he was killed by the Indian police while resisting arrest.[108]
The end of Crazy Horse came sooner, in a mêlée in a guard-house
on the 7th of September, 1877. He was stabbed in the abdomen,
and died from the effects of the wound. He was dissatisfied always,
in spite of his surrender, and had been conspiring to take the war-
path again. Believing that his intentions had become known and that
he would be rigorously dealt with on account of the discovery, he
started to run amuck, with a knife of which he had become
possessed by some means, in the guard-house. When the fracas
was over, he was found on the ground, with a desperate wound in
the abdomen. Whether the wound was given by the bayonet of the
sentry at the door, whether the blow was delivered by some of the
Indians who threw themselves upon him, and with whom he
struggled, is a matter which cannot be determined. However it was
come by, it was enough, for from the effects he died in a short time.
So that was the melancholy end of Crazy Horse, the protagonist of
these tales, and one of the most famous Indians that ever lived.
Captain Bourke[109]
thus describes him:
“I saw before me a man who looked quite young, not over thirty
years old, five feet eight inches high, lithe and sinewy, with a scar in
the face. The expression of his countenance was one of quiet
dignity, but morose, dogged, tenacious, and melancholy. He behaved
with stolidity, like a man who realized that he had to give in to Fate,
but would do so as sullenly as possible.... All Indians gave him a
high reputation for courage and generosity. In advancing upon an
enemy, none of his warriors were allowed to pass him. He had made
himself hundreds of friends by his charity toward the poor, as it was
a point of honor with him never to keep anything for himself,
excepting weapons of war. I never heard an Indian mention his
name save in terms of respect. In the Custer Massacre, the attack by
Reno had first caused a panic among the women and children and
some of the warriors, who started to flee; but Crazy Horse, throwing
away his rifle, brained one of the incoming soldiers with his stone
war-club, and jumped upon his horse.”
Crazy Horse was a born soldier, whose talents for warfare and
leadership were of the highest order. He had repulsed Reynolds on
the Powder River, wresting a victory from apparent defeat. He had
thrown himself in succession upon the columns of Crook on the
Rosebud and of Custer on the Little Big Horn; and it must be
admitted that he had not only checked, but had driven back, Crook
by a crushing attack upon him, while he had annihilated half of
Custer’s command. He had fought a desperate, and, from a military
point of view, highly creditable, action with Crook’s vastly superior
forces at Slim Buttes. The only man who had fairly and squarely
defeated him was Miles at Wolf Mountain, and even there Crazy
Horse managed to keep his force well in hand as he withdrew from
the field.
He would probably never have surrendered, had it not been for the
defections around him, and for the disastrous defeat of the
Cheyennes by Mackenzie, and the destruction of so much of his
camp equipage at Wolf Mountain. As it was, he might have
continued the fighting, had not his warriors been freezing and
starving, and almost entirely out of ammunition. There was nothing
left for the Indians but surrender. As one of them said to Miles:
“We are poor compared with you and your force. We cannot make a
rifle, a round of ammunition, or a knife. In fact, we are at the mercy
of those who are taking possession of our country. Your terms are
harsh and cruel, but we are going to accept them, and place
ourselves at your mercy.”
That summed up the situation, although the terms granted the
Indians were very far from being harsh or cruel.
So passed out of history the great war chief of the Sioux, one of the
bravest of the brave, and one of the most capable and sagacious of
captains in spite of his absurd name. He had many of the vices,
perhaps all the vices, of his race; but he had all their rude virtues,
too, and great abilities, which most of them lacked. Sitting Bull,
wise, crafty, indomitable as he was, was not to be compared with
him for a moment.
It was a tragedy any way you look at it. You cannot but feel much
admiration for those Sioux and Cheyennes—cruel, ruthless though
they were. I bid good-by to them with a certain regret.
Some one has said, as the Rosebud and the Little Big Horn marked
the high-water of Indian supremacy in the Northwest, so the
forgotten grave of Crazy Horse marks an ebb from which no tide has
ever risen.
As he passes to the happy hunting-ground in the land of the Great
Spirit, I stand and salute him with a feeling of respect which I have
gathered not only from a study of his career, but from the
statements and writings of men who could best judge of his qualities
—for they were the soldiers who fought him.
NOTES ON THE LAME DEER FIGHT
By Colonel D. L. Brainard, U. S. A.[110]
The command, consisting of four troops of the Second Cavalry, “F,” “G,” “H,” and
“L,” two companies of the Fifth Infantry, two of the Twenty-second Infantry, and a
company of mounted scouts, all under command of Colonel Nelson A. Miles, left
the cantonment on Tongue River May 1, 1877, and marched up Tongue River, with
a view of intercepting a band of hostile Indians, under Lame Deer, known to be at
or near the head-waters of the Rosebud River. The transportation consisted of bull
teams, mule teams, and a few pack animals. The command marched up Tongue
River four days, when the train was left in charge of a small guard, the main
command pushing on with pack trains, the cavalry leading and the infantry
following more slowly, striking across country toward the Rosebud River, marching
day and night, stopping only long enough to make coffee for the men, and to rest
and graze the animals.
We bivouacked on the evening of the 6th in a deep valley near Little Muddy Creek,
and about two o’clock the following morning were again in the saddle, moving
silently and swiftly down the valley toward the Indian camp, which had been
located the previous evening by White Bull, Brave Wolf, Bob Jackson, and the
other scouts. The scouts had reported that the camp was only about six miles
distant, but it was soon discovered that it was much farther than this, and at early
dawn we were still some distance away. The command had been moving at a trot,
but the gallop was immediately taken up, and just as the sun appeared above the
horizon, we rounded a bend in the valley and came in sight of the Indian camp,
which was located on the right side, close to the hills.
At first we saw no Indians except a few boys guarding the ponies, which were
grazing a little distance beyond the camp, but they came out immediately, and
dropping in the grass, began to fire in our direction, though without effect. As we
charged down on the camp, these Indians, together with squaws and children, ran
for the hills, driving with them the few horses that were near the tepees. “H”
Company, under command of Lieutenant Lovell H. Jerome, charged through the
camp and beyond, capturing the pony herd. The other companies, all under
command of Captain Ball, charged to the village, formed line to the right, deployed
as skirmishers, and pursued the Indians up the hill.
The hills were so steep at this point that it was necessary to dismount the
command and advance on foot, the horses being sent around by an easier route
to join us later near the summit of the hill. The line as formed was “F” troop
(Tyler) on the right, “L” troop (Norwood) center, and “G” troop (Wheelan) on the
left. The Indians were driven up over the hills, where they scattered like quail. Our
horses were brought up, and mounting, we charged across the country for two or
three miles, and later returned to the village.
As my recollections serve me, four soldiers and fourteen Indians were killed, ten
soldiers being wounded, myself being one of the number. About four hundred
ponies were captured, which were afterward used for mounting a battalion of
Infantry, which later performed much effective work in the field. There were over
sixty tepees, in which we found tons of dried buffalo meat, a few arms, some
ammunition, and a great many buffalo robes, saddles, and an assortment of camp
property, all of which were burned that afternoon, thus so effectually crippling the
band that the remnant came in and surrendered a few weeks later.
We camped on the battle-ground that night, the following day moving back in the
direction of our wagon train.
One of the most interesting incidents of the fight occurred just as the troop to
which I belonged (“L”) charged on the village. I saw General Miles riding toward
the first tepee, near which were two Indians, followed by his orderly. He called out
something to these Indians which I did not understand, but I later understood he
had called on them to surrender. One of these was evidently the Chief, Lame Deer,
for he wore a long head-dress of eagle feathers, the head-dress reaching to the
ground. As Miles approached on horseback, the Chief walked rapidly toward him,
with his hand extended, as though to shake hands, but when within ten or twelve
feet of him, the Indian in the rear, who was said to be Iron Star, a son of Lame
Deer, and also a medicine man of the tribe, called sharply to Lame Deer,
presumably warning him of the approaching troops, and urging him to follow the
other Indians to the hills.
Lame Deer stopped, turned, hesitated, then ran back a few steps, and picking up
a loaded carbine from the ground, fired point blank at General Miles, who, seeing
the movement, wheeled his horse sharply and bent forward. The bullet passed
over him, striking his orderly in the breast, killing him instantly. The Chief then ran
up the steep hill, accompanied by the other Indian. The head-dress made a very
conspicuous mark, and many shots were immediately fired in that direction. From
his tottering steps we saw that the Chief was badly wounded, and at this point his
companion, instead of escaping as he could have done, placed his arm around the
Chief’s waist, and supported him up the hill. About this time the Chief drew a
revolver, and without turning about, held it in rear of him and fired in our
direction, the bullets striking the ground only a few feet in his rear. This act, we
assumed, was one of defiance of a man who knew he could not escape, but who
was game to the last. Iron Star supported the Chief until the latter fell, when he
escaped over the hill, only to be killed by “G” troop, which had been pushing up
on that side. After the devotion and bravery he had displayed in supporting Lame
Deer up the hill, we were almost sorry he had not escaped alive.
A few days later Bob Jackson told me that, on examining Lame Deer’s body after
the fight, he had found that he had been hit seventeen times.
Another incident which illustrates the valor of the United States soldier was that of
Private Leonard, Troop “L,” Second Cavalry, who had dropped behind to readjust
his saddle, a couple of miles from the Indian camp. The command was moving
rapidly, and the Indians slipped in between the rear of the column and this lone
soldier. However, when he saw them he rode to the top of a hill, and lying down
behind some rocks, held these Indians at bay for several hours until relief came to
him. It was fortunate that relief came as it did, for he had nearly exhausted his
ammunition in firing at these Indians, who had several times charged his position.
103.
Personal Recollections of General Nelson A. Miles, U. S. A.
104.
As an instance of Miles’ capacity in handling men, this is what
Baldwin says in a private letter, afterward made public, of the
orders he received: “When I was given command of this
battalion opposite the mouth of Squaw Creek, and the General
took command of a less number of men, it was a question as
to which would find the hostile Indians, and with the only
order or suggestion given by him in that earnest manner
characteristic of him, he said, ‘Now, Baldwin, do the best you
can. I am responsible for disaster, success will be to your
credit; you know what my plans are, and what we are here
for.’” There is a dashing, manly ring about such words which I
rejoice to recognize. It is a great soldier who can first choose
and then trust his subordinates.
105.
At the battle of King’s Mountain, in the American Revolution,
the small loss of life among the Americans was due to the fact
that the English, trained marksmen though they were, firing
down the slopes of the mountain, overshot their opponents,
although they had them in full view all the way up the slope;
and it is the tendency of troops always to do the same thing.
Troops on a level usually fire too low, and the ground between
the advancing lines of soldiers is often plowed up by bullets
from the depressed muzzles, which should have gone into the
breasts of the approaching enemy.
106.
Each lodge accounted for from five to ten persons.
107. See close of this chapter for another account of the Lame Deer
Fight.
108.
These affairs are to be discussed at length in a forthcoming
volume.
109.
“On the Border With Crook,” Captain John G. Bourke, U. S. A.
110.
Colonel Brainard won his commission by his heroic conduct in
the Greely Arctic Expedition, 1881–4.—C. T. B.
T
CHAPTER TWELVE
What They Are There For A Sketch of General
Guy V. Henry, a Typical American Soldier
I. Savage Warfare
he most thankless task that can be undertaken by a nation is
warfare against savage or semi-civilized peoples. In it there is
usually little glory; nor is there any reward, save the
consciousness of disagreeable duty well performed. The risk to the
soldier is greater than in ordinary war, since the savages usually
torture the wounded and the captured. Success can only be
achieved by an arduous, persistent, wearing down process, which
affords little opportunity for scientific fighting, yet which demands
military talents of the highest order.
Almost anybody can understand the strategy or the tactics of a
pitched battle where the number engaged is large, the casualties
heavy, and the results decisive; but very few non-professional critics
appreciate a campaign of relentless pursuit by a small army of a
smaller body of mobile hostiles, here and there capturing a little
band, now and then killing or disabling a few, until in the final
round-up the enemy, reduced to perhaps less than a score,
surrenders. There is nothing spectacular about the performance, and
everybody wonders why it took so long.
And as injustice and wrong have not been infrequent in the
preliminary dealings between the government and the savages, the
soldier, who has only to obey his orders, comes in for much
unmerited censure from those who think darkly though they speak
bitterly. Especially is he criticized if, when maddened by the
suffering, the torture, of some comrade, the soldier sinks to the
savage level in his treatment of his ruthless foeman. No one justifies
such a lapse, of course, but few there be who even try to
understand it. The incessant campaigning in the Philippines, with its
resulting scandals, is an instance in point.
Long before the Spanish-American War and its Philippine corollary,
however, our little army had shown itself capable of the hardest and
most desperate campaigning against the Indians of the West—as
difficult and dangerous a work as any army ever undertook. There
was so much of it, and it abounded with so many thrilling incidents,
that volumes could be written upon it without exhausting its tragedy,
its romance. There were few soldiers who served beyond the
Mississippi from 1865 to 1890 who did not participate in a score of
engagements, whose lives were not in peril more than once in many
a hard, but now forgotten, campaign.
From the collection of
J. Robert Coster
COL. RANALD S. MACKENZIE GEN. GUY V. HENRY
CAPT. ANSON MILLS W. F. CODY (BUFFALO BILL)
GROUP OF DISTINGUISHED INDIAN FIGHTERS
All except General Henry from contemporary photographs
One of the bravest of our Indian fighters was Guy V. Henry.
Personally, he was a typical representative of the knightly American
soldier. Officially, it was his fortune to perform conspicuous services
in at least three expeditions subsequent to the Civil War. He was a
West Pointer, and the son of another, born in the service at Fort
Smith in the Indian Territory. Graduating in 1861, a mere boy, he
participated in four years of the hardest fighting in the Civil War,
from Bull Run to Cold Harbor. At the age of twenty-three, his merit
won him the appointment of Colonel of the Fortieth Massachusetts
Volunteers, “a regiment that was never whipped.” The tall, brawny
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
Let us accompany you on the journey of exploring knowledge and
personal growth!
ebookultra.com

Python for DevOps Learn Ruthlessly Effective Automation 1st Edition Noah Gift

  • 1.
    Visit https://ebookultra.com todownload the full version and explore more ebooks Python for DevOps Learn Ruthlessly Effective Automation 1st Edition Noah Gift _____ Click the link below to download _____ https://ebookultra.com/download/python-for-devops- learn-ruthlessly-effective-automation-1st-edition-noah- gift/ Explore and download more ebooks at ebookultra.com
  • 2.
    Here are somesuggested products you might be interested in. Click the link to download Devops Automation Cookbook 1st Edition Michael Duffy https://ebookultra.com/download/devops-automation-cookbook-1st- edition-michael-duffy/ Learn Python the Hard Way 5th Edition Zed A. Shaw https://ebookultra.com/download/learn-python-the-hard-way-5th-edition- zed-a-shaw/ A Smarter Way to Learn Python Learn it faster Remember it longer First Edition Mark Myers https://ebookultra.com/download/a-smarter-way-to-learn-python-learn- it-faster-remember-it-longer-first-edition-mark-myers/ Python Testing with pytest Simple Rapid Effective and Scalable 1st Edition Brian Okken https://ebookultra.com/download/python-testing-with-pytest-simple- rapid-effective-and-scalable-1st-edition-brian-okken/
  • 3.
    Developing on AWSwith C A Comprehensive Guide on Using C to Build Solutions on the AWS Platform 1st Edition Noah Gift https://ebookultra.com/download/developing-on-aws-with-c-a- comprehensive-guide-on-using-c-to-build-solutions-on-the-aws- platform-1st-edition-noah-gift/ Losing Afghanistan An Obituary for the Intervention 1st Edition Noah Coburn https://ebookultra.com/download/losing-afghanistan-an-obituary-for- the-intervention-1st-edition-noah-coburn/ PowerShell for Sysadmins Workflow Automation Made Easy 1st Edition Adam Bertram https://ebookultra.com/download/powershell-for-sysadmins-workflow- automation-made-easy-1st-edition-adam-bertram/ Practical GitLab Services A Complete DevOps Guide for Developers and Administrators 1st Edition Jeffrey Painter https://ebookultra.com/download/practical-gitlab-services-a-complete- devops-guide-for-developers-and-administrators-1st-edition-jeffrey- painter/ Dead Simple Python Idiomatic Python for the Impatient Programmer Jason C. Mcdonald https://ebookultra.com/download/dead-simple-python-idiomatic-python- for-the-impatient-programmer-jason-c-mcdonald/
  • 5.
    Python for DevOpsLearn Ruthlessly Effective Automation 1st Edition Noah Gift Digital Instant Download Author(s): Noah Gift, Kennedy Behrman, Alfredo Deza, Robert Jordan, Grig Gheorghiu ISBN(s): 9781492057697, 149205769X Edition: 1 File Details: PDF, 10.79 MB Year: 2020 Language: english
  • 6.
    Noah Gift, KennedyBehrman, Alfredo Deza & Grig Gheorghiu Python for DevOps Learn Ruthlessly Effective Automation
  • 8.
    Noah Gift, KennedyBehrman, Alfredo Deza, and Grig Gheorghiu Python for DevOps Learn Ruthlessly Effective Automation Boston Farnham Sebastopol Tokyo Beijing Boston Farnham Sebastopol Tokyo Beijing
  • 9.
    978-1-492-05769-7 [LSI] Python for DevOps byNoah Gift, Kennedy Behrman, Alfredo Deza, and Grig Gheorghiu Copyright © 2020 Noah Gift, Kennedy Behrman, Alfredo Deza, Grig Gheorghiu. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Rachel Roumeliotis Development Editor: Corbin Collins Production Editor: Christopher Faucher Copyeditor: nSight, Inc. Proofreader: Sonia Saruba Indexer: WordCo Indexing Services, Inc. Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Rebecca Demarest December 2019: First Edition Revision History for the First Release 2019-12-11: First Release 2020-06-19: Second Release See http://oreilly.com/catalog/errata.csp?isbn=9781492057697 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Python for DevOps, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the authors, and do not represent the publisher’s views. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.
  • 10.
    Table of Contents Preface.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii 1. Python Essentials for DevOps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Installing and Running Python 2 The Python Shell 2 Jupyter Notebooks 3 Procedural Programming 3 Variables 4 Basic Math 4 Comments 5 Built-in Functions 5 Print 5 Range 6 Execution Control 6 if/elif/else 7 for Loops 8 while Loops 9 Handling Exceptions 10 Built-in Objects 10 What Is an Object? 11 Object Methods and Attributes 11 Sequences 12 Functions 23 Anatomy of a Function 24 Functions as Objects 25 iii
  • 11.
    Anonymous Functions 26 UsingRegular Expressions 26 Searching 27 Character Sets 27 Character Classes 28 Groups 29 Named Groups 29 Find All 29 Find Iterator 30 Substitution 30 Compiling 30 Lazy Evaluation 31 Generators 31 Generator Comprehensions 32 More IPython Features 33 Using IPython to Run Unix Shell Commands 33 Exercises 34 2. Automating Files and the Filesystem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Reading and Writing Files 35 Using Regular Expressions to Search Text 44 Dealing with Large Files 46 Encrypting Text 47 Hashing with Hashlib 47 Encryption with Cryptography 47 The os Module 49 Managing Files and Directories Using os.path 51 Walking Directory Trees Using os.walk 54 Paths as Objects with Pathlib 55 3. Working with the Command Line. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Working with the Shell 57 Talking to the Interpreter with the sys Module 57 Dealing with the Operating System Using the os Module 58 Spawn Processes with the subprocess Module 59 Creating Command-Line Tools 61 Using sys.argv 62 Using argparse 65 Using click 69 iv | Table of Contents
  • 12.
    fire 73 Implementing Plug-ins78 Case Study: Turbocharging Python with Command-Line Tools 79 Using the Numba Just-in-Time (JIT) Compiler 80 Using the GPU with CUDA Python 82 Running True Multicore Multithreaded Python Using Numba 83 KMeans Clustering 84 Exercises 85 4. Useful Linux Utilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Disk Utilities 88 Measuring Performance 88 Partitions 90 Retrieving Specific Device Information 91 Network Utilities 93 SSH Tunneling 93 Benchmarking HTTP with Apache Benchmark (ab) 93 Load Testing with molotov 95 CPU Utilities 97 Viewing Processes with htop 98 Working with Bash and ZSH 99 Customizing the Python Shell 100 Recursive Globbing 101 Searching and Replacing with Confirmation Prompts 101 Removing Temporary Python Files 103 Listing and Filtering Processes 103 Unix Timestamp 104 Mixing Python with Bash and ZSH 104 Random Password Generator 104 Does My Module Exist? 105 Changing Directories to a Module’s Path 106 Converting a CSV File to JSON 107 Python One-Liners 107 Debuggers 108 How Fast Is this Snippet? 108 strace 109 Exercises 112 Case Study Question 112 Table of Contents | v
  • 13.
    5. Package Management.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Why Is Packaging Important? 114 When Packaging Might Not Be Needed 114 Packaging Guidelines 114 Descriptive Versioning 115 The changelog 116 Choosing a Strategy 117 Packaging Solutions 118 Native Python Packaging 118 Debian Packaging 124 RPM Packaging 131 Management with systemd 137 Long-Running Processes 138 Setting It Up 138 The systemd Unit File 140 Installing the Unit 141 Log Handling 143 Exercises 144 Case Study Question 144 6. Continuous Integration and Continuous Deployment. . . . . . . . . . . . . . . . . . . . . . . . . . . 145 Real-World Case Study: Converting a Poorly Maintained WordPress Site to Hugo 145 Setting Up Hugo 147 Converting WordPress to Hugo Posts 148 Creating an Algolia Index and Updating It 150 Orchestrating with a Makefile 151 Deploying with AWS CodePipeline 152 Real-World Case Study: Deploying a Python App Engine Application with Google Cloud Build 153 Real-World Case Study: NFSOPS 160 7. Monitoring and Logging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Key Concepts in Building Reliable Systems 163 Immutable DevOps Principles 164 Centralized Logging 164 Case Study: Production Database Kills Hard Drives 165 Did You Build It or Buy It? 166 Fault Tolerance 166 vi | Table of Contents
  • 14.
    Monitoring 168 Graphite 168 StatsD169 Prometheus 169 Instrumentation 173 Naming Conventions 176 Logging 177 Why Is It Hard? 177 The basicconfig 178 Deeper Configuration 179 Common Patterns 183 The ELK Stack 184 Logstash 185 Elasticsearch and Kibana 187 Exercises 190 Case Study Question 191 8. Pytest for DevOps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Testing Superpowers with pytest 193 Getting Started with pytest 194 Testing with pytest 194 Differences with unittest 196 pytest Features 198 conftest.py 198 The Amazing assert 199 Parametrization 200 Fixtures 202 Getting Started 202 Built-in Fixtures 204 Infrastructure Testing 206 What Is System Validation? 207 Introduction to Testinfra 208 Connecting to Remote Nodes 209 Features and Special Fixtures 212 Examples 213 Testing Jupyter Notebooks with pytest 216 Exercises 216 Case Study Question 217 Table of Contents | vii
  • 15.
    9. Cloud Computing.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Cloud Computing Foundations 220 Types of Cloud Computing 222 Types of Cloud Services 223 Infrastructure as a Service 223 Metal as a Service 227 Platform as a Service 227 Serverless Computing 228 Software as a Service 231 Infrastructure as Code 232 Continuous Delivery 232 Virtualization and Containers 232 Hardware Virtualization 232 Software Defined Networks 233 Software Defined Storage 233 Containers 234 Challenges and Opportunities in Distributed Computing 235 Python Concurrency, Performance, and Process Management in the Cloud Era 237 Process Management 237 Manage Processes with Subprocess 237 Using Multiprocessing to Solve Problems 240 Forking Processes with Pool() 241 Function as a Service and Serverless 243 High Performance Python with Numba 243 Using Numba Just in Time Compiler 243 Using High-Performance Servers 244 Conclusion 245 Exercises 245 Case Study Questions 246 10. Infrastructure as Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 A Classification of Infrastructure Automation Tools 249 Manual Provisioning 250 Automated Infrastructure Provisioning with Terraform 251 Provisioning an S3 Bucket 252 Provisioning an SSL Certificate with AWS ACM 255 Provisioning an Amazon CloudFront Distribution 256 Provisioning a Route 53 DNS Record 258 viii | Table of Contents
  • 16.
    Copying Static Filesto S3 259 Deleting All AWS Resources Provisioned with Terraform 260 Automated Infrastructure Provisioning with Pulumi 260 Creating a New Pulumi Python Project for AWS 261 Creating Configuration Values for the Staging Stack 265 Provisioning an ACM SSL Certificate 266 Provisioning a Route 53 Zone and DNS Records 266 Provisioning a CloudFront Distribution 269 Provisioning a Route 53 DNS Record for the Site URL 270 Creating and Deploying a New Stack 271 Exercises 273 11. Container Technologies: Docker and Docker Compose. . . . . . . . . . . . . . . . . . . . . . . . . . . 275 What Is a Docker Container? 276 Creating, Building, Running, and Removing Docker Images and Containers 276 Publishing Docker Images to a Docker Registry 280 Running a Docker Container with the Same Image on a Different Host 281 Running Multiple Docker Containers with Docker Compose 283 Porting the docker-compose Services to a New Host and Operating System 295 Exercises 298 12. Container Orchestration: Kubernetes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Short Overview of Kubernetes Concepts 300 Using Kompose to Create Kubernetes Manifests from docker-compose.yaml 301 Deploying Kubernetes Manifests to a Local Kubernetes Cluster Based on minikube 302 Launching a GKE Kubernetes Cluster in GCP with Pulumi 316 Deploying the Flask Example Application to GKE 319 Installing Prometheus and Grafana Helm Charts 325 Destroying the GKE Cluster 330 Exercises 331 13. Serverless Technologies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 Deploying the Same Python Function to the “Big Three” Cloud Providers 336 Installing Serverless Framework 336 Deploying Python Function to AWS Lambda 336 Deploying Python Function to Google Cloud Functions 339 Deploying Python Function to Azure 344 Deploying a Python Function to Self-Hosted FaaS Platforms 348 Table of Contents | ix
  • 17.
    Deploying Python Functionto OpenFaaS 349 Provisioning DynamoDB Table, Lambda Functions, and API Gateway Methods Using the AWS CDK 356 Exercises 375 14. MLOps and Machine learning Engineering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 What Is Machine Learning? 377 Supervised Machine Learning 377 Modeling 380 Python Machine learning Ecosystem 382 Deep Learning with PyTorch 383 Cloud Machine learning Platforms 387 Machine learning Maturity Model 388 Machine Learning Key Terminology 388 Level 1: Framing, Scope Identification, and Problem Definition 389 Level 2: Continuous Delivery of Data 390 Level 3: Continuous Delivery of Clean Data 391 Level 4: Continuous Delivery of Exploratory Data Analysis 393 Level 5: Continuous Delivery of Traditional ML and AutoML 393 Level 6: ML Operational Feedback Loop 394 Sklearn Flask with Kubernetes and Docker 395 Sklearn Flask with Kubernetes and Docker 398 EDA 399 Modeling 400 Tune Scaled GBM 401 Fit Model 402 Evaluate 402 adhoc_predict 403 JSON Workflow 404 Scale Input 404 adhoc_predict from Pickle 405 Scale Input 406 Exercises 406 Case Study Question 406 Learning Assessments 407 15. Data Engineering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 Small Data 410 Dealing with Small Data Files 410 x | Table of Contents
  • 18.
    Write a File411 Read a File 411 Generator Pipeline to Read and Process Lines 411 Using YAML 412 Big Data 413 Big Data Tools, Components, and Platforms 415 Data Sources 415 Filesystems 416 Data Storage 417 Real-Time Streaming Ingestion 418 Case Study: Building a Homegrown Data Pipeline 419 Serverless Data Engineering 420 Using AWS Lambda with CloudWatch Events 421 Using Amazon CloudWatch Logging with AWS Lambda 421 Using AWS Lambda to Populate Amazon Simple Queue Service 422 Wiring Up CloudWatch Event Trigger 427 Creating Event-Driven Lambdas 428 Reading Amazon SQS Events from AWS Lambda 428 Conclusion 432 Exercises 433 Case Study Question 433 16. DevOps War Stories and Interviews. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 Film Studio Can’t Make Film 436 Game Studio Can’t Ship Game 438 Python Scripts Take 60 Seconds to Launch 440 Putting Out a Fire with a Cache and Intelligent Instrumentation 441 You’ll Automate Yourself Out of a Job! 442 DevOps Antipatterns 443 No Automated Build Server Antipattern 443 Flying Blind 444 Difficulties in Coordination as an Ongoing Accomplishment 444 No Teamwork 445 Interviews 451 Glenn Solomon 451 Andrew Nguyen 451 Gabriella Roman 453 Rigoberto Roche 454 Jonathan LaCour 456 Table of Contents | xi
  • 19.
    Ville Tuulos 458 JosephReis 460 Teijo Holzer 461 Matt Harrison 463 Michael Foord 464 Recommendations 467 Exercises 468 Challenges 468 Capstone Project 469 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 xii | Table of Contents
  • 20.
    Preface One time Noahwas in the ocean, and a wave crashed on top of him and took his breath away as it pulled him deeper into the sea. Just as he started to recover his breath, another wave dropped on top. It extracted much of his remaining energy. It pulled him even deeper into the ocean. Just as he started to recover, yet another wave crashed down on top. The more he would fight the waves and the sea, the more energy was drained. He seriously wondered if he would die at that moment. He couldn’t breathe, his body ached, and he was terrified he was going to drown. Being close to death helped him focus on the only thing that could save him, which was conserving his energy and using the waves—not fighting them. Being in a startup that doesn’t practice DevOps is a lot like that day at the beach. There are production fires that burn for months; everything is manual, alerts wake you up for days on end damaging your health. The only escape from this death spiral is the DevOps way. Do one right thing, then another, until you find clarity. First, set up a build server, start testing your code, and automate manual tasks. Do something; it can be anything, but have a “bias for action.” Do that first thing right and make sure it is automated. A common trap in startups or any company is the search for superheroes. “We need a performance engineer” because they will fix our performance problems. “We need a Chief Revenue Officer” because they will fix all sales problems. “We need DevOps engineers” because they will fix our deployment process. At one company, Noah had a project that was over a year late, and the web applica‐ tion had been rewritten three times in multiple languages. This next release only needed a “performance engineer” to get it finished. I remember being the only one brave or stupid enough to say, “What is a performance engineer?” This engineer made everything work at scale. He realized at that point that they were looking for a superhero to save them. Superhero hiring syndrome is the best way to pick up on something being very wrong on a new product or a new startup. No employee will save a company unless they first save themselves. xiii
  • 21.
    At other companies,Noah heard similar things: “If we could only hire a senior Erlang engineer,” or “If we could only hire someone to make us revenue,” or “If we could only hire someone to teach us to be financially disciplined,” or “If we could only hire a Swift developer,” etc. This hire is the last thing your startup or new product needs— it needs to understand what it is doing wrong that only a superhero can save the day. In the case of the company that wanted to hire a performance engineer, it turned out that the real issue was inadequate technical supervision. The wrong people were in charge (and verbally shouting down the people who could fix it). By removing a poor performer, listening to an existing team member who knew how to fix the problem all along, deleting that job listing, doing one right thing at a time, and inserting qualified engineering management, the issue resolved itself without a superhero hire. No one will save you at your startup; you and your team have to protect yourselves by creating great teamwork, a great process, and believing in your organization. The sol‐ ution to the problem isn’t a new hire; it is being honest and mindful about the situa‐ tion you are in, how you got there, and doing one right thing at a time until you work your way out. There is no superhero unless it is you. Just like being in the ocean in a storm and slowly drowning, no one is going to save you or the company unless it is you. You are the superhero your company needs, and you might discover your coworkers are too. There is a way out of the chaos, and this book can be your guide. Let’s get started. What Does DevOps Mean to the Authors? Many abstract concepts in the software industry are hard to define precisely. Cloud Computing, Agile, and Big Data are good examples of topics that can have many defi‐ nitions depending on whom you talk to. Instead of strictly defining what DevOps is, let’s use some phrases that show evidence DevOps is occurring: • Two-way collaboration between Development and Operation teams. • Turnaround of Ops tasks in minutes to hours, not days to weeks. • Strong involvement from developers; otherwise, it’s back to Devs versus Ops. • Operations people need development skills—at least Bash and Python. • Developer people need operational skills—their responsibilities don’t end with writing the code, but with deploying the system to production and monitoring alerts. • Automation, automation, automation: you can’t accurately automate without Dev skills, and you can’t correctly automate without Ops skills • Ideally: self-service for developers, at least in terms of deploying code. xiv | Preface
  • 22.
    • Can beachieved via CI/CD pipelines. • GitOps. • Bidirectional everything between Development and Operations (tooling, knowl‐ edge, etc.). • Constant collaboration in design, implementation, deployment—and yes, auto‐ mation—can’t be successful without cooperation. • If it isn’t automated, it’s broken. • Cultural: Hierarchy < Process. • Microservices > Monolithic. • The continuous deployment system is the heart and soul of the software team. • There are no superheroes. • Continuous delivery isn’t an option; it is a mandate. How to Use This Book This book is useful in any order. You can randomly open any chapter you like, and you should be able to find something helpful to apply to your job. If you are an expe‐ rienced Python programmer, you may want to skim Chapter 1. Likewise, if you are interested in war stories, case studies, and interviews, you may want to read the Chapter 16 first. Conceptual Topics The content is broken up into several conceptual topics. The first group is Python Foundations, and it covers a brief introduction to the language as well as automating text, writing command-line tools, and automating the file system. Next up is Operations, which includes useful Linux utilities, package management, build systems, monitoring and instrumentation, and automated testing. These are all essential topics to master to become a competent DevOps practitioner. Cloud Foundations are in the next section, and there are chapters on Cloud Comput‐ ing, Infrastructure as Code, Kubernetes, and Serverless. There is currently a crisis in the software industry around finding enough talent trained in the Cloud. Mastering this section will pay immediate dividends to both your salary and your career. Next up is the Data section. Machine Learning Operations and Data Engineering are both covered from the perspective of DevOps. There is also a full soup to nuts machine learning project walkthrough that takes you through the building, deploying, and operationalizing of a machine learning model using Flask, Sklearn, Docker, and Kubernetes. Preface | xv
  • 23.
    The last sectionis Chapter 16 on case studies, interviews, and DevOps war stories. This chapter makes for good bed time reading. Python Foundations • Chapter 1, Python Essentials for DevOps • Chapter 2, Automating Files and the Filesystem • Chapter 3, Working with the Command Line Operations • Chapter 4, Useful Linux Utilities • Chapter 5, Package Management • Chapter 6, Continuous Integration and Continuous Deployment • Chapter 7, Monitoring and Logging • Chapter 8, Pytest for DevOps Cloud Foundations • Chapter 9, Cloud Computing • Chapter 10, Infrastructure as Code • Chapter 11, Container Technologies: Docker and Docker Compose • Chapter 12, Container Orchestration: Kubernetes • Chapter 13, Serverless Technologies Data • Chapter 14, MLOps and Machine learning Engineering • Chapter 15, Data Engineering Case Studies • Chapter 16, DevOps War Stories and Interviews xvi | Preface
  • 24.
    Conventions Used inThis Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program ele‐ ments such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This element signifies a tip or suggestion. This element signifies a general note. This element indicates a warning or caution. Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://pythondevops.com. You can also view DevOps content related to the code in the book at the Pragmatic AI Labs YouTube channel. If you have a technical question for the authors or a problem using the code exam‐ ples, please email technical@pythondevops.com. Preface | xvii
  • 25.
    This book ishere to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Python for DevOps by Noah Gift, Kennedy Behrman, Alfredo Deza, and Grig Gheorghiu. (O’Reilly). Copy‐ right 2020 Noah Gift, Kennedy Behrman, Alfredo Deza, Grig Gheorghiu, 978-1-492-05769-7.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com. O’Reilly Online Learning For more than 40 years, O’Reilly Media has provided technol‐ ogy and business training, knowledge, and insight to help companies succeed. Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, please visit http:// oreilly.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) xviii | Preface
  • 26.
    We have aweb page for this book, where we list errata, examples, and any additional information. You can access this page at oreil.ly/python-for-devops. Email bookquestions@oreilly.com to comment or ask technical questions about this book. For news and more information about our books and courses, see our website at http://www.oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia Acknowledgments To start off, the authors would like to thank the two main technical reviewers of the book: Wes Novack is an architect and engineer specializing in public cloud systems and web-scale SaaS applications. He designs, builds, and manages complex systems that enable highly available infrastructure, continuous delivery pipelines, and rapid relea‐ ses within large, polyglot microservice ecosystems hosted on AWS and GCP. Wes makes extensive use of languages, frameworks, and tools to define Infrastructure as Code, drive automation, and eliminate toil. He is vocal in the tech community by par‐ ticipating in mentorship, workshops, and conferences, and he is also a Pluralsight video course author. Wes is an advocate for the CALMS of DevOps; Culture, Auto‐ mation, Lean, Measurement, and Sharing. You can find him on Twitter @WesleyTech or visit his personal blog. Brad Andersen is a software engineer and architect. He has designed and developed software professionally for 30 years. He works as a catalyst for change and innova‐ tion; he has assumed leadership and development roles across a spectrum from enter‐ prise organizations to startups. Brad is currently pursuing a master’s degree in data science at the University of California, Berkeley. You can find more information on Brad’s LinkedIn profile. We would also like to thank Jeremy Yabrow and Colin B. Erdman for chipping in with many great ideas and bits of feedback. Noah I would like to thank the coauthors of the book: Grig, Kennedy, and Alfredo. It was incredible working with a team that was this effective. Preface | xix
  • 27.
    Kennedy Thanks to mycoauthors, it has been a pleasure to work with you. And thanks for the patience and understanding of my family. Alfredo In 2010—nine years ago as of this writing—I landed my first software engineering job. I was 31 years old with no college education and no previous engineering experi‐ ence. That job meant accepting a reduced salary and no health insurance. I learned a lot, met amazing people, and gained expertise through relentless determination. Throughout those years, it would’ve been impossible to get here without people opening opportunities and pointing me in the right direction. Thanks to Chris Benson, who saw that I was hungry for learning and kept finding opportunities to have me around. Thanks to Alejandro Cadavid, who realized that I could fix things nobody else wanted to fix. You helped me get work when no one (including myself) thought I could be useful. Carlos Coll got me into programming and didn’t let me quit even when I asked him to. Learning to program changed my life, and Carlos had the patience to push me to learn and land my first program in production. To Joni Benton, for believing in me and helping me land my first full-time job. Thanks to Jonathan LaCour, an inspiring boss who continues to help me get to a bet‐ ter place. Your advice has always been invaluable to me. Noah, thanks for your friendship and guidance you are a tremendous source of moti‐ vation to me. I always enjoy working together, like that one time when we rebuilt infrastructure from scratch. Your patience and guidance when I had no idea about Python was life-changing. Lastly, a tremendous thanks to my family. My wife Claudia, who never doubts my ability to learn and improve, and so generous and understanding of the time I spent working toward this book. My children, Efrain, Ignacio, and Alana: I love you all. Grig My thanks to all creators of open source software. Without them, our jobs would be so much more bleak and unfulfilling. Also thank you to all who blog and share your knowledge freely. Lastly, I also wish to thank the coauthors of this book. It’s been a really fun ride. xx | Preface
  • 28.
    CHAPTER 1 Python Essentialsfor DevOps DevOps, the combination of software development with information technology operations, has been a hot field during the last decade. Traditional boundaries among software development, deployment, maintenance, and quality assurance have been broken, enabling more integrated teams. Python has been a popular language both in traditional IT operations and in DevOps due to its combination of flexibility, power, and ease of use. The Python programming language was publicly released in the early 1990s for use in system administration. It has been a great success in this area and has gained wide adoption. Python is a general-purpose programming language used in just about every domain. The visual effects and the motion picture industries embraced it. More recently, it has become the de facto language of data science and machine learning (ML). It has been used across industries from aviation to bioinformatics. Python has an extensive arsenal of tools to cover the wide-ranging needs of its users. Learning the whole Python Standard Library (the capabilities that come with any Python installation) would be a daunting task. Trying to learn all the third-party packages that enliven the Python ecosystem would be an immense undertaking. The good news is that you don’t need to do those things. You can become a powerful DevOps practitioner by learning only a small subset of Python. In this chapter, we draw on our decades of Python DevOps experience to teach only the elements of the language that you need. These are the parts of Python DevOps that are used daily. They form the essential toolbox to get things done. Once you have these core concepts down, you can add more complicated tools, as you’ll see in later chapters. 1
  • 29.
    Installing and RunningPython If you want to try the code in this overview, you need Python 3.7 or later installed (the latest release is 3.8.0 as of this writing) and access to a shell. In macOS X, Win‐ dows, and most Linux distributions, you can open the terminal application to access a shell. To see what version of Python you are using, open a shell, and type python --version: $ python --version Python 3.8.0 Python installers can be downloaded directly from the Python.org website. Alterna‐ tively, you can use a package manager such as Apt, RPM, MacPorts, Homebrew, Chocolatey, or many others. The Python Shell The simplest way to run Python is to use the built-in interactive interpreter. Just type python in a shell. You can then interactively run Python statements. Type exit() to exit the shell. $ python Python 3.8.0 (default, Sep 23 2018, 09:47:03) [Clang 9.0.0 (clang-900.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> 1 + 2 3 >>> exit() Python scripts Python code runs from a file with the .py extension: # This is my first Python script print('Hello world!') Save this code to a file named hello.py. To invoke the script, in a shell run python followed by the filename: $ python hello.py Hello world! Python scripts are how most production Python code runs. IPython Besides the built-in interactive shell, several third-party interactive shells run Python code. One of the most popular is IPython. IPython offers introspection (the ability to dynamically get information about objects), syntax highlighting, special magic com‐ mands (which we touch on later in this chapter), and many more features, making it 2 | Chapter 1: Python Essentials for DevOps
  • 30.
    a pleasure touse for exploring Python. To install IPython, use the Python package manager, pip: $ pip install ipython Running is similar to running the built-in interactive shell described in the previous section: $ ipython Python 3.8.0 (default, Sep 23 2018, 09:47:03) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: print('Hello') Hello In [2]: exit() Jupyter Notebooks A spin-off from the iPython project, the Jupyter project allows documents containing text, code, and visualizations. These documents are powerful tools for combining running code, output, and formatted text. Jupyter enables the delivery of documenta‐ tion along with the code. It has achieved widespread popularity, especially in the data science world. Here is how to install and run Jupyter notebooks: $ pip install jupyter $ jupyter notebook This command opens a web browser tab showing the current working directory. From here, you can open existing notebooks in the current project or create new ones. Procedural Programming If you’ve been around programming at all, you’ve probably heard terms like object- oriented programming (OOP) and functional programming. These are different architectural paradigms used to organize programs. One of the most basic paradigms, procedural programming, is an excellent place to start. Procedural programming is the issuing of instructions to a computer in an ordered sequence: >>> i = 3 >>> j = i +1 >>> i + j 7 As you can see in this example, there are three statements that are executed in order from the first line to the last. Each statement uses the state produced by the previous ones. In this case, the first statement assigns the value 3 to a variable named i. In the second statement, this variable’s value is used to assign a value to a variable named j, Procedural Programming | 3
  • 31.
    and in thethird statement, the values from both variables are added together. Don’t worry about the details of these statements yet; notice that they are executed in order and rely on the state created by the previous statements. Variables A variable is a name that points to some value. In the previous example, the variables are i and j . Variables in Python can be assigned to new values: >>> dog_name = 'spot' >>> dog_name 'spot' >>> dog_name = 'rex' >>> dog_name 'rex' >>> dog_name = 't-' + dog_name >>> dog_name 't-rex' >>> Python variables use dynamic typing. In practice, this means that they can be reas‐ signed to values of different types or classes: >>> big = 'large' >>> big 'large' >>> big = 1000*1000 >>> big 1000000 >>> big = {} >>> big {} >>> Here the same variable is set to a string, a number, and a dictionary. Variables can be reassigned to values of any type. Basic Math Basic math operations such as addition, subtraction, multiplication, and division can all be performed using built-in math operators: >>> 1 + 1 2 >>> 3 - 4 –1 >>> 2*5 10 >>> 2/3 0.6666666666666666 4 | Chapter 1: Python Essentials for DevOps
  • 32.
    Note that a// symbol is for integer division. The symbol ** creates an exponent, and % is the modulo operator: >>> 5/2 2.5 >>> 5//2 2 >>> 3**2 9 >>> 5%2 1 Comments Comments are text ignored by the Python interpreter. They are useful for documen‐ tation of code and can be mined by some services to provide standalone documenta‐ tion. Single-line comments are delineated by prepending with #. A single-line comment can start at the beginning of a line, or at any point thereafter. Everything after the # is part of the comment until a new line break occurs: # This is a comment 1 + 1 # This comment follows a statement Multiline comments are enclosed themselves in blocks beginning and ending with either """ or ''': """ This statement is a block comment. It can run for multiple lines """ ''' This statement is also a block comment ''' Built-in Functions Functions are statements grouped as a unit. You invoke a function by typing the func‐ tion name, followed by parentheses. If the function takes arguments, the arguments appear within the parentheses. Python has many built-in functions. Two of the most widely used built-in functions are print and range. Print The print function produces output that a user of a program can view. It is less rele‐ vant in interactive environments but is a fundamental tool when writing Python scripts. In the previous example, the argument to the print function is written as output when the script runs: Procedural Programming | 5
  • 33.
    # This ismy first Python script print("Hello world!") $ python hello.py Hello world! print can be used to see the value of a variable or to give feedback as to the state of a program. print generally outputs the standard output stream and is visible as pro‐ gram output in a shell. Range Though range is a built-in function, it is technically not a function at all. It is a type representing a sequence of numbers. When calling the range() constructor, an object representing a sequence of numbers is returned. Range objects count through a sequence of numbers. The range function takes up to three integer arguments. If only one argument appears, then the sequence is represented by the numbers from zero up to, but not including, that number. If a second argument appears, it represents the starting point, rather than the default of starting from 0. The third argument can be used to specify the step distance, and it defaults to 1. >>> range(10) range(0, 10) >>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(range(5, 10)) [5, 6, 7, 8, 9] >>> list(range(5, 10, 3)) [5, 8] >>> range maintains a small memory footprint, even over extended sequences, as it only stores the start, stop, and step values. The range function can iterate through long sequences of numbers without performance constraints. Execution Control Python has many constructs to control the flow of statement execution. You can group statements you wish to run together as a block of code. These blocks can be run multiple times using for and while loops or only run under certain conditions using if statements, while loops, or try-except blocks. Using these constructs is the first step to taking advantage of the power of programming. Different languages demarcate blocks of code using different conventions. Many languages with syntax similar to the C language (a very influential language used in writing Unix) use curly brackets around a group of statements to define a block. In Python, indentation is used to indicate a block. Statements are grouped by indentation into blocks that exe‐ cute as a unit. 6 | Chapter 1: Python Essentials for DevOps
  • 34.
    The Python interpreterdoes not care if you use tabs or spaces to indent, as long as you are consistent. The Python style guide, PEP-8, however, recommends using four whitespaces for each level of indentation. if/elif/else if/elif/else statements are common ways to branch between decisions in code. A block directly after an if statement runs if that statement evaluates to True: >>> i = 45 >>> if i == 45: ... print('i is 45') ... ... i is 45 >>> Here we used the == operator, which returns True if items are equal and False if not. Optionally, this block can follow an elif or else statement with an accompanying block. In the case of an elif statement, this block only executes if the elif evaluates to True: >>> i = 35 >>> if i == 45: ... print('i is 45') ... elif i == 35: ... print('i is 35') ... ... i is 35 >>> Multiple elif loops can append together. If you are familiar with switch statements in other languages, this simulates that same behavior of choosing from multiple choices. Adding an else statement at the end runs a block if none of the other condi‐ tions evaluate as True: >>> i = 0 >>> if i == 45: ... print('i is 45') ... elif i == 35: ... print('i is 35') ... elif i > 10: ... print('i is greater than 10') ... elif i%3 == 0: ... print('i is a multiple of 3') ... else: ... print('I don't know much about i...') ... Execution Control | 7
  • 35.
    ... i is amultiple of 3 >>> You can nest if statements, creating blocks containing if statements that only exe‐ cute if an outer if statement is True: >>> cat = 'spot' >>> if 's' in cat: ... print("Found an 's' in a cat") ... if cat == 'Sheba': ... print("I found Sheba") ... else: ... print("Some other cat") ... else: ... print(" a cat without 's'") ... ... Found an 's' in a cat Some other cat >>> for Loops for loops allow you to repeat a block of statements (a code block) once for each member of a sequence (ordered group of items). As you iterate through the sequence, the current item can be accessed by the code block. One of most common uses of loops is to iterate through a range object to do a task a set number of times: >>> for i in range(10): ... x = i*2 ... print(x) ... ... 0 2 4 6 8 10 12 14 16 18 >>> In this example, our block of code is as follows: ... x = i*2 ... print(x) 8 | Chapter 1: Python Essentials for DevOps
  • 36.
    We repeat thiscode 10 times, each time assigning the variable i to the next number in the sequence of integers from 0–9. for loops can be used to iterate through any of the Python sequence types. You will see these later in this chapter. continue The continue statement skips a step in a loop, jumping to the next item in the sequence: >>> for i in range(6): ... if i == 3: ... continue ... print(i) ... ... 0 1 2 4 5 >>> while Loops while loops repeat a block as long as a condition evaluates to True: >>> count = 0 >>> while count < 3: ... print(f"The count is {count}") ... count += 1 ... ... The count is 0 The count is 1 The count is 2 >>> It is essential to define a way for your loop to end. Otherwise, you will be stuck in the loop until your program crashes. One way to handle this is to define your conditional statement such that it eventually evaluates to False. An alternative pattern uses the break statement to exit a loop using a nested conditional: >>> count = 0 >>> while True: ... print(f"The count is {count}") ... if count > 5: ... break ... count += 1 ... ... The count is 0 while Loops | 9
  • 37.
    The count is1 The count is 2 The count is 3 The count is 4 The count is 5 The count is 6 >>> Handling Exceptions Exceptions are a type of error causing your program to crash if not handled (caught). Catching them with a try-except block allows the program to continue. These blocks are created by indenting the block in which the exception might be raised, putting a try statement before it and an except statement after it, followed by a code block that should run when the error occurs: >>> thinkers = ['Plato', 'PlayDo', 'Gumby'] >>> while True: ... try: ... thinker = thinkers.pop() ... print(thinker) ... except IndexError as e: ... print("We tried to pop too many thinkers") ... print(e) ... break ... ... ... Gumby PlayDo Plato We tried to pop too many thinkers pop from empty list >>> There are many built-in exceptions, such as IOError, KeyError, and ImportError. Many third-party packages also define their own exception classes. They indicate that something has gone very wrong, so it only pays to catch them if you are confident that the problem won’t be fatal to your software. You can specify explicitly which exception type you will catch. Ideally, you should catch the exact exception type (in our example, this was the exception IndexError). Built-in Objects In this overview, we will not be covering OOP. The Python language, however, comes with quite a few built-in classes. 10 | Chapter 1: Python Essentials for DevOps
  • 38.
    What Is anObject? In OOP, data or state and functionality appear together. The essential concepts to understand when working with objects are class instantiation (creating objects from classes) and dot syntax (the syntax for accessing an object’s attributes and methods). A class defines attributes and methods shared by its objects. Think of it as the techni‐ cal drawing of a car model. The class can then be instantiated to create an instance. The instance, or object, is a single car built based on those drawings. >>> # Define a class for fancy defining fancy cars >>> class FancyCar(): ... pass ... >>> type(FancyCar) <class 'type'> >>> # Instantiate a fancy car >>> my_car = FancyCar() >>> type(my_car) <class '__main__.FancyCar'> You don’t need to worry about creating your own classes at this point. Just under‐ stand that each object is an instantiation of a class. Object Methods and Attributes Objects store data in attributes. These attributes are variables attached to the object or object class. Objects define functionality in object methods (methods defined for all objects in a class) and class methods (methods attached to a class and shared by all objects in the class), which are functions attached to the object. In Python documentation, functions attached to objects and classes are referred to as methods. These functions have access to the object’s attributes and can modify and use the object’s data. To call an object’s method or access one of its attributes, we use dot syntax: >>> # Define a class for fancy defining fancy cars >>> class FancyCar(): ... # Add a class variable ... wheels = 4 ... # Add a method ... def driveFast(self): ... print("Driving so fast") ... ... Built-in Objects | 11
  • 39.
    ... >>> # Instantiatea fancy car >>> my_car = FancyCar() >>> # Access the class attribute >>> my_car.wheels 4 >>> # Invoke the method >>> my_car.driveFast() Driving so fast >>> So here our FancyCar class defines a method called driveFast and an attribute wheels. When you instantiate an instance of FancyCar named my_car, you can access the attribute and invoke the method using the dot syntax. Sequences Sequences are a family of built-in types, including the list, tuple, range, string, and binary types. Sequences represent ordered and finite collections of items. Sequence operations There are many operations that work across all of the types of sequences. We cover some of the most commonly used operations here. You can use the in and not in operators to test whether or not an item exists in a sequence: >>> 2 in [1,2,3] True >>> 'a' not in 'cat' False >>> 10 in range(12) True >>> 10 not in range(2, 4) True You can reference the contents of a sequence by using its index number. To access the item at some index, use square brackets with the index number as an argument. The first item indexed is at position 0, the second at 1, and so forth up to the number one less than the number of items: >>> my_sequence = 'Bill Cheatham' >>> my_sequence[0] 'B' >>> my_sequence[2] 'l' >>> my_sequence[12] 'm' 12 | Chapter 1: Python Essentials for DevOps
  • 40.
    Indexing can appearfrom the end of a sequence rather than from the front using neg‐ ative numbers. The last item has the index of –1, the second to last has the index of – 2, and so forth: >>> my_sequence = "Bill Cheatham" >>> my_sequence[–1] 'm' >>> my_sequence[–2] 'a' >>> my_sequence[–13] 'B' The index of an item results from the index method. By default, it returns the index of the first occurrence of the item, but optional arguments can define a subrange in which to search: >>> my_sequence = "Bill Cheatham" >>> my_sequence.index('C') 5 >>> my_sequence.index('a') 8 >>> my_sequence.index('a',9, 12) 11 >>> my_sequence[11] 'a' >>> You can produce a new sequence from a sequence using slicing. A slice appears by invoking a sequence with brackets containing optional start, stop, and step arguments: my_sequence[start:stop:step] start is the index of the first item to use in the new sequence, stop the first index beyond that point, and step, the distance between items. These arguments are all optional and are replaced with default values if omitted. This statement produces a copy of the original sequence. The default value for start is 0, for stop is the length of the sequence, and for step is 1. Note that if the step does not appear, the corre‐ sponding : can also be dropped: >>> my_sequence = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] >>> my_sequence[2:5] ['c', 'd', 'e'] >>> my_sequence[:5] ['a', 'b', 'c', 'd', 'e'] >>> my_sequence[3:] ['d', 'e', 'f', 'g'] >>> Built-in Objects | 13
  • 41.
    Negative numbers canbe used to index backward: >>> my_sequence[–6:] ['b', 'c', 'd', 'e', 'f', 'g'] >>> my_sequence[3:–1] ['d', 'e', 'f'] >>> Sequences share many operations for getting information about them and their con‐ tents. len returns the length of the sequence, min the smallest member, max the larg‐ est, and count the number of a particular item. min and max work only on sequences with items that are comparable. Remember that these work with any sequence type: >>> my_sequence = [0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4] >>> len(my_sequence) 12 >>> min(my_sequence) 0 >>> max(my_sequence) 4 >>> my_sequence.count(1) 3 >>> Lists Lists, one of the most commonly used Python data structures, represent an ordered collection of items of any type. The use of square brackets indicates a list syntax. The function list() can be used to create an empty list or a list based on another finite iterable object (such as another sequence): >>> list() [] >>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list("Henry Miller") ['H', 'e', 'n', 'r', 'y', ' ', 'M', 'i', 'l', 'l', 'e', 'r'] >>> Lists created by using square brackets directly are the most common form. Items in the list need to be enumerated explicitly in this case. Remember that the items in a list can be of different types: >>> empty = [] >>> empty [] >>> nine = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> nine [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> mixed = [0, 'a', empty, 'WheelHoss'] >>> mixed 14 | Chapter 1: Python Essentials for DevOps
  • 42.
    [0, 'a', [],'WheelHoss'] >>> The most efficient way to add a single item to a list is to append the item to the end of the list. A less efficient method, insert, allows you to insert an item at the index posi‐ tion of your choice: >>> pies = ['cherry', 'apple'] >>> pies ['cherry', 'apple'] >>> pies.append('rhubarb') >>> pies ['cherry', 'apple', 'rhubarb'] >>> pies.insert(1, 'cream') >>> pies ['cherry', 'cream', 'apple', 'rhubarb'] >>> The contents of one list can be added to another using the extend method: >>> pies ['cherry', 'cream', 'apple', 'rhubarb'] >>> desserts = ['cookies', 'paste'] >>> desserts ['cookies', 'paste'] >>> desserts.extend(pies) >>> desserts ['cookies', 'paste', 'cherry', 'cream', 'apple', 'rhubarb'] >>> The most efficient and common way of removing the last item from a list and return‐ ing its value is to pop it. An index argument can be supplied to this method, removing and returning the item at that index. This technique is less efficient, as the list needs to be re-indexed: >>> pies ['cherry', 'cream', 'apple', 'rhubarb'] >>> pies.pop() 'rhubarb' >>> pies ['cherry', 'cream', 'apple'] >>> pies.pop(1) 'cream' >>> pies ['cherry', 'apple'] There is also a remove method, which removes the first occurrence of an item. >>> pies.remove('apple') >>> pies ['cherry'] >>> Built-in Objects | 15
  • 43.
    One of themost potent and idiomatic Python features, list comprehensions, allows you to use the functionality of a for loop in a single line. Let’s look at a simple exam‐ ple, starting with a for loop squaring all of the numbers from 0–9 and appending them to a list: >>> squares = [] >>> for i in range(10): ... squared = i*i ... squares.append(squared) ... ... >>> squares [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> In order to replace this with a list comprehension, we do the following: >>> squares = [i*i for i in range(10)] >>> squares [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> Note that the functionality of the inner block is put first, followed by the for state‐ ment. You can also add conditionals to list comprehensions, filtering the results: >>> squares = [i*i for i in range(10) if i%2==0] >>> squares [0, 4, 16, 36, 64] >>> Other techniques for list comprehensions include nesting them and using multiple variables, but the more straightforward form shown here is the most common. Strings The string sequence type is a collection of ordered characters surrounded by quota‐ tion marks. As of Python 3, strings default to using UTF-8 encoding. You can create strings either by using the string constructor method, str(), or by directly enclosing the text in quotation marks: >>> str() '' >>> "some new string!" 'some new string!' >>> 'or with single quotes' 'or with single quotes' The string constructor can be used to make strings from other objects: >>> my_list = list() >>> str(my_list) '[]' 16 | Chapter 1: Python Essentials for DevOps
  • 44.
    You can createmultiline strings by using triple quotes around the content: >>> multi_line = """This is a ... multi-line string, ... which includes linebreaks. ... """ >>> print(multi_line) This is a multi-line string, which includes linebreaks. >>> In addition to the methods shared by all sequences, strings have quite a few methods distinct to their class. It is relatively common for user text to have trailing or leading whitespace. If some‐ one types " yes " in a form instead of “yes” you usually want to treat them the same. Python strings have a strip method just for this case. It returns a string with the whitespace removed from the beginning and end. There are also methods to remove the whitespace from only the right or left side of the string: >>> input = " I want more " >>> input.strip() 'I want more' >>> input.rstrip() ' I want more' >>> input.lstrip() 'I want more ' On the other hand, if you want to add padding to a string, you can use the ljust or rjust methods. Either one pads with whitespace by default, or takes a character argument: >>> output = 'Barry' >>> output.ljust(10) 'Barry ' >>> output.rjust(10, '*') '*****Barry' Sometimes you want to break a string up into a list of substrings. Perhaps you have a sentence you want to turn into a list of words, or a string of words separated by com‐ mas. The split method breaks a string into a list of strings. By default, it uses white‐ space as the token to make the breaks. An optional argument can be used to add in another character where the split can break: >>> text = "Mary had a little lamb" >>> text.split() ['Mary', 'had', 'a', 'little', 'lamb'] >>> url = "gt.motomomo.io/v2/api/asset/143" >>> url.split('/') ['gt.motomomo.io', 'v2', 'api', 'asset', '143'] Built-in Objects | 17
  • 45.
    You can easilycreate a new string from a sequence of strings and join them into a single string. This method inserts a string as a separator between a list of other strings: >>> items = ['cow', 'milk', 'bread', 'butter'] >>> " and ".join(items) 'cow and milk and bread and butter' Changing the case of text is a common occurrence, whether it is making the case uni‐ form for comparison or changing in preparation for user consumption. Python strings have several methods to make this an easy process: >>> name = "bill monroe" >>> name.capitalize() 'Bill monroe' >>> name.upper() 'BILL MONROE' >>> name.title() 'Bill Monroe' >>> name.swapcase() 'BILL MONROE' >>> name = "BILL MONROE" >>> name.lower() 'bill monroe' Python also provides methods to understand a string’s content. Whether it’s checking the case of the text, or seeing if it represents a number, there are quite a few built-in methods for interrogation. Here are just a few of the most commonly used methods: >>> "William".startswith('W') True >>> "William".startswith('Bill') False >>> "Molly".endswith('olly') True >>> "abc123".isalnum() True >>> "abc123".isalpha() False >>> "abc".isalnum() True >>> "123".isnumeric() True >>> "Sandy".istitle() True >>> "Sandy".islower() False >>> "SANDY".isupper() True 18 | Chapter 1: Python Essentials for DevOps
  • 46.
    You can insertcontent into a string and control its format at runtime. Your program can use the values of variables or other calculated content in strings. This approach is used in both creating user-consumed text and for writing software logs. The older form of string formatting in Python comes from the C language printf function. You can use the modulus operator, %, to insert formatted values into a string. This technique applies to the form string % values, where values can be a single nontuple or a tuple of multiple values. The string itself must have a conversion specifier for each value. The conversion specifier, at a minimum, starts with a % and is followed by a character representing the type of value inserted: >>> "%s + %s = %s" % (1, 2, "Three") '1 + 2 = Three' >>> Additional format arguments include the conversion specifier. For example, you can control the number of places a float, %f, prints: >>> "%.3f" % 1.234567 '1.235' This mechanism for string formatting was the dominant one in Python for years, and you encounter it in legacy code. This approach offers some compelling features, such as sharing syntax with other languages. It also has some pitfalls. In particular, due to the use of a sequence to hold the arguments, errors related to displaying tuple and dict objects are common. We recommend adopting newer formatting options, such as the string format method, template strings, and f-strings, to both avoid these errors and increase the simplicity and readability of your code. Python 3 introduced a new way of formatting strings using the string method format. This way of formatting has been backported to Python 2 as well. This specification uses curly brackets in the string to indicate replacement fields rather than the modulus-based conversion specifiers of the old-style formatting. The insert values become arguments to the string format method. The order of the arguments deter‐ mines their placement order in the target string: >>> '{} comes before {}'.format('first', 'second') 'first comes before second' >>> You can specify index numbers in the brackets to insert values in an order different than that in the argument list. You can also repeat a value by specifying the same index number in multiple replacement fields: >>> '{1} comes after {0}, but {1} comes before {2}'.format('first', 'second', 'third') 'second comes after first, but second comes before third' >>> Built-in Objects | 19
  • 47.
    An even morepowerful feature is that the insert values can be specified by name: >>> '''{country} is an island. ... {country} is off of the coast of ... {continent} in the {ocean}'''.format(ocean='Indian Ocean', ... continent='Africa', ... country='Madagascar') 'Madagascar is an island. Madagascar is off of the coast of Africa in the Indian Ocean' Here a dict works to supply the key values for name-based replacement fields: >>> values = {'first': 'Bill', 'last': 'Bailey'} >>> "Won't you come home {first} {last}?".format(**values) "Won't you come home Bill Bailey?" You can also specify format specification arguments. Here they add left and right pad‐ ding using > and <. In the second example, we specify a character to use in the padding: >>> text = "|{0:>22}||{0:<22}|" >>> text.format('O','O') '| O||O |' >>> text = "|{0:<>22}||{0:><22}|" >>> text.format('O','O') '|<<<<<<<<<<<<<<<<<<<<<O||O>>>>>>>>>>>>>>>>>>>>>|' Format specifications are done using the format specification mini-language. Our topic also uses another type of language called f-strings. Python f-strings use the same formatting language as the format method, but offer a more straightforward and intuitive mechanism for using them. f-strings are prepen‐ ded with either f or F before the first quotation mark. Like the format string previ‐ ously described, f-strings use curly braces to demarcate replacement fields. In an f- string, however, the content of the replacement field is an expression. This approach means it can refer to variables defined in the current scope or involve calculations: >>> a = 1 >>> b = 2 >>> f"a is {a}, b is {b}. Adding them results in {a + b}" 'a is 1, b is 2. Adding them results in 3' As in format strings, format specifications in f-strings happen within the curly brack‐ ets after the value expression and start with a :: >>> count = 43 >>> f"|{count:5d}" '| 43' The value expression can contain nested expressions, referencing variables, and expressions in the construction of the parent expression: 20 | Chapter 1: Python Essentials for DevOps
  • 48.
    >>> padding =10 >>> f"|{count:{padding}d}" '| 43' We highly recommend using f-strings for the majority of your string formatting. They combine the power of the specification mini-language with a simple and intuitive syntax. Template strings are designed to offer a straightforward string substitution mecha‐ nism. These built-in methods work for tasks such as internationalization, where sim‐ ple word substitutions are necessary. They use $ as a substitution character, with optional curly braces surrounding them. The characters directly following the $ iden‐ tify the value to be inserted. When the substitute method of the string template exe‐ cutes, these names are used to assign values. Built-in types and functions are available whenever you run Python code, but to access the broader world of functionality available in the Python ecosystem, you need to use the import statement. This approach lets you add functionality from the Python Standard Library or third-party services into your environment. You can selectively import parts of a package by using the from keyword: >>> from string import Template >>> greeting = Template("$hello Mark Anthony") >>> greeting.substitute(hello="Bonjour") 'Bonjour Mark Anthony' >>> greeting.substitute(hello="Zdravstvuyte") 'Zdravstvuyte Mark Anthony' >>> greeting.substitute(hello="Nǐn hǎo") 'Nǐn hǎo Mark Anthony' Dicts Aside from strings and lists, dicts may be the most used of the Python built-in classes. A dict is a mapping of keys to values. The lookup of any particular value using a key is highly efficient and fast. The keys can be strings, numbers, custom objects, or any other nonmutable type. A mutable object is one whose contents can change in place. Lists are a primary example; the contents of the list can change without the list’s identity changing. Strings are not mutable. You create a new string each time you change the contents of an existing one. Built-in Objects | 21
  • 49.
    Dicts are representedas comma–separated key/value pairs surrounded by curly braces. The key/value pairs consist of a key, a colon (:), and then a value. You can create a dict object using the dict() constructor. With no arguments, it cre‐ ates an empty dict. It takes a sequence of key/value pairs as an argument as well: >>> map = dict() >>> type(map) <class 'dict'> >>> map {} >>> kv_list = [['key-1', 'value-1'], ['key-2', 'value-2']] >>> dict(kv_list) {'key-1': 'value-1', 'key-2': 'value-2'} You can also create a dict directly using curly braces: >>> map = {'key-1': 'value-1', 'key-2': 'value-2'} >>> map {'key-1': 'value-1', 'key-2': 'value-2'} You can access the value associated with a key using square bracket syntax: >>> map['key-1'] 'value-1' >>> map['key-2'] 'value-2' You can use the same syntax to set a value. If the key is not in the dict, it adds as a new entry. If it already exists, the value changes to the new value: >>> map {'key-1': 'value-1', 'key-2': 'value-2'} >>> map['key-3'] = 'value-3' >>> map {'key-1': 'value-1', 'key-2': 'value-2', 'key-3': 'value-3'} >>> map['key-1'] = 13 >>> map {'key-1': 13, 'key-2': 'value-2', 'key-3': 'value-3'} If you try to access a key that has not been defined in a dict, a KeyError exception will be thrown: >>> map['key-4'] Traceback (most recent call last): File "<input>", line 1, in <module> map['key-4'] KeyError: 'key-4' You can check to see if the key exists in a dict using the in syntax we saw with sequences. In the case of dicts, it checks for the existence of keys: >>> if 'key-4' in map: ... print(map['key-4']) 22 | Chapter 1: Python Essentials for DevOps
  • 50.
    ... else: ... print('key-4not there') ... ... key-4 not there A more intuitive solution is to use the get() method. If you have not defined a key in a dict, it returns a supplied default value. If you have not supplied a default value, it returns None: >>> map.get('key-4', 'default-value') 'default-value' Use del to remove a key-value pair from a dict: >>> del(map['key-1']) >>> map {'key-2': 'value-2', 'key-3': 'value-3'} The keys() method returns a dict_keys object with the dict’s keys. The values() method returns an dict_values object, and the items() method returns key-value pairs. This last method is useful for iterating through the contents of a dict: >>> map.keys() dict_keys(['key-1', 'key-2']) >>> map.values() dict_values(['value-1', 'value-2']) >>> for key, value in map.items(): ... print(f"{key}: {value}") ... ... key-1: value-1 key-2: value-2 Similar to list comprehensions, dict comprehensions are one-line statements return‐ ing a dict by iterating through a sequence: >>> letters = 'abcde' >>> # mapping individual letters to their upper-case representations >>> cap_map = {x: x.upper() for x in letters} >>> cap_map['b'] 'B' Functions You have seen some Python built-in functions already. Now move on to writing your own. Remember, a function is a mechanism for encapsulating a block of code. You can repeat the behavior of this block in multiple spots without having to duplicate the code. Your code will be better organized, more testable, maintainable, and easier to understand. Functions | 23
  • 51.
    Another Random ScribdDocument with Unrelated Content
  • 52.
    The offer ofmercy was made a fourth time. A young Indian stepped out and received additional assurance that no harm should come if they surrendered. He went back into the cave and presently reappeared with another young warrior, supporting between them the tall, splendid figure of brave old American Horse. He had been shot through the bowels, and his intestines protruded from the wound. He was suffering frightful agony, and was biting hard upon a piece of wood to control himself. He handed his gun to Crook and gave up the contest. The surgeons with the command did everything they possibly could for him, but his wound was beyond human skill. That night, surrounded by his wives and children, he died, as stoically and as bravely as he had lived. Inside the cave the rocky walls were cut and scored by the rain of bullets which had been poured into it, and lying on the floor were the bodies of the two Indian warriors, together with a woman and a child, who had been killed. The soldiers had not known, until the squaws came out, that there were any women or children there. The little band had sold their lives dearly. Even the women had used guns, and had displayed all the bravery and courage of the Sioux. Too late Crazy Horse, with some six hundred warriors, appeared on the scene. Imagining he had only to deal with Mills’ small force, he galloped gallantly forward to the attack at about five o’clock. He was greatly astonished at the number of antagonists developed thereby. He retired to the top of the buttes, and the soldiers in gallant style dashed after him. They scaled the cliffs, finally gaining the level plateau. Crazy Horse made one or two attempts to break through the line, but it was impossible, and seeing himself greatly outnumbered, he wisely retired, having sustained some loss. The battle was one of the most picturesque ever fought in the West. Crook and his officers stood in the camp, the center of a vast amphitheater ringed with fire, up the sides of which the soldiers steadily climbed to get at the Indians, silhouetted in all their war finery against the sky. The loss of life on either side was not great,
  • 53.
    but the captureof the village and the provisions which had been accumulated for the winter was a serious one. In the camp were discovered many articles that had belonged to the Seventh Cavalry—a guidon, money, one of Captain Keogh’s gauntlets, marked with his name, orderly books, saddles, etc. Among other things, were letters written by officers and soldiers to friends in the East, some of them still sealed and ready for mailing. They must have come like voices from the dead when they reached those to whom they had been written. 99. One of the scouts killed in this battle was a great admirer of Buffalo Bill, whose manners, methods, and appearance he aped as well as he could. He rejoiced in an unfortunate sobriquet, which was received in this wise: General Sheridan, seeking Buffalo Bill to lead a hunting expedition on one occasion, was met by this swaggerer, with the remark that Buffalo Bill was gone away, and when Buffalo Bill was gone he was Buffalo Bill himself. “The h—l you are!” said Sheridan contemptuously. “Buffalo Chip, you mean!” The poor braggart never got away from the name of “Buffalo Chip Charlie.” He was a brave man for all his vanity, and the soldiers were sorry enough for their mockery when they buried him that night at the foot of the buttes, where he had fallen in the attack on the cave.
  • 54.
    C CHAPTER TEN A DecisiveBlow I. Mackenzie’s Winter Battle rook now gave over the pursuit, and returned to Fort Fetterman to organize a winter campaign. This expedition was one of the best equipped that ever started on an Indian campaign. It contained all arms of the service, with an abundance of everything necessary to success. To follow its marches to the Big Horn Range would reveal little of interest; but late in November it was learned, from a captured Cheyenne, that the principal Cheyenne village was located in a cañon through which flowed one of the main sources of Crazy Woman’s Fork of the Powder River. Colonel Ranald S. Mackenzie was ordered, with the Indian scouts and ten troops of cavalry from the Second, Fourth, and Fifth regiments, to find and destroy the village. The Cheyennes were not so numerous as the Sioux, and the greater number of their allies has sometimes caused people to minimize the quality of the Cheyennes; but no braver, more magnificent fighters ever lived than this same tribe. They had some of the Homeric qualities of the ancient Greeks. I believe it will generally be admitted that they were the finest of the Plains Indians. They were foemen worthy Mackenzie’s or anybody else’s steel. The battle which ensued was in some respects one of the most terrible in Western history, and in its results exemplified, as few others have done, the horrible character of the war. It was, perhaps, as great a contribution to the downfall of the Sioux as any single incident that occurred.
  • 55.
    Mackenzie’s men leftthe main encampment on the 23d of November. The ground was covered with snow. The weather was arctic in its severity. The scouts and friendly Indians—Pawnees, Crows, Shoshones, the hereditary enemies of the Cheyennes, including certain Cheyennes also who had entered the service of the United States[100] —had located the camp in Willow Creek Cañon. Some of the Indians had kept the camp under observation while Mackenzie brought up his troops. He had seven hundred and fifty cavalrymen and three hundred and fifty Indians. Halting at the mouth of the cañon, which he reached on the night of the 24th, he resolved to await the still hours before the break of day the next morning before delivering his attack. The cañon was a gloomy gorge in the Big Horn Mountains. A swift, ice-bound river rushed over the rocks between precipitous walls, which soared into the sky for perhaps a thousand feet on either side. Numberless icy brooks poured their contents into the main stream through lateral cañons scarcely less forbidding in their appearance than the main one, and which made the trail of the creek almost impossible. Here and there the cañon widened, and in one of these open places the Cheyennes, under the leadership of Dull Knife, had pitched their camp. They fondly believed the place impregnable—as, indeed, with careful guarding it would have been. The greatest precaution was taken by Mackenzie to prevent his men from making any noise. They stood in ranks by their horses in the snow in that polar cold, waiting for the order for the advance. Presently the moon rose, flooding the recesses of the ravine with silvery light, which sparkled with dazzling brilliancy upon patches of snow here and there on the dark walls. Mackenzie, calculating that day would be breaking just about the time he would reach the camp from his present position, at last gave order to take up the march. With what relief the benumbed troopers sprang to their saddles and urged their shivering horses forward, can scarcely be imagined by dwellers in peaceful lands around warm firesides. As they struggled up the cañon they could hear the sound of dancing and revelry in the Indian camp, faintly blown back to
  • 56.
    them by thenight wind. They learned afterward that the Cheyennes had just returned from a successful raid on the Shoshones, and that the dance was in celebration of an important victory they had gained. They halted again, therefore, until all was silence, before they once more advanced. Day was beginning to break as they reached the village. The sleeping Indians in the camp had not the slightest suspicion that the enemy was within a hundred miles. The troops, cheering and shouting, burst upon them like a winter storm. Indians, when not apprehensive of attack, invariably sleep naked. The Cheyennes had just time to seize rifles and cartridge belts, while the women caught hasty blankets about the children, when the soldiers were upon them. Indeed, so quick and sudden was the attack that some of the warriors could not get out of the tepees. With their knives they slashed the wigwams, and from these openings fired upon the soldiers as they galloped through the village. Many were shot dead where a few moments before they had slept in peace. Most of the pony herd was captured, and the village in a short time was in possession of Mackenzie. The Cheyennes, though overwhelmed, were undismayed. They had retreated headlong up the cañon, but were soon rallied by their subchiefs. Dull Knife, their leader, was found in the village with half a dozen bullets in him. He had fought gallantly in the open until he died. Presently the Indians came swarming back along the side of the cañon. They occupied points of vantage, and, naked though they were in the frightful weather, with the thermometer ranging from ten to twenty degrees below zero during this campaign, they opened fire upon their opponents. Unless they could be dislodged, Mackenzie’s position was untenable. He sent his Shoshone and other Indian scouts, who, animated with bitter hatred of the Cheyennes, were eager to obey his commands, to the summits of the cliffs to clear the Indians from them. Meanwhile he directed Lieutenant John A. McKinney, with his troop, to charge and drive the Indians from a rocky eminence where they
  • 57.
    were concentrating andfrom which they were pouring a hot fire upon the soldiers. McKinney’s charge was entirely successful, for he drove the Cheyennes back until he was stopped by a ravine. Wheeling his men, he attempted to find a crossing, when he was fired upon by a flanking party of Indians and instantly killed, being hit no less than six times. Six of his troopers were wounded, and a number of horses were shot. The troop was thrown into confusion, and some of the men started to retreat. Mackenzie, observing the situation, immediately ordered Captain John M. Hamilton and Major G. A. Gordon to charge to the rescue. The charge was gallantly made and stubbornly resisted. The fighting was hand to hand, of the fiercest description; and the Cheyennes, while keeping the rest of Mackenzie’s forces engaged, began concentrating on these two troops, which had been joined by Captain Davis, with his men. There was no reserve; the cavalry were all in, and this detachment might have been wiped out had it not been for the success of the Shoshones and other Indians, who cleared the key to the position on the summit of the plateau above the cañon, and then came to the assistance of the sorely beset soldiers. Twenty Cheyennes were killed here and several of the soldiers. Relieved in a measure by these two movements, although not altogether, for the Cheyennes with their superior knowledge of the topography of the country could not be entirely dislodged from their position, and kept up a fierce fire upon the soldiers all day long, to which he could make little reply, Mackenzie sent back word to Crook of his success, and meanwhile began the destruction of the village. All the winter supplies for over a thousand Indians were there. The Cheyennes were a forehanded, prosperous tribe of Indians, as Indians go, and the property destroyed was enormous. II. The Sufferings of the Cheyennes
  • 58.
    What must havebeen the despair of the surprised warriors, with their women and children, naked, shivering in the hills, as they saw their belongings consumed by the flames! It was simply impossible for them to maintain their position during the night. They had to move away or die of cold. As it was, twelve little Indian babies froze to death that awful night. Many of the older men and women were kept alive only by having their hands and feet, and in the case of the children, their whole bodies, thrust into the warm bodies of the few ponies not captured by the soldiers, which had been disemboweled for the purpose. Courtesy of The Century Co. MACKENZIE’S MEN IN DULL KNIFE’S VILLAGE Drawing by Frederic Remington There was no fighting on the 26th. The Cheyennes took up a strong position six miles farther up the cañon, from which Mackenzie could not dislodge them, and on the 27th he started on his return to the camp. Crook, who made a forced march night and day, with Colonel Dodge and the infantry, who came forward with astonishing speed in
  • 59.
    spite of stormand cold, met Mackenzie retiring just after he left the cañon, and the whole army returned to the encampment. The subsequent sufferings of the Indians were frightful. Naturally, they repaired to Crazy Horse, expecting that he would succor them, feed them, and clothe them. The Sioux and the Cheyennes had been warm friends and allies, and had fought together on many a field. Had they come in their prosperity, Crazy Horse would have given them a warm welcome. As it was, he had little with which to support his own band during the winter, owing to Crook’s pursuit of him, and with short-sighted, yet natural—from an Indian point of view—policy, he refused to receive these Cheyennes, or to share anything with them. Exasperated beyond measure by their treatment by the Sioux, and swearing eternal vengeance upon Crazy Horse, the wretched band struggled into the nearest agency and surrendered, and in the following spring moved out with the soldiers against Crazy Horse and his men. It is appalling to think of that night attack in that awful weather upon that sleeping camp—to read of those wretched women and children, wandering naked in that bitter cold; to learn of those little ones frozen to death; of the old men and women abandoned by the road to die—yet there is another side to the picture, scarcely less horrible. In this Indian camp also were found many relics of the Custer battle. So far as that is in question, I may say that I consider that action to have been a fair and square stand-up fight, in which one side was defeated and its members all died fighting.[101] Naturally, the Indians despoiled the slain for trophies. White soldiers have done the same when conditions have been reversed, as has been noted in the preceding chapters of this book. Of course, the Indians mutilated the dead and tortured the living, but some instances of both practices are found among white men, and we cannot judge the Indian by our standards, anyway.
  • 60.
    But in thecamp there were other evidences of savage ferocity, from which the soul shrinks in horror, and which showed that these Indians were among the most cruel and ruthless on the continent, and that they were only getting what they had given. Two instances will suffice. The troops took from the body of a dead warrior an unique necklace of human forefingers, which had been displayed with pride upon his barbaric breast;[102] and a bag was found which contained the right hands of twelve little Shoshone babies and children, which had been recently cut from little arms to give some ruthless warrior a ghastly trophy. 100. It is a singular thing to note the looseness of the tie with which the members of the various tribes were bound. Frequently we find bands of the same tribe fighting for and against the United States on the same field. One of the most fruitful causes of the success of our arms has been this willingness on the part of the Indians to fight against their own people, of which the government has been quick to avail itself. 101. See Preface for discussion of the term “Massacre.” 102. A picture of a similar necklace may be seen in Captain J. Lee Humfreville’s interesting book, “Twenty Years Among Our Hostile Indians.”
  • 61.
    N CHAPTER ELEVEN Miles’ GreatCampaigning I. Miles and His Foot Cavalry Defeat Sitting Bull ow let us turn to Miles and his men. General Miles was ordered to march his command up the Yellowstone to the mouth of the Tongue River, and establish a temporary post or cantonment there for the winter. He was an officer in whom great confidence was reposed, and from whom much was to be expected. He had as brilliant a record in the Civil War as Custer, and had practically fought one decisive battle in the closing campaign on his own responsibility, with splendidly successful results. He was a natural-born soldier, and he never showed his talents to better advantage than in the operations which followed. His career before and after this period is still fresh in the minds of a grateful people. While Crook and his men were hammering away in one portion of the field, Miles was doing splendid service in the other. The original intention had been to place under his command some fifteen hundred men, but the force he really received amounted only to about five hundred. With these he was not expected to do more than maintain his position, and acquire such information as he could in preparing for the spring and summer campaign of the following year. That was not, from his point of view, a satisfactory program.
  • 62.
    Veteran Indian fightersin the Northwest informed him that it would be useless to try to reach the Indians in the winter; but Miles was not that kind of a soldier. If the Indians could live in tepees in that season, he saw no reason why white soldiers should not move against them in spite of the weather. He had one of the finest regiments of infantry in the service—the Fifth. Based upon the report of courts-martial, discipline, etc., no regiment surpassed or even equaled its record. Miles himself proved to be the most successful commander against Indians that the war produced, and his success was not due to what envious people called good luck. It was well merited and thoroughly earned. The government, upon the representations of Sheridan and Sherman, which were based upon Miles’ previous successful fighting with the Southwestern Indians, allowed the young colonel everything he asked for. If his troops were not completely equipped for the work in which their commander designed to employ them, it would be his fault. With wise forethought, he provided the soldiers as if for an arctic expedition. They cut up blankets for underwear. They were furnished with fur boots and the heaviest kind of leggings and overshoes. Every man had a buffalo overcoat and a woolen or fur mask to go over his face under his fur cap. Their hands were protected by fur gloves. It was well for them that they were thus provided, for the winter of 1876–7 was one of the most severe that had ever visited that section of the country. The mercury frequently froze in the thermometer, and on one occasion a temperature of sixty degrees below zero was recorded by the spirit thermometer. Busying themselves during the late fall, which was, in effect, winter, in the erection of the cantonment on the Tongue and Yellowstone, the first important touch they got with the Indians was on the 18th of October, when Lieutenant-Colonel Elwell S. Otis, commanding a battalion of four companies of the Twenty-third Infantry, escorting a wagon-load of supplies from Glendive, Montana, to the cantonment, was attacked by a large force of hostiles. The attack was not delivered with any great degree of force at first, but it grew in power until the troops had to corral the train. The soldiers had a hard fight
  • 63.
    to keep theanimals from being stampeded and the train captured. Having beaten off the Indians, the train advanced, fighting, until Clear Creek was reached. During a temporary cessation of the attacks a messenger rode out from the Indian lines, waving a paper, which he left upon a hill in line with the advance of the train. When it was picked up, Colonel Otis found it to be an imperious message— probably written by some half-breed—from the chief whom he had been fighting. It ran as follows: “Yellowstone. “I want to know what you are doing traveling on this road. You scare all the buffalo away. I want to hunt in this place. I want you to turn back from here. If you don’t, I’ll fight you again. I want you to leave what you have got here, and turn back from here. I am your friend, Sitting Bull. “I mean all the rations you have got and some powder. Wish you would write me as soon as you can.” I consider this document unique in the history of Indian warfare, and it well illustrates not only the spirit, but the naïveté of the great chief. Otis despatched a scout to Sitting Bull with the information that he intended to take the train through to the cantonment in spite of all the Indians on earth, and if Sitting Bull wanted to have a fight, he (Otis) would be glad to accommodate him at any time and on any terms. The train thereupon moved out, and the Indians promptly recommenced the fight. But the engagement was soon terminated by a flag of truce. A messenger appeared, who stated that the Indians were tired and hungry and wanted to treat for peace. Otis asked Sitting Bull to come into his lines, but that wily old chief refused, although he sent three chiefs to represent him. Otis had no authority to treat for peace or anything else, but he gave the Indians a small quantity of hardtack and a couple of sides of bacon, and advised them to go to the Tongue River and communicate with General Miles. The train then moved on, and after
  • 64.
    following a shortdistance, with threatening movements, the Indians withdrew. On the same night Otis fell in with Miles and his whole force. Miles, being alarmed for Otis’ safety, had marched out to meet him. The train was sent down to the cantonment, and the troops, numbering three hundred and ninety-eight, with one gun, started out in pursuit of Sitting Bull. They overtook him on the 21st of October at Cedar Creek. With Sitting Bull were Gall and other celebrated chiefs, and one thousand warriors of the Miniconjous, San Arcs, Brulés, and Unkpapas, together with their wives and children, in all over three thousand Indians. Crazy Horse, with the Oglalas and Two Moon’s band of the Northern Cheyennes, were not with Sitting Bull, while Dull Knife’s band, as we have seen, had gone to Wyoming for the winter. The reason for this separation is obvious. They could better support the hardships of the winter, more easily find shelter, and with less difficulty escape from the pursuing soldiers, if they were broken up in smaller parties. Sitting Bull asked Miles for an interview, which was arranged. He was attended by a subchief and six warriors, Miles by an aide and six troopers. The meeting took place between the lines, all parties being on horseback. Sitting Bull wanted peace on the old basis. The Indians demanded permission to retain their arms, with liberty to hunt and roam at will over the plains and through the mountains, with no responsibility to any one, while the government required them to surrender their arms and come into the agencies. The demands were irreconcilable therefore. The interview was an interesting one, and although it began calmly enough, it grew exciting toward the end. Sitting Bull, whom Miles describes as a fine, powerful, intelligent, determined looking man, was evidently full of bitter and persistent animosity toward the white race. He said no Indian that ever lived loved the white man, and that no white man that ever lived loved
  • 65.
    the Indian; thatGod Almighty had made him an Indian, but He didn’t make him an Agency Indian, and he didn’t intend to be one. The manner of the famous chief had been cold, but dignified and courteous. As the conversation progressed, he became angry—so enraged, in fact, that in Miles’ words “he finally gave an exhibition of wild frenzy. His whole manner seemed more like that of a wild beast than a human being. His face assumed a furious expression. His jaws were tightly closed, his lips were compressed, and you could see his eyes glisten with the fire of savage hatred.”[103] One cannot help admiring the picture presented by the splendid, if ferocious, savage. I have no doubt that General Miles himself admired him. At the height of the conference a young warrior stole out from the Indian lines and slipped a carbine under Sitting Bull’s blanket. He was followed by several other Indians to the number of a dozen, who joined the band, evidently meditating treachery. Miles, who, with his aide, was armed with revolver only, promptly required these new auxiliaries to retire, else the conference would be terminated immediately. His demand was reluctantly obeyed. After some further talk, a second meeting was appointed for the morrow, and the conference broke up. During the night Miles moved his command in position to be able to intercept the movement of the Indians the next day. There was another interview with the picturesque and imperious savage, whose conditions of peace were found to be absolutely impossible, since they involved the abandonment of all the military posts, the withdrawal of all settlers, garrisons, etc., from the country. He wanted everything and would give nothing. He spoke like a conqueror, and he looked like one, although his subsequent actions were not in keeping with the part. Miles, seeing the futility of further discussion, peremptorily broke up the conference. He told Sitting Bull that he would take no advantage of the flag of truce, but that he would give him just fifteen minutes to get back to his people to
  • 66.
    prepare for fighting.Shouting defiance, the chiefs rode back to the Indian lines. There was “mounting in hot haste” surely, and hurried preparations were made for immediate battle on both sides. Watch in hand, Miles checked off the minutes, and exactly at the time appointed he ordered an advance. The Indians set fire to the dry grass, which was not yet covered with snow, and the battle was joined amid clouds of flame and smoke. Although outnumbered nearly three to one, the attack of the soldiers was pressed home so relentlessly that the Indians were driven back from their camp, which fell into the possession of Miles. The Sioux were not beaten, however, for the discomfited warriors rallied a force to protect their flying women and children, under the leadership of Gall and others, Sitting Bull not being as much of a fighter as a talker. They were led to the attack again and again by their intrepid chiefs. On one occasion, so impetuous was their gallantry that the troops were forced to form square to repel their wild charges. Before the battle was over—and it continued into the next day—the Indians had been driven headlong for over forty miles. They had suffered a serious loss in warriors, but a greater in the destruction of their camp equipage, winter supplies, and other property. Two thousand of them came in on the third day and surrendered, under promises of good treatment. Several hundred broke into small parties and scattered. Miles’ little force was too small to be divided to form a guard for the Indians who had been captured; and besides, he had other things to do, so he detained a number of the principal chiefs as hostages, and exacted promises from the rest that they would surrender at the Spotted Tail or Red Cloud Agency—a promise which, by the way, the great majority of them kept. Sitting Bull, Gall, and about four hundred others refused to surrender, and made for the boundary line, escaping pursuit for the time being. This was the first and most serious defection from the Indian Confederacy. It was followed by others. In a subsequent campaign,
  • 67.
    in the depthof winter, a battalion under Lieutenant Baldwin struck Sitting Bull’s depleted and starving camp on two separate occasions, inflicting further loss upon that implacable chieftain.[104] II. Miles’ Crushing Defeat of Crazy Horse at Wolf Mountain Late in December Miles, having practically eliminated Sitting Bull from the game, moved out against Crazy Horse. He had with him five companies of the Fifth Infantry and two of the Twenty-second, in all four hundred and thirty-six officers and men and two Napoleon guns. These guns were fitted with canvas wagon-tops, and were so disguised as exactly to resemble the supply wagons of the train. The men left the cantonment on the 29th of December, 1876. It had been learned that Crazy Horse was in the valley of the Tongue River, south of the Yellowstone. There were sharp skirmishes on the first and third of January between the advance and war parties of Indians, who were moving gradually up the Tongue toward the mountains. On the evening of the 7th of January, 1877, a young warrior and a woman were captured, belonging to those Cheyennes who were still with Crazy Horse and the Unkpapas, and were related to some of the principal members of the band. From them much was learned of the situation of the Indian position.
  • 68.
    From the collectionof J. Robert Coster GEN. JOHN GIBBON GEN. NELSON A. MILES GEN. WESLEY MERRITT GEN. ALFRED H. TERRY SOME FAMOUS INDIAN FIGHTERS The next morning, the weather being bitterly cold, the men moved out to attack the Indian camp. Crazy Horse’s warriors numbered between eight and nine hundred. He had posted his men on the cliffs surmounting a valley in the Wolf Mountains, a spur of the Big Horn Range, not far from Crook’s battle-ground on the Rosebud. The troops entered the valley in full view of the Indians occupying the heights. The position was well chosen; for in order to make the attack, the soldiers would have to climb straight up the walls to get at the Indians, who were enabled, by the configuration of the ground and by their numbers, almost to surround the soldiers. One reason why Crazy Horse was willing to fight was because of his great desire to get possession of the Indians recently captured.
  • 69.
    Seeing that CrazyHorse was willing to accept battle, Miles made his preparations deliberately. The troops, out of range of the Indians, calmly had breakfast and made their camp secure. Having done everything at his leisure, Miles moved out to the attack. The Sioux were plainly visible on the cliffs. They could be seen shaking their fists and brandishing their rifles as the soldiers slowly advanced through the deep snow which covered the ground. The Indians seemed absolutely confident that Miles was marching into a trap, that when he got into the cañon he would be unable to scale the slopes, and they would have him at their mercy. There was no ambush about it. The whole thing was open and plain. They had chosen their position and had invited the soldiers to make at them. There was, indeed, no other way for Miles to get to them, so cunningly had they taken advantage of the ground, except the way which lay open before them. As the troops drew nearer, the gestures of defiance and contempt were accompanied by yells and jeers. Among the things they shouted in their confident assurance of success were these significant words: “You have had your last breakfast!” Indeed, the grim prophecy did not seem unlikely of fulfilment. It might have been supposed that men, encumbered as were the soldiers with their heavy, winter clothing, could never have scaled those heights, especially in the face of such opposition as the redoubtable warriors of Crazy Horse would offer. If they did not succeed in clearing the cliffs of the Indians, they would probably be shot down in scores in the valley. They would then be forced to retreat to their train, if any of them were left alive to do so, and stand a siege; and as they were three or four hundred miles from any possible relieving force, and in the depth of a Dakota winter, that would mean a speedy annihilation. It was a serious risk to take, but no battle was ever won without taking risks, and the nice art of the soldier consists in knowing what risks to take and when to take them. Not the least of Miles’ claims to admiration as a commander
  • 70.
    was his determination,under all circumstances, to fight then and there. Undaunted by the threatening prospect and unmoved by the savage shouts and jeers, although some of the scouts who knew the Sioux language retorted in kind, the troops deployed, and at as rapid a pace as they could manage, started for the hills. The artillery was exposed and unlimbered, and the shells thrown into the Indian position caused great surprise and consternation. The key to the position was a high elevation upon the left. The Indians who held it were led by Big Crow, the chief medicine man. As the battle began he exposed himself freely between the lines, dressed in a magnificent Indian war shirt and bonnet, running up and down and yelling like a fiend. Miles massed a little column against Big Crow and the warriors defending the eminence. At the same time he ordered a general escalade of the cliff along the whole line. Under a heavy fire, which, however, like most plunging fires down the sides of mountains or slopes, did but little damage, the troops slowly toiled up the icy, snow-covered bluffs.[105] Led by Major Casey and Captains McDonald and Baldwin, the charge was delivered with the utmost resolution. It was not a dash. No men, encumbered as were those soldiers, could move rapidly up icy cliffs, covered, wherever the sharpness of the acclivity permitted, with from one to three feet of snow. It was rather a slow, dogged, determined crawl, with a stop every few moments to fire at some Indian silhouetted above them on the gray sky-line of that winter morning. The fighting for the high cliff on the left of the line was spirited and desperate. Finally, the men came to a hand-to-hand struggle. The Indians clung tenaciously to the post until Big Crow was shot, when the soldiers succeeded in dislodging them. This bluff commanded the lines. It was occupied by the troops, who poured an enfilading fire upon the army of Crazy Horse. The Indian position, therefore, became untenable, and fighting sullenly and stubbornly, they withdrew in good order, though closely pursued by the troops. In the
  • 71.
    latter part ofthe advance snow began to fall, and before the battle was closed the combatants were fighting in the midst of a blinding storm. Miles says that the moment at which the Indians turned their backs and began the retreat was one in which he felt relief scarcely to be expressed, so desperate had been the fighting, so difficult the ascent, and so doubtful the result. The Indians were pursued for some distance, and a large portion of their camp equipage, with supplies, was captured. On the whole, they had suffered a most disheartening and disorganizing defeat. Their ammunition was about gone, their confederates in other tribes had been captured, the main body of the redoubtable Cheyennes had been crushed and were starving, the Unkpapas, the Miniconjous, the Sans Arcs, and the Brulés had surrendered. The game was up. There was nothing for Crazy Horse and the exhausted remnant which remained faithful to him to do but to surrender, which they accordingly did in the early spring. III. The Capture of Lame Deer’s Village There remained, then, in the field practically but one band of sixty lodges,[106] under Lame Deer and Iron Star, who refused positively to surrender. The indefatigable and brilliantly successful Miles pursued this band, overtook it, surprised it one morning in May, captured the village, dispersed the greater portion of the Indians, and succeeded in isolating and surrounding Lame Deer and Iron Star, with half a dozen principal warriors. Miles was very desirous of taking them alive. He advanced with some of his officers toward the desperate little body of Indians who had been cut off from the fleeing mass of savages, making peace signs and crying peace words. The Indians were tremendously excited and remained on guard, but committed no act of hostility. Miles rode up, and leaning over the saddle, extended his hand to Lame Deer. The intrepid chieftain, who was quivering with emotion under his Indian stoicism, grasped the
  • 72.
    general’s hand andclung to it tightly. Iron Star took Baldwin’s hand. The other Indians came forward, reluctantly, with hands extended, and all was going well. At this juncture one of the white scouts, not knowing what was going on, dashed up to the group, and possibly under a misapprehension that the life of the commanding officer was threatened, covered Lame Deer with his rifle. The Indian, probably thinking that he was to be killed in any event, resolved to die fighting. Miles strove to hold him and to reassure him, but by a powerful wrench he freed himself, lifting his rifle as he did so, and pointing it straight at the general. Miles had been in many battles, but he was never nearer death than at that moment. His quickness and resource did not desert him. Just as the Indian’s finger pressed the trigger he dug his spurs into his horse and swung the animal aside in a powerful swerve. Lame Deer’s bullet, which missed him by a hair’s breadth, struck one of the escort and instantly killed him. Iron Star also drew away from Baldwin and raised his rifle, as the other Indian had done. None of them were so quick, however, as Lame Deer had been. The soldiers closing in had seen Lame Deer’s motion, and before any further damage was done by the Indians they were overwhelmed by a rapid fire, which stretched them all dead upon the ground. The fighting had been short, but exceedingly sharp. The troops lost four killed and seven wounded, the Sioux fourteen killed and a large number wounded. The band was completely broken up, and most of the Indians surrendered soon after.[107] Of all the Indians who had borne prominent parts in this greatest of our Indian wars with the savage tribes, there remained at large only the indomitable Sitting Bull, and he had escaped capture because, with a wretched band of starving but resolute followers, he succeeded in crossing the British Columbia boundary line. Crook’s persistence, Mills’ bold stroke, Mackenzie’s desperate dash up Willow Creek Cañon, Miles’ splendid campaigning, his hard fighting at Cedar Creek and Wolf Mountain, his pursuit of Lame Deer,
  • 73.
    his policy andskill in dealing with the critical situations which had arisen, at last brought peace to the blood-drenched land. The most important work ever done by the United States Army outside of the greater wars of the nation had been successfully and brilliantly accomplished. IV. Farewell to a Great Chief and His Hopes A note of the fate of the two chief antagonists of the United States may fittingly close this chapter. Sitting Bull returned to the United States, and surrendered to the army a few years later. Ever a malcontent, he was one of the moving spirits in the Ghost Dance uprising, which culminated in the battle of Wounded Knee in 1890, and he was killed by the Indian police while resisting arrest.[108] The end of Crazy Horse came sooner, in a mêlée in a guard-house on the 7th of September, 1877. He was stabbed in the abdomen, and died from the effects of the wound. He was dissatisfied always, in spite of his surrender, and had been conspiring to take the war- path again. Believing that his intentions had become known and that he would be rigorously dealt with on account of the discovery, he started to run amuck, with a knife of which he had become possessed by some means, in the guard-house. When the fracas was over, he was found on the ground, with a desperate wound in the abdomen. Whether the wound was given by the bayonet of the sentry at the door, whether the blow was delivered by some of the Indians who threw themselves upon him, and with whom he struggled, is a matter which cannot be determined. However it was come by, it was enough, for from the effects he died in a short time. So that was the melancholy end of Crazy Horse, the protagonist of these tales, and one of the most famous Indians that ever lived. Captain Bourke[109] thus describes him:
  • 74.
    “I saw beforeme a man who looked quite young, not over thirty years old, five feet eight inches high, lithe and sinewy, with a scar in the face. The expression of his countenance was one of quiet dignity, but morose, dogged, tenacious, and melancholy. He behaved with stolidity, like a man who realized that he had to give in to Fate, but would do so as sullenly as possible.... All Indians gave him a high reputation for courage and generosity. In advancing upon an enemy, none of his warriors were allowed to pass him. He had made himself hundreds of friends by his charity toward the poor, as it was a point of honor with him never to keep anything for himself, excepting weapons of war. I never heard an Indian mention his name save in terms of respect. In the Custer Massacre, the attack by Reno had first caused a panic among the women and children and some of the warriors, who started to flee; but Crazy Horse, throwing away his rifle, brained one of the incoming soldiers with his stone war-club, and jumped upon his horse.” Crazy Horse was a born soldier, whose talents for warfare and leadership were of the highest order. He had repulsed Reynolds on the Powder River, wresting a victory from apparent defeat. He had thrown himself in succession upon the columns of Crook on the Rosebud and of Custer on the Little Big Horn; and it must be admitted that he had not only checked, but had driven back, Crook by a crushing attack upon him, while he had annihilated half of Custer’s command. He had fought a desperate, and, from a military point of view, highly creditable, action with Crook’s vastly superior forces at Slim Buttes. The only man who had fairly and squarely defeated him was Miles at Wolf Mountain, and even there Crazy Horse managed to keep his force well in hand as he withdrew from the field. He would probably never have surrendered, had it not been for the defections around him, and for the disastrous defeat of the Cheyennes by Mackenzie, and the destruction of so much of his camp equipage at Wolf Mountain. As it was, he might have continued the fighting, had not his warriors been freezing and
  • 75.
    starving, and almostentirely out of ammunition. There was nothing left for the Indians but surrender. As one of them said to Miles: “We are poor compared with you and your force. We cannot make a rifle, a round of ammunition, or a knife. In fact, we are at the mercy of those who are taking possession of our country. Your terms are harsh and cruel, but we are going to accept them, and place ourselves at your mercy.” That summed up the situation, although the terms granted the Indians were very far from being harsh or cruel. So passed out of history the great war chief of the Sioux, one of the bravest of the brave, and one of the most capable and sagacious of captains in spite of his absurd name. He had many of the vices, perhaps all the vices, of his race; but he had all their rude virtues, too, and great abilities, which most of them lacked. Sitting Bull, wise, crafty, indomitable as he was, was not to be compared with him for a moment. It was a tragedy any way you look at it. You cannot but feel much admiration for those Sioux and Cheyennes—cruel, ruthless though they were. I bid good-by to them with a certain regret. Some one has said, as the Rosebud and the Little Big Horn marked the high-water of Indian supremacy in the Northwest, so the forgotten grave of Crazy Horse marks an ebb from which no tide has ever risen. As he passes to the happy hunting-ground in the land of the Great Spirit, I stand and salute him with a feeling of respect which I have gathered not only from a study of his career, but from the statements and writings of men who could best judge of his qualities —for they were the soldiers who fought him. NOTES ON THE LAME DEER FIGHT
  • 76.
    By Colonel D.L. Brainard, U. S. A.[110] The command, consisting of four troops of the Second Cavalry, “F,” “G,” “H,” and “L,” two companies of the Fifth Infantry, two of the Twenty-second Infantry, and a company of mounted scouts, all under command of Colonel Nelson A. Miles, left the cantonment on Tongue River May 1, 1877, and marched up Tongue River, with a view of intercepting a band of hostile Indians, under Lame Deer, known to be at or near the head-waters of the Rosebud River. The transportation consisted of bull teams, mule teams, and a few pack animals. The command marched up Tongue River four days, when the train was left in charge of a small guard, the main command pushing on with pack trains, the cavalry leading and the infantry following more slowly, striking across country toward the Rosebud River, marching day and night, stopping only long enough to make coffee for the men, and to rest and graze the animals. We bivouacked on the evening of the 6th in a deep valley near Little Muddy Creek, and about two o’clock the following morning were again in the saddle, moving silently and swiftly down the valley toward the Indian camp, which had been located the previous evening by White Bull, Brave Wolf, Bob Jackson, and the other scouts. The scouts had reported that the camp was only about six miles distant, but it was soon discovered that it was much farther than this, and at early dawn we were still some distance away. The command had been moving at a trot, but the gallop was immediately taken up, and just as the sun appeared above the horizon, we rounded a bend in the valley and came in sight of the Indian camp, which was located on the right side, close to the hills. At first we saw no Indians except a few boys guarding the ponies, which were grazing a little distance beyond the camp, but they came out immediately, and dropping in the grass, began to fire in our direction, though without effect. As we charged down on the camp, these Indians, together with squaws and children, ran for the hills, driving with them the few horses that were near the tepees. “H” Company, under command of Lieutenant Lovell H. Jerome, charged through the camp and beyond, capturing the pony herd. The other companies, all under command of Captain Ball, charged to the village, formed line to the right, deployed as skirmishers, and pursued the Indians up the hill. The hills were so steep at this point that it was necessary to dismount the command and advance on foot, the horses being sent around by an easier route to join us later near the summit of the hill. The line as formed was “F” troop (Tyler) on the right, “L” troop (Norwood) center, and “G” troop (Wheelan) on the left. The Indians were driven up over the hills, where they scattered like quail. Our horses were brought up, and mounting, we charged across the country for two or three miles, and later returned to the village.
  • 77.
    As my recollectionsserve me, four soldiers and fourteen Indians were killed, ten soldiers being wounded, myself being one of the number. About four hundred ponies were captured, which were afterward used for mounting a battalion of Infantry, which later performed much effective work in the field. There were over sixty tepees, in which we found tons of dried buffalo meat, a few arms, some ammunition, and a great many buffalo robes, saddles, and an assortment of camp property, all of which were burned that afternoon, thus so effectually crippling the band that the remnant came in and surrendered a few weeks later. We camped on the battle-ground that night, the following day moving back in the direction of our wagon train. One of the most interesting incidents of the fight occurred just as the troop to which I belonged (“L”) charged on the village. I saw General Miles riding toward the first tepee, near which were two Indians, followed by his orderly. He called out something to these Indians which I did not understand, but I later understood he had called on them to surrender. One of these was evidently the Chief, Lame Deer, for he wore a long head-dress of eagle feathers, the head-dress reaching to the ground. As Miles approached on horseback, the Chief walked rapidly toward him, with his hand extended, as though to shake hands, but when within ten or twelve feet of him, the Indian in the rear, who was said to be Iron Star, a son of Lame Deer, and also a medicine man of the tribe, called sharply to Lame Deer, presumably warning him of the approaching troops, and urging him to follow the other Indians to the hills. Lame Deer stopped, turned, hesitated, then ran back a few steps, and picking up a loaded carbine from the ground, fired point blank at General Miles, who, seeing the movement, wheeled his horse sharply and bent forward. The bullet passed over him, striking his orderly in the breast, killing him instantly. The Chief then ran up the steep hill, accompanied by the other Indian. The head-dress made a very conspicuous mark, and many shots were immediately fired in that direction. From his tottering steps we saw that the Chief was badly wounded, and at this point his companion, instead of escaping as he could have done, placed his arm around the Chief’s waist, and supported him up the hill. About this time the Chief drew a revolver, and without turning about, held it in rear of him and fired in our direction, the bullets striking the ground only a few feet in his rear. This act, we assumed, was one of defiance of a man who knew he could not escape, but who was game to the last. Iron Star supported the Chief until the latter fell, when he escaped over the hill, only to be killed by “G” troop, which had been pushing up on that side. After the devotion and bravery he had displayed in supporting Lame Deer up the hill, we were almost sorry he had not escaped alive. A few days later Bob Jackson told me that, on examining Lame Deer’s body after the fight, he had found that he had been hit seventeen times.
  • 78.
    Another incident whichillustrates the valor of the United States soldier was that of Private Leonard, Troop “L,” Second Cavalry, who had dropped behind to readjust his saddle, a couple of miles from the Indian camp. The command was moving rapidly, and the Indians slipped in between the rear of the column and this lone soldier. However, when he saw them he rode to the top of a hill, and lying down behind some rocks, held these Indians at bay for several hours until relief came to him. It was fortunate that relief came as it did, for he had nearly exhausted his ammunition in firing at these Indians, who had several times charged his position. 103. Personal Recollections of General Nelson A. Miles, U. S. A. 104. As an instance of Miles’ capacity in handling men, this is what Baldwin says in a private letter, afterward made public, of the orders he received: “When I was given command of this battalion opposite the mouth of Squaw Creek, and the General took command of a less number of men, it was a question as to which would find the hostile Indians, and with the only order or suggestion given by him in that earnest manner characteristic of him, he said, ‘Now, Baldwin, do the best you can. I am responsible for disaster, success will be to your credit; you know what my plans are, and what we are here for.’” There is a dashing, manly ring about such words which I rejoice to recognize. It is a great soldier who can first choose and then trust his subordinates. 105. At the battle of King’s Mountain, in the American Revolution, the small loss of life among the Americans was due to the fact that the English, trained marksmen though they were, firing down the slopes of the mountain, overshot their opponents, although they had them in full view all the way up the slope; and it is the tendency of troops always to do the same thing. Troops on a level usually fire too low, and the ground between the advancing lines of soldiers is often plowed up by bullets
  • 79.
    from the depressedmuzzles, which should have gone into the breasts of the approaching enemy. 106. Each lodge accounted for from five to ten persons. 107. See close of this chapter for another account of the Lame Deer Fight. 108. These affairs are to be discussed at length in a forthcoming volume. 109. “On the Border With Crook,” Captain John G. Bourke, U. S. A. 110. Colonel Brainard won his commission by his heroic conduct in the Greely Arctic Expedition, 1881–4.—C. T. B.
  • 80.
    T CHAPTER TWELVE What TheyAre There For A Sketch of General Guy V. Henry, a Typical American Soldier I. Savage Warfare he most thankless task that can be undertaken by a nation is warfare against savage or semi-civilized peoples. In it there is usually little glory; nor is there any reward, save the consciousness of disagreeable duty well performed. The risk to the soldier is greater than in ordinary war, since the savages usually torture the wounded and the captured. Success can only be achieved by an arduous, persistent, wearing down process, which affords little opportunity for scientific fighting, yet which demands military talents of the highest order. Almost anybody can understand the strategy or the tactics of a pitched battle where the number engaged is large, the casualties heavy, and the results decisive; but very few non-professional critics appreciate a campaign of relentless pursuit by a small army of a smaller body of mobile hostiles, here and there capturing a little band, now and then killing or disabling a few, until in the final round-up the enemy, reduced to perhaps less than a score, surrenders. There is nothing spectacular about the performance, and everybody wonders why it took so long. And as injustice and wrong have not been infrequent in the preliminary dealings between the government and the savages, the soldier, who has only to obey his orders, comes in for much unmerited censure from those who think darkly though they speak
  • 81.
    bitterly. Especially ishe criticized if, when maddened by the suffering, the torture, of some comrade, the soldier sinks to the savage level in his treatment of his ruthless foeman. No one justifies such a lapse, of course, but few there be who even try to understand it. The incessant campaigning in the Philippines, with its resulting scandals, is an instance in point. Long before the Spanish-American War and its Philippine corollary, however, our little army had shown itself capable of the hardest and most desperate campaigning against the Indians of the West—as difficult and dangerous a work as any army ever undertook. There was so much of it, and it abounded with so many thrilling incidents, that volumes could be written upon it without exhausting its tragedy, its romance. There were few soldiers who served beyond the Mississippi from 1865 to 1890 who did not participate in a score of engagements, whose lives were not in peril more than once in many a hard, but now forgotten, campaign.
  • 82.
    From the collectionof J. Robert Coster COL. RANALD S. MACKENZIE GEN. GUY V. HENRY CAPT. ANSON MILLS W. F. CODY (BUFFALO BILL) GROUP OF DISTINGUISHED INDIAN FIGHTERS All except General Henry from contemporary photographs One of the bravest of our Indian fighters was Guy V. Henry. Personally, he was a typical representative of the knightly American soldier. Officially, it was his fortune to perform conspicuous services in at least three expeditions subsequent to the Civil War. He was a West Pointer, and the son of another, born in the service at Fort Smith in the Indian Territory. Graduating in 1861, a mere boy, he participated in four years of the hardest fighting in the Civil War, from Bull Run to Cold Harbor. At the age of twenty-three, his merit won him the appointment of Colonel of the Fortieth Massachusetts Volunteers, “a regiment that was never whipped.” The tall, brawny
  • 83.
    Welcome to ourwebsite – the ideal destination for book lovers and knowledge seekers. With a mission to inspire endlessly, we offer a vast collection of books, ranging from classic literary works to specialized publications, self-development books, and children's literature. Each book is a new journey of discovery, expanding knowledge and enriching the soul of the reade Our website is not just a platform for buying books, but a bridge connecting readers to the timeless values of culture and wisdom. With an elegant, user-friendly interface and an intelligent search system, we are committed to providing a quick and convenient shopping experience. Additionally, our special promotions and home delivery services ensure that you save time and fully enjoy the joy of reading. Let us accompany you on the journey of exploring knowledge and personal growth! ebookultra.com