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

Compare commits

..

349 Commits

Author SHA1 Message Date
Jarrod Johnson 6f194f26c0 Fix contents and permissions
NetworkManager demands specific
permissions
2022-02-25 16:18:54 -05:00
Jarrod Johnson 71c60be659 Fix el8 dns configuration
The modification to add dns search must only be suggested
if the respective ip version section is enabled.
2022-02-25 15:22:45 -05:00
Jarrod Johnson 58a9aa03ef Add DNS domain to el8 network manager 2022-02-25 09:48:56 -05:00
Jarrod Johnson 19a370b0f5 Add explicit client version dependency 2022-02-25 07:31:12 -05:00
Jarrod Johnson 47a517aec1 Decrease retries to do https retries with bad TLS cert 2022-02-24 16:37:48 -05:00
Jarrod Johnson 1f7bd1a28a Fix autoconsole output on diskless 2022-02-24 16:27:32 -05:00
Jarrod Johnson 89cc49c4fc Add loginname to nodeshell man page 2022-02-24 16:08:50 -05:00
Jarrod Johnson 50da83b4f5 Fix api token message not being pushed 2022-02-24 15:56:29 -05:00
Jarrod Johnson 15f4cc085d Aggressively flush out error output 2022-02-24 15:46:38 -05:00
Jarrod Johnson d7df1e7891 Prevent users from dupe group memberships 2022-02-24 15:06:41 -05:00
Jarrod Johnson 1a5f5aea3a Try an alternative approach to autoconsole errors 2022-02-24 12:18:41 -05:00
Jarrod Johnson 7068287ba3 Fix autocons spurious output 2022-02-24 10:25:59 -05:00
Jarrod Johnson fb1f6b70bb Improve error handling on bad TLS cert
Bad TLS cert is a common problem, provide better feedback.
2022-02-24 09:27:40 -05:00
Jarrod Johnson 2c9be7a4c4 Remove slp snoop of XCC
SSDP snoop catches XCC, and do only
SSDP for consistent format of
snoop info coming into the
xcc handler.
2022-02-24 08:08:50 -05:00
Jarrod Johnson 24ef12e029 Disable autoconf of ipv6 in el
If autoconf is allowed when link is brought up, it scan
confuse redhat network configuration when it already finds
an ipv6 address.
2022-02-23 16:58:29 -05:00
Jarrod Johnson e390618dd9 Fix handling without olduuid in database 2022-02-23 10:13:06 -05:00
Jarrod Johnson 8f4846c248 Fix for partial returns
full_net_config may not always apply,
be sure to gracefully degrade.
2022-02-22 17:08:23 -05:00
Jarrod Johnson ac8918c2b9 Add ips to ssh principals
For any static address, also grant
certificate for that.
2022-02-22 16:48:58 -05:00
Jarrod Johnson fdc9d94408 Also register to run before coreos-ignition-setup-user
For coreos, make sure we preempt either name.
2022-02-22 14:30:48 -05:00
Jarrod Johnson 3cf9edeeb8 Stub out buffering for shell sessions
This is not yet handled anyway.

For future, establish norm of a nodeid
to prefix multiple distinct sessions.
2022-02-22 08:49:31 -05:00
Jarrod Johnson 8fab8238ed Disambiguate console from shell buffer
There is room for the console replay to get confused,
fix by fully qualifying the console name.
2022-02-18 17:31:13 -05:00
Jarrod Johnson 80293efe57 Address coverity false-positives
Technically, the fread won't reach the
length index, but change the order
anyway to reassure coverity.
2022-02-17 17:09:21 -05:00
Jarrod Johnson b463a53146 Cleanup per coverity
Fix a number of concerns that coverity reports
2022-02-17 17:05:00 -05:00
Jarrod Johnson 58b55b6ef6 Error on trying to double-add nodes or groups
Prevent user from repeatedly adding the
same group to a node or same node to a group.
2022-02-16 11:58:22 -05:00
Jarrod Johnson 33be75a9a2 Markup bandit exceptions
Apply bandit exceptions and explain
the rationale in each case
2022-02-16 09:10:33 -05:00
Jarrod Johnson f10a27fd7a Switch to mkstemp
Use mkstemp to more confidently reserve a filename as expected.
2022-02-15 17:13:04 -05:00
Jarrod Johnson 3f53cb939a Add mkdir -p to build script for genesis 2022-02-15 10:32:39 -05:00
Jarrod Johnson 961398e34e Bump version to cover 8.6 update 2022-02-15 10:29:18 -05:00
Jarrod Johnson 13b3b081cf Update to new rdma version in el8.6 2022-02-15 10:28:19 -05:00
Jarrod Johnson 3e6b67f9a7 Error if user omits attributes for options
If user indicates manipulation of
attributes through -c, -p, or -e,
but none are specified, print an error
instead.
2022-02-14 09:24:15 -05:00
Jarrod Johnson 93a5496899 Reject reverse range noderange
It has been stated that no one would want to do this
on purpose, and thus it should reject. The rationale
being that if no one wants to do this, but did it
anyway, they presumably made a mistake.

So now such attempts will be blocked.

Keep the dead logic around for now
in case a future opinion changes things back.
2022-02-11 15:20:41 -05:00
Jarrod Johnson bd428790ce Try for more informative messoge an expression syntax error 2022-02-11 14:51:53 -05:00
Jarrod Johnson f0c4943612 Merge branch 'master' of github.com:lenovo/confluent 2022-02-11 14:33:47 -05:00
Jarrod Johnson f547071d38 Warn user of unworkable syntax
When used in {} expressions, attributes must obey python syntax rules,
try out the attribute name and report the issue when it would be a problem.
2022-02-11 14:32:52 -05:00
Jarrod Johnson fbd3a442ac Support numeric owner/group in syncfile list 2022-02-08 17:38:01 -05:00
Jarrod Johnson b809514ef9 Fix osdeploy initialize dependency on master key
Make sure confluent has made /etc/confluent, and further always initialize the
encryption key, as it will almost certainly
be needed and easiest to just always
generate on first startup.
2022-02-08 16:40:41 -05:00
Jarrod Johnson b1032d8c4c Specify write mode for confluent_uuid file 2022-02-08 12:31:04 -05:00
Jarrod Johnson 00bedf6946 Shuffle confluenntuuid to earlier in copernicus
Currently, ssdp handler behavior needs confluentuuid first, if
it is to have any effect.
2022-02-08 12:06:52 -05:00
Jarrod Johnson e7b1791df3 Modify input in bandit-friendly way
bandit erroneously flags 'input' based
on possible python2-ism.  Avoid the
error by using 'getinput', making that
input or raw_input based on the python version.
2022-02-08 10:59:15 -05:00
Jarrod Johnson 6e03f6ee0a Correct syntax typo 2022-02-08 10:49:42 -05:00
Jarrod Johnson 358b719cec Implement deployment binding for new installs
When doing osdeploy initialize,
save the uuid and have deployment
targets specifically pair back with site via
uuid.
2022-02-08 10:41:27 -05:00
Jarrod Johnson b0b8ee128b Correct imgutil typo 2022-02-08 09:35:10 -05:00
Jarrod Johnson 094c97696f Merge tag '3.3.5'
Fixes for esxi7 and el7
2022-02-07 13:07:36 -05:00
Jarrod Johnson c2b66958d7 Update imgutil to not need rpm-python
EL7 does not have rpm for python3, use the cli instead for
such a case.
2022-02-07 12:23:44 -05:00
Jarrod Johnson 4a38a88136 Add recognition of RHEL9 media 2022-02-07 09:20:05 -05:00
Jarrod Johnson b1cbca414a Fix compatibility with CentOS7 for imgutil build -s 2022-02-07 08:39:45 -05:00
Jarrod Johnson 522d7e2b59 Fix configmanager issue witht more recent python3 2022-02-04 16:21:39 -05:00
Jarrod Johnson 7c169f635a Actually correct deb builder for vtbufferd 2022-02-04 16:06:12 -05:00
Jarrod Johnson 82f85b8aef Fix deb build of vtbuffered 2022-02-04 16:04:27 -05:00
Jarrod Johnson 97476f51da Remove some el7-irrelevant ramfs content 2022-02-03 12:26:09 -05:00
Jarrod Johnson 6769c171c1 Fix confignet for python2 2022-02-03 11:58:44 -05:00
Jarrod Johnson 638a1b3587 Fix confignet for python2 2022-02-03 11:50:34 -05:00
Jarrod Johnson 5f4a565feb Use python2-compatible apiclient load for el7 diskless 2022-02-03 10:10:23 -05:00
Jarrod Johnson 700fe1bc3b Fix for EL7 compatibility
Some python 3 exclusive assumptions were made.
Unfortunately, EL7 is python2
centric.
2022-02-03 10:10:10 -05:00
Jarrod Johnson e94d0a5236 Fix for EL7 compatibility
Some python 3 exclusive assumptions were made.
Unfortunately, EL7 is python2
centric.
2022-02-03 10:09:28 -05:00
Jarrod Johnson 4665fb96a2 Fix relative hostpath for mismatched build platform
Relative path was erroneously
resulting in the host system
being consulted rather than the target.
2022-02-03 09:07:02 -05:00
Jarrod Johnson 33761e8870 Exlicitly add AES and DRBG for EL7 2022-02-02 16:54:41 -05:00
Jarrod Johnson 0bca6e0852 Apply more fixes for EL7 diskless 2022-02-02 15:22:58 -05:00
Jarrod Johnson c6816099f7 Add el7 diskless draft to packaging 2022-02-02 13:23:34 -05:00
Jarrod Johnson 71bcdc2e4e Continuation of work on draft el7 diskless 2022-02-02 13:11:38 -05:00
Jarrod Johnson eaa0921420 Draft EL7 diskless support 2022-02-02 13:09:26 -05:00
Jarrod Johnson 554e25d6cb Assume libcrypt.so.1 if not 2 in more generic way 2022-02-02 11:17:08 -05:00
Jarrod Johnson bc7d480a17 Use more widely known paths for libcrypt 2022-02-02 11:03:44 -05:00
Jarrod Johnson 92dea32dd4 Change to python 2/3 agnostic syntax 2022-02-02 11:03:12 -05:00
Jarrod Johnson f346cae683 Use more widely known paths for libcrypt 2022-02-02 10:53:46 -05:00
Jarrod Johnson 1431f9ce13 Change to python 2/3 agnostic syntax 2022-02-02 08:37:22 -05:00
Jarrod Johnson 485c323608 Stage uploads in memory
The strategy of duping file descriptors
is inadequate. The copies share
identical offsets.

Fix this by reading the file once into
memory, and using BytesIO to fake a file.

This is relatively memory intensive in theory, but in practice
pyghmi library had been duping everything to memory
anyway, so it is a wash for now.
2022-01-27 17:29:17 -05:00
Jarrod Johnson 2bc080f4e0 Improve noderun message when command not found
Rather than raise an ominous looking trace, simply
state that thte command was not found and continue.
2022-01-26 11:35:43 -05:00
Jarrod Johnson effaba9661 Fix adding nodes with uuid for pxe
When adding nodes, they wouldn't be tracked for PXE
until id.uuid changed, or service restarted.

Fix by explicitly remapping all added and new names.
2022-01-26 08:35:10 -05:00
Jarrod Johnson fd00a70075 Merge branch 'master' of github.com:lenovo/confluent 2022-01-25 11:21:16 -05:00
Jarrod Johnson daa6b8208c Handle update being cancelled server side
The result may not be a nicely formed result, handle less
formed results.
2022-01-25 11:09:49 -05:00
Jarrod Johnson 9f071c1fdb Provide more details in example syncfiles 2022-01-20 14:10:30 -05:00
Jarrod Johnson 3f90c35bc9 Raise error on missing piece of syncfiles entry 2022-01-20 13:13:04 -05:00
Jarrod Johnson 456b43eeb7 Correctly align rtattr and nlmsg
When advancing through messages,
must pad to nearest multiple of
4.  This resolves erroneously landing on incorrect offsets.
2022-01-19 13:21:59 -05:00
Jarrod Johnson 2d13921d54 Amend deployment initialize for consistent ownership
Depending on the options selected/not selected, the
/var/lib/confluent directory may have been initialized
incorrectly.  Have all the potential paths begin with
ensuring /var/lib/confluent is correct, and then
use seteuid consistently to take care of the rest.
2022-01-14 15:08:19 -05:00
Jarrod Johnson 8b95e8f507 Improve osdeploy import error handling
Before, conditions that were unexpected
would result in hangs.  Now
transition to and report error state.
2022-01-14 09:23:54 -05:00
Jarrod Johnson fdc3d1c457 Broaden scope of logging issues with connecting to a leader.
Notably, certificate mismatch should be caught.
2022-01-14 07:39:25 -05:00
Jarrod Johnson 8d5d8ef5ed Have later syncfiles entries supersede earlier
This allows a vague entry to
be followed by a specific
entry to allow 'common' content
and then a specific override.
2022-01-13 16:52:10 -05:00
Jarrod Johnson aa816c0f1d Have attribute update support wildcards
This makes nodeattrib behave more like nodeconfig.
2022-01-13 13:27:07 -05:00
Jarrod Johnson b7a786dc83 Choose more accurate description of ambiguous switch situation
Technically, it need not be an aggregation, but link between switches generically covers it.
2022-01-12 09:31:44 -05:00
Jarrod Johnson f46fc9f7de Provide error about in-use dhcp
The events will more clearly indicate
when dnsmasq is not adequately configured.
2022-01-12 09:27:49 -05:00
Jarrod Johnson 2d95fb1d74 Improve 'ambiguous' notification in macmap.
When two nodes are ambiguous, provide more
info (mac) and also a suggestion of
which of the two appears most
trunk-like if one of them looks
suspiciously like a trunk.
2022-01-12 08:31:13 -05:00
Jarrod Johnson 04b462712d Tolerate redfish variations and fix loop
Some redfish omit the trailing /, and the correct move on non-redfish
is to continue to next candidate,
not break.
2022-01-11 16:53:46 -05:00
Jarrod Johnson 9c40569429 Add el9 to spec files 2022-01-11 16:02:47 -05:00
Jarrod Johnson 89187ef97d Merge branch '3.3' 2022-01-11 09:57:27 -05:00
Jarrod Johnson 0186f002e0 Have configbmc stall on TSM
TSM will stall out for an extended
period of time after a network change.

Accomodate by forcing a pause
on detection of TSM.
2022-01-10 12:02:46 -05:00
Jarrod Johnson 8d80efec64 Correct configbmc misidentification of lan chan
The lan channel get mac address was incorrect, change
to correctly request.
2022-01-10 12:02:14 -05:00
Jarrod Johnson dbaae417e9 Have configbmc stall on TSM
TSM will stall out for an extended
period of time after a network change.

Accomodate by forcing a pause
on detection of TSM.
2022-01-10 12:01:15 -05:00
Jarrod Johnson 181f704331 Correct configbmc misidentification of lan chan
The lan channel get mac address was incorrect, change
to correctly request.
2022-01-10 09:29:40 -05:00
Jarrod Johnson 6df73d88b3 If SSDP happens but HTTPS not ready, ignore 2022-01-07 10:45:10 -05:00
Jarrod Johnson d5a8e881ab Defer handler processing
This was done in slp, do it also for ssdp. This
hopefully does it's best to flush udp buffer more
quickly in at scale scenarios.
2022-01-07 09:55:49 -05:00
Jarrod Johnson c475e4801f Fix incorrect quotes in autocons.c 2022-01-07 09:28:24 -05:00
Jarrod Johnson e54277f8f8 Fix ssdp snoop of XCC
SSDP was declaring victory too early, wait until
the right volume of information is confirmed available
before commiting to shared structures.
2022-01-07 09:00:44 -05:00
Jarrod Johnson d18d7592d3 Suppress extraneous slp trace errors
If a half-behaving peer appears, suppress the error and permit a retry later.
2022-01-07 07:17:27 -05:00
Jarrod Johnson 60398bf8f7 Add passive detection of XCC via SSDP
Take ssdp alive packets to pick up
XCCs outside of active scans.
2022-01-06 16:48:39 -05:00
Jarrod Johnson 22024b23b8 Prefer shortname for collective
When a colleective begins, default
to using the shortname, even
if gethostname returns a long one.
2022-01-06 12:38:15 -05:00
Jarrod Johnson 8da513b152 Fix TSM to lower case uuid for discovery
TSM were inconsistent by presenting
all caps UUID sometimes.
2022-01-05 12:50:31 -05:00
Jarrod Johnson 6f272b9d50 Skip generic redfish check
For now, it won't do anything but waste
resources.
2022-01-05 12:35:42 -05:00
Jarrod Johnson b3c0810574 Fix XCC preconfig with SSDP change
The SSDP unfortunately changes
some structure, change XCC
preconfig to expect the SSDP structure.
2022-01-05 12:10:34 -05:00
Jarrod Johnson b90756d2a6 Move XCC detection to SSDP
XCC will be retiring SLP and to prepare
for that, move over to SSDP.

This required that the snoop be wired
up to detection correctly, that slp
no longer report XCC, that
the model/serial number be moved to xcc handler, and various fixes to SSDP.
2022-01-05 11:43:04 -05:00
Jarrod Johnson 978b52dbb6 Fix selection of suboptimal alias
When picking deploy server, stop on a close match if net.ipv4_address suggests
a same-subnet match rather than adding the last alias.
2021-12-28 09:47:47 -05:00
Jarrod Johnson d47b088753 Fix mistake in error output 2021-12-22 23:29:21 -05:00
Jarrod Johnson 47a83f4628 Do not trace out if a packet can't be replied to 2021-12-20 12:37:18 -05:00
Jarrod Johnson abec8c498c Break netlink address fetch on invalid rta_len
It is considered valid for kernel to return a null rta_len
in the midst of data and expect the caller to terminate.
2021-12-20 12:28:35 -05:00
Jarrod Johnson a8132d87a7 Blacklist the tianocore default uuid
If a platform doesn't have a uuid, Tiano hardbakes
yet another invalid uuid to ignore.
2021-12-17 09:13:49 -05:00
Jarrod Johnson 65d6ff13fa Ignore and warn on bad gateway
If user specifies a bad gateway,
spare the firmware from seeing
it and log that the gateway is a problem.
2021-12-17 08:44:46 -05:00
Jarrod Johnson 9b178c0653 Provide nicer error message on existing name
Packing ontop of an existing profile is not presently supported,
provide a clearer indication of this.
2021-12-15 13:26:32 -05:00
Jarrod Johnson 016ee3ecb0 Fall back if timedatectl doesn't work 2021-12-15 12:34:28 -05:00
Jarrod Johnson 48ea6225aa Remove stderr from subprocess error
it is not a supported keyword
2021-12-15 12:28:09 -05:00
Jarrod Johnson c88f34d1e1 Merge tag '3.2.3'
Provide more reasonable hostname on install
2021-12-14 11:27:25 -05:00
Jarrod Johnson e488560090 Induce probe by randomized port
Replying to SLP caused some SLP
software to have a bug.

Since we aren't looking for a reply,
instead just throw a byte randomly at a high numbered
port.
2021-12-14 07:49:09 -05:00
Jarrod Johnson faa20660ed Assure confluent myname file is owned correctly
A lot of osdeploy initialize (which runs as root)
may induce creation of 'myname' file.  Fix by
becoming confluent user, if applicable.
2021-12-14 07:24:47 -05:00
Jarrod Johnson ee950ca8a2 Skip directories in initramfs symlink
If part of a debug effort leaves stray directories,
ignore them as they can't possibly be initramfs content.
2021-12-13 15:43:12 -05:00
Jarrod Johnson 743f984b3d Fix raw integer value support 2021-12-09 14:08:24 -05:00
Jarrod Johnson ad33572290 Add debug information to coreos init 2021-12-09 11:55:12 -05:00
Jarrod Johnson 773be640f8 Fix for long hostname on CA node 2021-12-09 11:19:16 -05:00
Jarrod Johnson 624984b1c9 Do not assume SPCR until confluent confirms text console
TIOCCONS was called for users that did not want to use serial.  This
makes the serial console delayeed when automatic, but avoids video
users from being confused.
2021-12-09 10:34:12 -05:00
Jarrod Johnson c2de127947 Bring el8 completely over to prefer ipv4 2021-12-09 08:31:07 -05:00
Jarrod Johnson cf686f8018 Prefer IPv4 deployment over IPv6 when both are available
Some older vintages of RHEL and clones do not support IPv4,
best to prefer ipv4 to avoid messing up on places that have ipv6
configured.
2021-12-08 17:04:04 -05:00
Jarrod Johnson d258a2ae0d Fix py2 compatibility of util 2021-12-08 12:12:39 -05:00
Jarrod Johnson 68bfb950f3 Correect which subprocess module is used by util 2021-12-08 11:26:08 -05:00
Jarrod Johnson 343e5eabe5 Utility function for running commands
Given the python2/3 differences, good to have a single run
that returns stdout and stderr.

This should trigger the same behavior as timeout did, but
in a manner consistent between 2 and 3.
2021-12-08 10:20:34 -05:00
Jarrod Johnson 02da50af8b Tighter permissions on /var/log/confluent 2021-12-08 07:42:19 -05:00
Jarrod Johnson 77de3ab25b Apply more restrictive permissions to /var/log/confluent/
While confluent shouldn't put anything sensitive in the log, custom
content may. To mitigate the risk, it will now lock down the
log permissions.
2021-12-07 07:21:04 -05:00
Jarrod Johnson dcca844e9b Refresh eventlet resolver on resolv.conf change
Eventlet holds on to stale resolv.conf. As a workaround,
monitor resolv.conf and explicitly reinitialize greendns
resolver.
2021-12-06 12:22:42 -05:00
Jarrod Johnson 1b21b9316c Correct variable typo in imageboot scripts 2021-12-06 09:00:44 -05:00
Jarrod Johnson 4260db20b7 Make confluent contennt from initramfs avalibale to normal root system 2021-12-03 16:06:47 -05:00
Jarrod Johnson 496be816ff Add exit 0 to avoid failed systemd unit 2021-12-03 12:50:03 -05:00
Jarrod Johnson 4e0a76d955 Fix typo in confluent name in coreos script 2021-12-03 12:47:05 -05:00
Jarrod Johnson f2b2d8519c Add onboot link to coreos relabel activity 2021-12-03 12:45:42 -05:00
Jarrod Johnson d829db4407 Add onboot hook to coreos 2021-12-03 12:37:03 -05:00
Jarrod Johnson 0e1da6ebd6 Explicitly check for TPM 2.0 before trying to use TPM 2.0 2021-12-03 11:05:11 -05:00
Jarrod Johnson d2435d315b Schedule fetch of private data before ignition cares 2021-12-03 10:39:47 -05:00
Jarrod Johnson 4388399f78 Fetch config.ign file during rootfs setup in CoreOS 2021-12-03 09:34:46 -05:00
Jarrod Johnson a146a54172 Move ignition to profile private data, extracted 2021-12-03 08:48:27 -05:00
Jarrod Johnson 4482b675b4 Wait for network to get ssh certificates 2021-12-03 08:32:25 -05:00
Jarrod Johnson 2b8c8e9af9 Add support for specifying login name in nodeshell 2021-12-03 08:03:09 -05:00
Jarrod Johnson 714a883d9c Purge the conflucting default nmconnection if we have better 2021-12-02 17:00:10 -05:00
Jarrod Johnson 77865ba71c Disable fakecmdline for now 2021-12-02 16:40:41 -05:00
Jarrod Johnson 3adb82370f Specify correct filename for fakecmdline 2021-12-02 16:27:55 -05:00
Jarrod Johnson 7a95c3e64c Set selinux context of fakecmdline 2021-12-02 16:17:04 -05:00
Jarrod Johnson e3d5a058d6 Change nodestorage to interactive and alias it to be interactive by default 2021-12-02 14:52:16 -05:00
Jarrod Johnson 25b68665e0 Provide a bit more elaboration on some attribute descriptions. 2021-12-02 11:42:32 -05:00
Jarrod Johnson 42f766d5f4 Fix osdeploy initialize when only ssh or tls is done
ssh is technically opt-in, though strongly recommended, so
osdeploy should cope with that.

TLS is pretty much required, but perhaps not done immediately,
so it gets similar treatmennt.
2021-12-02 11:38:18 -05:00
Jarrod Johnson 0092915cab Fix rerunning tls initialization 2021-12-02 10:19:47 -05:00
Jarrod Johnson 8ecbcb0f00 Fix coreos tendency to disable ipv6 2021-12-01 16:26:13 -05:00
Jarrod Johnson e5a748f0a2 Fix coreos pre-pivot to work better 2021-12-01 16:00:36 -05:00
Jarrod Johnson 7f476d64b5 Update style of nodesetboot usage message 2021-12-01 15:33:21 -05:00
Jarrod Johnson 90f3d5f323 Fix syntax error in coreos pre-pivot 2021-11-30 09:14:51 -05:00
Jarrod Johnson 00e853130f Fallback to other location for grub 2021-11-29 16:03:06 -05:00
Jarrod Johnson a4b1f1d30a Fix typo in fedora coreos signature 2021-11-29 15:54:38 -05:00
Jarrod Johnson 5718e07238 Treat empty attributes as unset
This fixes behavior when, for example, crypted.rootpassword
is explicitly blanked
2021-11-29 11:22:48 -05:00
Jarrod Johnson d049934626 Add a breadcrum to distinfo.yaml to indicate where an image came from 2021-11-29 09:55:38 -05:00
Jarrod Johnson 425f5276b3 Merge pull request #82 from PenghuiCui/master
Support getting message extention in plugin
2021-11-29 07:17:36 -05:00
Penghui Cui 079a04b28b Support getting message extention in plugin 2021-11-25 10:01:35 +08:00
Jarrod Johnson 2e10289807 Invoke mkinitrd after zypper runs 2021-11-22 10:46:23 -05:00
Jarrod Johnson e1c15cca8e Switch to secureboot signed grub for ubuntu
It was pulling an efi binary incompatible with secureboot before.
2021-11-19 11:34:06 -05:00
Jarrod Johnson 6e4c939cd9 Various ubuntu diskless fixes/improvements
Needed the real losetup to handle read-only file.

busybox wc is way too slow, use ls instead.

Explicitly make /sysroot and load xfs, as that doesn't happen implicitly.

Accept a dangling symlink as viable init, since this is the case in Ubuntu.

Update hostname and ca certificates consistent with
ubuntu standards
2021-11-19 11:00:50 -05:00
Jarrod Johnson 82a783fa55 Register CA certs in global location for ubuntu 2021-11-18 16:51:18 -05:00
Jarrod Johnson 6d623b046a Fixes for imageboot.sh with ubuntu 2021-11-18 16:34:27 -05:00
Jarrod Johnson 398563df55 Add dirname to the ubuntu diskless 2021-11-18 16:14:44 -05:00
Jarrod Johnson e653c5dff6 Switch to bash for running functions
functions is a bashified facility. Just use bash for it rather
than making it busybox friendly.
2021-11-18 14:56:38 -05:00
Jarrod Johnson 1b7d63da5d Fix wrong sense of apikey 2021-11-18 13:12:56 -05:00
Jarrod Johnson c02651b2de Switch from fork to source for premount 2021-11-18 12:56:53 -05:00
Jarrod Johnson 4316b8690e Make oscategory consistent for ubuntu 2021-11-18 12:38:11 -05:00
Jarrod Johnson 045caadff9 Add ubuntu scheme to boot assets in imgutil
This brings us to the redhat, suse, and ubuntu schemes being understood.
2021-11-18 12:12:55 -05:00
Jarrod Johnson 095339ed90 Have apt-get not prompt to install 2021-11-18 11:43:25 -05:00
Jarrod Johnson 5769cba3d1 Provide functions and update imageboot for ubuntu diskless 2021-11-18 11:25:51 -05:00
Jarrod Johnson 75c5151567 Add hook to hook the premount script 2021-11-18 10:42:22 -05:00
Jarrod Johnson 4a724f28bd Package imgutil ubuntu content 2021-11-18 10:12:34 -05:00
Jarrod Johnson ecb9a6633e Ignore common malformed dhcpv6 request 2021-11-18 09:16:57 -05:00
Jarrod Johnson bf4842baa9 Provide a starter profile for ubuntu diskless 2021-11-17 16:18:06 -05:00
Jarrod Johnson 172515a6e8 Add packaging of ubuntu20.04-diskless 2021-11-17 16:05:00 -05:00
Jarrod Johnson dde9eebf92 Further advance ubuntu diskless implementation 2021-11-17 15:57:21 -05:00
Jarrod Johnson 6b8dec7764 Fix license save through collective 2021-11-17 15:56:36 -05:00
Jarrod Johnson 9e47c1cfed Add mktemp to the confluent hook 2021-11-17 11:15:23 -05:00
Jarrod Johnson 4a13711972 Fix discovery of pre-nonce firmware 2021-11-17 09:39:34 -05:00
Jarrod Johnson 44cc580364 Correct handling of blank value in attribute 2021-11-17 09:24:26 -05:00
Jarrod Johnson cc1b887c31 Allow blanking to disable a crypted password 2021-11-17 09:00:58 -05:00
Jarrod Johnson ef324daf52 Advance ubuntu diskless
Add tpm2 and fuse dependecies that were missing.

Begin draft work on the init-premount script.
2021-11-16 17:03:48 -05:00
Jarrod Johnson cd1dff7c42 Add tpm2-tools to pkglist and switch to apt
The debootstrap was inadequate to
cover tpm2-tools, switch to apt.
2021-11-16 13:15:18 -05:00
Jarrod Johnson 10c019df2a Fix ordering of chmod arguments 2021-11-16 11:42:46 -05:00
Jarrod Johnson bd9c261534 Implement initramfs customization for debian build
The initramfstools are extended
to naturally create the materials needed to do diskless.
2021-11-16 11:33:58 -05:00
Jarrod Johnson c11da229b0 Amend draft ubuntu diskless
Have imgutil build work to call
debootstrap with a pkglist that appears to succeed.
2021-11-16 07:44:41 -05:00
Jarrod Johnson 2ef2e4d039 Fix gathered output with non-rectangular geometry
If we don't have a nice rectangular count of systems,
the last row must tolerate index error in column.
2021-11-16 06:37:59 -05:00
Jarrod Johnson 8ee3630aa1 Simplify get_nonce
Use the helper function, for example
to cope with the prospect of gzip response.
2021-11-15 16:09:17 -05:00
Jarrod Johnson 01a21ec248 Correct syntax error 2021-11-15 11:43:10 -05:00
Jarrod Johnson 218b914754 Fix relative pkglist includes 2021-11-15 11:42:15 -05:00
Jarrod Johnson d59ac74f3a Support relative path on < include
For pkglist and syncfile, detect
relative path and interpret relative
to the file that does the include.
2021-11-15 11:34:34 -05:00
Jarrod Johnson 7b23ee9c7c Organize squeeze vertically
The web gui and ls on cli use organize output this way,
so squeezed node output should be same.
2021-11-15 10:25:00 -05:00
Jarrod Johnson 55fc3bda51 Fix nodersync with output redirection
Variables were left unitialized, use them only
in the tty path.
2021-11-15 09:20:31 -05:00
Jarrod Johnson d5e4d1b90a Add navigating nonce login for xcc discovery
The nonce login from newer firmware is now required.
2021-11-12 11:27:58 -05:00
Jarrod Johnson cde45bb170 Commence draaft work on ubuntu diskless
Provide a initramfs hook (akin to dracut module) and
a potential pkglist to cover what debootstrap does not
provide by default.
2021-11-11 16:41:13 -05:00
Jarrod Johnson 524f0d460f Have imgutil exec manifest / mount
When execing, make the environment see
the '/' normally in mount/df contexts.
2021-11-11 12:05:53 -05:00
Jarrod Johnson 2b6418bc33 Script to walk identify light along a noderange 2021-11-10 15:19:37 -05:00
Jarrod Johnson 9a6d422fd8 Fix the python3 of rsp read 2021-11-09 16:01:45 -05:00
Jarrod Johnson ef10237cfe Update smm rules to relax 2021-11-09 15:58:17 -05:00
Jarrod Johnson 94761ba7c6 Refresh SMM fix expiration sample 2021-11-09 15:54:27 -05:00
Jarrod Johnson b59c88beed Explain more about need for -c versus =''
The concept of inherited and blank override is confusing
and warrants more documentation.
2021-11-09 08:26:35 -05:00
Jarrod Johnson cbed20bb7f Fix suse15 firstboot.sh
It was stripping the wrong value from the path.
2021-11-09 07:10:38 -05:00
Jarrod Johnson 61eecd672c Add confignet to default firstboot/onboot as appropriate for el8 and
suse15
2021-11-08 16:34:26 -05:00
Jarrod Johnson 8df1c5b8e4 Make confignet available as a 'common' script 2021-11-08 16:23:56 -05:00
Jarrod Johnson 766230bcd3 Fix loop extending pkgs
The attempt at include support modified incorrect
list.
2021-11-06 14:38:44 -04:00
Jarrod Johnson f1bc82cd08 Fix logic of websocket handling
Do not fall through to non-websocket path after handling the websocket
2021-11-05 14:15:39 -04:00
Jarrod Johnson b139f9cd2c Consistently return string from consume_termdata
If None is allowed to return, it can mess up caller.
2021-11-05 11:30:43 -04:00
Jarrod Johnson a23d5b9b55 Provide both v4 and v6 in deploycfg
When implicitly working by lookup, both v4 and v6
were provided, but first match only
on explicit specification.

Correct this discrepency.
2021-11-04 17:20:49 -04:00
Jarrod Johnson df7d34dc28 Add attribute support for confluent2hosts
Implement something akin to makehosts
from xCAT.
2021-11-04 14:44:06 -04:00
Jarrod Johnson e749f8f8d5 Prioritize columns over rows
Do not make columns when the noderange is too small
to warrant them
2021-11-04 12:44:47 -04:00
Jarrod Johnson 5ceaa0e4fe Change logreader to python3 2021-11-04 10:53:50 -04:00
Jarrod Johnson 5270f604eb Fix missing el9 from spec install section 2021-11-04 08:00:37 -04:00
Jarrod Johnson ce1d89c741 Set nodename as hostname prior to post for el class distros 2021-11-04 07:56:04 -04:00
Jarrod Johnson 4e9020c3a7 Add SUSE support for confignet 2021-11-02 14:04:11 -04:00
Jarrod Johnson 366a2feaaf Provide api for node to just fetch all it's attributes 2021-11-02 11:21:19 -04:00
Jarrod Johnson db7cb6450b Temporarily stop firewalld and add pause to try to wait for address assignment 2021-11-02 09:43:42 -04:00
Jarrod Johnson ea310e472a Improve preservation of existing connections/settings
When teaming up nics, salvage
things like dns settings from members.

When a connection already seems to exist, modify it rather than delete and replace,
for less disruption.

Running repeatedly should now be safer.
2021-11-01 12:34:37 -04:00
Jarrod Johnson a5f96ccc5d Correct whitespace mistake in previous commit 2021-11-01 11:09:45 -04:00
Jarrod Johnson 5e0aced2ea Merge branch 'master' of github.com:lenovo/confluent 2021-11-01 07:01:56 -04:00
Jarrod Johnson 28a13da118 Fix non-noderange alert specification.
The change in to accomodate nodarange without per-node
distinct input claimed the single input data exclusively
for multimode.

Correct by pulling the singleton code to be common if the
check breaks from the for loop.
2021-11-01 06:59:54 -04:00
Jarrod Johnson 6df8170401 Draft confignet implementation
This will team and set up additional interfaces,
but, for one, no dns.  Need to merge with deploycfg info, since
networkmanager is intent on making dns a
connection oriented thing.
2021-10-29 17:16:16 -04:00
Jarrod Johnson 224663d0c8 Fix execution of confluent functions after run_remote_python 2021-10-29 13:50:04 -04:00
Jarrod Johnson 729408a0cb Fix run_remote aafter the cleanup was added
The removal of the temp dir needed to be linked with
whether the script was dedicated or shared tmp with a run_remote_parts.
2021-10-29 13:01:59 -04:00
Jarrod Johnson b1ee751c7b Add newline to error output 2021-10-29 09:43:52 -04:00
Jarrod Johnson 9428c1fb8e Merge pull request #77 from vmaneagit/patch-80
Update nodelicense.ronn
2021-10-29 09:40:39 -04:00
Jarrod Johnson 391963e3f3 Merge pull request #78 from vmaneagit/patch-79
Update nodelicense
2021-10-29 09:40:25 -04:00
Jarrod Johnson 78163f1d09 Correct issue in parameter rename for apiclient 2021-10-29 07:23:26 -04:00
Jarrod Johnson 8ef70d463f Draft client for net configuration
Common code to struction networks into structure for configuration.

This organizes and autodetects
overlap, in prep for auto-teaming.
2021-10-28 17:09:09 -04:00
Jarrod Johnson aff0c82b0b Fix apiclient with explicit host 2021-10-28 16:15:21 -04:00
Jarrod Johnson 925ea3e95a Refactor apiclient and allow specific host
For getnetcfg, will need to select
spsecific interfaces.  While
tocuhing that anyway, simplify the
v4/v6 paths to be more agnostic.
2021-10-28 15:48:58 -04:00
Jarrod Johnson 5b128238e7 Fix netcfg self api call 2021-10-28 15:47:07 -04:00
Jarrod Johnson 6f967a3afa Bind apiclient search to make for viable firewall rule 2021-10-28 12:55:42 -04:00
Jarrod Johnson a2a9a38147 Fix mistaken sort 2021-10-28 12:30:06 -04:00
Jarrod Johnson 7eeba4e42a Rdeuce netlink traffic to get net config
Avoid superfluous calls to
get addresses and reuse
result through the lifetime
of a reply.
2021-10-27 17:25:54 -04:00
Jarrod Johnson 5a7f0db899 Fix use of autodetected plen 2021-10-27 17:19:22 -04:00
Jarrod Johnson 00b22bb18b Improve autosense of other nics 2021-10-27 17:12:03 -04:00
Jarrod Johnson 621ded7256 Prioritize 'none' and support names in _address fields 2021-10-27 17:03:26 -04:00
Jarrod Johnson ce8e671584 Have includes interpret section names
If <including> a syncfiles, then
be sure to honor MERGE/APPENDONCE in those files without perturbing flow
of the parent file.
2021-10-27 15:47:52 -04:00
Jarrod Johnson 41ba28e525 Connect network configuration to selfservice
This permits a client to ask via various nics for pertinent configuration .
2021-10-27 15:35:22 -04:00
Jarrod Johnson ce9b0bf556 Support terminating include with >
The previous thought of <base.pkglist is fine, but upon seing <base.pkglist>, that looks better.
2021-10-27 14:21:30 -04:00
Jarrod Johnson 74775fd2be Remove errant period in attributes 2021-10-27 13:11:24 -04:00
Jarrod Johnson feb12b95cb Add facility to enable confignet
Provide function to populate
a map of all networking, relative to the interface facing thte client.
2021-10-27 11:52:09 -04:00
Jarrod Johnson 154a328194 Merge pull request #81 from PenghuiCui/master
Add check for attribute value to avoid exception when attribute is None
2021-10-26 07:35:10 -04:00
Jarrod Johnson 8eb6ce0b69 Specify absolute path for mktemp
mktemp defaults to relative, to produce the correct
behavior, must be absolute template.
2021-10-25 12:03:26 -04:00
Jarrod Johnson 0d53612e9c Fix variable yielded when not including 2021-10-25 11:57:18 -04:00
Jarrod Johnson 81675faec1 Avoid clearing disk if autopartition is not to be used 2021-10-25 10:05:15 -04:00
Penghui Cui 6e10b3d2b1 Add check for attribute value to avoid exception when attribute is None 2021-10-25 16:11:38 +08:00
Jarrod Johnson a9bd31a9c4 Provide error when makedirs fails in genesis 2021-10-23 13:08:14 -04:00
Jarrod Johnson 219ad8f56b Fixes for confluent2hosts
python2 compatibility and various scenarios not handled.
2021-10-22 16:07:20 -04:00
Jarrod Johnson 9c0ec25b6e Merge branch 'master' of github.com:lenovo/confluent 2021-10-22 13:13:25 -04:00
Jarrod Johnson 412e2aaf76 Add '<' include syntax to syncfiles and pkglist
Provide means of combining
multiple pkglist and syncfiles
based on hierarchy.

This enables construction of
a more complex structure of
images for those that may want it.
2021-10-22 13:11:50 -04:00
Jarrod Johnson a48732923e Merge pull request #80 from PenghuiCui/master
Support resource register from plugin
2021-10-21 21:44:39 -04:00
Jarrod Johnson 40dea6a747 Support older python subprocess
Older python did not provide timeout.  Keep the timeout
for the modern python that skips select without a timeout,
but try again without timeout to retain compatibility.
2021-10-20 18:23:35 -04:00
Jarrod Johnson d441ff3d63 Fix pxe offers made in spite of managercandidate restriction 2021-10-20 16:45:40 -04:00
Jarrod Johnson 6550d584a2 Give scan callers a context for tracking local interface
When trying to leverage autodetect strategy for net config,
will need to keep track of receiving interface.  For now
only use ipv6 for this capability, as getting the incoming nic
for ipv4 would be more work.
2021-10-20 14:10:59 -04:00
Jarrod Johnson 8a01a0bbdc Use the scan_confluent for getting apikey, if relevant 2021-10-20 14:03:03 -04:00
Jarrod Johnson 484a3f8c64 Have apiclient priotize based on responsiveness 2021-10-19 17:09:52 -04:00
Jarrod Johnson 75557091ef Treat blank enclosure/bay as unset
If someone does the obvious approach to blank something,
treat that as equivalent.
2021-10-19 15:43:23 -04:00
Jarrod Johnson decee2ed93 Add fallback to apiclient
When apiclient fails using deploycfg, add
a multicast scan looking for replacements.
2021-10-19 15:39:47 -04:00
Jarrod Johnson 0725717359 Correct some issues in the ssdp snoop
We still need the node attributes even if
forcing reply.

Further,, sometimes eventlet returns None, tolerate
that better.
2021-10-19 13:12:35 -04:00
Jarrod Johnson f8f60052d0 Fix confluent uuid handling 2021-10-19 10:37:08 -04:00
Jarrod Johnson 3cd78500ce Make client adaptive to receive global data
When following, let the leader filter the
relevant global data rather than the follower.
2021-10-19 10:01:44 -04:00
Jarrod Johnson fd8dd03587 Implement a confluent instance uuid
This allows clients to indicate after install
whether they belong to the collective or
not, allowing ascertaining new deployment
server.
2021-10-19 09:57:49 -04:00
Penghui Cui 05a683698e Support resource register from plugin 2021-10-19 15:31:13 +08:00
Jarrod Johnson 6e7b6188dd Fix api client attempts and speed up checking connections 2021-10-18 16:31:59 -04:00
Jarrod Johnson ae7e69d73d Fix mistake in the apiclient 2021-10-18 15:52:22 -04:00
Jarrod Johnson 0f80b4a019 Change apiclient to embed apikey request
No longer require the clorthto utility
to request a token if we are already in
python.
2021-10-18 12:28:59 -04:00
Jarrod Johnson f93b8da1b7 Handle invalid vendor-class-identifier better
If the vci fails to decode, just ignore it and move on.
2021-10-15 16:49:45 -04:00
Jarrod Johnson 1d71cee61d Fix syncfiles not running with just APPENDONCE 2021-10-15 16:37:02 -04:00
Jarrod Johnson 957679db3c Update syncfile examples and el7 support 2021-10-15 16:24:22 -04:00
Jarrod Johnson 98a6ffd9b6 Update more profiles with the APPENDONCE support 2021-10-15 16:16:11 -04:00
Jarrod Johnson 2e7b315f29 Add 'APPENDONCE:' section to syncfiles
This will APPEND if the target file doesn't
already have the entire source contents in
a contiguous location already. This makes
it more safe to rerun without negative consequence.
2021-10-15 15:48:49 -04:00
Jarrod Johnson f33ed9b4d9 Cleanup after script execution
Leave cleaner tmp directories when functions are ran.
2021-10-15 12:16:48 -04:00
Jarrod Johnson 04dd374bfb Synchronize functions across the supported OSes 2021-10-15 12:10:24 -04:00
Jarrod Johnson 980c3dbcb3 Make functions common between redhat and suse 2021-10-15 12:07:00 -04:00
Jarrod Johnson 08f61e9391 Fix syntax mistake in the previous commit 2021-10-15 10:06:54 -04:00
Jarrod Johnson 653c0bd695 Bring up both IPv4 and IPv6 in EL8
If both are configured, then enable both as early as possible.
2021-10-15 09:30:08 -04:00
Jarrod Johnson 52549ec563 Fix module autoloading in diskless profiles
The initramfs has a reduced set of modules to boot and then
in diskless mode hands over to the 'real' OS for all userspace
to use the more substantial root filesystem with all of the modules.

However one exception is kernel module autoload, which induces modprobe
to run in the initial mount namespace unconditionally.

Thus, preserve the ramfs drivers (just in case) but have the autoloading
pivot
to the normal root filesystem to get the full complement of modules.
2021-10-13 11:37:48 -04:00
Jarrod Johnson 351d6a85ee Fix firstboot with ipv6 deployment in el8 2021-10-12 17:23:18 -04:00
Jarrod Johnson fda61bd354 Attempt to extend the v6 deployment support in el8 2021-10-12 16:58:42 -04:00
Jarrod Johnson d06e130af5 Fix ipv6 address specification in el8 scripted install 2021-10-12 16:42:07 -04:00
Jarrod Johnson 5ca3b68b8a Attempt to support ipv6 deployment in el8 2021-10-12 16:06:37 -04:00
Jarrod Johnson 4f40d9635a Fix nic config with mismatched gateway family
When a gateway exists on a family the node
does not exist for, handle the gateway better.
2021-10-12 15:32:30 -04:00
Jarrod Johnson b96e9f4f70 Provide better feedback on missing key and skip tpm when not present 2021-10-12 11:39:18 -04:00
Jarrod Johnson 58cf220203 Mask spurious output on disk cleanup 2021-10-11 13:30:31 -04:00
Jarrod Johnson a0dfd3f42d Move scripted install to pre-trigger
In Stream 9, initqueue is too late to pre-empt the auto dhcp,
move up to pre-trigger and manually trigger to get network
ready for pre-emption.
2021-10-11 13:28:55 -04:00
Jarrod Johnson 57f346fa4c Implement syncfiles parameters
chmod and chown as directed
by the syncfiles file if present
2021-10-08 10:27:57 -04:00
Jarrod Johnson deb5c2eed8 Fix potential delay in ssh-agent start
Be consistent with other check_output
calls to allow eventlet to properly
background the process.
2021-10-08 10:24:35 -04:00
Jarrod Johnson 7ccaa245b6 Provide both string and numeric for owner/group
To provide a fallback for unrecognized user/group,
provide an id for syncfileclient to resort for.
2021-10-08 10:02:13 -04:00
Jarrod Johnson 52a7921676 Correct mistakes in syncfile option passing 2021-10-07 18:23:57 -04:00
Jarrod Johnson 3f7a7d09dd Correct mistake in referencing option string 2021-10-07 18:02:56 -04:00
Jarrod Johnson c0287e93ed Roll back rsync ownership
Unfortunately, it will try to change parent directories on the way to
the files actually being written.
2021-10-07 16:34:37 -04:00
Jarrod Johnson a676eb0b83 Syncfile option passing to client
Provide a mechanism for a syncfiles to indicate options to the client.

This will be used for owner=, group=, and permissions=
options.
2021-10-07 15:54:53 -04:00
Jarrod Johnson a0ca3d3122 Extend snmp timeout
Default is 1 second, bump to 2 seconds for some
extraordinarily slow switches.  This changes
overall to about 10 seconds as there are, by default,
5 retries.
2021-10-07 13:03:05 -04:00
Jarrod Johnson e52a9ff70f Have syncfiles attempt to preserve more
We cannot preserve symlinks (since we need them intact),
but we can use other rsync features to make synchronization work
better by default.
2021-10-07 13:01:48 -04:00
Jarrod Johnson 3b7e7afc24 Fix inconsistent handling of marginal id in syncfileclient
Also, resynchronize syncfileclient across the distributions.
2021-10-07 12:36:52 -04:00
Jarrod Johnson 26c6f1407b Add syntax for noderange filtering
/etc/passwd -> compute,-n11:/etc/passwd

Will cause the line to be used only when the node matches the
compute,-n11 noderange.
2021-10-07 11:26:13 -04:00
Jarrod Johnson 6222883188 Syncronize syncfileclient for el8 diskless and diskful 2021-10-07 11:10:56 -04:00
Jarrod Johnson a4fc64ea56 Move el9bin build out of spec
Since the rpm itself is built in an el7 container, build
binary before spec driven build to be compatible.
2021-10-07 09:18:24 -04:00
Jarrod Johnson d23f9db6e0 Syncronize diskfull and diskless el8 and el9 functions 2021-10-07 08:38:29 -04:00
Jarrod Johnson d5bbda9e00 Extend el8 to el9 diskless 2021-10-07 08:22:38 -04:00
Jarrod Johnson 23ff788f09 Fix target for el9 binaries 2021-10-07 08:20:51 -04:00
Jarrod Johnson 172008f942 Attempt to cover centos stream 9 by el8
At first glance, there should be similarities,
apart from python differences and need for
different clortho build due to library mismatch.
2021-10-06 16:59:47 -04:00
Jarrod Johnson 2ef41fedf6 Add el9 compatible build
CentOS Stream 9 is not backwards compatible enough to run the
same confluent utilities that the other distributions use.

For now, use fedora to build.
2021-10-06 15:51:12 -04:00
Jarrod Johnson d666050ef7 Consolidate neigh wait time in slp
Rather than incurring it on each iteration (causing a scan to take
15 seconds in test), defer to
handle them all later (reducing to 5 seconds to scan).
2021-10-05 10:59:17 -04:00
Jarrod Johnson 3c72f9f18f Refactor slp neigh warm-up
Rather than pulling it out at active_scan level, intervene deeply.

This fixes the problem where an ip gets used as a key rather than mac.
2021-10-05 10:50:51 -04:00
Jarrod Johnson f50abd662a Provide analogus improvements to ssdp from slp
Recent changes to try to make neigh table fresh and viable in slp
can also apply to ssdp.

Bring those changes forward.
2021-10-05 10:03:28 -04:00
Jarrod Johnson 1fda0e50d8 Fix stub from refactor
foobar was a stand-in for continue to
allow refactor, and needed
to be changed to return.
2021-10-05 08:04:58 -04:00
Jarrod Johnson 1a80d9f31a Extend neigh table refresh to snoop
Improve reliability of 'snooping' slp.
2021-10-04 16:36:22 -04:00
Jarrod Johnson c569826ff1 Improve scan result with missing neigh entries
If no neigh table is present for
a given address, send a packet to
induce kernel activity.

Then wait for a bit over 2 seconds to allow for
2 retries (at default settings)
and then proceed assuming all findable neighbor table
entries will be found.
2021-10-04 16:22:12 -04:00
Jarrod Johnson 1c69700e1d Fix missing port 22
Either this was overlooked or the sshd_config stopped including
Port 22.  Fix by putting in port 22.  If port 22 already existed,
sshd is fine with the configuration being configured twice.
2021-10-01 08:34:51 -04:00
Jarrod Johnson 92949f012f Merge tag '3.2.2'
Fix collective mode
2021-10-01 08:03:50 -04:00
Jarrod Johnson 398c8b3a71 Improve syncfiles permission denied reporting
When a non-readable file was
encountered, confluent would
cryptically report rsync failure.

Check for the usual culprit, unreadable files if rsync fails.

Cause this error to manifest with clearer text.
2021-09-30 12:01:29 -04:00
Jarrod Johnson c93917e4a4 Add a failsafe for errors trying to connect to a new leader
Provide better experience in the face of the class of errors such
as the one fixed by previous commit.
2021-09-30 08:18:39 -04:00
Jarrod Johnson c1113430a2 Pass only the name rather than full sockaddr on retire
Followers of a server that is retiring to join the better leader
were being given incorrect format which would fail in the dns lookup.
2021-09-30 08:11:19 -04:00
Jarrod Johnson d899cfbf1a Clean disk to install in pre
Installers frequently fail if there's a derelict lvm on disk.

Explicitly remove the on-disk content and deactive lvm to let
anaconda work reliably.
2021-09-29 14:31:18 -04:00
Jarrod Johnson 99ba4e7765 Handle ip aliases in pxe
Use netutil assessment of the
best server ip for pxe responses.

Using 'recvip' is too simplistic for broadcast
packets.  recvip just gets the first ipv4 address on the interface,
when an alias may be better.

netutil assesses all the possible aliases, thus has better logic and pxe.py
now uses it.
2021-09-27 11:04:42 -04:00
Jarrod Johnson 929392c746 Fix hangs on check_output
Eventlet narrowly targets overriding
select in subprocess, to avoid rewriting adequate functions.

However, subprocess does an 'optimization' to skip
select if there's fewer than 3 pipes to juggle and no timeout specified.

Induce python to always use select
by specifying a very long timeout.

This causes confluent to be able to spawn multiple subprocesses and
not be hung waiting for input.
2021-09-23 10:42:47 -04:00
Jarrod Johnson 5d20ee6cca Serialize ssh agent interaction
Race conditions could occur when multiple
requests came in too close together.
2021-09-23 09:40:10 -04:00
Jarrod Johnson 42038048da Drop PC from the speed prefix 2021-09-22 10:52:29 -04:00
Jarrod Johnson d08d597818 Properly show DCPMM in nodeinventory 2021-09-22 10:50:32 -04:00
Jarrod Johnson 3869dd6918 Fix imgutil capture
Adding options for oshandler
broke fingerprinting for capture.

Fix by passing None and triggering the AttributeErrors.
2021-09-22 08:17:18 -04:00
Jarrod Johnson 4ab5cac3eb Generate random serial number for certificate
Hardcoding 0x123 serial number would cause strict clients to reject the
certificate.

While we are still not guaranteeing uniqueness, the chances of a
duplicate are impossibly small.
2021-09-22 07:48:44 -04:00
Jarrod Johnson 3a911acb81 Fix the efibootmgr override logic
Fully realize the chroot invocation of efibootmgr and also
bail out if no new boot number is detected.
2021-09-21 15:50:45 -04:00
Jarrod Johnson 1cb5efae75 Call efibootmgr in chroot for ubuntu 2021-09-21 11:21:13 -04:00
Jarrod Johnson 861c0230c7 Reorder ubuntu to first in post
Ubuntu tends to install itself as behind network
device.  Amend to match other OSes and avoid network
boot.
2021-09-21 10:03:29 -04:00
Jarrod Johnson 3b3fffb36b Note workaround for 'No data available'
In a diskless boot, sometimes the build system
might not label the scratch contents
correctly.  setfiles can be used to fix it.

Notably, this seems to cause the overlay+squashfs to
return 'no data available' on select
calls even with selinux disabled, so it may be another
gap with overlay and/or limitation of squashfs when
queried about modification without a label.
2021-09-21 08:13:06 -04:00
Jarrod Johnson dd89c48b9a Fix problem where syncfiles could fail on certan hosts
If the set of oldhosts and newhosts were just so,
targlines or sourcelines could be all
null and cause index errors.
2021-09-20 08:01:41 -04:00
Jarrod Johnson 6acd3c2b7d Add ability to execute functions as a command
This gives easier access over nodeshell and similar to run functions by executing the
functions as a script with arguments.
2021-09-20 08:00:35 -04:00
Jarrod Johnson fdc9b3c283 Add confluent2hosts utility
Add utility to help add/modify /etc/hosts for a noderange.

Also fix buggy code in syncfileclient noticed while
reusing it for confluent2hosts.
2021-09-17 12:57:18 -04:00
Jarrod Johnson 1b88e44c59 Ignore broken lldp results
Some switches may have broken incomplete records,
skip such records to focus on viable complete records.
2021-09-16 15:49:36 -04:00
Jarrod Johnson f881f955d4 Correct typo in ubunut initramfs scripts 2021-09-15 12:20:08 -04:00
Jarrod Johnson 9687ce89b8 Use fe80:: match to find index for getting addresses
This fixes a problem where fetching
from fe80:: fails to identify nic.
2021-09-13 17:54:18 -04:00
Jarrod Johnson 5276618b73 Actually remove group on split 2021-09-13 14:09:07 -04:00
Jarrod Johnson 38669ddbbc Have nodegroupremove accept comma delimited groups 2021-09-13 14:01:46 -04:00
Jarrod Johnson 2dcd0f769a Support defining multiple groups in one go 2021-09-13 13:48:56 -04:00
Jarrod Johnson d254cb08ac Default to None for second arg to get_apikey
In some context, we don't need to check both, we have a confirmed manager. Just take
one argument for that scenario.
2021-09-13 11:48:36 -04:00
Jarrod Johnson 66ddcfc194 Separate connection check from connection
Messing with connect corrupted
the http client too much,
have a separate helper function to evaluate
the candidate deployers,
then pass only the winner on.
2021-09-13 11:21:38 -04:00
Jarrod Johnson 95ea913108 Have genesis opt into ipv6 deploycfg 2021-09-13 10:26:50 -04:00
Jarrod Johnson 52bebe41d1 Provide ipv6 only for deploycfg2
Older profiles may trip over new network
lines, spare them 'v6' entries until
they opt into deploycfg2.
2021-09-13 10:26:17 -04:00
Jarrod Johnson 06e32c53cc Fix overly vague extraction of deploy_server
If deploy_server appears anywhere, it confuses
the scripts
2021-09-13 10:23:13 -04:00
Jarrod Johnson 7ba10e559e Correct ipv4/v6 handling in get_apikey 2021-09-13 10:03:32 -04:00
Jarrod Johnson cf097b153e Fix typo in genesis network bringup 2021-09-10 12:50:06 -04:00
Jarrod Johnson 269d9c79d1 Let functions set confluent_mgr
The better logic in functions is used instead of the simplistic logic.
2021-09-10 12:28:00 -04:00
Jarrod Johnson 1c3c54a35b Update functions to be more thorough
confluent_mgr could be ipv6 or
may need to be updated from a
link local or may need to go to copernicus data to work.
2021-09-10 11:40:17 -04:00
Jarrod Johnson 66d378a007 Simplify the ipv6 support and fix invocation before deploycfg 2021-09-10 09:16:08 -04:00
Jarrod Johnson 35b74764bf Amend apiclient to support v4/v6 2021-09-09 17:15:29 -04:00
Jarrod Johnson 11796cda81 Correct the ipv6 prefix name 2021-09-09 16:51:27 -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
vmaneagit 5728e141a4 Update nodelicense.ronn
Man page clarification - file as license file, name as license feature name.
2021-07-14 13:46:08 +03:00
vmaneagit f531c1b856 Update nodelicense
Usage clarification - file as license file, name as license feature name.
2021-07-14 13:43:57 +03:00
158 changed files with 6691 additions and 1068 deletions
+2 -1
View File
@@ -972,7 +972,7 @@ def consume_termdata(fh, bufferonly=False):
data = None
if type(data) == dict:
updatestatus(data)
return
return ''
if data is not None:
data = client.stringify(data)
if clearpowermessage:
@@ -1021,6 +1021,7 @@ def consume_termdata(fh, bufferonly=False):
doexit = True
inconsole = False
sys.stdout.write("\r\n[remote disconnected]\r\n")
return ''
if __name__ == '__main__':
errcode = 0
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/python2
import argparse
import os
import re
import signal
import sys
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except AttributeError:
pass
path = os.path.dirname(os.path.realpath(__file__))
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
if path.startswith('/opt'):
sys.path.append(path)
import confluent.client as client
import confluent.sortutil as sortutil
def partitionhostsline(line):
comment = ''
if '#' in line:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
if not line.strip():
return '', [], comment
ipaddr, names = line.split(None, 1)
names = names.split()
return ipaddr, names, comment
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
self.byname6 = {}
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
byname = self.byname
if ':' in currip:
byname = self.byname6
for name in names:
byname[name] = x
def add_entry(self, ip, names):
targ = self.byname
if ':' in ip:
targ = self.byname6
line = '{:<39} {}'.format(ip, names)
x = len(self.sourcelines)
self.sourcelines.append(line)
for name in names.split():
if not name:
continue
targ[name] = x
self.byip[ip] = x
def read_target(self, targetfile):
if not os.path.exists(targetfile):
return
with open(targetfile, 'r') as hfile:
lines = hfile.read().split('\n')
while lines and not lines[-1]:
lines = lines[:-1]
for y in range(len(lines)):
line = lines[y]
currip, names, comment = partitionhostsline(line)
byname = self.byname
if ':' in currip:
byname = self.byname6
if currip in self.byip:
x = self.byip[currip]
if self.sourcelines[x] is None:
# have already consumed this entry
continue
self.targlines.append(self.sourcelines[x])
self.sourcelines[x] = None
continue
for name in names:
if name in byname:
x = 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 self.targlines and not self.targlines[-1]:
self.targlines = self.targlines[:-1]
while self.sourcelines and not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
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')
def main():
ap = argparse.ArgumentParser(description="Create/amend /etc/hosts file for given noderange")
ap.add_argument('noderange', help='Noderange to generate/update /etc/hosts for')
ap.add_argument('-a', '--attrib', help='Pull ip addresses and hostnames from attribute database', action='store_true')
ap.add_argument('-i', '--ip', help='Expression to generate addresses (e.g. 172.16.1.{n1} or fd2b:246f:8a50::{n1:x})')
ap.add_argument('-n', '--name', help='Expression for name to add ({node}-compute, etc). If unspecified, "{node} {node}.{dns.domain}" will be used', action='append')
args = ap.parse_args()
c = client.Command()
if args.name:
names = ' '.join(args.name)
else:
names = '{node} {node}.{dns.domain}'
merger = HostMerger()
if not args.ip and not args.attrib:
sys.stderr.write('-a or -i is currently required\n')
sys.exit(1)
if args.attrib:
ip4bynode = {}
ip6bynode = {}
namesbynode = {}
domainsbynode = {}
for ent in c.read('/noderange/{0}/attributes/current'.format(args.noderange)):
ent = ent.get('databynode', {})
for node in ent:
for attrib in ent[node]:
val = ent[node][attrib]
if not isinstance(val, dict):
continue
val = val.get('value', None)
if attrib == 'dns.domain':
domainsbynode[node] = val
if attrib.startswith('net.'):
nameparts = attrib.split('.')
if len(nameparts) == 2:
currnet = None
else:
currnet = '.'.join(nameparts[1:-1])
for currdict in (ip4bynode, ip6bynode, namesbynode):
if node not in currdict:
currdict[node] = {}
if attrib.endswith('.ipv4_address') and val:
ip4bynode[node][currnet] = val.split('/', 1)[0]
elif attrib.endswith('.ipv6_address') and val:
ip6bynode[node][currnet] = val.split('/', 1)[0]
elif attrib.endswith('.hostname'):
namesbynode[node][currnet] = re.split('\s+|,', val)
for node in ip4bynode:
mydomain = domainsbynode.get(node, None)
for ipdb in (ip4bynode, ip6bynode):
for currnet in ipdb[node]:
if args.name:
sys.stderr.write('Custom name and attribute addresses not currently supported together\n')
sys.exit(1)
else:
names = list(namesbynode.get(node, {}).get(currnet, [node]))
if mydomain:
for name in names:
if mydomain in names:
break
else:
for name in list(names):
names.append('{0}.{1}'.format(name, mydomain))
names = ' '.join(names)
merger.add_entry(ipdb[node][currnet], names)
merger.write_out('/etc/whatnowhosts')
else:
namesbynode = {}
ipbynode = {}
expurl = '/noderange/{0}/attributes/expression'.format(args.noderange)
expression = names
exitcode = 0
exitcode |= expand_expression(c, namesbynode, expurl, names)
exitcode |= expand_expression(c, ipbynode, expurl, args.ip)
if exitcode:
sys.exit(exitcode)
for node in ipbynode:
merger.add_entry(ipbynode[node], namesbynode[node])
if os.path.exists('/etc/hosts'):
merger.read_target('/etc/hosts')
os.rename('/etc/hosts', '/etc/hosts.confluentbkup')
merger.write_out('/etc/hosts')
def expand_expression(c, namesbynode, expurl, expression):
exitcode = 0
for exp in c.create(expurl, {'expression': expression}):
if 'error' in exp:
sys.stderr.write(exp['error'] + '\n')
exitcode |= exp.get('errorcode', 1)
ex = exp.get('databynode', ())
for node in ex:
namesbynode[node] = ex[node]['value']
return exitcode
if __name__ == '__main__':
main()
+4 -1
View File
@@ -105,7 +105,10 @@ if len(args) > 1:
requestargs=args[1:]
except:
pass
elif options.clear or options.environment or options.prompt:
sys.stderr.write('Attribute names required with specified options\n')
argparser.print_help()
exitcode = 400
if exitcode != 0:
sys.exit(exitcode)
+4 -4
View File
@@ -88,9 +88,9 @@ client.check_globbing(noderange)
def get_update_progress(session, url):
for res in session.read(url):
status = res['phase']
percent = res['progress']
detail = res['detail']
status = res.get('phase', 'error')
percent = res.get('progress', None)
detail = res.get('detail', repr(res)),
if status == 'error':
text = 'error!'
else:
@@ -164,7 +164,7 @@ def show_firmware(session):
firmware_shown = True
printfirm(node, prefix, inv[prefix])
if not nodes_matched:
sys.stderr.write('No matching nodes for noderange "{0}"'.format(noderange))
sys.stderr.write('No matching nodes for noderange "{0}"\n'.format(noderange))
elif not firmware_shown and not exitcode:
argparser.print_help()
+11 -10
View File
@@ -45,14 +45,15 @@ except IndexError:
client.check_globbing(noderange)
session = client.Command()
exitcode = 0
attribs = {'name': noderange}
for arg in args[1:]:
key, val = arg.split('=', 1)
attribs[key] = val
for r in session.create('/nodegroups/', attribs):
if 'error' in r:
sys.stderr.write(r['error'] + '\n')
exitcode |= 1
if 'created' in r:
print('{0}: created'.format(r['created']))
for group in noderange.split(','):
attribs = {'name': group}
for arg in args[1:]:
key, val = arg.split('=', 1)
attribs[key] = val
for r in session.create('/nodegroups/', attribs):
if 'error' in r:
sys.stderr.write(r['error'] + '\n')
exitcode |= 1
if 'created' in r:
print('{0}: created'.format(r['created']))
sys.exit(exitcode)
+7 -6
View File
@@ -43,10 +43,11 @@ noderange = args[0]
client.check_globbing(noderange)
session = client.Command()
exitcode = 0
for r in session.delete('/nodegroups/{0}'.format(noderange)):
if 'error' in r:
sys.stderr.write(r['error'] + '\n')
exitcode |= 1
if 'deleted' in r:
print('{0}: deleted'.format(r['deleted']))
for group in noderange.split(','):
for r in session.delete('/nodegroups/{0}'.format(group)):
if 'error' in r:
sys.stderr.write(r['error'] + '\n')
exitcode |= 1
if 'deleted' in r:
print('{0}: deleted'.format(r['deleted']))
sys.exit(exitcode)
+3
View File
@@ -53,6 +53,9 @@ def print_mem_info(node, prefix, meminfo):
memdescfmt += '3-{1} '
elif 'DDR4' in meminfo['memory_type']:
memdescfmt += '4-{1} '
elif 'DCPMM' in meminfo['memory_type']:
memdescfmt = '{0}GB {1} '
meminfo['module_type'] = 'DCPMM'
else:
print('{0}: {1}: Unrecognized Memory'.format(node, prefix))
return
+1 -1
View File
@@ -36,7 +36,7 @@ exitcode = 0
argparser = optparse.OptionParser(
usage="Usage: "
"%prog <noderange> [list][install <filename>|save <directory>|delete <name>]")
"%prog <noderange> [list][install <license_filename>|save <directory>|delete <license_feature_name>]")
argparser.add_option('-m', '--maxnodes', type='int',
help='Specify a maximum number of '
'nodes to delete licenses from, '
+8 -2
View File
@@ -131,8 +131,14 @@ def run():
def run_cmdv(node, cmdv, all, pipedesc):
nopen = subprocess.Popen(
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
nopen = subprocess.Popen(
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError as e:
if e.errno == 2:
sys.stderr.write('{0}: Unable to find local executable file "{1}"'.format(node, cmdv[0]))
return
raise
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
'type': 'stdout'}
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
+1 -1
View File
@@ -32,7 +32,7 @@ if path.startswith('/opt'):
import confluent.client as client
argparser = optparse.OptionParser(
usage='Usage: %prog [options] noderange [default|cd|network|setup|hd|usb|floppy]')
usage='Usage: %prog [options] <noderange> [default|cd|network|setup|hd|usb|floppy]')
argparser.add_option('-b', '--bios', dest='biosmode',
action='store_true', default=False,
help='Request BIOS style boot (rather than UEFI)')
+7 -3
View File
@@ -44,6 +44,8 @@ def run():
"'ipmitool -H {hardwaremanagement.manager}' will be expanded.")
argparser.add_option('-f', '-c', '--count', type='int', default=168,
help='Number of commands to run at a time')
argparser.add_option('-l', '--loginname', type='str',
help='Username to use when connecting, defaults to current user.')
argparser.add_option('-n', '--nonodeprefix', action='store_true',
help='Do not prefix output with node names')
argparser.add_option('-p', '--port', type='int', default=0,
@@ -97,10 +99,12 @@ def run():
sshnode = nodemap.get(node, node)
if not isinstance(cmd, str) and not isinstance(cmd, bytes):
cmd = cmd.encode('utf-8')
cmdv = ['ssh']
if options.port:
cmdv = ['ssh', '-p', '{0}'.format(options.port), sshnode, cmd]
else:
cmdv = ['ssh', sshnode, cmd]
cmdv += ['-p', '{0}'.format(options.port)]
if options.loginname:
cmdv += ['-l', options.loginname]
cmdv += [sshnode, cmd]
if currprocs < concurrentprocs:
currprocs += 1
run_cmdv(node, cmdv, all, pipedesc)
+13
View File
@@ -20,6 +20,10 @@ import os
import signal
import sys
try:
input = raw_input
except NameError:
pass
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except AttributeError:
@@ -195,6 +199,8 @@ def main():
help='Comma separated list of disks to use, or the '
'word "rest" to indicate use of all available '
'disks')
argparser.add_option('-i', '--interactive', default=False, action='store_true',
help='Confirm any potentially destructive changes interactively')
argparser.add_option('-s', '--size', type='str',
help='Comma separated list of sizes to use when '
'creating volumes. The sizes may be absolute '
@@ -230,6 +236,13 @@ def main():
except KeyError:
argparser.print_help()
sys.exit(1)
if options.interactive and operation != 'show':
showstorage(noderange, None, None)
sys.stdout.write('The above configuration will be changed by this action, do you wish to continue (y/N): ')
confirm = input()
if confirm and confirm.lower()[0] != 'y':
sys.stdout.write('Aborting\n')
sys.exit(1)
handler(noderange, options, args[2:])
+5 -5
View File
@@ -42,9 +42,9 @@ _attraliases = {
}
try:
input = raw_input
getinput = raw_input
except NameError:
pass
getinput = input
class NestedDict(dict):
@@ -284,9 +284,9 @@ class Command(object):
nodename = list(self.read(
'/noderange/{0}/nodes/'.format(noderange)))[0].get('item', {}).get('href', None)
nodename = nodename[:-1]
p = input('Command is about to affect node {0}, continue (y/n)? '.format(nodename))
p = getinput('Command is about to affect node {0}, continue (y/n)? '.format(nodename))
else:
p = input('Command is about to affect {0} nodes, continue (y/n)? '.format(nsize))
p = getinput('Command is about to affect {0} nodes, continue (y/n)? '.format(nsize))
if p.lower() != 'y':
sys.stderr.write('Aborting at user request\n')
sys.exit(1)
@@ -401,7 +401,7 @@ class Command(object):
if fingerprint == khf[hostid]:
return
else:
replace = input(
replace = getinput(
"MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):")
if replace not in ('y', 'Y'):
raise Exception("BAD CERTIFICATE")
+22 -14
View File
@@ -17,9 +17,9 @@ import sys
import struct
import termios
def get_screenwidth():
def get_screengeom():
return struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,
b'....'))[1]
b'....'))
class ScreenPrinter(object):
def __init__(self, noderange, client, textlen=4):
@@ -44,7 +44,7 @@ class ScreenPrinter(object):
self.fieldwidth = maxlen + textlen + 1 # 1 for column
def set_output(self, node, text):
if self.nodeoutput[node] == text:
if self.nodeoutput.get(node, None) == text:
return
self.nodeoutput[node] = text
if len(text) >= self.textlen:
@@ -54,23 +54,31 @@ class ScreenPrinter(object):
def drawscreen(self):
if self.squeeze:
currwidth = get_screenwidth()
currheight, currwidth = get_screengeom()
currheight -= 1
numfields = currwidth // self.fieldwidth
fieldformat = '{{0:>{0}}}:{{1:{1}}}'.format(self.nodenamelen,
self.textlen)
sys.stdout.write('\x1b[2J\x1b[;H') # clear screen
if len(self.nodelist) < (numfields * currheight):
numfields = len(self.nodelist) // currheight + 1
else:
numfields = 1
fieldformat = '{0}: {1}'
currfields = 0
for node in self.nodelist:
if currfields >= numfields:
if self.squeeze:
columns = [self.nodelist[x:x+currheight] for x in range(0, len(self.nodelist), currheight)]
for currow in range(0, len(columns[0])):
for col in columns:
try:
node = col[currow]
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
except IndexError:
break
sys.stdout.write('\n')
else:
for node in self.nodelist:
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
sys.stdout.write('\n')
currfields = 1
else:
currfields += 1
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
sys.stdout.write('\n')
sys.stdout.flush()
@@ -80,8 +88,8 @@ class ScreenPrinter(object):
if __name__ == '__main__':
import confluent.client as client
c = client.Command()
p = ScreenPrinter('n1-n13', c)
p.set_output('n3', 'Upload: 67%')
p = ScreenPrinter('d1-d12', c)
p.set_output('d3', 'Upload: 67%')
@@ -27,15 +27,23 @@ a confluent server.
%if "%{dist}" == ".el8"
python3 setup.py build
%else
%if "%{dist}" == ".el9"
python3 setup.py build
%else
python2 setup.py build
%endif
%endif
%install
%if "%{dist}" == ".el8"
python3 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
%else
%if "%{dist}" == ".el9"
python3 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
%else
python2 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
%endif
%endif
%clean
+1 -1
View File
@@ -37,7 +37,7 @@ alias nodereseat='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export C
alias noderun='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; noderun'
alias nodesensors='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodesensors'
alias nodesetboot='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodesetboot'
alias nodestorage='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodestorage'
alias nodestorage='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodestorage -i'
alias nodeshell='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeshell'
alias nodelicense='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodelicense'
# Do not continue for non-bash shells, the rest of this sets up bash completion functions
@@ -34,7 +34,13 @@ For the `groups` attribute, it is possible to add a group by doing
`groups,=<newgroup>` and to remove by doing `groups^=<oldgroup>`
Note that `nodeattrib <group>` will likely not provide the expected behavior.
See nodegroupattrib(8) command on how to manage attributes on a group level.
See nodegroupattrib(8) command on how to manage attributes on a group level. Running
nodeattrib on a group will simply set node-specific attributes on each individual
member of the group.
Additionally, if wanting to change from a node level attribute value to inherit from a
member group, it is required to use '-c' to clear the attribute. Conversely, assigning
to a blank value will allow masking a group defined attribute with an empty value.
## OPTIONS
+1 -1
View File
@@ -3,7 +3,7 @@ nodelicense(8) -- Manage license keys on BMC
## SYNOPSIS
`nodelicense <noderange> [list][install <filename>|save <directory>|delete <license>]`
`nodelicense <noderange> [list][install <license_filename>|save <directory>|delete <license_feature_name>]`
## DESCRIPTION
+3
View File
@@ -17,6 +17,9 @@ as stderr, unlike psh which combines all stdout and stderr into stdout.
* `-c COUNT`, `-f COUNT`, `--count=COUNT`
Specify the maximum number of instances to run concurrently
* `-l LOGINNAME`, `--loginname=LOGINNAME`
Username to use when connecting, defaults to current user.
* `-m MAXNODES`, `--maxnodes=MAXNODES`
Specify a maximum number of nodes to run remote ssh command to, prompting
if over the threshold
+11
View File
@@ -8,4 +8,15 @@ cd ..
tar Jcvf confluent_osdeploy.tar.xz confluent_osdeploy
mv confluent_osdeploy.tar.xz ~/rpmbuild/SOURCES/
cd -
mkdir -p el9bin/opt/confluent/bin
mkdir -p el9bin/stateless-bin
podman run --privileged --rm -v $(pwd)/utils:/buildutils -i -t fedorabuilder make -C /buildutils
cd utils
mv confluent_imginfo copernicus clortho autocons ../el9bin/opt/confluent/bin
mv start_root urlmount ../el9bin/stateless-bin/
make clean
cd ..
tar Jcvf confluent_el9bin.tar.xz el9bin/
mv confluent_el9bin.tar.xz ~/rpmbuild/SOURCES/
rm -rf el9bin
rpmbuild -ba confluent_osdeploy.spec
@@ -0,0 +1,424 @@
#!/usr/bin/python
try:
import http.client as client
except ImportError:
import httplib as client
import ctypes
import ctypes.util
import glob
import os
import select
import socket
import subprocess
import ssl
import sys
import struct
import time
class InvalidApiKey(Exception):
pass
def msg_align(len):
return (len + 3) & ~3
cryptname = ctypes.util.find_library('crypt')
if not cryptname:
if os.path.exists('/lib64/libcrypt.so.2') or os.path.exists('/usr/lib64/libcrypt.so.2'):
cryptname = 'libcrypt.so.2'
else:
cryptname = 'libcrypt.so.1'
c_libcrypt = ctypes.CDLL(cryptname)
c_crypt = c_libcrypt.crypt
c_crypt.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
c_crypt.restype = ctypes.c_char_p
def get_my_addresses():
nlhdrsz = struct.calcsize('IHHII')
ifaddrsz = struct.calcsize('BBBBI')
# RTM_GETADDR = 22
# nlmsghdr struct: u32 len, u16 type, u16 flags, u32 seq, u32 pid
nlhdr = struct.pack('IHHII', nlhdrsz + ifaddrsz, 22, 0x301, 0, 0)
# ifaddrmsg struct: u8 family, u8 prefixlen, u8 flags, u8 scope, u32 index
ifaddrmsg = struct.pack('BBBBI', 0, 0, 0, 0, 0)
s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
s.bind((0, 0))
s.sendall(nlhdr + ifaddrmsg)
addrs = []
while True:
pdata = s.recv(65536)
v = memoryview(pdata)
if struct.unpack('H', v[4:6])[0] == 3: # netlink done message
break
while len(v):
length, typ = struct.unpack('IH', v[:6])
if typ == 20:
fam, plen, _, scope, ridx = struct.unpack('BBBBI', v[nlhdrsz:nlhdrsz+ifaddrsz])
if scope in (253, 0):
rta = v[nlhdrsz+ifaddrsz:length]
while len(rta):
rtalen, rtatyp = struct.unpack('HH', rta[:4])
if rtalen < 4:
break
if rtatyp == 1:
addrs.append((fam, rta[4:rtalen], plen, ridx))
rta = rta[msg_align(rtalen):]
v = v[msg_align(length):]
return addrs
def scan_confluents():
srvs = {}
s6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
s6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s6.bind(('::', 1900))
s4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s4.bind(('0.0.0.0', 1900))
doneidxs = set([])
msg = 'M-SEARCH * HTTP/1.1\r\nST: urn:xcat.org:service:confluent:'
with open('/etc/confluent/confluent.deploycfg') as dcfg:
for line in dcfg.read().split('\n'):
if line.startswith('confluent_uuid:'):
confluentuuid = line.split(': ')[1]
msg += '/confluentuuid=' + confluentuuid
break
with open('/sys/devices/virtual/dmi/id/product_uuid') as uuidin:
msg += '/uuid=' + uuidin.read().strip()
for addrf in glob.glob('/sys/class/net/*/address'):
with open(addrf) as addrin:
hwaddr = addrin.read().strip()
msg += '/mac=' + hwaddr
msg = msg.encode('utf8')
for addr in get_my_addresses():
if addr[0] == socket.AF_INET6:
if addr[-1] in doneidxs:
continue
try:
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1])
except TypeError:
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1].tobytes())
try:
s6.sendto(msg, ('ff02::c', 1900))
except OSError:
pass
doneidxs.add(addr[-1])
elif addr[0] == socket.AF_INET:
try:
s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1])
except TypeError:
s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1].tobytes())
try:
s4.sendto(msg, ('239.255.255.250', 1900))
except OSError:
pass
r = select.select((s4, s6), (), (), 4)
srvlist = []
if r:
r = r[0]
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
rsp = rsp.split(b'\r\n')
current = None
for line in rsp:
if line.startswith(b'NODENAME: '):
current = {}
elif line.startswith(b'DEFAULTNET: 1'):
current['isdefault'] = True
elif line.startswith(b'MGTIFACE: '):
current['mgtiface'] = line.replace(b'MGTIFACE: ', b'').strip().decode('utf8')
if len(peer) > 2:
current['myidx'] = peer[-1]
srvs[peer[0]] = current
srvlist.append(peer[0])
r = select.select((s4, s6), (), (), 2)
if r:
r = r[0]
return srvlist, srvs
def get_net_apikey(nodename, mgr):
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./'
newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))])
salt = '$5$' + ''.join([alpha[x >> 2] for x in bytearray(os.urandom(8))])
newpass = newpass.encode('utf8')
salt = salt.encode('utf8')
crypted = c_crypt(newpass, salt)
for addrinfo in socket.getaddrinfo(mgr, 13001, 0, socket.SOCK_STREAM):
try:
clisock = socket.socket(addrinfo[0], addrinfo[1])
clisock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if addrinfo[0] == socket.AF_INET:
cliaddr = ('0.0.0.0', 302)
else:
cliaddr = ('::', 302)
clisock.bind(cliaddr)
clisock.connect(addrinfo[-1])
rsp = clisock.recv(8)
if rsp != b'\xc2\xd1-\xa8\x80\xd8j\xba':
raise Exception('Unrecognized credential banner')
hellostr = bytearray([1, len(nodename)]) + bytearray(nodename.encode('utf8')) + bytearray(b'\x00\x00')
clisock.send(hellostr)
rsp = bytearray(clisock.recv(2))
if not rsp:
continue
if rsp[0] == 128:
continue
if rsp[0] == 2:
echotoken = clisock.recv(rsp[1])
clisock.recv(2) # drain \x00\x00
clisock.send(bytes(bytearray([3, rsp[1]])))
clisock.send(echotoken)
clisock.send(bytes(bytearray([4, len(crypted)])))
clisock.send(crypted)
clisock.send(b'\x00\x00')
rsp = bytearray(clisock.recv(2))
if rsp[0] == 5:
return newpass.decode('utf8')
finally:
clisock.close()
return ''
def get_apikey(nodename, hosts, errout=None):
apikey = ""
if os.path.exists('/etc/confluent/confluent.apikey'):
apikey = open('/etc/confluent/confluent.apikey').read().strip()
if apikey:
return apikey
while not apikey:
for host in hosts:
try:
apikey = get_net_apikey(nodename, host)
except OSError:
apikey = None
if apikey:
break
else:
srvlist, _ = scan_confluents()
for host in srvlist:
try:
apikey = get_net_apikey(nodename, host)
except OSError:
apikey = None
if apikey:
break
if not apikey:
errmsg = "Failed getting API token, check deployment.apiarmed attribute on {}\n".format(nodename)
sys.stderr.write(errmsg)
if errout:
errout.write(errmsg)
time.sleep(10)
with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
apiout.write(apikey)
apikey = apikey.strip()
os.chmod('/etc/confluent/confluent.apikey', 0o600)
return apikey
class HTTPSClient(client.HTTPConnection, object):
def __init__(self, usejson=False, port=443, host=None, errout=None):
self.errout = None
if errout:
self.errout = open(errout, 'w')
self.errout.flush()
self.stdheaders = {}
mgtiface = None
if usejson:
self.stdheaders['ACCEPT'] = 'application/json'
if host:
self.hosts = [host]
with open('/etc/confluent/confluent.info') as cinfo:
info = cinfo.read().split('\n')
for line in info:
if line.startswith('NODENAME:'):
node = line.split(' ')[1]
self.stdheaders['CONFLUENT_NODENAME'] = node
else:
self.hosts = []
info = open('/etc/confluent/confluent.info').read().split('\n')
havedefault = '0'
for line in info:
if line.startswith('NODENAME:'):
node = line.split(' ')[1]
self.stdheaders['CONFLUENT_NODENAME'] = node
if line.startswith('MANAGER:') and not host:
host = line.split(' ')[1]
if line.startswith('EXTMGRINFO:'):
extinfo = line.split(' ')[1]
extinfo = extinfo.split('|')
if not mgtiface:
host, mgtiface, havedefault = extinfo[:3]
if havedefault == '0' and extinfo[2] == '1':
host, mgtiface, havedefault = extinfo[:3]
if '%' in host:
ifidx = host.split('%', 1)[1]
with open('/tmp/confluent.ifidx', 'w+') as ifout:
ifout.write(ifidx)
self.hosts.append(host)
try:
info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
except Exception:
info = None
if info:
for line in info:
if line.startswith('deploy_server: ') or line.startswith('deploy_server_v6: '):
self.hosts.append(line.split(': ', 1)[1])
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, self.hosts, errout=self.errout)
if mgtiface:
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
self.port = port
self.host = None
self.node = node
host = self.check_connections()
client.HTTPConnection.__init__(self, host, port)
self.connect()
def set_header(self, key, val):
self.stdheaders[key] = val
def check_connections(self):
foundsrv = None
hosts = self.hosts
for timeo in (0.1, 5):
for host in hosts:
try:
addrinf = socket.getaddrinfo(host, self.port)[0]
psock = socket.socket(addrinf[0])
psock.settimeout(timeo)
psock.connect(addrinf[4])
foundsrv = host
psock.close()
break
except OSError:
continue
else:
continue
break
if not foundsrv:
srvlist, srvs = scan_confluents()
hosts = []
for srv in srvlist:
if srvs[srv].get('isdefault', False):
hosts = [srv] + hosts
else:
hosts = hosts + [srv]
for host in hosts:
try:
addrinf = socket.getaddrinfo(host, self.port)[0]
psock = socket.socket(addrinf[0])
psock.settimeout(timeo)
psock.connect(addrinf[4])
foundsrv = host
psock.close()
break
except OSError:
continue
else:
raise Exception('Unable to reach any hosts')
return foundsrv
def connect(self):
addrinf = socket.getaddrinfo(self.host, self.port)[0]
psock = socket.socket(addrinf[0])
psock.connect(addrinf[4])
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.load_verify_locations('/etc/confluent/ca.pem')
host = self.host.split('%', 1)[0]
if '[' not in host and ':' in host:
self.stdheaders['Host'] = '[{0}]'.format(host)
else:
self.stdheaders['Host'] = '{0}'.format(host)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.check_hostname = True
try:
self.sock = ctx.wrap_socket(psock, server_hostname=host)
except ssl.SSLError:
errmsg = 'Error validating certificate on deployer (try `osdeploy initialize -t` on the deployment server {0})\n'.format(host)
sys.stderr.write(errmsg)
if self.errout:
self.errout.write(errmsg)
self.errout.flush()
sys.exit(1)
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:
method = 'GET'
authed = False
while not authed:
authed = True
self.request(method, url, data, headers=self.stdheaders)
rsp = self.getresponse()
if rsp.status >= 200 and rsp.status < 300:
if returnrsp:
return rsp.status, rsp
else:
return rsp.status, rsp.read()
if rsp.status == 401:
authed = False
rsp.read()
with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
akfile.write('')
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
self.node, [self.host], errout=self.errout)
raise Exception(rsp.read())
if __name__ == '__main__':
data = None
usejson = False
if '-j' in sys.argv:
usejson = True
if len(sys.argv) == 1:
HTTPSClient()
sys.exit(0)
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
try:
errout = sys.argv.index('-e')
sys.argv.pop(errout)
errout = sys.argv.pop(errout)
except ValueError:
errout = None
if outbin:
with open(outbin, 'ab+') as outf:
reader = HTTPSClient(usejson=usejson, errout=errout).grab_url(
sys.argv[1], data, returnrsp=True)
chunk = reader.read(16384)
while chunk:
outf.write(chunk)
chunk = reader.read(16384)
sys.exit(0)
if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
data = open(sys.argv[-1]).read()
if waitfor:
client = HTTPSClient(usejson, errout=errout)
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(usejson, errout=errout).grab_url(sys.argv[1], data).decode())
@@ -1,173 +0,0 @@
#!/usr/bin/python
try:
import http.client as client
except ImportError:
import httplib as client
import os
import socket
import subprocess
import ssl
import sys
import time
class InvalidApiKey(Exception):
pass
def get_apikey(nodename, mgr):
apikey = ""
if os.path.exists('/etc/confluent/confluent.apikey'):
apikey = open('/etc/confluent/confluent.apikey').read().strip()
if apikey:
return apikey
while not apikey:
try:
apikey = subprocess.check_output(['/opt/confluent/bin/clortho', nodename, mgr])
except subprocess.CalledProcessError:
apikey = ''
if not isinstance(apikey, str):
apikey = apikey.decode('utf8')
if apikey.startswith('SEALED:'):
apikey = ""
if not apikey:
sys.stderr.write(
"Failed getting API token, check deployment.apiarmed attribute on {}\n".format(nodename))
time.sleep(10)
with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
apiout.write(apikey)
apikey = apikey.strip()
os.chmod('/etc/confluent/confluent.apikey', 0o600)
return apikey
class HTTPSClient(client.HTTPConnection, object):
def __init__(self, json=False, port=443):
self.stdheaders = {}
info = open('/etc/confluent/confluent.info').read().split('\n')
host = None
mgtiface = None
havedefault = '0'
for line in info:
if line.startswith('NODENAME:'):
node = line.split(' ')[1]
self.stdheaders['CONFLUENT_NODENAME'] = node
if line.startswith('MANAGER:') and not host:
host = line.split(' ')[1]
if line.startswith('EXTMGRINFO:'):
extinfo = line.split(' ')[1]
extinfo = extinfo.split('|')
if not mgtiface:
host, mgtiface, havedefault = extinfo[:3]
if havedefault == '0' and extinfo[2] == '1':
host, mgtiface, havedefault = extinfo[:3]
if '%' in host:
ifidx = host.split('%', 1)[1]
with open('/tmp/confluent.ifidx', 'w+') as ifout:
ifout.write(ifidx)
if json:
self.stdheaders['ACCEPT'] = 'application/json'
try:
info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
except Exception:
info = None
if info:
for line in info:
if line.startswith('deploy_server: '):
host = line.split(': ', 1)[1]
break
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, host)
if mgtiface:
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
client.HTTPConnection.__init__(self, host, port)
self.host = host
self.node = node
self.connect()
def set_header(self, key, val):
self.stdheaders[key] = val
def connect(self):
addrinf = socket.getaddrinfo(self.host, self.port)[0]
psock = socket.socket(addrinf[0])
psock.connect(addrinf[4])
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.load_verify_locations('/etc/confluent/ca.pem')
host = self.host.split('%', 1)[0]
if '[' not in host and ':' in host:
self.stdheaders['Host'] = '[{0}]'.format(host)
else:
self.stdheaders['Host'] = '{0}'.format(host)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.check_hostname = True
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:
method = 'GET'
authed = False
while not authed:
authed = True
self.request(method, url, data, headers=self.stdheaders)
rsp = self.getresponse()
if rsp.status >= 200 and rsp.status < 300:
if returnrsp:
return rsp.status, rsp
else:
return rsp.status, rsp.read()
if rsp.status == 401:
authed = False
rsp.read()
with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
akfile.write('')
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
self.node, self.host)
raise Exception(rsp.read())
if __name__ == '__main__':
data = None
json = False
if '-j' in sys.argv:
json = True
if len(sys.argv) == 1:
HTTPSClient()
sys.exit(0)
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)
while chunk:
outf.write(chunk)
chunk = reader.read(16384)
sys.exit(0)
if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
data = open(sys.argv[-1]).read()
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())
@@ -0,0 +1,351 @@
#!/usr/bin/python
import glob
import json
import os
import socket
import sys
import time
import shlex
import subprocess
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 add_lla(iface, mac):
pieces = mac.split(':')
initbyte = int(pieces[0], 16) ^ 2
lla = 'fe80::{0:x}{1}:{2}ff:fe{3}:{4}{5}/64'.format(initbyte, pieces[1], pieces[2], pieces[3], pieces[4], pieces[5])
try:
subprocess.check_call(['ip', 'addr', 'add', 'dev', iface, lla, 'scope', 'link'])
except Exception:
return None
return lla
#cli = apiclient.HTTPSClient(json=True)
#c = cli.grab_url_with_status('/confluent-api/self/netcfg')
def add_missing_llas():
#NetworkManager goes out of its way to suppress ipv6 lla, so will just add some
added = {}
linkinfo = subprocess.check_output(['ip', '-br', 'l']).decode('utf8')
ifaces = {}
for line in linkinfo.split('\n'):
line = line.strip().split()
if not line or 'LOOPBACK' in line[-1] or 'NO-CARRIER' in line[-1]:
continue
if 'UP' not in line[-1]:
subprocess.call(['ip', 'link', 'set', line[0], 'up'])
ifaces[line[0]] = line[2]
ips = {}
ipinfo = subprocess.check_output(['ip', '-br', '-6', 'a']).decode('utf8')
for line in ipinfo.split('\n'):
line = line.strip().split(None, 2)
if not line:
continue
ips[line[0]] = line[2]
for iface in ifaces:
for addr in ips.get(iface, '').split():
if addr.startswith('fe80::'):
break
else:
newlla = add_lla(iface, ifaces[iface])
if newlla:
added[iface] = newlla
return added
def rm_tmp_llas(tmpllas):
for iface in tmpllas:
subprocess.check_call(['ip', 'addr', 'del', 'dev', iface, tmpllas[iface]])
def await_tentative():
maxwait = 10
while b'tentative' in subprocess.check_output(['ip', 'a']):
if maxwait == 0:
break
maxwait -= 1
time.sleep(1)
def map_idx_to_name():
map = {}
devtype = {}
prevdev = None
for line in subprocess.check_output(['ip', 'l']).decode('utf8').splitlines():
if line.startswith(' ') and 'link/' in line:
typ = line.split()[0].split('/')[1]
devtype[prevdev] = typ if type != 'ether' else 'ethernet'
if line.startswith(' '):
continue
idx, iface, rst = line.split(':', 2)
prevdev = iface.strip()
rst = rst.split()
try:
midx = rst.index('master')
continue
except ValueError:
pass
idx = int(idx)
iface = iface.strip()
map[idx] = iface
return map, devtype
def get_interface_name(iname, settings):
explicitname = settings.get('interface_names', None)
if explicitname:
return explicitname
if settings.get('current_nic', False):
return iname
return None
class WickedManager(object):
def __init__(self):
self.teamidx = 0
self.read_connections()
def read_connections(self):
self.cfgbydev = {}
for ifcfg in glob.glob('/etc/sysconfig/network/ifcfg-*'):
devname = ifcfg.replace('/etc/sysconfig/network/ifcfg-', '')
if devname == 'lo':
continue
currcfg = {}
self.cfgbydev[devname] = currcfg
for cfg in open(ifcfg).read().splitlines():
cfg = cfg.split('#', 1)[0]
kv = ' '.join(shlex.split(cfg)).split('=', 1)
if len(kv) != 2:
continue
k, v = kv
k = k.strip()
v = v.strip()
currcfg[k] = v
def apply_configuration(self, cfg):
stgs = cfg['settings']
ipcfg = 'STARTMODE=auto\n'
routecfg = ''
bootproto4 = stgs.get('ipv4_method', 'none')
bootproto6 = stgs.get('ipv6_method', 'none')
if bootproto4 == 'dhcp' and bootproto6 == 'dhcp':
ipcfg += 'BOOTPROTO=dhcp\n'
elif bootproto4 == 'dhcp':
ipcfg += 'BOOTPROTO=dhcp4\n'
elif bootproto6 == 'dhcp':
ipcfg += 'BOOTPROTO=dhcp6\n'
else:
ipcfg += 'BOOTPROTO=static\n'
if stgs.get('ipv4_address', None):
ipcfg += 'IPADDR=' + stgs['ipv4_address'] + '\n'
v4gw = stgs.get('ipv4_gateway', None)
if stgs.get('ipv6_address', None):
ipcfg += 'IPADDR_V6=' + stgs['ipv6_address'] + '\n'
v6gw = stgs.get('ipv6_gateway', None)
cname = None
if len(cfg['interfaces']) > 1: # creating new team
if not stgs.get('team_mode', None):
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
return
if not stgs.get('connection_name', None):
stgs['connection_name'] = 'team{0}'.format(self.teamidx)
self.teamidx += 1
cname = stgs['connection_name']
with open('/etc/sysconfig/network/ifcfg-{0}'.format(cname), 'w') as teamout:
teamout.write(ipcfg)
teamout.write('TEAM_RUNNER={0}\n'.format(stgs['team_mode']))
idx = 1
for iface in cfg['interfaces']:
subprocess.call(['wicked', 'ifdown', iface])
try:
os.remove('/etc/sysconfig/network/ifcfg-{0}'.format(iface))
os.remove('/etc/sysconfig/network/ifroute-{0}'.format(iface))
except OSError:
pass
teamout.write('TEAM_PORT_DEVICE_{0}={1}\n'.format(idx, iface))
idx += 1
else:
cname = list(cfg['interfaces'])[0]
priorcfg = self.cfgbydev.get(cname, {})
for cf in priorcfg:
if cf.startswith('TEAM_'):
ipcfg += '{0}={1}\n'.format(cf, priorcfg[cf])
with open('/etc/sysconfig/network/ifcfg-{0}'.format(cname), 'w') as iout:
iout.write(ipcfg)
if v4gw:
routecfg += 'default {0} - {1}\n'.format(v4gw, cname)
if v6gw:
routecfg += 'default {0} - {1}\n'.format(v6gw, cname)
if routecfg:
with open('/etc/sysconfig/network/ifroute-{0}'.format(cname), 'w') as routeout:
routeout.write(routecfg)
subprocess.call(['wicked', 'ifup', cname])
class NetworkManager(object):
def __init__(self, devtypes):
self.connections = {}
self.uuidbyname = {}
self.uuidbydev = {}
self.connectiondetail = {}
self.read_connections()
self.teamidx = 0
self.devtypes = devtypes
def read_connections(self):
self.connections = {}
self.uuidbyname = {}
self.uuidbydev = {}
self.connectiondetail = {}
ci = subprocess.check_output(['nmcli', '-t', 'c']).decode('utf8')
for inf in ci.splitlines():
n, u, t, dev = inf.split(':')
if n == 'NAME':
continue
if dev == '--':
dev = None
self.uuidbyname[n] = u
if dev:
self.uuidbydev[dev] = u
self.connections[u] = {'name': n, 'uuid': u, 'type': t, 'dev': dev}
deats = {}
for deat in subprocess.check_output(['nmcli', 'c', 's', u]).decode('utf8').splitlines():
k, v = deat.split(':', 1)
v = v.strip()
if v == '--':
continue
if '(default)' in v:
continue
deats[k] = v
self.connectiondetail[u] = deats
def add_team_member(self, team, member):
bondcfg = {}
if member in self.uuidbydev:
myuuid = self.uuidbydev[member]
deats = self.connectiondetail[myuuid]
currteam = deats.get('connection.master', None)
if currteam == team:
return
for stg in ('ipv4.dhcp-hostname', 'ipv4.dns', 'ipv6.dns', 'ipv6.dhcp-hostname'):
if deats.get(stg, None):
bondcfg[stg] = deats[stg]
if member in self.uuidbyname:
subprocess.check_call(['nmcli', 'c', 'del', self.uuidbyname[member]])
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'team-slave', 'master', team, 'con-name', member, 'connection.interface-name', member])
if bondcfg:
args = []
for parm in bondcfg:
args.append(parm)
args.append(bondcfg[parm])
subprocess.check_call(['nmcli', 'c', 'm', team] + args)
def apply_configuration(self, cfg):
cmdargs = {}
stgs = cfg['settings']
cmdargs['ipv6.method'] = stgs.get('ipv6_method', 'link-local')
if stgs.get('ipv6_address', None):
cmdargs['ipv6.addresses'] = stgs['ipv6_address']
cmdargs['ipv4.method'] = stgs.get('ipv4_method', 'disabled')
if stgs.get('ipv4_address', None):
cmdargs['ipv4.addresses'] = stgs['ipv4_address']
if stgs.get('ipv4_gateway', None):
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
if stgs.get('ipv6_gateway', None):
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
if len(cfg['interfaces']) > 1: # team time.. should be..
if not cfg['settings'].get('team_mode', None):
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
return
if not cfg['settings'].get('connection_name', None):
cfg['settings']['connection_name'] = 'team{0}'.format(self.teamidx)
self.teamidx += 1
cname = cfg['settings']['connection_name']
cargs = []
for arg in cmdargs:
cargs.append(arg)
cargs.append(cmdargs[arg])
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'team', 'con-name', cname, 'connection.interface-name', cname, 'team.runner', stgs['team_mode']] + cargs)
for iface in cfg['interfaces']:
self.add_team_member(cname, iface)
subprocess.check_call(['nmcli', 'c', 'u', cname])
else:
cname = stgs.get('connection_name', None)
iname = list(cfg['interfaces'])[0]
if not cname:
cname = iname
u = self.uuidbyname.get(cname, None)
cargs = []
for arg in cmdargs:
cargs.append(arg)
cargs.append(cmdargs[arg])
if u:
cmdargs['connection.interface-name'] = iname
subprocess.check_call(['nmcli', 'c', 'm', u] + cargs)
subprocess.check_call(['nmcli', 'c', 'u', u])
else:
subprocess.check_call(['nmcli', 'c', 'add', 'type', self.devtypes[iname], 'con-name', cname, 'connection.interface-name', iname] + cargs)
if __name__ == '__main__':
havefirewall = subprocess.call(['systemctl', 'status', 'firewalld'])
havefirewall = havefirewall == 0
if havefirewall:
subprocess.check_call(['systemctl', 'stop', 'firewalld'])
tmpllas = add_missing_llas()
await_tentative()
idxmap, devtypes = map_idx_to_name()
netname_to_interfaces = {}
myaddrs = apiclient.get_my_addresses()
srvs, _ = apiclient.scan_confluents()
doneidxs = set([])
for srv in srvs:
s = socket.create_connection((srv, 443))
myname = s.getsockname()
s.close()
if len(myname) == 4:
curridx = myname[-1]
else:
myname = myname[0]
myname = socket.inet_pton(socket.AF_INET, myname)
for addr in myaddrs:
if myname == addr[1].tobytes():
curridx = addr[-1]
if curridx in doneidxs:
continue
status, nc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/netcfg')
nc = json.loads(nc)
iname = get_interface_name(idxmap[curridx], nc.get('default', {}))
if iname:
if 'default' in netname_to_interfaces:
netname_to_interfaces['default']['interfaces'].add(iname)
else:
netname_to_interfaces['default'] = {'interfaces': set([iname]), 'settings': nc['default']}
for netname in nc.get('extranets', {}):
uname = '_' + netname
iname = get_interface_name(idxmap[curridx], nc['extranets'][netname])
if iname:
if uname in netname_to_interfaces:
netname_to_interfaces[uname]['interfaces'].add(iname)
else:
netname_to_interfaces[uname] = {'interfaces': set([iname]), 'settings': nc['extranets'][netname]}
doneidxs.add(curridx)
rm_tmp_llas(tmpllas)
if os.path.exists('/usr/bin/nmcli'):
nm = NetworkManager(devtypes)
elif os.path.exists('/usr/sbin/wicked'):
nm = WickedManager()
for netn in netname_to_interfaces:
nm.apply_configuration(netname_to_interfaces[netn])
if havefirewall:
subprocess.check_call(['systemctl', 'start', 'firewalld'])
await_tentative()
+31 -10
View File
@@ -6,6 +6,7 @@ Summary: OS Deployment support for confluent
License: Apache2
URL: https://hpc.lenovo.com/
Source0: confluent_osdeploy.tar.xz
Source1: confluent_el9bin.tar.xz
BuildArch: noarch
Requires: confluent_ipxe mtools tar
BuildRoot: /tmp
@@ -17,7 +18,7 @@ This contains support utilities for enabling deployment of x86_64 architecture s
%define debug_package %{nil}
%prep
%setup -n confluent_osdeploy
%setup -n confluent_osdeploy -a 1
%build
mkdir -p opt/confluent/bin
@@ -27,23 +28,37 @@ make all
cp confluent_imginfo copernicus clortho autocons ../opt/confluent/bin
cp start_root urlmount ../stateless-bin/
cd ..
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 coreos; do
ln -s el8 el9
ln -s el8-diskless el9-diskless
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 coreos el9; do
mkdir ${os}out
cd ${os}out
cp -a ../opt .
if [ -d ../${os}bin ]; then
cp -a ../${os}bin/opt .
else
cp -a ../opt .
fi
cp -a ../${os}/initramfs/* .
cp -a ../common/* .
cp -a ../common/initramfs/* .
find . | cpio -H newc -o > ../addons.cpio
mv ../addons.cpio .
cd ..
done
for os in el8 suse15; do
for os in el7 el8 suse15 el9 ubuntu20.04; do
mkdir ${os}disklessout
cd ${os}disklessout
cp -a ../opt .
if [ -d ../${os}bin ]; then
cp -a ../${os}bin/opt .
else
cp -a ../opt .
fi
cp -a ../${os}-diskless/initramfs/* .
cp -a ../common/* .
cp -a ../stateless-bin/* opt/confluent/bin
cp -a ../common/initramfs/* .
if [ -d ../${os}bin ]; then
cp -a ../${os}bin/stateless-bin/* opt/confluent/bin
else
cp -a ../stateless-bin/* opt/confluent/bin
fi
find . | cpio -H newc -o > ../addons.cpio
mv ../addons.cpio .
cd ..
@@ -52,7 +67,7 @@ mkdir esxi7out
cd esxi7out
cp -a ../opt .
cp -a ../esxi7/initramfs/* .
cp -a ../common/* .
cp -a ../common/initramfs/* .
chmod +x bin/* opt/confluent/bin/*
tar zcvf ../addons.tgz *
mv ../addons.tgz .
@@ -61,16 +76,22 @@ cp -a esxi7out esxi6out
cp -a esxi7 esxi6
%install
for os in rhvh4 el7 el8 genesis suse15 ubuntu20.04 esxi6 esxi7 coreos; do
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 esxi6 esxi7 coreos; do
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
cp -a $os/profiles/* %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
for targ in %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles/*; do
cp -a common/profile/* $targ
done
if [ -d ${os}disklessout ]; then
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/${os}-diskless/initramfs
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/${os}-diskless/profiles
cp ${os}disklessout/addons.* %{buildroot}/opt/confluent/lib/osdeploy/${os}-diskless/initramfs
cp -a ${os}-diskless/profiles/* %{buildroot}/opt/confluent/lib/osdeploy/$os-diskless/profiles
for targ in %{buildroot}/opt/confluent/lib/osdeploy/$os-diskless/profiles/*; do
cp -a common/profile/* $targ
done
fi
done
find %{buildroot}/opt/confluent/lib/osdeploy/ -name .gitignore -exec rm -f {} +
@@ -6,21 +6,29 @@ mkdir -p /etc/confluent
echo -n > /etc/confluent/confluent.info
umask $oum
cd /sys/class/net
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 60 ]; do
TRIES=$((TRIES + 1))
for currif in *; do
ip link set $currif up
echo "Searching for confluent deployment server...." > /dev/console
while ! grep ^NODE /etc/confluent/confluent.info; do
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 60 ]; do
TRIES=$((TRIES + 1))
for currif in *; do
ip link set $currif up
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
if ! grep ^NODE /etc/confluent/confluent.info; then
echo 'Current net config:' > /dev/console
ip -br a > /dev/console
fi
done
echo "Found confluent deployment services on local network" > /dev/console
cd /
grep ^EXTMGRINFO: /etc/confluent/confluent.info || return 0 # Do absolutely nothing if no data at all yet
echo -n "" > /tmp/confluent.initq
# restart cmdline
echo -n "" > /etc/cmdline.d/01-confluent.conf
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
cat /tls/*.pem > /etc/confluent/ca.pem
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info|head -n 1 | awk '{print $2}')
echo "Connecting to confluent server: $confluent_mgr" > /dev/console
if [[ $confluent_mgr == *%* ]]; then
echo $confluent_mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
ifidx=$(cat /tmp/confluent.ifidx)
@@ -33,13 +41,13 @@ umask 0077
while [ -z "$confluent_apikey" ]; do
/opt/confluent/bin/clortho $nodename $confluent_mgr > /etc/confluent/confluent.apikey
if grep ^SEALED: /etc/confluent/confluent.apikey > /dev/null; then
needseal=0
needseal=0
sed -e s/^SEALED:// /etc/confluent/confluent.apikey | clevis-decrypt-tpm2 > /etc/confluent/confluent.apikey.decrypt
mv /etc/confluent/confluent.apikey.decrypt /etc/confluent/confluent.apikey
fi
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
if [ -z "$confluent_apikey" ]; then
echo "Unable to acquire node api key, no TPM2 sealed nor fresh token available, retrying..."
echo "Unable to acquire node api key, no TPM2 sealed nor fresh token available, retrying..." > /dev/console
sleep 10
fi
done
@@ -53,6 +61,9 @@ if [ $needseal == 1 ]; then
fi
fi
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/profileprivate/pending/config.ign > /config.ign
[ -s /config.ign ] || rm /config.ign
umask $oldumask
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
@@ -91,6 +102,10 @@ done < /etc/confluent/confluent.deploycfg
if [ -e /lib/nm-lib.sh ]; then
. /lib/nm-lib.sh
nm_generate_connections
sed -i 's/method=disabled/method=link-local/' /run/NetworkManager/system-connections/*.nmconnection
if [ -f /run/NetworkManager/system-connections/$ifname.nmconnection ]; then
rm /run/NetworkManager/system-connections/default_connection.nmconnection
fi
if [[ "$ifname" == ib* ]]; then
sed -i s/type=ethernet/type=infiniband/ /run/NetworkManager/system-connections/$ifname.nmconnection
if ! grep '\[infiniband\]' /run/NetworkManager/system-connections/$ifname.nmconnection > /dev/null; then
@@ -100,7 +115,8 @@ if [ -e /lib/nm-lib.sh ]; then
fi
fi
fi
cat /proc/cmdline /etc/cmdline.d/01-confluent.conf | tr '\n' ' ' > /run/fakecmdline
mount -o bind /run/fakecmdline /proc/cmdline
#cat /proc/cmdline /etc/cmdline.d/01-confluent.conf | tr '\n' ' ' > /run/fakecmdline
#chcon system_u:object_r:proc_t:s0 /run/fakecmdline
#mount -o bind /run/fakecmdline /proc/cmdline
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/rootfs.img | rdcore stream-hash /etc/coreos-live-want-rootfs | bsdtar -xf - -C /
@@ -1,28 +1,93 @@
#!/bin/sh
# if ignition configuration is driving, defer to it
if [ ! -f /config.ign ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info| head -n 1| awk '{print $2}' | sed -e s/%/%25/)
if [[ $confluent_mgr = *:* ]]; then
confluent_mgr=[$confluent_mgr]
fi
rootpassword=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg)
rootpassword=${rootpassword#rootpassword: }
if [ "$rootpassword" = "null" ]; then
rootpassword=""
fi
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info| head -n 1| awk '{print $2}' | sed -e s/%/%25/)
if [[ $confluent_mgr = *:* ]]; then
confluent_mgr=[$confluent_mgr]
fi
if [ ! -z "$rootpassword" ]; then
sed -i "s@root:[^:]*:@root:$rootpassword:@" /sysroot/etc/shadow
fi
mkdir -p /sysroot/var/home/core/.ssh/
chmod 700 /sysroot/var/home/core/.ssh
cat /ssh/*.rootpubkey >> /sysroot/var/home/core/.ssh/authorized_keys
chmod 600 /sysroot/var/home/core/.ssh/authorized_keys
cp -a /etc/confluent /sysroot/etc/confluent
mkdir -p /sysroot/opt/confluent/bin/
cat > /sysroot/opt/confluent/bin/setssh.sh << 'EOF'
#!/bin/sh
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info| head -n 1| awk '{print $2}' | sed -e s/%/%25/)
if [[ $confluent_mgr = *:* ]]; then
confluent_mgr=[$confluent_mgr]
fi
mount -o bind /dev /sysroot/dev
chroot /sysroot ssh-keygen -A
umount /sysroot/dev
for i in /sysroot/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
echo HostKey ${i%.pub} | sed -e 's!/sysroot!!' >> /sysroot/etc/ssh/sshd_config
echo HostCertificate $certname | sed -e 's!/sysroot!!' >> /sysroot/etc/ssh/sshd_config
for i in /etc/ssh/ssh_host*key.pub; do
certname=${i/.pub/-cert.pub}
curl --cacert /etc/confluent/ca.pem -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$i https://$confluent_mgr/confluent-api/self/sshcert > $certname
if ! grep HostKey ${i%.pub} /etc/ssh/sshd_config > /dev/null; then
echo HostKey ${i%.pub} >> /etc/ssh/sshd_config
fi
if ! grep HostCertificate $certname /etc/ssh/sshd_config > /dev/null; then
echo HostCertificate $certname >> /etc/ssh/sshd_config
fi
done
EOF
chmod 755 /sysroot/opt/confluent/bin/setssh.sh
chcon system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/setssh.sh
cat > /sysroot/etc/systemd/system/confluent-ssh.service << EOF
[Service]
Type=oneshot
ExecStart=/opt/confluent/bin/setssh.sh
[Unit]
PartOf=sshd.service
Before=sshd.service
After=sshd-keygen.target
Wants=sshd-keygen.target
Requires=network-online.target
After=network-online.target
[Install]
WantedBy=sshd.service
EOF
mkdir -p /sysroot/etc/systemd/system/sshd.service.wants
ln -s /etc/systemd/system/confluent-ssh.service /sysroot/etc/systemd/system/sshd.service.wants/
ln -s /etc/systemd/system/confluent-onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/
cat > /sysroot/opt/confluent/bin/onboot.sh << 'EOF'
#!/bin/sh
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info| head -n 1| awk '{print $2}' | sed -e s/%/%25/)
if [[ $confluent_mgr = *:* ]]; then
confluent_mgr=[$confluent_mgr]
fi
curl -sf https://$confluent_mgr/confluent-public/os/${confluent_profile}/scripts/onboot.sh > /tmp/onboot.sh
[ -s /tmp/onboot.sh ] && . /tmp/onboot.sh
exit 0
EOF
chmod 755 /sysroot/opt/confluent/bin/onboot.sh
chcon system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/onboot.sh
cat > /sysroot/etc/systemd/system/confluent-onboot.service << EOF
[Service]
Type=oneshot
ExecStart=/opt/confluent/bin/onboot.sh
[Unit]
Requires=sshd.service
After=sshd.service
Requires=network-online.target
After=network-online.target
EOF
chcon -h system_u:object_r:systemd_unit_file_t:s0 /sysroot/etc/systemd/system/confluent-ssh.service /sysroot/etc/systemd/system/sshd.service.wants/confluent-ssh.service /sysroot/etc/systemd/system/confluent-onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/confluent-onboot.service
cp -a /opt/confluent/bin/* /sysroot/opt/confluent/bin/
chcon system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/*
@@ -5,9 +5,12 @@ DefaultDependencies=false
After=basic.target
# Network is enabled here
After=dracut-initqueue.service
Before=ignition-setup-user.service
Before=coreos-ignition-setup-user.service
Before=coreos-livepxe-rootfs.service
# If we fail, the boot will fail. Be explicit about it.
OnFailure=emergency.target
OnFailureJobMode=isolate
@@ -1,8 +1,14 @@
#!/bin/sh
profname=$(basename $2)
if file -L $1/images/ignition.img | grep gzip > /dev/null; then
mkdir -p /var/lib/confluent/private/os/${profname}/pending/
cd /var/lib/confluent/private/os/${profname}/pending/
zcat $1/images/ignition.img | cpio -dumiv
fi
ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \
ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution && \
ln -s $1/images/ignition.img $2/boot/initramfs/ignition.img && \
mkdir -p $2/boot/efi/boot/ && \
ln -s $1/images/pxeboot/rootfs.img $2/ && \
mcopy -i $1/images/efiboot.img ::efi/redhat/grubx64.efi $2/boot/efi/boot/ && \
mcopy -i $1/images/efiboot.img ::efi/boot/bootx64.efi $2/boot/efi/boot/
(mcopy -i $1/images/efiboot.img ::efi/redhat/grubx64.efi $2/boot/efi/boot/ || \
mcopy -i $1/images/efiboot.img ::efi/boot/grubx64.efi $2/boot/efi/boot/) && \
mcopy -i $1/images/efiboot.img ::efi/boot/bootx64.efi $2/boot/efi/boot/
@@ -0,0 +1,245 @@
get_remote_apikey() {
while [ -z "$confluent_apikey" ]; do
/opt/confluent/bin/clortho $nodename $confluent_mgr > /etc/confluent/confluent.apikey
if grep ^SEALED: /etc/confluent/confluent.apikey > /dev/null; then
# we don't support remote sealed api keys anymore
echo > /etc/confluent/confluent.apikey
fi
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
if [ -z "$confluent_apikey" ]; then
echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..."
sleep 10
elif [ -c /dev/tpmrm0 -a -x /usr/bin/tpm2_startauthsession ]; then
tmpdir=$(mktemp -d)
cd $tmpdir
tpm2_startauthsession --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
tpm2_createprimary -G ecc -Q --key-context=prim.ctx
(echo -n "CONFLUENT_APIKEY:";cat /etc/confluent/confluent.apikey) | tpm2_create -Q --policy=pcr15.sha256.policy --public=data.pub --private=data.priv -i - -C prim.ctx
tpm2_load -Q --parent-context=prim.ctx --public=data.pub --private=data.priv --name=confluent.apikey --key-context=data.ctx
tpm2_evictcontrol -Q -c data.ctx
tpm2_flushcontext session.ctx
cd - > /dev/null
rm -rf $tmpdir
fi
done
}
root=1
rootok=1
netroot=confluent
clear
mkdir -p /etc/ssh
mkdir -p /var/tmp/
mkdir -p /var/empty/sshd
mkdir -p /etc/confluent
sed -i '/^root:x/d' /etc/passwd
echo root:x:0:0::/:/bin/bash >> /etc/passwd
echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd
if ! grep console= /proc/cmdline >& /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
autocons=${autocons##*/}
echo "Automatic console configured for $autocons"
fi
echo "Initializing confluent diskless environment"
echo -n "udevd: "
/usr/lib/systemd/systemd-udevd --daemon
echo -n "Loading drivers..."
udevadm trigger
udevadm trigger --type=devices --action=add
udevadm settle
modprobe ib_ipoib
modprobe ib_umad
modprobe hfi1
modprobe mlx5_ib
echo "done"
cat > /etc/ssh/sshd_config << EOF
Port 2222
Subsystem sftp /usr/libexec/openssh/sftp-server
PermitRootLogin yes
AuthorizedKeysFile .ssh/authorized_keys
EOF
mkdir /root/.ssh
mkdir /.ssh
cat /ssh/*pubkey > /root/.ssh/authorized_keys 2>/dev/null
cp /root/.ssh/authorized_keys /.ssh/
cat /tls/*.pem > /etc/confluent/ca.pem
mkdir -p /etc/pki/tls/certs
cat /tls/*.pem > /etc/pki/tls/certs/ca-bundle.crt
TRIES=0
oldumask=$(umask)
umask 0077
tpmdir=$(mktemp -d)
cd $tpmdir
lasthdl=""
if [ -c /dev/tpmrm0 -a -x /usr/bin/tpm2_getcap ]; then
for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
tpm2_startauthsession --policy-session --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null)
tpm2_flushcontext session.ctx
if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then
confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:}
echo $confluent_apikey > /etc/confluent/confluent.apikey
if [ -n "$lasthdl" ]; then
tpm2_evictcontrol -c $lasthdl
fi
lasthdl=$hdl
fi
done
fi
cd - > /dev/null
rm -rf $tpmdir
touch /etc/confluent/confluent.info
cd /sys/class/net
echo -n "Scanning for network configuration..."
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 30 ]; do
TRIES=$((TRIES + 1))
for i in *; do
ip link set $i up
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
done
cd /
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
hostname $nodename
confluent_mgr=$(grep '^EXTMGRINFO:.*1$' /etc/confluent/confluent.info | head -n 1 | awk -F': ' '{print $2}' | awk -F'|' '{print $1}')
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info|head -n 1 | awk '{print $2}')
fi
if [[ $confluent_mgr == *%* ]]; then
echo $confluent_mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
fi
ready=0
while [ $ready = "0" ]; do
get_remote_apikey
if [[ $confluent_mgr == *:* ]]; then
confluent_mgr="[${confluent_mgr/\%/%25}]"
fi
tmperr=$(mktemp)
curl -gsSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg 2> $tmperr
if grep 401 $tmperr > /dev/null; then
confluent_apikey=""
if [ -n "$lasthdl" ]; then
tpm2_evictcontrol -c $lasthdl
fi
confluent_mgr=${confluent_mgr#[}
confluent_mgr=${confluent_mgr%]}
else
ready=1
fi
rm $tmperr
done
if [ ! -z "$autocons" ] && grep textconsole: true /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
if [ -c /dev/tpmrm0 -a -x /usr/bin/tpm2_pcrextend ]; then
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
fi
umask $oldumask
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
echo "Complete:"
ip addr show dev $ifname
else
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg)
v4addr=${v4addr#ipv4_address: }
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg)
v4gw=${v4gw#ipv4_gateway: }
if [ "$v4gw" = "null" ]; then
v4gw=""
fi
v4nm=$(grep ^prefix: /etc/confluent/confluent.deploycfg)
v4nm=${v4nm#prefix: }
echo "Setting up $ifname as static at $v4addr/$v4nm"
ip addr add dev $ifname $v4addr/$v4nm
if [ ! -z "$v4gw" ]; then
ip route add default via $v4gw
fi
mkdir -p /run/NetworkManager/system-connections
cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
[connection]
EOC
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
type=ethernet
autoconnect-retries=1
EOC
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
multi-connect=1
permissions=
wait-device-timeout=60000
[ethernet]
mac-address-blacklist=
[ipv4]
EOC
echo address1=$v4addr/$v4nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
if [ ! -z "$v4gw" ]; then
echo gateway=$v4gw >> /run/NetworkManager/system-connections/$ifname.nmconnection
fi
nameserversec=0
nameservers=""
while read -r entry; do
if [ $nameserversec = 1 ]; then
if [[ $entry == "-"* ]]; then
nameservers="$nameservers"${entry#- }";"
continue
fi
fi
nameserversec=0
if [ "${entry%:*}" = "nameservers" ]; then
nameserversec=1
continue
fi
done < /etc/confluent/confluent.deploycfg
echo dns=$nameservers >> /run/NetworkManager/system-connections/$ifname.nmconnection
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
dnsdomain=${dnsdomain#dnsdomain: }
echo dns-search=$dnsdomain >> /run/NetworkManager/system-connections/$ifname.nmconnection
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
may-fail=false
method=manual
[ipv6]
addr-gen-mode=eui64
method=auto
[proxy]
EOC
fi
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
echo -n "Initializing ssh..."
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
privfile=${pubkey%.pub}
curl -gsf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile
if [ -s $certfile ]; then
echo HostCertificate $certfile >> /etc/ssh/sshd_config
fi
echo HostKey $privfile >> /etc/ssh/sshd_config
done
/usr/sbin/sshd
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg| awk '{print $2}')
confluent_proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg| awk '{print $2}')
confluent_urls=""
for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed -e s/%/%25/); do
if [[ $addr == *:* ]]; then
confluent_urls="$confluent_urls $confluent_proto://[$addr]/confluent-public/os/$confluent_profile/rootimg.sfs"
else
confluent_urls="$confluent_urls $confluent_proto://$addr/confluent-public/os/$confluent_profile/rootimg.sfs"
fi
done
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
mkdir -p /etc/confluent
curl -gsf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
. /etc/confluent/functions
source_remote imageboot.sh
@@ -0,0 +1,4 @@
. /etc/confluent/functions
# 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.
@@ -0,0 +1,11 @@
[Unit]
Description=First Boot Process
Requires=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/confluent/bin/firstboot.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,40 @@
#!/bin/sh
# This script is executed on the first boot after install has
# completed. It is best to edit the middle of the file as
# noted below so custom commands are executed before
# the script notifies confluent that install is fully complete.
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
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
chmod 600 /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
while ! ping -c 1 $confluent_mgr >& /dev/null; do
sleep 1
done
if [ ! -f /etc/confluent/firstboot.ran ]; then
touch /etc/confluent/firstboot.ran
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
# 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
@@ -0,0 +1,196 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
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 /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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 /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ -x /usr/bin/chcon ]; then
chcon system_u:object_r:bin_t:s0 $cmd
fi
shift
./$cmd $*
retcode=$?
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
run_remote_python() {
echo
set_confluent_vars
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -0,0 +1,93 @@
import subprocess
import os
class DiskInfo(object):
def __init__(self, devname):
self.name = devname
self.wwn = None
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.mdcontainer = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
if not isinstance(qprop, str):
qprop = qprop.decode('utf8')
for prop in qprop.split('\n'):
if '=' not in prop:
continue
k, v = prop.split('=', 1)
if k == 'DEVTYPE' and v != 'disk':
raise Exception('Not a disk')
elif k == 'DM_NAME':
raise Exception('Device Mapper')
elif k == 'ID_MODEL':
self.model = v
elif k == 'DEVPATH':
self.path = v
elif k == 'ID_WWN':
self.wwn = v
elif k == 'MD_CONTAINER':
self.mdcontainer = v
attrs = subprocess.check_output(['udevadm', 'info', '-a', devnode])
if not isinstance(attrs, str):
attrs = attrs.decode('utf8')
for attr in attrs.split('\n'):
if '==' not in attr:
continue
k, v = attr.split('==', 1)
k = k.strip()
if k == 'ATTRS{size}':
self.size = v.replace('"', '')
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
self.size = int(sizesrc.read()) * 512
if int(self.size) < 536870912:
raise Exception("Device too small for install")
@property
def priority(self):
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
if self.driver == 'ahci':
return 2
if self.driver.startswith('megaraid'):
return 3
if self.driver.startswith('mpt'):
return 4
return 99
def __repr__(self):
return repr({
'name': self.name,
'path': self.path,
'wwn': self.wwn,
'driver': self.driver,
'size': self.size,
'model': self.model,
})
def main():
disks = []
for disk in sorted(os.listdir('/sys/class/block')):
try:
disk = DiskInfo(disk)
disks.append(disk)
except Exception as e:
print("Skipping {0}: {1}".format(disk, str(e)))
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
if nd:
open('/tmp/installdisk', 'w').write(nd[0])
if __name__ == '__main__':
main()
@@ -0,0 +1,376 @@
#!/usr/bin/python3
import glob
import json
import os
import re
import time
import shutil
import socket
import stat
import struct
import sys
import subprocess
def get_next_part_meta(img, imgsize):
if img.tell() == imgsize:
return None
pathlen = struct.unpack('!H', img.read(2))[0]
mountpoint = img.read(pathlen).decode('utf8')
jsonlen = struct.unpack('!I', img.read(4))[0]
metadata = json.loads(img.read(jsonlen).decode('utf8'))
img.seek(16, 1) # skip the two 64-bit values we don't use, they are in json
nextlen = struct.unpack('!H', img.read(2))[0]
img.seek(nextlen, 1) # skip filesystem type
nextlen = struct.unpack('!H', img.read(2))[0]
img.seek(nextlen, 1) # skip orig devname (redundant with json)
nextlen = struct.unpack('!H', img.read(2))[0]
img.seek(nextlen, 1) # skip padding
nextlen = struct.unpack('!Q', img.read(8))[0]
img.seek(nextlen, 1) # go to next section
return metadata
def get_multipart_image_meta(img):
img.seek(0, 2)
imgsize = img.tell()
img.seek(16)
seekamt = img.read(1)
img.seek(struct.unpack('B', seekamt)[0], 1)
partinfo = get_next_part_meta(img, imgsize)
while partinfo:
yield partinfo
partinfo = get_next_part_meta(img, imgsize)
def get_image_metadata(imgpath):
with open(imgpath, 'rb') as img:
header = img.read(16)
if header == b'\x63\x7b\x9d\x26\xb7\xfd\x48\x30\x89\xf9\x11\xcf\x18\xfd\xff\xa1':
for md in get_multipart_image_meta(img):
yield md
else:
raise Exception('Installation from single part image not supported')
class PartedRunner():
def __init__(self, disk):
self.disk = disk
def run(self, command):
command = command.split()
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
return subprocess.check_output(command).decode('utf8')
def fixup(rootdir, vols):
devbymount = {}
for vol in vols:
devbymount[vol['mount']] = vol['targetdisk']
fstabfile = os.path.join(rootdir, 'etc/fstab')
with open(fstabfile) as tfile:
fstab = tfile.read().split('\n')
while not fstab[0]:
fstab = fstab[1:]
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
os.unlink(os.path.join(rootdir, '.autorelabel'))
with open(fstabfile, 'w') as tfile:
for tab in fstab:
entry = tab.split()
if tab.startswith('#ORIGFSTAB#'):
if entry[1] in devbymount:
targetdev = devbymount[entry[1]]
if targetdev.startswith('/dev/localstorage/'):
entry[0] = targetdev
else:
uuid = subprocess.check_output(['blkid', '-s', 'UUID', '-o', 'value', targetdev]).decode('utf8')
uuid = uuid.strip()
entry[0] = 'UUID={}'.format(uuid)
elif entry[2] == 'swap':
entry[0] = '/dev/mapper/localstorage-swap'
entry[0] = entry[0].ljust(42)
entry[1] = entry[1].ljust(16)
entry[3] = entry[3].ljust(28)
tab = '\t'.join(entry)
tfile.write(tab + '\n')
with open(os.path.join(rootdir, 'etc/hostname'), 'w') as nameout:
nameout.write(socket.gethostname() + '\n')
selinuxconfig = os.path.join(rootdir, 'etc/selinux/config')
policy = None
if os.path.exists(selinuxconfig):
with open(selinuxconfig) as cfgin:
sec = cfgin.read().split('\n')
for l in sec:
l = l.split('#', 1)[0]
if l.startswith('SELINUXTYPE='):
_, policy = l.split('=')
for sshkey in glob.glob(os.path.join(rootdir, 'etc/ssh/*_key*')):
os.unlink(sshkey)
for sshkey in glob.glob('/etc/ssh/*_key*'):
newkey = os.path.join(rootdir, sshkey[1:])
shutil.copy2(sshkey, newkey)
finfo = os.stat(sshkey)
os.chown(newkey, finfo[stat.ST_UID], finfo[stat.ST_GID])
for ifcfg in glob.glob(os.path.join(rootdir, 'etc/sysconfig/network-scripts/*')):
os.unlink(ifcfg)
for ifcfg in glob.glob(os.path.join(rootdir, 'etc/NetworkManager/system-connections/*')):
os.unlink(ifcfg)
for ifcfg in glob.glob('/run/NetworkManager/system-connections/*'):
newcfg = ifcfg.split('/')[-1]
newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg))
shutil.copy2(ifcfg, newcfg)
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
if policy:
sys.stdout.write('Applying SELinux labeling...')
sys.stdout.flush()
subprocess.check_call(['setfiles', '-r', rootdir, os.path.join(rootdir, 'etc/selinux/{}/contexts/files/file_contexts'.format(policy)), os.path.join(rootdir, 'etc')])
subprocess.check_call(['setfiles', '-r', rootdir, os.path.join(rootdir, 'etc/selinux/{}/contexts/files/file_contexts'.format(policy)), os.path.join(rootdir, 'opt')])
sys.stdout.write('Done\n')
sys.stdout.flush()
for metafs in ('proc', 'sys', 'dev'):
subprocess.check_call(['mount', '-o', 'bind', '/{}'.format(metafs), os.path.join(rootdir, metafs)])
with open(os.path.join(rootdir, 'etc/sysconfig/grub')) as defgrubin:
defgrub = defgrubin.read().split('\n')
with open(os.path.join(rootdir, 'etc/sysconfig/grub'), 'w') as defgrubout:
for gline in defgrub:
gline = gline.split()
newline = []
for ent in gline:
if ent.startswith('resume=') or ent.startswith('rd.lvm.lv'):
continue
newline.append(ent)
defgrubout.write(' '.join(newline) + '\n')
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/')
subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg])
newroot = None
with open('/etc/shadow') as shadowin:
shents = shadowin.read().split('\n')
for shent in shents:
shent = shent.split(':')
if not shent:
continue
if shent[0] == 'root' and shent[1] not in ('*', '!!', ''):
newroot = shent[1]
if newroot:
shlines = None
with open(os.path.join(rootdir, 'etc/shadow')) as oshadow:
shlines = oshadow.read().split('\n')
with open(os.path.join(rootdir, 'etc/shadow'), 'w') as oshadow:
for line in shlines:
if line.startswith('root:'):
line = line.split(':')
line[1] = newroot
line = ':'.join(line)
oshadow.write(line + '\n')
partnum = None
targblock = None
for vol in vols:
if vol['mount'] == '/boot/efi':
targdev = vol['targetdisk']
partnum = re.search('(\d+)$', targdev).group(1)
targblock = re.search('(.*)\d+$', targdev).group(1)
if targblock:
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
#other network interfaces
def had_swap():
with open('/etc/fstab') as tabfile:
tabs = tabfile.read().split('\n')
for tab in tabs:
tab = tab.split()
if len(tab) < 3:
continue
if tab[2] == 'swap':
return True
return False
def install_to_disk(imgpath):
lvmvols = {}
deftotsize = 0
mintotsize = 0
deflvmsize = 0
minlvmsize = 0
biggestsize = 0
biggestfs = None
plainvols = {}
allvols = []
swapsize = 0
if had_swap():
with open('/proc/meminfo') as meminfo:
swapsize = meminfo.read().split('\n')[0]
swapsize = int(swapsize.split()[1])
if swapsize < 2097152:
swapsize = swapsize * 2
elif swapsize > 8388608 and swapsize < 67108864:
swapsize = swapsize * 0.5
elif swapsize >= 67108864:
swapsize = 33554432
swapsize = int(swapsize * 1024)
deftotsize = swapsize
mintotsize = swapsize
for fs in get_image_metadata(imgpath):
allvols.append(fs)
deftotsize += fs['initsize']
mintotsize += fs['minsize']
if fs['initsize'] > biggestsize:
biggestfs = fs
biggestsize = fs['initsize']
if fs['device'].startswith('/dev/mapper'):
lvmvols[fs['device'].replace('/dev/mapper/', '')] = fs
deflvmsize += fs['initsize']
minlvmsize += fs['minsize']
else:
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
with open('/tmp/installdisk') as diskin:
instdisk = diskin.read()
instdisk = '/dev/' + instdisk
parted = PartedRunner(instdisk)
dinfo = parted.run('unit s print')
dinfo = dinfo.split('\n')
sectors = 0
sectorsize = 0
for inf in dinfo:
if inf.startswith('Disk {0}:'.format(instdisk)):
_, sectors = inf.split(': ')
sectors = int(sectors.replace('s', ''))
if inf.startswith('Sector size (logical/physical):'):
_, sectorsize = inf.split(':')
sectorsize = sectorsize.split('/')[0]
sectorsize = sectorsize.replace('B', '')
sectorsize = int(sectorsize)
# for now, only support resizing/growing the largest partition
minexcsize = deftotsize - biggestfs['initsize']
mintotsize = deftotsize - biggestfs['initsize'] + biggestfs['minsize']
minsectors = mintotsize // sectorsize
if sectors < (minsectors + 65536):
raise Exception('Disk too small to fit image')
biggestsectors = sectors - (minexcsize // sectorsize)
biggestsize = sectorsize * biggestsectors
parted.run('mklabel gpt')
curroffset = 2048
for volidx in sorted(plainvols):
vol = plainvols[volidx]
if vol is not biggestfs:
size = vol['initsize'] // sectorsize
else:
size = biggestsize // sectorsize
size += 2047 - (size % 2048)
end = curroffset + size
if end > sectors:
end = sectors
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
curroffset += size + 1
if not lvmvols:
if swapsize:
swapsize = swapsize // sectorsize
swapsize += 2047 - (size % 2048)
end = curroffset + swapsize
if end > sectors:
end = sectors
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
else:
parted.run('mkpart lvm {}s 100%'.format(curroffset))
lvmpart = instdisk + '{}'.format(volidx + 1)
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
vginfo = vginfo.split('\n')
pesize = 0
pes = 0
for infline in vginfo:
infline = infline.split()
if len(infline) >= 3 and infline[:2] == ['PE', 'Size']:
pesize = int(infline[2])
if len(infline) >= 5 and infline[:2] == ['Free', 'PE']:
pes = int(infline[4])
takeaway = swapsize // pesize
for volidx in lvmvols:
vol = lvmvols[volidx]
if vol is biggestfs:
continue
takeaway += vol['initsize'] // pesize
takeaway += 1
biggestextents = pes - takeaway
for volidx in lvmvols:
vol = lvmvols[volidx]
if vol is biggestfs:
extents = biggestextents
else:
extents = vol['initsize'] // pesize
extents += 1
if vol['mount'] == '/':
lvname = 'root'
else:
lvname = vol['mount'].replace('/', '_')
subprocess.check_call(['lvcreate', '-l', '{}'.format(extents), '-y', '-n', lvname, 'localstorage'])
vol['targetdisk'] = '/dev/localstorage/{}'.format(lvname)
if swapsize:
subprocess.check_call(['lvcreate', '-y', '-l', '{}'.format(swapsize // pesize), '-n', 'swap', 'localstorage'])
subprocess.check_call(['mkswap', '/dev/localstorage/swap'])
os.makedirs('/run/imginst/targ')
for vol in allvols:
with open(vol['targetdisk'], 'wb') as partition:
partition.write(b'\x00' * 1 * 1024 * 1024)
subprocess.check_call(['mkfs.{}'.format(vol['filesystem']), vol['targetdisk']])
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
source = vol['mount'].replace('/', '_')
source = '/run/imginst/sources/' + source
blankfsstat = os.statvfs('/run/imginst/targ')
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
with subprocess.Popen(['cp', '-ax', source + '/.', '/run/imginst/targ']) as copier:
stillrunning = copier.poll()
lastprogress = 0.0
while stillrunning is None:
currfsstat = os.statvfs('/run/imginst/targ')
currused = (currfsstat.f_blocks - currfsstat.f_bfree) * currfsstat.f_bsize
currused -= blankused
with open('/proc/meminfo') as meminf:
for line in meminf.read().split('\n'):
if line.startswith('Dirty:'):
_, dirty, _ = line.split()
dirty = int(dirty) * 1024
progress = (currused - dirty) / vol['minsize']
if progress < lastprogress:
progress = lastprogress
if progress > 0.99:
progress = 0.99
lastprogress = progress
progress = progress * 100
sys.stdout.write('\x1b[1K\rWriting {0}: {1:3.2f}%'.format(vol['mount'], progress))
sys.stdout.flush()
time.sleep(0.5)
stillrunning = copier.poll()
if stillrunning != 0:
raise Exception("Error copying volume")
with subprocess.Popen(['sync']) as syncrun:
stillrunning = syncrun.poll()
while stillrunning is None:
with open('/proc/meminfo') as meminf:
for line in meminf.read().split('\n'):
if line.startswith('Dirty:'):
_, dirty, _ = line.split()
dirty = int(dirty) * 1024
progress = (vol['minsize'] - dirty) / vol['minsize']
if progress < lastprogress:
progress = lastprogress
if progress > 0.99:
progress = 0.99
lastprogress = progress
progress = progress * 100
sys.stdout.write('\x1b[1K\rWriting {0}: {1:3.2f}%'.format(vol['mount'], progress))
sys.stdout.flush()
time.sleep(0.5)
stillrunning = syncrun.poll()
sys.stdout.write('\x1b[1K\rDone writing {0}'.format(vol['mount']))
sys.stdout.write('\n')
sys.stdout.flush()
subprocess.check_call(['umount', '/run/imginst/targ'])
for vol in allvols:
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
fixup('/run/imginst/targ', allvols)
if __name__ == '__main__':
install_to_disk(os.environ['mountsrc'])
@@ -0,0 +1,128 @@
. /lib/dracut-lib.sh
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
if [ "untethered" = "$(getarg confluent_imagemethod)" ]; then
mount -t tmpfs untethered /mnt/remoteimg
curl https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs
else
confluent_urls="$confluent_urls https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs"
modprobe fuse
/opt/confluent/bin/urlmount $confluent_urls /mnt/remoteimg
fi
/opt/confluent/bin/confluent_imginfo /mnt/remoteimg/rootimg.sfs > /tmp/rootimg.info
modprobe loop
loopdev=$(losetup -f)
export mountsrc=$loopdev
losetup -r $loopdev /mnt/remoteimg/rootimg.sfs
if grep '^Format: confluent_crypted' /tmp/rootimg.info > /dev/null; then
while ! curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key; do
echo "Unable to retrieve private key from $confluent_mgr (verify that confluent can access /var/lib/confluent/private/$confluent_profile/pending/rootimg.key)"
sleep 1
done
cipher=$(head -n 1 /tmp/rootimg.key)
key=$(tail -n 1 /tmp/rootimg.key)
len=$(wc -c /mnt/remoteimg/rootimg.sfs | awk '{print $1}')
len=$(((len-4096)/512))
dmsetup create cryptimg --table "0 $len crypt $cipher $key 0 $loopdev 8"
/opt/confluent/bin/confluent_imginfo /dev/mapper/cryptimg > /tmp/rootimg.info
mountsrc=/dev/mapper/cryptimg
fi
if grep '^Format: squashfs' /tmp/rootimg.info > /dev/null; then
mount -o ro $mountsrc /mnt/remote
elif grep '^Format: confluent_multisquash' /tmp/rootimg.info; then
tail -n +3 /tmp/rootimg.info | awk '{gsub("/", "_"); print "echo 0 " $4 " linear '$mountsrc' " $3 " | dmsetup create mproot" $7}' > /tmp/setupmount.sh
. /tmp/setupmount.sh
cat /tmp/setupmount.sh |awk '{printf "mount /dev/mapper/"$NF" "; sub("mproot", ""); gsub("_", "/"); print "/mnt/remote"$NF}' > /tmp/mountparts.sh
. /tmp/mountparts.sh
fi
#mount -t tmpfs overlay /mnt/overlay
modprobe zram
memtot=$(grep ^MemTotal: /proc/meminfo|awk '{print $2}')
memtot=$((memtot/2))$(grep ^MemTotal: /proc/meminfo | awk '{print $3'})
echo $memtot > /sys/block/zram0/disksize
mkfs.xfs /dev/zram0 > /dev/null
mount -o discard /dev/zram0 /mnt/overlay
if [ ! -f /tmp/mountparts.sh ]; then
mkdir -p /mnt/overlay/upper /mnt/overlay/work
mount -t overlay -o upperdir=/mnt/overlay/upper,workdir=/mnt/overlay/work,lowerdir=/mnt/remote disklessroot /sysroot
else
for srcmount in $(cat /tmp/mountparts.sh | awk '{print $3}'); do
mkdir -p /mnt/overlay${srcmount}/upper /mnt/overlay${srcmount}/work
mount -t overlay -o upperdir=/mnt/overlay${srcmount}/upper,workdir=/mnt/overlay${srcmount}/work,lowerdir=${srcmount} disklesspart /sysroot${srcmount#/mnt/remote}
done
fi
mkdir -p /sysroot/etc/ssh
mkdir -p /sysroot/etc/confluent
mkdir -p /sysroot/root/.ssh
cp /root/.ssh/* /sysroot/root/.ssh
chmod 700 /sysroot/root/.ssh
cp /etc/confluent/* /sysroot/etc/confluent/
cp /etc/ssh/*key* /sysroot/etc/ssh/
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
privfile=${pubkey%.pub}
if [ -s $certfile ]; then
echo HostCertificate $certfile >> /sysroot/etc/ssh/sshd_config
fi
echo HostKey $privfile >> /sysroot/etc/ssh/sshd_config
done
mkdir -p /sysroot/dev /sysroot/sys /sysroot/proc /sysroot/run
if [ ! -z "$autocons" ]; then
autocons=${autocons%,*}
mkdir -p /run/systemd/generator/getty.target.wants
ln -s /usr/lib/systemd/system/serial-getty@.service /run/systemd/generator/getty.target.wants/serial-getty@${autocons}.service
fi
while [ ! -e /sysroot/sbin/init ]; do
echo "Failed to access root filesystem or it is missing /sbin/init"
echo "System should be accessible through ssh at port 2222 with the appropriate key"
while [ ! -e /sysroot/sbin/init ]; do
sleep 1
done
done
rootpassword=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg)
rootpassword=${rootpassword#rootpassword: }
if [ "$rootpassword" = "null" ]; then
rootpassword=""
fi
if [ ! -z "$rootpassword" ]; then
sed -i "s@root:[^:]*:@root:$rootpassword:@" /sysroot/etc/shadow
fi
for i in /ssh/*.ca; do
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
done
echo HostbasedAuthentication yes >> /sysroot/etc/ssh/sshd_config
echo HostbasedUsesNameFromPacketOnly yes >> /sysroot/etc/ssh/sshd_config
echo IgnoreRhosts no >> /sysroot/etc/ssh/sshd_config
sshconf=/sysroot/etc/ssh/ssh_config
if [ -d /sysroot/etc/ssh/ssh_config.d/ ]; then
sshconf=/sysroot/etc/ssh/ssh_config.d/01-confluent.conf
fi
echo 'Host *' >> $sshconf
echo ' HostbasedAuthentication yes' >> $sshconf
echo ' EnableSSHKeysign yes' >> $sshconf
echo ' HostbasedKeyTypes *ed25519*' >> $sshconf
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /sysroot/etc/ssh/shosts.equiv
cp /sysroot/etc/ssh/shosts.equiv /sysroot/root/.shosts
chmod 640 /sysroot/etc/ssh/*_key
chroot /sysroot chgrp ssh_keys /etc/ssh/*_key
cp /tls/*.pem /sysroot/etc/pki/ca-trust/source/anchors/
chroot /sysroot/ update-ca-trust
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.service > /sysroot/etc/systemd/system/onboot.service
mkdir -p /sysroot/opt/confluent/bin
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh > /sysroot/opt/confluent/bin/onboot.sh
chmod +x /sysroot/opt/confluent/bin/onboot.sh
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service
cp /etc/confluent/functions /sysroot/etc/confluent/functions
if grep installtodisk /proc/cmdline > /dev/null; then
. /etc/confluent/functions
run_remote installimage
exec reboot -f
fi
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
exec /opt/confluent/bin/start_root
@@ -0,0 +1,43 @@
#!/bin/bash
. /etc/confluent/functions
# the image will be used to deploy itself
# provide both access to image (for parsing metadata)
# and existing mounts of image (to take advantage of caching)
mount -o bind /sys /sysroot/sys
mount -o bind /dev /sysroot/dev
mount -o bind /proc /sysroot/proc
mount -o bind /run /sysroot/run
if [ ! -f /tmp/mountparts.sh ]; then
mkdir -p /sysroot/run/imginst/sources/_
mount -o bind /mnt/remote /sysroot/run/imginst/sources/_
else
for srcmount in $(cat /tmp/mountparts.sh | awk '{print $2}'); do
srcname=${srcmount#/dev/mapper/mproot}
srcdir=$(echo $srcmount | sed -e 's!/dev/mapper/mproot!/mnt/remote!' -e 's!_!/!g')
mkdir -p /sysroot/run/imginst/sources/$srcname
mount -o bind $srcdir /sysroot/run/imginst/sources/$srcname
done
fi
cd /sysroot/run
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python getinstalldisk"
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_parts pre.d"
if [ ! -f /sysroot/tmp/installdisk ]; then
echo 'Unable to find a suitable installation target device, ssh to port 2222 to investigate'
while [ ! -f /sysroot/tmp/installdisk ]; do
sleep 1
done
fi
lvm vgchange -a n
udevadm control -e
chroot /sysroot /usr/lib/systemd/systemd-udevd --daemon
chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python image2disk.py"
echo "Port 22" >> /etc/ssh/sshd_config
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
kill -HUP $(cat /run/sshd.pid)
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
@@ -0,0 +1,11 @@
[Unit]
Description=Confluent onboot hook
Requires=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/confluent/bin/onboot.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,33 @@
#!/bin/sh
# This script is executed on each boot as it is
# completed. It is best to edit the middle of the file as
# noted below so custom commands are executed before
# the script notifies confluent that install is fully complete.
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
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
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-onboot.log
exec 2>> /var/log/confluent/confluent-onboot.log
chmod 600 /var/log/confluent/confluent-onboot.log
tail -f /var/log/confluent/confluent-onboot.log > /dev/console &
logshowpid=$!
run_remote_python syncfileclient
run_remote_python confignet
run_remote onboot.custom
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
run_remote_parts onboot.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/
run_remote_config onboot.d
#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
kill $logshowpid
@@ -0,0 +1,39 @@
#!/bin/sh
# This script is executed 'chrooted' into a cloned disk target before rebooting
#
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
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
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
chmod 600 /var/log/confluent/confluent-post.log
tail -f /var/log/confluent/confluent-post.log > /dev/console &
logshowpid=$!
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /etc/systemd/system/firstboot.service
mkdir -p /opt/confluent/bin
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /opt/confluent/bin/firstboot.sh
chmod +x /opt/confluent/bin/firstboot.sh
systemctl enable firstboot
selinuxpolicy=$(grep ^SELINUXTYPE /etc/selinux/config |awk -F= '{print $2}')
if [ ! -z "$selinuxpolicy" ]; then
setfiles /etc/selinux/${selinuxpolicy}/contexts/files/file_contexts /etc/
fi
run_remote_python syncfileclient
run_remote post.custom
# post scripts may be placed into post.d, e.g. post.d/01-firstaction.sh, post.d/02-secondaction.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: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
kill $logshowpid
@@ -0,0 +1,279 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
import shutil
import pwd
import grp
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(object):
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]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
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 name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif 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[0] in ('+', '#', '@'):
continue
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 appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
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')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
synchronize()
@@ -0,0 +1,29 @@
# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public
# is served without authentication and thus any sensitive content would be a risk. If wanting to host
# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other
# subdirectory other than public.
#
# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user.
#
# This file lists files to synchronize or merge to the deployed systems from the deployment server
# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts:
# Note particularly the use of '->' to delineate source from target.
# /some/path/hosts -> /etc/hosts
# If wanting to simply use the same path for source and destinaiton, the -> may be skipped:
# /etc/hosts
# More function is available, for example to limit the entry to run only on n1 through n8, and to set
# owner, group, and permissions in octal notation:
# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600)
# Entries under APPENDONCE: will be added to specified target, only if the target does not already
# contain the data in the source already in its entirety. This allows append in a fashion that
# is friendly to being run repeatedly
# 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
@@ -52,7 +52,7 @@ tar
%pre
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /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
@@ -60,7 +60,7 @@ curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pr
%post --nochroot
mkdir -p /mnt/sysimage/etc/confluent
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /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
@@ -77,7 +77,7 @@ 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
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /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
@@ -267,7 +267,7 @@ def get_lan_channel(s):
medtype = int(rsp[1]) & 0b1111111
if medtype not in (4, 6):
continue
rsp = s.raw_command(0xc, 2, bytearray([2, chan, 5, 0, 0]))
rsp = s.raw_command(0xc, 2, bytearray([chan, 5, 0, 0]))
if rsp.get('code', 1) == 0:
return chan
return 1
@@ -7,7 +7,7 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
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
@@ -16,6 +16,7 @@ while ! ping -c 1 $confluent_mgr >& /dev/null; do
done
exec >> /var/log/confluent/confluent-firstboot.log
exec 2>> /var/log/confluent/confluent-firstboot.log
chmod 600 /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -22,30 +63,42 @@ fetch_remote() {
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -56,17 +109,25 @@ source_remote() {
shift
source ./$cmd
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -84,6 +145,11 @@ run_remote() {
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -95,20 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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 $*
else
/usr/bin/python $*
fi
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -121,16 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -1,7 +1,9 @@
#!/bin/sh
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
chmod 600 /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}')
@@ -13,6 +13,7 @@ if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
fi
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
chmod 600 /tmp/confluent-pre.log
tail -f /tmp/confluent-pre.log > /dev/tty &
logshowpid=$!
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
@@ -3,6 +3,8 @@ import importlib
import tempfile
import json
import os
import pwd
import grp
import shutil
try:
from importlib.machinery import SourceFileLoader
@@ -31,7 +33,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -81,8 +83,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -135,12 +141,14 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
if name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
@@ -173,6 +181,8 @@ class CredMerger:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name[0] in ('+', '#', '@'):
continue
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
@@ -188,17 +198,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -217,6 +245,30 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
@@ -1,11 +1,27 @@
# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public
# is served without authentication and thus any sensitive content would be a risk. If wanting to host
# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other
# subdirectory other than public.
#
# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user.
#
# 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:
# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts:
# Note particularly the use of '->' to delineate source from target.
# /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
# More function is available, for example to limit the entry to run only on n1 through n8, and to set
# owner, group, and permissions in octal notation:
# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600)
# Entries under APPENDONCE: will be added to specified target, only if the target does not already
# contain the data in the source already in its entirety. This allows append in a fashion that
# is friendly to being run repeatedly
# 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:
@@ -8,8 +8,9 @@ get_remote_apikey() {
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
if [ -z "$confluent_apikey" ]; then
echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..."
if [ ! -z "$autoconsdev" ]; then echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..." > $autoconsdev; fi
sleep 10
else
elif [ -c /dev/tpmrm0 ]; then
tmpdir=$(mktemp -d)
cd $tmpdir
tpm2_startauthsession --session=session.ctx
@@ -38,6 +39,7 @@ echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd
if ! grep console= /proc/cmdline >& /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
autoconsdev=${autocons%,*}
autocons=${autocons##*/}
echo "Automatic console configured for $autocons"
fi
@@ -72,20 +74,22 @@ umask 0077
tpmdir=$(mktemp -d)
cd $tpmdir
lasthdl=""
for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
tpm2_startauthsession --policy-session --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null)
tpm2_flushcontext session.ctx
if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then
confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:}
echo $confluent_apikey > /etc/confluent/confluent.apikey
if [ -n "$lasthdl" ]; then
tpm2_evictcontrol -c $lasthdl
if [ -c /dev/tpmrm0 ]; then
for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
tpm2_startauthsession --policy-session --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null)
tpm2_flushcontext session.ctx
if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then
confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:}
echo $confluent_apikey > /etc/confluent/confluent.apikey
if [ -n "$lasthdl" ]; then
tpm2_evictcontrol -c $lasthdl
fi
lasthdl=$hdl
fi
lasthdl=$hdl
fi
done
done
fi
cd - > /dev/null
rm -rf $tpmdir
touch /etc/confluent/confluent.info
@@ -127,14 +131,23 @@ while [ $ready = "0" ]; do
fi
confluent_mgr=${confluent_mgr#[}
confluent_mgr=${confluent_mgr%]}
elif grep 'SSL' $tmperr > /dev/null; then
confluent_mgr=${confluent_mgr#[}
confluent_mgr=${confluent_mgr%]}
echo 'Failure establishing TLS conneection to '$confluent_mgr' (try `osdeploy initialize -t` on the deployment server)'
if [ ! -z "$autoconsdev" ]; then echo 'Failure establishing TLS conneection to '$confluent_mgr' (try `osdeploy initialize -t` on the deployment server)' > $autoconsdev; fi
sleep 10
else
ready=1
fi
rm $tmperr
done
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
if [ ! -z "$autocons" ] && grep "textconsole: true" /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
if [ -c /dev/tpmrm0 ]; then
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
fi
umask $oldumask
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}')
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
@@ -7,12 +7,13 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
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
chmod 600 /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
while ! ping -c 1 $confluent_mgr >& /dev/null; do
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -22,38 +63,42 @@ fetch_remote() {
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/libexec/platform-python $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/libexec/platform-python $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -64,17 +109,25 @@ source_remote() {
shift
source ./$cmd
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -92,6 +145,11 @@ run_remote() {
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -103,20 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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 $*
else
/usr/bin/python $*
fi
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -129,16 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -12,7 +12,10 @@ loopdev=$(losetup -f)
export mountsrc=$loopdev
losetup -r $loopdev /mnt/remoteimg/rootimg.sfs
if grep '^Format: confluent_crypted' /tmp/rootimg.info > /dev/null; then
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key
while ! curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key; do
echo "Unable to retrieve private key from $confluent_mgr (verify that confluent can access /var/lib/confluent/private/$confluent_profile/pending/rootimg.key)"
sleep 1
done
cipher=$(head -n 1 /tmp/rootimg.key)
key=$(tail -n 1 /tmp/rootimg.key)
len=$(wc -c /mnt/remoteimg/rootimg.sfs | awk '{print $1}')
@@ -118,4 +121,6 @@ if grep installtodisk /proc/cmdline > /dev/null; then
run_remote installimage
exec reboot -f
fi
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
exec /opt/confluent/bin/start_root
@@ -7,17 +7,20 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
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
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-onboot.log
exec 2>> /var/log/confluent/confluent-onboot.log
chmod 600 /var/log/confluent/confluent-onboot.log
tail -f /var/log/confluent/confluent-onboot.log > /dev/console &
logshowpid=$!
run_remote_python syncfileclient
run_remote_python confignet
run_remote onboot.custom
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
@@ -5,13 +5,15 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
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
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
chmod 600 /var/log/confluent/confluent-post.log
tail -f /var/log/confluent/confluent-post.log > /dev/console &
logshowpid=$!
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /etc/systemd/system/firstboot.service
@@ -4,6 +4,8 @@ import tempfile
import json
import os
import shutil
import pwd
import grp
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
@@ -25,7 +27,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -75,8 +77,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -129,12 +135,14 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
if name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
@@ -167,6 +175,8 @@ class CredMerger:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name[0] in ('+', '#', '@'):
continue
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
@@ -182,17 +192,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -211,9 +239,34 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
synchronize()
synchronize()
@@ -1,11 +1,27 @@
# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public
# is served without authentication and thus any sensitive content would be a risk. If wanting to host
# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other
# subdirectory other than public.
#
# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user.
#
# 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:
# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts:
# Note particularly the use of '->' to delineate source from target.
# /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
# More function is available, for example to limit the entry to run only on n1 through n8, and to set
# owner, group, and permissions in octal notation:
# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600)
# Entries under APPENDONCE: will be added to specified target, only if the target does not already
# contain the data in the source already in its entirety. This allows append in a fashion that
# is friendly to being run repeatedly
# 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:
@@ -1,5 +1,6 @@
#!/bin/bash
echo -n "" >> /tmp/net.ifaces
echo -n "" > /tmp/01-autocons.devnode
cat /tls/*.0 >> /etc/pki/tls/certs/ca-bundle.crt
if ! grep console= /proc/cmdline >& /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
@@ -1,5 +1,23 @@
#!/bin/sh
[ -e /tmp/confluent.initq ] && return 0
udevadm trigger
udevadm trigger --type=devices --action=add
udevadm settle
modprobe ib_ipoib
modprobe ib_umad
modprobe hfi1
modprobe mlx5_ib
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
if [ -f /tmp/dd_disk ]; then
for dd in $(cat /tmp/dd_disk); do
if [ -e $dd ]; then
@@ -24,6 +42,7 @@ cd /sys/class/net
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 60 ]; do
TRIES=$((TRIES + 1))
for currif in *; do
echo 0 > /proc/sys/net/ipv6/conf/${currif}/autoconf
ip link set $currif up
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
@@ -38,7 +57,14 @@ nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
#TODO: blkid --label <whatever> to find mounted api
cat /tls/*.pem > /etc/confluent/ca.pem
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
autocons=$(cat /tmp/01-autocons.devnode)
errout=""
if [ ! -z "$autocons" ]; then
errout="-e $autocons"
fi
while ! confluentpython /opt/confluent/bin/apiclient $errout /confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg; do
sleep 10
done
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
@@ -51,8 +77,18 @@ hostname=$nodename
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
hostname=$hostname.$dnsdomain
fi
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server: }
v6cfg=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg)
v6cfg=${v6cfg#ipv6_method: }
v4cfg=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server: }
else
mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server_v6: }
mgr="[$mgr]"
fi
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
profilename=${profilename#profile: }
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
@@ -64,6 +100,7 @@ if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null; the
if [ ! -z "$autocons" ]; then
echo Auto-configuring installed system to use text console
echo Auto-configuring installed system to use text console > $autocons
/opt/confluent/bin/autocons -c > /dev/null
cp /tmp/01-autocons.conf /etc/cmdline.d/
else
echo "Unable to automatically detect requested text console"
@@ -80,7 +117,7 @@ autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "dhcp" ]; then
echo ip=$ifname:dhcp >> /etc/cmdline.d/01-confluent.conf
else
elif [ "$autoconfigmethod" = "static" ]; then
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg)
v4addr=${v4addr#ipv4_address: }
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg)
@@ -92,11 +129,30 @@ else
v4nm=${v4nm#ipv4_netmask: }
echo ip=$v4addr::$v4gw:$v4nm:$hostname:$ifname:none >> /etc/cmdline.d/01-confluent.conf
fi
if [ "$v6cfg" = "static" ]; then
v6addr=$(grep ^ipv6_address: /etc/confluent/confluent.deploycfg)
v6addr=${v6addr#ipv6_address: }
v6addr="[$v6addr]"
v6gw=$(grep ^ipv6_gateway: /etc/confluent/confluent.deploycfg)
v6gw=${v6gw#ipv6_gateway: }
if [ "$v6gw" = "null" ]; then
v6gw=""
else
v6gw="[$v6gw]"
fi
v6nm=$(grep ipv6_prefix: /etc/confluent/confluent.deploycfg)
v6nm=${v6nm#ipv6_prefix: }
echo ip=$v6addr::$v6gw:$v6nm:$hostname:$ifname:none >> /etc/cmdline.d/01-confluent.conf
fi
nameserversec=0
v4dns=0
v6dns=0
while read -r entry; do
if [ $nameserversec = 1 ]; then
if [[ $entry == "-"* ]] && [[ $entry != "- ''" ]]; then
echo nameserver=${entry#- } >> /etc/cmdline.d/01-confluent.conf
[[ "$entry" == *:* ]] && v6dns=1
[[ "$entry" == *.* ]] && v4dns=1
continue
fi
fi
@@ -109,6 +165,24 @@ done < /etc/confluent/confluent.deploycfg
if [ -e /lib/nm-lib.sh ]; then
. /lib/nm-lib.sh
nm_generate_connections
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
grep -v ^dns-search= /run/NetworkManager/system-connections/$ifname.nmconnection > /run/NetworkManager/system-connections/$ifname.nmconnection.new
mv /run/NetworkManager/system-connections/$ifname.nmconnection.new /run/NetworkManager/system-connections/$ifname.nmconnection
if [ "$v4dns" = 1 ]; then
awk '/^\[ipv4\]/ {print;print "dns-search='"$dnsdomain"'";next}1' /run/NetworkManager/system-connections/$ifname.nmconnection > /run/NetworkManager/system-connections/$ifname.nmconnection.new
else
awk '/^\[ipv4\]/ {print;print "dns-search=";next}1' /run/NetworkManager/system-connections/$ifname.nmconnection > /run/NetworkManager/system-connections/$ifname.nmconnection.new
fi
mv /run/NetworkManager/system-connections/$ifname.nmconnection.new /run/NetworkManager/system-connections/$ifname.nmconnection
if [ "$v6dns" = 1 ]; then
awk '/^\[ipv6\]/ {print;print "dns-search='"$dnsdomain"'";next}1' /run/NetworkManager/system-connections/$ifname.nmconnection > /run/NetworkManager/system-connections/$ifname.nmconnection.new
else
awk '/^\[ipv6\]/ {print;print "dns-search=";next}1' /run/NetworkManager/system-connections/$ifname.nmconnection > /run/NetworkManager/system-connections/$ifname.nmconnection.new
fi
mv /run/NetworkManager/system-connections/$ifname.nmconnection.new /run/NetworkManager/system-connections/$ifname.nmconnection
chmod 600 /run/NetworkManager/system-connections/$ifname.nmconnection
fi
if [[ "$ifname" == ib* ]]; then
sed -i s/type=ethernet/type=infiniband/ /run/NetworkManager/system-connections/$ifname.nmconnection
if ! grep '\[infiniband\]' /run/NetworkManager/system-connections/$ifname.nmconnection > /dev/null; then
@@ -54,8 +54,17 @@ tar
%include /tmp/kickstart.custom
%pre
v4cfg=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
fi
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
@@ -63,7 +72,16 @@ curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pr
%post --nochroot
mkdir -p /mnt/sysimage/etc/confluent
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
v4cfg=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
fi
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/postinst.sh
. /tmp/postinst.sh
@@ -80,7 +98,16 @@ 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
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
v4cfg=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
fi
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /tmp/postinst.sh
. /tmp/postinst.sh
%end
@@ -267,7 +267,7 @@ def get_lan_channel(s):
medtype = int(rsp[1]) & 0b1111111
if medtype not in (4, 6):
continue
rsp = s.raw_command(0xc, 2, bytearray([2, chan, 5, 0, 0]))
rsp = s.raw_command(0xc, 2, bytearray([chan, 5, 0, 0]))
if rsp.get('code', 1) == 0:
return chan
return 1
@@ -7,15 +7,27 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
v4cfg=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
confluent_pingtarget=$confluent_mgr
else
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_pingtarget=$confluent_mgr
confluent_mgr="[$confluent_mgr]"
fi
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
chmod 600 /var/log/confluent/confluent-firstboot.log
tail -f /var/log/confluent/confluent-firstboot.log > /dev/console &
logshowpid=$!
while ! ping -c 1 $confluent_mgr >& /dev/null; do
while ! ping -c 1 $confluent_pingtarget >& /dev/null; do
sleep 1
done
@@ -23,6 +35,7 @@ if [ ! -f /etc/confluent/firstboot.ran ]; then
touch /etc/confluent/firstboot.ran
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
run_remote_python confignet
run_remote firstboot.custom
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -22,30 +63,42 @@ fetch_remote() {
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -56,17 +109,25 @@ source_remote() {
shift
source ./$cmd
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -84,6 +145,11 @@ run_remote() {
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -95,20 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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 $*
else
/usr/bin/python $*
fi
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -121,16 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -1,7 +1,9 @@
#!/bin/sh
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
chmod 600 /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}')
@@ -11,11 +11,23 @@ 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
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
chmod 600 /tmp/confluent-pre.log
tail -f /tmp/confluent-pre.log > /dev/tty &
logshowpid=$!
/usr/libexec/platform-python /etc/confluent/apiclient >& /dev/null
confluentpython /etc/confluent/apiclient >& /dev/null
nicname=$(ip link|grep ^$(cat /tmp/confluent.ifidx): | awk '{print $2}' | awk -F: '{print $1}')
nmcli c u $nicname
while ip -6 addr | grep tentative > /dev/null; do
@@ -76,22 +88,20 @@ fi
export confluent_mgr confluent_profile nodename
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
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
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
run_remote pre.custom
run_remote_parts pre.d
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart -o /tmp/kickstart.base
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || touch /tmp/installdisk
if [ ! -e /tmp/installdisk ]; then
run_remote_python getinstalldisk
fi
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || rm /tmp/installdisk
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
dd if=/dev/zero of=/dev/$(cat /tmp/installdisk) bs=1M count=1 >& /dev/null
vgchange -a n >& /dev/null
fi
kill $logshowpid
@@ -21,6 +21,7 @@ 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 Port 22 >> /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)
@@ -4,6 +4,8 @@ import tempfile
import json
import os
import shutil
import pwd
import grp
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
@@ -25,7 +27,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -75,8 +77,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -129,7 +135,7 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
@@ -186,17 +192,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -215,8 +239,33 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
@@ -1,11 +1,27 @@
# It is advised to avoid /var/lib/confluent/public as a source for syncing. /var/lib/confluent/public
# is served without authentication and thus any sensitive content would be a risk. If wanting to host
# syncfiles on a common share, it is suggested to have /var/lib/confluent be the share and use some other
# subdirectory other than public.
#
# Syncing is performed as the 'confluent' user, so all source files must be accessible by the confluent user.
#
# 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:
# To specify taking /some/path/hosts on the deployment server and duplicating it to /etc/hosts:
# Note particularly the use of '->' to delineate source from target.
# /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
# More function is available, for example to limit the entry to run only on n1 through n8, and to set
# owner, group, and permissions in octal notation:
# /example/source -> n1-n8:/etc/target (owner=root,group=root,permissions=600)
# Entries under APPENDONCE: will be added to specified target, only if the target does not already
# contain the data in the source already in its entirety. This allows append in a fashion that
# is friendly to being run repeatedly
# 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:
@@ -76,7 +76,10 @@ for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
done
cd - > /dev/null
rm -rf $tpmdir
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
while ! /usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg; do
sleep 10
done
if [ ! -z "$autocons" ] && grep "textconsole: true" /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
umask $oldumask
new_apikey=$(cat /etc/confluent/confluent.apikey)
if [ "$new_apikey" != "$confluent_apikey" ]; then
@@ -108,8 +111,8 @@ if [ "$autoconfigmethod" = "static" ]; then
if [ "$ipgw" = "null" ]; then
ipgw=""
fi
ipnm=$(grep ^prefix: /etc/confluent/confluent.deploycfg)
ipnm=${ipnm#prefix: }
ipnm=$(grep ^ipv6_prefix: /etc/confluent/confluent.deploycfg)
ipnm=${ipnm#ipv6_prefix: }
echo "Setting up $ifname as static at $ipaddr/$ipnm"
ip addr add dev $ifname $ipaddr/$ipnm
if [ ! -z "$ipgw" ]; then
@@ -122,7 +125,7 @@ if [ "$autoconfigmethod" = "dhcp" ]; then
dhclient $ifname
echo "Complete:"
ip addr show dev $ifname
elif [ "$autocinfigmethtod" = "static" ]; then
elif [ "$autoconfigmethod" = "static" ]; then
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg)
v4addr=${v4addr#ipv4_address: }
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg)
@@ -151,11 +154,12 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
done
/usr/sbin/sshd
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"
export confluent_profile
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
set_confluent_vars
export confluent_mgr
echo "Running https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh"
run_remote onboot.sh
while :; do
bash
@@ -165,9 +165,18 @@ def set_port(s, port, vendor, model):
return 1
def get_remote_config_mod(vendor, model):
def get_remote_config_mod(vendor, model, waiters):
if vendor in ('IBM', 'Lenovo'):
if _is_tsm(model):
for waiter in waiters:
if waiter:
sys.stdout.write('Waiting for TSM network to activate')
for x in range(0, 90):
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(1)
sys.stdout.write('Complete\n')
break
return 'tsm'
else:
return 'xcc'
@@ -275,7 +284,7 @@ def get_lan_channel(s):
medtype = int(rsp[1]) & 0b1111111
if medtype not in (4, 6):
continue
rsp = s.raw_command(0xc, 2, bytearray([2, chan, 5, 0, 0]))
rsp = s.raw_command(0xc, 2, bytearray([chan, 5, 0, 0]))
if rsp.get('code', 1) == 0:
return chan
return 1
@@ -415,7 +424,7 @@ def main():
dotwait()
sys.stdout.write('done\n')
sys.stdout.flush()
cfgmod = get_remote_config_mod(vendor, model)
cfgmod = get_remote_config_mod(vendor, model, (awaitip, awaitvlan, awaitprefix, awaitgw))
if cfgmod:
with open('configbmc.configmod', 'w+') as cm:
cm.write('configmod: {0}\n'.format(cfgmod))
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -11,28 +52,86 @@ function set_confluent_vars() {
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
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
}
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 /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
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://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
@@ -42,8 +141,15 @@ 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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -55,15 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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 $*
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -76,10 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -4,6 +4,8 @@ import tempfile
import json
import os
import shutil
import pwd
import grp
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
@@ -25,7 +27,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -75,8 +77,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -129,12 +135,14 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
if name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
@@ -167,6 +175,8 @@ class CredMerger:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name[0] in ('+', '#', '@'):
continue
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
@@ -182,17 +192,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -211,9 +239,34 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
synchronize()
synchronize()
@@ -43,7 +43,7 @@ reboot
%pre
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /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
. /tmp/preinst.sh
%end
@@ -51,7 +51,7 @@ curl -f https://$mgr/confluent-public/os/$profile/scripts/pre.sh > /tmp/preinst.
%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}')
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
. /tmp/postinst.sh
@@ -68,7 +68,7 @@ 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}')
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
. /tmp/postinst.sh
%end
@@ -7,7 +7,7 @@
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}')
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
@@ -133,6 +133,7 @@ while [ $ready = "0" ]; do
fi
rm $tmperr
done
if [ ! -z "$autocons" ] && grep textconsole: true /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
umask $oldumask
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg |awk '{print $2}')
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -22,38 +63,42 @@ fetch_remote() {
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/libexec/platform-python $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(/usr/libexec/platform-python $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Sourcing from $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -64,17 +109,25 @@ source_remote() {
shift
source ./$cmd
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
fi
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
@@ -92,6 +145,11 @@ run_remote() {
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -103,20 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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 $*
else
/usr/bin/python $*
fi
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -129,16 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -136,4 +136,6 @@ if grep installtodisk /proc/cmdline > /dev/null; then
run_remote installimage
exec reboot -f
fi
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
exec /opt/confluent/bin/start_root
@@ -7,16 +7,20 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
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
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-onboot.log
exec 2>> /var/log/confluent/confluent-onboot.log
chmod 600 /var/log/confluent/confluent-onboot.log
tail -f /var/log/confluent/confluent-onboot.log > /dev/console &
logshowpid=$!
run_remote_python syncfileclient
run_remote_python confignet
run_remote onboot.custom
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
run_remote_parts onboot.d
@@ -52,6 +52,7 @@ if [ -z "$mgtiface" ]; then
else
curl -H "CONFLUENT_MGTIFACE: $mgtiface" -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
fi
if [ ! -z "$autocons" ] && grep textconsole: true /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
profilename=${profilename#profile: }
curl https://$mgr/confluent-public/os/$profilename/profile.yaml > /tmp/profile.yaml
@@ -3,10 +3,20 @@
# 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
chmod 600 /var/log/confluent/confluent-firstboot.log
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
v6cfg=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg)
v6cfg=${v6cfg#ipv6_method: }
if [ "$v6cfg" = "static" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
else
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
fi
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
. /etc/confluent/functions
@@ -19,6 +29,7 @@ for i in /etc/ssh/ssh_host*key.pub; do
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_python confignet
run_remote firstboot.custom
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
@@ -1,77 +1,178 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
run_remote() {
fetch_remote() {
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
requestedcmd="'$*'"
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd 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 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
shift
./$cmd $*
retcode=$?
echo "$requestedcmd exited with code $retcode"
cd - > /dev/null
return $retcode
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)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
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/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d)
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
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/^- //')
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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 /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ -x /usr/bin/chcon ]; then
chcon system_u:object_r:bin_t:s0 $cmd
fi
shift
./$cmd $*
retcode=$?
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
run_remote_python() {
echo
set_confluent_vars
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $1)
curl -f -sS https://$confluent_mgr/confluent-public/os/$confluent_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
python3 $*
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -84,10 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -9,8 +9,10 @@
# If there are dependencies on the kernel (drivers or special filesystems)
# then firstboot.sh would be the script to customize.
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
exec 2>> /var/log/confluent/confluent-post.log
chmod 600 /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}')
@@ -5,6 +5,7 @@
exec >> /tmp/confluent-pre.log
exec 2>> /tmp/confluent-pre.log
chmod 600 /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
@@ -4,6 +4,8 @@ import tempfile
import json
import os
import shutil
import pwd
import grp
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
@@ -25,7 +27,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -75,8 +77,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -129,12 +135,14 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
if name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
@@ -167,6 +175,8 @@ class CredMerger:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name[0] in ('+', '#', '@'):
continue
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
@@ -182,17 +192,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -211,9 +239,34 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
synchronize()
synchronize()
@@ -0,0 +1,8 @@
if ! grep console= /proc/cmdline > /dev/null; then
/opt/confluent/bin/autocons > /custom-installation/autocons.info
cons=$(cat /custom-installation/autocons.info)
if [ ! -z "$cons" ]; then
echo "Auto-detected serial console: $cons" > ${cons%,*}
fi
fi
echo . /scripts/init-premount/confluent >> /scripts/init-premount/ORDER
@@ -0,0 +1,147 @@
#!/bin/sh
get_tpm_apikey() {
lasthdl=""
if [ -c /dev/tpmrm0 ]; then
for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
tpm2_startauthsession --policy-session --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null)
tpm2_flushcontext session.ctx
if echo $unsealeddata | grep "^CONFLUENT_APIKEY:" > /dev/null; then
confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:}
echo $confluent_apikey > /etc/confluent/confluent.apikey
if [ -n "$lasthdl" ]; then
tpm2_evictcontrol -c $lasthdl
fi
lasthdl=$hdl
fi
done
fi
if [ ! -z "$confluent_apikey" ]; then
if ! curl --cacert /etc/confluent/ca.pem -sSf -H "CONFLUENT_NODENAME: $confluent_nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://${confluent_http_mgr}/confluent-api/self/myattribs > /dev/null; then
tpm2_evictcontrol -c $lasthdl
confluent_apikey=""
fi
fi
}
get_remote_apikey() {
while [ -z "$confluent_apikey" ]; do
/opt/confluent/bin/clortho $confluent_nodename $confluent_mgr > /etc/confluent/confluent.apikey
if grep ^SEALED: /etc/confluent/confluent.apikey > /dev/null; then
# we don't support remote sealed api keys anymore
echo > /etc/confluent/confluent.apikey
fi
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
if [ -z "$confluent_apikey" ]; then
echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$confluent_nodename', retrying..."
sleep 10
elif [ -c /dev/tpmrm0 ]; then
tmpdir=$(mktemp -d)
cd $tmpdir
tpm2_startauthsession --session=session.ctx
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
tpm2_createprimary -G ecc -Q --key-context=prim.ctx
(echo -n "CONFLUENT_APIKEY:";cat /etc/confluent/confluent.apikey) | tpm2_create -Q --policy=pcr15.sha256.policy --public=data.pub --private=data.priv -i - -C prim.ctx
tpm2_load -Q --parent-context=prim.ctx --public=data.pub --private=data.priv --name=confluent.apikey --key-context=data.ctx
tpm2_evictcontrol -Q -c data.ctx
tpm2_flushcontext session.ctx
cd - > /dev/null
rm -rf $tmpdir
fi
done
}
if ! grep console= /proc/cmdline > /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
autocons=${autocons##*/}
echo "Automatic console configured for $autocons"
fi
echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd
cd /sys/class/net
for nic in *; do
ip link set $nic up
done
mkdir -p /etc/confluent
cd - > /dev/null
mkdir -p /etc/ssl/certs
cat /tls/*.pem > /etc/ssl/certs/ca-certificates.crt
cat /tls/*.pem > /etc/confluent/ca.pem
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
TRIES=0
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 > /dev/null && [ "$TRIES" -lt 30 ]; do
TRIES=$((TRIES + 1))
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
sleep 1
done
confluent_mgr=$(grep '^EXTMGRINFO:.*1$' /etc/confluent/confluent.info | head -n 1 | awk -F': ' '{print $2}' | awk -F'|' '{print $1}')
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info|head -n 1 | awk '{print $2}')
fi
if echo $confluent_mgr | grep : >/dev/null; then
confluent_http_mgr="[$confluent_mgr]"
else
confluent_http_mgr=$confluent_mgr
fi
confluent_nodename=$(grep ^NODENAME: /etc/confluent/confluent.info |awk '{print $NF}')
hostname $confluent_nodename
get_tpm_apikey
if [ -z "$confluent_apikey" ]; then
get_remote_apikey
fi
if echo $confluent_mgr | grep '%' > /dev/null; then
echo $confluent_mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
fi
curl --cacert /etc/confluent/ca.pem -sSf -H "CONFLUENT_NODENAME: $confluent_nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://${confluent_http_mgr}/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg
if [ ! -z "$autocons" ] && grep textconsole: true /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
v6meth=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
if [ "$v6meth" = static ]; then
v6addr=$(grep ^ipv6_address: /etc/confluent/confluent.deploycfg | awk '{print $2}')
v6prefix=$(grep ^ipv6_prefix: /etc/confluent/confluent.deploycfg | awk '{print $2}')
ip addr add dev $ifname $v6addr/$v6prefix
v6gw=$(grep ^ipv6_gateway: /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ ! -z "$v6gw" -a "$v6gw" != "null" ]; then
ip route add default via $v6gw
fi
fi
v4meth=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
if [ "$v4meth" = static ]; then
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg | awk '{print $2}')
v4prefix=$(grep ^prefix: /etc/confluent/confluent.deploycfg | awk '{print $2}')
ip addr add dev $ifname $v4addr/$v4prefix
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ ! -z "$v4gw" -a "$v4gw" != "null" ]; then
ip route add default via $v4gw
fi
fi
mkdir -p /run/sshd
mkdir -p /etc/ssh
echo Port 2222 > /etc/ssh/sshd_config
ssh-keygen -A
for k in /etc/ssh/*key.pub; do
certfile=$(echo $k|sed -e s/.pub/-cert.pub/)
privkey=$(echo $k|sed -e s/.pub//)
curl --cacert /etc/confluent/ca.pem -sSf -H "CONFLUENT_NODENAME: $confluent_nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$k https://${confluent_http_mgr}/confluent-api/self/sshcert >> $certfile
if [ -s $certfile ]; then
if ! grep $certfile /etc/ssh/sshd_config > /dev/null; then
echo HostCertificate $certfile >> /etc/ssh/sshd_config
fi
if ! grep "HostKey $privkey" /etc/ssh/sshd_config > /dev/null; then
echo HostKey $privkey >> /etc/ssh/sshd_config
fi
fi
done
/usr/sbin/sshd
mkdir -p /root/.ssh
cat /ssh/*pubkey > /root/.ssh/authorized_keys
if [ -c /dev/tpmrm0 ]; then
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
fi
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg| awk '{print $2}')
confluent_proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg| awk '{print $2}')
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
exec /bin/bash /etc/confluent/functions source_remote imageboot.sh
@@ -0,0 +1,197 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
confluent_nodename=$nodename
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
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 /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
unsettmpdir=0
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 /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ -x /usr/bin/chcon ]; then
chcon system_u:object_r:bin_t:s0 $cmd
fi
shift
./$cmd $*
retcode=$?
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
run_remote_python() {
echo
set_confluent_vars
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -0,0 +1,137 @@
confluent_urls=""
confluent_proto=https
for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed -e s/%/%25/); do
if [[ $addr == *:* ]]; then
confluent_urls="$confluent_urls $confluent_proto://[$addr]/confluent-public/os/$confluent_profile/rootimg.sfs"
else
confluent_urls="$confluent_urls $confluent_proto://$addr/confluent-public/os/$confluent_profile/rootimg.sfs"
fi
done
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
if grep confluennt_imagemethtod=untethered /proc/cmdline > /dev/null; then
mount -t tmpfs untethered /mnt/remoteimg
curl https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs
else
confluent_urls="$confluent_urls https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs"
/opt/confluent/bin/urlmount $confluent_urls /mnt/remoteimg
fi
/opt/confluent/bin/confluent_imginfo /mnt/remoteimg/rootimg.sfs > /tmp/rootimg.info
loopdev=$(losetup -f)
export mountsrc=$loopdev
losetup -r $loopdev /mnt/remoteimg/rootimg.sfs
if grep '^Format: confluent_crypted' /tmp/rootimg.info > /dev/null; then
while ! curl -sf -H "CONFLUENT_NODENAME: $confluent_nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key; do
echo "Unable to retrieve private key from $confluent_mgr (verify that confluent can access /var/lib/confluent/private/$confluent_profile/pending/rootimg.key)"
sleep 1
done
cipher=$(head -n 1 /tmp/rootimg.key)
key=$(tail -n 1 /tmp/rootimg.key)
len=$(ls -l /mnt/remoteimg/rootimg.sfs | awk '{print $3}')
len=$(((len-4096)/512))
dmsetup create cryptimg --table "0 $len crypt $cipher $key 0 $loopdev 8"
/opt/confluent/bin/confluent_imginfo /dev/mapper/cryptimg > /tmp/rootimg.info
mountsrc=/dev/mapper/cryptimg
fi
if grep '^Format: squashfs' /tmp/rootimg.info > /dev/null; then
mount -o ro $mountsrc /mnt/remote
elif grep '^Format: confluent_multisquash' /tmp/rootimg.info; then
tail -n +3 /tmp/rootimg.info | awk '{gsub("/", "_"); print "echo 0 " $4 " linear '$mountsrc' " $3 " | dmsetup create mproot" $7}' > /tmp/setupmount.sh
. /tmp/setupmount.sh
cat /tmp/setupmount.sh |awk '{printf "mount /dev/mapper/"$NF" "; sub("mproot", ""); gsub("_", "/"); print "/mnt/remote"$NF}' > /tmp/mountparts.sh
. /tmp/mountparts.sh
fi
#mount -t tmpfs overlay /mnt/overlay
modprobe zram
memtot=$(grep ^MemTotal: /proc/meminfo|awk '{print $2}')
memtot=$((memtot/2))$(grep ^MemTotal: /proc/meminfo | awk '{print $3'})
echo $memtot > /sys/block/zram0/disksize
modprobe xfs
mkdir /sysroot
mkfs.xfs /dev/zram0 > /dev/null
mount -o discard /dev/zram0 /mnt/overlay
if [ ! -f /tmp/mountparts.sh ]; then
mkdir -p /mnt/overlay/upper /mnt/overlay/work
mount -t overlay -o upperdir=/mnt/overlay/upper,workdir=/mnt/overlay/work,lowerdir=/mnt/remote disklessroot /sysroot
else
for srcmount in $(cat /tmp/mountparts.sh | awk '{print $3}'); do
mkdir -p /mnt/overlay${srcmount}/upper /mnt/overlay${srcmount}/work
mount -t overlay -o upperdir=/mnt/overlay${srcmount}/upper,workdir=/mnt/overlay${srcmount}/work,lowerdir=${srcmount} disklesspart /sysroot${srcmount#/mnt/remote}
done
fi
mkdir -p /sysroot/etc/ssh
mkdir -p /sysroot/etc/confluent
mkdir -p /sysroot/root/.ssh
cp /root/.ssh/* /sysroot/root/.ssh
chmod 700 /sysroot/root/.ssh
cp /etc/confluent/* /sysroot/etc/confluent/
cp /etc/ssh/*key* /sysroot/etc/ssh/
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
privfile=${pubkey%.pub}
if [ -s $certfile ]; then
echo HostCertificate $certfile >> /sysroot/etc/ssh/sshd_config
fi
echo HostKey $privfile >> /sysroot/etc/ssh/sshd_config
done
mkdir -p /sysroot/dev /sysroot/sys /sysroot/proc /sysroot/run
if [ ! -z "$autocons" ]; then
autocons=${autocons%,*}
mkdir -p /run/systemd/generator/getty.target.wants
ln -s /usr/lib/systemd/system/serial-getty@.service /run/systemd/generator/getty.target.wants/serial-getty@${autocons}.service
fi
while [ ! -e /sysroot/sbin/init ] && [ ! -h /sysroot/sbin/init ]; do
echo "Failed to access root filesystem or it is missing /sbin/init"
echo "System should be accessible through ssh at port 2222 with the appropriate key"
while [ ! -e /sysroot/sbin/init ]; do
sleep 1
done
done
rootpassword=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg)
rootpassword=${rootpassword#rootpassword: }
if [ "$rootpassword" = "null" ]; then
rootpassword=""
fi
if [ ! -z "$rootpassword" ]; then
sed -i "s@root:[^:]*:@root:$rootpassword:@" /sysroot/etc/shadow
fi
for i in /ssh/*.ca; do
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
done
echo HostbasedAuthentication yes >> /sysroot/etc/ssh/sshd_config
echo HostbasedUsesNameFromPacketOnly yes >> /sysroot/etc/ssh/sshd_config
echo IgnoreRhosts no >> /sysroot/etc/ssh/sshd_config
sshconf=/sysroot/etc/ssh/ssh_config
if [ -d /sysroot/etc/ssh/ssh_config.d/ ]; then
sshconf=/sysroot/etc/ssh/ssh_config.d/01-confluent.conf
fi
echo 'Host *' >> $sshconf
echo ' HostbasedAuthentication yes' >> $sshconf
echo ' EnableSSHKeysign yes' >> $sshconf
echo ' HostbasedKeyTypes *ed25519*' >> $sshconf
curl -sf -H "CONFLUENT_NODENAME: $confluent_nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_mgr/confluent-api/self/nodelist > /sysroot/etc/ssh/shosts.equiv
cp /sysroot/etc/ssh/shosts.equiv /sysroot/root/.shosts
echo $confluent_nodename > /sysroot/etc/hostname
chmod 600 /sysroot/etc/ssh/*_key
mkdir -p /sysroot/usr/share/ca-certificates/confluent/
cp /tls/*.pem /sysroot/usr/share/ca-certificates/confluent/
chroot /sysroot/ update-ca-certificates
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.service > /sysroot/etc/systemd/system/onboot.service
mkdir -p /sysroot/opt/confluent/bin
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh > /sysroot/opt/confluent/bin/onboot.sh
chmod +x /sysroot/opt/confluent/bin/onboot.sh
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service
cp /etc/confluent/functions /sysroot/etc/confluent/functions
if grep installtodisk /proc/cmdline > /dev/null; then
. /etc/confluent/functions
run_remote installimage
exec reboot -f
fi
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
exec /opt/confluent/bin/start_root
@@ -16,7 +16,7 @@ deploycfg=/root/custom-installation/confluent/confluent.deploycfg
if [ -z "$MGTIFACE" ]; then
chroot . usr/bin/curl -f -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_APIKEY: $(cat /root//custom-installation/confluent/confluent.apikey)" https://${MGR}/confluent-api/self/deploycfg > $deploycfg
else
chroot . usr/bin/curl -f -H "ONFLUENT_MGTIFACE: $MGTIFACE" -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_APIKEY: $(cat /root//custom-installation/confluent/confluent.apikey)" https://${MGR}/confluent-api/self/deploycfg > $deploycfg
chroot . usr/bin/curl -f -H "CONFLUENT_MGTIFACE: $MGTIFACE" -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_APIKEY: $(cat /root//custom-installation/confluent/confluent.apikey)" https://${MGR}/confluent-api/self/deploycfg > $deploycfg
fi
umask $oum
nic=$(grep ^MANAGER /custom-installation/confluent/confluent.info|grep fe80::|sed -e s/.*%//|head -n 1)
@@ -1,9 +1,50 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
return 0
fi
return 1
}
function confluentpython() {
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
elif [ -x /usr/bin/python3 ]; then
/usr/bin/python3 $*
elif [ -x /usr/bin/python ]; then
/usr/bin/python $*
elif [ -x /usr/bin/python2 ]; then
/usr/bin/python2 $*
fi
}
function set_confluent_vars() {
if [ -z "$nodename" ]; then
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
fi
if [[ "$confluent_mgr" == *"%"* ]]; then
confluent_mgr=""
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if ! test_mgr $confluent_mgr; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
fi
if ! test_mgr $confluent_mgr; then
BESTMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|1$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
OKMGRS=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info | grep '|0$' | sed -e 's/EXTMGRINFO: //' -e 's/|.*//')
for confluent_mgr in $BESTMGRS $OKMGRS; do
if [[ $confluent_mgr == *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if test_mgr $confluent_mgr; then
break
fi
done
fi
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -11,6 +52,7 @@ function set_confluent_vars() {
}
fetch_remote() {
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
@@ -20,39 +62,94 @@ fetch_remote() {
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
source_remote_parts() {
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
source_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
run_remote_parts() {
scriptlist=$(python3 /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
scriptlist=$(confluentpython $apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1/$script
done
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
}
source_remote() {
set_confluent_vars
unsettmpdir=0
echo
echo '---------------------------------------------------------------------------'
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
rm -rf $confluentscripttmpdir
return $retcode
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
unsettmpdir=0
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd 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://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ -z "$confluentscripttmpdir" ]; then
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
unsettmpdir=1
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
#if [ -x /usr/bin/chcon ]; then
# chcon system_u:object_r:bin_t:s0 $cmd
#fi
if [ -x /usr/bin/chcon ]; then
chcon system_u:object_r:bin_t:s0 $cmd
fi
shift
./$cmd $*
retcode=$?
echo "$requestedcmd exited with code $retcode"
if [ $retcode -ne 0 ]; then
echo "$requestedcmd exited with code $retcode"
fi
cd - > /dev/null
if [ "$unsettmpdir" = 1 ]; then
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
unsettmpdir=0
fi
return $retcode
}
@@ -64,16 +161,18 @@ run_remote_python() {
fi
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
echo Executing in $confluentscripttmpdir
cd $confluentscripttmpdir
mkdir -p $(dirname $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
python3 $*
confluentpython $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
rm -rf $confluentscripttmpdir
unset confluentscripttmpdir
return $retcode
}
@@ -86,11 +185,12 @@ run_remote_config() {
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
confluentpython $apiclient /confluent-api/self/remoteconfig/"$*" -d {}
confluentpython $apiclient /confluent-api/self/remoteconfig/status -w 204
echo
echo 'Completed remote configuration'
echo '---------------------------------------------------------------------------'
return
}
#If invoked as a command, use the arguments to actually run a function
(return 0 2>/dev/null) || $1 "${@:2}"
@@ -63,6 +63,15 @@ echo "Port 2222" >> /etc/ssh/sshd_config
echo "Match LocalPort 22" >> /etc/ssh/sshd_config
echo " ChrootDirectory /target" >> /etc/ssh/sshd_config
kill -HUP $(cat /run/sshd.pid)
if [ -e /sys/firmware/efi ]; then
bootnum=$(chroot /target efibootmgr | grep ubuntu | sed -e 's/ .*//' -e 's/\*//' -e s/Boot//)
if [ ! -z "$bootnum" ]; then
currboot=$(chroot /target efibootmgr | grep ^BootOrder: | awk '{print $2}')
nextboot=$(echo $currboot| awk -F, '{print $1}')
[ "$nextboot" = "$bootnum" ] || chroot /target efibootmgr -o $bootnum,$currboot
chroot /target efibootmgr -D
fi
fi
cat /target/etc/confluent/tls/*.pem > /target/etc/confluent/ca.pem
cat /target/etc/confluent/tls/*.pem > /etc/confluent/ca.pem
chroot /target bash -c "source /etc/confluent/functions; run_remote_python syncfileclient"
@@ -4,6 +4,8 @@ import tempfile
import json
import os
import shutil
import pwd
import grp
from importlib.machinery import SourceFileLoader
try:
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
@@ -25,7 +27,7 @@ def partitionhostsline(line):
names = names.split()
return ipaddr, names, comment
class HostMerger:
class HostMerger(object):
def __init__(self):
self.byip = {}
self.byname = {}
@@ -75,8 +77,12 @@ class HostMerger:
def write_out(self, targetfile):
while not self.targlines[-1]:
self.targlines = self.targlines[:-1]
if not self.targlines:
break
while not self.sourcelines[-1]:
self.sourcelines = self.sourcelines[:-1]
if not self.sourcelines:
break
with open(targetfile, 'w') as hosts:
for line in self.targlines:
hosts.write(line + '\n')
@@ -129,12 +135,14 @@ class CredMerger:
except ValueError:
continue
if targfile:
if uid <= minid or uid >= maxid:
if uid < minid or uid > maxid:
self.targdata.append(line)
else:
self.discardnames[name] = 1
else:
if uid >= minid and uid <= maxid:
if name[0] in ('+', '#', '@'):
self.sourcedata.append(line)
elif uid >= minid and uid <= maxid:
self.sourcedata.append(line)
def read_shadow(self, source):
@@ -167,6 +175,8 @@ class CredMerger:
shadout.write(line + '\n')
for line in self.sourcedata:
name, _ = line.split(':', 1)
if name[0] in ('+', '#', '@'):
continue
if name in self.shadowednames:
continue
shadout.write(name + ':!:::::::\n')
@@ -182,17 +192,35 @@ class CredMerger:
continue
shadout.write(name + ':!::\n')
def appendonce(basepath, filename):
with open(filename, 'rb') as filehdl:
thedata = filehdl.read()
targname = filename.replace(basepath, '')
try:
with open(targname, 'rb') as filehdl:
targdata = filehdl.read()
except IOError:
targdata = b''
if thedata in targdata:
return
with open(targname, 'ab') as targhdl:
targhdl.write(thedata)
def synchronize():
tmpdir = tempfile.mkdtemp()
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir})
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
while status != 204:
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
if not isinstance(rsp, str):
rsp = rsp.decode('utf8')
if status == 200:
lastrsp = rsp
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
if os.path.exists(pendpasswd):
cm = CredMerger()
@@ -211,9 +239,34 @@ def synchronize():
cm.read_source(pendhosts)
cm.read_target('/etc/hosts')
cm.write_out('/etc/hosts')
for dirn in os.walk(appendoncedir):
for filen in dirn[2]:
appendonce(appendoncedir, os.path.join(dirn[0], filen))
if lastrsp:
lastrsp = json.loads(lastrsp)
opts = lastrsp.get('options', {})
for fname in opts:
uid = -1
gid = -1
for opt in opts[fname]:
if opt == 'owner':
try:
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
except KeyError:
uid = opts[fname][opt]['id']
elif opt == 'group':
try:
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
except KeyError:
gid = opts[fname][opt]['id']
elif opt == 'permissions':
os.chmod(fname, int(opts[fname][opt], 8))
if uid != -1 or gid != -1:
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
synchronize()
synchronize()
+1 -1
View File
@@ -17,4 +17,4 @@ $(TARGETS): % : %.c
strip -s $@
clean:
rm $(TARGETS) clortho
rm $(TARGETS) clortho urlmount
+10 -2
View File
@@ -62,6 +62,10 @@ int main(int argc, char* argv[]) {
exit(0);
}
ttyf = open(buff, O_RDWR | O_NOCTTY);
if (ttyf < 0) {
fprintf(stderr, "Unable to open tty\n");
exit(1);
}
if (currspeed == SPEED9600) {
cspeed = B9600;
strcpy(offset, ",9600");
@@ -87,7 +91,9 @@ int main(int argc, char* argv[]) {
cfmakeraw(&tty2);
tcsetattr(ttyf, TCSANOW, &tty2);
flags = fcntl(ttyf, F_GETFL, 0);
fcntl(ttyf, F_SETFL, flags | O_NONBLOCK);
if (fcntl(ttyf, F_SETFL, flags | O_NONBLOCK) < 0) {
fprintf(stderr, "Failed setting flags on tty\n");
}
while (read(ttyf, buff, 64) > 0) {
// Drain any pending reads
}
@@ -124,6 +130,8 @@ int main(int argc, char* argv[]) {
if (ws.ws_row < 24) { ws.ws_col = 24; }
ioctl(ttyf, TIOCSWINSZ, &ws);
tcsetattr(ttyf, TCSANOW, &tty);
ioctl(ttyf, TIOCCONS, 0);
if (argc > 1 && (strcmp(argv[1], "-c") == 0)) {
ioctl(ttyf, TIOCCONS, 0);
}
}
+15 -6
View File
@@ -23,11 +23,16 @@ unsigned char* genpasswd(int len) {
int urandom, ret;
passwd = calloc(len + 1, sizeof(char));
urandom = open("/dev/urandom", O_RDONLY);
if (urandom < 0) {
fprintf(stderr, "Failed reading /dev/urandom\n");
exit(1);
}
ret = read(urandom, passwd, len);
close(urandom);
for (urandom = 0; urandom < len; urandom++) {
passwd[urandom] = cryptalpha[passwd[urandom] >> 2];
}
passwd[len] = 0; // Should be redundant with calloc, but be explicit
return passwd;
}
@@ -80,7 +85,9 @@ int main(int argc, char* argv[]) {
for (curr = addrs; curr != NULL; curr = curr->ai_next) {
sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
if (sock < 0) continue;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) {
fprintf(stderr, "Failed setting reusaddr\n");
}
if (curr->ai_family == AF_INET) {
if (bind(sock, (struct sockaddr*)&net4bind, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "Unable to bind port 302\n");
@@ -100,7 +107,9 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "Unable to reach %s\n", argv[2]);
exit(1);
}
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
fprintf(stderr, "Unable to set timeout\n");
}
freeaddrinfo(addrs);
ret = read(sock, buffer, 8);
if (memcmp(buffer, "\xc2\xd1-\xa8\x80\xd8j\xba", 8) != 0) {
@@ -124,7 +133,7 @@ int main(int argc, char* argv[]) {
memset(buffer, 0, MAXPACKET);
if (currlen > 1000) {
fprintf(stderr, "Received oversized message\n");
exit(1);
exit(1);
}
if (currlen) {
ret = read(sock, buffer, currlen); // Max is 1000, well under MAX_PACKET
@@ -137,9 +146,9 @@ int main(int argc, char* argv[]) {
dprintf(sock, "\x04%c%s", slen, cryptedpass);
ret = write(sock, "\x00\x00", 2);
} else if (currtype == 128) {
printf("SEALED:%s", buffer);
printf("\n");
exit(0);
printf("SEALED:%s", buffer);
printf("\n");
exit(0);
} else if (currtype == 5) {
printf("%s", passwd);
printf("\n");

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