2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-29 00:31:09 +00:00

Compare commits

..

334 Commits

Author SHA1 Message Date
Jarrod Johnson ce1d89c741 Set nodename as hostname prior to post for el class distros 2021-11-04 07:56:04 -04:00
Jarrod Johnson cbee00d3bb Fix initial collective join
Initial collective join combined
with the orderly collective startup
hit a chicken and egg problem.

Disable initting on first enrollment
to let enrollment drive
that specific initialization to
restore behavior.
2021-07-19 10:26:05 -04:00
Jarrod Johnson 14bb841b2d Fix nodebmcreset partial success
The error handling is improved, to
catch both noderange global issues as well
as partial completions.
2021-06-08 09:13:56 -04:00
Jarrod Johnson 03a75ec67d Error if noderange includes too many )
By default, pyparsing consumes only as much of the
input as matches the grammar. Tell it to consume all
of the noderange and error if there's more string than
matches our grammar.
2021-06-04 14:21:45 -04:00
Jarrod Johnson 269300119a Skip newer agent behaviors with incompatible old ssh
The previous attempt to support older ssh failed to completely
enact old behavior when needed.
2021-06-04 13:41:47 -04:00
Jarrod Johnson a2edf46b8a Improve ctrl-c and other behaviors of osdeploy import
More reliably delete an import attempt to avoid odd behaviors.
2021-05-27 16:10:55 -04:00
Jarrod Johnson 74e011e964 Implement node name subtitution in nodeshell
If wanting to ssh to 'altenative interfaces', provide -s to facilitate that
behavior, with added flexibility
compared to previous incarnations.
2021-05-25 15:46:49 -04:00
Jarrod Johnson 66372bea0c Fix hang on ctrl-e,c,p,o
Leave the function to
consume termdata if data
is available.
2021-05-25 14:27:37 -04:00
Jarrod Johnson d250a3e850 Fix typo in confetty 2021-05-25 11:58:01 -04:00
Jarrod Johnson 49b69a2e19 Explicitly provide content-length 0 on 204
For some vintages of eventlet+apache, this is required
to avoid invalid responses from the server.
2021-05-25 10:19:11 -04:00
Jarrod Johnson 93d4847763 Conditionally apply agent to sshutil
Older OSes (RHEL7/SLES12) cannot
do ssh-keygen with an agent.

Degrade to classic confluent behavior when that happens.
2021-05-18 12:28:42 -04:00
Jarrod Johnson b283b50cc6 Improve error on nodeconfig parse errors
The current error is vague and confusing, clarify with
more data showing what went wrong.
2021-05-18 10:34:09 -04:00
Jarrod Johnson 1f320af8e5 Add error message for long profile names
If a profile name pushes the filename field of dhcp
beyond what it can support, log an event and
do not offer a corrupted
dhcp offer packet.
2021-05-18 09:44:27 -04:00
Jarrod Johnson 5fea265dc8 Change CSV to have unix style line endings
The excel dialog has nice minimal quoting, but DOS line endings.

Unix dialog has good line endings, but excessive quoting.

Create a hybrid dialect and use it for nodesensors output.
2021-05-13 10:39:03 -04:00
Jarrod Johnson b74d648e7f Fix refactored inline command run
This addresses the mistake in
refactoring the inline command
shortcut
2021-05-12 13:49:06 -04:00
Jarrod Johnson 380f21853c Merge branch 'master' into 3.2 2021-05-11 08:10:41 -04:00
Jarrod Johnson 9900d76b0c Merge remote-tracking branch 'lenovo/master' 2021-05-11 08:09:57 -04:00
Jarrod Johnson aa7701ea3c More usefully indicate unfound interfaces
Rather than no data, provide
not found error when requesting a currently unsupported name.
2021-05-11 08:02:19 -04:00
Jarrod Johnson 3044c662ad Merge pull request #55 from vmaneagit/patch-45
Update nodesupport.ronn
2021-05-11 07:59:24 -04:00
Jarrod Johnson 7e02d1d008 Merge pull request #57 from vmaneagit/patch-43
Update collective.ronn
2021-05-11 07:56:55 -04:00
Jarrod Johnson f321e06e6e Merge pull request #51 from vmaneagit/patch-49
Update nodersync.ronn
2021-05-11 07:50:47 -04:00
Jarrod Johnson 5c0af9ebc7 Merge pull request #50 from vmaneagit/patch-50
Update nodersync.ronn
2021-05-11 07:50:18 -04:00
Jarrod Johnson c146b50b5b Merge pull request #49 from vmaneagit/patch-51
Update nodereseat.ronn
2021-05-11 07:49:37 -04:00
Jarrod Johnson cae8b2d0f8 Merge pull request #48 from vmaneagit/patch-52
Update noderemove.ronn
2021-05-11 07:49:24 -04:00
Jarrod Johnson 2664adefa4 Merge pull request #47 from vmaneagit/patch-53
Update nodepower.ronn
2021-05-11 07:49:10 -04:00
Jarrod Johnson c5f03ee62d Merge pull request #46 from vmaneagit/patch-54
Update nodelicense.ronn
2021-05-11 07:49:00 -04:00
Jarrod Johnson a932e91b17 Merge pull request #45 from vmaneagit/patch-56
Update nodegroupattrib.ronn.tmpl
2021-05-11 07:48:45 -04:00
Jarrod Johnson 1c00aa3c4e Merge pull request #44 from vmaneagit/patch-57
Update confetty.ronn
2021-05-11 07:48:35 -04:00
Jarrod Johnson bd889f4c78 Merge pull request #43 from vmaneagit/patch-58
Update collate.ronn
2021-05-11 07:48:17 -04:00
Jarrod Johnson 409a0796a1 Merge pull request #42 from vmaneagit/patch-59
Update nodesupport.ronn
2021-05-11 07:48:04 -04:00
Jarrod Johnson 70ffef6b06 Merge pull request #41 from vmaneagit/patch-55
Update nodelicense.ronn
2021-05-11 07:47:53 -04:00
Jarrod Johnson 77a043faa6 Change apiclient to append if outputting to existing file 2021-05-05 17:45:39 -04:00
Jarrod Johnson d69eb6ef12 Change apiclient to append if outputting to existing file 2021-05-05 17:45:04 -04:00
Jarrod Johnson 984c3044fe Invalidate sealed token on new token
If a new token grant occurs, do not
retain stale token sealed, as it can be misleading.
2021-05-05 09:18:28 -04:00
Jarrod Johnson 8543129fb9 Invalidate sealed token on new token
If a new token grant occurs, do not
retain stale token sealed, as it can be misleading.
2021-05-05 09:17:42 -04:00
Jarrod Johnson 257edff3a7 Redo ssh certs on firstboot for SUSE
For whatever reason, SUSE loses the certificate during firstboot.
2021-05-03 16:47:24 -04:00
Jarrod Johnson 763feda63b Fix missed references to old variable names 2021-05-03 15:54:39 -04:00
Jarrod Johnson 508e5b4a46 Fix suse run_remote_parts
Behave more consistently
2021-05-03 15:36:27 -04:00
Jarrod Johnson 7676c4fd26 Restore EL8 treatment
The changes caused EL8 to lose some required special treatment.
2021-05-03 15:18:33 -04:00
Jarrod Johnson c3b954a743 Move suse15 to log like rhel 2021-05-03 14:50:04 -04:00
Jarrod Johnson a94a341582 Fix ansible path compatibility. 2021-05-03 14:31:28 -04:00
Jarrod Johnson 08dfd55ea0 Rename variables in the ubuntu initramfs portion 2021-05-03 13:28:28 -04:00
Jarrod Johnson cf6975ded3 Fix python2/3 handling in el8 pre.sh 2021-05-03 13:11:52 -04:00
Jarrod Johnson d17b1d060c Prepend confluent_ to vars and switch to explicitly requesting directory
The 'profile' variable notably induces dracut to be excruciatingly slow,
mitigate chance by putting confluent before apikey, mgr, and profile.

Further, it has been requested to have the scripts use same name on
server for directory moving forward. Implement this request while
allowing existing OS profiles to keep working.
2021-05-03 12:48:08 -04:00
Jarrod Johnson fcb4b33e8e Include stderr in the script execution logs 2021-05-03 08:57:59 -04:00
Jarrod Johnson 121e0727cf Add Rocky linux recognition 2021-05-03 08:36:57 -04:00
Jarrod Johnson b5b9631b3f Correct typo in pre script 2021-04-30 16:27:16 -04:00
Jarrod Johnson c09f96b8c9 Remove pivot_root from genesis
It is not used (if we did use something, it would be switch_root).
2021-04-30 16:20:58 -04:00
Jarrod Johnson 196ed31bee Retry node api token retrieval, with message 2021-04-30 15:17:02 -04:00
Jarrod Johnson bb05f72583 Add source_remote_parts
This function will enable a script to invoke multiple
pieces in same variable/function context
2021-04-30 14:33:02 -04:00
Jarrod Johnson 68b4f83627 Have firstboot script be logged better
Also show on screen firstboot script execution.
2021-04-30 14:08:42 -04:00
Jarrod Johnson 672bc4f754 Amend postscript logging strategy
This adjusts to avoid hangs in
anaconda
2021-04-30 13:38:31 -04:00
Jarrod Johnson 4df3fda8ea Change logging/display for RH oses
The pre and post phases are
desirable to see both in logs and consoles.

Unfortunately, anaconda cheaps out by using communicate(), which means
the logs are unwritten until the end.

Change by using bash's redirectiors
to create a dynamically updated logfile
and tail -f that.
2021-04-30 09:39:00 -04:00
Jarrod Johnson f32619aa3e Allow firstboot to reboot in RH postscripts
Sometimes firstboot scripts want to reboot.

In such a case, start firstboot once, and if reboot prevents completion,
then next boot will updatestatus, but not run firstboot scripts and
deregister itself.
2021-04-29 09:20:24 -04:00
Jarrod Johnson dd7b9f0244 Bump genesis version 2021-04-28 16:44:24 -04:00
Jarrod Johnson def534e73f Fixes for boot by DHCP and by MAC 2021-04-28 16:43:11 -04:00
Jarrod Johnson 2cb641e734 Fix PXE based on mac
We normally use UUID, on a broken platform with bad UUID,
user may need to use hwaddr.  This was supposed to work, but
didn't. Fix it to work correctly.
2021-04-28 15:36:12 -04:00
Jarrod Johnson ca78b664c9 Get rid of minor warning about RSAAuthentication 2021-04-28 14:06:57 -04:00
Jarrod Johnson 0e55d390e3 Fix up post output to console 2021-04-28 13:46:26 -04:00
Jarrod Johnson 002146b2f7 Try to output pre/post content to console during install 2021-04-28 13:27:34 -04:00
Jarrod Johnson 917a51a406 Error if bind to privileged port fails 2021-04-28 08:37:23 -04:00
Jarrod Johnson d3fa08d78b Amend syncfiles to handle directory targets better
Rather than using symlinks for directories, recursively recreate tree
and symlink only the non-directory
entries.

This improves mixing and matching
files and directories to target a directory.
2021-04-27 12:48:27 -04:00
Jarrod Johnson 38eb88249e Permit wildcard in syncfiles
This was a supported and used feature in xCAT
syncfiles, carry it into confluent syncfiles.
2021-04-27 09:12:11 -04:00
Jarrod Johnson c333c3eb9c Fix missing data on XCC discovery
The XCC scan now calls the
imm scan to fill out the lost data.
2021-04-26 08:57:52 -04:00
Jarrod Johnson f32a9a2f08 Rework inline command handling
Previously, if hotkey entry
had text data come in, it
would corrupt the state of
the client.

Minimize the corruption and request the server to pause.
2021-04-23 14:22:24 -04:00
Jarrod Johnson f584b9bc11 Protect against binary sealed data
It was detected that binary sealed data
could happen. Ensure that it is str
before passing to configuration.
2021-04-23 14:17:54 -04:00
Jarrod Johnson affbbcc89b Fix syncfiles for el7 2021-04-23 12:27:42 -04:00
Jarrod Johnson 45cc4b7788 Add documentation syncfiles examples
Also, check to make sure a syncfiles has actual
work to do before triggering a greenthread and
the client to start polling.
2021-04-23 11:09:35 -04:00
Jarrod Johnson 945f108548 Fix inconsistencies between el7 and el8
While there are still differences, minimize
the ones that aren't for specific reasons
2021-04-23 09:08:50 -04:00
Jarrod Johnson 1f9c440525 Handle non-utf8 console data
Sometimes console uses non-utf8.

Fallback to cp437 when utf8 fails us.
2021-04-22 15:14:52 -04:00
Jarrod Johnson 8397c5defc Only close datfile if datfile is a file
If going by filename alone, there will be
no file handle to close. Correct by not
attempting a close in such a case.
2021-04-22 13:55:10 -04:00
Jarrod Johnson b7af6b5c27 Add model name to discovery info
Sometimes the model name is
useful criteria for evaluating systems,
and the model number isn't
quite that handy.

For XCC, we can provide this data too. Provide it in xcc scan
method and then offer it up to clients.
2021-04-22 13:38:51 -04:00
Jarrod Johnson c38aac88df Avoid overwriting customized installdisk or partitioning 2021-04-22 12:12:58 -04:00
Jarrod Johnson 6d1348f5fa Add pre.d to EL8 profiles 2021-04-22 12:08:25 -04:00
Jarrod Johnson c05f18d865 Remove abortive custom names for pre.sh.custom 2021-04-22 11:48:05 -04:00
Jarrod Johnson bf00c75f43 Specify supported protocols to be confluent.console
Eventlet will return no supported protocols if
none are specified. Firefox doesn't care, but
chrome rejects such a reply.
2021-04-22 07:44:18 -04:00
Jarrod Johnson 2e04647c95 Add xz to confluent genesis
xz provides better compression than gzip, handy
to have as an option.
2021-04-21 16:59:33 -04:00
Jarrod Johnson 479ddd582e Induce python2 to unicode
eventlet will treat str as binary,
correct by forcing unicode.

For python3, it is a no-op, but
python2 needs it to pass the 'text_type' check in eventlet.
2021-04-21 15:33:27 -04:00
Jarrod Johnson d82690f0d9 Fix python3-ism
python2 doesn't understand how to return
an iterator from within an iterator.
2021-04-21 13:31:11 -04:00
Jarrod Johnson 34cfd99fd8 Periodic reassimilation
Have leader periodically try to
assimilate offline members.

This will recover from some scenarios
where a rogue non-quorum collective
or a stray non-collective state
persists.
2021-04-21 10:30:54 -04:00
Jarrod Johnson 9009f63b2d Implement websocket based terminal access 2021-04-20 15:49:07 -04:00
Jarrod Johnson ab6bf82da6 Begin draft work to support websocket
First use case will be consoles.

Then we can consider the async case
for smoother async operation as well
though that is less critical.
2021-04-19 17:04:26 -04:00
Jarrod Johnson 0fae615756 Have nodediscover properly set exitcode
It was not propogating error to the caller in script context
2021-04-19 08:58:08 -04:00
Jarrod Johnson 37dca72579 Allow managercandidates to limit switch interrogation
If a switch is a node and has denoted allowed managers,
do not interrogate that switch if this member is not
permitted.
2021-04-16 14:19:51 -04:00
Jarrod Johnson 1aea406852 Restore error handling to SNMP
Properly carry and reconstitute exceptions
used to drive the specific errors.
2021-04-16 14:09:44 -04:00
Jarrod Johnson 374b87e2d7 Offload macmap SNMP activity to an auxillary process
This doesn't make the code more efficient, but it
keeps it from slowing down the main process
and allows it to leverage an additional core
to do the work.

Still needs work to restore the error reporting.
2021-04-15 17:22:15 -04:00
Jarrod Johnson 3469c8ab2b Fix affluent communication
Affluent would hang on error, particularly
certificate problem.  Fix by catching and being
explicit about the error, and generically
fixing the arguments for 'get' to have
the inntended timeout.
2021-04-14 15:51:18 -04:00
Jarrod Johnson e08ca5fb7e Have confetty show correct trace more easily
In some scenarios, the quitconfetty will overwrite the
stack trace.

Get the relevant trace before trying the quitconfetty, then print
the saved trace.
2021-04-14 10:10:44 -04:00
Jarrod Johnson 23dffe882e Do a keepalive to track idle, but alive collective
When relaying a command, use a keepalive to indicate that
the connection is alive, just not sending data.
2021-04-09 17:27:00 -04:00
Jarrod Johnson b6fb91b228 Have follower thread avoid killing self 2021-04-09 15:36:35 -04:00
Jarrod Johnson ac4d1fa8e1 More collective startup/recovery cleanup
If assimilated during startup, stop doing startup.
2021-04-09 13:32:32 -04:00
Jarrod Johnson 1d902c4d90 Backoff if trying to find a currently live server 2021-04-09 11:49:56 -04:00
Jarrod Johnson f11687998f Fix typo in cosmetic message 2021-04-08 20:48:46 -04:00
Jarrod Johnson 153956b2cd Further refine collective startup behavior 2021-04-08 18:26:59 -04:00
Jarrod Johnson 64d5081be3 Fix collective retry logic
It erroneously treated a thread as a bool, need
to check if None to know if it is scheduled.
2021-04-08 15:56:33 -04:00
Jarrod Johnson 8d16b412ae Further refine collective start process
Serialize assimilation, do not induce activity that may have been
aborted by an earlier chain.

Further, accelerate initial startup by making potential timeouts
occur concurrently, rather than sequentially.
2021-04-08 13:44:20 -04:00
Jarrod Johnson c5ec34d5a5 Actually hook in new assimilation behavior
Additionally, use collective member name as
tiebreaker if txcount and follower count is
identical.
2021-04-07 13:15:36 -04:00
Jarrod Johnson 6a4086679c Rework collective assimilation logic
Followers will only depart if their current leader
is assimilated.

Leaders with quorum will refuse assimilation and instruct
member trying to assimilate to join it.

Leaders without quorum will either follow the assimilation leader
or refuse, depending on who has highest transaction count, and if
a tie, which has the larger set of followers
2021-04-07 13:05:02 -04:00
Jarrod Johnson 3d2422ded3 Coerce iterator to list for length check 2021-04-07 08:40:18 -04:00
Jarrod Johnson 7d0f47bbcb Correct syntax for measuring collective size 2021-04-07 08:31:29 -04:00
Jarrod Johnson 9a779f2dd2 Improve collective startup behavior
Have confluent focus on establishing quorum before
initiating headless console and discovery activity.
2021-04-07 07:58:10 -04:00
Jarrod Johnson 4724d9e45b Eliminate one source of stale data leaking
If nodes are swapped, then clearing the attributes
did not clear some mappings.
2021-04-06 18:19:13 -04:00
Jarrod Johnson 8404ddf3a2 Fix import of sortutil 2021-04-06 10:43:37 -04:00
Jarrod Johnson 872a13589c Sort collective members
Improve consistency of output by sorting the output.
2021-04-06 09:35:34 -04:00
Jarrod Johnson 433a22e6e7 Finish rename of variable 2021-04-06 08:22:51 -04:00
Jarrod Johnson 1f238a3f8b Fix variable that was not declared after refactor 2021-04-06 08:20:10 -04:00
Jarrod Johnson 670fc87e1d Address several collective issues
When a stream has been deleted from cfgstreams, contiue exception
handling since the desired result.

For connections to a manager, institute a 15 second socket level timeout.
This avoids an abandoned server conversation from locking a colleective member startup.

When scheduling the failover check, first block any redundant attempts to schedule.

Wrap the collective startup in an exception
handler, so that a retry is
more well guaranteed.
2021-04-05 16:39:41 -04:00
Jarrod Johnson 3be3d4a588 Always send null byte out stdout
If the buffer doesn't exist or if it does,
either way make sure to write out a null
byte to let the caller know that it
is done.
2021-04-02 14:01:25 -04:00
Jarrod Johnson 14d13749ad Guarantee consoleserver init before use
During a restart, a client may aggressively trigger
console reconnect before the consoleserver starts.

Make sure that the daemon is running and globals
ready before API could possible ask for console.
2021-04-02 13:57:45 -04:00
Jarrod Johnson 92a10ede11 Pull in tmt fixes from Martin Hughes fork
Fix SGR being applied and fix default fg/bg color situation.
2021-04-02 13:13:19 -04:00
Jarrod Johnson d9051e80d3 Fix console buffer interaction
In some environments, the read method on the pipe object fails
to work, os.read should be the same, but seems to work better and
is happy to perform the opportunisticly large reads I want.
2021-04-02 12:29:41 -04:00
Jarrod Johnson 2d43fac1b5 Add a contingency for temporary read error 2021-04-02 11:54:38 -04:00
Jarrod Johnson 4a0d419f85 Improve behavior on python2 systems 2021-04-02 11:47:14 -04:00
Jarrod Johnson 7fb5f9756d Explictly request newer C version
C99 is needed for tmt, might as well go GNU11
2021-04-02 10:36:56 -04:00
Jarrod Johnson b482410072 Have server pull in vtbufferd 2021-04-01 17:31:52 -04:00
Jarrod Johnson 4d47f58e53 Make tar creation less verbose 2021-04-01 17:22:10 -04:00
Jarrod Johnson c5bb55e8f2 Fix name in the rpm spec 2021-04-01 17:21:33 -04:00
Jarrod Johnson 2320718618 Fix build issues with the vtbuffer daemon 2021-04-01 17:17:34 -04:00
Jarrod Johnson d64b3e6a4e Fix up build process for vtbufferd 2021-04-01 17:15:35 -04:00
Jarrod Johnson 637e0f0a81 Draft RPM spec for vtbufferd component 2021-04-01 16:44:40 -04:00
Jarrod Johnson e26218eb92 Implement SGR recreation and fix ECH
The tmt library had a bug with ECH with cursor position.

Flesh out the dump_vt to translate the attributes to VT codes
for the buffer to look the same as the attributes indicate.
2021-04-01 16:28:28 -04:00
Jarrod Johnson 03eb026b61 Improve VT dump behavior
Ensure the resultant dump has consistently long lines.  This
will matter when we start rendering attributes on blanks.

Additionally, simplify the code
significantly taking advantage
of first loop.
2021-04-01 12:51:32 -04:00
Jarrod Johnson beab6a3c02 Migrate VT buffering to c
C implementation to improve memory and cpu utilization.

Forked off to further move the work off the main process.

Still needs attribute rendition and packaging before merging to main
branch.
2021-03-31 17:28:26 -04:00
Jarrod Johnson 5e0ebce300 Add logs for offering boot
Make it easier to debug a failure
to boot due to misconfiguration.
2021-03-29 16:58:03 -04:00
Jarrod Johnson 85c4ec5654 Skip fqdn in cert generation
There are scenarios where getqfdn can induce a hang.
The certificate having FQDN isn't that useful anyway,
since confluent never uses it and external use of it
may need more carefully crafted certificate to have
a good chance of matching it anyway.

Also, the chances a user would import our cert as a
CA to something like a browser are low.
2021-03-29 14:29:42 -04:00
Jarrod Johnson 7957a6abd4 Add invoking syncfile activity to the default profiles 2021-03-29 14:03:11 -04:00
Jarrod Johnson 39a7d8d131 Move the syncfileclient into scripts
This does not need to be in initramfs,
so do not grow addons.cpio.
2021-03-29 13:55:44 -04:00
Jarrod Johnson 54c5bf128e Finish syncflieclient 2021-03-29 13:50:07 -04:00
Jarrod Johnson ca0c592044 Commence work on syncfileclient
This will be used to wait for deployer
to finish, then execute handlers
for 'MERGE' entries.
2021-03-25 16:55:56 -04:00
Jarrod Johnson 829d1316b2 Remove 'APPEND:'
If possible, we wannt to stick to 'MERGE', since the handlers
are designed to make that
idempotent for repeat.

APPEND: would not be idempotent...
2021-03-25 12:37:21 -04:00
Jarrod Johnson c2c1c85651 Increase verbosity of syncfiles 2021-03-25 08:59:16 -04:00
Jarrod Johnson c5833f1417 Reserve some characters for special syntax
May need to modify some behaviors in future, provide
a healthy supply of reserved characters toward that end.
2021-03-25 08:57:48 -04:00
Jarrod Johnson 07ae3593c3 Syncfiles fixes 2021-03-24 17:33:26 -04:00
Jarrod Johnson 8ab35b11cd Add -a notation about syncfiles relevance 2021-03-24 16:01:48 -04:00
Jarrod Johnson 35ef6170ba Implement syncfiles server side 2021-03-24 16:00:54 -04:00
Jarrod Johnson d650f11255 Begin work on syncfiles concept for confluent 2021-03-23 17:32:45 -04:00
Jarrod Johnson b4926b39fe Fix CA in installer env in ubuntu 2021-03-22 12:16:32 -04:00
Jarrod Johnson 53ccee734f Add more lvm dependencies 2021-03-22 11:00:21 -04:00
Jarrod Johnson fe9d0e84d3 Ubuntu firstboot ansible and scripts directory support 2021-03-22 10:36:14 -04:00
Jarrod Johnson 4e7e12f90f Add ansible to ubuntu post and scripts/post.d 2021-03-22 10:35:11 -04:00
Jarrod Johnson 8ae68cd34a Merge pull request #40 from vmaneagit/patch-60
Update nodesetboot.ronn
2021-03-22 08:05:59 -04:00
vmaneagit 607d69b1e1 Update nodesetboot.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Description chapter was updated
2021-03-22 13:47:04 +02:00
vmaneagit 75e13239c2 Update nodesupport.ronn 2021-03-22 13:40:49 +02:00
vmaneagit c88d8d047a Update collate.ronn
OPTIONS chapter has been updated to match the output from confetty -h
2021-03-22 13:38:13 +02:00
vmaneagit 304fbba2bb Update confetty.ronn
OPTIONS chapter has been updated to match the output from confetty -h
2021-03-22 13:31:54 +02:00
vmaneagit d2f35ccbde Update nodegroupattrib.ronn.tmpl
Changes made for Option chapter:
 - added -h command
2021-03-22 12:40:46 +02:00
vmaneagit 273c32d4a7 Update nodelicense.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Update typo in Description chapter: "Using delete will remove the specified license name from  th eBMC."
2021-03-22 12:38:55 +02:00
vmaneagit 1616999cb9 Update nodelicense.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:36:24 +02:00
vmaneagit 195db5c6e8 Update nodepower.ronn
Changes made for Option chapter:
 - added -m command
2021-03-22 12:34:23 +02:00
vmaneagit fa0e216c7e Update noderemove.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:29:35 +02:00
vmaneagit 1605169b92 Update nodereseat.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:28:17 +02:00
vmaneagit 7fa0016536 Update nodersync.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:26:51 +02:00
vmaneagit b4d26c3c4e Update nodersync.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:21:28 +02:00
vmaneagit 7470d9b55c Update nodesupport.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-22 12:06:57 +02:00
vmaneagit aaf1fb8c94 Update collective.ronn
Changes made for Option chapter:
 - added -h command
2021-03-22 12:01:24 +02:00
Jarrod Johnson 50e62c7b73 Bump release to pick up new lvm content 2021-03-19 19:24:31 -04:00
Jarrod Johnson 363eff831f Add devicemapper modules to genesis
This enables at least common lvm access for rescue purposes.
2021-03-19 18:52:18 -04:00
Jarrod Johnson 31820ddfd7 Fix ansible support of suse15
Must install python3 and we must have the python apiclient
intact with appropriate CA certs to proceed
2021-03-19 16:07:59 -04:00
Jarrod Johnson 153749aebe Change ssh duringe suse 15 post
Have port 22 be inside the target environment, and
2222 point to the installer. This improves ansible
experience.
2021-03-19 15:13:37 -04:00
Jarrod Johnson 7c5dd85e74 Copy in ansible to genesis profiles
Make it clearer that there could be ansible support in genesis
2021-03-19 13:09:21 -04:00
Jarrod Johnson 7a5bf8a06f Merge pull request #23 from vmaneagit/patch-25
Update noderun.ronn
2021-03-19 11:04:28 -04:00
Jarrod Johnson 9c05057deb Merge pull request #24 from vmaneagit/patch-26
Update nodemedia.ronn
2021-03-19 11:04:22 -04:00
Jarrod Johnson 8b1902601f Merge pull request #28 from vmaneagit/patch-30
Update nodepower.ronn
2021-03-19 11:04:17 -04:00
Jarrod Johnson d94d9d0a78 Merge pull request #32 from vmaneagit/patch-35
Update nodefirmware.ronn
2021-03-19 11:04:09 -04:00
Jarrod Johnson 457b0851c9 Merge pull request #33 from vmaneagit/patch-36
Update nodeeventlog.ronn
2021-03-19 11:04:05 -04:00
Jarrod Johnson d2cae9faca Merge pull request #34 from vmaneagit/patch-37
Update nodedeploy.ronn
2021-03-19 11:02:24 -04:00
Jarrod Johnson e27739eafd Merge pull request #37 from vmaneagit/patch-40
Update nodebmcreset.ronn
2021-03-19 11:02:16 -04:00
Jarrod Johnson 874302537d Merge pull request #36 from vmaneagit/patch-39
Update nodeboot.ronn
2021-03-19 11:01:56 -04:00
Jarrod Johnson cd3b67d4cc Merge pull request #38 from vmaneagit/patch-41
Update nodeattrib.ronn.tmpl
2021-03-19 11:01:41 -04:00
Jarrod Johnson 7aacac2fb7 Merge pull request #39 from vmaneagit/patch-42
Update confluentdbutil.ronn
2021-03-19 11:01:27 -04:00
Jarrod Johnson a69fb3fec3 Merge pull request #35 from vmaneagit/patch-38
Update nodeconfig.ronn
2021-03-19 11:01:13 -04:00
Jarrod Johnson 2d9cbe4f33 Merge pull request #22 from vmaneagit/patch-24
Update nodesupport
2021-03-19 11:00:57 -04:00
vmaneagit e33ed0afc8 Update confluentdbutil.ronn
Changes made for Option chapter:
 - added -h command
2021-03-19 16:55:09 +02:00
vmaneagit ace6544444 Update nodeattrib.ronn.tmpl
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-19 16:50:59 +02:00
vmaneagit 1468c56e1f Update nodebmcreset.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command
2021-03-19 16:48:40 +02:00
vmaneagit b36756ebbc Update nodeboot.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Synopsis chapter was reduced to single line
2021-03-19 15:28:33 +02:00
vmaneagit a8f378deb7 Update nodeconfig.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Synopsis chapter was reduced to single line
2021-03-19 15:22:14 +02:00
vmaneagit cb4e53c459 Update nodedeploy.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Synopsis chapter was reduced to single line
2021-03-19 15:20:08 +02:00
vmaneagit 3e0b283593 Update nodeeventlog.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Synopsis chapter was reduced to single line
2021-03-19 15:17:41 +02:00
vmaneagit 3b4e9fd2e4 Update nodefirmware.ronn
Changes made for Option chapter:
 - updated -m command
 - added -h command

Synopsis chapter was reduced to the same line as the usage:

nodefirmware <noderange> [list][update [--backup <file>]]|[<components>]
2021-03-19 15:06:14 +02:00
vmaneagit 6af6195798 Update nodepower.ronn
Changes made for Option chapter:

 - added -h command
2021-03-19 14:22:47 +02:00
vmaneagit a8d1f484b5 Update nodemedia.ronn
Changes made for Option chapter:
 - added -h command
- removed -c command
2021-03-19 13:17:30 +02:00
vmaneagit 619c60533d Update noderun.ronn
Changes made for Option chapter:
 - added -h command
 - updated -m command
2021-03-19 13:13:03 +02:00
vmaneagit c6fd760006 Update nodesupport
Updated line 119 with to show  "<filename or directory>"
2021-03-19 11:49:46 +02:00
Jarrod Johnson 03f88c6e3e Swap ssh during post phase to installed image
Preserve installer access on 2222

This enables easier ansible usage.
2021-03-18 15:53:30 -04:00
Jarrod Johnson 697b33ae80 Put a lower bound on autocons geometry
If some glitch happens during the read, do not end up with
absurdly low geometry.
2021-03-18 15:48:25 -04:00
Jarrod Johnson 79ef1c4811 Correct typo in functions update 2021-03-18 14:58:53 -04:00
Jarrod Johnson f442e949aa Add firstboot/post ansible play support to SUSE15 2021-03-18 14:37:54 -04:00
Jarrod Johnson 8050d3f69a Add playbook examples for default genesis 2021-03-18 14:30:18 -04:00
Jarrod Johnson 66efa1da5c Implement remote config for EL8 2021-03-18 14:12:19 -04:00
Jarrod Johnson df97e808c6 Wire up client retrieval of remoteconfig
remoteconfig can now watch for completion and return
data to client
2021-03-17 17:46:27 -04:00
Jarrod Johnson 98d14344ce Have runansible complete execution
It now completes runs and stores results for retrieval by deploying node.
2021-03-17 16:54:37 -04:00
Jarrod Johnson a149b0bcd0 Merge branch 'master' into ansibleplay 2021-03-17 10:25:57 -04:00
Jarrod Johnson 0be1e350e9 Merge pull request #21 from vmaneagit/patch-23
Update stats
2021-03-17 10:24:41 -04:00
Jarrod Johnson 4818bd57bb Advance state of runansible
Begin work to wire the results back to the supervisor and ultimately
back to the node affected.
2021-03-16 17:19:23 -04:00
Jarrod Johnson 7763327a63 Skip confluent import on exec
In main context, we don't need
sshutil, and python path is not
cooperative, so just skip it.
2021-03-16 16:23:27 -04:00
Jarrod Johnson dac957364f Merge branch 'master' into ansibleplay 2021-03-16 14:54:53 -04:00
Jarrod Johnson 528dd4c3e8 Merge branch '3.1' 2021-03-16 14:54:42 -04:00
Jarrod Johnson e40c362743 Fix network configuration changes through collective 2021-03-16 14:54:26 -04:00
Jarrod Johnson 7fdb4dccca Fix network configuration changes through collective 2021-03-16 14:53:55 -04:00
Jarrod Johnson a3c8c305c1 Add ansible supervisory code 2021-03-16 14:19:44 -04:00
Jarrod Johnson aeb1b704b3 Add mechanism to supply data from argv
Some POST calls may want body from CLI, make this
easier by no longer requiring a file to do so.
2021-03-16 11:18:46 -04:00
Jarrod Johnson f2cb6ea535 Merge branch 'master' into ansibleplay 2021-03-16 10:46:54 -04:00
Jarrod Johnson 5a81279b9d Fix servicedata fetch
In the new framework with
filehandle passing, the provided
callback must accept a data
argument.  get_diags can't
actually use it, but does have
to accept a token value.
2021-03-15 16:45:43 -04:00
vmaneagit 9954bf5b51 Update stats
Updated stats script with lines 40-41

Added:

if gui and mpl.get_backend() == 'agg':
        sys.stderr.write('Error: No GUI backend available and -g specified!\n')
2021-03-15 13:57:29 +02:00
Jarrod Johnson 309ea00f10 Merge branch 'master' into ansibleplay 2021-03-13 12:54:01 -05:00
Jarrod Johnson df328d6812 Have tty2 also run a shell
Some may find switching VTs to be more intuitive
than tmux session management. Provide one extra
on tty2 for such a scenario.
2021-03-13 12:49:26 -05:00
Jarrod Johnson 7da0dfa0bb Have tmux keep reattaching
If someone accidentally detaches, then intervene and
reattach.
2021-03-13 12:47:48 -05:00
Jarrod Johnson d3a699a8fb Have autocons attempt sizing of serial console
If a terminal is open during autocons, that terminal
will be the size of the console.

Otherwise, fallback to 100x31.
2021-03-13 12:33:53 -05:00
Jarrod Johnson 82598d02d8 Merge branch 'master' into ansibleplay 2021-03-12 13:56:02 -05:00
Jarrod Johnson 8032ea36be Fix local console behavior in genesis
This will have the vga console be less concerning.
2021-03-12 13:55:14 -05:00
Jarrod Johnson 8ad130e14c Fix local console behavior in genesis
This will have the vga console be less concerning.
2021-03-12 13:54:36 -05:00
Jarrod Johnson 5aa6fbc019 Fix typo in confluent profile.yaml
The quiet argument was missed due to typo in parameter name.
2021-03-12 12:42:57 -05:00
Jarrod Johnson 7026fdfa3a WIP change to enhance selfservice api for ansible
Begin work on extending selfservice to allow nodes
to request the deployer run ansible plays.
2021-03-12 12:23:58 -05:00
Jarrod Johnson 9dbecd4c95 Merge branch 'master' into ansibleplay 2021-03-12 12:23:48 -05:00
Jarrod Johnson d85219d5fa Merge branch 'master' of github.com:lenovo/confluent 2021-03-12 12:22:20 -05:00
Jarrod Johnson 26782811d6 Fix configbmc gateway check
It erroneously linked to prefix to check gateway or not.
2021-03-11 16:27:17 -05:00
Jarrod Johnson e38dbc4470 Pull in the automation key into default profiles 2021-03-10 15:41:01 -05:00
Jarrod Johnson a26624a614 Use ssh-agent to store keys
Also add the 'automation' key for ansible to
take advantage of.
2021-03-10 15:41:01 -05:00
Jarrod Johnson 6a88f35fc2 Fix compatibility with some switch configurations
While some implementations mess up portid and need portdescr instead, others are
just the opposite.

Tolerate match either by description or name.
2021-03-10 13:40:47 -05:00
Jarrod Johnson 7a27fba94b Workaround non-cisco switch crash
Querying Cisco MIB on certain
firmware levels of non-cisco switches
causes a crash.  Tolerate and
wait a bit to give SNMP a chance to restart.
2021-03-10 09:51:05 -05:00
Jarrod Johnson 2a87c32800 Handle malformed json data more gracefully 2021-03-08 02:55:58 -05:00
Jarrod Johnson d99bc37ed3 Fix missing exit code in nomededia
nodemedia was not setting return code on exit properly.
2021-03-08 08:01:19 -05:00
Jarrod Johnson 2c40d2fd0d Merge pull request #14 from vmaneagit/patch-22
Update nodesupport.ronn
2021-03-08 07:49:13 -05:00
Jarrod Johnson 611927d2ff Merge pull request #15 from vmaneagit/patch-16
Update nodelicense.ronn
2021-03-08 07:49:03 -05:00
Jarrod Johnson eb9ae61e75 Merge pull request #16 from vmaneagit/patch-17
Update noderemove.ronn
2021-03-08 07:48:51 -05:00
Jarrod Johnson 4ae102018a Merge pull request #17 from vmaneagit/patch-18
Update nodereseat.ronn
2021-03-08 07:48:42 -05:00
Jarrod Johnson 4f01545328 Merge pull request #18 from vmaneagit/patch-19
Update nodersync.ronn
2021-03-08 07:48:25 -05:00
Jarrod Johnson 762fb8259d Merge pull request #19 from vmaneagit/patch-20
Update noderun.ronn
2021-03-08 07:48:00 -05:00
Jarrod Johnson 2ca146c1aa Merge pull request #20 from vmaneagit/patch-21
Update nodesetboot.ronn
2021-03-08 07:47:38 -05:00
vmaneagit 16f40efb69 Update nodesupport.ronn
OPTIONS section has been added to match the output from nodesupport -h
2021-03-08 10:46:37 +02:00
vmaneagit a51a428761 Update nodesetboot.ronn
OPTIONS section has been updated to match the output from nodesetboot -h
2021-03-08 10:30:21 +02:00
vmaneagit a5261cc1da Update noderun.ronn
OPTIONS section has been added to match the output from noderun -h
2021-03-08 10:23:47 +02:00
vmaneagit c8166b7cce Update nodersync.ronn
OPTIONS section has updated to match the output from nodersync -h
2021-03-08 10:14:17 +02:00
vmaneagit a11317c994 Update nodereseat.ronn
OPTIONS section has been added to match the output from nodereseat -h
2021-03-08 10:06:08 +02:00
vmaneagit 279f622026 Update noderemove.ronn
OPTIONS section has been updated to match the output from noderemove -h
2021-03-08 09:58:33 +02:00
vmaneagit 32fa07d9ce Update nodelicense.ronn
OPTIONS section has been addeed to match the output from nodelicense -h
2021-03-08 09:51:42 +02:00
Jarrod Johnson 2e63ff3aca Close other places that may be false negative
Have checks for neightable be preceeded by an attempt to refresh,
to mitigate false negatives.
2021-03-05 13:14:41 -05:00
Jarrod Johnson e3c17491e5 Attempt refresh of neigh table on miss
When an address is new it may not be in the last
captured neighbor table. Induce refresh before deciding
that neighbor is unavailable.
2021-03-05 13:08:25 -05:00
Jarrod Johnson da7b7c8923 Add a script to filter passwd/shadow/group for replication 2021-03-05 10:52:25 -05:00
Jarrod Johnson 5861af799c Merge pull request #13 from vmaneagit/patch-13
Update nodegroupattrib.ronn.tmpl
2021-03-05 09:21:33 -05:00
Jarrod Johnson 2b0353c771 Merge pull request #12 from vmaneagit/patch-12
Update nodefirmware.ronn
2021-03-05 09:21:16 -05:00
Jarrod Johnson bc6c3c5e68 Merge pull request #11 from vmaneagit/patch-11
Update nodeeventlog.ronn
2021-03-05 09:20:53 -05:00
Jarrod Johnson 37ec16058f Merge pull request #10 from vmaneagit/patch-10
Update nodedeploy.ronn
2021-03-05 09:20:32 -05:00
Jarrod Johnson b3efd2f31d Merge pull request #9 from vmaneagit/patch-9
Update nodeconfig.ronn
2021-03-05 09:20:16 -05:00
Jarrod Johnson 357ebfca3f Merge pull request #8 from vmaneagit/patch-8
Update nodeboot.ronn
2021-03-05 09:19:52 -05:00
vmaneagit 7e0cd9d709 Update nodegroupattrib.ronn.tmpl
OPTIONS section has been updated to match the output from nodegroupattrib -h
2021-03-05 15:56:27 +02:00
vmaneagit ba321551e4 Update nodefirmware.ronn
OPTIONS section has been added to match the output from nodefirmware -h
2021-03-05 15:51:52 +02:00
vmaneagit d96026ef5f Update nodeeventlog.ronn
OPTIONS section has been added to match the output from nodeeventlog -h
2021-03-05 15:46:11 +02:00
vmaneagit 08aa667b13 Update nodedeploy.ronn
OPTIONS chapter has been updated to match the output from nodedeploy -h
2021-03-05 11:49:24 +02:00
vmaneagit 0cb93c4bea Update nodeconfig.ronn
OPTIONS chapter has been updated to match the output from nodeconfig -h
2021-03-05 11:44:02 +02:00
vmaneagit 7b3ecc6e4f Update nodeboot.ronn
OPTIONS chapter has been updated to match the output from nodeboot -h
2021-03-05 11:37:42 +02:00
Jarrod Johnson 5a5b3f9927 Correct incorrect function call on image import 2021-03-04 16:55:26 -05:00
Jarrod Johnson 4455226ee0 Merge pull request #7 from vmaneagit/patch-7
Update nodebmcreset.ronn
2021-03-04 16:25:36 -05:00
Jarrod Johnson 0881d74731 Merge pull request #6 from vmaneagit/patch-6
Update nodeattrib.ronn.tmpl
2021-03-04 16:25:26 -05:00
Jarrod Johnson 55d7191573 Merge pull request #5 from vmaneagit/patch-5
Update confluentdbutil.ronn
2021-03-04 16:25:19 -05:00
Jarrod Johnson bc9d0a9d3b Merge remote-tracking branch 'lenovo/master' 2021-03-04 14:05:48 -05:00
Jarrod Johnson 866f138652 Fix mistakes in sample mofed post script 2021-03-04 13:30:00 -05:00
vmaneagit ee8ad66959 Update nodebmcreset.ronn
OPTIONS chapter has been added to match the output from nodebmcreset -h
2021-03-04 18:52:10 +02:00
vmaneagit be0e47142b Update nodeattrib.ronn.tmpl
OPTIONS chapter has been updated to match the output from nodeattrib -h
2021-03-04 18:45:29 +02:00
vmaneagit 4131f1ba4f Update confluentdbutil.ronn
OPTIONS chapter has been updated to match the output from confluentdbutil -h
2021-03-04 18:36:48 +02:00
Jarrod Johnson f2966bfeaf Merge pull request #4 from vmaneagit/patch-4
Update confetty.ronn
2021-03-04 11:16:38 -05:00
Jarrod Johnson eee6330aaf Merge pull request #3 from vmaneagit/patch-3
Update collective.ronn
2021-03-04 11:15:52 -05:00
Jarrod Johnson aa9ece529f Merge pull request #2 from vmaneagit/patch-2
Update collate.ronn
2021-03-04 11:15:36 -05:00
Jarrod Johnson 865d18d367 Back off progress update on file copy
The output was too frenzied, cool it down to fewer
updates.
2021-03-04 11:01:17 -05:00
Jarrod Johnson b27d07f304 Fix nic index map with bonding
The assumption that /sys/class/net is interfaces is incorrect,
when encountering entries that are not interfaces, do not
mess up the call.
2021-03-04 10:47:28 -05:00
vmaneagit 50e60a5a17 Update confetty.ronn
OPTIONS chapter has been added to match the output from confetty -h
2021-03-04 17:33:59 +02:00
vmaneagit 8bf7e045d5 Update collective.ronn
SYNOPSIS chapter has been updated to match the output from collective -h
2021-03-04 17:05:50 +02:00
vmaneagit f0f4427dc3 Update collate.ronn
SYNOPSIS and OPTIONS chapters have been updated to match the output from collate -h
2021-03-04 16:52:30 +02:00
Jarrod Johnson b1f1f15ba8 Fix filecopy and enhnance
shutil was copying from wrong
index. Stop using shutil and
bring it in, and instrument the
percentage.
2021-03-04 09:29:31 -05:00
Jarrod Johnson 09ca449eab Fix iso import distinfo.yaml
If a copy method (e.g. ubuntu) is used, then distinfo.yaml is not
written. Fix by not overwriting the directory name.
2021-03-04 09:02:43 -05:00
Jarrod Johnson d08ce9e563 Correct fd used in libarchive
Actually use the cloned file descriptors
2021-03-04 08:43:17 -05:00
Jarrod Johnson ce92ea4084 Separate buffered and raw operation on filehandle
python and libarchive need different seek behaviors
with default file objects.
2021-03-04 08:30:05 -05:00
Jarrod Johnson 0695f8e827 Change seek method
The wrapper around seek would sometimes change offsets before
passing to lseek.  Directly call lseek since python appears
to be making some sort of mistake in some cases.
2021-03-04 07:55:18 -05:00
Jarrod Johnson 7609a46594 Merge remote-tracking branch 'lenovo/master' 2021-03-03 14:46:51 -05:00
Jarrod Johnson 247838f70c Redirect non-enclosure reseat to nodes
This allows new PSU AC cycle functionality to work.
2021-03-03 13:01:25 -05:00
Jarrod Johnson 594d419ca0 Merge pull request #1 from vmaneagit/patch-1
Update nodeshell.ronn
2021-03-02 15:57:09 -05:00
vmaneagit da0c83513a Update nodeshell.ronn
Updated SYNOPSIS and OPTIONS chapters
2021-03-02 22:51:48 +02:00
Jarrod Johnson 5ed0b50110 Fix non-client fallback
If client doesn't provide file,
then we need to open by name.
2021-03-02 15:15:54 -05:00
Jarrod Johnson e01a7cf4fb Merge branch 'master' into passfilehandle 2021-03-02 14:05:54 -05:00
Jarrod Johnson ad92ab13f3 Correct mistake in nodediscover correction 2021-03-02 14:05:28 -05:00
Jarrod Johnson 236e5466cb Merge branch 'master' into passfilehandle 2021-03-02 13:22:16 -05:00
Jarrod Johnson cc16e58c3a Merge branch '3.1' 2021-03-02 11:13:44 -05:00
Jarrod Johnson 0ff9d5dc7e Merge branch 'master' into passfilehandle 2021-03-01 10:32:39 -05:00
Jarrod Johnson f176ebe4c2 Fix typo in confluentdbutil
The restore function would fail
to chown directories due to typo
2021-03-01 10:31:28 -05:00
Jarrod Johnson e24852c480 Fix chained discovery of DWC SMMv2
SMMv2 for DWC has more ports. Make the code
not care about which port is which for checking
for matching smm fingerprints.
2021-02-27 20:53:33 -05:00
Jarrod Johnson 008089c4c0 license install through passed fd
Allow install of licenses that confluent cannot read.
2021-02-26 16:37:34 -05:00
Jarrod Johnson f34e184d31 Restore libc access to sockapi
Cleanup went too for in python2 port.
2021-02-26 16:25:51 -05:00
Jarrod Johnson 814257fbf8 Fix leaking file descriptors
When passing a filehandle for update, clean it up on end
of the update/upload.
2021-02-26 16:18:58 -05:00
Jarrod Johnson 83d92ecfcc Make file descriptor passing python2 friendly
python 2 did not have recvmsg/sendmsg,
so have to use ctypes to access
them through the c library.
2021-02-26 16:17:43 -05:00
Jarrod Johnson 0fbe39690e Improve concurrency of SLP
During a scan, unicast TCP
interrogation of candidates
was done serially. Do this
concurrently so that poorly
behaving targets do not prolong
a scan.
2021-02-26 13:30:36 -05:00
Jarrod Johnson ec2ad9861a Improve concurrency of SLP
During a scan, unicast TCP
interrogation of candidates
was done serially. Do this
concurrently so that poorly
behaving targets do not prolong
a scan.
2021-02-26 12:39:13 -05:00
Jarrod Johnson 16096ad745 FD passing for firmware and media upload
Extend use of client side file descriptors to
media and firmmware upload.
2021-02-25 12:55:38 -05:00
Jarrod Johnson 481a70c304 Fix traceback when multiple criteria have no matches
When a parent criteria has no match, then the api
returns a 404 as there is no parent.  Handle this
by only listing positively matched items.
2021-02-24 16:39:01 -05:00
Jarrod Johnson facd501100 Merge branch 'master' into passfilehandle 2021-02-19 15:44:55 -05:00
Jarrod Johnson 4c6f41ffb2 Have nodeconfig error on missing attributes per node 2021-02-19 14:36:51 -05:00
Jarrod Johnson 3e93ab1966 Have OS import work for unreadable input 2021-02-18 16:33:14 -05:00
Jarrod Johnson 0b5c4f6f0f Progress client managed filedescriptor 2021-02-18 14:58:45 -05:00
Jarrod Johnson e8778cb992 Merge branch 'master' into passfilehandle 2021-02-18 10:57:25 -05:00
Jarrod Johnson 2fae35b2c4 Be more consistent between man page and -h 2021-02-18 10:56:37 -05:00
Jarrod Johnson 08c7bd4e42 Merge branch 'master' into passfilehandle 2021-02-18 10:52:09 -05:00
Jarrod Johnson 1a17bf8fbb Further correct formatting discrepency 2021-02-18 10:51:30 -05:00
Jarrod Johnson f21b0570e8 Further amend formatting discrepencies 2021-02-18 10:50:21 -05:00
Jarrod Johnson 9cac77cac7 Correct minor formatting discrepency 2021-02-18 10:49:17 -05:00
Jarrod Johnson 3831d409f5 Merge branch 'master' into passfilehandle 2021-02-18 10:48:09 -05:00
Jarrod Johnson 10fcb5fa6d Add missing options to nodestorage man page 2021-02-18 10:35:37 -05:00
Jarrod Johnson ff00f48e58 Add missing [options] from -h on nodestorage 2021-02-18 10:31:54 -05:00
Jarrod Johnson 225a49f05e Merge branch 'master' into passfilehandle 2021-02-18 09:12:47 -05:00
Jarrod Johnson d3c9c90f35 Implement simple address scanning for SLP 2021-02-18 09:04:46 -05:00
Jarrod Johnson 78592bfe2a Merge branch 'master' into passfilehandle 2021-02-18 07:46:47 -05:00
Jarrod Johnson 1b2f5b6019 Add a misc slpscan.py 2021-02-18 07:35:11 -05:00
Jarrod Johnson 3c6511a0e7 Actually have a file handle pass
Rely upon python 3 for now. When the dust settles, either convert to
python 2 friendly use of ctypes, or alternatively just block using
the pass filehandle function in older python.
2021-02-17 17:04:16 -05:00
Jarrod Johnson c525a08c17 Correct a number of mistakes in the draft commit 2021-02-17 14:34:45 -05:00
Jarrod Johnson edaaafa059 Begin work on passing filehandles for local cli
This would enable files to be uploaded/downloaded
using the client filehandles, overcoming awkward
difference in file privilege between client and
server.
2021-02-17 13:54:18 -05:00
Jarrod Johnson f16e84de32 Fix group display of crypted attributes 2021-02-12 13:32:36 -05:00
Jarrod Johnson 4c99bc142b Bump genesis build number 2021-02-11 16:43:01 -05:00
Jarrod Johnson 8e4f5fcae6 Register sftp in genesis by default 2021-02-11 16:42:30 -05:00
Jarrod Johnson c4d3cb409d Also add sftp, to make ansible not complain 2021-02-11 16:41:24 -05:00
Jarrod Johnson 9eed1378f6 Enable ansible to target genesis 2021-02-11 16:39:17 -05:00
Jarrod Johnson 5f282dd40a Attempt removing needless cache from python
Try to shrink image by removing duplicate
cached modules.
2021-02-11 15:39:25 -05:00
Jarrod Johnson 6e88a44399 Update name of tpm2 utility
pcrlist became pcrread
2021-02-11 15:37:38 -05:00
Jarrod Johnson 94e731274a Add debug in case where ESXi has no network
Do away with appearance of mysterious hang
2021-02-11 11:44:53 -05:00
Jarrod Johnson 50c150f4b4 Fixup issues in esxi deployment
When installer boots, the autonic behavior it tries is discarded
by default. Explicitly preserve the autonic behavior into
the install phase and the installed system.
2021-02-11 11:06:02 -05:00
Jarrod Johnson a46810ca29 Merge branch '3.1' 2021-02-08 09:39:55 -05:00
Jarrod Johnson 76cdd958ec Add AlmaLinux recognition
This imports ALmaLinux
2021-02-08 09:24:46 -05:00
Jarrod Johnson e72292f989 Update status at end of esxi install 2021-02-05 14:03:41 -05:00
Jarrod Johnson ed4db91383 Add Usb to nodesetboot
Particularly for redfish, this is a more specific target
2021-02-01 08:42:35 -05:00
Jarrod Johnson 209430ed35 Fix path mistake in mofed.post script
mofed.post had incorrect path to mofed.tgz after
fetch. Correct the path mistake.
2021-01-28 16:45:39 -05:00
Jarrod Johnson 3d2b579f1a Implement workaround for install over infiniband
8.3 implemented networkmanager to manage the install time
networking, but didn't correctly generate infiniband
network manager configuration.

Workaround by checking for infiniband install, and
then checking if the configuration is wrong, and
adding an infiniband section if missing.
2021-01-28 13:47:28 -05:00
Jarrod Johnson c99ab2ac73 Improve nodestorage local documentation.
Include clearer information about the `diskset` facility
in particular in usage and man page.
2021-01-27 13:02:57 -05:00
Jarrod Johnson e4591eaf57 Update nodeconfig man page with more information 2021-01-27 09:04:24 -05:00
Jarrod Johnson 695bb3757b Merge pull request #111 from brianfinley/osdeploy-list
Add 'list' option to osdeploy command
2021-01-27 08:55:17 -05:00
Jarrod Johnson 4e2767ce9d Update osdeploy 2021-01-27 08:51:03 -05:00
Brian Elliott Finley 8cdc9c9479 Add 'list' option to osdeploy command 2021-01-26 16:17:56 -06:00
152 changed files with 6509 additions and 957 deletions
+98 -88
View File
@@ -663,6 +663,28 @@ def get_session_node(shellargs):
return None
def run_inline_command(path, arg, completion, **kwargs):
tlvdata.send(session.connection, {'operation': 'pause',
'path': currconsole})
buffdata = ''
while select.select((session.connection,), (), (), 0)[0]:
buffdata += consume_termdata(session.connection, bufferonly=True)
rc = session.simple_noderange_command(consolename, path, arg, **kwargs)
tlvdata.send(session.connection, {'operation': 'resume',
'path': currconsole})
sys.stdout.write(completion)
sys.stdout.flush()
if buffdata:
try:
sys.stdout.write(buffdata)
except UnicodeEncodeError:
sys.stdout.buffer.write(buffdata.encode('utf8'))
except IOError: # Some times circumstances are bad
# resort to byte at a time...
for d in buffdata:
sys.stdout.write(d)
return rc
def conserver_command(filehandle, localcommand):
# x - conserver has that as 'show baud', I am inclined to replace that with
# 'request exclusive'
@@ -698,13 +720,13 @@ def conserver_command(filehandle, localcommand):
localcommand = get_command_bytes(filehandle, localcommand, cmdlen)
if localcommand[1] == 'o': # off
print("powering off...")
session.simple_noderange_command(consolename, '/power/state', 'off')
print("complete]\r")
sys.stdout.write("powering off...")
sys.stdout.flush()
run_inline_command('/power/state', 'off', 'complete]')
elif localcommand[1] == 's': # shutdown
print("shutting down...")
session.simple_noderange_command(consolename, '/power/state', 'shutdown')
print("complete]\r")
sys.stdout.write("shutting down...")
sys.stdout.flush()
run_inline_command('/power/state', 'shutdown', 'complete]')
elif localcommand[1] == 'b': # boot
cmdlen += 1
localcommand = get_command_bytes(filehandle, localcommand, cmdlen)
@@ -715,59 +737,46 @@ def conserver_command(filehandle, localcommand):
bootmode = 'uefi'
bootdev = 'setup'
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
if rc:
print("Error]\r")
else:
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
rc = run_inline_command('/power/state', 'boot', 'complete]')
if rc:
print("Error]\r")
else:
print("complete]\r")
print("Error]")
elif localcommand[2] == 'n': # boot to network
print("booting to network...")
sys.stdout.write("booting to network...")
sys.stdout.flush()
bootmode = 'uefi'
bootdev = 'network'
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
if rc:
print("Error]\r")
else:
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
rc = run_inline_command('/power/state', 'boot', 'complete]')
if rc:
print("Error]\r")
else:
print("complete]\r")
elif localcommand[2] == '\x0d': # boot to default
print("booting to default...")
bootmode = 'uefi'
bootdev = 'default'
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
if rc:
print("Error]\r")
else:
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
rc = run_inline_command('/power/state', 'boot', 'complete]')
if rc:
print("Error]\r")
else:
print("complete]\r")
else:
print("Unknown boot state.]\r")
else:
print("Unknown power state.]\r")
check_power_state()
elif localcommand[0] == 'r':
sys.stdout.write('\x1b7\x1b[999;999H\x1b[6n')
@@ -929,65 +938,7 @@ def main():
rdylist = ()
for fh in rdylist:
if fh == session.connection:
# this only should get called in the
# case of a console session
# each command should slurp up all relevant
# recv potential
#fh.read()
try:
data = tlvdata.recv(fh)
except Exception:
data = None
if type(data) == dict:
updatestatus(data)
continue
if data is not None:
data = client.stringify(data)
if clearpowermessage:
sys.stdout.write("\x1b[2J\x1b[;H")
clearpowermessage = False
try:
sys.stdout.write(data)
except UnicodeEncodeError:
sys.stdout.buffer.write(data.encode('utf8'))
except IOError: # Some times circumstances are bad
# resort to byte at a time...
for d in data:
sys.stdout.write(d)
now = time.time()
if ('showtime' not in laststate or
(now // 60) != laststate['showtime'] // 60):
# don't bother churning if minute does not change
laststate['showtime'] = now
updatestatus()
try:
sys.stdout.flush()
except Exception:
# EWOULDBLOCK causes this to raise, ignore
# this scenario comfortable that it
# will come out soon enough
pass
else:
deadline = 5
connected = False
while not connected and deadline > 0:
try:
server_connect()
connected = True
except Exception:
pass
if not connected:
time.sleep(1)
deadline -=1
if connected:
do_command(
"start /nodes/%s/console/session skipreplay=True" % consolename,
netserver)
else:
doexit = True
inconsole = False
sys.stdout.write("\r\n[remote disconnected]\r\n")
break
consume_termdata(fh)
else:
try:
myinput = fh.read()
@@ -1013,6 +964,64 @@ def main():
sys.stdout.write('Lost connection to server')
quitconfetty(fullexit=True)
def consume_termdata(fh, bufferonly=False):
global clearpowermessage
try:
data = tlvdata.recv(fh)
except Exception:
data = None
if type(data) == dict:
updatestatus(data)
return
if data is not None:
data = client.stringify(data)
if clearpowermessage:
sys.stdout.write("\x1b[2J\x1b[;H")
clearpowermessage = False
if bufferonly:
return data
try:
sys.stdout.write(data)
except UnicodeEncodeError:
sys.stdout.buffer.write(data.encode('utf8'))
except IOError: # Some times circumstances are bad
# resort to byte at a time...
for d in data:
sys.stdout.write(d)
now = time.time()
if ('showtime' not in laststate or
(now // 60) != laststate['showtime'] // 60):
# don't bother churning if minute does not change
laststate['showtime'] = now
updatestatus()
try:
sys.stdout.flush()
except Exception:
# EWOULDBLOCK causes this to raise, ignore
# this scenario comfortable that it
# will come out soon enough
pass
else:
deadline = 5
connected = False
while not connected and deadline > 0:
try:
server_connect()
connected = True
except Exception:
pass
if not connected:
time.sleep(1)
deadline -=1
if connected:
do_command(
"start /nodes/%s/console/session skipreplay=True" % consolename,
netserver)
else:
doexit = True
inconsole = False
sys.stdout.write("\r\n[remote disconnected]\r\n")
if __name__ == '__main__':
errcode = 0
deadline = 0
@@ -1024,11 +1033,12 @@ if __name__ == '__main__':
errcode = e.errorcode
except Exception as e:
import traceback
excinfo = traceback.print_exc()
try:
quitconfetty()
except Exception:
pass
traceback.print_exc()
print(excinfo)
finally:
if deadline and os.times()[4] < deadline:
sys.stderr.write('[Exited early, hit enter to continue]')
+2 -2
View File
@@ -47,14 +47,14 @@ exitcode = 0
errorNodes = set([])
session.stop_if_noderange_over(noderange, options.maxnodes)
success = session.simple_noderange_command(noderange, 'configuration/management_controller/reset', 'reset', key='state', errnodes=errorNodes) # = 0 if successful
if success != 0:
sys.exit(success)
# Determine which nodes were successful and print them
allNodes = set([])
for node in session.read('/noderange/{0}/nodes/'.format(noderange)):
if 'error' in node and success != 0:
sys.exit(success)
allNodes.add(node['item']['href'].replace("/", ""))
goodNodes = allNodes - errorNodes
+2 -2
View File
@@ -159,7 +159,7 @@ def parse_config_line(arguments, single=False):
if setmode is None:
setmode = True
if setmode != True:
bailout('Cannot do set and query in same command')
bailout('Cannot do set and query in same command: Query detected but "{0}" appears to be set'.format(param))
if '=' in param:
key, _, value = param.partition('=')
_assign_value()
@@ -171,7 +171,7 @@ def parse_config_line(arguments, single=False):
if setmode is None:
setmode = False
if setmode != False:
bailout('Cannot do set and query in same command')
bailout('Cannot do set and query in same command: Set mode detected but "{0}" appears to be a query'.format(param))
if '.' not in param:
if param == 'bmc':
printallbmc = True
+17 -1
View File
@@ -34,6 +34,7 @@ defcolumns = ['Node', 'Model', 'Serial', 'UUID', 'Mac Address', 'Type',
columnmapping = {
'Node': 'nodename',
'Model': 'modelnumber',
'Model Name': 'modelname',
'Serial': 'serialnumber',
'UUID': 'uuid',
'Type': 'types',
@@ -148,6 +149,7 @@ unique_fields = frozenset(['serial', 'mac', 'uuid'])
def import_csv(options, session):
nodedata = []
unique_data = {}
exitcode = 0
with open(options.importfile, 'r') as datasrc:
records = csv.reader(datasrc)
fields = process_header(next(records))
@@ -188,6 +190,7 @@ def import_csv(options, session):
for res in session.create('/nodes/', datum):
if 'error' in res:
sys.stderr.write(res['error'] + '\n')
exitcode |= res.get('errorcode', 1)
continue
elif 'created' in res:
print('Defined ' + res['created'])
@@ -198,11 +201,14 @@ def import_csv(options, session):
{'node': nodename}):
if 'error' in res:
sys.stderr.write(res['error'] + '\n')
exitcode |= res.get('errorcode', 1)
continue
elif 'assigned' in res:
print('Discovered ' + res['assigned'])
else:
print(repr(res))
if exitcode:
sys.exit(exitcode)
def list_discovery(options, session):
@@ -263,7 +269,11 @@ def list_matching_macs(options, session, node=None, checknode=True):
return [options.mac.replace(':', '-')]
else:
path += 'by-mac/'
return [x['item']['href'] for x in session.read(path)]
ret = []
for x in session.read(path):
if 'item' in x and 'href' in x['item']:
ret.append(x['item']['href'])
return ret
def assign_discovery(options, session, needid=True):
abort = False
@@ -287,12 +297,18 @@ def assign_discovery(options, session, needid=True):
if not matches:
sys.stderr.write("No matching discovery candidates found\n")
sys.exit(1)
exitcode = 0
for res in session.update('/discovery/by-mac/{0}'.format(matches[0]),
{'node': options.node}):
if 'assigned' in res:
print('Assigned: {0}'.format(res['assigned']))
elif 'error' in res:
sys.stderr.write('Error: {0}\n'.format(res['error']))
exitcode |= res.get('errorcode', 1)
else:
print(repr(res))
if exitcode:
sys.exit(exitcode)
def blocking_scan(session):
list(session.update('/discovery/rescan', {'rescan': 'start'}))
+6
View File
@@ -109,6 +109,12 @@ def update_firmware(session, filename):
if options.backup:
upargs['bank'] = 'backup'
noderrs = {}
if session.unixdomain:
of = open(filename, 'rb')
try:
session.add_file(filename, of.fileno(), 'rb')
except Exception:
pass
for res in session.create(resource, upargs):
if 'created' not in res:
for nodename in res.get('databynode', ()):
+6
View File
@@ -69,6 +69,12 @@ def install_license(session, filename):
'management_controller/licenses/'.format(noderange)
filename = os.path.abspath(filename)
instargs = {'filename': filename}
if session.unixdomain:
of = open(filename, 'rb')
try:
session.add_file(filename, of.fileno(), 'rb')
except Exception:
pass
for res in session.create(resource, instargs):
for node in res.get('databynode', []):
if 'error' in res['databynode'][node]:
+6
View File
@@ -111,6 +111,12 @@ def upload_media(noderange, media):
resource = '/noderange/{0}/media/uploads/'.format(noderange)
upargs = {'filename': filename}
noderrs = {}
if session.unixdomain:
of = open(filename, 'rb')
try:
session.add_file(filename, of.fileno(), 'rb')
except Exception:
pass
nodeurls = {}
for res in session.create(resource, upargs):
if 'created' not in res:
+8 -1
View File
@@ -32,6 +32,13 @@ path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
if path.startswith('/opt'):
sys.path.append(path)
class hybridcsv(csv.excel):
lineterminator = '\n'
csv.register_dialect('hybrid', hybridcsv)
import confluent.client as client
sensorcollections = {
@@ -209,7 +216,7 @@ def main():
headernames.append(headername)
if options.csv:
linebyline = False
csvwriter = csv.writer(sys.stdout)
csvwriter = csv.writer(sys.stdout, dialect='hybrid')
if options.interval:
csvwriter.writerow(['time', 'node'] + headernames)
else:
+18 -2
View File
@@ -48,6 +48,8 @@ def run():
help='Do not prefix output with node names')
argparser.add_option('-p', '--port', type='int', default=0,
help='Specify a custom port for ssh')
argparser.add_option('-s', '--substitutename',
help='Use a different name other than the nodename for ssh')
argparser.add_option('-m', '--maxnodes', type='int',
help='Specify a maximum number of '
'nodes to run remote ssh command to, '
@@ -71,6 +73,19 @@ def run():
exitcode = 0
c.stop_if_noderange_over(args[0], options.maxnodes)
nodemap = {}
if options.substitutename:
subname = options.substitutename
if '{' not in subname:
subname = '{node}' + subname
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
{'expression': subname}):
if 'error' in exp:
sys.stderr.write(exp['error'] + '\n')
exitcode |= exp.get('errorcode', 1)
ex = exp.get('databynode', ())
for node in ex:
nodemap[node] = ex[node]['value']
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
{'expression': cmdstr}):
if 'error' in exp:
@@ -79,12 +94,13 @@ def run():
ex = exp.get('databynode', ())
for node in ex:
cmd = ex[node]['value']
sshnode = nodemap.get(node, node)
if not isinstance(cmd, str) and not isinstance(cmd, bytes):
cmd = cmd.encode('utf-8')
if options.port:
cmdv = ['ssh', '-p', '{0}'.format(options.port), node, cmd]
cmdv = ['ssh', '-p', '{0}'.format(options.port), sshnode, cmd]
else:
cmdv = ['ssh', node, cmd]
cmdv = ['ssh', sshnode, cmd]
if currprocs < concurrentprocs:
currprocs += 1
run_cmdv(node, cmdv, all, pipedesc)
+1 -1
View File
@@ -186,7 +186,7 @@ funmap = {
def main():
argparser = OptParser(
usage='Usage: %prog <noderange> [show|create|delete|diskset]',
usage='Usage: %prog <noderange> [show|create|delete|diskset] [hotspare|jbod|unconfigured] [options]',
epilog='',
)
argparser.add_option('-r', '--raidlevel', type='int',
+1 -1
View File
@@ -116,7 +116,7 @@ class OptParser(optparse.OptionParser):
def main():
argparser = OptParser(
usage="Usage: %prog <noderange> servicedata "
"<filename>",
"<directory or filename>",
epilog='\nservicedata will save service data to the given '
'directory. It is saved to the location on the relevant '
'management server (the confluent server if running remote, '
+2
View File
@@ -37,6 +37,8 @@ except ImportError:
def plot(gui, output, plotdata, bins):
import matplotlib as mpl
if gui and mpl.get_backend() == 'agg':
sys.stderr.write('Error: No GUI backend available and -g specified!\n')
if not gui:
mpl.use('Agg')
import matplotlib.pyplot as plt
+53 -12
View File
@@ -46,6 +46,13 @@ try:
except NameError:
pass
class NestedDict(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
def stringify(instr):
# Normalize unicode and bytes to 'str', correcting for
# current python version
@@ -154,6 +161,7 @@ class Command(object):
self._prevkeyname = None
self.connection = None
self._currnoderange = None
self.unixdomain = False
if server is None:
if 'CONFLUENT_HOST' in os.environ:
self.serverloc = os.environ['CONFLUENT_HOST']
@@ -163,11 +171,13 @@ class Command(object):
self.serverloc = server
if os.path.isabs(self.serverloc) and os.path.exists(self.serverloc):
self._connect_unix()
self.unixdomain = True
elif self.serverloc == '/var/run/confluent/api.sock':
raise Exception('Confluent service is not available')
else:
self._connect_tls()
tlvdata.recv(self.connection)
self.protversion = int(tlvdata.recv(self.connection).split(
b'--')[1].strip()[1:])
authdata = tlvdata.recv(self.connection)
if authdata['authpassed'] == 1:
self.authenticated = True
@@ -178,6 +188,13 @@ class Command(object):
passphrase = os.environ['CONFLUENT_PASSPHRASE']
self.authenticate(username, passphrase)
def add_file(self, name, handle, mode):
if self.protversion < 3:
raise Exception('Not supported with connected confluent server')
if not self.unixdomain:
raise Exception('Can only add a file to a unix domain connection')
tlvdata.send(self.connection, {'filename': name, 'mode': mode}, handle)
def authenticate(self, username, password):
tlvdata.send(self.connection,
{'username': username, 'password': password})
@@ -417,7 +434,7 @@ def send_request(operation, path, server, parameters=None):
result = tlvdata.recv(server)
def attrrequested(attr, attrlist, seenattributes):
def attrrequested(attr, attrlist, seenattributes, node=None):
for candidate in attrlist:
truename = candidate
if candidate.startswith('hm'):
@@ -425,10 +442,16 @@ def attrrequested(attr, attrlist, seenattributes):
if candidate in _attraliases:
candidate = _attraliases[candidate]
if fnmatch.fnmatch(attr.lower(), candidate.lower()):
seenattributes.add(truename)
if node is None:
seenattributes.add(truename)
else:
seenattributes[node][truename] = True
return True
elif attr.lower().startswith(candidate.lower() + '.'):
seenattributes.add(truename)
if node is None:
seenattributes.add(truename)
else:
seenattributes[node][truename] = 1
return True
return False
@@ -444,13 +467,15 @@ def _sort_attrib(k):
def print_attrib_path(path, session, requestargs, options, rename=None, attrprefix=None):
exitcode = 0
seenattributes = set([])
seenattributes = NestedDict()
allnodes = set([])
for res in session.read(path):
if 'error' in res:
sys.stderr.write(res['error'] + '\n')
exitcode = 1
continue
for node in sorted(res['databynode']):
allnodes.add(node)
for attr, val in sorted(res['databynode'][node].items(), key=_sort_attrib):
if attr == 'error':
sys.stderr.write('{0}: Error: {1}\n'.format(node, val))
@@ -458,7 +483,7 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
if attr == 'errorcode':
exitcode |= val
continue
seenattributes.add(attr)
seenattributes[node][attr] = True
if rename:
printattr = rename.get(attr, attr)
else:
@@ -466,7 +491,7 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
if attrprefix:
printattr = attrprefix + printattr
currattr = res['databynode'][node][attr]
if show_attr(attr, requestargs, seenattributes, options):
if show_attr(attr, requestargs, seenattributes, options, node):
if 'value' in currattr:
if currattr['value'] is not None:
val = currattr['value']
@@ -547,23 +572,39 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
except TypeError:
pass
cprint(attrout)
somematched = set([])
printmissing = set([])
badnodes = NestedDict()
if not exitcode:
if requestargs:
for attr in requestargs:
if attr not in seenattributes:
sys.stderr.write('Error: {0} not a valid attribute\n'.format(attr))
exitcode = 1
for node in allnodes:
if attr in seenattributes[node]:
somematched.add(attr)
else:
badnodes[node][attr] = True
exitcode = 1
for node in sortutil.natural_sort(badnodes):
for attr in badnodes[node]:
if attr in somematched:
sys.stderr.write(
'Error: {0} matches no valid value for {1}\n'.format(
attr, node))
else:
printmissing.add(attr)
for missing in printmissing:
sys.stderr.write('Error: {0} not a valid attribute\n'.format(attr))
return exitcode
def show_attr(attr, requestargs, seenattributes, options):
def show_attr(attr, requestargs, seenattributes, options, node):
try:
reverse = options.exclude
except AttributeError:
reverse = False
if requestargs is None or requestargs == []:
return True
processattr = attrrequested(attr, requestargs, seenattributes)
processattr = attrrequested(attr, requestargs, seenattributes, node)
if reverse:
processattr = not processattr
return processattr
+1 -1
View File
@@ -17,4 +17,4 @@
class Types(object):
text, json = range(2)
text, json, filehandle = range(3)
+130 -10
View File
@@ -15,9 +15,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import array
import ctypes
import ctypes.util
import confluent.tlv as tlv
import eventlet.green.socket as socket
import eventlet.green.select as select
from datetime import datetime
import json
import os
import struct
try:
@@ -30,6 +36,72 @@ try:
except NameError:
pass
class iovec(ctypes.Structure): # from uio.h
_fields_ = [('iov_base', ctypes.c_void_p),
('iov_len', ctypes.c_size_t)]
iovec_ptr = ctypes.POINTER(iovec)
class cmsghdr(ctypes.Structure): # also from bits/socket.h
_fields_ = [('cmsg_len', ctypes.c_size_t),
('cmsg_level', ctypes.c_int),
('cmsg_type', ctypes.c_int)]
@classmethod
def init_data(cls, cmsg_len, cmsg_level, cmsg_type, cmsg_data):
Data = ctypes.c_ubyte * ctypes.sizeof(cmsg_data)
class _flexhdr(ctypes.Structure):
_fields_ = cls._fields_ + [('cmsg_data', Data)]
datab = Data(*bytearray(cmsg_data))
return _flexhdr(cmsg_len=cmsg_len, cmsg_level=cmsg_level,
cmsg_type=cmsg_type, cmsg_data=datab)
def CMSG_LEN(length):
sizeof_cmshdr = ctypes.sizeof(cmsghdr)
return ctypes.c_size_t(CMSG_ALIGN(sizeof_cmshdr).value + length)
SCM_RIGHTS = 1
class msghdr(ctypes.Structure): # from bits/socket.h
_fields_ = [('msg_name', ctypes.c_void_p),
('msg_namelen', ctypes.c_uint),
('msg_iov', ctypes.POINTER(iovec)),
('msg_iovlen', ctypes.c_size_t),
('msg_control', ctypes.c_void_p),
('msg_controllen', ctypes.c_size_t),
('msg_flags', ctypes.c_int)]
def CMSG_ALIGN(length): # bits/socket.h
ret = (length + ctypes.sizeof(ctypes.c_size_t) - 1
& ~(ctypes.sizeof(ctypes.c_size_t) - 1))
return ctypes.c_size_t(ret)
def CMSG_SPACE(length): # bits/socket.h
ret = CMSG_ALIGN(length).value + CMSG_ALIGN(ctypes.sizeof(cmsghdr)).value
return ctypes.c_size_t(ret)
class ClientFile(object):
def __init__(self, name, mode, fd):
self.fileobject = os.fdopen(fd, mode)
self.filename = name
libc = ctypes.CDLL(ctypes.util.find_library('c'))
recvmsg = libc.recvmsg
recvmsg.argtypes = [ctypes.c_int, ctypes.POINTER(msghdr), ctypes.c_int]
recvmsg.restype = ctypes.c_int
sendmsg = libc.sendmsg
sendmsg.argtypes = [ctypes.c_int, ctypes.POINTER(msghdr), ctypes.c_int]
sendmsg.restype = ctypes.c_size_t
def decodestr(value):
ret = None
try:
@@ -65,7 +137,7 @@ def _unicode_list(currlist):
_unicode_list(currlist[i])
def send(handle, data):
def send(handle, data, filehandle=None):
if isinstance(data, unicode):
try:
data = data.encode('utf-8')
@@ -93,9 +165,27 @@ def send(handle, data):
if tl > 16777215:
raise Exception("JSON data exceeds protocol limits")
# xor in the type (0b1 << 24)
tl |= 16777216
handle.sendall(struct.pack("!I", tl))
handle.sendall(sdata)
if filehandle is None:
tl |= 16777216
handle.sendall(struct.pack("!I", tl))
handle.sendall(sdata)
else:
tl |= (2 << 24)
handle.sendall(struct.pack("!I", tl))
cdtype = ctypes.c_ubyte * len(sdata)
cdata = cdtype.from_buffer(bytearray(sdata))
ciov = iovec(iov_base=ctypes.addressof(cdata),
iov_len=ctypes.c_size_t(ctypes.sizeof(cdata)))
fd = ctypes.c_int(filehandle)
cmh = cmsghdr.init_data(
cmsg_len=CMSG_LEN(
ctypes.sizeof(fd)), cmsg_level=socket.SOL_SOCKET,
cmsg_type=SCM_RIGHTS, cmsg_data=fd)
mh = msghdr(msg_name=None, msg_len=0, msg_iov=iovec_ptr(ciov),
msg_iovlen=1, msg_control=ctypes.addressof(cmh),
msg_controllen=ctypes.c_size_t(ctypes.sizeof(cmh)))
sendmsg(handle.fileno(), mh, 0)
def recvall(handle, size):
rd = handle.recv(size)
@@ -125,12 +215,42 @@ def recv(handle):
datatype = (tl & 2130706432) >> 24 # grab 7 bits from near beginning
if dlen == 0:
return None
data = handle.recv(dlen)
while len(data) < dlen:
ndata = handle.recv(dlen - len(data))
if not ndata:
raise Exception("Error reading data")
data += ndata
if datatype == tlv.Types.filehandle:
filehandles = array.array('i')
rawbuffer = bytearray(2048)
pkttype = ctypes.c_ubyte * 2048
data = pkttype.from_buffer(rawbuffer)
cmsgsize = CMSG_SPACE(ctypes.sizeof(ctypes.c_int)).value
cmsgarr = bytearray(cmsgsize)
cmtype = ctypes.c_ubyte * cmsgsize
cmsg = cmtype.from_buffer(cmsgarr)
cmsg.cmsg_level = socket.SOL_SOCKET
cmsg.cmsg_type = SCM_RIGHTS
cmsg.cmsg_len = CMSG_LEN(ctypes.sizeof(ctypes.c_int))
iov = iovec()
iov.iov_base = ctypes.addressof(data)
iov.iov_len = 2048
msg = msghdr()
msg.msg_iov = ctypes.pointer(iov)
msg.msg_iovlen = 1
msg.msg_control = ctypes.addressof(cmsg)
msg.msg_controllen = ctypes.sizeof(cmsg)
select.select([handle], [], [])
i = recvmsg(handle.fileno(), ctypes.pointer(msg), 0)
cdata = cmsgarr[CMSG_LEN(0).value:]
data = rawbuffer[:i]
if cmsg.cmsg_level == socket.SOL_SOCKET and cmsg.cmsg_type == SCM_RIGHTS:
filehandles.fromstring(bytes(
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
data = json.loads(bytes(data))
return ClientFile(data['filename'], data['mode'], filehandles[0])
else:
data = handle.recv(dlen)
while len(data) < dlen:
ndata = handle.recv(dlen - len(data))
if not ndata:
raise Exception("Error reading data")
data += ndata
if datatype == tlv.Types.text:
return data
elif datatype == tlv.Types.json:
+22 -11
View File
@@ -3,7 +3,7 @@ collate(1) -- Organize text input by node
## SYNOPSIS
`<other command> | collate [-a] [-d] [-w] [-s] [-c] [-r] [-l lognametemplate]`
`<other command> | collate [-a] [-b] [-d] [-w] [-g] [-s] [-c] [-r] [-l lognametemplate]`
## DESCRIPTION
@@ -21,30 +21,41 @@ node and group names sorted alphanumerically.
when output differs along a telling group boundary. For example, if
output suggests a large number of nodes are unreachable, abbreviate
showing 'rack1' as being unreachable may make more obvious a possible cause.
* `-b BASE`, `--base=BASE`:
Use given node as reference for comparison when using
-d, instead of using the most common result
* `-d`, `--diff`:
Express all but the most common result group in terms of diff from
the most common result group
* `-l`, `--log`:
Save output per node to individual log files, replacing {node} in the name
with the nodename of each
* `-w`, `--watch`:
Update results dynamically as data becomes available, rather than
waiting for the command to fully complete.
* `-g`, `--groupcount`:
Show count of output groups rather than the actual
output
* `-s`, `--skipcommon`:
Suppress printing of the most common result text group. This is used to
focus on stray output against a well known and expected result.
* `-c`, `--count`:
Print a count of the number of nodes in an output group under the
noderange.
noderange.
* `-r`, `--reverse`:
Rather than starting with most common to least common, start with
the least common and print the most common last.
* `-r`, `--reverse`:
Rather than starting with most common to least common, start with
the least common and print the most common last.
* `-l LOG`, `--log=LOG`:
Save output per node to individual log files, replacing {node} in the name
with the nodename of each
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Organizing power state of multiple nodes:
+5 -1
View File
@@ -6,7 +6,8 @@ collective(1) -- Check and manage a confluent collective
`collective invite <server>`
`collective join <server> [-i TOKEN]`
`collective show`
`collective gencert`
`collective gencert`
`collective delete`
## DESCRIPTION
@@ -36,6 +37,9 @@ on circumstances.
* `-i`:
Provide the token as an argument rather than interactively.
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Inviting a server called mgt2:
+15
View File
@@ -15,6 +15,21 @@ be mostly an aid for developing client software, with
day to day administration generally being easier with
the various function specific commands.
## OPTIONS
* `-s SERVER:PORT`, `--server=SERVER:PORT`:
Confluent instance to connect to
* `-c PATH`, `--control=PATH`:
Path to offer terminal control
* `-m MINTIME`, `--mintime=MINTIME`:
Minimum time to run or else pause for input (used to
keep a terminal from closing quickly on error)
* `-h`, `--help`:
Show help message and exit
## COMMANDS
The CLI may be navigated by shell commands and some other
+19 -3
View File
@@ -3,8 +3,7 @@ confluentdbutil(8) -- Backup or restore confluent database
## SYNOPSIS
`confluentdbutil [options] <dump|restore> <path>`
`confluentdbutil [options] [dump|restore] <path>`
## DESCRIPTION
@@ -19,8 +18,25 @@ the json files (password protected, removed from the files, or unprotected).
* `-p`, `--password`:
If specified, information such as usernames and passwords will be encrypted
using the given password.
* `-i`, `--interactivepassword`:
Prompt for password.
* `-r`, `--redact`:
Indicates to replace usernames and passwords with a dummy string rather
than included.
* `-u`, `--unprotected`:
The keys.json file will include the encryption keys without any protection.
The keys.json file will include the encryption keys without any protection.
* `-s`, `--skipkeys`:
This specifies to dump the encrypted data without
dumping the keys needed to decrypt it. This is
suitable for an automated incremental backup, where an
earlier password protected dump has a protected
keys.json file, and only the protected data is needed.
keys do not change and as such they do not require
incremental backup.
* `-h`, `--help`:
Show help message and exit
+16 -3
View File
@@ -40,12 +40,25 @@ See nodegroupattrib(8) command on how to manage attributes on a group level.
* `-b`, `--blame`:
Annotate inherited and expression based attributes to show their base value.
* `-c`, `--clear`:
Clear specified nodeattributes
Clear specified nodeattributes.
* `-e`, `--environment`:
Set specified attributes based on exported environment variable of matching name. Environment variable names may be lower case or all upper case. Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE
Set specified attributes based on exported environment variable of matching name.
Environment variable names may be lower case or all upper case.
Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE
* `-p`, `--prompt`:
Request interactive prompting to provide values rather than the command line or environment variables.
Request interactive prompting to provide values rather than the command line
or environment variables.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Prompt if trying to set attributes on more than
specified number of nodes.
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Listing matching nodes of a simple noderange:
@@ -9,6 +9,15 @@ nodebmcreset(8) -- Reset management controller
`nodebmcreset` allows you to reset the management controller of the specified noderange
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Number of nodes to affect before prompting for
confirmation
* `-h`, `--help`:
Show help message and exit
## EXAMPLES:
* Reset the management controller for nodes n1 through n4:
+8 -2
View File
@@ -3,8 +3,7 @@ nodeboot(8) -- Reboot a confluent node to a specific device
## SYNOPSIS
`nodeboot <noderange>`
`nodeboot [options] <noderange>` [default|cd|network|setup|hd]
`nodeboot [options] <noderange> [default|cd|network|setup|hd]`
## DESCRIPTION
@@ -25,6 +24,13 @@ device without inducing a reboot, see the `nodesetboot` command.
* `-p`, `--persist`:
For a system that supports it, mark the boot override to persist rather than
be a one time change. Many systems do not support this functionality.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to boot, prompting
if over the threshold
* `-h`, `--help`:
Show help message and exit
* `default`:
Request a normal default boot with no particular device override
+23 -8
View File
@@ -3,8 +3,7 @@ nodeconfig(8) -- Show or change node configuration
## SYNOPSIS
`nodeconfig <noderange> [options] [<configuration>..]`
`nodeconfig <noderange> [options] [<configuration=value>..]`
`nodeconfig [options] <noderange> [setting|setting=value]`
## DESCRIPTION
@@ -14,6 +13,12 @@ running configuration on the node firmware. Calling without '=' will show the
current value, and '=' will change the value. Network information can be
given as a node expression, as documented in the man page for nodeattribexpressions(5).
Note that when using nodeconfig to submit changes, it will exit when the change
is accepted, but the endpoint may not have fully processed it. Doing a show
immediately after doing a set may reflect older information. Also, if changing
BIOS/UEFI settings, the change may appear in output, but generally won't
actually be in effect until a reboot.
## OPTIONS
* `-c`, `--comparedefault`:
@@ -21,6 +26,13 @@ given as a node expression, as documented in the man page for nodeattribexpressi
no configuration values are specified, it will show only those that differ.
If combined with `-x`, will show all differing values except those indicated
by `-x`
* `-b`, `--batch`:
Provide arguments as lines of a file, rather than the command line.
* `-d`, `--detail`:
Provide detailed data as available. This can include help text and valid
values for a setting.
* `-e`, `--extra`:
Read settings that are generally not needed, but may be slow to retrieve.
@@ -31,18 +43,21 @@ given as a node expression, as documented in the man page for nodeattribexpressi
Rather than listing only the specified configuration parameters, list all
attributes except for the specified ones
* `-d`, `--detail`:
Provide detailed data as available. This can include help text and valid
values for a setting.
* `-a`, `--advanced`:
Include advanced settings, which are normally not intended to be used
without direction from the relevant server vendor.
* `-r`, `--restoredefault`:
Request that the specified component of the targeted nodes will have its
configuration reset to default. Currently the only component implemented
is uefi.
* `-b`, '--batch':
Provide arguments as lines of a file, rather than the command line.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to configure, prompting if over
the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Showing the current IP configuration of noderange BMC/IMM/XCC:
+8 -4
View File
@@ -3,9 +3,7 @@ nodedeploy(8) -- Request preparation and/or initiating a node deployment
## SYNOPSIS
`nodedeploy <noderange>`
`nodedeploy <noderange> -c`
`nodedeploy <noderange> [-n] [-p] <name of profile>`
`nodedeploy [-h] [-c] [-n] [-p] [-m MAXNODES] <noderange> [profile]`
## DESCRIPTION
@@ -19,7 +17,7 @@ deployment status.
## OPTIONS
* '-c', '--clear`:
* `-c`, `--clear`:
Remove any pending deployment action
* `-n`, `--network`:
@@ -28,6 +26,12 @@ deployment status.
* `-p`, `--prepareonly`:
Prepare the network services for deployment, but do not interact with BMCs. This is intended for scenarios where
the boot device control and server restart will be handled outside of confluent.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specifiy a maximum nodes to be deployed.
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Begin the instalalation of a profile of CentOS 8.2:
+10 -2
View File
@@ -3,14 +3,22 @@ nodeeventlog(8) -- Pull eventlog from confluent nodes
## SYNOPSIS
`nodeeventlog <noderange>`
`nodeeventlog <noderange> [clear]`
`nodeeventlog [options] <noderange> [clear]`
## DESCRIPTION
`nodeeventlog` pulls and optionally clears the event log from the requested
noderange.
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to clear if clearing log, prompting if
over the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Pull the event log from n2 and n3:
`# nodeeventlog n2,n3`
+13 -3
View File
@@ -3,9 +3,7 @@ nodefirmware(8) -- Report firmware information on confluent nodes
## SYNOPSIS
`nodefirmware <noderange>`
`nodefirmware <noderange> list|<components>|core`
`nodefirmware <noderange> update [--backup] <filename>`
`nodefirmware <noderange> [list][update [--backup <file>]]|[<components>]`
## DESCRIPTION
@@ -26,6 +24,18 @@ the out of band facilities. Firmware updates can end in one of three states:
* `pending`: The firmware update process has completed, but the firmware will not be active until the relevant component next resets. Generally speaking, for UEFI update the system will need a reboot, and for BMC updates, the `nodebmcreset` command will begin the process to activate the firmware.
* `complete`: The firmware update process has completed and activation has proceeded. Note that while the activation process has commenced, the component may still be in the process of rebooting when nodefirmware exits.
## OPTIONS
* `-b`, `--backup`:
Target a backup bank rather than primary
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
When updating, prompt if more than the specified number of servers will
be affected
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Pull firmware from a node:
@@ -25,8 +25,20 @@ the attributes are set on the node versus a group to which a node belongs.
## OPTIONS
* `-b`, `--blame`:
Show information about how attributes inherited
* `-e`, `--environment`:
Set attributes, but from environment variable of same name
* `-c`, `--clear`:
Clear specified nodeattributes.
* `-p`, `--prompt`:
Prompt for attribute values interactively
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
+8 -1
View File
@@ -9,9 +9,16 @@ nodelicense(8) -- Manage license keys on BMC
`nodelicense` manages license keys on supported BMCs. Without an argument, the command
lists currently installed license. Using `delete` will remove the specified license name
from th eBMC. The `save` subcommand will take the passed directory (which may be in the form
from the BMC. The `save` subcommand will take the passed directory (which may be in the form
of /path/to/{node}/ to have the node name substituted for each node) and back up installed licenses
to that directory. The `install` command will take the specified filename and install. The filename
argument may be of the form xcc_fod_0034_7X21{id.serial}.key to have the serial number substituted
to allow unique licenses to be specified in a single command.
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to delete licenses from, prompting if over the threshold
* `-h`, `--help`:
Show help message and exit
+2 -2
View File
@@ -34,8 +34,8 @@ the host platform attempts.
## OPTIONS
* `-c`:
Specify the maximum number of instances to run concurrently.
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Listing currently mounted media:
+7
View File
@@ -30,6 +30,13 @@ off will not react to this request.
* `-p`, '--showprevious':
Show previous power state for all directives that may change power state.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to change power state, prompting if
over the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Get power state of nodes n1 through n4:
+8 -1
View File
@@ -9,6 +9,14 @@ noderemove(8) -- Remove nodes from the confluent management service
`noderemove` simply removes the given noderange from the confluent database.
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to delete, prompting if over the
threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
@@ -22,4 +30,3 @@ noderemove(8) -- Remove nodes from the confluent management service
`r2u3: deleted`
`r2u2: deleted`
`r2u1: deleted`
+8 -1
View File
@@ -11,9 +11,16 @@ nodereseat(8) -- Request a reseat of a node
node's slot. This should be equivalent to removing the system entirely from
the chassis and putting it back in, but without actually having to do so.
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to reseat, prompting if over the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Reseating the node `s1`:
`# nodereseat s1`
`s1: Reseat successful`
+9 -5
View File
@@ -12,10 +12,14 @@ noderange. This will present progress as percentage for all nodes.
## OPTIONS
* `-m`:
Specify maximum number of nodes for noderange max.
* `-c`:
* `-c`, `-f`, `--count`:
Specify how many rsync executions to do concurrently. If noderange
exceeds the count, then excess nodes will wait until one of the
active count completes.
active count completes.
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to run rsync to, prompting if over the
threshold
* `-h`, `--help`:
Show help message and exit
+15
View File
@@ -13,6 +13,21 @@ nodeattribexpressions(5) are expanded prior to execution of the command. For
noderun, the commands are locally executed. To execute commands on the nodes
themselves, see nodeshell(8).
## OPTIONS
* `-f`, `-c`, `--count`:
Number of commands to run at a time
* `-n`, `--nonodeprefix`:
Do not prefix output with node names
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to run the command with, prompting if over
the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Run ping against nodes n1 through n4:
+10 -2
View File
@@ -2,8 +2,7 @@ nodesetboot(8) -- Check or set next boot device for noderange
====================================================
## SYNOPSIS
`nodesetboot <noderange>`
`nodesetboot [options] <noderange> [default|cd|network|setup|hd|usb|floppy]`
## DESCRIPTION
@@ -21,6 +20,8 @@ to use `nodesetboot <noderange> setup` and then initiate a reboot from within
the operating system with ssh or similar rather than using the remote hardware
control.
Running the command with no target queries the current setting.
## OPTIONS
* `-b`, `--bios`:
@@ -33,6 +34,13 @@ control.
* `-u`, `--uefi`:
This flag does nothing, it is for command compatibility with xCAT's rsetboot
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to modify next boot device, prompting if
over the threshold
* `-h`, `--help`:
Show help message and exit
* `default`:
Request a normal default boot with no particular device override
+20 -3
View File
@@ -3,7 +3,7 @@ nodeshell(8) -- Execute command on many nodes in a noderange through ssh
## SYNOPSIS
`nodeshell <noderange> [options] <command to execute on each node>`
`nodeshell [options] <noderange> <command to execute on each node>`
## DESCRIPTION
@@ -13,9 +13,26 @@ nodeattribexpressions(5). `nodeshell` provides stdout as stdout and stderr
as stderr, unlike psh which combines all stdout and stderr into stdout.
## OPTIONS
* `-c COUNT`, `-f COUNT`, `--count=COUNT`
Specify the maximum number of instances to run concurrently
* `-c`:
Specify the maximum number of instances to run concurrently.
* `-m MAXNODES`, `--maxnodes=MAXNODES`
Specify a maximum number of nodes to run remote ssh command to, prompting
if over the threshold
* `-n`, `--nonodeprefix`
Do not prefix output with node names
* `-p PORT`, `--port=PORT`
Specify a custom port for ssh
* `-s SUBSTITUTION`, `--substitutename=SUBSTITITUTION`
Specify a substitution name instead of the nodename. If no {} are in the substitution,
it is considered to be an append. For example, '-s -ib' would produce 'node1-ib' from 'node1'.
Full expression syntax is supported, in which case the substitution is considered to be the entire
new name. {node}-ib would be equivalent to -ib. For example, nodeshell -s {bmc} node1
would ssh to the BMC instead of the node.
## EXAMPLES
+14 -2
View File
@@ -3,12 +3,15 @@ nodestorage(8) -- Examine/Modify storage configuration of a node
## SYNOPSIS
`nodestorage <noderange> [show|create|delete] [options]`
`nodestorage <noderange> [show|create|delete|diskset] [hotspare|jbod|unconfigured] [options]`
## DESCRIPTION
`nodestorage` provides access to the remote storage configuration of
the noderange.
the noderange. The `show` subcommand will show current storage configuration,
`create` can be used to create new arrays or volumes, `delete` can be used to
remove volumes and arrays, and `diskset` can modify the usage of disks
indicated by `-d` to either be `unconfigured`, `jbod`, or `hotspare`.
## OPTIONS
* `-r` **RAIDLEVEL**, `--raidlevel`=**RAIDLEVEL**:
@@ -29,6 +32,15 @@ the noderange.
volumes, or selecting a volume for delete. Default
behavior is to use implementation provided default
* `-z` **STRIPSIZES**, `--stripsizes`=**STRIPSIZES**:
Comma separated list of stripsizes to use when creating volumes.
This value is in kilobytes. The default behavior is to allow the
storage controller to decide
* `-m` **MAXNODES**, `--maxnodes`=**MAXNODES**:
Specify a maximum number of nodes to configure storage on, prompting
if over the threshold
## EXAMPLES
* Deleting the volume `somedata`:
@@ -19,6 +19,15 @@ running in collective mode.
Note that due to vendor filename requirements, any filename may have vendor
specific suffixes added to any file produced.
## OPTIONS
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Specify a maximum number of nodes to download diagnostic data from, prompting
if over the threshold
* `-h`, `--help`:
Show help message and exit
## EXAMPLES
* Download support data from a single node to a specific filename
+1 -1
View File
@@ -5,7 +5,7 @@ osdeploy(8) --- Configure general OS deployment facilities of confluent
`osdeploy import <iso>`
`osdeploy updateboot <profile>`
`osdeploy initialize [-h] [-g] [-u] [-s] [-k] [-t] [-p] [-i] [-l]`
`osdeploy initialize [-h] [-g] [-u] [-s] [-k] [-t] [-p] [-i] [-l] [-a]`
## DESCRIPTION
@@ -100,6 +100,9 @@ class HTTPSClient(client.HTTPConnection, object):
self.sock = ctx.wrap_socket(psock, server_hostname=host)
def grab_url(self, url, data=None, returnrsp=False):
return self.grab_url_with_status(url, data, returnrsp)[1]
def grab_url_with_status(self, url, data=None, returnrsp=False):
if data:
method = 'POST'
else:
@@ -108,9 +111,9 @@ class HTTPSClient(client.HTTPConnection, object):
rsp = self.getresponse()
if rsp.status >= 200 and rsp.status < 300:
if returnrsp:
return rsp
return rsp.status, rsp
else:
return rsp.read()
return rsp.status, rsp.read()
raise Exception(rsp.read())
if __name__ == '__main__':
@@ -121,8 +124,26 @@ if __name__ == '__main__':
if len(sys.argv) == 1:
HTTPSClient()
sys.exit(0)
if sys.argv[-2] == '-o':
with open(sys.argv[3], 'wb') as outf:
try:
outbin = sys.argv.index('-o')
sys.argv.pop(outbin)
outbin = sys.argv.pop(outbin)
except ValueError:
outbin = None
try:
waitfor = sys.argv.index('-w')
sys.argv.pop(waitfor)
waitfor = int(sys.argv.pop(waitfor))
except ValueError:
waitfor = None
try:
data = sys.argv.index('-d')
sys.argv.pop(data)
data = sys.argv.pop(data)
except ValueError:
data = None
if outbin:
with open(outbin, 'ab+') as outf:
reader = HTTPSClient(json=json).grab_url(
sys.argv[1], data, returnrsp=True)
chunk = reader.read(16384)
@@ -130,6 +151,13 @@ if __name__ == '__main__':
outf.write(chunk)
chunk = reader.read(16384)
sys.exit(0)
if os.path.exists(sys.argv[-1]):
if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
data = open(sys.argv[-1]).read()
sys.stdout.write(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
if waitfor:
client = HTTPSClient(json=json)
status = 201
while status != waitfor:
status, rsp = client.grab_url_with_status(sys.argv[1], data)
sys.stdout.write(rsp.decode())
else:
sys.stdout.write(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
@@ -12,7 +12,7 @@ sed -i 's/install::/install:*:/' /sysroot/etc/shadow
sed -i 's/root::/root:*:/' /sysroot/etc/shadow
mkdir -p /sysroot/root/.ssh
#chmod 700 /sysroot/root/.ssh
cat /ssh/*.rootpubkey > /sysroot/root/.ssh/authorized_keys
cat /ssh/*pubkey > /sysroot/root/.ssh/authorized_keys
#chmod 600 /sysroot/root/.ssh/authorized_keys
mkdir -p /sysroot/etc/ssh/
for i in /ssh/*.ca; do
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -1,5 +1,5 @@
#!/bin/sh
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/' $2/profile.yaml
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/; s/alma/AlmaLinux/' $2/profile.yaml
ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \
ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution
mkdir -p $2/boot/efi/boot && \
@@ -33,14 +33,15 @@
reboot
%packages
#-kernel-uek # This can opt out of the UEK for the relevant distribution
chrony
rsync
python
tar
pciutils
%include /tmp/addonpackages
%end
# A kickstart.custom file is provided to enable easily adding
# kickstart content without modifying the stock file.
# While the stock file may be safely modified, using the .custom
@@ -49,32 +50,33 @@ pciutils
%include /tmp/kickstart.custom
%pre
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/preinst.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/preinst.sh
. /tmp/preinst.sh
%end
%post --nochroot
mkdir -p /mnt/sysimage/etc/confluent
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/postinst.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/postinst.sh
. /tmp/postinst.sh
# Hook firstboot.sh
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/etc/confluent/firstboot.sh
chmod +x /mnt/sysimage/etc/confluent/firstboot.sh
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
mkdir -p /mnt/sysimage/opt/confluent/bin
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh
chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh
%end
%post
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
systemctl enable firstboot
chgrp ssh_keys /etc/ssh/ssh*key
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /etc/confluent/firstboot.sh
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/postinst.sh
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /opt/confluent/bin/firstboot.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /tmp/postinst.sh
. /tmp/postinst.sh
%end
@@ -1,3 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet
kernelargs: quiet # These arguments are passed to the installer
#installedargs: example # These arguments would be added to the installed system
@@ -0,0 +1,414 @@
# Copyright 2017 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import ctypes
import fcntl
import json
from select import select
import socket
import struct
import os
import subprocess
import sys
import time
if os.path.exists('/opt/confluent/bin/apiclient'):
apiclient = '/opt/confluent/bin/apiclient'
elif os.path.exists('/etc/confluent/apiclient'):
apiclient = '/etc/confluent/apiclient'
class IpmiMsg(ctypes.Structure):
_fields_ = [('netfn', ctypes.c_ubyte),
('cmd', ctypes.c_ubyte),
('data_len', ctypes.c_short),
('data', ctypes.POINTER(ctypes.c_ubyte))]
class IpmiSystemInterfaceAddr(ctypes.Structure):
_fields_ = [('addr_type', ctypes.c_int),
('channel', ctypes.c_short),
('lun', ctypes.c_ubyte)]
class IpmiRecv(ctypes.Structure):
_fields_ = [('recv_type', ctypes.c_int),
('addr', ctypes.POINTER(IpmiSystemInterfaceAddr)),
('addr_len', ctypes.c_uint),
('msgid', ctypes.c_long),
('msg', IpmiMsg)]
class IpmiReq(ctypes.Structure):
_fields_ = [('addr', ctypes.POINTER(IpmiSystemInterfaceAddr)),
('addr_len', ctypes.c_uint),
('msgid', ctypes.c_long),
('msg', IpmiMsg)]
_IONONE = 0
_IOWRITE = 1
_IOREAD = 2
IPMICTL_SET_MY_ADDRESS_CMD = (
_IOREAD << 30 | ctypes.sizeof(ctypes.c_uint) << 16
| ord('i') << 8 | 17) # from ipmi.h
IPMICTL_SEND_COMMAND = (
_IOREAD << 30 | ctypes.sizeof(IpmiReq) << 16
| ord('i') << 8 | 13) # from ipmi.h
# next is really IPMICTL_RECEIVE_MSG_TRUNC, but will only use that
IPMICTL_RECV = (
(_IOWRITE | _IOREAD) << 30 | ctypes.sizeof(IpmiRecv) << 16
| ord('i') << 8 | 11) # from ipmi.h
BMC_SLAVE_ADDR = ctypes.c_uint(0x20)
CURRCHAN = 0xf
ADDRTYPE = 0xc
class Session(object):
def __init__(self, devnode='/dev/ipmi0'):
"""Create a local session inband
:param: devnode: The path to the ipmi device
"""
self.ipmidev = open(devnode, 'r+')
fcntl.ioctl(self.ipmidev, IPMICTL_SET_MY_ADDRESS_CMD, BMC_SLAVE_ADDR)
# the interface is initted, create some reusable memory for our session
self.databuffer = ctypes.create_string_buffer(4096)
self.req = IpmiReq()
self.rsp = IpmiRecv()
self.addr = IpmiSystemInterfaceAddr()
self.req.msg.data = ctypes.cast(
ctypes.addressof(self.databuffer),
ctypes.POINTER(ctypes.c_ubyte))
self.rsp.msg.data = self.req.msg.data
self.userid = None
self.password = None
def await_reply(self):
rd, _, _ = select((self.ipmidev,), (), (), 1)
while not rd:
rd, _, _ = select((self.ipmidev,), (), (), 1)
def pause(self, seconds):
time.sleep(seconds)
@property
def parsed_rsp(self):
response = {'netfn': self.rsp.msg.netfn, 'command': self.rsp.msg.cmd,
'code': bytearray(self.databuffer.raw)[0],
'data': bytearray(
self.databuffer.raw[1:self.rsp.msg.data_len])}
return response
def await_config(s, bmccfg, channel):
vlan = bmccfg.get('bmcvlan', None)
ipv4 = bmccfg.get('bmcipv4', None)
prefix = bmccfg.get('prefixv4', None)
gw = bmccfg.get('bmcgw', None)
def raw_command(self,
netfn,
command,
data=(),
bridge_request=None,
retry=True,
delay_xmit=None,
timeout=None,
waitall=False, rslun=0):
self.addr.channel = CURRCHAN
self.addr.addr_type = ADDRTYPE
self.addr.lun = rslun
self.req.addr_len = ctypes.sizeof(IpmiSystemInterfaceAddr)
self.req.addr = ctypes.pointer(self.addr)
self.req.msg.netfn = netfn
self.req.msg.cmd = command
if data:
data = memoryview(bytearray(data))
try:
self.databuffer[:len(data)] = data[:len(data)]
except ValueError:
self.databuffer[:len(data)] = data[:len(data)].tobytes()
self.req.msg.data_len = len(data)
fcntl.ioctl(self.ipmidev, IPMICTL_SEND_COMMAND, self.req)
self.await_reply()
self.rsp.msg.data_len = 4096
self.rsp.addr = ctypes.pointer(self.addr)
self.rsp.addr_len = ctypes.sizeof(IpmiSystemInterfaceAddr)
fcntl.ioctl(self.ipmidev, IPMICTL_RECV, self.rsp)
return self.parsed_rsp
def _is_tsm(model):
return model in ('7y00', '7z01', '7y98', '7y99')
def set_port(s, port, vendor, model):
oport = port
if vendor not in ('IBM', 'Lenovo'):
raise Exception('{0} not implemented'.format(vendor))
if _is_tsm(model):
return set_port_tsm(s, port, model)
else:
set_port_xcc(s, port, model)
return 1
def set_port_tsm(s, port, model):
oport = port
sys.stdout.write('Setting TSM port to "{}"...'.format(oport))
sys.stdout.flush()
if port == 'ocp':
s.raw_command(0x32, 0x71, b'\x00\x01\x00')
elif port == 'dedicated':
s.raw_command(0x32, 0x71, b'\x00\x00\x00')
else:
raise Exception("Unsupported port for TSM")
timer = 15
while timer:
timer = timer - 1
time.sleep(1.0)
sys.stdout.write('.')
sys.stdout.flush()
if port == 'ocp':
iface = 0
s.raw_command(0x32, 0x71, b'\x00\x00\x03')
elif port == 'dedicated':
iface = 1
s.raw_command(0x32, 0x71, b'\x00\x01\x03')
rsp = s.raw_command(0x32, 0x72, bytearray([4, iface, 0]))
print('Complete')
return int(rsp['data'][0])
def set_port_xcc(s, port, model):
oport = port
if port.lower() == 'dedicated':
port = b'\x01'
elif port.lower() in ('ml2', 'ocp'):
port = b'\x02\x00'
elif port.lower() == 'lom':
if model == '7x58':
port = b'\x00\x02'
else:
port = b'\x00\x00'
else:
port = port.split(' ')
port = bytes(bytearray([int(x) for x in port]))
currport = bytes(s.raw_command(0xc, 2, b'\x01\xc0\x00\x00')['data'][1:])
if port == currport:
sys.stdout.write('XCC port already set to "{}"\n'.format(oport))
return
sys.stdout.write('Setting XCC port to "{}"...'.format(oport))
sys.stdout.flush()
s.raw_command(0xc, 1, b'\x01\xc0' + port)
tries = 60
while currport != port and tries:
tries -= 1
time.sleep(0.5)
sys.stdout.write('.')
sys.stdout.flush()
currport = bytes(s.raw_command(0xc, 2, b'\x01\xc0\x00\x00')['data'][1:])
if not tries:
raise Exception('Timeout attempting to set port')
sys.stdout.write('Complete\n')
def check_vlan(s, vlan, channel):
if vlan == 'off':
vlan = b'\x00\x00'
else:
vlan = int(vlan)
if vlan:
vlan = vlan | 32768
vlan = struct.pack('<H', vlan)
currvlan = bytes(s.raw_command(0xc, 2, bytearray([channel, 0x14 ,0, 0]))['data'][1:])
if bytearray(currvlan)[1] & 0b10000000 == 0:
currvlan = b'\x00\x00'
return currvlan == vlan
def set_vlan(s, vlan, channel):
ovlan = vlan
if vlan == 'off':
vlan = b'\x00\x00'
else:
vlan = int(vlan)
if vlan:
vlan = vlan | 32768
vlan = struct.pack('<H', vlan)
if check_vlan(s, ovlan, channel):
sys.stdout.write('VLAN already configured to "{0}"\n'.format(ovlan))
return False
rsp = s.raw_command(0xc, 1, bytearray([channel, 0x14]) + vlan)
if rsp.get('code', 1) == 0:
print('VLAN configured to "{}"'.format(ovlan))
else:
print('Error setting vlan: ' + repr(rsp))
return True
def get_lan_channel(s):
for chan in range(1, 16):
rsp = s.raw_command(6, 0x42, bytearray([chan]))['data']
if not rsp:
continue
medtype = int(rsp[1]) & 0b1111111
if medtype not in (4, 6):
continue
rsp = s.raw_command(0xc, 2, bytearray([2, chan, 5, 0, 0]))
if rsp.get('code', 1) == 0:
return chan
return 1
def check_ipv4(s, ipaddr, channel):
ipaddr = bytearray(socket.inet_aton(ipaddr))
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
return rsp == ipaddr
def set_ipv4(s, ipaddr, channel):
oipaddr = ipaddr
ipaddr = bytearray(socket.inet_aton(ipaddr))
if check_ipv4(s, oipaddr, channel):
print('IP Address already set to {}'.format(oipaddr))
return False
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
if rsp != 1:
sys.stdout.write("Changing configuration to static...")
sys.stdout.flush()
resp = s.raw_command(0xc, 1, bytearray([channel, 4, 1]))
tries = 0
while rsp != 1 and tries < 30:
sys.stdout.write('.')
sys.stdout.flush()
tries += 1
time.sleep(0.5)
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
sys.stdout.write('Complete\n')
sys.stdout.flush()
print('Setting IP to {}'.format(oipaddr))
s.raw_command(0xc, 1, bytearray([channel, 3]) + ipaddr)
return True
def check_subnet(s, prefix, channel):
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
return rsp == mask
def set_subnet(s, prefix, channel):
oprefix = prefix
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
if check_subnet(s, prefix, channel):
print('Subnet Mask already set to /{}'.format(oprefix))
return False
print('Setting subnet mask to /{}'.format(oprefix))
s.raw_command(0xc, 1, bytearray([channel, 6]) + mask)
return True
def check_gateway(s, gw, channel):
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
return rsp == gw
def set_gateway(s, gw, channel):
ogw = gw
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
if check_gateway(s, ogw, channel):
print('Gateway already set to {}'.format(ogw))
return False
print('Setting gateway to {}'.format(ogw))
s.raw_command(0xc, 1, bytearray([channel, 12]) + gw)
return True
def dotwait():
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(0.5)
def main():
a = argparse.ArgumentParser(description='Locally configure a BMC device')
a.add_argument('-v', help='vlan id or off to disable vlan tagging')
a.add_argument('-p', help='Which port to use (dedicated, lom, ocp, ml2)')
a.add_argument('-i', help='JSON configuration file to read for '
'configuration')
a.add_argument('-c', help='Use Confluent API to direct BMC configuration',
action='store_true')
args = a.parse_args()
if args.i:
bmccfg = json.load(open(args.i))
elif args.c:
bmccfgsrc = subprocess.check_output(
[sys.executable, apiclient, '/confluent-api/self/bmcconfig', '-j'])
bmccfg = json.loads(bmccfgsrc)
else:
bmccfg = {}
if args.p is not None:
bmccfg['bmcport'] = args.p
if args.v is not None:
bmccfg['bmcvlan'] = args.v
vendor = open('/sys/devices/virtual/dmi/id/sys_vendor').read()
vendor = vendor.strip()
try:
model = open('/sys/devices/virtual/dmi/id/product_sku').read().strip()
if model == 'none':
raise Exception('No SKU')
except Exception:
model = open('/sys/devices/virtual/dmi/id/product_name').read()
if vendor in ('Lenovo', 'IBM'):
if '[' in model and ']' in model:
model = model.split('[')[1].split(']')[0]
model = model[:4].lower()
s = Session('/dev/ipmi0')
if not bmccfg:
print("No BMC configuration specified, exiting.")
return
if bmccfg.get('bmcport', None):
channel = set_port(s, bmccfg['bmcport'], vendor, model)
else:
channel = get_lan_channel(s)
awaitvlan = False
awaitip = False
awaitprefix = False
awaitgw = False
if bmccfg.get('bmcvlan', None):
awaitvlan = set_vlan(s, bmccfg['bmcvlan'], channel)
if bmccfg.get('bmcipv4', None):
awaitip = set_ipv4(s, bmccfg['bmcipv4'], channel)
if bmccfg.get('prefixv4', None):
awaitprefix = set_subnet(s, bmccfg['prefixv4'], channel)
if bmccfg.get('bmcgw', None):
awaitgw = set_gateway(s, bmccfg['bmcgw'], channel)
sys.stdout.write('Waiting for changes to take effect...')
sys.stdout.flush()
while awaitvlan and not check_vlan(s, bmccfg['bmcvlan'], channel):
dotwait()
while awaitip and not check_ipv4(s, bmccfg['bmcipv4'], channel):
dotwait()
while awaitprefix and not check_subnet(s, bmccfg['prefixv4'], channel):
dotwait()
while awaitprefix and not check_gateway(s, bmccfg['bmcgw'], channel):
dotwait()
sys.stdout.write('done\n')
sys.stdout.flush()
#await_config(s, bmccfg, channel)
if __name__ == '__main__':
main()
@@ -4,7 +4,7 @@ Requires=network-online.target
After=network-online.target
[Service]
ExecStart=/etc/confluent/firstboot.sh
ExecStart=/opt/confluent/bin/firstboot.sh
[Install]
WantedBy=multi-user.target
@@ -6,17 +6,31 @@
# the script notifies confluent that install is fully complete.
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
export nodename mgr profile
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export nodename confluent_mgr confluent_profile
. /etc/confluent/functions
exec >> /var/log/confluent/confluent-firstboot.log
exec 2>> /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
if [ ! -f /etc/confluent/firstboot.ran ]; then
touch /etc/confluent/firstboot.ran
run_remote firstboot.custom
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
run_remote firstboot.custom
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
run_remote_parts firstboot.d
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
# Induce execution of remote configuration, e.g. ansible plays in ansible/firstboot.d/
run_remote_config firstboot.d
fi
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
systemctl disable firstboot
rm /etc/systemd/system/firstboot.service
rm /etc/confluent/firstboot.ran
kill $logshowpid
@@ -1,37 +1,73 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
fetch_remote $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
chmod +x $1
cmd=$1
shift
source $cmd
cd - > /dev/null
return $retcode
}
run_remote() {
requestedcmd="'$*'"
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
fetch_remote $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -41,7 +77,9 @@ run_remote() {
shift
./$cmd $*
retcode=$?
echo "$requestedcmd exited with code $retcode"
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
return $retcode
}
@@ -53,12 +91,12 @@ run_remote_python() {
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
@@ -70,3 +108,26 @@ run_remote_python() {
cd - > /dev/null
return $retcode
}
run_remote_config() {
echo
set_confluent_vars
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
echo '---------------------------------------------------------------------------'
echo Requesting to run remote configuration for "'$*'" from $confluent_mgr under profile $confluent_profile
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/status -w 204
else
/usr/bin/python $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
/usr/bin/python $apiclient /confluent-api/self/remoteconfig/status -w 204
fi
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
@@ -9,12 +9,11 @@ if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
# two lines to use the .iso instead of the tgz packaging
#fetch_remote infiniband/mofed.iso
#mkdir MLNX_OFED
#mount -o loop ofed.iso MLNX_OFED
#mount -o loop infiniband/mofed.iso MLNX_OFED
fetch_remote infiniband/mofed.tgz
tar xf infiniband/mofed.tgz
# The rest is common between tar and iso
cd MLNX_OFED*
mount -o loop ofed
./mlnxofedinstall --force
fi
@@ -2,6 +2,6 @@
# This is a convenient place to keep customizations separate from modifying the stock scripts
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
# stock profile if the '.custom' files are used.
# An example for installing OFED for infiniband follows (see the file for more detail):
#run_remote infiniband/mofed.post
@@ -1,20 +1,22 @@
#!/bin/sh
# need to copy over ssh key info
mkdir -p /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
tail -f /var/log/confluent/confluent-post.log > /dev/tty &
logshowpid=$!
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
chmod 700 /etc/confluent
chmod og-rwx /etc/confluent/*
export mgr profile nodename
export confluent_mgr confluent_profile nodename
. /etc/confluent/functions
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
if [ -f /tmp/cryptboot ]; then
run_remote tpm_luks.sh
fi
# By default, the install repository is ignored, change
# this by manually adding local repositories
@@ -30,6 +32,18 @@ run_remote_python add_local_repositories
# run_remote_python will use the appropriate python interpreter path to run the specified script
# A post.custom is provided to more conveniently hold customizations, see the post.custom file.
# This will induce server side processing of the syncfile contents if
# present
run_remote_python syncfileclient
# run_remote example.sh
# run_remote_python example.py
run_remote post.custom
# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh
run_remote_parts post.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
run_remote_config post.d
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$confluent_mgr/confluent-api/self/updatestatus
kill $logshowpid
@@ -11,4 +11,3 @@
#Some addons improve efficiency by adding dependencies during install
#here is an example for adding OFED install prereqs to the install
#run_remote infiniband/mofed.pre
@@ -7,7 +7,14 @@
# If you want to use a more custom partition plan, the easiest
# method is to edit the kicktstart file and comment out or
# delete %include /tmp/partitioning
if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
TMUX= tmux a <> $consoledev >&0 2>&1 &
fi
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
tail -f /tmp/confluent-pre.log > /dev/tty &
logshowpid=$!
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
locale=$(grep ^locale: /etc/confluent/confluent.deploycfg)
locale=${locale#locale: }
@@ -28,7 +35,7 @@ if [ "$rootpw" = null ]; then
else
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
fi
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/profile.yaml > /tmp/instprofile.yaml
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
if [ ! -z "$blargs" ]; then
blargs=' --append="'$blargs'"'
@@ -43,33 +50,33 @@ if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host_*_key.pub; do
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$mgr/confluent-api/self/sshcert > $certfile
curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile
echo HostCertificate $certfile >> /etc/ssh/sshd_config.anaconda
done
/usr/sbin/sshd -f /etc/ssh/sshd_config.anaconda
if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
TMUX= tmux a <> $consoledev >&0 2>&1 &
fi
touch /tmp/addonpackages
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
LUKSPARTY=''
touch /tmp/addonpackages
if [ "$cryptboot" == "tpm2" ]; then
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
echo $cryptboot >> /tmp/cryptboot
fi
export mgr profile nodename
curl -f https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions
export confluent_mgr confluent_profile nodename
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
run_remote_python getinstalldisk
if [ -e /tmp/installdisk ]; then
run_remote pre.custom
run_remote_parts pre.d
if [ ! -e /tmp/installdisk ]; then
run_remote_python getinstalldisk
fi
if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then
echo clearpart --all --initlabel >> /tmp/partitioning
echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning
echo autopart --nohome $LUKSPARTY >> /tmp/partitioning
fi
python /etc/confluent/apiclient /confluent-public/os/$profile/kickstart.custom -o /tmp/kickstart.custom
run_remote pre.custom
python /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
kill $logshowpid
@@ -8,11 +8,22 @@
# for customization, which will run in a more normal mechanism
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
export mgr profile nodename
export confluent_mgr confluent_profile nodename
cp -a /etc/confluent /mnt/sysimage/etc
chmod -R og-rwx /mnt/sysimage/etc/confluent
cp /tmp/functions /mnt/sysimage/etc/confluent/
hostnamectl set-hostname $nodename
cp /etc/hostname /mnt/sysimage/etc/hostname
. /tmp/functions
cp /tmp/cryptboot /mnt/sysimage/tmp/
if [ -f /tmp/cryptboot ]; then
cp /tmp/cryptboot /mnt/sysimage/tmp/
fi
mkdir -p /mnt/sysimage/var/log/confluent
mv /tmp/confluent-pre.log /mnt/sysimage/var/log/confluent
echo Port 2222 >> /etc/ssh/sshd_config.anaconda
echo Match LocalPort 22 >> /etc/ssh/sshd_config.anaconda
echo " ChrootDirectory /mnt/sysimage" >> /etc/ssh/sshd_config.anaconda
kill -HUP $(cat /run/sshd.pid)
# Preserve the ssh setup work done for the installer
# by copying into the target system and setting up
@@ -18,6 +18,6 @@ chmod 700 /mnt/sysimage/root/.ssh/
cp /root/.ssh/authorized_keys /mnt/sysimage/root/.ssh/
chmod 600 /mnt/sysimage/root/.ssh/authorized_keys
cp /etc/ssh/ssh_known_hosts /mnt/sysimage/etc/ssh/
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/nodelist > /tmp/allnodes
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /tmp/allnodes
cp /tmp/allnodes /mnt/sysimage/etc/ssh/shosts.equiv
cp /tmp/allnodes /mnt/sysimage/root/.shosts
@@ -0,0 +1,225 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
import shutil
try:
from importlib.machinery import SourceFileLoader
def load_source(mod, path):
return SourceFileLoader(mod, path).load_module()
except ImportError:
from imp import load_source
try:
apiclient = load_source('apiclient', '/opt/confluent/bin/apiclient')
except IOError:
apiclient = load_source('apiclient', '/etc/confluent/apiclient')
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger:
def __init__(self):
self.byip = {}
self.byname = {}
self.sourcelines = []
self.targlines = []
def read_source(self, sourcefile):
with open(sourcefile, 'r') as hfile:
self.sourcelines = hfile.read().split('\n')
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
for x in range(len(self.sourcelines)):
line = self.sourcelines[x]
currip, names, comment = partitionhostsline(line)
if currip:
self.byip[currip] = x
for name in names:
self.byname[name] = x
def read_target(self, targetfile):
with open(targetfile, 'r') as hfile:
lines = hfile.read().split('\n')
if not lines[-1]:
lines = lines[:-1]
for y in range(len(lines)):
line = lines[y]
currip, names, comment = partitionhostsline(line)
if currip in self.byip:
x = self.byip[currip]
if self.sourcelines[x] is None:
# have already consumed this enntry
continue
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
continue
for name in names:
if name in self.byname:
x = self.byname[name]
if self.sourcelines[x] is None:
break
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
break
else:
self.targlines.append(line)
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
for line in self.sourcelines:
if line is not None:
hosts.write(line + '\n')
class CredMerger:
def __init__(self):
try:
with open('/etc/login.defs', 'r') as ldefs:
defs = ldefs.read().split('\n')
except FileNotFoundError:
defs = []
lkup = {}
self.discardnames = {}
self.shadowednames = {}
for line in defs:
try:
line = line[:line.index('#')]
except ValueError:
pass
keyval = line.split()
if len(keyval) < 2:
continue
lkup[keyval[0]] = keyval[1]
self.uidmin = int(lkup.get('UID_MIN', 1000))
self.uidmax = int(lkup.get('UID_MAX', 60000))
self.gidmin = int(lkup.get('GID_MIN', 1000))
self.gidmax = int(lkup.get('GID_MAX', 60000))
self.shadowlines = None
def read_passwd(self, source, targfile=False):
self.read_generic(source, self.uidmin, self.uidmax, targfile)
def read_group(self, source, targfile=False):
self.read_generic(source, self.gidmin, self.gidmax, targfile)
def read_generic(self, source, minid, maxid, targfile):
if targfile:
self.targdata = []
else:
self.sourcedata = []
with open(source, 'r') as inputfile:
for line in inputfile.read().split('\n'):
try:
name, _, uid, _ = line.split(':', 3)
uid = int(uid)
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
self.shadowlines = []
try:
with open(source, 'r') as inshadow:
for line in inshadow.read().split('\n'):
try:
name, _ = line.split(':' , 1)
except ValueError:
continue
if name in self.discardnames:
continue
self.shadowednames[name] = 1
self.shadowlines.append(line)
except FileNotFoundError:
return
def write_out(self, outfile):
with open(outfile, 'w') as targ:
for line in self.targdata:
targ.write(line + '\n')
for line in self.sourcedata:
targ.write(line + '\n')
if outfile == '/etc/passwd':
if self.shadowlines is None:
self.read_shadow('/etc/shadow')
with open('/etc/shadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
if outfile == '/etc/group':
if self.shadowlines is None:
self.read_shadow('/etc/gshadow')
with open('/etc/gshadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':' , 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!::\n')
def synchronize():
tmpdir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
cm.read_passwd(pendpasswd, targfile=False)
cm.read_passwd('/etc/passwd', targfile=True)
cm.write_out('/etc/passwd')
pendgroup = os.path.join(tmpdir, 'etc/group')
if os.path.exists(pendgroup):
cm = CredMerger()
cm.read_group(pendgroup, targfile=False)
cm.read_group('/etc/group', targfile=True)
cm.write_out('/etc/group')
pendhosts = os.path.join(tmpdir, 'etc/hosts')
if os.path.exists(pendhosts):
cm = HostMerger()
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
finally:
shutil.rmtree(tmpdir)
if __name__ == '__main__':
synchronize()
@@ -0,0 +1,13 @@
# This file lists files to synchronize or merge to the deployed systems from the deployment server
# To specify taking /some/path/hosts on the deploymennt server and duplicating it to /etc/hosts:
# /some/path/hosts -> /etc/hosts
# If wanting to simply use the same path for source and destinaiton, the -> may be skipped:
# /etc/hosts
# Entries under MERGE will attempt to be intelligently merged. This supports /etc/group and /etc/passwd
# Any supporting entries in /etc/shadow or /etc/gshadow are added automatically, with password disabled
# It also will not inject 'system' ids (under 1,000 usually) as those tend to be local and rpm managed.
MERGE:
# /etc/passwd
# /etc/group
@@ -13,7 +13,7 @@ sed -i 's/install::/install:*:/' /sysroot/etc/shadow
sed -i 's/root::/root:*:/' /sysroot/etc/shadow
mkdir -p /sysroot/root/.ssh
chmod 700 /sysroot/root/.ssh
cat /ssh/*.rootpubkey > /sysroot/root/.ssh/authorized_keys
cat /ssh/*pubkey > /sysroot/root/.ssh/authorized_keys
chmod 600 /sysroot/root/.ssh/authorized_keys
mkdir -p /sysroot/etc/ssh/
for i in /ssh/*.ca; do
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -1,5 +1,5 @@
#!/bin/sh
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/' $2/profile.yaml
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/; s/alma/AlmaLinux/' $2/profile.yaml
ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \
ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution
mkdir -p $2/boot/efi/boot && \
@@ -52,23 +52,23 @@ pciutils
%include /tmp/kickstart.custom
%pre
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/preinst.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/preinst.sh
. /tmp/preinst.sh
%end
%post --nochroot
mkdir -p /mnt/sysimage/etc/confluent
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/postinst.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/postinst.sh
. /tmp/postinst.sh
# Hook firstboot.sh
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
mkdir -p /mnt/sysimage/opt/confluent/bin
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh
chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh
%end
@@ -77,8 +77,8 @@ cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
systemctl enable firstboot
chgrp ssh_keys /etc/ssh/ssh*key
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /opt/confluent/bin/firstboot.sh
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/postinst.sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /tmp/postinst.sh
. /tmp/postinst.sh
%end
@@ -6,19 +6,31 @@
# the script notifies confluent that install is fully complete.
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
export nodename mgr profile
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export nodename confluent_mgr confluent_profile
. /etc/confluent/functions
exec >> /var/log/confluent/confluent-firstboot.log
exec 2>> /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
if [ ! -f /etc/confluent/firstboot.ran ]; then
touch /etc/confluent/firstboot.ran
run_remote firstboot.custom
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
run_remote_parts firstboot
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
run_remote firstboot.custom
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
run_remote_parts firstboot.d
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
# Induce execution of remote configuration, e.g. ansible plays in ansible/firstboot.d/
run_remote_config firstboot.d
fi
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
systemctl disable firstboot
rm /etc/systemd/system/firstboot.service
rm /etc/confluent/firstboot.ran
kill $logshowpid
@@ -1,44 +1,73 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
run_remote_parts() {
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1.d/$script
done
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
fetch_remote $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
chmod +x $1
cmd=$1
shift
source $cmd
cd - > /dev/null
return $retcode
}
run_remote() {
requestedcmd="'$*'"
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
fetch_remote $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -48,7 +77,9 @@ run_remote() {
shift
./$cmd $*
retcode=$?
echo "$requestedcmd exited with code $retcode"
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
return $retcode
}
@@ -60,16 +91,43 @@ run_remote_python() {
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
else
/usr/bin/python $*
fi
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
return $retcode
}
run_remote_config() {
echo
set_confluent_vars
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
echo '---------------------------------------------------------------------------'
echo Requesting to run remote configuration for "'$*'" from $confluent_mgr under profile $confluent_profile
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/status -w 204
else
/usr/bin/python $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
/usr/bin/python $apiclient /confluent-api/self/remoteconfig/status -w 204
fi
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
@@ -9,12 +9,11 @@ if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
# two lines to use the .iso instead of the tgz packaging
#fetch_remote infiniband/mofed.iso
#mkdir MLNX_OFED
#mount -o loop ofed.iso MLNX_OFED
#mount -o loop infiniband/mofed.iso MLNX_OFED
fetch_remote infiniband/mofed.tgz
tar xf infiniband/mofed.tgz
# The rest is common between tar and iso
cd MLNX_OFED*
mount -o loop ofed
./mlnxofedinstall --force
fi
@@ -1,15 +1,17 @@
#!/bin/sh
# need to copy over ssh key info
mkdir -p /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
tail -f /var/log/confluent/confluent-post.log > /dev/tty &
logshowpid=$!
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
chmod 700 /etc/confluent
chmod og-rwx /etc/confluent/*
export mgr profile nodename
export confluent_mgr confluent_profile nodename
. /etc/confluent/functions
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
if [ -f /tmp/cryptboot ]; then
run_remote tpm_luks.sh
@@ -30,9 +32,18 @@ run_remote_python add_local_repositories
# run_remote_python will use the appropriate python interpreter path to run the specified script
# A post.custom is provided to more conveniently hold customizations, see the post.custom file.
# This will induce server side processing of the syncfile contents if
# present
run_remote_python syncfileclient
# run_remote example.sh
# run_remote_python example.py
run_remote post.custom
# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh
run_remote_parts post
run_remote_parts post.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
run_remote_config post.d
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$confluent_mgr/confluent-api/self/updatestatus
kill $logshowpid
@@ -7,7 +7,14 @@
# If you want to use a more custom partition plan, the easiest
# method is to edit the kicktstart file and comment out or
# delete %include /tmp/partitioning
if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
TMUX= tmux a <> $consoledev >&0 2>&1 &
fi
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
tail -f /tmp/confluent-pre.log > /dev/tty &
logshowpid=$!
/usr/libexec/platform-python /etc/confluent/apiclient >& /dev/null
nicname=$(ip link|grep ^$(cat /tmp/confluent.ifidx): | awk '{print $2}' | awk -F: '{print $1}')
nmcli c u $nicname
@@ -34,7 +41,7 @@ if [ "$rootpw" = null ]; then
else
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
fi
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/profile.yaml > /tmp/instprofile.yaml
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
if [ ! -z "$blargs" ]; then
blargs=' --append="'$blargs'"'
@@ -48,16 +55,13 @@ fi
if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$mgr/confluent-api/self/sshcert > $certfile
curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile
echo HostCertificate $certfile >> /etc/ssh/sshd_config.anaconda
done
/usr/sbin/sshd -f /etc/ssh/sshd_config.anaconda
if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
tmux a <> $consoledev >&0 2>&1 &
fi
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
LUKSPARTY=''
touch /tmp/cryptpkglist
@@ -65,18 +69,29 @@ touch /tmp/addonpackages
if [ "$cryptboot" == "tpm2" ]; then
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
echo $cryptboot >> /tmp/cryptboot
echo clevis-dracut >> /tmp/cryptpkglist
echo clevis-dracut >> /tmp/cryptpkglist
fi
export mgr profile nodename
curl -f https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions
export confluent_mgr confluent_profile nodename
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
run_remote_python getinstalldisk
if [ -e /tmp/installdisk ]; then
run_remote pre.custom
run_remote_parts pre.d
if [ ! -e /tmp/installdisk ]; then
run_remote_python getinstalldisk
fi
if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then
echo clearpart --all --initlabel >> /tmp/partitioning
echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning
echo autopart --nohome $LUKSPARTY >> /tmp/partitioning
fi
curl -f https://$mgr/confluent-public/os/$profile/kickstart.custom > /tmp/kickstart.custom
run_remote pre.custom
if [ -e /usr/libexec/platform-python ]; then
python=/usr/libexec/platform-python
elif [ -e /usr/bin/python3 ]; then
python=/usr/byn/python3
else
python=/usr/bin/python
fi
$python /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
kill $logshowpid
@@ -8,12 +8,22 @@
# for customization, which will run in a more normal mechanism
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
export mgr profile nodename
export confluent_mgr confluent_profile nodename
cp -a /etc/confluent /mnt/sysimage/etc
chmod -R og-rwx /mnt/sysimage/etc/confluent
cp /tmp/functions /mnt/sysimage/etc/confluent/
hostnamectl set-hostname $nodename
cp /etc/hostname /mnt/sysimage/etc/hostname
. /tmp/functions
cp /tmp/cryptboot /mnt/sysimage/tmp/
if [ -f /tmp/cryptboot ]; then
cp /tmp/cryptboot /mnt/sysimage/tmp/
fi
mkdir -p /mnt/sysimage/var/log/confluent
mv /tmp/confluent-pre.log /mnt/sysimage/var/log/confluent
echo Port 2222 >> /etc/ssh/sshd_config.anaconda
echo Match LocalPort 22 >> /etc/ssh/sshd_config.anaconda
echo " ChrootDirectory /mnt/sysimage" >> /etc/ssh/sshd_config.anaconda
kill -HUP $(cat /run/sshd.pid)
# Preserve the ssh setup work done for the installer
# by copying into the target system and setting up
@@ -18,6 +18,6 @@ chmod 700 /mnt/sysimage/root/.ssh/
cp /root/.ssh/authorized_keys /mnt/sysimage/root/.ssh/
chmod 600 /mnt/sysimage/root/.ssh/authorized_keys
cp /etc/ssh/ssh_known_hosts /mnt/sysimage/etc/ssh/
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/nodelist > /tmp/allnodes
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /tmp/allnodes
cp /tmp/allnodes /mnt/sysimage/etc/ssh/shosts.equiv
cp /tmp/allnodes /mnt/sysimage/root/.shosts
@@ -0,0 +1,219 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
import shutil
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
except FileNotFoundError:
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger:
def __init__(self):
self.byip = {}
self.byname = {}
self.sourcelines = []
self.targlines = []
def read_source(self, sourcefile):
with open(sourcefile, 'r') as hfile:
self.sourcelines = hfile.read().split('\n')
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
for x in range(len(self.sourcelines)):
line = self.sourcelines[x]
currip, names, comment = partitionhostsline(line)
if currip:
self.byip[currip] = x
for name in names:
self.byname[name] = x
def read_target(self, targetfile):
with open(targetfile, 'r') as hfile:
lines = hfile.read().split('\n')
if not lines[-1]:
lines = lines[:-1]
for y in range(len(lines)):
line = lines[y]
currip, names, comment = partitionhostsline(line)
if currip in self.byip:
x = self.byip[currip]
if self.sourcelines[x] is None:
# have already consumed this enntry
continue
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
continue
for name in names:
if name in self.byname:
x = self.byname[name]
if self.sourcelines[x] is None:
break
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
break
else:
self.targlines.append(line)
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
for line in self.sourcelines:
if line is not None:
hosts.write(line + '\n')
class CredMerger:
def __init__(self):
try:
with open('/etc/login.defs', 'r') as ldefs:
defs = ldefs.read().split('\n')
except FileNotFoundError:
defs = []
lkup = {}
self.discardnames = {}
self.shadowednames = {}
for line in defs:
try:
line = line[:line.index('#')]
except ValueError:
pass
keyval = line.split()
if len(keyval) < 2:
continue
lkup[keyval[0]] = keyval[1]
self.uidmin = int(lkup.get('UID_MIN', 1000))
self.uidmax = int(lkup.get('UID_MAX', 60000))
self.gidmin = int(lkup.get('GID_MIN', 1000))
self.gidmax = int(lkup.get('GID_MAX', 60000))
self.shadowlines = None
def read_passwd(self, source, targfile=False):
self.read_generic(source, self.uidmin, self.uidmax, targfile)
def read_group(self, source, targfile=False):
self.read_generic(source, self.gidmin, self.gidmax, targfile)
def read_generic(self, source, minid, maxid, targfile):
if targfile:
self.targdata = []
else:
self.sourcedata = []
with open(source, 'r') as inputfile:
for line in inputfile.read().split('\n'):
try:
name, _, uid, _ = line.split(':', 3)
uid = int(uid)
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
self.shadowlines = []
try:
with open(source, 'r') as inshadow:
for line in inshadow.read().split('\n'):
try:
name, _ = line.split(':' , 1)
except ValueError:
continue
if name in self.discardnames:
continue
self.shadowednames[name] = 1
self.shadowlines.append(line)
except FileNotFoundError:
return
def write_out(self, outfile):
with open(outfile, 'w') as targ:
for line in self.targdata:
targ.write(line + '\n')
for line in self.sourcedata:
targ.write(line + '\n')
if outfile == '/etc/passwd':
if self.shadowlines is None:
self.read_shadow('/etc/shadow')
with open('/etc/shadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
if outfile == '/etc/group':
if self.shadowlines is None:
self.read_shadow('/etc/gshadow')
with open('/etc/gshadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':' , 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!::\n')
def synchronize():
tmpdir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
cm.read_passwd(pendpasswd, targfile=False)
cm.read_passwd('/etc/passwd', targfile=True)
cm.write_out('/etc/passwd')
pendgroup = os.path.join(tmpdir, 'etc/group')
if os.path.exists(pendgroup):
cm = CredMerger()
cm.read_group(pendgroup, targfile=False)
cm.read_group('/etc/group', targfile=True)
cm.write_out('/etc/group')
pendhosts = os.path.join(tmpdir, 'etc/hosts')
if os.path.exists(pendhosts):
cm = HostMerger()
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
finally:
shutil.rmtree(tmpdir)
if __name__ == '__main__':
synchronize()
@@ -0,0 +1,13 @@
# This file lists files to synchronize or merge to the deployed systems from the deployment server
# To specify taking /some/path/hosts on the deploymennt server and duplicating it to /etc/hosts:
# /some/path/hosts -> /etc/hosts
# If wanting to simply use the same path for source and destinaiton, the -> may be skipped:
# /etc/hosts
# Entries under MERGE will attempt to be intelligently merged. This supports /etc/group and /etc/passwd
# Any supporting entries in /etc/shadow or /etc/gshadow are added automatically, with password disabled
# It also will not inject 'system' ids (under 1,000 usually) as those tend to be local and rpm managed.
MERGE:
# /etc/passwd
# /etc/group
@@ -7,13 +7,40 @@ echo $uuid > /sys/devices/virtual/dmi/id/product_uuid
mkdir -p /etc/confluent
localcli network firewall unload
touch /etc/confluent/confluent.info
begin=$(date +%s)
while ! grep NODENAME /etc/confluent/confluent.info; do
echo "Searching for deployment service on local network..."
/opt/confluent/bin/copernicus > /etc/confluent/confluent.info
esxcfg-nics -l
if [ $(date +%s) -gt $((begin + 90)) ]; then
if [ ! -f /var/run/vmware/show-esx-shell-login ]; then
chvt 1
sleep 1
chvt 2
/etc/init.d/ESXShell start
fi
echo "Unable to locate a deployment system on the local network, verify network connectivity"
echo "A debug session has been made available on Alt-F1"
sleep 30
fi
done
node=$(grep NODENAME: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
mgr=$(grep MANAGER: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
cp /opt/confluent/bin/clortho /clortho
/clortho $node $mgr > /etc/confluent/confluent.apikey
APIKEY=$(cat /etc/confluent/confluent.apikey)
while [ -z "$APIKEY" ]; do
if [ ! -f /var/run/vmware/show-esx-shell-login ]; then
chvt 1
sleep 1
chvt 2
/etc/init.d/ESXShell start
fi
echo "Deployment server failed to grant node deployment token, retrying token retrieval"
echo "A debug session has been made available on Alt-F1"
/clortho $node $mgr > /etc/confluent/confluent.apikey
APIKEY=$(cat /etc/confluent/confluent.apikey)
done
cat /tls/*.pem > /etc/confluent/ca.pem
/opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg.new
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg.new | sed -e 's/^profile: //')
@@ -4,7 +4,7 @@ install --firstdisk --overwritevmfs
%include /tmp/ksnet
%include /tmp/rootpw
reboot
%post
%post --interpreter=busybox
localcli network firewall unload
STATUP=$(mktemp)
echo '{"status": "complete"}' > $STATUP
@@ -1,5 +1,8 @@
#!/usr/bin/python
import re
import subprocess
uplinkmatch = re.compile('^\s*Uplinks:\s*(.*)')
nodename = None
for inf in open('/etc/confluent/confluent.info', 'r').read().split('\n'):
if inf.startswith('NODENAME: '):
@@ -24,8 +27,15 @@ for line in deploycfg:
else:
nslist=False
cfg['nameservers'] = ','.join(nameservers)
vswinfo = subprocess.check_output(['localcli', 'network', 'vswitch', 'standard', 'list']).decode('utf8')
vmnic = None
for info in vswinfo.split('\n'):
upinfo = uplinkmatch.match(info)
if upinfo:
vmnic = upinfo.group(1)
netline = 'network --hostname={0} --bootproto={1}'.format(nodename, cfg['ipv4_method'])
if vmnic:
netline += ' --device={0}'.format(vmnic)
if cfg['ipv4_method'] == 'static':
netline += ' --ip={0} --netmask={1}'.format(cfg['ipv4_address'], cfg['ipv4_netmask'])
if cfg.get('ipv4_gateway', 'null') not in (None, '', 'null'):
@@ -32,11 +32,12 @@ echo "done"
cat > /etc/ssh/sshd_config << EOF
Port 22
Port 3389
Subsystem sftp /usr/libexec/openssh/sftp-server
PermitRootLogin yes
AuthorizedKeysFile .ssh/authorized_keys
EOF
mkdir ~/.ssh
cat /ssh/*.rootpubkey > ~/.ssh/authorized_keys
cat /ssh/*pubkey > ~/.ssh/authorized_keys 2>/dev/null
cat /tls/*.pem > /etc/confluent/ca.pem
mkdir -p /etc/pki/tls/certs
cat /tls/*.pem > /etc/pki/tls/certs/ca-bundle.crt
@@ -58,6 +59,7 @@ hostname $nodename
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}')
if [ "$autoconfigmethod" = "dhcp" ]; then
echo -n "Attempting to use dhcp to bring up $ifname..."
dhclient $ifname
@@ -79,7 +81,7 @@ else
ip route add default via $v4gw
fi
fi
echo -n "Initialiing ssh..."
echo -n "Initializing ssh..."
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
@@ -91,11 +93,11 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
echo HostKey $privfile >> /etc/ssh/sshd_config
done
/usr/sbin/sshd
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | awk '{print $2}')
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | awk '{print $2}')
export profile mgr
echo "Running https://$mgr/confluent-public/os/$profile/scripts/onboot.sh"
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/functions > /tmp/functions
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | awk '{print $2}')
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | awk '{print $2}')
export confluent_profile confluent_mgr
echo "Running https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh"
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
run_remote onboot.sh
while :; do
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -1,9 +1,9 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
@@ -12,7 +12,7 @@ fetch_remote() {
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -25,11 +25,11 @@ run_remote() {
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -51,11 +51,11 @@ run_remote_python() {
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
retcode=$?
@@ -63,3 +63,20 @@ run_remote_python() {
cd - > /dev/null
return $retcode
}
run_remote_config() {
echo
set_confluent_vars
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
echo '---------------------------------------------------------------------------'
echo Requesting to run remote configuration for "'$*'" from $confluent_mgr under profile $confluent_profile
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
/usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
@@ -6,6 +6,13 @@
# run_remote and run_remote_python are available to download scripts and
# execute them.
# This will induce server side processing of the syncfile contents if
# present
run_remote_python syncfileclient
# Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/
run_remote_config onboot
# This is an example to request the BMC be configured on the network
# according to how confluent has things configured:
# run_remote_python configbmc -c
@@ -0,0 +1,219 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
import shutil
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
except FileNotFoundError:
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger:
def __init__(self):
self.byip = {}
self.byname = {}
self.sourcelines = []
self.targlines = []
def read_source(self, sourcefile):
with open(sourcefile, 'r') as hfile:
self.sourcelines = hfile.read().split('\n')
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
for x in range(len(self.sourcelines)):
line = self.sourcelines[x]
currip, names, comment = partitionhostsline(line)
if currip:
self.byip[currip] = x
for name in names:
self.byname[name] = x
def read_target(self, targetfile):
with open(targetfile, 'r') as hfile:
lines = hfile.read().split('\n')
if not lines[-1]:
lines = lines[:-1]
for y in range(len(lines)):
line = lines[y]
currip, names, comment = partitionhostsline(line)
if currip in self.byip:
x = self.byip[currip]
if self.sourcelines[x] is None:
# have already consumed this enntry
continue
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
continue
for name in names:
if name in self.byname:
x = self.byname[name]
if self.sourcelines[x] is None:
break
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
break
else:
self.targlines.append(line)
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
for line in self.sourcelines:
if line is not None:
hosts.write(line + '\n')
class CredMerger:
def __init__(self):
try:
with open('/etc/login.defs', 'r') as ldefs:
defs = ldefs.read().split('\n')
except FileNotFoundError:
defs = []
lkup = {}
self.discardnames = {}
self.shadowednames = {}
for line in defs:
try:
line = line[:line.index('#')]
except ValueError:
pass
keyval = line.split()
if len(keyval) < 2:
continue
lkup[keyval[0]] = keyval[1]
self.uidmin = int(lkup.get('UID_MIN', 1000))
self.uidmax = int(lkup.get('UID_MAX', 60000))
self.gidmin = int(lkup.get('GID_MIN', 1000))
self.gidmax = int(lkup.get('GID_MAX', 60000))
self.shadowlines = None
def read_passwd(self, source, targfile=False):
self.read_generic(source, self.uidmin, self.uidmax, targfile)
def read_group(self, source, targfile=False):
self.read_generic(source, self.gidmin, self.gidmax, targfile)
def read_generic(self, source, minid, maxid, targfile):
if targfile:
self.targdata = []
else:
self.sourcedata = []
with open(source, 'r') as inputfile:
for line in inputfile.read().split('\n'):
try:
name, _, uid, _ = line.split(':', 3)
uid = int(uid)
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
self.shadowlines = []
try:
with open(source, 'r') as inshadow:
for line in inshadow.read().split('\n'):
try:
name, _ = line.split(':' , 1)
except ValueError:
continue
if name in self.discardnames:
continue
self.shadowednames[name] = 1
self.shadowlines.append(line)
except FileNotFoundError:
return
def write_out(self, outfile):
with open(outfile, 'w') as targ:
for line in self.targdata:
targ.write(line + '\n')
for line in self.sourcedata:
targ.write(line + '\n')
if outfile == '/etc/passwd':
if self.shadowlines is None:
self.read_shadow('/etc/shadow')
with open('/etc/shadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
if outfile == '/etc/group':
if self.shadowlines is None:
self.read_shadow('/etc/gshadow')
with open('/etc/gshadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':' , 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!::\n')
def synchronize():
tmpdir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
cm.read_passwd(pendpasswd, targfile=False)
cm.read_passwd('/etc/passwd', targfile=True)
cm.write_out('/etc/passwd')
pendgroup = os.path.join(tmpdir, 'etc/group')
if os.path.exists(pendgroup):
cm = CredMerger()
cm.read_group(pendgroup, targfile=False)
cm.read_group('/etc/group', targfile=True)
cm.write_out('/etc/group')
pendhosts = os.path.join(tmpdir, 'etc/hosts')
if os.path.exists(pendhosts):
cm = HostMerger()
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
finally:
shutil.rmtree(tmpdir)
if __name__ == '__main__':
synchronize()
@@ -12,7 +12,7 @@ sed -i 's/install::/install:*:/' /sysroot/etc/shadow
sed -i 's/root::/root:*:/' /sysroot/etc/shadow
mkdir -p /sysroot/root/.ssh
#chmod 700 /sysroot/root/.ssh
cat /ssh/*.rootpubkey > /sysroot/root/.ssh/authorized_keys
cat /ssh/*pubkey > /sysroot/root/.ssh/authorized_keys
#chmod 600 /sysroot/root/.ssh/authorized_keys
mkdir -p /sysroot/etc/ssh/
for i in /ssh/*.ca; do
@@ -1,7 +1,7 @@
#!/bin/bash
echo "Installing certificates"
echo '<authorized_keys xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns" config:type="list">' > /tmp/rootkeys.xml
for pub in /ssh/*.rootpubkey; do
for pub in /ssh/*pubkey; do
echo '<listentry>'$(cat $pub)'</listentry>' >> /tmp/rootkeys.xml
done
echo '</authorized_keys>' >> /tmp/rootkeys.xml
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -0,0 +1,25 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -39,6 +39,7 @@ dynamic behavior and replace with static configuration.
<pattern>base</pattern>
</patterns>
<packages config:type="list">
<package>python3</package>
<package>openssl</package>
<package>chrony</package>
<package>rsync</package>
@@ -102,10 +103,10 @@ dynamic behavior and replace with static configuration.
<source>
<![CDATA[
#!/bin/sh
mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl $proto://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/pre.sh
curl $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/pre.sh
. /tmp/pre.sh
]]>
</source>
@@ -118,13 +119,13 @@ curl $proto://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/pre.sh
<source>
<![CDATA[
#!/bin/sh
mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl $proto://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/prechroot.sh
curl $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/prechroot.sh
. /tmp/prechroot.sh
curl -f $proto://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/etc/confluent/firstboot.sh
curl -f $proto://$mgr/confluent-public/os/$profile/scripts/post.sh > /mnt/etc/confluent/post.sh
curl -f $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /mnt/etc/confluent/firstboot.sh
curl -f $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /mnt/etc/confluent/post.sh
chmod +x /mnt/etc/confluent/firstboot.sh
chmod +x /mnt/etc/confluent/post.sh
cp /mnt/etc/confluent/post.sh /mnt/var/adm/autoinstall/scripts/
@@ -1,13 +1,27 @@
#!/bin/sh
#!/bin/bash
# This script runs at the end of the final boot, updating status
exec >> /var/log/confluent/confluent-firstboot.log
exec 2>> /var/log/confluent/confluent-firstboot.log
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
. /etc/confluent/functions
for i in /etc/ssh/ssh_host*key.pub; do
certname=${i/.pub/-cert.pub}
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$i https://$confluent_mgr/confluent-api/self/sshcert > $certname
done
systemctl restart sshd
run_remote firstboot.custom
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -f -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
run_remote_parts firstboot.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/firstboot.d/
run_remote_config firstboot.d
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -f -X POST -d "status: complete" https://$confluent_mgr/confluent-api/self/updatestatus
@@ -1,12 +1,23 @@
function set_confluent_vars() {
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
run_remote() {
set_confluent_vars
requestedcmd="'$*'"
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -18,14 +29,41 @@ run_remote() {
return $retcode
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/bin/python3 $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/bin/python3 $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
unset confluentscripttmpdir
}
run_remote_python() {
echo
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
python3 $*
retcode=$?
@@ -33,3 +71,20 @@ run_remote_python() {
cd - > /dev/null
return $retcode
}
run_remote_config() {
echo
set_confluent_vars
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
echo '---------------------------------------------------------------------------'
echo Requesting to run remote configuration for "'$*'" from $confluent_mgr under profile $confluent_profile
python3 $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
python3 $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# This script runs at the end of install in the installed system
# but still under the installer kernel.
@@ -9,19 +9,31 @@
# If there are dependencies on the kernel (drivers or special filesystems)
# then firstboot.sh would be the script to customize.
mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
chmod 700 /etc/confluent
chmod og-rwx /etc/confluent/*
export mgr profile nodename
export confluent_mgr confluent_profile nodename
. /etc/confluent/functions
# This will induce server side processing of the syncfile contents if
# present
run_remote_python syncfileclient
run_remote post.custom
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh
run_remote_parts post.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
run_remote_config post.d
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
@@ -1,8 +1,10 @@
#!/bin/sh
#!/bin/bash
# This script runs before the installer executes, and sets up ssh during install as well
# as rewriting the autoyast file with any substitutions prior to it being evaluated for real
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
rootpw=$(grep rootpassword: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
if [ "$rootpw" = "null" ]; then
@@ -16,16 +18,17 @@ if [ "$cryptboot" != "" ] && [ "$cryptboot" != "none" ] && [ "$cryptboot" != "n
fi
mkdir ~/.ssh
cat /ssh/*.rootpubkey > ~/.ssh/authorized_keys
cat /ssh/*pubkey > ~/.ssh/authorized_keys 2>/dev/null
ssh-keygen -A
for i in /etc/ssh/ssh_host*key.pub; do
certname=${i/.pub/-cert.pub}
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$i https://$mgr/confluent-api/self/sshcert > $certname
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$i https://$confluent_mgr/confluent-api/self/sshcert > $certname
echo HostKey ${i%.pub} >> /etc/ssh/sshd_config
echo HostCertificate $certname >> /etc/ssh/sshd_config
done
/usr/sbin/sshd
curl -f https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
ntpcfg=""
if grep ^ntpservers: /etc/confluent/confluent.deploycfg > /dev/null; then
@@ -4,10 +4,10 @@
# is still running, with the to-be-booted system mounted in /mnt
# carry over deployment configuration and api key for OS install action
mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
export mgr profile nodename
export confluent_mgr confluent_profile nodename
mkdir -p /mnt/etc/confluent
chmod 700 /mnt/etc/confluent
cp /tmp/functions /mnt/etc/confluent/
@@ -17,5 +17,16 @@ cp -a /tls /mnt/etc/confluent/
cp -a /tls/* /mnt/var/lib/ca-certificates/openssl
cp -a /tls/* /mnt/var/lib/ca-certificates/pem
cp -a /tls/*.pem /mnt/etc/pki/trust/anchors
cat /tls/*.pem > /mnt/etc/confluent/ca.pem
cp /opt/confluent/bin/apiclient /mnt/etc/confluent
run_remote setupssh.sh
echo Port 22 >> /etc/ssh/sshd_config
echo Port 2222 >> /etc/ssh/sshd_config
echo Match LocalPort 22 >> /etc/ssh/sshd_config
echo " ChrootDirectory /mnt" >> /etc/ssh/sshd_config
kill -HUP $(cat /run/sshd.pid)
mkdir -p /mnt/var/log/confluent
cp /tmp/confluent*log /mnt/var/log/confluent
@@ -28,7 +28,7 @@ echo /usr/lib/ssh/ssh-keysign root:root 4711 >> /mnt/etc/permissions.local
chmod 4711 /mnt/usr/lib/ssh/ssh-keysign
# Download list of nodes from confluent, and put it into shosts.equiv (for most users) and .shosts (for root)
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/nodelist > /tmp/allnodes
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /tmp/allnodes
cp /tmp/allnodes /mnt/root/.shosts
cp /tmp/allnodes /mnt/etc/ssh/shosts.equiv
@@ -0,0 +1,219 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
import shutil
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
except FileNotFoundError:
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger:
def __init__(self):
self.byip = {}
self.byname = {}
self.sourcelines = []
self.targlines = []
def read_source(self, sourcefile):
with open(sourcefile, 'r') as hfile:
self.sourcelines = hfile.read().split('\n')
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
for x in range(len(self.sourcelines)):
line = self.sourcelines[x]
currip, names, comment = partitionhostsline(line)
if currip:
self.byip[currip] = x
for name in names:
self.byname[name] = x
def read_target(self, targetfile):
with open(targetfile, 'r') as hfile:
lines = hfile.read().split('\n')
if not lines[-1]:
lines = lines[:-1]
for y in range(len(lines)):
line = lines[y]
currip, names, comment = partitionhostsline(line)
if currip in self.byip:
x = self.byip[currip]
if self.sourcelines[x] is None:
# have already consumed this enntry
continue
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
continue
for name in names:
if name in self.byname:
x = self.byname[name]
if self.sourcelines[x] is None:
break
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
break
else:
self.targlines.append(line)
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
for line in self.sourcelines:
if line is not None:
hosts.write(line + '\n')
class CredMerger:
def __init__(self):
try:
with open('/etc/login.defs', 'r') as ldefs:
defs = ldefs.read().split('\n')
except FileNotFoundError:
defs = []
lkup = {}
self.discardnames = {}
self.shadowednames = {}
for line in defs:
try:
line = line[:line.index('#')]
except ValueError:
pass
keyval = line.split()
if len(keyval) < 2:
continue
lkup[keyval[0]] = keyval[1]
self.uidmin = int(lkup.get('UID_MIN', 1000))
self.uidmax = int(lkup.get('UID_MAX', 60000))
self.gidmin = int(lkup.get('GID_MIN', 1000))
self.gidmax = int(lkup.get('GID_MAX', 60000))
self.shadowlines = None
def read_passwd(self, source, targfile=False):
self.read_generic(source, self.uidmin, self.uidmax, targfile)
def read_group(self, source, targfile=False):
self.read_generic(source, self.gidmin, self.gidmax, targfile)
def read_generic(self, source, minid, maxid, targfile):
if targfile:
self.targdata = []
else:
self.sourcedata = []
with open(source, 'r') as inputfile:
for line in inputfile.read().split('\n'):
try:
name, _, uid, _ = line.split(':', 3)
uid = int(uid)
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
self.shadowlines = []
try:
with open(source, 'r') as inshadow:
for line in inshadow.read().split('\n'):
try:
name, _ = line.split(':' , 1)
except ValueError:
continue
if name in self.discardnames:
continue
self.shadowednames[name] = 1
self.shadowlines.append(line)
except FileNotFoundError:
return
def write_out(self, outfile):
with open(outfile, 'w') as targ:
for line in self.targdata:
targ.write(line + '\n')
for line in self.sourcedata:
targ.write(line + '\n')
if outfile == '/etc/passwd':
if self.shadowlines is None:
self.read_shadow('/etc/shadow')
with open('/etc/shadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
if outfile == '/etc/group':
if self.shadowlines is None:
self.read_shadow('/etc/gshadow')
with open('/etc/gshadow', 'w') as shadout:
for line in self.shadowlines:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':' , 1)
if name in self.shadowednames:
continue
shadout.write(name + ':!::\n')
def synchronize():
tmpdir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
cm.read_passwd(pendpasswd, targfile=False)
cm.read_passwd('/etc/passwd', targfile=True)
cm.write_out('/etc/passwd')
pendgroup = os.path.join(tmpdir, 'etc/group')
if os.path.exists(pendgroup):
cm = CredMerger()
cm.read_group(pendgroup, targfile=False)
cm.read_group('/etc/group', targfile=True)
cm.write_out('/etc/group')
pendhosts = os.path.join(tmpdir, 'etc/hosts')
if os.path.exists(pendhosts):
cm = HostMerger()
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
finally:
shutil.rmtree(tmpdir)
if __name__ == '__main__':
synchronize()
@@ -1,7 +1,7 @@
#!/bin/bash
deploycfg=/custom-installation/confluent/confluent.deploycfg
mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
profile=$(grep ^profile: $deploycfg|awk '{print $2}')
export deploycfg mgr profile
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/post.sh
confluent_mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: $deploycfg|awk '{print $2}')
export deploycfg confluent_mgr confluent_profile
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /tmp/post.sh
. /tmp/post.sh
@@ -1,6 +1,6 @@
#!/bin/bash
deploycfg=/custom-installation/confluent/confluent.deploycfg
mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
profile=$(grep ^profile: $deploycfg|awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/pre.sh
confluent_mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: $deploycfg|awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/pre.sh
. /tmp/pre.sh
@@ -7,8 +7,12 @@ if [ ! -z "$rootpw" -a "$rootpw" != "null" ]; then
echo root:$rootpw | chpasswd -e
fi
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg |awk '{print $2}')
hostnamectl set-hostname $(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
touch /etc/cloud/cloud-init.disabled
curl --capath /etc/confluent/tls -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
source /etc/confluent/functions
run_remote_parts firstboot.d
run_remote_config firstboot.d
curl --capath /etc/confluent/tls -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -X POST -d "status: complete" https://$confluent_mgr/confluent-api/self/updatestatus

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