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

Compare commits

...

449 Commits

Author SHA1 Message Date
Jarrod Johnson 03fcd3f568 Fix indentation mistake in add_local_repositories 2023-01-11 09:53:01 -05:00
Jarrod Johnson 0e18a0c141 Fix routed nodeconfig in nodediscover 2023-01-09 08:55:46 -05:00
Jarrod Johnson cc0962f5e5 Fix add_local_repositories in v6 environment 2023-01-06 15:35:36 -05:00
Jarrod Johnson 57b6d8677b Fix syncfiles compatibility with IPv6 2023-01-06 09:33:06 -05:00
Jarrod Johnson 989066920d Fix formatting of the confluent2hosts man page 2022-12-16 09:49:19 -05:00
Jarrod Johnson d2a2383c92 Add man page for confluent2hosts 2022-12-16 09:44:52 -05:00
Jarrod Johnson 92a4b81247 Fix sample featuring non-existant attribute 2022-12-15 16:04:48 -05:00
Jarrod Johnson 130fce0320 Prevent bulk renames from stomping on itself
If multiple things try to renam to the same thing, block the action.
2022-12-15 15:42:10 -05:00
Jarrod Johnson 367854128a Several fixes for imgutil
imgutil had a number of issues
contending with a distribution-less
image being packed/unpacked.
2022-12-14 16:51:39 -05:00
Jarrod Johnson a5ee3a425f Provide full path to DSA key for deletion 2022-12-14 16:15:30 -05:00
Jarrod Johnson 34741cfaf2 Clear terminal after full draw
A resize may need cleanup below
the output.
2022-12-14 16:14:34 -05:00
Jarrod Johnson 9dce7d31ea Change screensqueeze to render in-line
Try to avoid clear screen and work within a logical
area under the command being run
2022-12-14 15:47:33 -05:00
Jarrod Johnson f524114e56 Remove DSA key from install 2022-12-14 13:00:24 -05:00
Jarrod Johnson 511fbe987b Suppress redundant output when writing to non-tty 2022-12-12 13:41:48 -05:00
Jarrod Johnson 7ccd1a42cd Add r8152 driver to diskless/genesis
It is a popularly requested USB ethernet device for network booting
2022-12-12 12:59:37 -05:00
Jarrod Johnson 848e5acea1 Make nfs available to diskless
This allows nfs based flows to be built by users
2022-12-12 12:35:00 -05:00
Jarrod Johnson e4dc57a810 Fix syntax of grep 2022-12-09 14:57:47 -05:00
Jarrod Johnson 0d5f48b116 Be more patient and more noisy about DHCP attempt over time 2022-12-09 13:05:10 -05:00
Jarrod Johnson 0a9fd8351b Correct firstboot on remote media deployment 2022-12-09 11:19:06 -05:00
Jarrod Johnson f55c25a52c Avoid mangling ipv4 in url in media deployment 2022-12-09 10:59:50 -05:00
Jarrod Johnson 93f6641a16 Carry deployment hook through to firstboot 2022-12-09 10:33:23 -05:00
Jarrod Johnson 7e2ae4de62 Restore ipv4-only media based deployment 2022-12-09 10:11:09 -05:00
Jarrod Johnson 82fe68216a Save deployer to mgr when media directed
Pre-empt the IPv4/v6 fallback designed.
2022-12-09 09:04:11 -05:00
Jarrod Johnson fb98cbaa4a Restore fallback to plain MANAGER specification
This is used in the media based deployment
2022-12-08 09:28:32 -05:00
Jarrod Johnson 2d5a016ad4 Tweak various issues for static analysis.
For autocons, though it's copying from a static source, use strncpy anyway,
despite the length being hardcoded already.  This makes static analysis happier.

Terminate the buff with a NULL.  This is superfluous as the strcpies that preceed
are guaranteed to null terminate, or exit the program.

In clortho, free(tmps), which is a valid leak, though clortho isn't long running.
Also, explicitly return 0, which is ultimately returned by main().

Static analysis could not figure out that padneeded implies that keylen is short of
chunk size, so change the check to be expressly the scenario that static analysis
was worried about directly, rather than indirectly.

Hint to static analysis that we don't care about the time as a time value by masking the
lower 32 bit explicitly.  This was already happening, but static analysis was afraid
that we wanted this as time instead of just some mutating value.
2022-12-06 15:35:49 -05:00
Jarrod Johnson 0cb3e3d216 Add rebase to osdeploy man page 2022-12-02 11:35:10 -05:00
Jarrod Johnson adbf96f23f Use bytearray in PXE processing
Python 2 and 3 are inconsistent
with how they treat memoryview,
but they are consistent on bytearray
treatment

Since rqv is merely a cheaply sliceable view of rq, use rq directly
for functions where the difference
between 2 and 3 would matter.
2022-12-02 11:24:00 -05:00
Jarrod Johnson c3576e4bd3 Enhance tab completion and extend some documentation and usage 2022-12-02 08:23:21 -05:00
Jarrod Johnson 84783c0c19 Add error check to noderange for nodedeploy 2022-12-01 14:09:25 -05:00
Jarrod Johnson 58a4c22aa2 Allow custom privilege levels through messages layer
If a user has created custom roles, designate custom. as
a prefix to indicate they really
mean what they say
2022-11-30 11:40:36 -05:00
Jarrod Johnson dce80de9d5 Correct syntax error in sample script 2022-11-30 11:09:57 -05:00
Jarrod Johnson c0f669cbdf Add DHCP bringup to confluent= el8diskless
For routed, if user specifies confluent=, use that as a clue to
initiate a dhcp transaction to
facilitate routed requests.
2022-11-30 11:09:39 -05:00
Jarrod Johnson 2e20341ea0 Have imgutil exec attempt to provide source repos 2022-11-30 10:15:12 -05:00
Jarrod Johnson 57d01ddcaa Base all web forwarding from 3901
This makes the web forwarding more predictable for
firewall rules
2022-11-28 15:17:59 -05:00
Jarrod Johnson 05bbd8f63a Further refine pxe logging and fix external DHCP pxe 2022-11-22 11:08:45 -05:00
Jarrod Johnson bb54ca0f8f Fix mistake caused by erant paste 2022-11-22 09:31:39 -05:00
Jarrod Johnson 2c7b58d47a Put brakes on configuration if no gateway and target is remote
This configuration would certainly destroy remote connectivity.
2022-11-22 09:24:18 -05:00
Jarrod Johnson 7341164f36 Have pure proxyDHCP trigger discovery and logs
For users that fully delegate core DHCP, provide discovery and
logging for PXE as it comes in.
2022-11-22 09:09:21 -05:00
Jarrod Johnson 626aca0691 Implement proxyDHCP remote operation
Provide means to function if we are
only the proxyDHCP service, delegated
from DHCP server.
2022-11-21 13:26:37 -05:00
Jarrod Johnson 132e40cdcb Fix syntax mistak in attribute update 2022-11-21 10:18:58 -05:00
Jarrod Johnson 90a8d80b45 Rework trusted networks to attribute
This allows  more flexibility and less oddity with how remote subnets are treated.
2022-11-21 09:57:27 -05:00
Jarrod Johnson 9eb2d25e37 Fix the subscribe/unsubscribe behavior in nodediscover client 2022-11-18 14:30:10 -05:00
Jarrod Johnson 963b35cd32 More aggressive timeout redfish checking
To make scans go faster, be more aggressive in
giving up on non-responsive targets.
2022-11-18 14:24:58 -05:00
Jarrod Johnson 9837ad7932 Add multi-address registration
This enables scanning a range or subnet
2022-11-18 14:14:31 -05:00
Jarrod Johnson 2c1e88d927 Provide usage when arguments omitted for nodegroupattrib 2022-11-17 14:04:16 -05:00
Jarrod Johnson 4af1dd5dd3 Omit extraneous related rpms 2022-11-15 11:46:52 -05:00
Jarrod Johnson b6487df32a Add missing ipmitool COPYING file to build 2022-11-14 16:20:42 -05:00
Jarrod Johnson cee3943e4d If node was found by key lookup, skip the sysdisco attempt 2022-11-14 15:26:52 -05:00
Jarrod Johnson 03e632b630 Add register to nodediscover command
This connects the remote registration
api to the usual CLI usage
2022-11-14 09:42:59 -05:00
Jarrod Johnson 6a9024d87e Fix bad/unsupported targets in registration 2022-11-11 15:00:59 -05:00
Jarrod Johnson 9fa8a7124e Pull the typical XCC scanning to remote registration path 2022-11-11 14:33:54 -05:00
Jarrod Johnson c6079a1ee6 Add ability to register remote systems in discovery
To start with, support Lenovo XCC systems
2022-11-11 12:16:41 -05:00
Jarrod Johnson ed94804b90 Change timezone substitution strategy
This uses a more self-evident breadcrumb to intuitively override
for users not wanting to use the confluent facility for timezone
adjustment.

There are other 'peculiar' substitutions that may prefer a breadcrumb
but they may require structure that would be tricky to implement
while also passing validation.
2022-11-10 08:57:04 -05:00
Jarrod Johnson 35abe0b9ad Fix IPv6 address support in geist plugin 2022-11-09 15:11:25 -05:00
Jarrod Johnson 81c14db9dd Pull EL8 IPv6 diskless to EL8 2022-11-08 16:37:00 -05:00
Jarrod Johnson 9179e02464 Fix diskless imgutil compatibility issues with EL9 2022-11-08 14:25:21 -05:00
Jarrod Johnson 48ac07e71d Correct placement of initialization of the confluent_websrv variable 2022-11-08 11:33:43 -05:00
Jarrod Johnson 635b559c87 Fix IPv6 syntax for ssh certs in EL8 diskless 2022-11-08 10:51:31 -05:00
Jarrod Johnson 6865e3259e Detect broken ipv4 and try ipv6 2022-11-08 09:45:08 -05:00
Jarrod Johnson 3d37692360 Add IPv6 to the imageboot script for EL profiles 2022-11-08 09:16:20 -05:00
Jarrod Johnson 20e6e1e521 Refresh functions to cope with v6-only usage 2022-11-08 08:52:29 -05:00
Jarrod Johnson 645abc2db7 Add [] to escape ipv6 prior to fetch of functions 2022-11-07 13:54:17 -05:00
Jarrod Johnson 578b05604b Reject fe80:: as always not same
For our purposes, this same-subnet check doesn't
apply to link-local
2022-11-07 13:50:45 -05:00
Jarrod Johnson e6e89bcee5 Steer v6 boot appropriately to v6 server 2022-11-07 13:25:26 -05:00
Jarrod Johnson a21af719c2 Get ipv6 compatible deploycfg api call 2022-11-07 11:43:51 -05:00
Jarrod Johnson 13f3d4c1a6 First pass at adding ipv6-only boot for diskless el8 2022-11-07 11:07:45 -05:00
Jarrod Johnson bd416f5000 Fix ipv4-free configuration in netutil 2022-11-03 14:43:49 -04:00
Jarrod Johnson ca1f459364 Add remote scan to autosense startup 2022-11-03 13:59:14 -04:00
Jarrod Johnson 7e02987a8c Extend rescan to cover remote switches 2022-11-03 13:31:57 -04:00
Jarrod Johnson 327a45ef33 WIP toward rescan hitting the renotify on subscripbed switches 2022-11-03 10:49:58 -04:00
Jarrod Johnson f1a0d22fe5 Fix subscription handling for the discovery api 2022-11-03 09:58:57 -04:00
Jarrod Johnson 1e78f11c2b Fix nodediscover CLI for subscribe/unsubscribe 2022-11-03 09:38:39 -04:00
Jarrod Johnson 7561b68b57 Rework subscriptions API
It's clear that the service will need
to explicitly track subscriptions
to enable rescan, for example,
and thus might as well restructure
the API around this information.
2022-11-03 09:21:15 -04:00
Jarrod Johnson a6090877ff Implement subscribe/unsubscribe in nodediscover
This provides a self-evident entry point from
CLI to extending the discovery to
affluent switches that support it.

This function will work with newer affluent
and Lenovo XCC2 systems.
2022-11-01 14:16:00 -04:00
Jarrod Johnson eec20a6457 Rename discovery remote to subscription
This will implement a consistent vocabulary
2022-11-01 13:51:54 -04:00
Jarrod Johnson 52de243090 Merge pull request #116 from andywray/master
Update nodeconsole -w to work with arbitrary terminal types
2022-11-01 13:40:11 -04:00
Andrew Wray 9742a5d81b Merge branch 'master' of https://github.com/lenovo/confluent 2022-11-01 13:30:18 -04:00
Andrew Wray e20e978e04 Add xterm as default terminal for nodeconsole -w 2022-11-01 13:25:20 -04:00
Jarrod Johnson 299785a7b8 Add manifest data for diskless images 2022-11-01 13:01:14 -04:00
Andrew Wray 8a3105cd51 Update nodeconsole -w to work in arbitrary terminal programs. 2022-11-01 11:29:41 -04:00
Jarrod Johnson 4802c52854 If attempt to auto-restart service, reduce severity of result
Provide feedback as a warning rather than aborting the command entirely
2022-11-01 10:05:24 -04:00
Jarrod Johnson 4a3834b481 Add missing sample syncfiles to suse15 profiles 2022-11-01 09:26:17 -04:00
Jarrod Johnson 13065a3c9d Add missing bits of suse 15 diskless profile 2022-11-01 09:10:17 -04:00
Jarrod Johnson 817038c6cf Specify the valid values for apiarmed
Further, add more warning text around apiarmed, as it is a serious security
decision to take on continuous.
2022-11-01 08:37:03 -04:00
Jarrod Johnson e0feb104ff Add facilities to subscribe/unsubscribe from discovery agents
This connects the new affluent discovery facility
to local discovery view.
2022-10-28 16:58:30 -04:00
Jarrod Johnson 3afd6ecb5d Temporarily enable IPv6
NetworkManage may go further out of it's way disabling ipv6, disable using proc to
overcome that
2022-10-28 12:10:03 -04:00
Jarrod Johnson fd14221ab5 Avoid truncating console logging of firstboot
With significant firstboot output, there was a tendency
for tail to be killed before it relayed all the content.

Change to run the firstboot in a subshell in the background,
and have tail explicitly run until that subshell naturally
exits and then tail will cleanly exit
2022-10-28 09:30:12 -04:00
Jarrod Johnson d534f29c57 Implement fastpath for delegated discovery
When an enlisted discovery agent
notifies, skip slow searches and use
the agents information directly.
2022-10-27 15:42:58 -04:00
Jarrod Johnson f6d8294e83 Check IP viability before commencing configuration
This avoids a pointless partial configuration from proceeding.
2022-10-27 15:41:13 -04:00
Jarrod Johnson 6c806c8171 Fix tentative path for real path 2022-10-27 10:03:18 -04:00
Jarrod Johnson 8bf067cac8 Fix issues in the auth nets logic 2022-10-25 12:52:22 -04:00
Jarrod Johnson 0d2a1b856b Fixes for the auth_nets configuration 2022-10-25 12:35:18 -04:00
Jarrod Johnson 4864d6abb0 Add mechanism to extend authentication to remote networks
This allows user to designate certain networks to be treated as
if they were local.

This enables the initial token grant to be allowed to a remote network.

This still requires that the api be armed (which should generally be a narrow window of
opportunity) and that the
request be privileged, it
just allows remote networks to be
elevated to be as trusted as local.
2022-10-25 11:26:44 -04:00
Jarrod Johnson 5794cd5d12 Modify firstboot to fall through to ipv6 if ipv4 failed 2022-10-25 08:21:42 -04:00
Jarrod Johnson 9964b33414 Fall back to v6 in more scenarios 2022-10-24 17:03:00 -04:00
Jarrod Johnson be2959f365 Fall through to ipv6 if v4 is blank 2022-10-24 16:42:02 -04:00
Jarrod Johnson 31bf8f2a11 Numerous fixes for the cmdline directed deployment in EL8 2022-10-24 16:13:59 -04:00
Jarrod Johnson 6df9ca54ca Ensure access to dracut utility funcitons in pre-trigger hook 2022-10-24 15:52:40 -04:00
Jarrod Johnson c57090a670 Correct order of find and replace strings in by-mac boot 2022-10-24 13:26:06 -04:00
Jarrod Johnson 29ad1bd57e Add various ways to look up boot file 2022-10-24 12:31:23 -04:00
Jarrod Johnson f245f5cac5 Inject a hook for cmdline to specify confulent server
This enables a more manual approach
to indicate the deployment server.

This carries the assumption that a
normal OS autonetwork config
will get the node to the right network.

This is one step toward enabling a scenario where the target is remote and the DHCP is not going to relay, but instead the deployment feeds the DHCP a confluent URL entry point to get going.

Using this parameter precludes:
-Enhanced NIC auto selection.  If the OS auto-selection fails to
identify the correct interface, the profile will need nic name baked in.
-Auto-select deployment server from several.  This will mean that any
HA will require IP takeover be externally handled

This is of course on top of the manual process of
indicating confluent in kernelargs.
2022-10-17 13:07:18 -04:00
andywray 3da2ac61cc Update nodeconsole man page with more -w examples 2022-10-15 21:52:14 -04:00
andywray a25443c81d Update help, check for null env variable 2022-10-15 21:29:56 -04:00
andywray bd00059667 Update man page for -w with arbitrary terminal types 2022-10-15 21:09:31 -04:00
andywray 2c58320f32 Update nodeconsole -w to work with arbitrary terminal types 2022-10-15 21:03:35 -04:00
Jarrod Johnson 167ef0ae33 Merge pull request #87 from erderial/patch-5
Changed the Popen to skip the communication
2022-10-14 11:06:57 -04:00
erderial 8827e7efe8 Changed the Popen to skip the communication
Changed the Popen to skip the communication
added escape method
2022-10-14 18:03:51 +03:00
Jarrod Johnson 4ba0087c93 Close typo in edited attributes 2022-10-14 08:10:39 -04:00
Jarrod Johnson c3afd45475 Normalize uid
python2 was crashing when getting unicode over the link.
2022-10-13 17:52:26 -04:00
Jarrod Johnson 730af73069 Correct open string in the attributes edit 2022-10-13 14:58:41 -04:00
Jarrod Johnson 84407e3d2b Correct syntax error in pxe handler 2022-10-13 14:49:09 -04:00
Jarrod Johnson a0702681da Merge remote-tracking branch 'lenovo/master' 2022-10-13 12:14:41 -04:00
Jarrod Johnson 88b741e026 Add 'firmwarenone' ip method
Add a seting to allow user to suppress all DHCP offer during
PXE/HTTP activity.  This enables configurations
where users want to externally manage filename explicitly in their own dhcp configuration.
2022-10-13 12:11:46 -04:00
Jarrod Johnson 4486fb5f81 Merge pull request #86 from erderial/patch-4
changed subprocess .run to Popen
2022-10-13 10:56:59 -04:00
Jarrod Johnson 8de7402b56 Add ability to get booturl redirect
In some environments, there's a desire to manually manage DHCP configuration.
In such a case, provide a url
that can be given to the dhcp server
to allow confluent to control the profile
without updating such a DHCP service.

With this change, a node can be told to boot:
http://confluentserver/confluent-api/booturl/by-node/n123/boot.ipxe

To be redirected to the currently applicable os profile.
2022-10-13 10:54:11 -04:00
erderial dc62fc3f51 changed subprocess .run to Popen
changed subprocess .run to Popen to open separate subprocesses for simultaneous run of console windows.
2022-10-12 22:35:05 +03:00
Jarrod Johnson 23ec46bb8b Add vlanid to the vmware deployment support 2022-10-12 13:08:57 -04:00
Jarrod Johnson 145be7ce31 Correct omission in private key access message during diskless boot 2022-10-07 16:47:48 -04:00
Jarrod Johnson d5d0852890 Tighten redfish check timeout
The default timeout is overkill in the nodediscover scenario.
Notably, we can receive replies from unreachable IP addresses,
and those will extend rescan to the full timeout.  The devices should
comfortably reply within 3 seconds, making scans exit in
a timely fashion.
2022-10-07 09:22:37 -04:00
Jarrod Johnson 4fed609050 Avoid enumerating members of a bond
Various parts of confluent that go to try to use
all the interfaces will now skip bond members.

One example problem is that joining the SSDP multicast
group for SSDP would cause the kernel to IGMPv6 out
on bond members as well as the bond itself.  This change
ensures that the bond interface is only used and never
bypassed.
2022-10-07 08:52:02 -04:00
Jarrod Johnson a77f211b8e Fix json restore of users and groups
Defaults were erroneously used
do to oversight and mistake in teh json restore code.
2022-10-06 09:23:43 -04:00
Jarrod Johnson 3c29a5aa7f Enable non-admin users for web gui 2022-10-06 08:49:00 -04:00
Jarrod Johnson 6eb4bf28e5 Another iteration to try to have IP adaptive syncfiles
It is likely that a client connects from fe80::, which
is explicitly omitted from ssh principals.

This time, have the client provide all currently set IP addresses
and the server will make a determination.

There remains the possibility it misconfigures a nic and tries to use that,
inducing failure.  One strategy would be to filter the addresses and
only provide from the 'current' interface.  Another is to just take
the hit as the node is likely going to suffer a lot from such a
misconfiguration anyway.
2022-10-05 12:23:47 -04:00
Jarrod Johnson 4cb222068f Sync up EL7 syncfileclient 2022-10-05 11:53:59 -04:00
Jarrod Johnson c612129d64 Have syncfiles attempt to use client ip, if feasible
When a node installs, it may not have it's node mapped address up,
or may not have one at all. Try to use the ip if it would be in the
same set that produced it's ssh certificate.

There remains a gap if a system has no static addressing *and* doesn't
map nodename to IP, but we have an impasse as the situation is too fuzzy
to grant a prinicpal in an SSH cert, and without that we can't securely
attempt rsync.  For now, this scenario would still fail and I will
just hope that doesn't come up.
2022-10-05 08:31:37 -04:00
Jarrod Johnson 13ba95bb99 Give up on waiting for disks
Some profiles may have all disk support suppressed through blacklist until %pre comes
along to fix it.  This avoids /dev/disk ever existing.

Wait up until 10 seconds before giving up. This gives disk subsystem a fair chance to
speed up and avoid a wait, with a fallback worst case of 10 seconds
2022-10-04 10:39:02 -04:00
Jarrod Johnson 75484db014 Fix macok incorrect value on finding the mac 2022-10-03 10:33:21 -04:00
Jarrod Johnson 0adfec0c44 Bump genesis version 2022-09-30 15:04:25 -04:00
Jarrod Johnson 4f67ed661d Remove curl from genesis
It is redundant with python function, and drags
sasl along as a significant dependency.
2022-09-30 15:02:54 -04:00
Jarrod Johnson 763b157802 Fix syntax error 2022-09-30 12:36:12 -04:00
Jarrod Johnson 6e803e9fca Add insecure protocol check 2022-09-30 12:22:39 -04:00
Jarrod Johnson 9ecd3e3ac7 Add API check
Particularly SELinux is a frequently missed configuration
facet, alert when the selinux is blocking.
2022-09-30 12:17:31 -04:00
Jarrod Johnson ee3aef0a4c Mark COPYRIGHT as legal in rpm 2022-09-30 11:02:55 -04:00
Jarrod Johnson b7dfe20286 Add legal artifacts to confluent_server 2022-09-30 10:43:18 -04:00
Jarrod Johnson 28f4571c8e Go for xCAT-neutral terminology 2022-09-29 16:09:46 -04:00
Jarrod Johnson c647dec069 Add message on successful node attribute run 2022-09-29 15:45:07 -04:00
Jarrod Johnson 903de26dd8 Add node attribute checks to selfcheck 2022-09-29 15:27:12 -04:00
Jarrod Johnson 76491785db Merge pull request #84 from erderial/patch-2
Adding wcons functionality for nodeconsole
2022-09-29 13:17:04 -04:00
Jarrod Johnson b7a424f4cc Merge pull request #85 from erderial/patch-3
Added -w flag for nodeconsole
2022-09-29 13:16:30 -04:00
erderial e180ec0bc8 removed null from the flow 2022-09-29 20:15:55 +03:00
Jarrod Johnson fcf342165c Fix apiclient detection of default interface
It was failing to record the default interface in its checks.
2022-09-29 13:15:37 -04:00
erderial d88207a468 Added -w flag for nodeconsole
Added -w flag for nodeconsole command, for xterm for now.
2022-09-29 19:24:57 +03:00
erderial e09c594ffb Adding wcons functionality for nodeconsole
Adding wcons functionality for nodeconsole for xterm first.
2022-09-29 18:58:06 +03:00
Jarrod Johnson cf000d6872 Add node name resolution check
A common scenario for closed networks
is a misconfigured DNS situation.

Detect and report, as this can wreak havoc on a confluent instance.
2022-09-29 09:57:43 -04:00
Jarrod Johnson 04ee7a8c31 Add '-l' to nodersync command 2022-09-29 08:05:02 -04:00
Jarrod Johnson 5a62307d1e Restore config by name
The change to allow CIDR syntax
broke for configurations that use name for
bmc 'address'.

Fix by letting getaddrinfo have a chance to process the ip before
trying to pton it.
2022-09-27 17:05:13 -04:00
Jarrod Johnson e5360b3b2f Have suse give up after a minute
If a network problem prevents the deployment nic from working,
give up eventually
2022-09-27 08:33:00 -04:00
Jarrod Johnson 484dbd9f74 Fix default interface detection 2022-09-23 16:10:55 -04:00
Jarrod Johnson 52626c56a8 Fix issues with the license restructure 2022-09-22 09:51:02 -04:00
Jarrod Johnson 885354f6b7 Rework license storage structure 2022-09-22 09:09:46 -04:00
Jarrod Johnson 0c08d8f6d3 Provide helpful error on bad user/password in delta pdu 2022-09-21 16:19:13 -04:00
Jarrod Johnson 25c66e163c Some notes on TPM recovery 2022-09-21 10:50:05 -04:00
Jarrod Johnson ba7832eb40 Fix restoring blocking on exit
When confetty/nodeconsole exits, correctly
clear NONBLOCK flag is an idempotent way.
2022-09-21 09:48:39 -04:00
Jarrod Johnson 440fcf2663 Further tweak el8 ident image support 2022-09-14 12:18:11 -04:00
Jarrod Johnson 0286b2e776 Further fixes to identity image in el8 2022-09-14 11:57:02 -04:00
Jarrod Johnson aa2a8e2d5a Adjust ported coreos ident image support to el8 install environment 2022-09-14 11:18:01 -04:00
Jarrod Johnson 65e97073e2 Add identity image support to EL8 scripted install profiles 2022-09-14 11:00:04 -04:00
Jarrod Johnson 5a935d99fc Have SMM devices register cleanly
In remote discovery registration, the
SMM path was not fully implemented
2022-09-13 13:29:42 -04:00
Jarrod Johnson 785d7d40fa Add COPYRIGHT notice 2022-09-09 12:50:38 -04:00
Jarrod Johnson 91f4ef9747 Attempt to fill in more missing metadata 2022-09-09 12:26:38 -04:00
Jarrod Johnson 9025918609 Fix missing information in packaging metadata 2022-09-09 12:10:32 -04:00
Jarrod Johnson 7cbd105ae3 Workaround TSM issue with redfish configuration 2022-09-09 12:04:38 -04:00
Jarrod Johnson 2bc2736da4 Fix neighutil invocation of ipn_is_local 2022-09-08 16:07:04 -04:00
Jarrod Johnson 4a8af0ad85 Fix assumptions about ip going into netutil 2022-09-08 14:35:16 -04:00
Jarrod Johnson 7b98b7dc00 Fix wrong uid after duplicate identity check 2022-09-08 09:51:21 -04:00
Jarrod Johnson cde18bcd3a Cap the number of deferred packets
Prevent deferred packets from growing endlessly
if activity is keeping the loop running.
2022-09-07 08:04:47 -04:00
Jarrod Johnson 60cfa1d3c5 Skip peer probe on remote
When remote ip is detected,
communicate by returning False
instead of None.

Use this indication to let ssdp
skip a transmit and growing
pending list in such a case.
2022-09-06 16:40:34 -04:00
Jarrod Johnson 596fcb0f4c Implement mitigations for ovewhelming SSDP
First, for a given contiguous set of snoop activity, start ignoring a given peer during that contiguous chenk after it has been considered once.

Further, make get_hwaddr cheaper for attempts against
remote IPs.

To facilitate the above, create an efficient 'ip_is_local' to be
a relatively cheap function, with
potential to cache result in future
if it needs to be even cheaper.
2022-09-06 16:08:31 -04:00
Jarrod Johnson 7980534bad Fix confluentdbgcli.py for python3 2022-09-02 15:11:30 -04:00
Jarrod Johnson 6c1f87aeb7 Add mechanism for copernicus to request any confluent
This can be used for network debug in a generic way, to identify vlan adjacency without regard to nodedoploy state or uuid matching.
2022-09-02 13:32:05 -04:00
Jarrod Johnson 1c811dbf3e Fix python path automatically in confluent_selfcheck 2022-09-02 10:11:12 -04:00
Jarrod Johnson 503746131c Add selfcheck to packaging 2022-09-02 09:53:06 -04:00
Jarrod Johnson a0037a305c Add confluent_selfcheck to server package 2022-09-02 09:44:13 -04:00
Jarrod Johnson d1d15f29c1 Add facility to fix confluent uuid problem 2022-09-01 13:26:25 -04:00
Jarrod Johnson b98759698a Sync up getinstalldisk and add another m.2 model 2022-09-01 13:22:50 -04:00
Jarrod Johnson 20992b2d17 Add check for actual file download 2022-09-01 13:21:39 -04:00
Jarrod Johnson 67f0c8a81b Add IPv6 and insecure boot checking 2022-09-01 13:17:17 -04:00
Jarrod Johnson 28331adced Add TFTP and ssh key checks to checkconfluent 2022-08-31 17:30:41 -04:00
Jarrod Johnson ed91e0f2f3 Have askpass delete itself
This causes ssh-add to give up, instead of endlessly rerunning
the askpass script.
2022-08-31 17:17:33 -04:00
Jarrod Johnson 99a5c454ba Begin work on a sanity checker
Check various facets that are frequently not quite right, making
recommendations on how to correct.
2022-08-30 16:59:40 -04:00
Jarrod Johnson 908e51221c Correct minor formatting mistake in warning 2022-08-29 12:22:14 -04:00
Jarrod Johnson 8277701af6 Rewrite site ssh even if
the /etc copy already exists.

IT may be that /var/lib/confluent is being repaired, in which
case just copy existng over while giving warning.
2022-08-29 12:16:35 -04:00
Jarrod Johnson 570611f22b Have osdeploy initialize skip SSH regen
When generating new key materials, most people say 'yes' and cause problems
where they cycle valid keys without
realizing the significance.

Replace prompting with an emphasized warning instead.
2022-08-29 11:10:45 -04:00
Jarrod Johnson 2a3e6cd6f1 Change websocket dependency name in EL7 2022-08-26 08:16:22 -04:00
Jarrod Johnson 4e44188253 Ensure early existance of ca.pem
Prior to install, enable apiclient
to have the ca.pem where it would
expect it to be.
2022-08-25 16:45:00 -04:00
Jarrod Johnson 352da94005 Implement rebase feature ofr osdeploy
Permit user to opt into a rebase of a
profile, to pick up potential updates
from the confluent packaged stock
profiles for files the user has not yet
customized.
2022-08-25 15:21:49 -04:00
Jarrod Johnson e774add916 Set timezone on diskless boots by default
During onboot, use confluent directed timezone
info to set the timezone if the
disk image is incorrect.
2022-08-24 16:09:05 -04:00
Jarrod Johnson d72bac5faf Add more to the SUSE profiles
Add exetensibility to pre and sample directories
2022-08-24 16:07:38 -04:00
Jarrod Johnson 93b7547c58 Enable IPMI for user if IPMI has been enabled globally elsewhere
Scenarios have come up with trying to repair partially
configured configuration, break
the global and per-account check
into separate concerns.
2022-08-24 10:13:53 -04:00
Jarrod Johnson 31b3d6ea06 Move manifest data into dedicated file 2022-08-24 09:29:48 -04:00
Jarrod Johnson 42b4944542 Fix build script for vtbufferd
Update for new licensing standard.
2022-08-23 16:08:25 -04:00
Jarrod Johnson 04526ae8cc Update license to NOTICE file
Bundled third party license content is now described under NOTICE file.
2022-08-23 15:52:50 -04:00
Jarrod Johnson d97c508d86 Add hash manifest of new os profiles
When importing an image and taking stock copy, mark the files to allow detection of stock
versus customized profile content.

This will be used by a rebase command to know when
to overwrite or when to leave a file alone.
2022-08-23 15:25:17 -04:00
Jarrod Johnson 801e43936c Revise ESXi routed deployment
-Have apiclient set timeout on getting credential to avoid hang
-Change dcuiweasel to start shell earlier for better debug
-Do not expire the ident token if deployment is armed continuous anyway
2022-08-19 16:06:46 -04:00
Jarrod Johnson a445107c7f Fix setting privilege level alone for ipmi
The logic incorrectly had it depend on password also being present.
2022-08-19 09:10:52 -04:00
Jarrod Johnson dde66c53c9 Dynamically ascertain name scheme for Delta pdu
Some delta pdus have different name schemes
than others, take the hit of
awkward parsing to autodetect.
2022-08-17 10:20:26 -04:00
Jarrod Johnson 57fcc8a243 Start SSH agent even on older ssh
The unusual path to automation key for syncfiles and ansbile
is most easily handled by ssh-agent, even if no passphrase
will be used
2022-08-16 15:06:26 -04:00
Jarrod Johnson 41c3c4fb5f Handle metapackages
If a metapackage gets pulled in
as related, do not let it mess up
the license list.
2022-08-11 14:09:55 -04:00
Jarrod Johnson 7b3c1e5405 Capture license content from related packages
Some files that get gathered into genesis
have their license in a related rpm, but that related rpm does not otherwise get captured.

So pull all correlated license material
in the license capture process.
2022-08-11 13:42:36 -04:00
Jarrod Johnson 047cd6302a Add wait for IP connectivity
After config, there may be a delay
before the configuration takes effect.

This delay can break nodeconfig.

Try to wait for the delay to pass.
2022-08-09 08:50:19 -04:00
Jarrod Johnson f0c8eee956 Add facility to auto-exec nodeconfig on discovery
This permits more open ended node configuration when discovery happens.
2022-08-08 16:13:01 -04:00
Jarrod Johnson 1125e4c712 Advance concetpual debian support 2022-08-08 14:23:01 -04:00
Jarrod Johnson 2aed973a8b Have apiclient check all hosts for connectivity
Further allow it to output the result and feed that to esxi startup.
2022-08-08 11:18:35 -04:00
Jarrod Johnson 078e755ec7 Actually feed preseed into config
It must be done to the live cdebconf state *and* preseed.cfg,
since one is referenced immediately and then discarded when
preseed read in.
2022-08-05 16:05:26 -04:00
Jarrod Johnson e3ed54ebf3 Advance the bootstrap of debian installation concept 2022-08-05 14:05:27 -04:00
Jarrod Johnson 9cac0c1705 Amendments to confluent initialization 2022-08-04 16:51:33 -04:00
Jarrod Johnson 2fe852f122 Add draft of a debian-installer-startup script
This is a draft for use in debian profiles to get started
2022-08-04 16:27:58 -04:00
Jarrod Johnson 258c4970c0 Change makeksnet from yaml to json
Older vmware did just json, which is fine, just have to use
the json file that was there just in case something like this
came up.
2022-08-04 10:39:10 -04:00
Jarrod Johnson 610e7bf044 Constrain ip-less offers
PXE and HTTP client define a behavior for using non-address portion of an offer if the
offer has none. However, other clients, like
udhcpc will be confounded by such offers.

Ensure that client specifically sets VCI to indicate
it understand such an offer before replying.

This as yet generally doesn't matter as UUID is not sent by OS installers, however
some OSes can't do do API calls over LLA, and
thus we want to start allowing OS requests with
UUID and only offer when it makes sense.
2022-07-29 12:49:25 -04:00
Jarrod Johnson 6b95bc7932 Fix key name 2022-07-27 12:08:58 -04:00
Jarrod Johnson 35701cdf01 Allow ident net config to override deploycfg
In routed deployment, deploycfg will
be missing network info, use
the ident provided network if detected.
2022-07-27 11:41:29 -04:00
Jarrod Johnson c1ad4357dc Fix for loop variable name 2022-07-27 08:37:09 -04:00
Jarrod Johnson 9987cb5c03 Advance state of routed esxi deployment 2022-07-27 07:51:41 -04:00
Jarrod Johnson e8d2f550b8 Draft work on ESXi identity image deployment
Prepare for routed deployment of ESXi
2022-07-25 17:05:30 -04:00
Jarrod Johnson 9fe156601b Add netmask to net config data
Some clients may want to consume netmask
without going to the trouble of converting,
so provide the extra data a different way.
2022-07-25 15:54:07 -04:00
Jarrod Johnson 830c9e171f Add IP detection on local segment from remote registration
If an ip address is missing, but linklocal is set, try to search the
local nics for a viable connection to use.
2022-07-22 17:17:35 -04:00
Jarrod Johnson 0ac2bce883 Have XCC use a null string for ipaddr
This avoids the remote discovery failure for now.
2022-07-22 13:31:11 -04:00
Jarrod Johnson 6f619bc896 Change ipaddr to a property for allowing logic 2022-07-22 13:23:47 -04:00
Jarrod Johnson bdd5dddce0 Fix typo in log message 2022-07-21 11:20:31 -04:00
Jarrod Johnson d61dcee4fa Fix handling of explicit prefix
Switch to just ip in time and return prefix
length as int.
2022-07-21 11:06:01 -04:00
Jarrod Johnson d385ad1a0a Support explicit prefix in xcc discovery 2022-07-21 10:13:00 -04:00
Jarrod Johnson ef2c7b5bbc Fix errant reference 2022-07-21 09:57:29 -04:00
Jarrod Johnson 2256b341b9 Tolerate '/' in hardwaremanagement.manager
This paves the way to allowing CIDR syntax to
indicate prefix length for remote XCCs
2022-07-21 09:54:00 -04:00
Jarrod Johnson 79421a724f Fix base64 decode of fingerprint
base64 was being done against wrong variable
2022-07-21 08:33:12 -04:00
Jarrod Johnson 7751a71851 Merge branch 'remote_discovery' 2022-07-20 16:22:01 -04:00
Jarrod Johnson 2d8bcb4c0f Incorporate auto discovery for remote discovery
Avail ourselves of secure vouching to handle new and
replaced.
2022-07-20 16:21:25 -04:00
Jarrod Johnson 0dc7b532cc Implement registration and retrieval
Remote discovery can now be registered by
switches.
2022-07-20 13:01:11 -04:00
Jarrod Johnson 97c520db10 Make error message more clear 2022-07-19 10:16:48 -04:00
Jarrod Johnson 480849640e Make error message more clear 2022-07-19 10:16:18 -04:00
Jarrod Johnson 42055e2648 Merge branch 'master' into remote_discovery 2022-07-18 15:52:42 -04:00
Jarrod Johnson 789def17d1 Do not tail old content from firstboot
Tail only new content being fleshly logged
2022-07-18 09:35:40 -04:00
Jarrod Johnson 926f9e2cdd Enable more plugins for collective routing 2022-07-13 08:58:31 -04:00
Jarrod Johnson c627ac73ee Make a specific error on nodesupport 2022-07-11 16:18:33 -04:00
Jarrod Johnson 2058d1fba8 Add ESXi 8 support
ESXi changes security policies, adapt
to navigate both old and new sorts of rules
2022-07-08 13:36:21 -04:00
Jarrod Johnson beb4c66b65 Fix spelling in error message 2022-07-08 10:50:45 -04:00
Jarrod Johnson f467cfe7c4 Add log message for mac interrogation of switch
Clarify when things go wrong due to certificate.
2022-06-30 13:28:26 -04:00
Jarrod Johnson 3bd11da807 Remove extraneous line from setupssh.sh 2022-06-30 13:27:39 -04:00
Jarrod Johnson eaffac0433 Allow blanking a value to default a plugin attribute 2022-06-30 13:27:25 -04:00
Jarrod Johnson 500e083746 Enable ipmi user if required
If redfish models ipmi as an account type, and user wants ipmi, add it to the account.
2022-06-28 14:25:12 -04:00
Jarrod Johnson 29965f6ec9 Add a catch all to redfish for xcc
Newer xcc changes things yet again, but
we are comfortably in the firmware that can be bootstrapped
with redfish, so use that instead once we've cleared the
redfish incapable variants.
2022-06-28 14:24:48 -04:00
Jarrod Johnson f65ff1268a Add new state offline to confluent storage states 2022-06-28 12:15:54 -04:00
Jarrod Johnson a54a9a5d09 Enable ipmi user if required
If redfish models ipmi as an account type, and user wants ipmi, add it to the account.
2022-06-27 14:34:37 -04:00
Jarrod Johnson 9b79da9522 Add a catch all to redfish for xcc
Newer xcc changes things yet again, but
we are comfortably in the firmware that can be bootstrapped
with redfish, so use that instead once we've cleared the
redfish incapable variants.
2022-06-27 14:12:51 -04:00
Jarrod Johnson 04c2b1a322 Provide an authenticated path for discovery registration 2022-06-22 16:47:40 -04:00
Jarrod Johnson 559a7ca7b8 Merge branch 'master' into remote_discovery 2022-06-21 14:56:56 -04:00
Jarrod Johnson 0a10311fea Add more verbose ssh connection feedback
Make the nature of connecting more explicit and errors
more reliably holding the session up and asking for relog
2022-06-17 15:35:06 -04:00
Jarrod Johnson 715bcb2f50 Prevent shell session from playing console output 2022-06-17 14:54:41 -04:00
Jarrod Johnson 8d5ad0d4a6 Fix EL7 spec file syntax 2022-06-17 13:11:08 -04:00
Jarrod Johnson 1387a67b61 Correct spec syntax error 2022-06-17 12:37:20 -04:00
Jarrod Johnson 4b08273379 Move EL7 back to python2
EL7's python3 support is barren and would require a lot of dependency
work.

Move EL7 back to python2 and will deprecate EL7 support when we ditch
python2
2022-06-17 12:10:44 -04:00
Jarrod Johnson e0c3232180 Fix syntax error in el7 prescript 2022-06-17 09:30:26 -04:00
Jarrod Johnson bd95f00680 Fix EL7 typeo in postscript 2022-06-17 09:08:12 -04:00
Jarrod Johnson bfecaa389d Adjust to correct ntp for 9 and up 2022-06-17 09:06:33 -04:00
Jarrod Johnson 7aca7a19f9 Install apiclient in a normal location for EL7 2022-06-15 16:42:50 -04:00
Jarrod Johnson c400671ad7 Add old profile compatible apiclient location 2022-06-15 16:39:55 -04:00
Jarrod Johnson c96955b369 Add catch-all packaging for EL7
EL7 now precompiles and flags it in the packaging
2022-06-15 16:34:57 -04:00
Jarrod Johnson 2bb519c158 Fix EL7 deployment compatibility 2022-06-15 16:10:57 -04:00
Jarrod Johnson e5c33cacd5 Change SuSE rpm name to pyyaml 2022-06-15 15:11:07 -04:00
Jarrod Johnson 03f0f16ed8 Change to dnspython for suse 2022-06-15 15:06:52 -04:00
Jarrod Johnson 0f815dc7d3 Switch to python3 for all builds 2022-06-15 15:00:19 -04:00
Jarrod Johnson dbdc99905d Update confluent server to python3 dependencies only 2022-06-15 14:31:32 -04:00
Jarrod Johnson 2d665fb816 Merge branch 'py2to3' 2022-06-15 12:42:04 -04:00
Jarrod Johnson 26e0fa8c91 Add swraid example to misc 2022-06-15 09:50:06 -04:00
Jarrod Johnson 83da1359df Add a sample vroc 'pre' script for reference 2022-06-15 08:40:23 -04:00
Jarrod Johnson 0f1581f8f7 Add a sample script to fetch user/pass info from confluent db 2022-06-10 14:18:35 -04:00
Jarrod Johnson 13d4d1dd98 Ensure that python3 will execute before doing migrate
If python3 is not the executable name, prevent the restoration
attempt from happening.
2022-06-10 11:19:21 -04:00
Jarrod Johnson db5c31030d Migrate DB on start
If python2 db format detected, use python2 to
dump to text, then python3 to restore to
get the python3 native version
2022-06-09 16:23:35 -04:00
Jarrod Johnson 6f484aab53 Allow restore to replace unsupported format
Going from python 2 to python 3, the dbm format
goes from the default to unsupported.

This allows a python3 confluentdbutil restore to handle
a python2 dump of unsupported format.
2022-06-09 15:49:06 -04:00
Jarrod Johnson 44cf56857e Add fallbacks to PyCrypto compatible names
Cryptodome is not always packaged with the
explicit form, fall back to the Crypto names
hoping that the user wouldn't be trying to use
PyCrypto is this day and age.
2022-06-09 10:48:12 -04:00
Jarrod Johnson 17e223e21c Suppress stray fakecmdline output 2022-06-09 09:15:56 -04:00
Jarrod Johnson 5ee6d0ca5d Fix ordering of arguments
The -text was between startdate and its argument
2022-06-09 08:30:03 -04:00
Jarrod Johnson 2825af19c4 Do not clutter the pem file with text 2022-06-09 08:04:15 -04:00
Jarrod Johnson e9ac43f49e Add autocons to bootloader config
In newer suse, the fake cmdline trick is fouled, so carry into
autoyast file
2022-06-09 07:55:42 -04:00
Jarrod Johnson 5ee0572f54 Change to using 'full' openssl ca
OpenSSL does not allow access to custom start
date without the full 'ca' facility, do the
work to set up the full CA and then
backdate certificates.

This does open the way for managed CA if required.
2022-06-08 17:10:11 -04:00
Jarrod Johnson 3da67db806 Fix early bailout of nodeconfig
Do not have nodeconfig bail out on first sign of trouble, attempt to continue.
2022-06-08 13:43:25 -04:00
Jarrod Johnson 826c9eedb0 Document -l in nodeconsole man page 2022-06-08 11:14:51 -04:00
Jarrod Johnson af19e98ce8 Remove use of eventlet in client side
The client side does not use eventlet, so allow
fallback to the normal socket and select
to keep the client module whole in the
face of that missing.
2022-06-08 11:03:56 -04:00
Jarrod Johnson 0dcd1442c9 Avoid nodeshell/noderun getting stuck on stdin
Some applications will tend to hang on stdin if
stdin is readable. Provide /dev/null to suppress that behavior.
2022-06-08 10:21:08 -04:00
Jarrod Johnson 2f904d10e9 Fix identity yaml parsing
Switch to sed rather than trying to use head.  There may or
may not be a second match, so need sed logic to understand
whether it's a match or not to keep or throw out.
2022-06-08 09:23:56 -04:00
Jarrod Johnson fdaecf2cbc Relay input errors to caller in dispatch 2022-06-07 13:36:31 -04:00
Jarrod Johnson 8bb565e938 Implement error on invalid attribute filter
This will cause noderange to probably error out.

Note that invalid attribute names starting with net, power, or custom are treated as always valid.
2022-06-07 11:45:06 -04:00
Jarrod Johnson 0b20e0b634 Fix confuent scan for python 3.9
Python 3.9 removes the scope from the string
address, put it back if missing since it's required
to actually be usable.
2022-06-07 10:55:56 -04:00
Jarrod Johnson b32c343a0c Switch to importlib for newer python
imp is deprecated, despite being much simpler,
so use importlib in python3.6+ distributions.
2022-06-07 10:35:08 -04:00
Jarrod Johnson 942121d73a Remove unavaiable clear from el9 diskless 2022-06-07 09:48:52 -04:00
Jarrod Johnson 55f42bc909 Add el9 imgutil 2022-06-07 09:06:59 -04:00
Jarrod Johnson 84037420d1 Add el9 tree for diskless 2022-06-06 13:18:37 -04:00
Jarrod Johnson e7bea0df45 Advance work on EL9 diskless support 2022-06-06 13:10:22 -04:00
Jarrod Johnson 94955dd091 Bring down track number
Use 2 heads with 4 sectors per track to get the track number down for
reasonable images.

Newer mtools forbids large track numbers.
2022-06-06 11:57:19 -04:00
Jarrod Johnson 739890471d Support newer python that removes fromstring
Python 3.9 does not understand the fromstring function anymore.
2022-06-06 11:37:39 -04:00
Jarrod Johnson 0baa2eecab Break out on deployment failure 2022-06-06 11:05:22 -04:00
Jarrod Johnson 46fec89460 Fix routed deployment api key usage 2022-06-06 10:15:54 -04:00
Jarrod Johnson 997bd3ac34 Implement outlet control for Eaton PDU 2022-06-03 17:06:37 -04:00
Jarrod Johnson 799050fea2 In-progress Eaton PDU support 2022-06-03 16:26:07 -04:00
Jarrod Johnson 80186f2c77 Add a delta pdu plugin
This has received limited testing, and the PDUs cannot be used with secure protocols
2022-06-03 10:24:44 -04:00
Jarrod Johnson 64b8893b80 Add a delta pdu plugin
This has received limited testing, and the PDUs cannot be used with secure protocols
2022-06-03 10:24:25 -04:00
Jarrod Johnson ae4b5c5100 Amend nodeping behavior and documentation 2022-06-02 16:04:19 -04:00
Jarrod Johnson f13df801fd Merge branch 'master' into remote_discovery 2022-05-31 13:53:47 -04:00
Jarrod Johnson 83acdab216 Amend python 3.9 compatibility fix
Avoid checking None for is_alive or isAlive
2022-05-31 11:23:27 -04:00
Jarrod Johnson b42dafb085 Fix python 3.9 compatibility
In python 3.9, the % is no longer included
in the first member of the address tuple.

Handle both scenarios by adding '%' in for versions
that seem to want to remove it.
2022-05-31 11:01:09 -04:00
Jarrod Johnson b5f5ba69d2 Fix python 3.9 compatibility
Python 3.9 renamed an attribute on a threading object, try
both names to support old and new python
2022-05-31 10:18:42 -04:00
Jarrod Johnson 6d07dac46c Fix ipmi missing logging state and trim el9 dependencies 2022-05-27 16:10:08 -04:00
Jarrod Johnson e4e859c798 Fix invocation of monotonic_time 2022-05-26 18:07:19 -04:00
Jarrod Johnson ff698ea06b Merge branch 'webauthn' 2022-05-26 17:42:15 -04:00
Jarrod Johnson 9c7e23f29e Add missing nodepower statuses 2022-05-26 17:41:54 -04:00
Jarrod Johnson b638bb69f9 Change webauthn to use confluent user db
This allows persistence with the rest of the
configuration.
2022-05-26 17:41:16 -04:00
Jarrod Johnson 7072a85d79 Transition to multi-authenticator support
Provide a way to store a plurality of keys for a user.
This enables use of 'backup' authenticators.
2022-05-26 16:34:18 -04:00
Jarrod Johnson 9b6114f523 Break if stuck in loop for over a minute 2022-05-26 15:01:47 -04:00
Jarrod Johnson 3c3d6bb314 Fix auth handling of the session/info 2022-05-25 17:40:35 -04:00
Jarrod Johnson f6a17b5f32 Have validate serve as session info request
This should facilitate login.

Further, provide a quick persistence for the
credential test backend
2022-05-25 15:58:20 -04:00
Jarrod Johnson 4773acb37d Adapt geist to work with python2 2022-05-25 10:58:35 -04:00
Jarrod Johnson e0079b5a86 Amend webauthn validation api 2022-05-25 10:58:02 -04:00
Jarrod Johnson c93f09bc91 Provide hook to get registered credentials
This has to relax the session in getting and requesting validation.
2022-05-24 19:17:31 -04:00
Jarrod Johnson 9b39c96135 Begin work on webauthn support
Provide appropriate registration options as a first
step.
2022-05-24 10:22:34 -04:00
Jarrod Johnson 48d46bcfae Add resolv setup to genesis 2022-05-24 08:27:36 -04:00
Jarrod Johnson 371942b0ce Add myhostname to improve name resolution errors in genesis 2022-05-24 08:17:04 -04:00
Jarrod Johnson a3cce144bc Extend manager principals for ssh
When doing osdeploy initialize -l (not recommended usually),
add on more forms of the name and ip addresses
to be consistent with node ssh behavior.
2022-05-24 07:24:56 -04:00
Jarrod Johnson f9d47bb0d3 Fix markingrequest as not implemented
The Geist PDU support inadvertently took down
unrelated parts of a request, fix by
properly showing not implemented in
a node specific way.
2022-05-20 08:35:43 -04:00
Jarrod Johnson 6068a29434 Merge branch 'master' of github.com:lenovo/confluent 2022-05-17 10:45:17 -04:00
Jarrod Johnson 8deadec7a6 Provide hook to override rungesis
Some applications may want much of the genesis
distribution, but want to have a bootstrap process
independent of confluent.  Provide hooks either
through the identity image or the boot image itself.
2022-05-17 10:44:07 -04:00
Jarrod Johnson da764fb99e Merge pull request #83 from erderial/master
ndeping man page
2022-05-16 14:36:38 -04:00
erderial 56b112519d Merge pull request #1 from erderial/Er-Nodeping
Adding nodeping man page
2022-05-16 21:25:39 +03:00
erderial abb85d5034 Created nodeping file 2022-05-16 21:21:42 +03:00
Jarrod Johnson eb99fbd8b2 Switch from clear buffer to sized memoryview
Use the recvmsg hint to mask out the buffer rather
than zeroing the entire buffer. This is more efficient
and further improves efficiency of parsing of the packet.
2022-05-14 18:35:15 -04:00
Jarrod Johnson 54741517f1 Clear DHCP buffer between recv
Very large PXE requests can leave residual
information that small, non-pxe requests will
interact poorly with, leading to spurious pxe-client
with cloned uuid of most recent large request.

Clearing between IO normalizes the state to avoid the bleed
over.
2022-05-14 18:12:19 -04:00
Jarrod Johnson 459c9a5210 Wait for a login attempt to run its course
If an existing session was not quite logged in, but
may be getting there, join in and wait for result
instead of starting over again.
2022-05-12 16:39:58 -04:00
Jarrod Johnson c328fea49a Cleaner output on cli
Based on feedback, remove the added 'inlet_' from pdu
output.

Also, fix geist plugin to block unsupported
features for now.
2022-05-11 16:01:43 -04:00
Jarrod Johnson caba650143 Add nodepower arguments for PDU operations 2022-05-11 14:59:54 -04:00
Jarrod Johnson e4d7be649a Fix single inlet operations 2022-05-11 13:31:19 -04:00
Jarrod Johnson d8a0f111db Implement changing PDU state on set 2022-05-11 08:53:24 -04:00
Jarrod Johnson 8dbcc804ed Pull outlets into the generic hierarchy
This will more easily facilitate adding pdus without dependent nodes.
2022-05-10 16:05:37 -04:00
Jarrod Johnson 6229cb23e8 Begin PDU implementation 2022-05-10 16:00:08 -04:00
Jarrod Johnson 0e879dc3de Add el7 to alternat squashfs name 2022-05-09 09:57:59 -04:00
Jarrod Johnson ee1a763c68 Add el7 to alternat squashfs name 2022-05-09 09:57:35 -04:00
Jarrod Johnson f138b4513c Merge branch 'master' into remote_discovery 2022-05-09 08:33:24 -04:00
Jarrod Johnson 2925c291cf Increase ipmi concurrency
128 can be a bit limiting, try 512 for improved responsiveness
2022-05-05 09:28:09 -04:00
Jarrod Johnson 86891eb2e5 Rework resolv watcher
Handle symlinks better and do not trigger overly
eagerly
2022-05-05 09:26:55 -04:00
Jarrod Johnson 754cdfdd78 Fix rpm spec syntax for imgutil 2022-05-04 14:35:03 -04:00
Jarrod Johnson ffadb9cc3a Add squashfs dependency
It has been confusing when imgutil cannot pack by default
2022-05-04 14:23:41 -04:00
Jarrod Johnson d3129847b4 Add recognition of Alma 9 and (presumably) rocky 9 2022-05-04 14:16:51 -04:00
Jarrod Johnson 548111f4d1 Fix executable status of the confluent casper hook 2022-05-04 12:11:24 -04:00
Jarrod Johnson 62b27b1fcc Back off diskless profile for ubuntu 22.04, not yet done 2022-05-04 11:42:14 -04:00
Jarrod Johnson bf37c05d71 Add ubuntu 22.04 to packaging 2022-05-04 11:31:03 -04:00
Jarrod Johnson e2e4014db3 Make ubuntu case insensitive on import
Ubuntu changes their minds about case of some files.
2022-05-04 11:01:59 -04:00
Jarrod Johnson b2feb62d8a Add ubuntu22.04 profile
Ubuntu 22.04 makes some changes, notably removing the
custom-installation hooks.

Change to injecting our modifications more directly to where the
custom-installation hooks used to be.
2022-05-04 09:25:49 -04:00
Jarrod Johnson 0301f5ca68 Fix imgutil pack expecting addpackagelist
The attribute only populates on a different subcommand,
so only process if the attribute exists.
2022-05-04 06:44:07 -04:00
Jarrod Johnson 98d22ffabc Add a utility to induce an inventory 2022-05-03 08:29:43 -04:00
Jarrod Johnson ac762c7ad5 Note addition of enclosure uuid to nodediscover
This will help sort out dense invorenments more easily, particularly
if no switch or chained.
2022-05-02 17:04:13 -04:00
Jarrod Johnson 48fd496637 Fix discovery uuid cross-reference
The SSDP does an endian scramble of the smm uuid,
fix it, and also update the core to prefer the
processed enclosure.uuid attribute.
2022-04-27 15:09:40 -04:00
Jarrod Johnson a738be3b53 Add license file to imgutil packaging 2022-04-26 08:18:31 -04:00
Jarrod Johnson 7da8a2ec4c Properly stage the license file for osdeploy 2022-04-26 08:16:12 -04:00
Jarrod Johnson e842c2ddc8 Add licensing to osdeploy package 2022-04-26 08:09:50 -04:00
Jarrod Johnson 46ffe3f5f2 Fix license directory name for confluent server 2022-04-26 08:00:13 -04:00
Jarrod Johnson 060f639ab3 Flag license files appropriately 2022-04-25 17:02:35 -04:00
Jarrod Johnson 69b3aca815 Remove errant copy of LICENSE file in client 2022-04-25 16:54:06 -04:00
Jarrod Johnson 9333c999c9 Adjust setup.py licensing for older setuptools 2022-04-25 16:48:31 -04:00
Jarrod Johnson 5538f99376 Support older setuptools for license packaging 2022-04-25 16:47:19 -04:00
Jarrod Johnson 6270d57729 Add tmt license terms to vtbufferd package 2022-04-25 16:31:51 -04:00
Jarrod Johnson 66f2ba98ec Set up confluent licensisng in setupdist context 2022-04-25 16:15:49 -04:00
Jarrod Johnson 7234310519 Move licensing to files instead of rpm description for vtbuffer 2022-04-25 16:05:22 -04:00
Jarrod Johnson 2c058fe634 Properly mark license files as license 2022-04-25 15:46:29 -04:00
Jarrod Johnson f009f6b8c2 Trim to appropriate content for the license 2022-04-25 15:18:27 -04:00
Jarrod Johnson 061d4217ff Further amend license handling 2022-04-25 14:46:50 -04:00
Jarrod Johnson 5d03eb8863 Try to normalize license paths 2022-04-25 14:15:07 -04:00
Jarrod Johnson 4d218757f3 Amend license carry over logic 2022-04-25 14:02:08 -04:00
Jarrod Johnson 71a698f771 Carry over license content from donor rpms 2022-04-25 13:52:36 -04:00
Jarrod Johnson 2f8846ee75 Further change directory traversal 2022-04-25 13:14:02 -04:00
Jarrod Johnson 128cb61eaa Fix directory traversal 2022-04-25 13:12:04 -04:00
Jarrod Johnson d8a2671dd6 Fix directory traversal 2022-04-25 13:11:43 -04:00
Jarrod Johnson 8947ab08da Add file to show the rpm versions at build time of genesis 2022-04-25 13:05:16 -04:00
Jarrod Johnson 14efec3603 Add file to show the rpm versions at build time of genesis 2022-04-25 13:04:45 -04:00
Jarrod Johnson 00589d06f5 Refactor relay and add to XCC/SMM
Extract relay versus direct determination and
inject into XCC and SMM handlers
2022-04-21 09:40:09 -04:00
Jarrod Johnson 334ce65919 Add facility for forwarding agent
Remote discovery will require that the reporting agent
provides port forwarding facility for https access, to
allow access without route to the managing confluent server.

This will allow the info reported to indicate use of this
forwarding facility and the base handler adds ability to
generically get the certificate.
2022-04-20 17:11:01 -04:00
Jarrod Johnson f6a16a89f2 Prevent spaces in nodenames 2022-04-20 08:55:54 -04:00
Jarrod Johnson 3297667ef8 Prune out backspaces
Some utilities backspace prior to printing anything,
prevent such applications from encroaching on the node name space.
2022-04-19 09:13:18 -04:00
Jarrod Johnson f4eec09a41 ADd a sample for converting mac to lla 2022-04-18 10:42:35 -04:00
Jarrod Johnson f15802a9aa Fix capture of non-selinux systems 2022-04-18 09:15:09 -04:00
Jarrod Johnson 449b2662fa Provide addpkglist support for imgutil build
This allows packagelists to be supplied to addon to the basic list, without
having to duplicate and modify
2022-04-14 14:35:26 -04:00
Jarrod Johnson fb9546cbd6 Kill redundant udevd prior to starting the root fs
Having multiple udevd produces ambiguity and confusing
2022-04-14 13:56:42 -04:00
Jarrod Johnson 193d37029f Relabel target path if selinux disabled on build host 2022-04-14 12:47:14 -04:00
Jarrod Johnson 5285691344 Correct fix for the python3 compatibility 2022-04-13 17:32:43 -04:00
Jarrod Johnson c533c973b1 Fix python3 compatibility with termhandler 2022-04-13 17:00:27 -04:00
Jarrod Johnson 212aa5c6e2 Add ability to unsubscribe from a terminal session 2022-04-13 16:44:01 -04:00
Jarrod Johnson 2bf9a6d415 Add support for consolidated term websocket
Since browsers can be stingy with websockets, have
the consoles all share a single websocket.
2022-04-13 16:08:13 -04:00
Jarrod Johnson 272398884c Appl consistent name to apiclient for EL8 2022-04-13 11:27:30 -04:00
Jarrod Johnson 79bc27bc7a Have el8 diskless systems add local repositories
This sets up local yum action by default.
2022-04-13 09:50:39 -04:00
Jarrod Johnson 8c54b6eaf8 Fix path to apiclient in add_local_repositories 2022-04-13 09:48:33 -04:00
Jarrod Johnson 1e77448b3a Fix comment support in pkglist for imgutil 2022-04-12 09:57:06 -04:00
Jarrod Johnson 918658c2ac Cleanup loop device allocation on image unpack
loop device was left configured after an unpack
2022-04-11 16:06:08 -04:00
Jarrod Johnson dfc9b0901d Improve feedback on no match found for log search 2022-04-08 12:59:36 -04:00
Jarrod Johnson 4b988e0633 Integrate logreader into nodeconsole as a feature 2022-04-08 12:29:45 -04:00
Jarrod Johnson 1ef0a61ed1 Move logreader to be a library in client 2022-04-08 12:12:02 -04:00
Jarrod Johnson b42a4bd722 Pretty-ify search interaction
Have the search text and feedback at bottom of screen.
2022-04-08 12:09:40 -04:00
Jarrod Johnson 198d8b3e47 Add flag to disable expression expansion
Some scenarios that do not use the {} feature may
find it difficult to pre-format.  Allow
such cases to opt out.
2022-04-08 10:28:11 -04:00
Jarrod Johnson fc64d2d93f Add a SLES variant to profiles for SUSE 2022-04-07 14:18:10 -04:00
Jarrod Johnson 4b8be913ee Logreader enhancements
Highlight all visible matches on screen

Restore cursor visibility if trashed.

Backspace in search string works.
2022-04-06 15:28:48 -04:00
Jarrod Johnson eba668b96c First pass at a search function
Currently, the 'n' for next result fails to find the next result
if it is currently on screen.  Also, the feedback on
failed search could use improvement.
2022-04-06 14:19:28 -04:00
Jarrod Johnson ded3cc4467 Fix logreader for python2/3 compatibility 2022-04-06 09:32:03 -04:00
Jarrod Johnson 7a0dee8af8 Fix keepalive/logout behavior in ws async
Use the websocket liveness as the keepalive, so
the reaper is not scheduled for such sockets.

Additionally, register the async thread as to be killed on logout.
2022-04-05 16:56:36 -04:00
Jarrod Johnson 105536656e Implement async session on websocket
This eliminates long polling and sets the stage to restore
socket sharing by terminals and shells.
2022-04-05 16:33:03 -04:00
Jarrod Johnson 8ef91c16c0 Do not let an existing /var/lib/confluent block the requisite chown 2022-04-05 09:35:05 -04:00
Jarrod Johnson 6fab77264c Make paginate to handle multiple screen clear
Grub moves cursor and clears as two separate commands,
so recognize that sequence as clear screen as well
2022-04-02 07:33:50 -04:00
Jarrod Johnson ab7f0e0528 Fix error checking in imginfo
imginfo was checking fread when it should
have been checking number of records.
2022-04-01 09:32:43 -04:00
Jarrod Johnson 777bdfac5c Fix the incorrect parameter name in setting password policy
The mistake was keeping the requisite rule from applying
2022-03-30 11:16:33 -04:00
Jarrod Johnson d651c29149 Add password expiration recovery to smm discovery
SMM discover can now also unexpire password during onboarding.
2022-03-30 08:50:08 -04:00
Jarrod Johnson b4f021cfad Fix omission in SSDP snoop 2022-03-30 08:24:25 -04:00
Jarrod Johnson 69a06a6923 Implement a password unexpiration mechanism in xcc
If the node has expired password, do what is necessary to unexpire the
password
to get through assignment.
2022-03-30 08:07:25 -04:00
Jarrod Johnson d214e7e442 Normalize blank strings in cfgdata
In cfgdata, make '' replaced by None for
consistent behavior for cleared and blanked
attributes.
2022-03-25 08:59:46 -04:00
Jarrod Johnson e4e15d87a7 Background redfish check on snoop
When snooping, if a redfish device comes along, background the
query so that it is unable to block the main SSDP receive routine.
2022-03-24 17:18:05 -04:00
Jarrod Johnson 21bfc29a89 Make more clear the default behavior when prompting 2022-03-24 11:18:37 -04:00
Jarrod Johnson 7a66567625 Add missing monotic dependency 2022-03-24 09:20:14 -04:00
Jarrod Johnson b830a317f6 Change suse from team to bonding
Suse more naturally supports team, and the industry push to team
has evaporated
2022-03-24 09:10:42 -04:00
Jarrod Johnson 4e4fe03b62 Properly honor explicit interface_names
A mistake resulted in plural interface_names being treated as singular.
2022-03-24 07:44:13 -04:00
Jarrod Johnson 08264f277b Fix location of apiclient in suse 2022-03-23 15:46:37 -04:00
Jarrod Johnson ac1ba5cbc5 Correct mistakes in the first pass of routed deployment 2022-03-18 16:04:29 -04:00
Jarrod Johnson acd8cb9055 First pass at media based routed deployment 2022-03-18 15:39:22 -04:00
Jarrod Johnson 8a3688c1d6 Fix mistake in pre.sh for relocation of apiclient 2022-03-18 12:09:43 -04:00
Jarrod Johnson cd3d248a78 Add identimage to rpm build 2022-03-17 13:04:24 -04:00
Jarrod Johnson 2299ccc32f Handle VROC devices in autoyast
At time of running pre, array is not assembled by default,
inject a scan to check for them.

VROC must not be specified by 'md' name, or yast thinks it
needs to own making it, and complains that the setup doesn't
specify members.
2022-03-17 12:19:42 -04:00
Jarrod Johnson bfd40b51de Correct name of ident_image api 2022-03-17 09:35:49 -04:00
Jarrod Johnson 94ab644f5c Create mechanism to create node identity images
These images are used in the flow of routed deployment.
2022-03-16 15:41:07 -04:00
Jarrod Johnson 40a187d2aa Reverse ordering of reboot and api arming.
Technically there's room for a race condition where boot is attempted
before the profile is ready, but it's highly unlikely.

Conversely, there is a potential confusing race condition today where
restarting a deploymennt without armed api causes
it to be disarmed before the boot is attempted.
2022-03-16 14:57:46 -04:00
Jarrod Johnson fdd3ec4233 Fix check for confluent service having started
Give confluent full chance to set things up prior
to proceeding.
2022-03-16 10:28:44 -04:00
Jarrod Johnson b2603aa1f8 Set ownership of /var/lib/confluent on installation
Some paths fail to initialize ownership earlier, give it
a head start
2022-03-16 10:26:16 -04:00
Jarrod Johnson b6034f2e71 Update to fix new profiles and accomodate old profiles
/etc/confluent/apiclient is expected by older profiles
2022-03-16 09:40:55 -04:00
Jarrod Johnson fe40d7c15e Fix mispelling of confluent 2022-03-16 09:01:22 -04:00
Jarrod Johnson 32081edec8 Workaround ':' format specifier syntax
Older python will break by assuming that
: always means a format expression is coming.

Move the field value fetch to format_field, and ascertain if some of the
expression was shunted to format specification
by mistake.
2022-03-11 12:21:09 -05:00
Jarrod Johnson dc0183fdf4 Add [] slicing/indexing to confluent attribute expression syntax
This permits expressions like:
node[:-3]
To say nodename, but leave out 3 chars.
Or:
node[3:]
To skip the first three characters.
2022-03-11 11:23:43 -05:00
Jarrod Johnson f168c4be2b Allow free ordering of noderange/arguments in nodeping
With nodeping, no concern about passing arguments to arbitrary subcommand, so
allow arguments to be anywhere.
2022-03-11 10:55:00 -05:00
Jarrod Johnson 2194ca9018 Create a nodeping script for quick ping wrapping 2022-03-11 10:23:11 -05:00
Jarrod Johnson ceada3b7d9 Provide API for using one-time shared secret to register api key
This permits long haul node api key registration over a single port. It cannot validate that
the requester is privileged, but the auto-invalidation
offsets the risk of subsequent users having read access to the remote mount.
2022-03-10 16:06:02 -05:00
Jarrod Johnson ad40c46509 Remove now-redundant genpasshmac.c file 2022-03-10 09:32:44 -05:00
Jarrod Johnson 301ed7a798 Fix mistake in b64e invocation 2022-03-10 09:15:26 -05:00
Jarrod Johnson b42e2e4932 Change to b64 output for hmac
base64 utility is not always available, so natively
use base64 format for hmac output.
2022-03-10 09:00:54 -05:00
Jarrod Johnson 61d037ae31 Combine genpasshmac with clortho
This permits saving on addons size by using the same
binary for both networked api grant and hmac api
grant.
2022-03-09 13:36:47 -05:00
Jarrod Johnson 6a30afa31e Have SSDP ignore multicast disabled interfaces 2022-03-09 11:01:01 -05:00
Jarrod Johnson 625434fcaf Fix mistake in deploycfg parsing
More strictly match the field name.
2022-03-08 16:29:49 -05:00
Jarrod Johnson a8c2f859e4 Add a genpasshmac utility
For far edge deployment, create utility
that can hmac a password for use in a REST
api call to skip need for tcp port 13001 access.
2022-03-08 16:27:37 -05:00
Jarrod Johnson 31dad09b0c Update makefile to build in sh256 to clortho 2022-03-08 14:46:33 -05:00
Jarrod Johnson 0abe978bd9 Implement hmac of apikey
For routed deployment, we have to preshare some information.

Additionally, the API arm mechanism gets too open ended.

Add support for using a shared secret over another
channel to do HMAC of a key to authenticate peer,
which has an alternate api arming mechanism
that is hardened.
2022-03-08 14:46:00 -05:00
Jarrod Johnson e67bab4f12 Place cap on api password length
No more than 48 characters should ever be in
an api token. Cap it to avoid outrageous crypt
behavior at large password length.
2022-03-08 09:15:13 -05:00
Jarrod Johnson 21c0372a5b Support get_full_net_config without serverip
When trying to get a configuration
without a network context, it would fail.

Now, as intended, it generates network configuration without autosense in such a case.
2022-03-07 15:28:04 -05:00
Jarrod Johnson 98d8aaffe8 Merge branch '3.4' 2022-03-07 15:22:54 -05:00
Jarrod Johnson ecd114ca5a Add script for setting up ssh
A frequent scenario is to 'refresh' ssh configuration toward the
end of:
-changing trust nodes
-Adding a collective member
-Repairing a broken configuration
-As part of 'confluent-ifying' a node that wasn't confluent deployed
2022-03-03 12:34:37 -05:00
Jarrod Johnson 5fb766e62b Move apiclient consistently to /opt/confluent/bin
It's more reasonable to have
it in a bin directory
2022-03-03 11:11:29 -05:00
Jarrod Johnson 76fdf59122 Change genesis functions location
Put it in a place consistent with more normal use.
2022-03-03 08:34:57 -05:00
Jarrod Johnson 003196bc9e Allow -o with data file
This makes things like ssh key signing easier.
2022-03-03 08:25:04 -05:00
Jarrod Johnson 15e7e4464e Keep known_hosts cleaner
When repeating osdeploy initialize
of local known_hosts, more
gracefeully avoid duplicate entries.
2022-03-02 16:04:01 -05:00
Jarrod Johnson 687136131e Place Confluent CA certs into TLS anchors
When processes may update the certificate authorities, the confluent
CA trust would be lost. Place it appropriately so that
update-ca-trust will keep it in the appropriate place.
2022-03-02 08:41:47 -05:00
Jarrod Johnson 5f610b64b7 Place Confluent CA certs into TLS anchors
When processes may update the certificate authorities, the confluent
CA trust would be lost. Place it appropriately so that
update-ca-trust will keep it in the appropriate place.
2022-03-02 08:40:27 -05:00
239 changed files with 9827 additions and 771 deletions
-25
View File
@@ -175,28 +175,3 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+21
View File
@@ -0,0 +1,21 @@
Name: confluent-client
Project: https://hpc.lenovo.com/users/
Source: https://github.com/lenovo/confluent
Files: *
Copyright: 2014-2019 Lenovo
License: Apache-2.0
File: client.py:
Copyright: 2014 IBM Corporation
2015-2019 Lenovo
License: Apache-2.0
File: sortutil.py
Copyright: 2014 IBM Corporation
2015-2016 Lenovo
License: Apache-2.0
File: tlv.py
Copyright: 2014 IBM Corporation
License: Apache-2.0
+1 -1
View File
@@ -635,7 +635,7 @@ def quitconfetty(code=0, fullexit=False, fixterm=True):
global didconsole
if fixterm or didconsole:
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl & ~os.O_NONBLOCK)
if oldtcattr is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
# Request default color scheme, to undo potential weirdness of terminal
+2
View File
@@ -79,6 +79,8 @@ def collect_attribute_data(databynode, node):
xcatattrs['groups'] = ','.join(data.get('groups', []))
xcatattrs['bmc'] = data.get('hardwaremanagement.manager', {}).get(
'value', None)
if xcatattrs['bmc']:
xcatattrs['bmc'] = xcatattrs['bmc'].split('/', 1)[0]
xcatattrs['mpa'] = data.get('enclosure.manager', {}).get(
'value', None)
xcatattrs['slotid'] = data.get('enclosure.bay', {}).get(
+5 -5
View File
@@ -21,17 +21,17 @@ def create_image(directory, image, label=None):
currsz = (currsz // 512 +1) * 512
datasz += currsz
datasz += ents * 32768
datasz = datasz // 512 + 1
datasz = datasz // 4096 + 1
with open(image, 'wb') as imgfile:
imgfile.seek(datasz * 512 - 1)
imgfile.seek(datasz * 4096 - 1)
imgfile.write(b'\x00')
if label:
subprocess.check_call(['mformat', '-i', image, '-v', label,
'-r', '16', '-d', '1', '-t', str(datasz),
'-s', '1','-h', '1', '::'])
'-s', '4','-h', '2', '::'])
else:
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
str(datasz), '-s', '1','-h', '1', '::'])
str(datasz), '-s', '4','-h', '2', '::'])
# Some clustered filesystems will have the lock from mformat
# linger after close (mformat doesn't unlock)
# do a blocking wait for shared lock and then explicitly
@@ -58,4 +58,4 @@ if __name__ == '__main__':
label = None
if len(sys.argv) > 3:
label = sys.argv[3]
create_image(sys.argv[1], sys.argv[2], label)
create_image(sys.argv[1], sys.argv[2], label)
+2 -4
View File
@@ -289,13 +289,11 @@ else:
for path in queryparms:
if options.comparedefault:
continue
rc = client.print_attrib_path(path, session, list(queryparms[path]),
rcode |= client.print_attrib_path(path, session, list(queryparms[path]),
NullOpt(), queryparms[path])
if rc:
sys.exit(rc)
if printsys == 'all' or printextbmc or printbmc or printallbmc:
if printbmc or not printextbmc:
rcode = client.print_attrib_path(
rcode |= client.print_attrib_path(
'/noderange/{0}/configuration/management_controller/extended/all'.format(noderange),
session, printbmc, options, attrprefix='bmc.')
if options.extra:
+53
View File
@@ -25,6 +25,7 @@ if path.startswith('/opt'):
sys.path.append(path)
import confluent.client as client
import confluent.sortutil as sortutil
import confluent.logreader as logreader
confettypath = os.path.join(os.path.dirname(sys.argv[0]), 'confetty')
argparser = optparse.OptionParser(
@@ -35,10 +36,62 @@ argparser = optparse.OptionParser(
"console")
argparser.add_option('-t', '--tile', action='store_true', default=False,
help='Tile console windows in the terminal')
argparser.add_option('-l', '--log', action='store_true', default=False,
help='Enter log replay mode instead of showing a live console')
argparser.add_option('-w','--windowed', action='store_true', default=False,
help='Open terminal windows for each node. The '
'environment variable NODECONSOLE_WINDOWED_COMMAND '
'should be set, which should be a text string corresponding '
'to a command that can be used to open a windowed console,'
' omitting the "nodeconsole <noderange>" part of the '
'command, for example, to open a set of consoles for a '
'range of nodes in separate xterm windows, set '
'NODECONSOLE_WINDOWED_COMMAND to "xterm -e". To open a '
'set of consoles for a range of nodes in separate '
'GNOME Terminal windows with a size of 100 columns and '
'31 rows, set NODECONSOLE_WINDOWED_COMMAND '
'to "gnome-terminal --geometry 100x31 --" or in a WSL '
'environment, to open a set of consoles for a range of '
'nodes in separate Windows Terminal windows, with the '
'title set for each node, set NODECONSOLE_WINDOWED_COMMAND'
' to "wt.exe wsl.exe -d AlmaLinux-8 '
'--shell-type login". If the NODECONSOLE_WINDOWED_COMMAND '
'environment variable isn\'t set, xterm will be used by'
'default.')
(options, args) = argparser.parse_args()
if len(args) != 1:
argparser.print_help()
sys.exit(1)
if options.log:
logname = args[0]
if not os.path.exists(logname) and logname[0] != '/':
logname = os.path.join('/var/log/confluent/consoles', logname)
if not os.path.exists(logname):
sys.stderr.write('Unable to locate {0} on local system\n'.format(logname))
sys.exit(1)
logreader.replay_to_console(logname)
sys.exit(0)
#added functionality for wcons
if options.windowed:
envstring=os.environ.get('NODECONSOLE_WINDOWED_COMMAND')
if not envstring:
envlist=["xterm", "-e"]
else:
envlist=os.environ.get('NODECONSOLE_WINDOWED_COMMAND').split(' ')
nodes = []
sess = client.Command()
for res in sess.read('/noderange/{0}/nodes/'.format(args[0])):
node = res.get('item', {}).get('href', '/').replace('/', '')
if not node:
sys.stderr.write(res.get('error', repr(res)) + '\n')
sys.exit(1)
nodes.append(node)
for node in sortutil.natural_sort(nodes):
with open(os.devnull, 'wb') as devnull:
subprocess.Popen(envlist + [confettypath, '-m', '5', 'start', '/nodes/{0}/console/session'.format(node)], stdin=devnull)
sys.exit(0)
#end of wcons
if options.tile:
null = open('/dev/null', 'w')
nodes = []
+16 -11
View File
@@ -85,6 +85,22 @@ def main(args):
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
c = client.Command()
c.stop_if_noderange_over(args.noderange, args.maxnodes)
errnodes = set([])
for rsp in c.read('/noderange/{0}/nodes/'.format(args.noderange)):
if 'error' in rsp:
sys.stderr.write(rsp['error'] + '\n')
sys.exit(1)
if not args.clear and args.network and not args.prepareonly:
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
bootmode='uefi',
persistent=False,
errnodes=errnodes)
if errnodes:
sys.stderr.write(
'Unable to set boot device for following nodes: {0}\n'.format(
','.join(errnodes)))
return 1
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
if args.clear:
cleararm(args.noderange, c)
clearpending(args.noderange, c)
@@ -139,18 +155,7 @@ def main(args):
armed = ''
print('{0}: {1}{2}'.format(node, profile, armed))
sys.exit(0)
errnodes = set([])
if args.network and not args.prepareonly:
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
bootmode='uefi',
persistent=False,
errnodes=errnodes)
if errnodes:
sys.stderr.write(
'Unable to set boot device for following nodes: {0}\n'.format(
','.join(errnodes)))
return 1
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
return rc
return 0
+54 -8
View File
@@ -48,20 +48,54 @@ columnmapping = {
'Advertised IP': 'otheripaddrs',
'Other IP': 'otheripaddrs',
}
#TODO: add chassis uuid
def register_endpoint(options, session, addr):
neednewline = False
current = 0
for rsp in session.update('/discovery/register', {'addresses': addr}):
if 'count' in rsp:
total = rsp['count']
elif total > 1:
current += 1
sys.stdout.write('\x1b[2K\r{0:.1f}% Scanned'.format(100 * current / total))
sys.stdout.flush()
neednewline = True
if 'created' in rsp:
if neednewline:
print('')
neednewline = False
print('Registered: {0}'.format(rsp['created']))
if neednewline:
print('')
def subscribe_discovery(options, session, subscribe, targ):
keyn = 'subscribe' if subscribe else 'unsubscribe'
payload = {keyn: targ}
if subscribe:
for rsp in session.update('/discovery/subscriptions/{0}'.format(targ), payload):
if 'status' in rsp:
print(rsp['status'])
else:
for rsp in session.delete('/discovery/subscriptions/{0}'.format(targ)):
if 'status' in rsp:
print(rsp['status'])
def print_disco(options, session, currmac, outhandler, columns):
procinfo = {}
for tmpinfo in session.read('/discovery/by-mac/{0}'.format(currmac)):
procinfo.update(tmpinfo)
if 'Switch' in columns or 'Port' in columns:
for tmpinfo in session.read(
'/networking/macs/by-mac/{0}'.format(currmac)):
if 'ports' in tmpinfo:
# The api sorts so that the most specific available value
# is last
procinfo.update(tmpinfo['ports'][-1])
if 'switch' in procinfo:
procinfo['port'] = procinfo['switchport']
else:
for tmpinfo in session.read(
'/networking/macs/by-mac/{0}'.format(currmac)):
if 'ports' in tmpinfo:
# The api sorts so that the most specific available value
# is last
procinfo.update(tmpinfo['ports'][-1])
record = []
for col in columns:
rawcol = columnmapping[col]
@@ -319,7 +353,7 @@ def blocking_scan(session):
def main():
parser = optparse.OptionParser(
usage='Usage: %prog [list|assign|rescan|clear] [options]')
usage='Usage: %prog [list|assign|rescan|clear|subscribe|unsubscribe|register] [options]')
# -a for 'address' maybe?
# order by
# show state (discovered or..
@@ -357,9 +391,15 @@ def main():
parser.add_option('-o', '--order', dest='order',
help='Order output by given field', metavar='ORDER')
(options, args) = parser.parse_args()
if len(args) == 0 or args[0] not in ('list', 'assign', 'reassign', 'rescan', 'clear'):
if len(args) == 0 or args[0] not in ('list', 'assign', 'reassign', 'rescan', 'clear', 'subscribe', 'unsubscribe', 'register'):
parser.print_help()
sys.exit(1)
if args[0] == 'register' and len(args) != 2:
sys.stderr.write('Register requires target address or range (e.g. 192.168.2.30, 192.168.2.0/24, or 192.168.2.1-192.168.2.30)\n')
sys.exit(1)
if 'subscribe' in args[0] and len(args) != 2:
sys.stderr.write('subscribe/unsubscribe subcommands require switch name as argument\n')
sys.exit(1)
session = client.Command()
if args[0] == 'list':
list_discovery(options, session)
@@ -369,6 +409,12 @@ def main():
assign_discovery(options, session)
if args[0] == 'reassign':
assign_discovery(options, session, False)
if args[0] == 'register':
register_endpoint(options, session, args[1])
if args[0] == 'subscribe':
subscribe_discovery(options, session, True, args[1])
if args[0] == 'unsubscribe':
subscribe_discovery(options, session, False, args[1])
if args[0] == 'rescan':
blocking_scan(session)
print("Rescan complete")
+3 -1
View File
@@ -60,10 +60,12 @@ nodetype="nodegroups"
try:
nodegroups = args[0]
client.check_globbing(nodegroups)
nodelist = '/{0}/{1}/'.format(nodetype,nodegroups)
except IndexError:
argparser.print_help()
sys.exit(1)
nodelist = '/nodegroups/'
client.check_globbing(nodegroups)
session = client.Command()
exitcode = 0
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/python2
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2016-2017 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import deque
import optparse
import os
import select
import signal
import subprocess
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 run():
argparser = optparse.OptionParser(
usage="Usage: %prog [options] noderange")
argparser.add_option('-f', '-c', '--count', type='int', default=168,
help='Number of commands to run at a time')
argparser.add_option('-s', '--substitutename',
help='Use a different name other than the nodename for ping, with {}, it is the entire name evaluated as an expression, otherwise it is used as a suffix')
# among other things, FD_SETSIZE limits. Besides, spawning too many
# processes can be unkind for the unaware on memory pressure and such...
(options, args) = argparser.parse_args()
if len(args) < 1:
argparser.print_help()
sys.exit(1)
client.check_globbing(args[0])
concurrentprocs = options.count
c = client.Command()
currprocs = 0
all = set([])
pipedesc = {}
pendingexecs = deque()
exitcode = 0
nodemap = {}
if options.substitutename:
subname = options.substitutename
if '{' not in subname:
subname = '{node}' + subname
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
{'expression': subname}):
if 'error' in exp:
sys.stderr.write(exp['error'] + '\n')
exitcode |= exp.get('errorcode', 1)
ex = exp.get('databynode', ())
for node in ex:
nodemap[node] = ex[node]['value']
for rsp in c.read('/noderange/{0}/nodes/'.format(args[0])):
if 'error' in rsp:
sys.stderr.write(rsp['error'] + '\n')
exitcode |= rsp.get('errorcode', 1)
if rsp.get('item', {}).get('href', None):
node = rsp['item']['href'][:-1]
pingnode = nodemap.get(node, node)
cmdv = ['ping', '-c', '1', '-W', '1', pingnode]
if currprocs < concurrentprocs:
currprocs += 1
run_cmdv(pingnode, cmdv, all, pipedesc)
else:
pendingexecs.append((pingnode, cmdv))
if not all or exitcode:
sys.exit(exitcode)
rdy, _, _ = select.select(all, [], [], 10)
while all:
pernodeout = {}
for r in rdy:
desc = pipedesc[r]
node = desc['node']
data = True
while data and select.select([r], [], [], 0)[0]:
data = r.readline()
if not data:
pop = desc['popen']
ret = pop.poll()
if ret is not None:
exitcode = exitcode | ret
all.discard(r)
r.close()
if desc['type'] == 'stdout':
if ret:
print('{0}: no_ping'.format(node))
else:
print('{0}: ping'.format(node))
if pendingexecs:
node, cmdv = pendingexecs.popleft()
run_cmdv(node, cmdv, all, pipedesc)
for node in sortutil.natural_sort(pernodeout):
for line in pernodeout[node]:
line = client.stringify(line)
if options.nonodeprefix:
sys.stdout.write(line)
else:
sys.stdout.write('{0}: {1}'.format(node, line))
sys.stdout.flush()
if all:
rdy, _, _ = select.select(all, [], [], 10)
sys.exit(exitcode)
def run_cmdv(node, cmdv, all, pipedesc):
nopen = subprocess.Popen(
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
'type': 'stdout'}
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
'type': 'stderr'}
all.add(nopen.stdout)
all.add(nopen.stderr)
if __name__ == '__main__':
run()
+14 -6
View File
@@ -33,7 +33,7 @@ import confluent.client as client
argparser = optparse.OptionParser(
usage="Usage: %prog [options] <noderange> "
"([status|on|off|shutdown|boot|reset])")
"([status|on|off|shutdown|boot|reset|pdu_status|pdu_off|pdu_on])")
argparser.add_option('-p', '--showprevious', dest='previous',
action='store_true', default=False,
help='Show previous power state')
@@ -51,22 +51,27 @@ except IndexError:
client.check_globbing(noderange)
setstate = None
if len(args) > 1:
setstate = args[1]
if setstate == 'softoff':
setstate = 'shutdown'
elif not args[1] in ('stat', 'state', 'status'):
setstate = args[1]
if setstate not in (None, 'on', 'off', 'shutdown', 'boot', 'reset'):
if setstate not in (None, 'on', 'off', 'shutdown', 'boot', 'reset', 'pdu_status', 'pdu_stat', 'pdu_on', 'pdu_off', 'status', 'stat', 'state'):
argparser.print_help()
sys.exit(1)
session = client.Command()
exitcode = 0
session.add_precede_key('oldstate')
powurl = 'state'
if setstate and setstate.startswith('pdu_'):
setstate = setstate.replace('pdu_', '')
powurl = 'inlets/all'
if setstate in ('status', 'state', 'stat'):
setstate = None
if options.previous:
# get previous states
prev = {}
for rsp in session.read("/noderange/{0}/power/state".format(noderange)):
for rsp in session.read("/noderange/{0}/power/{1}".format(noderange, powurl)):
# gets previous (current) states
databynode = rsp["databynode"]
@@ -77,4 +82,7 @@ if options.previous:
# add dictionary to session
session.add_precede_dict(prev)
sys.exit(session.simple_noderange_command(noderange, '/power/state', setstate, promptover=options.maxnodes))
def outhandler(node, res):
for k in res[node]:
client.cprint('{0}: {1}: {2}'.format(node, k.replace('inlet_', ''), res[node][k]))
sys.exit(session.simple_noderange_command(noderange, '/power/{0}'.format(powurl), setstate, promptover=options.maxnodes, key='state', outhandler=outhandler))
+6 -1
View File
@@ -46,6 +46,8 @@ def run():
help='Specify a maximum number of '
'nodes to run rsync to, '
'prompting if over the threshold')
argparser.add_option('-l', '--loginname', type='str',
help='Username to use when connecting, defaults to current user.')
argparser.add_option('-f', '-c', '--count', type='int', default=168,
help='Number of nodes to concurrently rsync')
# among other things, FD_SETSIZE limits. Besides, spawning too many
@@ -61,7 +63,10 @@ def run():
c = client.Command()
cmdstr = ' '.join(args[:-1])
cmdstr = 'rsync -av --info=progress2 ' + cmdstr
cmdstr += ' {node}:' + targpath
if options.loginname:
cmdstr += ' {}@'.format(options.loginname) + '{node}:' + targpath
else:
cmdstr += ' {node}:' + targpath
currprocs = 0
all = set([])
+4 -1
View File
@@ -35,9 +35,12 @@ if path.startswith('/opt'):
import confluent.client as client
import confluent.sortutil as sortutil
devnull = None
def run():
global devnull
devnull = open(os.devnull, 'rb')
argparser = optparse.OptionParser(
usage="Usage: %prog [options] <noderange> <command expression>",
epilog="Expressions are the same as in attributes, e.g. "
@@ -133,7 +136,7 @@ def run():
def run_cmdv(node, cmdv, all, pipedesc):
try:
nopen = subprocess.Popen(
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
cmdv, stdin=devnull, 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]))
+40 -25
View File
@@ -34,10 +34,11 @@ if path.startswith('/opt'):
import confluent.client as client
import confluent.sortutil as sortutil
devnull = None
def run():
global devnull
devnull = open(os.devnull, 'rb')
argparser = optparse.OptionParser(
usage="Usage: %prog [options] noderange commandexpression",
epilog="Expressions are the same as in attributes, e.g. "
@@ -52,6 +53,8 @@ def run():
help='Specify a custom port for ssh')
argparser.add_option('-s', '--substitutename',
help='Use a different name other than the nodename for ssh')
argparser.add_option('-x', '--noexpression', action='store_true',
help='Suppress expression expansion of command')
argparser.add_option('-m', '--maxnodes', type='int',
help='Specify a maximum number of '
'nodes to run remote ssh command to, '
@@ -88,28 +91,39 @@ def run():
ex = exp.get('databynode', ())
for node in ex:
nodemap[node] = ex[node]['value']
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
{'expression': cmdstr}):
if 'error' in exp:
sys.stderr.write(exp['error'] + '\n')
exitcode |= exp.get('errorcode', 1)
ex = exp.get('databynode', ())
for node in ex:
cmd = ex[node]['value']
sshnode = nodemap.get(node, node)
if not isinstance(cmd, str) and not isinstance(cmd, bytes):
cmd = cmd.encode('utf-8')
cmdv = ['ssh']
if options.port:
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)
else:
pendingexecs.append((node, cmdv))
cmdparms = []
if options.noexpression:
for res in c.read('/noderange/{0}/nodes/'.format(args[0])):
if 'error' in res:
sys.stderr.write(res['error'] + '\n')
exitcode |= res.get('errorcode', 1)
break
node = res['item']['href'][:-1]
cmdparms.append((node, cmdstr))
else:
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
{'expression': cmdstr}):
if 'error' in exp:
sys.stderr.write(exp['error'] + '\n')
exitcode |= exp.get('errorcode', 1)
ex = exp.get('databynode', ())
for node in ex:
cmdparms.append((node, ex[node]['value']))
for node, cmd in cmdparms:
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 += ['-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)
else:
pendingexecs.append((node, cmdv))
if not all or exitcode:
sys.exit(exitcode)
rdy, _, _ = select.select(all, [], [], 10)
@@ -149,6 +163,7 @@ def run():
if options.nonodeprefix:
sys.stdout.write(line)
else:
line = line.lstrip('\x08')
sys.stdout.write('{0}: {1}'.format(node, line))
sys.stdout.flush()
if all:
@@ -158,7 +173,7 @@ def run():
def run_cmdv(node, cmdv, all, pipedesc):
nopen = subprocess.Popen(
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
'type': 'stdout'}
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
+6 -4
View File
@@ -208,7 +208,7 @@ class Command(object):
def add_precede_dict(self, dict):
self._prevdict = dict
def handle_results(self, ikey, rc, res, errnodes=None):
def handle_results(self, ikey, rc, res, errnodes=None, outhandler=None):
if 'error' in res:
if errnodes is not None:
errnodes.add(self._currnoderange)
@@ -245,10 +245,12 @@ class Command(object):
node, val, self._prevdict[node]))
else:
cprint('{0}: {1}'.format(node, val))
elif outhandler:
outhandler(node, res)
return rc
def simple_noderange_command(self, noderange, resource, input=None,
key=None, errnodes=None, promptover=None, **kwargs):
key=None, errnodes=None, promptover=None, outhandler=None, **kwargs):
try:
self._currnoderange = noderange
rc = 0
@@ -262,13 +264,13 @@ class Command(object):
if input is None:
for res in self.read('/noderange/{0}/{1}'.format(
noderange, resource)):
rc = self.handle_results(ikey, rc, res, errnodes)
rc = self.handle_results(ikey, rc, res, errnodes, outhandler)
else:
self.stop_if_noderange_over(noderange, promptover)
kwargs[ikey] = input
for res in self.update('/noderange/{0}/{1}'.format(
noderange, resource), kwargs):
rc = self.handle_results(ikey, rc, res, errnodes)
rc = self.handle_results(ikey, rc, res, errnodes, outhandler)
self._currnoderange = None
return rc
except KeyboardInterrupt:
@@ -10,12 +10,13 @@ import termios
import tty
def writeout(data):
done = False
if isinstance(data, str) and not isinstance(data, bytes):
data = data.encode('utf8')
try:
sys.stdout.buffer.write(data)
done = True
if hasattr(sys.stdout, 'buffer'):
sys.stdout.buffer.write(data)
else:
sys.stdout.write(data)
except IOError:
time.sleep(0.1)
pass
@@ -31,6 +32,7 @@ class LogReplay(object):
self.priordata = collections.deque([])
self.laststamp = None
self.needclear = False
self.lasttxt = ''
def _rewind(self, datasize=None):
curroffset = self.bin.tell() - 16
@@ -99,10 +101,8 @@ class LogReplay(object):
if reverse and self.bin.tell() < endoffset:
output += txtout
continue
if b'\x1b[2J' in txtout:
self.cleardata = txtout.split(b'\x1b[2J')
for idx in range(1, len(self.cleardata)):
self.cleardata[idx] = b'\x1b[2J' + self.cleardata[idx]
self.paginate(txtout)
if self.cleardata:
self.clearidx = 0
if not self.cleardata[0]:
self.cleardata = self.cleardata[1:]
@@ -118,8 +118,26 @@ class LogReplay(object):
break
if endoffset is not None and endoffset >= 0:
self.bin.seek(endoffset)
self.lasttxt = output
return output, 1
def paginate(self, txtout):
cleardata = [txtout]
nextcleardata = []
for sep in (b'\x1b[2J', b'\x1b[H\x1b[J'):
replacementcleardata = []
for txtout in cleardata:
nextcleardata = txtout.split(sep)
if len(nextcleardata) > 1:
for idx in range(1, len(nextcleardata)):
nextcleardata[idx] = sep + nextcleardata[idx]
replacementcleardata.extend(nextcleardata)
nextcleardata = []
cleardata = replacementcleardata
if len(cleardata) > 1:
self.cleardata = cleardata
return
def begin(self):
self.needclear = True
self.bin.seek(0)
@@ -127,8 +145,36 @@ class LogReplay(object):
def end(self):
self.bin.seek(0, 2)
def search(self, searchstr, skipfirst=False):
overlap = len(searchstr)
firstoffset = self.bin.tell()
lastoffset = firstoffset
output = self.lasttxt
while searchstr not in output:
output, delay = self.get_output()
if not self.cleardata and lastoffset == self.bin.tell():
self.bin.seek(firstoffset)
return b'', 0
lastoffset = self.bin.tell()
if skipfirst:
output, delay = self.get_output()
while searchstr not in output:
output, delay = self.get_output()
if not self.cleardata and lastoffset == self.bin.tell():
self.bin.seek(firstoffset)
return b'', 0
lastoffset = self.bin.tell()
clear = b'\x1b[2J\x1b[H'
txtchunks = self.lasttxt.split(searchstr)
output = clear + txtchunks[0]
txtchunks = txtchunks[1:]
while txtchunks:
output += b'\x1b[7m' + searchstr + b'\x1b[27m' + txtchunks[0]
txtchunks = txtchunks[1:]
return output, 0
def main(txtfile, binfile):
def _replay_to_console(txtfile, binfile):
replay = LogReplay(txtfile, binfile)
oldtcattr = termios.tcgetattr(sys.stdin.fileno())
tty.setraw(sys.stdin.fileno())
@@ -137,7 +183,9 @@ def main(txtfile, binfile):
reverse = False
skipnext = False
quitit = False
searchstr = None
writeout('\x1b[2J\x1b[;H')
prepend = ''
try:
while not quitit:
if not skipnext:
@@ -145,11 +193,18 @@ def main(txtfile, binfile):
skipnext = False
reverse = False
if newdata:
if prepend:
newdata = prepend + newdata
prepend = b''
writeout(newdata)
newdata = ''
writeout('\x1b]0;[Time: {0}]\x07'.format(
time.strftime('%m/%d %H:%M:%S', time.localtime(replay.laststamp))))
try:
sys.stdout.buffer.flush()
if hasattr(sys.stdout, 'buffer'):
sys.stdout.buffer.flush()
else:
sys.stdout.flush()
except IOError:
pass
while True:
@@ -173,32 +228,73 @@ def main(txtfile, binfile):
break
elif myinput.lower() == 'd':
writeout('\x1b];{0}\x07'.format(replay.debuginfo()))
if hasattr(sys.stdout, 'buffer'):
sys.stdout.buffer.flush()
else:
sys.stdout.flush()
elif myinput.lower() == '/':
sys.stdout.write('\x1b7\x1b[99999;0H\x1b[2K')
searchstr = ''
nxtchr = '/'
while '\r' not in searchstr:
sys.stdout.write(nxtchr)
sys.stdout.flush()
select.select((sys.stdin,), (), (), 86400)
nxtchr = sys.stdin.read(1)
if nxtchr in ('\x08', '\x7f'):
searchstr = searchstr[:-1]
nxtchr = '\x08 \x08'
else:
searchstr += nxtchr
if not isinstance(searchstr, bytes):
searchstr = searchstr.encode('utf8')
searchstr = searchstr[:-1]
newdata, delay = replay.search(searchstr)
if not newdata:
sys.stdout.write('\x1b[1K\rNo match found')
prepend = b'\x1b[1K\x1b8'
else:
sys.stdout.write('\x1b[1K\x1b8')
sys.stdout.flush()
skipnext = True
break
elif myinput.lower() == 'n' and searchstr:
newdata, delay = replay.search(searchstr, True)
if not newdata:
sys.stdout.write('\x1b7\x1b[99999;0H\x1b[2KNo more matches found')
sys.stdout.flush()
prepend = b'\x1b[1K\x1b8'
skipnext = True
break
else:
pass # print(repr(myinput))
except Exception:
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
writeout('\x1b[m')
writeout('\x1b[m\x1b[?25h\n')
raise
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
writeout('\x1b[m')
writeout('\x1b[m\x1b[?25h\n')
def replay_to_console(txtfile):
if os.path.exists(txtfile + '.cbl'):
binfile = txtfile + '.cbl'
elif '.' not in txtfile:
if '/' not in txtfile:
txtfile = os.getcwd() + '/' + txtfile
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(txtfile + '.cbl'))
sys.exit(1)
else:
fileparts = txtfile.split('.')
prefix = '.'.join(fileparts[:-1])
binfile = prefix + '.cbl.' + fileparts[-1]
if not os.path.exists(binfile):
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(binfile))
sys.exit(1)
_replay_to_console(txtfile, binfile)
if __name__ == '__main__':
txtfile = sys.argv[1]
if len(sys.argv) > 2:
binfile = sys.argv[2]
else:
if os.path.exists(txtfile + '.cbl'):
binfile = txtfile + '.cbl'
else:
fileparts = txtfile.split('.')
prefix = '.'.join(fileparts[:-1])
binfile = prefix + '.cbl.' + fileparts[-1]
if not os.path.exists(binfile):
sys.stderr.write('Unable to locate cbl file\n')
sys.exit(1)
main(txtfile, binfile)
replay_to_console(sys.argv[1])
+43 -13
View File
@@ -29,6 +29,9 @@ class ScreenPrinter(object):
self.client = client
self.nodeoutput = {}
self.nodelist = []
self.nodepos = {}
self.lastrows = 0
self.fieldchanged = False
maxlen = 0
for ans in self.client.read('/noderange/{0}/nodes/'.format(noderange)):
if 'error' in ans:
@@ -50,16 +53,19 @@ class ScreenPrinter(object):
if len(text) >= self.textlen:
self.textlen = len(text) + 1
self.fieldwidth = self.textlen + self.nodenamelen + 1
self.drawscreen()
self.fieldchanged = True
self.drawscreen(node)
def drawscreen(self):
def drawscreen(self, node=None):
if self.squeeze:
currheight, currwidth = get_screengeom()
currheight -= 1
currheight -= 2
if currheight < 1:
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
#sys.stdout.write('\x1b[2J\x1b[;H') # clear screen
if len(self.nodelist) < (numfields * currheight):
numfields = len(self.nodelist) // currheight + 1
else:
@@ -67,16 +73,40 @@ class ScreenPrinter(object):
fieldformat = '{0}: {1}'
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')
if self.lastrows:
sys.stdout.write('\x1b[{0}A'.format(self.lastrows))
if node and self.lastrows == len(columns[0]) and not self.fieldchanged:
targline, targcol = self.nodepos[node]
if targline:
sys.stdout.write('\x1b[{0}B'.format(targline))
if targcol:
sys.stdout.write('\x1b[{0}C'.format(targcol))
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
sys.stdout.write('\r\x1b[{0}B'.format(self.lastrows - targline))
else:
self.lastrows = 0
self.fieldchanged = False
column = 0
for currow in range(0, len(columns[0])):
sys.stdout.write('\x1b[2K')
for col in columns:
try:
node = col[currow]
self.nodepos[node] = (currow, column)
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
column += len(fieldformat.format(node, self.nodeoutput[node]))
except IndexError:
break
sys.stdout.write('\n')
column = 0
self.lastrows += 1
sys.stdout.write('\x1b[J')
else:
for node in self.nodelist:
if node:
nodes = [node]
else:
nodes = self.nodelist
for node in nodes:
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
sys.stdout.write('\n')
sys.stdout.flush()
+7 -4
View File
@@ -53,13 +53,16 @@ class TermHandler(object):
connection = None
try:
connection, address = self.socket.accept()
connection.sendall("confetty control v1--\n")
connection.sendall(b"confetty control v1--\n")
cmd = connection.recv(8)
if 'GETWINID' == cmd:
connection.sendall(os.environ['WINDOWID'])
if b'GETWINID' == cmd:
winid = os.environ['WINDOWID']
if not isinstance(winid, bytes):
winid = winid.encode('utf8')
connection.sendall(winid)
connection.close()
except BaseException:
pass
finally:
if connection is not None:
connection.close()
connection.close()
+12 -4
View File
@@ -19,8 +19,12 @@ import array
import ctypes
import ctypes.util
import confluent.tlv as tlv
import eventlet.green.socket as socket
import eventlet.green.select as select
try:
import eventlet.green.socket as socket
import eventlet.green.select as select
except ImportError:
import socket
import select
from datetime import datetime
import json
import os
@@ -240,8 +244,12 @@ def recv(handle):
cdata = cmsgarr[CMSG_LEN(0).value:]
data = rawbuffer[:i]
if cmsg.cmsg_level == socket.SOL_SOCKET and cmsg.cmsg_type == SCM_RIGHTS:
filehandles.fromstring(bytes(
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
try:
filehandles.fromstring(bytes(
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
except AttributeError:
filehandles.frombytes(bytes(
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
data = json.loads(bytes(data))
return ClientFile(data['filename'], data['mode'], filehandles[0])
else:
+11 -16
View File
@@ -12,8 +12,8 @@ Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
Prefix: %{_prefix}
BuildArch: noarch
Vendor: Jarrod Johnson <jjohnson2@lenovo.com>
Url: http://xcat.sf.net/
Vendor: Lenovo
Url: http://github.com/lenovo/confluent
Obsoletes: confluent_common
%description
@@ -24,25 +24,17 @@ a confluent server.
%setup -n %{name}-%{version} -n %{name}-%{version}
%build
%if "%{dist}" == ".el8"
python3 setup.py build
%else
%if "%{dist}" == ".el9"
python3 setup.py build
%else
%if "%{dist}" == ".el7"
python2 setup.py build
%endif
%else
python3 setup.py build
%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
%if "%{dist}" == ".el7"
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
%else
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
%endif
@@ -50,4 +42,7 @@ python2 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUI
rm -rf $RPM_BUILD_ROOT
%files -f INSTALLED_FILES
%license /opt/confluent/share/licenses/confluent_client/LICENSE
%license /opt/confluent/share/licenses/confluent_client/COPYRIGHT
%defattr(-,root,root)
/opt/confluent
+40 -4
View File
@@ -82,6 +82,17 @@ function _confluent_generic_completion()
return;
fi
}
function _confluent_generic_ng_completion()
{
_confluent_get_args
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
fi
if [ $NUMARGS -lt 3 ]; then
_confluent_ng_completion
return;
fi
}
_confluent_nodeidentify_completion()
{
COMP_CANDIDATES=("on,off,blink -h")
@@ -142,7 +153,7 @@ _confluent_osimage_completion()
{
_confluent_get_args
if [ $NUMARGS == 2 ]; then
COMPREPLY=($(compgen -W "initialize import updateboot" -- ${COMP_WORDS[COMP_CWORD]}))
COMPREPLY=($(compgen -W "initialize import updateboot rebase" -- ${COMP_WORDS[COMP_CWORD]}))
return
elif [ ${CMPARGS[1]} == 'initialize' ]; then
COMPREPLY=($(compgen -W "-h -u -s -t -i" -- ${COMP_WORDS[COMP_CWORD]}))
@@ -150,7 +161,7 @@ _confluent_osimage_completion()
compopt -o default
COMPREPLY=()
return
elif [ ${CMPARGS[1]} == 'updateboot' ]; then
elif [ ${CMPARGS[1]} == 'updateboot' -o ${CMPARGS[1]} == 'rebase' ]; then
COMPREPLY=($(compgen -W "-n $(confetty show /deployment/profiles|sed -e 's/\///')" -- "${COMP_WORDS[COMP_CWORD]}"))
return
fi
@@ -268,7 +279,20 @@ _confluent_nodeattrib_completion()
_confluent_generic_completion
}
_confluent_define_completion()
{
COMP_CANDIDATES=$(nodegroupattrib 'everything' all | awk '{print $2}'|sed -e 's/://')
_confluent_get_args
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
fi
}
_confluent_nodegroupattrib_completion()
{
COMP_CANDIDATES=$(nodegroupattrib 'everything' all | awk '{print $2}'|sed -e 's/://')
_confluent_generic_ng_completion
}
_confluent_nn_completion()
{
_confluent_get_args
@@ -323,18 +347,30 @@ _confluent_ng_completion()
COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/)" -- "${COMP_WORDS[COMP_CWORD]}"))
}
_confluent_nodediscover_completion()
{
_confluent_get_args
if [ $NUMARGS == 2 ]; then
COMPREPLY=($(compgen -W "list assign rescan clear subscribe unsubscribe register" -- ${COMP_WORDS[COMP_CWORD]}))
return;
fi
}
complete -F _confluent_nodediscover_completion nodediscover
complete -F _confluent_nodeattrib_completion nodeattrib
complete -F _confluent_nodeattrib_completion nodegroupattrib
complete -F _confluent_nr_completion nodebmcreset
complete -F _confluent_nodesetboot_completion nodeboot
complete -F _confluent_nr_completion nodeconfig
complete -F _confluent_nn_completion nodeconsole
complete -F _confluent_define_completion nodedefine
complete -F _confluent_define_completion nodegroupdefine
complete -F _confluent_nr_completion nodeeventlog
complete -F _confluent_nodefirmware_completion nodefirmware
complete -F _confluent_nodedeploy_completion nodedeploy
complete -F _confluent_osimage_completion osdeploy
complete -F _confluent_imgutil_completion imgutil
complete -F _confluent_ng_completion nodegroupattrib
complete -F _confluent_nodegroupattrib_completion nodegroupattrib
complete -F _confluent_ng_completion nodegroupremove
complete -F _confluent_nr_completion nodehealth
complete -F _confluent_nodeidentify_completion nodeidentify
@@ -0,0 +1,28 @@
confluent2hosts(8) -- Generate /etc/hosts entries for nodes
========================================================
## SYNOPSIS
`confluent2hosts -i <ip expression> -n <name expression> <noderange>`
`confluent2hosts -a <noderange>`
## DESCRIPTION
`confluent2hosts` can be used to help generate /etc/hosts entries for a
noderange. There are two general approaches.
It can be used ad-hoc, using -i and -n to specify the address and name portions respectively. This accepts the standard confluent expression syntax, allowing for things like 172.30.1.{n1} or {node}.{dns.domain} or {bmc}.
It can also read from the confluent db, using `-a`. In this mode, each net.<value>.<attribute> group is pulled together into hosts lines. ipv4_address and ipv6_address fields are associated with the corresponding hostname attributes.
## EXAMPLES
* Generate /etc/hosts entries ad-hoc using default name:
`# confluent2hosts -i 10.2.3.{n1} d9-d12`
* Generate /etc/hosts entries ad-hoc using alternative name:
`# confluent2hosts -i 10.2.3.{n1} -n "{node}-alt {node}-alt.{dns.domain}" d9-d12`
* Generate /etc/hosts entries using the confluent DB as a reference:
`# confluent2hosts -a d9-d12`
@@ -83,12 +83,12 @@ to a blank value will allow masking a group defined attribute with an empty valu
`# nodeattrib n1,n2 hardwaremanagement --blame`
`n1: hardwaremanagement.manager: 172.30.3.1`
`n1: hardwaremanagement.method: ipmi (inherited from group everything)`
`n1: hardwaremanagement.switch: r8e1`
`n1: hardwaremanagement.switchport: 14`
`n1: net.switch: r8e1`
`n1: net.switchport: 14`
`n2: hardwaremanagement.manager: 172.30.3.2`
`n2: hardwaremanagement.method: ipmi (inherited from group everything)`
`n2: hardwaremanagement.switch: r8e1`
`n2: hardwaremanagement.switchport: 2`
`n2: net.switch: r8e1`
`n2: net.switchport: 2`
* Listing matching nodes of a simple noderange that are set:
`# nodeattrib n1-n2 current`
+25
View File
@@ -22,6 +22,31 @@ a new session.
Use tmux to arrange consoles of the given noderange into a tiled layout on
the terminal screen
* `-l`, `--log`:
Perform a log reply on the current, local log in /var/log/confluent/consoles.
If in collective mode, this only makes sense to use on the current collective
manager at this time.
* `-w`, `--windowed`:
Open terminal windows for each node. The
environment variable **NODECONSOLE_WINDOWED_COMMAND**
should be set, which should be a text string corresponding
to a command that can be used to open a windowed console,
omitting the `nodeconsole <noderange>` part of the
command, for example, to open a set of consoles for a
range of nodes in separate xterm windows, set
**NODECONSOLE_WINDOWED_COMMAND** to `xterm -e`. To open a
set of consoles for a range of nodes in separate
GNOME Terminal windows with a size of 100 columns and
31 rows, set **NODECONSOLE_WINDOWED_COMMAND**
to `gnome-terminal --geometry 100x31 --` or in a WSL
environment, to open a set of consoles for a range of
nodes in separate Windows Terminal windows, with the
title set for each node, set **NODECONSOLE_WINDOWED_COMMAND**
to `wt.exe wsl.exe -d AlmaLinux-8 --shell-type login. If the
NODECONSOLE_WINDOWED_COMMAND environment variable isn't set,
xterm will be used bydefault.
## ESCAPE SEQUENCE COMMANDS
While connected to a console, a number of commands may be performed through escape
+12 -3
View File
@@ -5,9 +5,12 @@ nodediscover(8) -- List or manage confluent node discovery
`nodediscover rescan`
`nodediscover [options] list`
`nodediscover [options] assign`
`nodediscover [options] rescan`
`nodediscover [options] clear`
`nodediscover [options] assign`
`nodediscover [options] rescan`
`nodediscover [options] clear`
`nodediscover [options] subscribe [switch]`
`nodediscover [options] unsubscribe [switch]`
`nodediscover [options] register [Addresses, e.g. 192.168.1.2 or 192.168.1.0/24 or 192.168.1.1-192.168.1.28]`
## DESCRIPTION
@@ -35,6 +38,12 @@ scan is the best approach to catch something that appears to be missing.
**nodedsicover clear** requests the server forget about a selection of
detected device. It takes the same arguments as **nodediscover list**.
**nodediscover subscribe** and **unsubscribe** instructs confluent to subscribe to or
unsubscribe from the designated switch running affluent with system discovery support.
**nodediscover register** instructs confluent to perform a remote probe of an address, subnet,
or range of IP addresses.
## CSV FORMAT
The CSV format used by nodediscover consists of one header describing the
+47
View File
@@ -0,0 +1,47 @@
nodeping(8) -- Pings a node or a noderange.
==============================
## SYNOPSIS
`nodeping [options] noderange`
## DESCRIPTION
**nodeping** is a command that pings the default NIC on a node.
It can also be used with the `-s` flag to change the ping location to something that is 'non primary'
## OPTIONS
* ` -f` COUNT, `-c` COUNT, --count=COUNT
Number of commands to run at a time
* `-h`, `--help`:
Show help message and exit
* `-s` SUBSTITUTENAME, --substitutename=SUBSTITUTENAME
Use a different name other than the nodename for ping. This may be a
expression, such as {bmc} or, if no { character is present, it is treated as a suffix. -s -eth1 would make n1 become n1-eth1, for example.
## EXAMPLES
* Pinging a node :
`# nodeping <node>`
`node : ping`
* Pinging a group:
`# nodeping <groupname>`
`Node1 : ping
Node2 : ping
Node3 : ping`
* Pinging BMC on a node:
`# nodeping -s {bmc} <noderange>`
` Node-bmc : ping`
* Pinging by specifying a suffix:
`# nodeping d1-d4 -s -eth1`
`d2-eth1: no_ping`
`d1-eth1: no_ping`
`d3-eth1: no_ping`
`d4-eth1: no_ping`
* Fail to ping node:
`# nodeping <node>`
`node : no_ping`
+3
View File
@@ -24,6 +24,9 @@ respond.
* `reset`: Request immediate reset of nodes of the noderange. Nodes that are
off will not react to this request.
* `status`: Behave identically to having no argument passed at all.
* `pdu_status`: Query state of associated PDU outlets, if configured.
* `pdu_on`: Energize all PDU outlets associated with the noderange.
* `pdu_off`: De-energize all PDU outlets associated with the noderange.
## OPTIONS
+4 -2
View File
@@ -5,6 +5,7 @@ osdeploy(8) --- Configure general OS deployment facilities of confluent
`osdeploy import <iso>`
`osdeploy updateboot <profile>`
`osdeploy rebase <profile>`
`osdeploy initialize [-h] [-g] [-u] [-s] [-k] [-t] [-p] [-i] [-l] [-a]`
## DESCRIPTION
@@ -13,5 +14,6 @@ osdeploy(8) --- Configure general OS deployment facilities of confluent
`import` subcommand will generate profiles from an iso image. `updateboot` will take any
changes in the specified update that need to be pushed into boot configuration and/or images
and ensure those needed changes are performed. `initialize` provides assistance in setting
up the most commonly required facilities. Run `osdeploy initialize -h` for more detail
on the options offered by `osdeploy initialize`
up the most commonly required facilities. `rebase` has confluent attempt to update profile
content that came from /opt/confluent in an rpm update, if profile supports it.
Run `osdeploy initialize -h` for more detail on the options offered by `osdeploy initialize`.
+9 -3
View File
@@ -1,7 +1,9 @@
from setuptools import setup
import os
data_files = [('/etc/profile.d', ['confluent_env.sh', 'confluent_env.csh'])]
data_files = [('/etc/profile.d', ['confluent_env.sh', 'confluent_env.csh']),
('/opt/confluent/share/licenses/confluent_client/', ['LICENSE', 'COPYRIGHT'])
]
try:
scriptlist = ['bin/{0}'.format(d) for d in os.listdir('bin/')]
data_files.append(('/opt/confluent/share/man/man1', ['man/man1/' + x for x in os.listdir('man/man1')]))
@@ -13,10 +15,14 @@ except OSError:
setup(
name='confluent_client',
version='#VERSION#',
author='Jarrod Johnson',
author='Lenovo',
author_email='jjohnson2@lenovo.com',
url='http://xcat.sf.net/',
url='http://github.com/lenovo/confluent/',
license='Apache-2.0',
description='Command line client and libraries for confluent management server',
long_description='Command line client and libraries for confluent management server',
packages=['confluent'],
platforms=['Linux'],
scripts=scriptlist,
data_files=data_files,
)
+1
View File
@@ -5,6 +5,7 @@ if [ "$NUMCOMMITS" != "$VERSION" ]; then
fi
sed -e "s/#VERSION#/$VERSION/" confluent_osdeploy.spec.tmpl > confluent_osdeploy.spec
cd ..
cp ../LICENSE .
tar Jcvf confluent_osdeploy.tar.xz confluent_osdeploy
mv confluent_osdeploy.tar.xz ~/rpmbuild/SOURCES/
cd -
@@ -132,8 +132,11 @@ def scan_confluents():
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])
currip = peer[0]
if currip.startswith('fe80::') and '%' not in currip:
currip = '{0}%{1}'.format(currip, peer[-1])
srvs[currip] = current
srvlist.append(currip)
r = select.select((s4, s6), (), (), 2)
if r:
r = r[0]
@@ -156,6 +159,7 @@ def get_net_apikey(nodename, mgr):
else:
cliaddr = ('::', 302)
clisock.bind(cliaddr)
clisock.settimeout(1)
clisock.connect(addrinfo[-1])
rsp = clisock.recv(8)
if rsp != b'\xc2\xd1-\xa8\x80\xd8j\xba':
@@ -219,7 +223,8 @@ def get_apikey(nodename, hosts, errout=None):
return apikey
class HTTPSClient(client.HTTPConnection, object):
def __init__(self, usejson=False, port=443, host=None, errout=None):
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None):
self.phmac = phmac
self.errout = None
if errout:
self.errout = open(errout, 'w')
@@ -240,24 +245,34 @@ class HTTPSClient(client.HTTPConnection, object):
self.hosts = []
info = open('/etc/confluent/confluent.info').read().split('\n')
havedefault = '0'
plainhost = ''
for line in info:
host = ''
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 not plainhost:
plainhost = host
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)
if '%' in host:
ifidx = host.split('%', 1)[1]
with open('/tmp/confluent.ifidx', 'w+') as ifout:
ifout.write(ifidx)
if not mgtiface:
host, mgtiface, havedefault = extinfo[:3]
if host:
if havedefault == '0':
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:
@@ -266,7 +281,13 @@ class HTTPSClient(client.HTTPConnection, object):
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 plainhost and not self.hosts:
self.hosts.append(plainhost)
if self.phmac:
with open(phmac, 'r') as hmacin:
self.stdheaders['CONFLUENT_CRYPTHMAC'] = hmacin.read()
else:
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, self.hosts, errout=self.errout)
if mgtiface:
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
self.port = port
@@ -403,6 +424,21 @@ if __name__ == '__main__':
errout = sys.argv.pop(errout)
except ValueError:
errout = None
try:
phmac = sys.argv.index('-p')
sys.argv.pop(phmac)
phmac = sys.argv.pop(phmac)
except ValueError:
phmac = None
try:
checkonly = False
idxit = sys.argv.index('-c')
sys.argv.pop(idxit)
checkonly = True
except Exception:
pass
if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
data = open(sys.argv[-1]).read()
if outbin:
with open(outbin, 'ab+') as outf:
reader = HTTPSClient(usejson=usejson, errout=errout).grab_url(
@@ -412,13 +448,13 @@ if __name__ == '__main__':
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()
client = HTTPSClient(usejson, errout=errout, phmac=phmac)
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())
elif checkonly:
sys.stdout.write(client.check_connections())
else:
sys.stdout.write(HTTPSClient(usejson, errout=errout).grab_url(sys.argv[1], data).decode())
sys.stdout.write(client.grab_url(sys.argv[1], data).decode())
@@ -25,6 +25,8 @@ def add_lla(iface, mac):
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:
with open('/proc/sys/net/ipv6/conf/{0}/disable_ipv6'.format(iface), 'w') as setin:
setin.write('0')
subprocess.check_call(['ip', 'addr', 'add', 'dev', iface, lla, 'scope', 'link'])
except Exception:
return None
@@ -154,12 +156,14 @@ class WickedManager(object):
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)
stgs['connection_name'] = 'bond{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']))
if stgs['team_mode'] == 'lacp':
stgs['team_mode'] = '802.3ad'
teamout.write("BONDING_MODULE_OPTS='mode={0} miimon=100'\nBONDING_MASTER=yes\n".format(stgs['team_mode']))
idx = 1
for iface in cfg['interfaces']:
subprocess.call(['wicked', 'ifdown', iface])
@@ -168,7 +172,7 @@ class WickedManager(object):
os.remove('/etc/sysconfig/network/ifroute-{0}'.format(iface))
except OSError:
pass
teamout.write('TEAM_PORT_DEVICE_{0}={1}\n'.format(idx, iface))
teamout.write('BONDING_SLAVE{0}={1}\n'.format(idx, iface))
idx += 1
else:
cname = list(cfg['interfaces'])[0]
@@ -325,18 +329,20 @@ if __name__ == '__main__':
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 iname in iname.split(','):
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]}
for iname in iname.split(','):
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'):
@@ -29,8 +29,7 @@ cp confluent_imginfo copernicus clortho autocons ../opt/confluent/bin
cp start_root urlmount ../stateless-bin/
cd ..
ln -s el8 el9
ln -s el8-diskless el9-diskless
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 coreos el9; do
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 coreos el9; do
mkdir ${os}out
cd ${os}out
if [ -d ../${os}bin ]; then
@@ -74,9 +73,13 @@ mv ../addons.tgz .
cd ..
cp -a esxi7out esxi6out
cp -a esxi7 esxi6
cp -a esxi7out esxi8out
cp -a esxi7 esxi8
%install
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 esxi6 esxi7 coreos; do
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu22.04 esxi6 esxi7 esxi8 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
@@ -98,3 +101,4 @@ find %{buildroot}/opt/confluent/lib/osdeploy/ -name .gitignore -exec rm -f {} +
%files
/opt/confluent/lib/osdeploy
%license /opt/confluent/share/licenses/confluent_osdeploy/LICENSE
@@ -5,6 +5,55 @@ umask 0077
mkdir -p /etc/confluent
echo -n > /etc/confluent/confluent.info
umask $oum
if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
tmnt=$(mktemp -d)
tcfg=$(mktemp)
mount /dev/disk/by-label/CNFLNT_IDNT $tmnt
cd $tmnt
deploysrvs=$(sed -n '/^deploy_servers:/, /^[^-]/p' cnflnt.yml |grep ^-|sed -e 's/^- //'|grep -v :)
nodename=$(grep ^nodename: cnflnt.yml|awk '{print $2}')
sed -n '/^net_cfgs:/, /^[^- ]/{/^[^- ]/!p}' cnflnt.yml |sed -n '/^-/, /^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "dhcp" ]; then
/usr/libexec/nm-initrd-generator ip=:dhcp
else
v4addr=$(grep ^ipv4_address: $tcfg)
v4addr=${v4addr#ipv4_address: }
v4addr=${v4addr%/*}
v4gw=$(grep ^ipv4_gateway: $tcfg)
v4gw=${v4gw#ipv4_gateway: }
if [ "$v4gw" = "null" ]; then
v4gw=""
fi
v4nm=$(grep ipv4_netmask: $tcfg)
v4nm=${v4nm#ipv4_netmask: }
/usr/libexec/nm-initrd-generator ip=$v4addr::$v4gw:$v4nm:$hostname::none
fi
NetworkManager --configure-and-quit=initrd --no-daemon
hmackeyfile=$(mktemp)
echo -n $(grep ^apitoken: cnflnt.yml|awk '{print $2}') > $hmackeyfile
cd -
umount $tmnt
passfile=$(mktemp)
passcrypt=$(mktemp)
hmacfile=$(mktemp)
ln -s /opt/confluent/bin/clortho /opt/confluent/bin/genpasshmac
/opt/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
for deployer in $deploysrvs; do
if curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt -k https://$deployer/confluent-api/self/registerapikey; then
cp $passfile /etc/confluent/confluent.apikey
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/profileprivate/pending/config.ign > /config.ign
[ -s /config.ign ] || rm /config.ign
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg)
confluent_profile=${confluent_profile#profile: }
curl -sf https://$deployer/confluent-public/os/$confluent_profile/rootfs.img | rdcore stream-hash /etc/coreos-live-want-rootfs | bsdtar -xf - -C /
exit 0
fi
done
fi
cd /sys/class/net
echo "Searching for confluent deployment server...." > /dev/console
while ! grep ^NODE /etc/confluent/confluent.info; do
@@ -18,6 +67,7 @@ while ! grep ^NODE /etc/confluent/confluent.info; do
if ! grep ^NODE /etc/confluent/confluent.info; then
echo 'Current net config:' > /dev/console
ip -br a > /dev/console
exit 1
fi
done
echo "Found confluent deployment services on local network" > /dev/console
@@ -65,7 +115,7 @@ curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_api
[ -s /config.ign ] || rm /config.ign
umask $oldumask
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg)
confluent_profile=${confluent_profile#profile: }
@@ -0,0 +1,97 @@
reverse_uuid() {
echo $1 | head -c 8 | tail -c 2
echo $1 | head -c 6 | tail -c 2
echo $1 | head -c 4 | tail -c 2
echo $1 | head -c 2 | tail -c 2
echo $1 | head -c 13 | tail -c 2
echo $1 | head -c 11 | tail -c 2
echo $1 | head -c 18 | tail -c 2
echo $1 | head -c 16 | tail -c 2
echo $1 | tail -c 18 | sed -e s/-//
}
setdebopt() {
debconf-set $1 $2
echo d-i $1 $3 $2 >> /preseed.cfg
}
dhuuid=$(reverse_uuid $(cat /sys/devices/virtual/dmi/id/product_uuid))
dhcpid=$(mktemp)
mkdir -p /etc/confluent
cp /tls/* /etc/ssl/certs/
for nic in $(ip link | grep mtu|grep -v LOOPBACK|cut -d: -f 2|sed -e 's/ //'); do
ip link set $nic up
done
for nic in $(ip link | grep mtu|grep -v LOOPBACK|grep LOWER_UP|cut -d: -f 2|sed -e 's/ //'); do
if udhcpc -i $nic -p $dhcpid -t 2 -T 2 -n -x 93:0007 -x 97:00$dhuuid -q; then
/opt/confluent/bin/copernicus > /etc/confluent/confluent.info
if grep ^MANAGER:.*\\. /etc/confluent/confluent.info ; then
break
fi
fi
ip -4 flush dev $nic
done
mgr=$(grep ^MANAGER:.*\\. /etc/confluent/confluent.info|head -n 1|cut -d: -f 2|sed -e 's/ //')
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info|head -n 1|cut -d: -f 2|sed -e 's/ //')
/opt/confluent/bin/clortho $nodename $mgr > /etc/confluent/confluent.apikey
apikey=$(cat /etc/confluent/confluent.apikey)
cd /etc/confluent
wget --header="CONFLUENT_NODENAME: $nodename" --header="CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/deploycfg
cd -
predir=$(mktemp -d)
cd $predir
profile=$(grep ^profile: /etc/confluent/deploycfg|cut -d ' ' -f 2)
wget https://$mgr/confluent-public/os/$profile/scripts/pre.sh
chmod u+x pre.sh
wget https://$mgr/confluent-public/os/$profile/preseed.cfg
mv preseed.cfg /
setdebopt auto-install/enable true boolean
setdebopt partman/early_command $predir/pre.sh string
cd -
ip -4 a flush dev $nic
setdebopt netcfg/choose_interface $nic select
setdebopt netcfg/get_hostname $nodename string
v4cfgmeth=$(grep ipv4_method: /etc/confluent/deploycfg |cut -d: -f 2|sed -e 's/ //')
if [ "$v4cfgmeth" = "static" ]; then
setdebopt netcfg/disable_dhcp true boolean
v4addr=$(grep ^ipv4_address: /etc/confluent/deploycfg|cut -d: -f 2|sed -e 's/ //')
v4gw=$(grep ^ipv4_gateway: /etc/confluent/deploycfg|cut -d: -f 2| sed -e 's/ //')
if [ "$v4gw" = "null" ]; then
v4gw=""
fi
v4nm=$(grep ^ipv4_netmask: /etc/confluent/deploycfg|cut -d: -f 2|sed -e 's/ //')
setdebopt netcfg/get_netmask $v4nm string
setdebopt netcfg/get_ipaddress $v4addr string
setdebopt netcfg/confirm_static true boolean
if [ ! -z "$v4gw" ]; then
setdebopt netcfg/get_gateway $v4gw string
fi
namesrvs=$(sed -n '/^nameservers:/,/^[^-]/p' /etc/confluent/deploycfg|grep ^- | cut -d ' ' -f 2|sed -e 's/ //')
for namesrv in "$namesrvs"; do
setdebopt netcfg/get_nameservers $namesrv string
done
elif [ "$vpcfgmeth" = "dhcp" ]; then
setdebopt netcfg/disable_dhcp false boolean
setdebopt netcfg/confirm_static false boolean
fi
rootpass=$(grep ^rootpassword: /etc/confluent/deploycfg|cut -d ' ' -f 2|sed -e 's/ //')
if [ "$rootpass" = null ] || [ -z "$rootpass" ]; then
setdebopt passwd/root-login false boolean
else
setdebopt passwd/root-login true boolean
setdebopt passwd/root-password-crypted $rootpass string
fi
setdebopt time/zone $(grep ^timezone: /etc/confluent/deploycfg|cut -d ' ' -f 2|sed -e 's/ //') string
ntpsrvs=$(sed -n '/^ntpservers:/,/^[^-]/p' /etc/confluent/deploycfg|grep ^- | cut -d ' ' -f 2|sed -e 's/ //')
for ntpsrv in "$ntpsrvs"; do
setdebopt clock-setup/ntp true boolean
setdebopt clock-setup/ntep-server $ntpsrv string
done
#setdebopt console-setup/layoutcode $(grep ^keymap: /etc/confluent/deploycfg|cut -d ' ' -f 2) string
setdebopt debian-installer/locale $(grep ^locale: /etc/confluent/deploycfg|cut -d ' ' -f 2) select
domainname=$(grep ^dnsdomain: /etc/confluent/deploycfg|cut -d ' ' -f 2)
if [ ! -z "$domainname" ] && [ "$domainname" != "null" ]; then
setdebopt netcfg/get_domain $domainname string
fi
@@ -0,0 +1,18 @@
anna-install openssh-server-udeb
mkdir -p ~/.ssh/
cat /ssh/*pubkey > ~/.ssh/authorized_keys
ssh-keygen -A
mgr=$(grep ^MANAGER:.*\\. /etc/confluent/confluent.info|head -n 1|cut -d: -f 2|sed -e 's/ //')
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info|head -n 1|cut -d: -f 2|sed -e 's/ //')
apikey=$(cat /etc/confluent/confluent.apikey)
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey%.pub}-cert.pub
keyfile=${pubkey%.pub}
wget --post-file=$pubkey --header='CONFLUENT_NODENAME: '$nodename --header="CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/sshcert -O $certfile
echo HostKey $keyfile >> /etc/ssh/sshd_config
echo HostCertificate $certfile >> /etc/ssh/sshd_config
done
echo sshd:x:939:939::/: >> /etc/passwd
/usr/sbin/sshd
@@ -11,11 +11,10 @@ confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{pr
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
@@ -37,4 +36,5 @@ curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLU
systemctl disable firstboot
rm /etc/systemd/system/firstboot.service
rm /etc/confluent/firstboot.ran
kill $logshowpid
) &
tail --pid $! -F /var/log/confluent/confluent-firstboot.log > /dev/console
@@ -1,6 +1,10 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/confluent-api/ > /dev/null; then
return 0
fi
return 1
@@ -58,7 +62,11 @@ fetch_remote() {
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -165,7 +173,11 @@ run_remote_python() {
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
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -54,7 +54,7 @@ class DiskInfo(object):
@property
def priority(self):
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
@@ -15,7 +15,7 @@ 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)"
echo "Unable to retrieve private key from $confluent_mgr (verify that confluent can access /var/lib/confluent/private/os/$confluent_profile/pending/rootimg.key)"
sleep 1
done
cipher=$(head -n 1 /tmp/rootimg.key)
@@ -9,6 +9,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_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export nodename confluent_mgr confluent_profile
. /etc/confluent/functions
mkdir -p /var/log/confluent
@@ -18,4 +18,6 @@ mkdir -p /sysroot/etc/ssh/
for i in /ssh/*.ca; do
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
done
cp /opt/confluent/bin/apiclient /sysroot/etc/confluent
mkdir -p /sysroot/opt/confluent/bin
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
cp /opt/confluent/bin/apiclient /sysroot/etc/confluent/
@@ -1,6 +1,10 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/confluent-api/ > /dev/null; then
return 0
fi
return 1
@@ -58,7 +62,11 @@ fetch_remote() {
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -165,7 +173,11 @@ run_remote_python() {
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
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -46,10 +46,15 @@ class DiskInfo(object):
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'):
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
@@ -79,5 +79,9 @@ if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then
echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning
echo autopart --nohome $LUKSPARTY >> /tmp/partitioning
fi
python /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
if [ -f /opt/confluent/bin/apiclient ]; then
python /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
else
python /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
fi
kill $logshowpid
@@ -10,6 +10,8 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
export confluent_mgr confluent_profile nodename
cp -a /etc/confluent /mnt/sysimage/etc
mkdir -p /mnt/sysimage/opt/confluent/bin
cp /opt/confluent/bin/apiclient /mnt/sysimage/opt/confluent/bin/
chmod -R og-rwx /mnt/sysimage/etc/confluent
cp /tmp/functions /mnt/sysimage/etc/confluent/
hostnamectl set-hostname $nodename
@@ -3,9 +3,9 @@ import importlib
import tempfile
import json
import os
import shutil
import pwd
import grp
import shutil
try:
from importlib.machinery import SourceFileLoader
def load_source(mod, path):
@@ -19,6 +19,7 @@ except IOError:
apiclient = load_source('apiclient', '/etc/confluent/apiclient')
def partitionhostsline(line):
comment = ''
try:
@@ -271,6 +272,7 @@ def synchronize():
os.chown(fname, uid, gid)
finally:
shutil.rmtree(tmpdir)
shutil.rmtree(appendoncedir)
if __name__ == '__main__':
@@ -25,6 +25,7 @@ get_remote_apikey() {
fi
done
}
. /lib/dracut-lib.sh
root=1
rootok=1
netroot=confluent
@@ -94,6 +95,41 @@ cd - > /dev/null
rm -rf $tpmdir
touch /etc/confluent/confluent.info
cd /sys/class/net
touch /etc/confluent/confluent.info
confluentsrv=$(getarg confluent)
DIDDHCP=0
if [ ! -z "$confluentsrv" ]; then
for i in *; do
ip link set $i up
done
if [[ "$confluentsrv" = *":"* ]]; then
confluenthttpsrv=[$confluentsrv]
/usr/libexec/nm-initrd-generator ip=:dhcp6
else
confluenthttpsrv=$confluentsrv
ifname=$(ip -br link|grep LOWER_UP|grep -v UNKNOWN|head -n 1|awk '{print $1}')
echo -n "Attempting to use dhcp to bring up $ifname..."
dhclient $ifname
while ! ip -br addr show dev $ifname | grep \\. > /dev/null; do
echo -n "Still waiting for IPv4 address on: "
ip -br link show dev $ifname
sleep 1
done
echo -n "Complete: "
ip -br addr show dev $ifname
DIDDHCP=1
fi
myids=uuid=$(cat /sys/devices/virtual/dmi/id/product_uuid)
for mac in $(ip -br link|grep -v LOOPBACK|awk '{print $3}'); do
myids=$myids"/mac="$mac
done
myname=$(curl -sH "CONFLUENT_IDS: $myids" https://$confluenthttpsrv/confluent-api/self/whoami)
if [ ! -z "$myname" ]; then
echo NODENAME: $myname > /etc/confluent/confluent.info
echo MANAGER: $confluentsrv >> /etc/confluent/confluent.info
echo EXTMGRINFO: $confluentsrv'||1' >> /etc/confluent/confluent.info
fi
fi
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))
@@ -123,7 +159,7 @@ while [ $ready = "0" ]; do
confluent_mgr="[$confluent_mgr]"
fi
tmperr=$(mktemp)
curl -sSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg 2> $tmperr
curl -sSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg 2> $tmperr
if grep 401 $tmperr > /dev/null; then
confluent_apikey=""
if [ -n "$lasthdl" ]; then
@@ -147,13 +183,43 @@ if [ -c /dev/tpmrm0 ]; then
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
fi
umask $oldumask
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=
EOC
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
auto6configmethod=$(grep ^ipv6_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
if [ "$DIDDHCP" = "0" ]; then
echo -n "Attempting to use dhcp to bring up $ifname..."
dhclient $ifname
while ! ip -br addr show dev $ifname | grep \\. > /dev/null; do
echo -n "Still waiting for IPv4 address on: "
ip -br link show dev $ifname
sleep 1
done
echo -n "Complete: "
ip -br addr show dev $ifname
fi
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
elif [ "$autoconfigmethod" = "static" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg)
v4addr=${v4addr#ipv4_address: }
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg)
@@ -167,28 +233,8 @@ else
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
fi
echo '[ipv4]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo address1=$v4addr/$v4nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
if [ ! -z "$v4gw" ]; then
echo gateway=$v4gw >> /run/NetworkManager/system-connections/$ifname.nmconnection
@@ -197,7 +243,7 @@ EOC
nameservers=""
while read -r entry; do
if [ $nameserversec = 1 ]; then
if [[ $entry == "-"* ]]; then
if [[ $entry == "-"*.* ]]; then
nameservers="$nameservers"${entry#- }";"
continue
fi
@@ -220,16 +266,69 @@ method=manual
addr-gen-mode=eui64
method=auto
[proxy]
EOC
elif [ "$auto6configmethod" = "static" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg| awk '{print $2}')
v6addr=$(grep ^ipv6_address: /etc/confluent/confluent.deploycfg)
v6addr=${v6addr#ipv6_address: }
v6gw=$(grep ^ipv6_gateway: /etc/confluent/confluent.deploycfg)
v6gw=${v6gw#ipv6_gateway: }
if [ "$v6gw" = "null" ]; then
v6gw=""
fi
v6nm=$(grep ^ipv6_prefix: /etc/confluent/confluent.deploycfg)
v6nm=${v6nm#ipv6_prefix: }
echo "Setting up $ifname as static at $v6addr/$v6nm"
ip addr add dev $ifname $v6addr/$v6nm
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
[ipv4]
dhcp-timeout=90
dhcp-vendor-class-identifier=anaconda-Linux
method=disabled
[ipv6]
addr-gen-mode=eui64
method=manual
may-fail=false
EOC
echo address1=$v6addr/$v6nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
if [ ! -z "$v6gw" ]; then
ip route add default via $v6gw
echo gateway=$v6gw >> /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
fi
echo '[proxy]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
confluent_websrv=$confluent_mgr
if [[ $confluent_websrv == *:* ]]; then
confluent_websrv="[$confluent_websrv]"
fi
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 -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile
curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_websrv/confluent-api/self/sshcert > $certfile
if [ -s $certfile ]; then
echo HostCertificate $certfile >> /etc/ssh/sshd_config
fi
@@ -246,8 +345,7 @@ for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed
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 -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
curl -sf https://$confluent_websrv/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
. /etc/confluent/functions
source_remote imageboot.sh
@@ -0,0 +1,54 @@
try:
import configparser
except ImportError:
import ConfigParser as configparser
import cStringIO
import imp
import sys
apiclient = imp.load_source('apiclient', '/opt/confluent/bin/apiclient')
repo = None
server4 = None
server = None
server6 = None
profile = None
v4cfg = None
with open('/etc/confluent/confluent.deploycfg') as dplcfgfile:
lines = dplcfgfile.read().split('\n')
for line in lines:
if line.startswith('deploy_server:'):
_, server4 = line.split(' ', 1)
if line.startswith('deploy_server_v6:'):
_, server6 = line.split(' ', 1)
if line.startswith('profile: '):
_, profile = line.split(' ', 1)
if line.startswith('ipv4_method: '):
_, v4cfg = line.split(' ', 1)
if v4cfg == 'static' or v4cfg =='dhcp':
server = server4
if not server:
server = '[{}]'.format(server6)
path = '/confluent-public/os/{0}/distribution/'.format(profile)
clnt = apiclient.HTTPSClient()
cfgdata = clnt.grab_url(path + '.treeinfo').decode()
c = configparser.ConfigParser()
try:
c.read_string(cfgdata)
except AttributeError:
f = cStringIO.StringIO(cfgdata)
c.readfp(f)
for sec in c.sections():
if sec.startswith('variant-'):
try:
repopath = c.get(sec, 'repository')
except Exception:
continue
_, varname = sec.split('-', 1)
reponame = '/etc/yum.repos.d/local-{0}.repo'.format(varname.lower())
with open(reponame, 'w') as repout:
repout.write('[local-{0}]\n'.format(varname.lower()))
repout.write('name=Local install repository for {0}\n'.format(varname))
if repopath[0] == '.':
repopath = repopath[1:]
repout.write('baseurl=https://{}/confluent-public/os/{}/distribution/{}\n'.format(server, profile, repopath))
repout.write('enabled=1\n')
@@ -11,11 +11,10 @@ confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{pr
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
@@ -37,4 +36,5 @@ curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLU
systemctl disable firstboot
rm /etc/systemd/system/firstboot.service
rm /etc/confluent/firstboot.ran
kill $logshowpid
) &
tail --pid $! -F /var/log/confluent/confluent-firstboot.log > /dev/console
@@ -1,6 +1,10 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/confluent-api/ > /dev/null; then
return 0
fi
return 1
@@ -58,7 +62,11 @@ fetch_remote() {
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -165,7 +173,11 @@ run_remote_python() {
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
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -54,7 +54,7 @@ class DiskInfo(object):
@property
def priority(self):
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
@@ -1,10 +1,14 @@
. /lib/dracut-lib.sh
confluent_whost=$confluent_mgr
if [[ "$confluent_whost" == *:* ]]; then
confluent_whost="[$confluent_mgr]"
fi
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
curl https://$confluent_whost/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"
confluent_urls="$confluent_urls https://$confluent_whost/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
@@ -12,8 +16,8 @@ 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)"
while ! curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_whost/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/os/$confluent_profile/pending/rootimg.key)"
sleep 1
done
cipher=$(head -n 1 /tmp/rootimg.key)
@@ -103,15 +107,15 @@ 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
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_whost/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
curl -sf https://$confluent_whost/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
curl -sf https://$confluent_whost/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
@@ -123,4 +127,5 @@ if grep installtodisk /proc/cmdline > /dev/null; then
fi
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
exec /opt/confluent/bin/start_root
@@ -7,8 +7,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}')
v4meth=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
if [ "$v4meth" = "null" -o -z "$v4meth" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg|awk '{print $2}')
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
fi
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export nodename confluent_mgr confluent_profile
. /etc/confluent/functions
mkdir -p /var/log/confluent
@@ -19,6 +26,9 @@ chmod 600 /var/log/confluent/confluent-onboot.log
tail -f /var/log/confluent/confluent-onboot.log > /dev/console &
logshowpid=$!
rpm --import /etc/pki/rpm-gpg/*
run_remote_python add_local_repositories
run_remote_python syncfileclient
run_remote_python confignet
@@ -1,4 +1,5 @@
#!/usr/bin/python
#!/usr/bin/python3
import subprocess
import importlib
import tempfile
import json
@@ -211,7 +212,20 @@ def synchronize():
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
myips = []
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
for line in ipaddrs:
isa = line.split()
if len(isa) < 3 or isa[1] != b'UP':
continue
for addr in isa[2:]:
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
continue
addr = addr.split(b'/')[0]
if not isinstance(addr, str):
addr = addr.decode('utf8')
myips.append(addr)
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
@@ -19,4 +19,6 @@ mkdir -p /sysroot/etc/ssh/
for i in /ssh/*.ca; do
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
done
cp /opt/confluent/bin/apiclient /sysroot/etc/confluent
mkdir -p /sysroot/opt/confluent/bin
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
cp /opt/confluent/bin/apiclient /sysroot/etc/confluent/
@@ -1,5 +1,6 @@
#!/bin/sh
[ -e /tmp/confluent.initq ] && return 0
. /lib/dracut-lib.sh
udevadm trigger
udevadm trigger --type=devices --action=add
udevadm settle
@@ -38,17 +39,97 @@ umask 0077
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
echo 0 > /proc/sys/net/ipv6/conf/${currif}/autoconf
ip link set $currif up
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
TRIES=5
while [ ! -e /dev/disk ] && [ $TRIES -gt 0 ]; do
sleep 2
TRIES=$((TRIES - 1))
done
if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
tmnt=/tmp/idntmnt
mkdir -p $tmnt
tcfg=/tmp/idnttmp
mount /dev/disk/by-label/CNFLNT_IDNT $tmnt
cd $tmnt
deploysrvs=$(sed -n '/^deploy_servers:/, /^[^-]/p' cnflnt.yml |grep ^-|sed -e 's/^- //'|grep -v :)
nodename=$(grep ^nodename: cnflnt.yml|awk '{print $2}')
sed -n '/^net_cfgs:/, /^[^- ]/{/^[^- ]/!p}' cnflnt.yml |sed -n '/^-/, /^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "dhcp" ]; then
/usr/libexec/nm-initrd-generator ip=:dhcp
else
v4addr=$(grep ^ipv4_address: $tcfg)
v4addr=${v4addr#ipv4_address: }
v4addr=${v4addr%/*}
v4gw=$(grep ^ipv4_gateway: $tcfg)
v4gw=${v4gw#ipv4_gateway: }
if [ "$v4gw" = "null" ]; then
v4gw=""
fi
v4nm=$(grep ipv4_netmask: $tcfg)
v4nm=${v4nm#ipv4_netmask: }
/usr/libexec/nm-initrd-generator ip=$v4addr::$v4gw:$v4nm:$hostname::none
fi
NetworkManager --configure-and-quit=initrd --no-daemon
hmackeyfile=/tmp/cnflnthmackeytmp
echo -n $(grep ^apitoken: cnflnt.yml|awk '{print $2}') > $hmackeyfile
cd -
umount $tmnt
passfile=/tmp/cnflnttmppassfile
passcrypt=/tmp/cnflntcryptfile
hmacfile=/tmp/cnflnthmacfile
ln -s /opt/confluent/bin/clortho /opt/confluent/bin/genpasshmac
/opt/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
echo 'NODENAME: '$nodename > /etc/confluent/confluent.info
for deploysrv in $deploysrvs; do
echo 'MANAGER: '$deploysrv >> /etc/confluent/confluent.info
done
for deployer in $deploysrvs; do
if curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt -k https://$deployer/confluent-api/self/registerapikey; then
cp $passfile /etc/confluent/confluent.apikey
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg)
confluent_profile=${confluent_profile#profile: }
mgr=$deployer
break
fi
done
fi
cd /sys/class/net
if ! grep MANAGER: /etc/confluent/confluent.info; then
confluentsrv=$(getarg confluent)
if [ ! -z "$confluentsrv" ]; then
if [[ "$confluentsrv" = *":"* ]]; then
confluenthttpsrv=[$confluentsrv]
/usr/libexec/nm-initrd-generator ip=:dhcp6
else
confluenthttpsrv=$confluentsrv
/usr/libexec/nm-initrd-generator ip=:dhcp
fi
NetworkManager --configure-and-quit=initrd --no-daemon
myids=uuid=$(cat /sys/devices/virtual/dmi/id/product_uuid)
for mac in $(ip -br link|grep -v LOOPBACK|awk '{print $3}'); do
myids=$myids"/mac="$mac
done
myname=$(curl -sH "CONFLUENT_IDS: $myids" https://$confluenthttpsrv/confluent-api/self/whoami)
if [ ! -z "$myname" ]; then
echo NODENAME: $myname > /etc/confluent/confluent.info
echo MANAGER: $confluentsrv >> /etc/confluent/confluent.info
echo EXTMGRINFO: $confluentsrv'||1' >> /etc/confluent/confluent.info
fi
fi
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
done
grep ^EXTMGRINFO: /etc/confluent/confluent.info || return 0 # Do absolutely nothing if no data at all yet
fi
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
@@ -66,10 +147,15 @@ while ! confluentpython /opt/confluent/bin/apiclient $errout /confluent-api/self
sleep 10
done
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
ifname=${ifname%@*}
echo $ifname > /tmp/net.ifaces
if [ ! -z "$ifidx" ]; then
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
ifname=${ifname%@*}
echo $ifname > /tmp/net.ifaces
else
ip -br a|grep UP|awk '{print $1}' > /tmp/net.ifaces
ifname=$(cat /tmp/net.ifaces)
fi
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
dnsdomain=${dnsdomain#dnsdomain: }
@@ -84,7 +170,8 @@ v4cfg=${v4cfg#ipv4_method: }
if [ "$v4cfg" = "static" ] || [ "$v4cfg" = "dhcp" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server: }
else
fi
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server_v6: }
mgr="[$mgr]"
@@ -59,10 +59,16 @@ 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
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr="[$confluent_mgr]"
fi
fi
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/preinst.sh
@@ -77,10 +83,16 @@ 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
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr="[$confluent_mgr]"
fi
fi
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/postinst.sh
. /tmp/postinst.sh
@@ -94,6 +106,7 @@ chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh
%post
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
cp /etc/confluent/tls/*.pem /etc/pki/ca-trust/source/anchors
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
@@ -103,10 +116,16 @@ 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
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server_v6: }
confluent_mgr="[$confluent_mgr]"
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
else
confluent_mgr="[$confluent_mgr]"
fi
fi
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /tmp/postinst.sh
. /tmp/postinst.sh
@@ -3,19 +3,34 @@ try:
except ImportError:
import ConfigParser as configparser
import cStringIO
import imp
import importlib.util
import importlib.machinery
import sys
apiclient = imp.load_source('apiclient', '/etc/confluent/apiclient')
modloader = importlib.machinery.SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient')
modspec = importlib.util.spec_from_file_location('apiclient', '/opt/confluent/bin/apiclient', loader=modloader)
apiclient = importlib.util.module_from_spec(modspec)
modspec.loader.exec_module(apiclient)
repo = None
server = None
v4cfg = None
server4 = None
server6 = None
profile = None
with open('/etc/confluent/confluent.deploycfg') as dplcfgfile:
lines = dplcfgfile.read().split('\n')
for line in lines:
if line.startswith('deploy_server:'):
_, server = line.split(' ', 1)
_, server4 = line.split(' ', 1)
if line.startswith('deploy_server_v6:'):
_, server6 = line.split(' ', 1)
if line.startswith('profile: '):
_, profile = line.split(' ', 1)
if line.startswith('ipv4_method: '):
_, v4cfg = line.split(' ', 1)
if v4cfg == 'static' or v4cfg =='dhcp':
server = server4
if not server:
server = '[{}]'.format(server6)
path = '/confluent-public/os/{0}/distribution/'.format(profile)
clnt = apiclient.HTTPSClient()
@@ -13,20 +13,26 @@ 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
fi
if [ -z "$confluent_mgr" ]; then
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]"
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
confluent_mgr=${confluent_mgr#deploy_server: }
confluent_pingtarget=$confluent_mgr
else
confluent_pingtarget=$confluent_mgr
confluent_mgr="[$confluent_mgr]"
fi
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_pingtarget >& /dev/null; do
sleep 1
done
@@ -49,4 +55,5 @@ curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLU
systemctl disable firstboot
rm /etc/systemd/system/firstboot.service
rm /etc/confluent/firstboot.ran
kill $logshowpid
) &
tail --pid $! -n 0 -F /var/log/confluent/confluent-firstboot.log > /dev/console
@@ -1,6 +1,10 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/confluent-api/ > /dev/null; then
return 0
fi
return 1
@@ -58,7 +62,11 @@ fetch_remote() {
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -165,7 +173,11 @@ run_remote_python() {
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
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -54,7 +54,7 @@ class DiskInfo(object):
@property
def priority(self):
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
@@ -42,11 +42,20 @@ echo lang $locale > /tmp/langinfo
echo keyboard --vckeymap=$keymap >> /tmp/langinfo
tz=$(grep ^timezone: /etc/confluent/confluent.deploycfg)
tz=${tz#timezone: }
MVER=$(grep VERSION_ID /etc/os-release|cut -d = -f 2 |cut -d . -f 1|cut -d '"' -f 2)
ntpsrvs=""
if grep ^ntpservers: /etc/confluent/confluent.deploycfg > /dev/null; then
ntpsrvs="--ntpservers="$(sed -n '/^ntpservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|sed 1d|sed '$d' | sed -e 's/^- //' | paste -sd,)
if [ "$MVER" -ge 9 ]; then
if grep ^ntpservers: /etc/confluent/confluent.deploycfg > /dev/null; then
for ntpsrv in $(sed -n '/^ntpservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|sed 1d|sed '$d' | sed -e 's/^- //'); do
echo timesource --ntp-server $ntpsrv >> /tmp/timezone
done
fi
else
if grep ^ntpservers: /etc/confluent/confluent.deploycfg > /dev/null; then
ntpsrvs="--ntpservers="$(sed -n '/^ntpservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|sed 1d|sed '$d' | sed -e 's/^- //' | paste -sd,)
fi
fi
echo timezone $ntpsrvs $tz --utc > /tmp/timezone
echo timezone $ntpsrvs $tz --utc >> /tmp/timezone
rootpw=$(grep ^rootpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ "$rootpw" = null ]; then
echo "rootpw --lock" > /tmp/rootpw
@@ -68,6 +77,7 @@ if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
ssh-keygen -A
rm /etc/ssh/ssh_host_dsa_key*
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$confluent_mgr/confluent-api/self/sshcert > $certfile
@@ -88,7 +98,7 @@ fi
export confluent_mgr confluent_profile nodename
curl -sf https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart.custom -o /tmp/kickstart.custom
confluentpython /opt/confluent/bin/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
@@ -10,6 +10,8 @@
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
export confluent_mgr confluent_profile nodename
cp -a /etc/confluent /mnt/sysimage/etc
mkdir -p /mnt/sysimage/opt/confluent/bin
cp /opt/confluent/bin/apiclient /mnt/sysimage/opt/confluent/bin/
chmod -R og-rwx /mnt/sysimage/etc/confluent
cp /tmp/functions /mnt/sysimage/etc/confluent/
hostnamectl set-hostname $nodename
@@ -1,4 +1,5 @@
#!/usr/bin/python
#!/usr/bin/python3
import subprocess
import importlib
import tempfile
import json
@@ -211,7 +212,20 @@ def synchronize():
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
myips = []
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
for line in ipaddrs:
isa = line.split()
if len(isa) < 3 or isa[1] != b'UP':
continue
for addr in isa[2:]:
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
continue
addr = addr.split(b'/')[0]
if not isinstance(addr, str):
addr = addr.decode('utf8')
myips.append(addr)
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
@@ -0,0 +1,309 @@
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..."
if [ ! -z "$autoconsdev" ]; then echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..." > $autoconsdev; fi
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
}
root=1
rootok=1
netroot=confluent
echo -ne '\033[H\033[2J\033[3J'
mkdir -p /etc/ssh
mkdir -p /var/tmp/
mkdir -p /var/empty/sshd
mkdir -p /usr/share/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)
autoconsdev=${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 ]; 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]"
fi
tmperr=$(mktemp)
curl -sSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg2 > /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%]}
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
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
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=
EOC
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
auto6configmethod=$(grep ^ipv6_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
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
elif [ "$autoconfigmethod" = "static" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
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
echo '[ipv4]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
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
EOC
elif [ "$auto6configmethod" = "static" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg| awk '{print $2}')
v6addr=$(grep ^ipv6_address: /etc/confluent/confluent.deploycfg)
v6addr=${v6addr#ipv6_address: }
v6gw=$(grep ^ipv6_gateway: /etc/confluent/confluent.deploycfg)
v6gw=${v6gw#ipv6_gateway: }
if [ "$v6gw" = "null" ]; then
v6gw=""
fi
v6nm=$(grep ^ipv6_prefix: /etc/confluent/confluent.deploycfg)
v6nm=${v6nm#ipv6_prefix: }
echo "Setting up $ifname as static at $v6addr/$v6nm"
ip addr add dev $ifname $v6addr/$v6nm
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
[ipv4]
dhcp-timeout=90
dhcp-vendor-class-identifier=anaconda-Linux
method=disabled
[ipv6]
addr-gen-mode=eui64
method=manual
may-fail=false
EOC
echo address1=$v6addr/$v6nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
if [ ! -z "$v6gw" ]; then
ip route add default via $v6gw
echo gateway=$v6gw >> /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
fi
echo '[proxy]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
confluent_websrv=$confluent_mgr
if [[ $confluent_websrv == *:* ]]; then
confluent_websrv="[$confluent_websrv]"
fi
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 -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_websrv/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
mkdir -p /etc/confluent
curl -sf https://$confluent_websrv/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
. /etc/confluent/functions
source_remote imageboot.sh
@@ -0,0 +1,58 @@
try:
import configparser
except ImportError:
import ConfigParser as configparser
import cStringIO
import importlib.util
import importlib.machinery
import sys
modloader = importlib.machinery.SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient')
modspec = importlib.util.spec_from_file_location('apiclient', '/opt/confluent/bin/apiclient', loader=modloader)
apiclient = importlib.util.module_from_spec(modspec)
modspec.loader.exec_module(apiclient)
repo = None
server = None
v4cfg = None
server4 = None
server6 = None
profile = None
with open('/etc/confluent/confluent.deploycfg') as dplcfgfile:
lines = dplcfgfile.read().split('\n')
for line in lines:
if line.startswith('deploy_server:'):
_, server4 = line.split(' ', 1)
if line.startswith('deploy_server_v6:'):
_, server6 = line.split(' ', 1)
if line.startswith('profile: '):
_, profile = line.split(' ', 1)
if line.startswith('ipv4_method: '):
_, v4cfg = line.split(' ', 1)
if v4cfg == 'static' or v4cfg =='dhcp':
server = server4
if not server:
server = '[{}]'.format(server6)
path = '/confluent-public/os/{0}/distribution/'.format(profile)
clnt = apiclient.HTTPSClient()
cfgdata = clnt.grab_url(path + '.treeinfo').decode()
c = configparser.ConfigParser()
try:
c.read_string(cfgdata)
except AttributeError:
f = cStringIO.StringIO(cfgdata)
c.readfp(f)
for sec in c.sections():
if sec.startswith('variant-'):
try:
repopath = c.get(sec, 'repository')
except Exception:
continue
_, varname = sec.split('-', 1)
reponame = '/etc/yum.repos.d/local-{0}.repo'.format(varname.lower())
with open(reponame, 'w') as repout:
repout.write('[local-{0}]\n'.format(varname.lower()))
repout.write('name=Local install repository for {0}\n'.format(varname))
if repopath[0] == '.':
repopath = repopath[1:]
repout.write('baseurl=https://{}/confluent-public/os/{}/distribution/{}\n'.format(server, profile, repopath))
repout.write('enabled=1\n')
@@ -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
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
) &
tail --pid $! -F /var/log/confluent/confluent-firstboot.log > /dev/console
@@ -0,0 +1,208 @@
#!/bin/bash
function test_mgr() {
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/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)
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/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)
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/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 ('m.2 nvme 2-bay raid kit', '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,131 @@
. /lib/dracut-lib.sh
confluent_whost=$confluent_mgr
if [[ "$confluent_whost" == *:* ]]; then
confluent_whost="[$confluent_mgr]"
fi
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
if [ "untethered" = "$(getarg confluent_imagemethod)" ]; then
mount -t tmpfs untethered /mnt/remoteimg
curl https://$confluent_whost/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs
else
confluent_urls="$confluent_urls https://$confluent_whost/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: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_whost/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/os/$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_whost/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_whost/confluent-public/os/$confluent_profile/scripts/onboot.service > /sysroot/etc/systemd/system/onboot.service
mkdir -p /sysroot/opt/confluent/bin
curl -sf https://$confluent_whost/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/
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
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,43 @@
#!/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)
v4meth=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
if [ "$v4meth" = "null" -o -z "$v4meth" ]; then
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg|awk '{print $2}')
fi
if [ -z "$confluent_mgr" ]; then
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
fi
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
timedatectl set-timezone $(grep ^timezone: /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=$!
rpm --import /etc/pki/rpm-gpg/*
run_remote_python add_local_repositories
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,286 @@
#!/usr/bin/python3
import subprocess
import importlib
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()
except FileNotFoundError:
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger(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()
myips = []
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
for line in ipaddrs:
isa = line.split()
if len(isa) < 3 or isa[1] != b'UP':
continue
for addr in isa[2:]:
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
continue
addr = addr.split(b'/')[0]
if not isinstance(addr, str):
addr = addr.decode('utf8')
myips.append(addr)
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
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
@@ -1,13 +1,82 @@
#!/bin/sh
# copernicus is hard coded, easier to script a fake sysfs
if [ ! -f /var/run/vmware/show-esx-shell-login ]; then
chvt 1
sleep 1
chvt 2
/etc/init.d/ESXShell start
fi
uuid=$(vsish -e get /hardware/bios/dmiInfo|grep -A15 UUID|sed -e 's/.*://'|sed -e ':a;N;$!ba;s/\n//g' | sed -e 's/ *0x//g')
uuid=${uuid:0:8}-${uuid:8:4}-${uuid:12:4}-${uuid:16:4}-${uuid:20:12}
kargs=$(vsish -e get /system/bootCmdLine|grep "command line:")
vlanid=""
for karg in $kargs; do
if [ ${karg%=*} = "vlanid" ]; then
vlanid=${karg/*=}
fi
done
if [ ! -z "$vlanid" ]; then
echo $vlanid > /tmp/confluent.vlanid
localcli network vswitch standard portgroup set -p "Management Network" -v $vlanid
fi
mkdir -p /sys/devices/virtual/dmi/id/
echo $uuid > /sys/devices/virtual/dmi/id/product_uuid
mkdir -p /etc/confluent
localcli network firewall unload
touch /etc/confluent/confluent.info
begin=$(date +%s)
localcli system secpolicy domain set -n appDom -l disabled
personality=''
for usb in /dev/disks/mpx.vmhba*; do
if mdir -i $usb |grep CNFLNT_IDNT > /dev/null; then
personality=$usb
break
fi
done
if [ ! -z "$personality" ]; then
mkdir -p /tmp/confluentident
mcopy -i $personality ::* /tmp/confluentident/
fi
touch /etc/confluent/confluent.apikey
if [ -e /tmp/confluentident/cnflnt.yml ]; then
cat /tls/*.pem >> /etc/confluent/ca.pem
deploysrvs=$(sed -n '/^deploy_servers:/,/^[^-]/p' /tmp/confluentident/cnflnt.yml |grep ^-|sed -e 's/^- //'|grep -v :)
nodename=$(grep ^nodename: /tmp/confluentident/cnflnt.yml|sed -e 's/nodename: //')
echo 'NODENAME: '$nodename > /etc/confluent/confluent.info
for deploysrv in $deploysrvs; do
echo 'MANAGER: '$deploysrv >> /etc/confluent/confluent.info
done
tcfg=$(mktemp)
sed -n '/^net_cfgs:/,/^[^- ]/{/^[^- ]/!p}' /tmp/confluentident/cnflnt.yml |sed -n '/^-/,/^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "static" ]; then
v4addr=$(grep ^ipv4_address: $tcfg)
v4addr=${v4addr#ipv4_address: }
v4addr=${v4addr%/*}
v4gw=$(grep ^ipv4_gateway: $tcfg)
v4gw=${v4gw#ipv4_gateway: }
if [ "$v4gw" = "null" ]; then
v4gw=""
fi
v4nm=$(grep ipv4_netmask: $tcfg)
v4nm=${v4nm#ipv4_netmask: }
localcli network ip interface ipv4 set -i vmk0 -I $v4addr -N $v4nm -g $v4gw -t static
localcli network ip route ipv4 add -n default -g $v4gw
fi
hmackeyfile=$(mktemp)
echo -n $(grep ^apitoken: /tmp/confluentident/cnflnt.yml|awk '{print $2}') > $hmackeyfile
cd -
passfile=$(mktemp)
passcrypt=$(mktemp)
hmacfile=$(mktemp)
ln -s /opt/confluent/bin/clortho /opt/confluent/bin/genpasshmac
/opt/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
echo -n 'Registering new API key with deployment server: '
/opt/confluent/bin/apiclient -p $hmacfile /confluent-api/self/registerapikey $passcrypt
echo
cp $passfile /etc/confluent/confluent.apikey
fi
while ! grep NODENAME /etc/confluent/confluent.info; do
echo "Searching for deployment service on local network..."
/opt/confluent/bin/copernicus > /etc/confluent/confluent.info
@@ -25,20 +94,18 @@ while ! grep NODENAME /etc/confluent/confluent.info; do
fi
done
node=$(grep NODENAME: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
mgr=$(grep MANAGER: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
cp /opt/confluent/bin/clortho /clortho
/clortho $node $mgr > /etc/confluent/confluent.apikey
mgr=$(python /opt/confluent/bin/apiclient -c)
APIKEY=$(cat /etc/confluent/confluent.apikey)
if [ -z "$APIKEY" ]; then
cp /opt/confluent/bin/clortho /clortho
(/clortho $node $mgr || /opt/confluent/bin/clortho $node $mgr) > /etc/confluent/confluent.apikey
APIKEY=$(cat /etc/confluent/confluent.apikey)
fi
while [ -z "$APIKEY" ]; do
if [ ! -f /var/run/vmware/show-esx-shell-login ]; then
chvt 1
sleep 1
chvt 2
/etc/init.d/ESXShell start
fi
echo "Deployment server failed to grant node deployment token, retrying token retrieval"
echo "A debug session has been made available on Alt-F1"
/clortho $node $mgr > /etc/confluent/confluent.apikey
(/clortho $node $mgr || /opt/confluent/bin/clortho $node $mgr) > /etc/confluent/confluent.apikey
APIKEY=$(cat /etc/confluent/confluent.apikey)
done
cat /tls/*.pem > /etc/confluent/ca.pem
@@ -1,7 +1,7 @@
#!/usr/bin/python
import re
import subprocess
import json
uplinkmatch = re.compile('^\s*Uplinks:\s*(.*)')
nodename = None
for inf in open('/etc/confluent/confluent.info', 'r').read().split('\n'):
@@ -33,6 +33,16 @@ for info in vswinfo.split('\n'):
upinfo = uplinkmatch.match(info)
if upinfo:
vmnic = upinfo.group(1)
try:
with open('/tmp/confluentident/cnflnt.jsn') as identin:
identcfg = json.load(identin)
ncfg = identcfg['net_cfgs'][0]
cfg['ipv4_method'] = ncfg['ipv4_method']
cfg['ipv4_address'] = ncfg['ipv4_address'].split('/')[0]
cfg['ipv4_netmask'] = ncfg['ipv4_netmask']
cfg['ipv4_gateway'] = ncfg['ipv4_gateway']
except Exception:
pass
netline = 'network --hostname={0} --bootproto={1}'.format(nodename, cfg['ipv4_method'])
if vmnic:
netline += ' --device={0}'.format(vmnic)
@@ -42,5 +52,12 @@ if cfg['ipv4_method'] == 'static':
netline += ' --gateway={0}'.format(cfg['ipv4_gateway'])
if cfg['nameservers']:
netline += ' --nameserver={0}'.format(cfg['nameservers'])
vlanid = None
try:
with open('/tmp/confluent.vlanid', 'r') as vlanin:
vlanid = vlanin.read()
except Exception:
pass
if vlanid:
netline += ' --vlanid={0}'.format(vlanid)
print(netline)
@@ -43,6 +43,20 @@ mkdir -p /etc/pki/tls/certs
cat /tls/*.pem > /etc/pki/tls/certs/ca-bundle.crt
TRIES=0
touch /etc/confluent/confluent.info
if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
mkdir -p /media/ident
mount /dev/disk/by-label/CNFLNT_IDNT /media/ident
if [ -e /media/ident/genesis_bootstrap.sh ]; then
exec sh /media/ident/genesis_bootstrap.sh
fi
fi
if [ -e /dev/disk/by-label/GENESIS-X86 ]; then
mkdir -p /media/genesis
mount /dev/disk/by-label/GENESIS-X86 /media/genesis
if [ -e /media/genesis/genesis_bootstrap.sh ]; then
exec sh /media/genesis/genesis_bootstrap.sh
fi
fi
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
@@ -141,6 +155,23 @@ elif [ "$autoconfigmethod" = "static" ]; then
ip route add default via $v4gw
fi
fi
nameserversec=0
while read -r entry; do
if [ $nameserversec = 1 ]; then
if [[ $entry == "-"* ]] && [[ $entry != "- ''" ]]; then
echo nameserver ${entry#- } >> /etc/resolv.conf
continue
fi
fi
nameserversec=0
if [ "${entry%:*}" = "nameservers" ]; then
nameserversec=1
continue
fi
done < /etc/confluent/confluent.deploycfg
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
dnsdomain=${dnsdomain#dnsdomain: }
echo search $dnsdomain >> /etc/resolv.conf
echo -n "Initializing ssh..."
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host*key.pub; do
@@ -155,8 +186,8 @@ done
/usr/sbin/sshd
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | awk '{print $2}')
export confluent_profile
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/scripts/functions > /tmp/functions
. /tmp/functions
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
. /etc/confluent/functions
set_confluent_vars
export confluent_mgr
echo "Running https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/onboot.sh"
@@ -1,10 +1,4 @@
#!/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
@@ -22,29 +16,9 @@ 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
confluent_mgr=$(confluentpython /opt/confluent/bin/apiclient -c)
if [[ "$confluent_mgr" = *":"* ]]; then
confluent_mgr="[$confluent_mgr]"
fi
if [ -z "$confluent_profile" ]; then
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
@@ -52,13 +26,13 @@ function set_confluent_vars() {
}
fetch_remote() {
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
apiclient=/opt/confluent/bin/apiclient
if [ -f /etc/confluent/apiclient ]; then
apiclient=/etc/confluent/apiclient
fi
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
confluentpython $apiclient /confluent-public/os/$confluent_profile/scripts/$1 -o $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -156,16 +130,13 @@ run_remote() {
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
confluentpython /opt/confluent/bin/apiclient /confluent-public/os/$confluent_profile/scripts/$1 -o $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -1,5 +1,5 @@
#!/bin/sh
. /tmp/functions
. /etc/confluent/functions
# This runs whenever this genesis profile boots for customization
# purposes
@@ -1,4 +1,5 @@
#!/usr/bin/python
#!/usr/bin/python3
import subprocess
import importlib
import tempfile
import json
@@ -211,7 +212,20 @@ def synchronize():
appendoncedir = tempfile.mkdtemp()
try:
ac = apiclient.HTTPSClient()
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir})
myips = []
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
for line in ipaddrs:
isa = line.split()
if len(isa) < 3 or isa[1] != b'UP':
continue
for addr in isa[2:]:
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
continue
addr = addr.split(b'/')[0]
if not isinstance(addr, str):
addr = addr.decode('utf8')
myips.append(addr)
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
if status == 202:
lastrsp = ''
@@ -46,10 +46,15 @@ class DiskInfo(object):
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'):
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
return 0
if 'imsm' in self.mdcontainer:
return 1
@@ -1,6 +1,10 @@
#!/bin/bash
function test_mgr() {
if curl -s https://${1}/confluent-api/ > /dev/null; then
whost=$1
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
if curl -s https://${whost}/confluent-api/ > /dev/null; then
return 0
fi
return 1
@@ -58,7 +62,11 @@ fetch_remote() {
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/$1 > $1
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
@@ -165,7 +173,11 @@ run_remote_python() {
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
whost=$confluent_mgr
if [[ "$whost" == *:* ]]; then
whost="[$whost]"
fi
curl -f -sS $curlargs https://$whost/confluent-public/os/$confluent_profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
confluentpython $*
retcode=$?
@@ -9,6 +9,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_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export nodename confluent_mgr confluent_profile
. /etc/confluent/functions
mkdir -p /var/log/confluent
@@ -0,0 +1,286 @@
#!/usr/bin/python3
import subprocess
import importlib
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()
except FileNotFoundError:
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
def partitionhostsline(line):
comment = ''
try:
cmdidx = line.index('#')
comment = line[cmdidx:]
line = line[:cmdidx].strip()
except ValueError:
pass
if not line:
return '', [], comment
ipaddr, names = line.split(maxsplit=1)
names = names.split()
return ipaddr, names, comment
class HostMerger(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()
myips = []
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
for line in ipaddrs:
isa = line.split()
if len(isa) < 3 or isa[1] != b'UP':
continue
for addr in isa[2:]:
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
continue
addr = addr.split(b'/')[0]
if not isinstance(addr, str):
addr = addr.decode('utf8')
myips.append(addr)
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
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
@@ -81,6 +81,7 @@ fi
tz=$(grep timezone: /etc/confluent/confluent.deploycfg | awk '{print $2}')
echo "<timezone>${tz}</timezone>" > /tmp/timezone
echo "${tz}" > /tmp/timezone.txt
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "dhcp" ]; then
@@ -119,6 +120,9 @@ proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
proto=${proto#protocol: }
append=$(grep ^installedargs: /tmp/profile.yaml | sed -e 's/^installedargs: //' -e 's/#.*//')
if grep console= /etc/fakecmdline >& /dev/null && [[ "$append" != *console=* ]]; then
append="$append console=${autocons#*/dev/}"
fi
if [ -z "$append" ]; then
echo "<bootloader/>" > /tmp/bootloader.xml
else
@@ -0,0 +1,132 @@
<?xml version="1.0"?>
<!DOCTYPE profile>
<!--
This autoyast file will be processed by pre.sh before applying. See pre.sh for
info on modifying its behavior, and also search and replace '%%' to remove
dynamic behavior and replace with static configuration.
-->
<profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
<timezone>
<hwclock>UTC</hwclock>
<timezone>%%TIMEZONE%%</timezone>
</timezone>
<general>
<self_update config:type="boolean">false</self_update>
<mode>
<confirm config:type="boolean">false</confirm>
</mode>
</general>
<!--INSERT:/tmp/bootloader.xml-->
<software>
<patterns config:type="list">
<pattern>base</pattern>
</patterns>
<packages config:type="list">
<package>openssh</package>
<package>iputils</package>
<package>python3</package>
<package>openssl</package>
<package>chrony</package>
<package>rsync</package>
<package>screen</package>
<package>vim</package>
<package>binutils</package>
<package>pciutils</package>
<package>usbutils</package>
<package>nfs-client</package>
<package>ethtool</package>
</packages>
</software>
<partitioning config:type="list">
<drive>
<device>%%INSTDISK%%</device>
<initialize config:type="boolean">true</initialize>
<use>all</use>
<partitions config:type="list">
<partition>
<filesystem config:type="symbol">xfs</filesystem>
<mount>/</mount>
<size>max</size>
</partition>
<partition>
<mount>swap</mount>
<size>auto</size>
</partition>
<partition>
<mount>/boot</mount>
<size>500M</size>
</partition>
</partitions>
</drive>
</partitioning>
<users config:type="list">
<user>
<username>root</username>
<user_password>%%ROOTPASSWORD%%</user_password>
<encrypted config:type="boolean">true</encrypted>
<!--INSERT:/tmp/rootkeys.xml-->
</user>
</users>
<networking>
<dns>
<hostname>%%NODENAME%%</hostname>
</dns>
<keep_install_network config:type="boolean">true</keep_install_network>
</networking>
<services-manager>
<services>
<enable config:type="list">
<service>sshd</service>
</enable>
</services>
</services-manager>
<scripts>
<pre-scripts config:type="list">
<script>
<filename>preinstall.sh</filename>
<source>
<![CDATA[
#!/bin/sh
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/pre.sh > /tmp/pre.sh
. /tmp/pre.sh
]]>
</source>
</script>
</pre-scripts>
<chroot-scripts config:type="list">
<script>
<filename>chroot.sh</filename>
<source>
<![CDATA[
#!/bin/sh
confluent_mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^profile: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/prechroot.sh > /tmp/prechroot.sh
. /tmp/prechroot.sh
curl -f $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /mnt/etc/confluent/firstboot.sh
curl -f $proto://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/post.sh > /mnt/etc/confluent/post.sh
chmod +x /mnt/etc/confluent/firstboot.sh
chmod +x /mnt/etc/confluent/post.sh
cp /mnt/etc/confluent/post.sh /mnt/var/adm/autoinstall/scripts/
]]>
</source>
</script>
<script>
<chrooted config:type="boolean">true</chrooted>
<filename>post.sh</filename>
<location>file:///etc/confluent/post.sh</location>
</script>
</chroot-scripts>
<init-scripts config:type="list">
<script>
<filename>firstboot.sh</filename>
<location>file:///etc/confluent/firstboot.sh</location>
</script>
</init-scripts>
</scripts>
</profile>
@@ -8,7 +8,7 @@ dynamic behavior and replace with static configuration.
<profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
<timezone>
<hwclock>UTC</hwclock>
<!--INSERT:/tmp/timezone-->
<timezone>%%TIMEZONE%%</timezone>
</timezone>
<general>
<self_update config:type="boolean">false</self_update>
@@ -16,7 +16,6 @@ dynamic behavior and replace with static configuration.
<confirm config:type="boolean">false</confirm>
</mode>
</general>
%%IFSLE%%
<add-on>
<add_on_products config:type="list">
<listentry><media_url/><!--INSERT:/tmp/pkgurl--><product>sle-module-basesystem</product><product_dir>/Module-Basesystem</product_dir></listentry>
@@ -27,14 +26,11 @@ dynamic behavior and replace with static configuration.
<listentry><media_url/><!--INSERT:/tmp/pkgurl--><product>Legacy-Module</product><product_dir>/Module-Legacy</product_dir></listentry>
</add_on_products>
</add-on>
%%ENDIFSLE%%
<!--INSERT:/tmp/bootloader.xml-->
<software>
%%IFSLE%%
<products config:type="list">
<product>SLE_HPC</product>
</products>
%%ENDIFSLE%%
<patterns config:type="list">
<pattern>base</pattern>
</patterns>

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