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

Compare commits

...

140 Commits

Author SHA1 Message Date
Jarrod Johnson cafbc1d1c2 Have tty2 also run a shell
Some may find switching VTs to be more intuitive
than tmux session management. Provide one extra
on tty2 for such a scenario.
2021-03-13 12:50:44 -05:00
Jarrod Johnson 797e197bc7 Have tmux keep reattaching
If someone accidentally detaches, then intervene and
reattach.
2021-03-13 12:50:37 -05:00
Jarrod Johnson 19d4a3a83f Have autocons attempt sizing of serial console
If a terminal is open during autocons, that terminal
will be the size of the console.

Otherwise, fallback to 100x31.
2021-03-13 12:50:21 -05:00
Jarrod Johnson 874947076d Fix local console behavior in genesis
This will have the vga console be less concerning.
2021-03-12 13:55:33 -05:00
Jarrod Johnson ffe0754dd9 Fix typo in confluent profile.yaml
The quiet argument was missed due to typo in parameter name.
2021-03-12 12:44:19 -05:00
Jarrod Johnson bbb8e50c3e Fix configbmc gateway check
It erroneously linked to prefix to check gateway or not.
2021-03-11 16:28:01 -05:00
Jarrod Johnson 4f246e6a41 Fix compatibility with some switch configurations
While some implementations mess up portid and need portdescr instead, others are
just the opposite.

Tolerate match either by description or name.
2021-03-10 13:41:59 -05:00
Jarrod Johnson c53e758170 Workaround non-cisco switch crash
Querying Cisco MIB on certain
firmware levels of non-cisco switches
causes a crash.  Tolerate and
wait a bit to give SNMP a chance to restart.
2021-03-10 13:41:52 -05:00
Jarrod Johnson 1a7c238b54 Handle malformed json data more gracefully 2021-03-10 13:41:45 -05:00
Jarrod Johnson 94a4a7e20b Fix missing exit code in nomededia
nodemedia was not setting return code on exit properly.
2021-03-08 08:01:30 -05:00
Jarrod Johnson c9157b90eb Close other places that may be false negative
Have checks for neightable be preceeded by an attempt to refresh,
to mitigate false negatives.
2021-03-05 13:15:37 -05:00
Jarrod Johnson 5d0423c38b Attempt refresh of neigh table on miss
When an address is new it may not be in the last
captured neighbor table. Induce refresh before deciding
that neighbor is unavailable.
2021-03-05 13:09:54 -05:00
Jarrod Johnson e583d34555 Fix nic index map with bonding
The assumption that /sys/class/net is interfaces is incorrect,
when encountering entries that are not interfaces, do not
mess up the call.
2021-03-04 10:49:23 -05:00
Jarrod Johnson 95466392f9 Fix typo in confluentdbutil
The restore function would fail
to chown directories due to typo
2021-03-01 10:32:13 -05:00
Jarrod Johnson b3857f8d33 Fix chained discovery of DWC SMMv2
SMMv2 for DWC has more ports. Make the code
not care about which port is which for checking
for matching smm fingerprints.
2021-03-01 10:32:05 -05:00
Jarrod Johnson 7eb06f2722 Improve concurrency of SLP
During a scan, unicast TCP
interrogation of candidates
was done serially. Do this
concurrently so that poorly
behaving targets do not prolong
a scan.
2021-02-26 13:31:07 -05:00
Jarrod Johnson 8cf264602d Update status at end of esxi install 2021-02-05 14:02:28 -05:00
Jarrod Johnson a2f5b11185 Add Usb to nodesetboot
Particularly for redfish, this is a more specific target
2021-02-01 08:43:47 -05:00
Jarrod Johnson a9e39eab96 Fix path mistake in mofed.post script
mofed.post had incorrect path to mofed.tgz after
fetch. Correct the path mistake.
2021-01-28 16:50:50 -05:00
Jarrod Johnson d29d2bf683 Implement workaround for install over infiniband
8.3 implemented networkmanager to manage the install time
networking, but didn't correctly generate infiniband
network manager configuration.

Workaround by checking for infiniband install, and
then checking if the configuration is wrong, and
adding an infiniband section if missing.
2021-01-28 13:49:10 -05:00
Jarrod Johnson 6458eac93b Tolerate multiple forms of 'stringy'
The fromstring needs to accept either, so
amend it to do so.
2021-01-22 12:43:15 -05:00
Jarrod Johnson 8df15b3a54 Tolerate different SR635/SR655 response
Some SR635/SR655 behave differently.  Adjust by adapting as possible,
but never making an assumption about data being present.
2021-01-22 10:30:19 -05:00
Jarrod Johnson b4f9bb78ae Address irrelavent call to yaml.load 2021-01-21 17:48:56 -05:00
Jarrod Johnson c8e1efecdb Mitigate XML parse risks
The intended xml never has !entity tags and
thus we can reject any such XML outright and
avoid billion laughs and similar abuses.
2021-01-21 17:46:21 -05:00
Jarrod Johnson 22dc852277 Remove now redundent errors
printerror now searches for deeper errors,
no longer need to call it as much.
2021-01-21 16:38:35 -05:00
Jarrod Johnson 784ac5ecba Fix gitignore to track ronn files 2021-01-21 11:52:22 -05:00
Jarrod Johnson 66c9777b3c Add man page for stats command 2021-01-21 11:36:08 -05:00
Jarrod Johnson dba4c40f0e Fix collective join with empty config
Collective join without a key set would fail on
first try.
2021-01-21 11:19:13 -05:00
Jarrod Johnson 6997508a0c Fallback to forced utf8 on unicodeerror
If LC_ALL is set to C, then unicode can be a problem.
2021-01-21 08:41:35 -05:00
Jarrod Johnson 203253e05f Fix mispelling of exception name in confetty 2021-01-20 11:19:24 -05:00
Jarrod Johnson edc4804146 Discover larger SMMv2 based chassis
SMMv2 can support 12 servers, so increase the limit.
2021-01-20 09:08:56 -05:00
Jarrod Johnson 7cfdf11bf2 Fix collective name return
It inadvertently would return None when
rereading from file.
2021-01-19 17:34:28 -05:00
Jarrod Johnson a3bd21d605 Cleanup confetty/nodeconsole exit
Unexpected exit from nodeconsole/confetty is now
handled better for feedback and terminal usefulness
2021-01-19 16:24:36 -05:00
Jarrod Johnson 6d8474a16a Fix node errors being swalled by print_error
Node specific errors were not processed, correct
that oversight.
2021-01-19 12:16:22 -05:00
Jarrod Johnson 5736c41daa Add more data to discovery
Some applications may
want to source more information
from systems to help
identify things, particularly with
partially preconfigured systems.
2021-01-15 10:34:43 -05:00
Jarrod Johnson a5c4b64c60 Have Genesis better work with BMC install
In a BMC install, the certificate may fail and we may
have ability to port forward 3389 but not 22.

Support normal and enhanced certificate behavior
when possible, but degrade to cert-less ssh and
also port 3389
2021-01-15 08:25:12 -05:00
Jarrod Johnson f7a940227d Move sixel under opportunistic import
Do not require sixel
to run stats.
2021-01-14 15:49:30 -05:00
Jarrod Johnson ebf50359f0 Fix file descriptor leak by web forwarder
It failed to close the two sockets when a socket was
done.
2021-01-13 16:50:17 -05:00
Jarrod Johnson 5160023cc4 Update nodedeploy error message
We can't tell which argument was omitted,
so warn that both are needed.
2021-01-13 16:43:41 -05:00
Jarrod Johnson a738b761b4 Fix XCC discovery with Whitley changes 2021-01-12 11:47:02 -05:00
Jarrod Johnson d27ef81e32 Fix PXE handling of candidate managers 2021-01-11 13:33:26 -05:00
Jarrod Johnson f5344fabaa Correct typo in new attribute text 2021-01-11 13:13:03 -05:00
Jarrod Johnson fa1c2f5c1e Only offer deployment if a candidate manager
If candidate managers are defined, and this node is not in
that set, ignore PXE and SSDP requests to opt out of
deployment.
2021-01-08 16:32:41 -05:00
Jarrod Johnson 25c3f40559 Cache manager name
Since the get_myname() may be called much much more
frequently now that it is in the deployment flow,
have it cache results to save a lot of disk I/O
2021-01-08 16:30:51 -05:00
Jarrod Johnson 5812a0eef6 Have a rebalance shortly after becoming leader
This will handle startup and takeover when the current leader dies.
2021-01-08 16:15:11 -05:00
Jarrod Johnson 086ce9823b First phase of collective manager candidate implementation
This implements recovery on loss of collective member to
the least loaded candidates for the node.
2021-01-08 16:00:24 -05:00
Jarrod Johnson 2d6bdffebe Finalize the ssh.trustnodes facility
This is the confluent approach to handling the same
problem that xCAT SSH Zones do.
2021-01-08 14:05:37 -05:00
Jarrod Johnson efdbeeae0d Fix SNMPv2 on non-cisco switches
The cisco change was causing problems elsewhere.
2021-01-08 09:16:40 -05:00
Jarrod Johnson a2a1142f18 Draft implementation of ssh trust segmentation
Have equiv optionally be restricted to a subset of nodes
so that node to node ssh may be enabled within subsets
without enabling across the board.

This is akin to 'zones' in xCAT, albeit a bit more flexible
and covering both users and administrative access.
2021-01-06 11:52:43 -05:00
Jarrod Johnson 8c89deaa95 Further defer use of deploycfg as normal
In ESXi flow, it is highly likely that IPv4 cannot
be ready yet, delay a bit more.
2021-01-05 15:32:46 -05:00
Jarrod Johnson 1ec5231ebe Do not reply to mismatched IP in confluent search
If an OS queries for confluent, but will not have a viable address,
avoid replying to let more usable network paths prevail.

For example, one OS was coming up with 169.254 with no dhcp server,
and being told it could do well to talk to 172.29, which obviously
would not work.
2021-01-05 15:17:23 -05:00
Jarrod Johnson 674e2887f3 Fix ESXi deployment without working DHCP
apiclient was instructing itself to use IPv4 prematurely. Change
the dcuiweasel hook to delay that change until after all data has been
fetched.
2021-01-05 11:55:30 -05:00
Jarrod Johnson 4768bc257a Handle API change for setting user name
The web api now requires the word Administrator instead of
the number 4.
2020-12-15 16:44:55 -05:00
Jarrod Johnson 7610f9b963 Clean out .gitignore files
Don't leave them laying around installed package.
2020-12-15 15:53:41 -05:00
Jarrod Johnson b29e7bc94a Add new requirement in newer ESXi versions
New ESXi versions use a new efi executable during
boot.
2020-12-15 14:39:15 -05:00
Jarrod Johnson 04d63a269d Fix detection of CentOS 8.3
They changed their package name to be consistent with
their focal shift.
2020-12-15 12:59:48 -05:00
Jarrod Johnson e1bf22911b Try to have example directories obvious
git can't do empty directories, try a .gitignore
to have directories appear in the profiles
2020-12-15 12:52:57 -05:00
Jarrod Johnson d6642f1bde Fix omitted directory for firstboot
firstboot being in /opt requires a mkdir
2020-12-15 12:51:16 -05:00
Jarrod Johnson 36f027ac71 Implement support for Cisco switches
Cisco bridge mib requires to be instanced by vlan.
Detect through proprietary mibs and use it to
guide bridge mib walking.
2020-12-15 10:46:14 -05:00
Jarrod Johnson c025f4d2fc Fix firstboot.sh with selinux enabled 2020-12-14 15:06:04 -05:00
Jarrod Johnson 1238babe60 Notate future development requirements 2020-12-14 13:09:02 -05:00
Jarrod Johnson f9a82bde00 Fix arch detection in CentOS stream 2020-12-14 10:42:52 -05:00
Jarrod Johnson 48c868e935 Detect architecture for CentOS stream
CentOS stream changed the release rpm to be noarch.
2020-12-14 10:23:05 -05:00
Jarrod Johnson caf9115439 Fix CentOS stream support 2020-12-14 10:04:31 -05:00
Jarrod Johnson 8b11acbcf2 Recognize CentOS Stream
Allow installation of CentOS stream as a profile.
2020-12-14 09:47:56 -05:00
Jarrod Johnson db0f91c160 Comment to show how to opt out of UEK
For users that use the distribution with UEK, but do
not want UEK at all.
2020-12-14 08:33:58 -05:00
Jarrod Johnson cbb46dec3a Allow firstboot to run with root access
Being it /etc is unusual, and restorecon treats it
as such. Manually use chcon to explicitly allow
it in /etc/
2020-12-12 14:57:02 -05:00
Jarrod Johnson 0afa4c217c Add directory based post/firstboot capability
el8 profiles gain post.d/firstboot.d capability
2020-12-11 16:29:12 -05:00
Jarrod Johnson 47f04c8462 Provide guidance if the user tries to use defaults
Default username/password is no longer a
viable long term credentiol for XCC, have user
clearly be told to change and that they
shouldn't have to worry about the default
user and password.
2020-12-11 10:37:00 -05:00
Jarrod Johnson 5b0e23b8d4 Provide better feedback on XCC security lockouts
Rather than 'NoneType' error about grab_json_response,
provide actual recognizable feedback
2020-12-11 10:21:21 -05:00
Jarrod Johnson 14d9284cc5 Fix older Oracle Linux 7
Older OL has another release file thtat
was tripping the fingerprinting code.
2020-12-10 13:48:51 -05:00
Jarrod Johnson cd251fa5d6 Add support for OL7 and older other EL7 flavors
Older EL7 didn't have platform-python in installer,
change to fallback to old /usr/bin/python if
needed.
2020-12-10 10:54:30 -05:00
Jarrod Johnson 8d47395e53 Add fetch of '<script>.d' scripts
This can be used by firstboot/post scripts to
get modularized scripts.
2020-12-09 16:46:58 -05:00
Jarrod Johnson d19b5e4376 Have apiclient preserve server response verbatim
Do not add extra \n to output that server would
have already terminated.
2020-12-09 16:37:12 -05:00
Jarrod Johnson 7a9276300a Have apiclient reach out to deploycfg server
If we already have deploycfg, use that to indicate the
target server rather than copernicus data.
2020-12-09 16:20:02 -05:00
Jarrod Johnson 87ef68e26a Add 'memory' console.logging
If console.logging is not desired, but reconstituting the screen is,
provide 'memory' as a method to do that.

On slow disks this can significantly improve performance.
2020-12-09 13:47:46 -05:00
Jarrod Johnson 55b97793fd Lower concurrency limit of ipmi actions
IPMI actions can be a bit sensitive. Introduce some serialization
for improved robustness in liue of better parallelism.

The ideal would be to have 128 per core/process in the end, but for now,
a pool for 128 concurrent operations in flight at a time.
2020-12-08 18:23:13 -05:00
Jarrod Johnson fa823510b6 Pretty-ify Oracle Linux name in profile.yaml
Give it a nicer looking automatic label when
Oracle Linux is detected.
2020-12-07 15:09:30 -05:00
Jarrod Johnson 99609aa669 Add Oracle Linux signature check to osimage
Oracle Linux was being misidenntified as RHEL,
fix so that oracle linux is treated differently.
2020-12-07 15:08:28 -05:00
Jarrod Johnson 906011a80b Support 8.3 install
In 8.3, they refactored how network configuration is
managed early in install. Fix by detecting the presence
of the nm-lib and calling it's function again to
re-read the new cmdline.
2020-12-07 12:35:33 -05:00
Jarrod Johnson ff7f5daac6 Parallelize and timeout ssdp queries
Badly behaving 'desc.tmpl' servers exist in the world,
do not get tripped up or slowed down too much by
having aggressive timeout and making it parallel.
2020-12-04 17:14:35 -05:00
Jarrod Johnson 2d58741f15 Fix PXE/HTTP boot UUID and Mac case sensitivity
Like the SSDP code, PXE too had case sensitivity issues
2020-12-04 12:42:14 -05:00
Jarrod Johnson 57b74d59af Force uuid to lowercase in uuid mapping
Most of the codebase presumes lower case uuid, but
the uuid mapping was preserving whatever case the
attribute was in, making it case sensitive.

In the normal discovery process, this was filled in
as lower case. However if id.uuid is filled in manually
with uppercase, this broke the node lookup by uuid.
2020-12-04 07:41:40 -05:00
Jarrod Johnson 191cd8192a Add example installedargs to new ubuntu profiles 2020-12-03 10:55:39 -05:00
Jarrod Johnson 475eaca56b Add installedargs support to ubuntu profiles 2020-12-03 10:44:14 -05:00
Jarrod Johnson f33ddf3ab9 Move fetch of profile.yaml
Need to use thte copernicus network setup,
as the 'final' setup will depend on subsequent autoyast
activity.

Therefore, move it up to when mgr is probably IPv6
2020-12-02 09:12:14 -05:00
Jarrod Johnson 3422f3cdc5 Add and comment use of the installedargs in SUSE
With the support in addons, hook it in the
profile.
2020-12-02 09:05:36 -05:00
Jarrod Johnson 4c74581f0c Try to add installedargs to suse profiles 2020-12-01 16:57:26 -05:00
Jarrod Johnson 674d32e9e5 Correct mistake in previous pre scripts 2020-12-01 12:55:41 -05:00
Jarrod Johnson 666059c8bf Ignore more extraneous material 2020-12-01 12:55:26 -05:00
Jarrod Johnson 0137f99636 Add installedargs to rhel family
Provide a profile.yaml line that can be used to add arguments to
installed kernel as well as install kernel.
2020-12-01 10:53:26 -05:00
Jarrod Johnson 0c66021d3e Add tagged vlan support to el8 deployment 2020-11-30 08:16:04 -05:00
Jarrod Johnson 014727d355 Label boot.img with profile name
This allows for easier
search should an image want it
2020-11-09 15:45:44 -05:00
Jarrod Johnson dc262c366c Fix false positive in affluent detection
Make sure we don't receive
a redirect or other
when asking for mac tables.
2020-11-09 11:23:54 -05:00
Jarrod Johnson 8f99d87fda Reduce calls to update_neigh
On a mostly stable system, update_neigh will
continue to drive a significant portion of
background activity. Mitigate to only call if
circumstances suggest a need, or once every
30 seconds.
2020-11-09 09:00:57 -05:00
Jarrod Johnson edaaa2393d Hook up apiclient to TPM2 persistence, when available 2020-11-06 16:38:05 -05:00
Jarrod Johnson 1ecef6f251 Be a bit paranoid about string boundary 2020-11-06 13:57:35 -05:00
Jarrod Johnson 31c2c5f6f7 Fix errors in the TPM2 support 2020-11-06 13:38:37 -05:00
Jarrod Johnson c8747ac369 Merge branch '3.0' 2020-11-06 12:51:15 -05:00
Jarrod Johnson f7e7d05729 Add TPM2 support to node api key handling
This is an optional capability that image payloads may use
to use the TPM2 to protect an apikey as an alternative to
arming a weak authentication invocation
2020-11-06 10:00:36 -05:00
Jarrod Johnson 71cc0adadd Bump genesis to build more packages
Also notate src.rpm retrieval procedure
2020-10-30 12:34:42 -04:00
Jarrod Johnson b4e6e7caa8 Check for some issues in a manual assign request
One is to provide clear feedback when a nodename is requested
that was not previously defined, to make it more clear that
it is a requirement and/or guard against going too far while
the config function will be missing data it needs to complete
onboarding.

Another is to break if the request is trying to assign a node
to a different definition when it already exists under a different
name.
2020-10-30 08:18:27 -04:00
Jarrod Johnson 10ac1756f1 Do not clear the entire nodes lookup on remap
remap may only amend part of the map,
do not cause that to clear out the good data.
2020-10-29 15:49:31 -04:00
Jarrod Johnson 95659db00a Stop trying to use generic cookie parsing
Trying to do so while guarding against errors and sanitizing input was more code and slower
than targeting the one possible cookie we might care about.

So the code is simpler and
the performance is better, and the effect of stray cookies are mitigated.
2020-10-29 11:36:26 -04:00
Jarrod Johnson bddbc37e8e Fix incorrect length of random strings 2020-10-29 10:57:49 -04:00
Jarrod Johnson 7a2b295945 Change default label for ESXi to be more clear 2020-10-26 12:35:37 -04:00
Jarrod Johnson af8429ebf9 Fix esxi updateboot
Updateboot was confounded by a normal of 'file exists'
problems.
2020-10-26 12:22:56 -04:00
Jarrod Johnson 3ac6677d2d Sanitize cookies
If an invalid cookie from another site breaks the cookie jar,
then sanitize it.

https://bugs.python.org/issue31456

Performance enhancement through setting a header in javascript in
lieu of cookie parsing seems a wise move for the future.
2020-10-24 11:10:52 -04:00
Jarrod Johnson 8b5744b7eb Drop attempts to restore cursor key mode
It would corrupt F1 setup menu. This may cause problems for
ESXi TUI, but F1 in UEFI is more commonly on serial
2020-10-23 15:32:16 -04:00
Jarrod Johnson 7fcfc05205 Fix syntax problem in processing common nameserver 2020-10-22 12:48:01 -04:00
Jarrod Johnson 4b42bbda7e Fix spelling error
firmware is now spelled correctly
2020-10-22 12:11:10 -04:00
Jarrod Johnson ed41d93de5 Add remote authentication configuration
While our security guidelines preclude allowing host to know the password,
it is considered acceptable to do the out-of-band authentication configuration.

Have configbmc request a unicast remote configuration. This should handle authentication
as well as ensuring ongoing consistency between out of band and in-band configuration
methods.
2020-10-20 15:51:46 -04:00
Jarrod Johnson d36712d014 Refactor scripting to custom for suse
This makes it marginally easier to modify safely.

Further, it moves progress to after custom scripting
2020-10-16 11:34:43 -04:00
Jarrod Johnson 21cc9d66db Correct execution of post.sh
post.sh was not retrieved correctly, workaround
by prefecting it to the expected place rather than
letting yast do it.
2020-10-16 11:07:35 -04:00
Jarrod Johnson 4508cfa364 Update suse15 message with fixed autocons 2020-10-16 09:04:14 -04:00
Jarrod Johnson 05e84f2a7c Fix double-console out in genesis
With fixed autocons,
genesis was double outputting to serial console.

Let rungenesis pick
the console instead.
2020-10-14 16:49:10 -04:00
Jarrod Johnson 184727408a Have configbmc wait for settings to complete
There has been some confusion when configbmc changes
are deferred until later.

Reduce confusion by waiting for the settings to take effect,
but avoiding checking each parameter
to preserve most of the speedup.
2020-10-13 12:24:04 -04:00
Jarrod Johnson e7fbbe2737 Fix issues with leftover ssh sessions
Upon connection loss, even though confluent internally
decides it is done with it, it fails to close the session.

Catch a number of these scenarios and ensure the connection closes.
2020-10-12 09:47:24 -04:00
Jarrod Johnson 9a0c4ce4ce Fix function handling of subdirs of scripts 2020-10-09 14:07:58 -04:00
Jarrod Johnson 745b82a603 Add examples for MOFED in EL7 distros 2020-10-08 13:37:19 -04:00
Jarrod Johnson 504bee2d2a Fix problem when domain was not set
domain was checked even if domain not defined,
make sure domain is defined before trying
to use it.
2020-10-08 10:39:29 -04:00
Jarrod Johnson 8285f2a3de Incorporate convenience for Mellanox OFED install 2020-10-07 15:27:06 -04:00
Jarrod Johnson cfa97f7a9a Cover Mellanox ethernet adapters for mofed
Some elect to use MOFED with ethernet as well
2020-10-07 14:27:11 -04:00
Jarrod Johnson cbf42469c3 Add a sample script to install mofed
This is an example install script to use in post.custom for mofed
2020-10-07 14:12:31 -04:00
Jarrod Johnson 8dd66211b7 Avoid setting uuid and mac in pxe if already set
Notably the uuid change can end up recursing. Fix the behavior that will cause never ending
loops, which in some IO situations
can end in recursion limits.
2020-10-06 17:14:20 -04:00
Jarrod Johnson d466595828 Recognize a different m.2 name 2020-10-06 09:36:12 -04:00
Jarrod Johnson b0b965db98 Use newer functions in genesis and el7 2020-10-05 16:57:46 -04:00
Jarrod Johnson 9e73979b5b Enhance the EL script enhancements
Make them easier to use ad-hoc and add some capability
2020-10-05 16:55:37 -04:00
Jarrod Johnson f4395abade Deprecate attempts to use default password with SMM
This is removed in some level of the product
2020-10-05 16:54:58 -04:00
Jarrod Johnson a194e2293e Fix syntax error on discovery core 2020-10-02 15:35:14 -04:00
Jarrod Johnson d27577d2b7 Fix missing close parenthesis 2020-10-02 14:57:59 -04:00
Jarrod Johnson 1113c2a849 Improve duplicate switch attribute errors 2020-10-02 13:36:45 -04:00
Jarrod Johnson 587197e934 Refresh chained SMM discovery for SMMv2
Additionally, amend overall
discovery to force chain validation
rather than theoretically
accepting a low mac count match.
2020-10-02 11:45:50 -04:00
Jarrod Johnson ef901f64af Merge branch '3.0' 2020-09-30 10:04:21 -04:00
Jarrod Johnson 2ba05fb7b1 Enable IPMI on SMMv2 2020-09-29 11:21:53 -04:00
Jarrod Johnson a263851614 Fix problem with autocons
autocons needed to open the devnode earlier
to have the correct name. Fixes TSM autocons
behavior
2020-09-24 08:26:37 -04:00
Jarrod Johnson eeb3a3fa65 Have a clause for redfish not yet ready
We need redfish, but redfish is slow to boot on TSM..
2020-09-22 14:33:58 -04:00
Jarrod Johnson 56f8ca0982 Implement redfish resilient discovery for TSM
TSM redfish stack has an issue where it refuses to recognize any
non-redfish password change. Use redfish to change.

Regretably, it takes about 10 seconds for that change to propogate
to the practical API, so we have a discovery delay now.
2020-09-22 14:31:28 -04:00
Jarrod Johnson 8f94149627 Have batch files optionally not need quotes 2020-09-22 12:25:25 -04:00
Jarrod Johnson ed842fcc1a Add mods to adapt an xcat stateless image to a confluennt payload 2020-09-17 14:23:37 -04:00
96 changed files with 1596 additions and 243 deletions
+3
View File
@@ -1,4 +1,7 @@
*.pyc
.*.
confluent_client/man/man*
.vscode
.*.sw*
.sw*
.idea/*
+10 -1
View File
@@ -373,7 +373,7 @@ def do_command(command, server):
if argv[0] == 'exit':
if os.environ.get('TERM', '') not in ('linux'):
sys.stdout.write('\x1b]0;\x07')
raise Bailout()
raise BailOut()
elif argv[0] in ('help', '?'):
return print_help()
elif argv[0] == 'cd':
@@ -948,6 +948,8 @@ def main():
clearpowermessage = False
try:
sys.stdout.write(data)
except UnicodeEncodeError:
sys.stdout.buffer.write(data.encode('utf8'))
except IOError: # Some times circumstances are bad
# resort to byte at a time...
for d in data:
@@ -1020,6 +1022,13 @@ if __name__ == '__main__':
main()
except BailOut as e:
errcode = e.errorcode
except Exception as e:
import traceback
try:
quitconfetty()
except Exception:
pass
traceback.print_exc()
finally:
if deadline and os.times()[4] < deadline:
sys.stderr.write('[Exited early, hit enter to continue]')
+12 -4
View File
@@ -8,7 +8,7 @@ import os
import subprocess
import sys
def create_image(directory, image):
def create_image(directory, image, label=None):
ents = 0
datasz = 512
for dir in os.walk(sys.argv[1]):
@@ -25,8 +25,13 @@ def create_image(directory, image):
with open(image, 'wb') as imgfile:
imgfile.seek(datasz * 512 - 1)
imgfile.write(b'\x00')
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
str(datasz), '-s', '1','-h', '1', '::'])
if label:
subprocess.check_call(['mformat', '-i', image, '-v', label,
'-r', '16', '-d', '1', '-t', str(datasz),
'-s', '1','-h', '1', '::'])
else:
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
str(datasz), '-s', '1','-h', '1', '::'])
# 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
@@ -50,4 +55,7 @@ if __name__ == '__main__':
sys.stderr.write("Usage: {0} <directory> <imagefile>".format(
sys.argv[0]))
sys.exit(1)
create_image(sys.argv[1], sys.argv[2])
label = None
if len(sys.argv) > 3:
label = sys.argv[3]
create_image(sys.argv[1], sys.argv[2], label)
+10 -5
View File
@@ -134,9 +134,10 @@ def _assign_value():
assignment[key] = value
def parse_config_line(arguments):
def parse_config_line(arguments, single=False):
global setmode, printallbmc, forceset, key, value, needval, candidate, path, attrib
for param in arguments:
for pidx in range(0, len(arguments)):
param = arguments[pidx]
if param == 'show':
continue # forgive muscle memory of pasu users
if param == 'set':
@@ -146,7 +147,12 @@ def parse_config_line(arguments):
if needval:
key = needval
needval = None
value = param
if single:
value = ' '.join(arguments[pidx:])
_assign_value()
break
else:
value = param
_assign_value()
continue
if '=' in param or param[-1] == ':' or forceset:
@@ -215,7 +221,7 @@ if options.batch:
pass
argset = argset.strip()
if argset:
parse_config_line(shlex.split(argset))
parse_config_line(shlex.split(argset), single=True)
argset = argfile.readline()
else:
parse_config_line(args[1:])
@@ -272,7 +278,6 @@ if setmode:
rcode |= client.printerror(fr)
for node in fr.get('databynode', []):
r = fr['databynode'][node]
rcode |= client.printerror(r, node)
if 'value' not in r:
continue
keyval = r['value']
+1 -1
View File
@@ -79,7 +79,7 @@ def main(args):
sys.stderr.write('-n is a required argument currently to perform an install, optionally with -p\n')
return 1
if not args.profile and args.network:
sys.stderr.write('profile is a required argument to request a network deployment\n')
sys.stderr.write('Both noderange and a profile name are required arguments to request a network deployment\n')
return 1
if extra:
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
-1
View File
@@ -149,7 +149,6 @@ def show_firmware(session):
if 'databynode' not in res:
continue
for node in res['databynode']:
exitcode |= client.printerror(res['databynode'][node], node)
if 'firmware' not in res['databynode'][node]:
continue
for inv in res['databynode'][node]['firmware']:
+2
View File
@@ -187,5 +187,7 @@ def main():
argparser.print_help()
sys.exit(1)
handler(noderange, media)
if __name__ == '__main__':
main()
sys.exit(exitcode)
+1 -1
View File
@@ -32,7 +32,7 @@ if path.startswith('/opt'):
import confluent.client as client
argparser = optparse.OptionParser(
usage='Usage: %prog [options] noderange [default|cd|network|setup|hd|floppy]')
usage='Usage: %prog [options] noderange [default|cd|network|setup|hd|usb|floppy]')
argparser.add_option('-b', '--bios', dest='biosmode',
action='store_true', default=False,
help='Request BIOS style boot (rather than UEFI)')
+8 -4
View File
@@ -22,13 +22,17 @@ import io
import numpy as np
import os
import sixel
import subprocess
import sys
class DumbWriter(sixel.SixelWriter):
def restore_position(self, output):
return
try:
import sixel
class DumbWriter(sixel.SixelWriter):
def restore_position(self, output):
return
except ImportError:
pass
def plot(gui, output, plotdata, bins):
+6
View File
@@ -111,6 +111,12 @@ def printerror(res, node=None):
exitcode = 0
if 'errorcode' in res:
exitcode = res['errorcode']
for node in res.get('databynode', {}):
exitcode = res['databynode'][node].get('errorcode', exitcode)
if 'error' in res['databynode'][node]:
sys.stderr.write('{0}: {1}\n'.format(node, res['databynode'][node]['error']))
if exitcode == 0:
exitcode = 1
if 'error' in res:
if node:
sys.stderr.write('{0}: {1}\n'.format(node, res['error']))
+1 -1
View File
@@ -91,7 +91,7 @@ _confluent_nodeidentify_completion()
_confluent_nodesetboot_completion()
{
COMP_CANDIDATES=("default,cd,network,setup,hd,floppy -h -b -p")
COMP_CANDIDATES=("default,cd,network,setup,hd,floppy,usb -h -b -p")
_confluent_generic_completion
}
+5 -1
View File
@@ -4,7 +4,7 @@ nodesetboot(8) -- Check or set next boot device for noderange
## SYNOPSIS
`nodesetboot <noderange>`
`nodesetboot [options] <noderange> [default|cd|network|setup|hd|floppy]`
`nodesetboot [options] <noderange> [default|cd|network|setup|hd|usb|floppy]`
## DESCRIPTION
@@ -46,6 +46,10 @@ control.
Request boot from floppy. Generally speaking firmware uses this to mean a USB
flash drive or similar (whether virtual or physical).
* `usb`:
Request boot from usb. Generally speaking firmware uses this to mean a USB
flash drive or similar (whether virtual or physical).
* `network`:
Request boot to network
+49
View File
@@ -0,0 +1,49 @@
stats(8) -- Common basic statistics on typical numeric data in output
==============================
## SYNOPSIS
`<other command> | stats [-c N] [-d D] [-x|-g|-t|-o image.png] [-s N] [-v] [-b N]
## DESCRIPTION
The **stats** command helps analyze common numerical data such as performance numbers
or temperatures or any other numerical value.
By default it looks for the last numerical output on the first line to identify the numerical column
and analyze that number. This can be overriden by **-c COLUMN** to indicate a column. By default,
whitespace and commas are treated to delimit columns, and **-d DELIMITER** can be used to override.
By default it outputs basic statistics, but a histogram is available either text or through X11 output
or sixel or output to an image file depending on whether **-x**, **-g**, **-t*, or **-o image.png** is
used.
## OPTIONS
* `-c N`:
Select column number. Defaults to last column that appears numeric
* `-d D`:
Specify a custom column delimiter
* `-x`:
Output in Sixel format (supported by mlterm and PuTTY, among others)
* `-g`:
Try to open histogram as an X window
* `-t`:
Output histogram as bars rendered by =
* `-o image.png`:
Write graphical histogram to image.png.
* `-s N`:
Ignore specified number of lines as header content before processing numbers
* `-v`:
Treat value before : on each line as a label, and show which labels belong to which histogram buckets.
* `-b N`:
Specify a custom number of buckets for histogram. The default is 10.
@@ -10,13 +10,32 @@ import ssl
import sys
def get_apikey(nodename, mgr):
sealnew = True
if os.path.exists('/etc/confluent/confluent.apikey'):
return open('/etc/confluent/confluent.apikey').read().strip()
apikey = subprocess.check_output(['/opt/confluent/bin/clortho', nodename, mgr])
if not isinstance(apikey, str):
apikey = apikey.decode('utf8')
if apikey.startswith('SEALED:'):
sealnew = False
with open('/etc/confluent/confluent.sealedapikey', 'w+') as apiout:
apiout.write(apikey[7:])
with open('/etc/confluent/confluent.sealedapikey') as inp:
sp = subprocess.Popen(['/usr/bin/clevis-decrypt-tpm2'],
stdin=inp, stdout=subprocess.PIPE)
apikey = sp.communicate()[0]
if not isinstance(apikey, str):
apikey = apikey.decode('utf8')
with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
apiout.write(apikey)
if sealnew and os.path.exists('/usr/bin/clevis-encrypt-tpm2'):
try:
with open('/etc/confluent/confluent.apikey') as apin:
sealed = subprocess.check_output(
['/usr/bin/clevis-encrypt-tpm2', '{}'], stdin=apin)
print(HTTPSClient().grab_url('/confluent-api/self/saveapikey', sealed).decode())
except Exception:
sys.stderr.write('Unable to persist API key through TPM2 sealing\n')
apikey = apikey.strip()
os.chmod('/etc/confluent/confluent.apikey', 0o600)
return apikey
@@ -47,6 +66,15 @@ class HTTPSClient(client.HTTPConnection, object):
ifout.write(ifidx)
if json:
self.stdheaders['ACCEPT'] = 'application/json'
try:
info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
except Exception:
info = None
if info:
for line in info:
if line.startswith('deploy_server: '):
host = line.split(': ', 1)[1]
break
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, host)
if mgtiface:
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
@@ -104,4 +132,4 @@ if __name__ == '__main__':
sys.exit(0)
if os.path.exists(sys.argv[-1]):
data = open(sys.argv[-1]).read()
print(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
sys.stdout.write(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
@@ -53,6 +53,7 @@ for os in rhvh4 el7 el8 genesis suse15 ubuntu20.04 esxi6 esxi7; do
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
cp -a $os/profiles/* %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
find %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles -name .gitignore -exec rm -f {} +
done
%files
@@ -36,6 +36,8 @@ reboot
chrony
rsync
python
pciutils
%include /tmp/addonpackages
%end
@@ -1,2 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet
#installedargs: example # These arguments would be added to the installed system
@@ -1,12 +1,37 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -23,14 +48,23 @@ 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://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
if [ -x /usr/libexec/platform-python ]; then
/usr/libexec/platform-python $*
else
/usr/bin/python $*
fi
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
@@ -0,0 +1,20 @@
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
# and modify the script below if wanting to use the iso instead of tgz
# It checks for mellanox devices and opts not to install, so this script could be added
# to a general profile without causing mofed to install on non-mellanox systems
. /etc/confluent/functions
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
# Uncomment the following three lines and comment out the next
# two lines to use the .iso instead of the tgz packaging
#fetch_remote infiniband/mofed.iso
#mkdir MLNX_OFED
#mount -o loop ofed.iso MLNX_OFED
fetch_remote infiniband/mofed.tgz
tar xf infiniband/mofed.tgz
# The rest is common between tar and iso
cd MLNX_OFED*
mount -o loop ofed
./mlnxofedinstall --force
fi
@@ -0,0 +1,10 @@
# Add needed base packages to the install
cat << EOF >> /tmp/addonpackages
perl
pkgconf-pkg-config
tcsh
lsof
tk
gcc-gfortran
tcl
EOF
@@ -2,3 +2,6 @@
# This is a convenient place to keep customizations separate from modifying the stock scripts
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
# stock profile if the '.custom' files are used.
# An example for installing OFED for infiniband follows (see the file for more detail):
#run_remote infiniband/mofed.post
@@ -2,3 +2,13 @@
# 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.
#
#Here is an example to locally configure the platform BMC according
#to confluent configuration so that the BMC would be on the correct
#network:
#run_remote_python configbmc -c
#Some addons improve efficiency by adding dependencies during install
#here is an example for adding OFED install prereqs to the install
#run_remote infiniband/mofed.pre
@@ -28,11 +28,19 @@ if [ "$rootpw" = null ]; then
else
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
fi
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
if [ ! -z "$blargs" ]; then
blargs=' --append="'$blargs'"'
fi
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ "$grubpw" = "null" ]; then
touch /tmp/grubpw
else
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
blargs=" --iscrypted --password=$grubpw $blargs"
fi
if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host_*_key.pub; do
@@ -45,6 +53,7 @@ if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
TMUX= tmux a <> $consoledev >&0 2>&1 &
fi
touch /tmp/addonpackages
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
LUKSPARTY=''
if [ "$cryptboot" == "tpm2" ]; then
@@ -7,6 +7,13 @@ if [ -f /tmp/dd_disk ]; then
fi
done
fi
vlaninfo=$(getarg vlan)
if [ ! -z "$vlaninfo" ]; then
vldev=${vlaninfo#*:}
vlid=${vlaninfo#*.}
vlid=${vlid%:*}
ip link add link $vldev name $vldev.$vlid type vlan id $vlid
fi
TRIES=0
oum=$(umask)
umask 0077
@@ -35,6 +42,7 @@ cat /tls/*.pem > /etc/confluent/ca.pem
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
ifname=${ifname%@*}
echo $ifname > /tmp/net.ifaces
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
@@ -98,4 +106,16 @@ while read -r entry; do
continue
fi
done < /etc/confluent/confluent.deploycfg
if [ -e /lib/nm-lib.sh ]; then
. /lib/nm-lib.sh
nm_generate_connections
if [[ "$ifname" == ib* ]]; then
sed -i s/type=ethernet/type=infiniband/ /run/NetworkManager/system-connections/$ifname.nmconnection
if ! grep '\[infiniband\]' /run/NetworkManager/system-connections/$ifname.nmconnection > /dev/null; then
echo >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo '[infiniband]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo transport-mode=datagram >> /run/NetworkManager/system-connections/$ifname.nmconnection
fi
fi
fi
@@ -1,5 +1,5 @@
#!/bin/sh
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/' $2/profile.yaml
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/' $2/profile.yaml
ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \
ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution
mkdir -p $2/boot/efi/boot && \
@@ -34,9 +34,13 @@ reboot
%packages
@^minimal-environment
#-kernel-uek # This can opt out of the UEK for the relevant distribution
chrony
rsync
python3
tar
pciutils
%include /tmp/addonpackages
%include /tmp/cryptpkglist
%end
@@ -63,15 +67,16 @@ curl -f https://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/po
# Hook firstboot.sh
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/etc/confluent/firstboot.sh
chmod +x /mnt/sysimage/etc/confluent/firstboot.sh
mkdir -p /mnt/sysimage/opt/confluent/bin
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh
chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh
%end
%post
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
systemctl enable firstboot
chgrp ssh_keys /etc/ssh/ssh*key
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /etc/confluent/firstboot.sh
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /opt/confluent/bin/firstboot.sh
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/postinst.sh
@@ -1,2 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet
kernelargs: quiet # These arguments are passed to the installer
#installedargs: example # These arguments would be added to the installed system
@@ -111,6 +111,14 @@ class Session(object):
self.databuffer.raw[1:self.rsp.msg.data_len])}
return response
def await_config(s, bmccfg, channel):
vlan = bmccfg.get('bmcvlan', None)
ipv4 = bmccfg.get('bmcipv4', None)
prefix = bmccfg.get('prefixv4', None)
gw = bmccfg.get('bmcgw', None)
def raw_command(self,
netfn,
command,
@@ -217,8 +225,7 @@ def set_port_xcc(s, port, model):
sys.stdout.write('Complete\n')
def set_vlan(s, vlan, channel):
ovlan = vlan
def check_vlan(s, vlan, channel):
if vlan == 'off':
vlan = b'\x00\x00'
else:
@@ -229,7 +236,19 @@ def set_vlan(s, vlan, channel):
currvlan = bytes(s.raw_command(0xc, 2, bytearray([channel, 0x14 ,0, 0]))['data'][1:])
if bytearray(currvlan)[1] & 0b10000000 == 0:
currvlan = b'\x00\x00'
if currvlan == vlan:
return currvlan == vlan
def set_vlan(s, vlan, channel):
ovlan = vlan
if vlan == 'off':
vlan = b'\x00\x00'
else:
vlan = int(vlan)
if vlan:
vlan = vlan | 32768
vlan = struct.pack('<H', vlan)
if check_vlan(s, ovlan, channel):
sys.stdout.write('VLAN already configured to "{0}"\n'.format(ovlan))
return False
rsp = s.raw_command(0xc, 1, bytearray([channel, 0x14]) + vlan)
@@ -237,7 +256,7 @@ def set_vlan(s, vlan, channel):
print('VLAN configured to "{}"'.format(ovlan))
else:
print('Error setting vlan: ' + repr(rsp))
return
return True
def get_lan_channel(s):
@@ -253,13 +272,18 @@ def get_lan_channel(s):
return chan
return 1
def check_ipv4(s, ipaddr, channel):
ipaddr = bytearray(socket.inet_aton(ipaddr))
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
return rsp == ipaddr
def set_ipv4(s, ipaddr, channel):
oipaddr = ipaddr
ipaddr = bytearray(socket.inet_aton(ipaddr))
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
if rsp == ipaddr:
if check_ipv4(s, oipaddr, channel):
print('IP Address already set to {}'.format(oipaddr))
return
return False
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
if rsp != 1:
sys.stdout.write("Changing configuration to static...")
@@ -276,29 +300,47 @@ def set_ipv4(s, ipaddr, channel):
sys.stdout.flush()
print('Setting IP to {}'.format(oipaddr))
s.raw_command(0xc, 1, bytearray([channel, 3]) + ipaddr)
return True
def check_subnet(s, prefix, channel):
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
return rsp == mask
def set_subnet(s, prefix, channel):
oprefix = prefix
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
if rsp == mask:
if check_subnet(s, prefix, channel):
print('Subnet Mask already set to /{}'.format(oprefix))
return
return False
print('Setting subnet mask to /{}'.format(oprefix))
s.raw_command(0xc, 1, bytearray([channel, 6]) + mask)
return True
def check_gateway(s, gw, channel):
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
return rsp == gw
def set_gateway(s, gw, channel):
ogw = gw
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
if rsp == gw:
if check_gateway(s, ogw, channel):
print('Gateway already set to {}'.format(ogw))
return
return False
print('Setting gateway to {}'.format(ogw))
s.raw_command(0xc, 1, bytearray([channel, 12]) + gw)
return True
def dotwait():
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(0.5)
def main():
a = argparse.ArgumentParser(description='Locally configure a BMC device')
@@ -341,14 +383,30 @@ def main():
channel = set_port(s, bmccfg['bmcport'], vendor, model)
else:
channel = get_lan_channel(s)
awaitvlan = False
awaitip = False
awaitprefix = False
awaitgw = False
if bmccfg.get('bmcvlan', None):
set_vlan(s, bmccfg['bmcvlan'], channel)
awaitvlan = set_vlan(s, bmccfg['bmcvlan'], channel)
if bmccfg.get('bmcipv4', None):
set_ipv4(s, bmccfg['bmcipv4'], channel)
awaitip = set_ipv4(s, bmccfg['bmcipv4'], channel)
if bmccfg.get('prefixv4', None):
set_subnet(s, bmccfg['prefixv4'], channel)
awaitprefix = set_subnet(s, bmccfg['prefixv4'], channel)
if bmccfg.get('bmcgw', None):
set_gateway(s, bmccfg['bmcgw'], channel)
awaitgw = set_gateway(s, bmccfg['bmcgw'], channel)
sys.stdout.write('Waiting for changes to take effect...')
sys.stdout.flush()
while awaitvlan and not check_vlan(s, bmccfg['bmcvlan'], channel):
dotwait()
while awaitip and not check_ipv4(s, bmccfg['bmcipv4'], channel):
dotwait()
while awaitprefix and not check_subnet(s, bmccfg['prefixv4'], channel):
dotwait()
while awaitprefix and not check_gateway(s, bmccfg['bmcgw'], channel):
dotwait()
sys.stdout.write('done\n')
sys.stdout.flush()
#await_config(s, bmccfg, channel)
@@ -4,7 +4,7 @@ Requires=network-online.target
After=network-online.target
[Service]
ExecStart=/etc/confluent/firstboot.sh
ExecStart=/opt/confluent/bin/firstboot.sh
[Install]
WantedBy=multi-user.target
@@ -15,6 +15,8 @@ export nodename mgr profile
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
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
@@ -1,12 +1,44 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
run_remote_parts() {
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
for script in $scriptlist; do
run_remote $1.d/$script
done
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -23,12 +55,17 @@ 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://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
mkdir -p $(dirname $1)
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
retcode=$?
@@ -0,0 +1,20 @@
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
# and modify the script below if wanting to use the iso instead of tgz
# It checks for mellanox devices and opts not to install, so this script could be added
# to a general profile without causing mofed to install on non-mellanox systems
. /etc/confluent/functions
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
# Uncomment the following three lines and comment out the next
# two lines to use the .iso instead of the tgz packaging
#fetch_remote infiniband/mofed.iso
#mkdir MLNX_OFED
#mount -o loop ofed.iso MLNX_OFED
fetch_remote infiniband/mofed.tgz
tar xf infiniband/mofed.tgz
# The rest is common between tar and iso
cd MLNX_OFED*
mount -o loop ofed
./mlnxofedinstall --force
fi
@@ -0,0 +1,10 @@
# Add needed base packages to the install
cat << EOF >> /tmp/addonpackages
perl
pkgconf-pkg-config
tcsh
lsof
tk
gcc-gfortran
tcl
EOF
@@ -2,3 +2,6 @@
# This is a convenient place to keep customizations separate from modifying the stock scripts
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
# stock profile if the '.custom' files are used.
# An example for installing OFED for infiniband follows (see the file for more detail):
#run_remote infiniband/mofed.post
@@ -33,3 +33,6 @@ run_remote_python add_local_repositories
# run_remote example.sh
# run_remote_python example.py
run_remote post.custom
# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh
run_remote_parts post
@@ -7,3 +7,7 @@
#to confluent configuration so that the BMC would be on the correct
#network:
#run_remote_python configbmc -c
#Some addons improve efficiency by adding dependencies during install
#here is an example for adding OFED install prereqs to the install
#run_remote infiniband/mofed.pre
@@ -34,11 +34,19 @@ if [ "$rootpw" = null ]; then
else
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
fi
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
if [ ! -z "$blargs" ]; then
blargs=' --append="'$blargs'"'
fi
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ "$grubpw" = "null" ]; then
touch /tmp/grubpw
else
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
blargs=" --iscrypted --password=$grubpw $blargs"
fi
if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
@@ -53,6 +61,7 @@ fi
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
LUKSPARTY=''
touch /tmp/cryptpkglist
touch /tmp/addonpackages
if [ "$cryptboot" == "tpm2" ]; then
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
echo $cryptboot >> /tmp/cryptboot
@@ -15,10 +15,11 @@ 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
cat /tls/*.pem > /etc/confluent/ca.pem
/opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/^profile: //')
/opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg.new
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg.new | sed -e 's/^profile: //')
/opt/confluent/bin/apiclient /confluent-public/os/$profile/kickstart > /etc/confluent/ks.cfg
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/modinstall > /tmp/modinstall
mv /etc/confluent/confluent.deploycfg.new /etc/confluent/confluent.deploycfg
export node mgr profile
. /tmp/modinstall
exec /bin/install
@@ -4,3 +4,8 @@ install --firstdisk --overwritevmfs
%include /tmp/ksnet
%include /tmp/rootpw
reboot
%post
localcli network firewall unload
STATUP=$(mktemp)
echo '{"status": "complete"}' > $STATUP
/opt/confluent/bin/apiclient /confluent-api/self/updatestatus $STATUP
@@ -1,3 +1,3 @@
label: VMware ESXi %%VERSION%% Hypervisor
label: Confluent installation of VMware ESXi %%VERSION%% Hypervisor
ostype: esxi
kernelargs: runweasel
@@ -1,5 +1,7 @@
#!/bin/sh
mv /etc/confluent/confluent.deploycfg /etc/confluent/confluent.newdeploycfg
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/makeksnet >> /tmp/makeksnet
mv /etc/confluent/confluent.newdeploycfg /etc/confluent/confluent.deploycfg
chmod +x /tmp/makeksnet
/tmp/makeksnet > /tmp/ksnet
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
@@ -8,11 +8,16 @@ if ! grep console= /proc/cmdline >& /dev/null; then
echo $autocons > /tmp/01-autocons.devnode
if [ ! -z "$autocons" ]; then
echo "Using $(cat /tmp/01-autocons.conf)"
tmux a <> $autocons >&0 2>&1 &
(while :; do tmux a <> $autocons >&0 2>&1; done) &
(while :; do TERM=linux tmux a <> /dev/tty1 >&0 2>&1; done) &
else
(while :; do tmux a <> /dev/console >&0 2>&1; done) &
fi
else
tmux a <> /dev/tty1 >&0 2>&1 &
(while :; do tmux a <> /dev/console >&0 2>&1; done) &
(while :; do TERM=linux tmux a <> /dev/tty1 >&0 2>&1; done) &
fi
(while :; do TERM=linux tmux <> /dev/tty2 >&0 2>&1; done) &
echo -n "udevd: "
/usr/lib/systemd/systemd-udevd --daemon
echo -n "Loading drivers..."
@@ -25,6 +30,8 @@ modprobe hfi1
modprobe mlx5_ib
echo "done"
cat > /etc/ssh/sshd_config << EOF
Port 22
Port 3389
PermitRootLogin yes
AuthorizedKeysFile .ssh/authorized_keys
EOF
@@ -78,7 +85,9 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
privfile=${pubkey%.pub}
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/sshcert $pubkey > $certfile
echo HostCertificate $certfile >> /etc/ssh/sshd_config
if [ -s $certfile ]; then
echo HostCertificate $certfile >> /etc/ssh/sshd_config
fi
echo HostKey $privfile >> /etc/ssh/sshd_config
done
/usr/sbin/sshd
@@ -92,11 +101,3 @@ run_remote onboot.sh
while :; do
bash
done
cd -
@@ -10,4 +10,7 @@ mkdir -p /var/empty/sshd
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
tmux new-session sh /opt/confluent/bin/rungenesis
tmux new-session -d sh /opt/confluent/bin/rungenesis
while :; do
sleep 86400
done
@@ -1,2 +1,2 @@
label: Genesis
kernelarags: quiet
kernelargs: quiet
@@ -111,6 +111,14 @@ class Session(object):
self.databuffer.raw[1:self.rsp.msg.data_len])}
return response
def await_config(s, bmccfg, channel):
vlan = bmccfg.get('bmcvlan', None)
ipv4 = bmccfg.get('bmcipv4', None)
prefix = bmccfg.get('prefixv4', None)
gw = bmccfg.get('bmcgw', None)
def raw_command(self,
netfn,
command,
@@ -157,6 +165,14 @@ def set_port(s, port, vendor, model):
return 1
def get_remote_config_mod(vendor, model):
if vendor in ('IBM', 'Lenovo'):
if _is_tsm(model):
return 'tsm'
else:
return 'xcc'
return None
def set_port_tsm(s, port, model):
oport = port
sys.stdout.write('Setting TSM port to "{}"...'.format(oport))
@@ -217,8 +233,7 @@ def set_port_xcc(s, port, model):
sys.stdout.write('Complete\n')
def set_vlan(s, vlan, channel):
ovlan = vlan
def check_vlan(s, vlan, channel):
if vlan == 'off':
vlan = b'\x00\x00'
else:
@@ -229,7 +244,19 @@ def set_vlan(s, vlan, channel):
currvlan = bytes(s.raw_command(0xc, 2, bytearray([channel, 0x14 ,0, 0]))['data'][1:])
if bytearray(currvlan)[1] & 0b10000000 == 0:
currvlan = b'\x00\x00'
if currvlan == vlan:
return currvlan == vlan
def set_vlan(s, vlan, channel):
ovlan = vlan
if vlan == 'off':
vlan = b'\x00\x00'
else:
vlan = int(vlan)
if vlan:
vlan = vlan | 32768
vlan = struct.pack('<H', vlan)
if check_vlan(s, ovlan, channel):
sys.stdout.write('VLAN already configured to "{0}"\n'.format(ovlan))
return False
rsp = s.raw_command(0xc, 1, bytearray([channel, 0x14]) + vlan)
@@ -237,7 +264,7 @@ def set_vlan(s, vlan, channel):
print('VLAN configured to "{}"'.format(ovlan))
else:
print('Error setting vlan: ' + repr(rsp))
return
return True
def get_lan_channel(s):
@@ -253,13 +280,18 @@ def get_lan_channel(s):
return chan
return 1
def check_ipv4(s, ipaddr, channel):
ipaddr = bytearray(socket.inet_aton(ipaddr))
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
return rsp == ipaddr
def set_ipv4(s, ipaddr, channel):
oipaddr = ipaddr
ipaddr = bytearray(socket.inet_aton(ipaddr))
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
if rsp == ipaddr:
if check_ipv4(s, oipaddr, channel):
print('IP Address already set to {}'.format(oipaddr))
return
return False
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
if rsp != 1:
sys.stdout.write("Changing configuration to static...")
@@ -276,29 +308,47 @@ def set_ipv4(s, ipaddr, channel):
sys.stdout.flush()
print('Setting IP to {}'.format(oipaddr))
s.raw_command(0xc, 1, bytearray([channel, 3]) + ipaddr)
return True
def check_subnet(s, prefix, channel):
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
return rsp == mask
def set_subnet(s, prefix, channel):
oprefix = prefix
prefix = int(prefix)
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
if rsp == mask:
if check_subnet(s, prefix, channel):
print('Subnet Mask already set to /{}'.format(oprefix))
return
return False
print('Setting subnet mask to /{}'.format(oprefix))
s.raw_command(0xc, 1, bytearray([channel, 6]) + mask)
return True
def check_gateway(s, gw, channel):
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
return rsp == gw
def set_gateway(s, gw, channel):
ogw = gw
gw = bytearray(socket.inet_aton(gw))
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
if rsp == gw:
if check_gateway(s, ogw, channel):
print('Gateway already set to {}'.format(ogw))
return
return False
print('Setting gateway to {}'.format(ogw))
s.raw_command(0xc, 1, bytearray([channel, 12]) + gw)
return True
def dotwait():
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(0.5)
def main():
a = argparse.ArgumentParser(description='Locally configure a BMC device')
@@ -341,15 +391,40 @@ def main():
channel = set_port(s, bmccfg['bmcport'], vendor, model)
else:
channel = get_lan_channel(s)
awaitvlan = False
awaitip = False
awaitprefix = False
awaitgw = False
if bmccfg.get('bmcvlan', None):
set_vlan(s, bmccfg['bmcvlan'], channel)
awaitvlan = set_vlan(s, bmccfg['bmcvlan'], channel)
if bmccfg.get('bmcipv4', None):
set_ipv4(s, bmccfg['bmcipv4'], channel)
awaitip = set_ipv4(s, bmccfg['bmcipv4'], channel)
if bmccfg.get('prefixv4', None):
set_subnet(s, bmccfg['prefixv4'], channel)
awaitprefix = set_subnet(s, bmccfg['prefixv4'], channel)
if bmccfg.get('bmcgw', None):
set_gateway(s, bmccfg['bmcgw'], channel)
#await_config(s, bmccfg, channel)
awaitgw = set_gateway(s, bmccfg['bmcgw'], channel)
sys.stdout.write('Waiting for changes to take effect...')
sys.stdout.flush()
while awaitvlan and not check_vlan(s, bmccfg['bmcvlan'], channel):
dotwait()
while awaitip and not check_ipv4(s, bmccfg['bmcipv4'], channel):
dotwait()
while awaitprefix and not check_subnet(s, bmccfg['prefixv4'], channel):
dotwait()
while awaitgw and not check_gateway(s, bmccfg['bmcgw'], channel):
dotwait()
sys.stdout.write('done\n')
sys.stdout.flush()
cfgmod = get_remote_config_mod(vendor, model)
if cfgmod:
with open('configbmc.configmod', 'w+') as cm:
cm.write('configmod: {0}\n'.format(cfgmod))
sys.stdout.write('Requesting remote configuration of authentication...')
sys.stdout.flush()
bmccfgsrc = subprocess.check_output(
[sys.executable, apiclient, '/confluent-api/self/remoteconfigbmc', 'configbmc.configmod'])
sys.stdout.write('done\n')
sys.stdout.flush()
if __name__ == '__main__':
@@ -1,12 +1,35 @@
function set_confluent_vars() {
if [ -z "$mgr" ]; then
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
if [ -z "$profile" ]; then
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
fi
}
fetch_remote() {
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
}
run_remote() {
requestedcmd="'$*'"
curlargs=""
if [ -f /etc/confluent/ca.pem ]; then
curlargs=" --cacert /etc/confluent/ca.pem"
fi
set_confluent_vars
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
chmod +x $1
cmd=$1
@@ -23,12 +46,16 @@ 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://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
retcode=$?
@@ -1,2 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet
#installedargs: example # These arguments would be added to the installed system
@@ -42,11 +42,19 @@ if [ "$rootpw" = null ]; then
else
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
fi
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
if [ ! -z "$blargs" ]; then
blargs=' --append="'$blargs'"'
fi
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ "$grubpw" = "null" ]; then
touch /tmp/grubpw
else
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
blargs=" --iscrypted --password=$grubpw $blargs"
fi
if [ ! -z "$blargs" ]; then
echo "bootloader $blargs" > /tmp/grubpw
fi
ssh-keygen -A
for pubkey in /etc/ssh/ssh_host_*_key.pub; do
@@ -12,7 +12,7 @@ autocons=""
if ! grep console /proc/cmdline > /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
if [ ! -z "$autocons" ]; then
echo "Serial console detected from firmmware: $autocons" > ${autocons%,*}
echo "Serial console detected from firmware: $autocons" > ${autocons%,*}
fi
fi
mkdir -p /etc/confluent
@@ -52,6 +52,9 @@ if [ -z "$mgtiface" ]; then
else
curl -H "CONFLUENT_MGTIFACE: $mgtiface" -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
fi
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
profilename=${profilename#profile: }
curl https://$mgr/confluent-public/os/$profilename/profile.yaml > /tmp/profile.yaml
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
dnsdomain=${dnsdomain#dnsdomain: }
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
@@ -60,11 +63,15 @@ fi
textconsole=$(grep ^textconsole: /etc/confluent/confluent.deploycfg)
textconsole=${textconsole#textconsole: }
if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null && [ ! -z "$autocons" ]; then
echo "Serial console autodetected and enabled, will be available upon install completion" > ${autocons%,*}
echo "The installer will run in text mode on the graphics console. When ssh is available," > ${autocons%,*}
echo "install progress may be checked by using ssh to access and run the screendump command" > ${autocons%,*}
echo "Install time serial console would require the profile to have console=${autocons#/dev/}" > ${autocons%,*}
echo "specified in the kernel command line arguments" > ${autocons%,*}
echo "Serial console has been autodected and enabled read-only for install" > ${autocons%,*}
echo "It will be read-write after install" > ${autocons%,*}
echo "If a fully functional console is desired over serial, add console=${autocons#/dev/} " > ${autocons%,*}
echo "to kerneralgs in the profile.yaml file of the profile and run 'osdeploy updateboot <profile>" > ${autocons%,*}
#echo "Serial console autodetected and enabled, will be available upon install completion" > ${autocons%,*}
#echo "The installer will run in text mode on the graphics console. When ssh is available," > ${autocons%,*}
#echo "install progress may be checked by using ssh to access and run the screendump command" > ${autocons%,*}
#echo "Install time serial console would require the profile to have console=${autocons#/dev/}" > ${autocons%,*}
#echo "specified in the kernel command line arguments" > ${autocons%,*}
echo ${autocons%,*} > /tmp/autoconsdev
sed -e s'/$/ 'console=${autocons#*/dev/}/ /proc/cmdline > /etc/fakecmdline
mount -o bind /etc/fakecmdline /proc/cmdline
@@ -98,7 +105,7 @@ else
fi
fi
nameserversec=0
if [ ${entry%:*} = "nameservers" ]; then
if [ "${entry%:*}" = "nameservers" ]; then
nameserversec=1
continue
fi
@@ -107,11 +114,16 @@ fi
echo done
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server: }
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
profilename=${profilename#profile: }
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
proto=${proto#protocol: }
append=$(grep ^installedargs: /tmp/profile.yaml | sed -e 's/^installedargs: //' -e 's/#.*//')
if [ -z "$append" ]; then
echo "<bootloader/>" > /tmp/bootloader.xml
else
echo "<bootloader><global><append>$append</append></global></bootloader>" > /tmp/bootloader.xml
fi
echo "<media_url>${proto}://${mgr}/confluent-public/os/${profilename}/product</media_url>" > /tmp/pkgurl
echo "AutoYaST: $proto://$mgr/confluent-public/os/$profilename/autoyast" >> /etc/linuxrc.d/01-confluent
@@ -28,6 +28,7 @@ dynamic behavior and replace with static configuration.
</add_on_products>
</add-on>
%%ENDIFSLE%%
<xi:include href="file:///tmp/bootloader.xml"/>
<software>
%%IFSLE%%
<products config:type="list">
@@ -126,6 +127,7 @@ curl -f $proto://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/e
curl -f $proto://$mgr/confluent-public/os/$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>
@@ -1,2 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet
kernelargs: quiet # These arguments are passed to the installer
#installedargs: example # These arguments would be added to the installed system
@@ -0,0 +1,9 @@
#!/bin/sh
# This script runs at the end of the final boot
. /etc/confluent/functions
# Custom scripts may go here
# run_remote example.sh
# run_remote_python example.py
@@ -7,9 +7,7 @@ mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
apikey=$(cat /etc/confluent/confluent.apikey)
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -f -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
. /etc/confluent/functions
# Custom scripts may go here
# run_remote example.sh
# run_remote_python example.py
run_remote firstboot.custom
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -f -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
@@ -0,0 +1,16 @@
#!/bin/sh
# This script runs at the end of install in the installed system
# but still under the installer kernel.
# This is a good place to run most customizations that do not have any
# dependency upon the install target kernel being active.
# If there are dependencies on the kernel (drivers or special filesystems)
# then firstboot.sh would be the script to customize.
. /etc/confluent/functions
# Examples:
# run_remote script.sh
# run_remote_python script.py
@@ -21,10 +21,7 @@ chmod og-rwx /etc/confluent/*
export mgr profile nodename
. /etc/confluent/functions
run_remote post.custom
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
# Customizations may go here
# Examples:
# run_remote script.sh
# run_remote_python script.py
@@ -2,5 +2,6 @@
deploycfg=/custom-installation/confluent/confluent.deploycfg
mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
profile=$(grep ^profile: $deploycfg|awk '{print $2}')
export deploycfg mgr profile
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/post.sh
. /tmp/post.sh
@@ -1,2 +1,3 @@
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
kernelargs: quiet osprofile=%%PROFILE%%
#installedargs: example # These arguments would be added to the installed system
@@ -29,6 +29,7 @@ if grep ^ntpservers: /target/etc/confluent/confluent.deploycfg > /dev/null; then
sed -i "s/#NTP=/NTP=$ntps/" /target/etc/systemd/timesyncd.conf
fi
textcons=$(grep ^textconsole: /target/etc/confluent/confluent.deploycfg |awk '{print $2}')
updategrub=0
if [ "$textcons" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
cons=""
if [ -f /custom-installation/autocons.info ]; then
@@ -36,11 +37,18 @@ if [ "$textcons" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
fi
if [ ! -z "$cons" ]; then
sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 console='${cons#/dev/}'"/' /target/etc/default/grub
mount -o bind /dev /target/dev
mount -o bind /proc /target/proc
mount -o bind /sys /target/sys
chroot /target update-grub
umount /target/sys /target/dev /target/proc
updategrub=1
fi
fi
kargs=$(curl https://$mgr/confluent-public/os/$profile/profile.yaml | grep ^installedargs: | sed -e 's/#.*//')
if [ ! -z "$kargs" ]; then
sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 '"${kargs}"'"/' /target/etc/default/grub
fi
if [ 1 = $updategrub ]; then
mount -o bind /dev /target/dev
mount -o bind /proc /target/proc
mount -o bind /sys /target/sys
chroot /target update-grub
umount /target/sys /target/dev /target/proc
fi
+55 -8
View File
@@ -1,3 +1,4 @@
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <fcntl.h>
@@ -20,20 +21,29 @@
int main(int argc, char* argv[]) {
struct termios tty;
struct termios tty2;
struct winsize ws;
unsigned width, height;
int ttyf;
int spcr;
int tmpi;
int currspeed;
int flags;
speed_t cspeed;
char buff[128];
int bufflen;
fd_set set;
struct timeval timeout;
char* offset;
uint64_t address;
spcr = open("/sys/firmware/acpi/tables/SPCR", O_RDONLY);
if (spcr < 0) {
bufflen = 0;
tmpi = open("/sys/firmware/acpi/tables/SPCR", O_RDONLY);
if (tmpi < 0) {
exit(0);
}
if (read(spcr, buff, 80) < 80) {
if (read(tmpi, buff, 80) < 80) {
exit(0);
}
close(tmpi);
if (buff[8] != 2) exit(0); //revision 2
if (buff[36] != 0) exit(0); //16550 only
if (buff[40] != 1) exit(0); //IO only
@@ -69,12 +79,49 @@ int main(int argc, char* argv[]) {
}
tcgetattr(ttyf, &tty);
if (cspeed) {
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
cfsetospeed(&tty, cspeed);
cfsetispeed(&tty, cspeed);
}
printf("%s\n", buff);
tcgetattr(ttyf, &tty2);
cfmakeraw(&tty2);
tcsetattr(ttyf, TCSANOW, &tty2);
flags = fcntl(ttyf, F_GETFL, 0);
fcntl(ttyf, F_SETFL, flags | O_NONBLOCK);
while (read(ttyf, buff, 64) > 0) {
// Drain any pending reads
}
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
FD_ZERO(&set);
FD_SET(ttyf, &set);
write(ttyf, "\0337\033[999;999H\033[6n\0338", 18);
while (select(ttyf + 1, &set, NULL, NULL, &timeout) > 0) {
if ((tmpi = read(ttyf, buff + bufflen, 127 - bufflen)) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
} else {
break;
}
}
bufflen += tmpi;
buff[bufflen] = 0;
if (strchr(buff, 'R')) {
break;
}
}
fcntl(ttyf, F_SETFL, flags);
ws.ws_xpixel = 0;
ws.ws_ypixel = 0;
if (sscanf(buff, "\033[%u;%uR", &height, &width) == 2) {
ws.ws_col = width;
ws.ws_row = height;
} else {
ws.ws_col = 100;
ws.ws_row = 31;
}
ioctl(ttyf, TIOCSWINSZ, &ws);
tcsetattr(ttyf, TCSANOW, &tty);
ioctl(ttyf, TIOCCONS, 0);
printf("%s\n", buff);
}
+19 -3
View File
@@ -36,7 +36,8 @@ unsigned char* genpasswd(int len) {
int main(int argc, char* argv[]) {
int sock, ret;
char slen;
unsigned char currlen, currtype;
unsigned char currtype;
size_t currlen;
unsigned char* passwd;
unsigned char* cryptedpass;
unsigned char* macaddr;
@@ -107,10 +108,21 @@ int main(int argc, char* argv[]) {
ret = read(sock, buffer, 2);
while (buffer[0] != 255) {
currtype = buffer[0];
currlen = buffer[1];
if (currtype & 0b10000000) {
currlen = buffer[1] << 8;
read(sock, buffer, 1);
currlen |= buffer[0];
} else {
currlen = buffer[1];
}
memset(buffer, 0, MAXPACKET);
if (currlen > 1000) {
fprintf(stderr, "Received oversized message\n");
exit(1);
}
if (currlen) {
ret = read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET
ret = read(sock, buffer, currlen); // Max is 1000, well under MAX_PACKET
buffer[currlen] = 0;
}
if (currtype == 2) {
dprintf(sock, "\x03%c", currlen);
@@ -118,6 +130,10 @@ int main(int argc, char* argv[]) {
slen = strlen(cryptedpass) & 0xff;
dprintf(sock, "\x04%c%s", slen, cryptedpass);
ret = write(sock, "\x00\x00", 2);
} else if (currtype == 128) {
printf("SEALED:%s", buffer);
printf("\n");
exit(0);
} else if (currtype == 5) {
printf("%s", passwd);
printf("\n");
+1 -1
View File
@@ -74,7 +74,7 @@ if args[0] == 'restore':
for targdir in os.walk('/etc/confluent'):
os.chown(targdir[0], owner, group)
for f in targdir[2]:
os.chown(os.patht.join(targdir[0], f), owner, group)
os.chown(os.path.join(targdir[0], f), owner, group)
except Exception as e:
print(str(e))
sys.exit(1)
@@ -19,6 +19,7 @@ import confluent.collective.invites as invites
import confluent.config.configmanager as cfm
import confluent.exceptions as exc
import confluent.log as log
import confluent.noderange as noderange
import confluent.tlvdata as tlvdata
import confluent.util as util
import eventlet
@@ -27,6 +28,7 @@ import eventlet.green.ssl as ssl
import eventlet.green.threading as threading
import greenlet
import random
import time
import sys
try:
import OpenSSL.crypto as crypto
@@ -38,6 +40,7 @@ except ImportError:
currentleader = None
follower = None
retrythread = None
failovercheck = None
class ContextBool(object):
def __init__(self):
@@ -193,15 +196,21 @@ def connect_to_collective(cert, member):
raise Exception("Certificate mismatch in the collective")
return remote
mycachedname = [None, 0]
def get_myname():
if mycachedname[1] > time.time() - 15:
return mycachedname[0]
try:
with open('/etc/confluent/cfg/myname', 'r') as f:
return f.read().strip()
mycachedname[0] = f.read().strip()
mycachedname[1] = time.time()
return mycachedname[0]
except IOError:
myname = socket.gethostname()
with open('/etc/confluent/cfg/myname', 'w') as f:
f.write(myname)
mycachedname[0] = myname
mycachedname[1] = time.time()
return myname
def handle_connection(connection, cert, request, local=False):
@@ -567,6 +576,7 @@ def become_leader(connection):
if dronecandidate in skipem or member == myname:
continue
eventlet.spawn_n(try_assimilate, dronecandidate)
schedule_rebalance()
def startup():
@@ -576,7 +586,53 @@ def startup():
return
eventlet.spawn_n(start_collective)
def check_managers():
global failovercheck
if not follower:
c = cfm.ConfigManager(None)
collinfo = {}
populate_collinfo(collinfo)
availmanagers = {}
offlinemgrs = set(collinfo['offline'])
offlinemgrs.add('')
for offline in collinfo['offline']:
nodes = noderange.NodeRange(
'collective.manager=={}'.format(offline), c).nodes
managercandidates = c.get_node_attributes(
nodes, 'collective.managercandidates')
expandednoderanges = {}
for node in nodes:
if node not in managercandidates:
continue
targets = managercandidates[node].get('collective.managercandidates', {}).get('value', None)
if not targets:
continue
if not availmanagers:
for active in collinfo['active']:
availmanagers[active] = len(
noderange.NodeRange(
'collective.manager=={}'.format(active), c).nodes)
availmanagers[collinfo['leader']] = len(
noderange.NodeRange(
'collective.manager=={}'.format(
collinfo['leader']), c).nodes)
if targets not in expandednoderanges:
expandednoderanges[targets] = set(
noderange.NodeRange(targets, c).nodes) - offlinemgrs
targets = sorted(expandednoderanges[targets], key=availmanagers.get)
if not targets:
continue
c.set_node_attributes({node: {'collective.manager': {'value': targets[0]}}})
availmanagers[targets[0]] += 1
failovercheck = None
def schedule_rebalance():
global failovercheck
if not failovercheck:
failovercheck = eventlet.spawn_after(10, check_managers)
def start_collective():
cfm.membership_callback = schedule_rebalance
global follower
global retrythread
if follower:
@@ -129,6 +129,14 @@ node = {
'Generally this is not directly modified, but is modified '
'by the "nodedeploy" command'),
},
'deployment.sealedapikey': {
'description': 'This attribute is used by some images to save a sealed '
'version of a node apikey, so that a subsequent run with '
'same TPM2 will use the TPM2 to protect the API key rather '
'than local network verification. If this is set, then '
'an api key request will receive this if the api key grant '
'is not armed',
},
#'id': {
# 'description': ('Numeric identifier for node')
#},
@@ -181,6 +189,10 @@ node = {
# 'autonode.servername, so that would not need to be '
# 'copied ')
# },
# 'collective.allowedmanagers': {
# 'description': ('Restricted set of deployment and managers in automatic selectien
# },
# ssh.equivnodes - control the list of nodes that go into equiv...
'collective.manager': {
'description': ('When in collective mode, the member of the '
'collective currently considered to be responsible '
@@ -189,6 +201,14 @@ node = {
'indicates candidate managers, either for '
'high availability or load balancing purposes.')
},
'collective.managercandidates': {
'description': ('A noderange of nodes permitted to be a manager for '
'the node. This controls failover and deployment. If '
'not defined, all managers may deploy and no '
'automatic failover will be performed. '
'Using this requires that collective members be '
'defined as nodes for noderange expansion')
},
'deployment.pendingprofile': {
'description': ('An OS profile that is pending deployment. This indicates to '
'the network boot subsystem what should be offered when a potential '
@@ -328,7 +348,7 @@ node = {
'description': ('Indicate logging level to apply to console. Valid '
'values are currently "full", "interactive", and '
'"none". Defaults to "full".'),
'validvalues': ('full', 'interactive', 'none'),
'validvalues': ('full', 'memory', 'interactive', 'none'),
},
'console.method': {
'description': ('Indicate the method used to access the console of '
@@ -522,6 +542,13 @@ node = {
'description': ('Password to use when connecting to the hardware '
'manager'),
},
'ssh.trustnodes': {
'description': ('Nodes that are allowed to ssh into the node, '
'expressed in noderange syntax. This is used during '
'deployment if the confluent SSH certificate '
'authority is configured. Default behavior is for '
'all nodes to trust each other.'),
},
'pubkeys.addpolicy': {
'description': ('Policy to use when encountering unknown public '
'keys. Choices are "automatic" to accept and '
@@ -115,6 +115,7 @@ _attraliases = {
}
_validroles = ('Administrator', 'Operator', 'Monitor')
membership_callback = None
def attrib_supports_expression(attrib):
if not isinstance(attrib, str):
@@ -409,6 +410,8 @@ def _push_rpc(stream, payload):
except Exception:
logException()
del cfgstreams[stream]
if membership_callback:
membership_callback()
stream.close()
@@ -615,6 +618,8 @@ def relay_slaved_requests(name, listener):
except Exception:
pass
del cfgstreams[name]
if membership_callback:
membership_callback()
cfgstreams[name] = listener
lh = StreamHandler(listener)
_hasquorum = len(cfgstreams) >= (
@@ -682,6 +687,8 @@ def relay_slaved_requests(name, listener):
_push_rpc,
[(cfgstreams[s], payload) for s in cfgstreams]):
pass
if membership_callback:
membership_callback()
if not cfgstreams and not cfgleader: # last one out, set cfgleader to boolean to mark dead collective
stop_following(True)
return False
@@ -739,6 +746,8 @@ def stop_leading():
del cfgstreams[stream]
except KeyError:
pass # may have already been deleted..
if membership_callback:
membership_callback()
_oldcfgstore = None
@@ -776,7 +785,7 @@ def commit_clear():
# currently defined as local to each collective member
# currently just 'autosense' which is intended to be active
# per collective member
for globvar in _oldcfgstore['globals']:
for globvar in _oldcfgstore.get('globals', ()):
if globvar.endswith('_key'):
continue
_cfgstore['globals'][globvar] = _oldcfgstore['globals'][globvar]
+5 -5
View File
@@ -234,7 +234,7 @@ class ConsoleHandler(object):
self._isondemand = False
else:
if (attrvalue[self.node]['console.logging']['value'] not in (
'full', '', 'buffer')):
'full', '', 'memory')):
self._isondemand = True
if (attrvalue[self.node]['console.logging']['value']) in ('none', 'memory'):
self._dologging = False
@@ -609,10 +609,10 @@ class ConsoleHandler(object):
if self.shiftin is not None: # detected that terminal requested a
# shiftin character set, relay that to the terminal that cannected
retdata += b'\x1b)' + self.shiftin
if self.appmodedetected:
retdata += b'\x1b[?1h'
else:
retdata += b'\x1b[?1l'
#if self.appmodedetected:
# retdata += b'\x1b[?1h'
#else:
# retdata += b'\x1b[?1l'
return retdata, connstate
def write(self, data):
+22 -3
View File
@@ -22,6 +22,16 @@ import eventlet
import eventlet.green.socket as socket
import eventlet.greenpool
import os
import struct
# cred grant tlvs:
# 0, 0 - null
# 1, len, <nodename>
# 2, len, token - echo request
# 3, len, token - echo reply
# 4, len, crypted - crypted apikey
# 5, 0, accept key
# 128, len, len, key - sealed key
class CredServer(object):
def __init__(self):
@@ -38,11 +48,20 @@ class CredServer(object):
client.close()
return
nodename = util.stringify(client.recv(tlv[1]))
tlv = bytearray(client.recv(2))
apiarmed = self.cfm.get_node_attributes(nodename, 'deployment.apiarmed')
apiarmed = apiarmed.get(nodename, {}).get('deployment.apiarmed', {}).get(
tlv = bytearray(client.recv(2)) # should always be null
apimats = self.cfm.get_node_attributes(nodename,
['deployment.apiarmed', 'deployment.sealedapikey'])
apiarmed = apimats.get(nodename, {}).get('deployment.apiarmed', {}).get(
'value', None)
if not apiarmed:
if apimats.get(nodename, {}).get(
'deployment.sealedapikey', {}).get('value', None):
sealed = apimats[nodename]['deployment.sealedapikey'][
'value']
if not isinstance(sealed, bytes):
sealed = sealed.encode('utf8')
reply = b'\x80' + struct.pack('>H', len(sealed) + 1) + sealed + b'\x00'
client.send(reply)
client.close()
return
if apiarmed not in ('once', 'continuous'):
+32 -6
View File
@@ -230,6 +230,8 @@ def send_discovery_datum(info):
yield msg.KeyValueData({'serialnumber': sn})
yield msg.KeyValueData({'modelnumber': mn})
yield msg.KeyValueData({'uuid': uuid})
if 'enclosure.uuid' in info:
yield msg.KeyValueData({'enclosure_uuid': info['enclosure.uuid']})
if 'enclosure.bay' in info:
yield msg.KeyValueData({'bay': int(info['enclosure.bay'])})
yield msg.KeyValueData({'macs': [info.get('hwaddr', '')]})
@@ -240,6 +242,16 @@ def send_discovery_datum(info):
yield msg.KeyValueData({'types': types})
if 'otheraddresses' in info:
yield msg.KeyValueData({'otheripaddrs': list(info['otheraddresses'])})
if 'location' in info:
yield msg.KeyValueData({'location': info['location']})
if 'room' in info:
yield msg.KeyValueData({'room': info['room']})
if 'rack' in info:
yield msg.KeyValueData({'rack': info['rack']})
if 'u' in info:
yield msg.KeyValueData({'lowest_u': info['u']})
if 'hostname' in info:
yield msg.KeyValueData({'hostname': info['hostname']})
def _info_matches(info, criteria):
@@ -772,12 +784,14 @@ def get_chained_smm_name(nodename, cfg, handler, nl=None, checkswitch=True):
'extend a single enclosure')
cd = cfg.get_node_attributes(nodename, ['hardwaremanagement.manager',
'pubkeys.tls_hardwaremanager'])
smmaddr = cd[nodename]['hardwaremanagement.manager']['value']
pkey = cd[nodename].get('pubkeys.tls_hardwaremanager', {}).get(
'value', None)
if not pkey:
# We cannot continue through a break in the chain
return None, False
smmaddr = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None)
if not smmaddr:
return None, False
if pkey:
cv = util.TLSCertVerifier(
cfg, nodename, 'pubkeys.tls_hardwaremanager').verify_cert
@@ -797,13 +811,17 @@ def get_smm_neighbor_fingerprints(smmaddr, cv):
if ':' in smmaddr:
smmaddr = '[{0}]'.format(smmaddr)
wc = webclient.SecureHTTPConnection(smmaddr, verifycallback=cv)
neighs = wc.grab_json_response('/scripts/neighdata.json')
try:
neighs = wc.grab_json_response('/scripts/neighdata.json')
except Exception:
log.log({'error': 'Failure getting LLDP information from {}'.format(smmaddr)})
return
if not neighs:
return
for idx in (4, 5):
if 'sha256' not in neighs[idx]:
for neigh in neighs:
if 'sha256' not in neigh:
continue
yield 'sha256$' + b64tohex(neighs[idx]['sha256'])
yield 'sha256$' + b64tohex(neigh['sha256'])
def get_nodename(cfg, handler, info):
@@ -855,6 +873,14 @@ def get_nodename(cfg, handler, info):
# while this started by switch, it was disambiguated
info['verified'] = v
return newnodename, None
else:
errorstr = ('Attempt to discover SMM in chain but '
'unable to follow chain to the specific '
'SMM, it may be waiting on an upstream '
'SMM, chain starts with {0}'.format(
nodename))
log.log({'error': errorstr})
return None, None
if (nodename and
not handler.discoverable_by_switch(macinfo['maccount'])):
if handler.devname == 'SMM':
@@ -1322,7 +1348,7 @@ def _map_unique_ids(nodes=None):
del nodes_by_uuid[uuid_by_nodes[node]]
if node in fprint_by_nodes:
del nodes_by_fprint[fprint_by_nodes[node]]
uuid = bigmap[node].get('id.uuid', {}).get('value', None)
uuid = bigmap[node].get('id.uuid', {}).get('value', '').lower()
if uuid_is_valid(uuid):
nodes_by_uuid[uuid] = node
fprint = bigmap[node].get(
@@ -47,10 +47,28 @@ class NodeHandler(bmchandler.NodeHandler):
self.info['uuid'] = uuidprefix + '-' + '-'.join(
wronguuid.split('-')[3:])
self.info['uuid'] = self.info['uuid'].lower()
room = slpattrs.get('room-id', [None])[0]
if room:
self.info['room'] = room
rack = slpattrs.get('rack-id', [None])[0]
if rack:
self.info['rack'] = rack
name = slpattrs.get('name', [None])[0]
if name:
self.info['hostname'] = name
unumber = slpattrs.get('lowest-u', [None])[0]
if unumber:
self.info['u'] = unumber
location = slpattrs.get('location', [None])[0]
if location:
self.info['location'] = location
if ff not in ('dense-computing', 'BC2'):
# do not probe unless it's a dense platform
return
self.isdense = True
encuuid = slpattrs.get('chassis-uuid', [None])[0]
if encuuid:
self.info['enclosure.uuid'] = encuuid
slot = int(slpattrs.get('slot', ['0'])[0])
if slot != 0:
self.info['enclosure.bay'] = slot
@@ -27,7 +27,16 @@ import confluent.netutil as netutil
import confluent.util as util
getaddrinfo = eventlet.support.greendns.getaddrinfo
from xml.etree.ElementTree import fromstring
from xml.etree.ElementTree import fromstring as rfromstring
def fromstring(inputdata):
if isinstance(inputdata, bytes):
cmpstr = b'!entity'
else:
cmpstr = '!entity'
if cmpstr in inputdata.lower():
raise Exception('!ENTITY not supported in this interface')
return rfromstring(inputdata)
def fixuuid(baduuid):
# SMM dumps it out in hex
@@ -41,7 +50,7 @@ def fixuuid(baduuid):
class NodeHandler(bmchandler.NodeHandler):
is_enclosure = True
devname = 'SMM'
maxmacs = 6 # support an enclosure, but try to avoid catching daisy chain
maxmacs = 14 # support an enclosure, but try to avoid catching daisy chain
def scan(self):
# the UUID is in a weird order, fix it up to match
@@ -82,6 +91,14 @@ class NodeHandler(bmchandler.NodeHandler):
def _webconfignet(self, wc, nodename):
cfg = self.configmanager
if 'service:lenovo-smm2' in self.info.get('services', []):
# need to enable ipmi for now..
wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x82,0x84)')
rsp = wc.getresponse()
rsp.read()
wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x42,0x44)')
rsp = wc.getresponse()
rsp.read()
cd = cfg.get_node_attributes(
nodename, ['hardwaremanagement.manager'])
smmip = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None)
@@ -196,7 +213,7 @@ class NodeHandler(bmchandler.NodeHandler):
raise Exception('Cannot support default password and setting password rules at same time')
if passwd == 'PASSW0RD':
# We must avoid hitting the web interface due to forced password change, best effert
self._bmcconfig(nodename)
raise Exception('Using the default password is no longer supported')
else:
# Switch to full web based configuration, to mitigate risks with the SMM
wc = self._webconfigcreds(username, passwd)
@@ -229,6 +229,19 @@ class NodeHandler(generic.NodeHandler):
rsp, status = wc.grab_json_response_with_status('/api/session', method='DELETE')
def remote_nodecfg(nodename, cfm):
cfg = cfm.get_node_attributes(
nodename, 'hardwaremanagement.manager')
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
'value', None)
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
if not ipaddr:
raise Excecption('Cannot remote configure a system without known '
'address')
info = {'addresses': [ipaddr]}
nh = NodeHandler(info, cfm)
nh.config(nodename)
if __name__ == '__main__':
import confluent.config.configmanager as cfm
c = cfm.ConfigManager(None)
@@ -39,6 +39,9 @@ def fixup_uuid(uuidprop):
return '-'.join(uuid).upper()
class LockedUserException(Exception):
pass
class NodeHandler(immhandler.NodeHandler):
@@ -66,11 +69,13 @@ class NodeHandler(immhandler.NodeHandler):
if ff not in ('dense-computing', [u'dense-computing']):
# skip preconfig for non-SD530 servers
return
currfirm = self.info.get('attributes', {}).get('firmware-image-info', [''])[0]
if not currfirm.startswith('TEI'):
return
self.trieddefault = None # Reset state on a preconfig attempt
# attempt to enable SMM
#it's normal to get a 'not supported' (193) for systems without an SMM
# need to branch on 3.00+ firmware
currfirm = self.info.get('attributes', {}).get('firmware-image-info', [''])[0]
currfirm = currfirm.split(':')
if len(currfirm) > 1:
currfirm = float(currfirm[1])
@@ -136,8 +141,14 @@ class NodeHandler(immhandler.NodeHandler):
'Content-Type': 'application/json'}
wc.request('POST', '/api/login', adata, headers)
rsp = wc.getresponse()
try:
rspdata = json.loads(rsp.read())
except Exception:
rspdata = {}
if rsp.status != 200 and password == 'PASSW0RD':
rsp.read()
if rspdata.get('locktime', 0) > 0:
raise LockedUserException(
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
adata = json.dumps({
'username': username,
'password': newpassword,
@@ -146,16 +157,21 @@ class NodeHandler(immhandler.NodeHandler):
'Content-Type': 'application/json'}
wc.request('POST', '/api/login', adata, headers)
rsp = wc.getresponse()
try:
rspdata = json.loads(rsp.read())
except Exception:
rspdata = {}
if rsp.status == 200:
pwdchanged = True
password = newpassword
else:
rsp.read()
return (None, None)
if rspdata.get('locktime', 0) > 0:
raise LockedUserException(
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
return (None, rspdata)
if rsp.status == 200:
self._currcreds = (username, password)
wc.set_basic_credentials(username, password)
rspdata = json.loads(rsp.read())
wc.set_header('Content-Type', 'application/json')
wc.set_header('Authorization', 'Bearer ' + rspdata['access_token'])
if '_csrf_token' in wc.cookies:
@@ -178,12 +194,16 @@ class NodeHandler(immhandler.NodeHandler):
wc = self.wc
self.set_password_policy('', wc)
return (wc, pwdchanged)
return (None, None)
elif rspdata.get('locktime', 0) > 0:
raise LockedUserException(
'The user "{0}" has been locked out by too many incorrect password attempts'.format(username))
return (None, rspdata)
@property
def wc(self):
passwd = None
isdefault = True
errinfo = {}
if self._wc is None:
self._wc = webclient.SecureHTTPConnection(
self.ipaddr, 443, verifycallback=self.validate_cert)
@@ -205,6 +225,9 @@ class NodeHandler(immhandler.NodeHandler):
'secret.hardwaremanagementpassword'], decrypt=True)
user, passwd, isdefault = self.get_node_credentials(
nodename, creds, 'USERID', 'PASSW0RD')
if not inpreconfig and isdefault:
raise Exception('Default user/password is not supported. Please set "secret.hardwaremanagementuser" and "secret.hardwaremanagementpassword" for {} to a non-default value. If the XCC is currently at defaults, it will automatically change to the specified values'.format(nodename))
savedexc = None
if not self.trieddefault:
if not passwd:
# So in preconfig context, we don't have admin permission to
@@ -215,7 +238,12 @@ class NodeHandler(immhandler.NodeHandler):
# This is replacing one well known password (PASSW0RD) with another
# (TempW0rd42)
passwd = 'TempW0rd42'
wc, pwdchanged = self.get_webclient('USERID', 'PASSW0RD', passwd)
try:
wc, pwdchanged = self.get_webclient('USERID', 'PASSW0RD', passwd)
except LockedUserException as lue:
wc = None
pwdchanged = 'The user "USERID" has been locked out by too many incorrect password attempts'
savedexc = lue
if wc:
if pwdchanged:
if inpreconfig:
@@ -223,16 +251,26 @@ class NodeHandler(immhandler.NodeHandler):
else:
self._needpasswordchange = False
return wc
else:
errinfo = pwdchanged
self.trieddefault = True
if isdefault:
return
self._atdefaultcreds = False
if self.tmppasswd:
wc, _ = self.get_webclient('USERID', self.tmppasswd, passwd)
if savedexc:
raise savedexc
wc, errinfo = self.get_webclient('USERID', self.tmppasswd, passwd)
else:
wc, _ = self.get_webclient(user, passwd, None)
if user == 'USERID' and savedexc:
raise savedexc
wc, errinfo = self.get_webclient(user, passwd, None)
if wc:
return wc
else:
if errinfo.get('description', '') == 'Invalid credentials':
raise Exception('The stored confluent password for user "{}" was not accepted by the XCC'.format(user))
raise Exception('Error connecting to webservice: ' + repr(errinfo))
def set_password_policy(self, strruleset, wc):
ruleset = {'USER_GlobalMinPassChgInt': '0'}
@@ -286,9 +324,13 @@ class NodeHandler(immhandler.NodeHandler):
wc.grab_json_response('/api/function',
{'USER_UserPassChange': '{0},{1}'.format(uid, passwd)})
if username != 'USERID':
wc.grab_json_response(
rsp, status = wc.grab_json_response_with_status(
'/api/function',
{'USER_UserModify': '{0},{1},,1,4,0,0,0,0,,8,'.format(uid, username)})
if status == 200 and rsp.get('return', 0) == 762:
rsp, status = wc.grab_json_response_with_status(
'/api/function',
{'USER_UserModify': '{0},{1},,1,Administrator,0,0,0,0,,8,'.format(uid, username)})
self.tmppasswd = None
self._currcreds = (username, passwd)
@@ -338,6 +380,10 @@ class NodeHandler(immhandler.NodeHandler):
nwc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
nwc.grab_json_response('/api/providers/logout')
nwc, pwdchanged = self.get_webclient(user, tpass, passwd)
if not nwc:
if not pwdchanged:
pwdchanged = 'Unknown'
raise Exception('Error converting from sha356account: ' + repr(pwdchanged))
if not pwdchanged:
nwc.grab_json_response(
'/api/function',
@@ -431,3 +477,16 @@ class NodeHandler(immhandler.NodeHandler):
if em:
self.configmanager.set_node_attributes(
{em: {'id.uuid': enclosureuuid}})
def remote_nodecfg(nodename, cfm):
cfg = cfm.get_node_attributes(
nodename, 'hardwaremanagement.manager')
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
'value', None)
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
if not ipaddr:
raise Excecption('Cannot remote configure a system without known '
'address')
info = {'addresses': [ipaddr]}
nh = NodeHandler(info, cfm)
nh.config(nodename)
@@ -23,6 +23,8 @@
# option 97 = UUID (wireformat)
import confluent.config.configmanager as cfm
import confluent.collective.manager as collective
import confluent.noderange as noderange
import confluent.log as log
import confluent.netutil as netutil
import ctypes
@@ -264,9 +266,7 @@ def proxydhcp():
if not myipn:
continue
if opts.get(77, None) == b'iPXE':
cfd = cfg.get_node_attributes(node, ('deployment.*'))
profile = cfd.get(node, {}).get(
'deployment.pendingprofile', {}).get('value', None)
profile = get_deployment_profile(node, cfg)
if not profile:
continue
myip = socket.inet_ntoa(myipn)
@@ -423,22 +423,36 @@ def remap_nodes(nodeattribs, configmanager):
for node in updates:
for attrib in updates[node]:
if attrib == 'id.uuid':
uuidmap[updates[node][attrib]['value']] = node
uuidmap[updates[node][attrib]['value'].lower()] = node
elif 'hwaddr' in attrib:
macmap[updates[node][attrib]['value']] = node
macmap[updates[node][attrib]['value'].lower()] = node
def get_deployment_profile(node, cfg, cfd=None):
if not cfd:
cfd = cfg.get_node_attributes(node, ('deployment.*'))
profile = cfd.get(node, {}).get('deployment.pendingprofile', {}).get('value', None)
if not profile:
return None
candmgrs = cfd.get(node, {}).get('collective.managercandidates', {}).get('value', None)
if candmgrs:
candmgrs = noderange.NodeRange(candmgrs, cfg).nodes
if collective.get_myname() not in candmgrs:
return None
return profile
staticassigns = {}
myipbypeer = {}
def check_reply(node, info, packet, sock, cfg, reqview):
httpboot = info['architecture'] == 'uefi-httpboot'
replen = 275 # default is going to be 286
cfd = cfg.get_node_attributes(node, ('deployment.*'))
profile = cfd.get(node, {}).get('deployment.pendingprofile', {}).get('value', None)
myipn = info['netinfo']['recvip']
myipn = socket.inet_aton(myipn)
profile = get_deployment_profile(node, cfg, cfd)
if not profile:
return
myipn = info['netinfo']['recvip']
myipn = socket.inet_aton(myipn)
rqtype = packet[53][0]
insecuremode = cfd.get(node, {}).get('deployment.useinsecureprotocols',
{}).get('value', 'never')
@@ -19,6 +19,7 @@ import confluent.util as util
import confluent.log as log
import os
import random
import eventlet.greenpool
import eventlet.green.select as select
import eventlet.green.socket as socket
import struct
@@ -106,6 +107,8 @@ def _parse_slp_packet(packet, peer, rsps, xidmap):
if '%' in addr:
addr = addr[:addr.index('%')]
mac = None
if addr not in neighutil.neightable:
neighutil.update_neigh()
if addr in neighutil.neightable:
identifier = neighutil.neightable[addr]
mac = identifier
@@ -360,6 +363,9 @@ def _add_attributes(parsed):
return
def unicast_scan(address):
pass
def query_srvtypes(target):
"""Query the srvtypes advertised by the target
@@ -465,15 +471,16 @@ def snoop(handler, protocol=None):
# will now yield dupe info over time
known_peers = set([])
peerbymacaddress = {}
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
ip = peer[0].partition('%')[0]
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
if ip not in neighutil.neightable:
neighutil.update_neigh()
if ip not in neighutil.neightable:
continue
known_peers.add(peer)
mac = neighutil.neightable[ip]
if mac in peerbymacaddress:
@@ -528,6 +535,8 @@ def active_scan(handler, protocol=None):
for scanned in scan():
for addr in scanned['addresses']:
ip = addr[0].partition('%')[0] # discard scope if present
if ip not in neighutil.neightable:
neighutil.update_neigh()
if ip not in neighutil.neightable:
continue
if addr in known_peers:
@@ -579,6 +588,8 @@ def scan(srvtypes=_slp_services, addresses=None, localonly=False):
# reduced chance of many responses overwhelming receive buffer.
_grab_rsps((net, net4), rsps, 1, xidmap)
# now to analyze and flesh out the responses
handleids = set([])
gp = eventlet.greenpool.GreenPool(128)
for id in rsps:
for srvurl in rsps[id].get('urls', ()):
if len(srvurl) > 4:
@@ -593,14 +604,22 @@ def scan(srvtypes=_slp_services, addresses=None, localonly=False):
break
else:
continue
_add_attributes(rsps[id])
gp.spawn_n(_add_attributes, rsps[id])
handleids.add(id)
gp.waitall()
for id in handleids:
if 'service:lighttpd' in rsps[id]['services']:
currinf = rsps[id]
curratt = currinf.get('attributes', {})
if curratt.get('System-Manufacturing', [None])[0] == 'Lenovo' and curratt.get('type', [None])[0] == 'LenovoThinkServer':
currinf['services'] = ['service:lenovo-tsm']
curratt['enclosure-serial-number'] = curratt['Product-Serial']
curratt['enclosure-machinetype-model'] = curratt['Machine-Type']
serialnumber = curratt.get('Product-Serial', curratt.get('SerialNumber', None))
if serialnumber:
curratt['enclosure-serial-number'] = serialnumber
mtm = curratt.get('Machine-Type', curratt.get('Product-Name', None))
if mtm:
mtm[0] = mtm[0].rstrip()
curratt['enclosure-machinetype-model'] = mtm
else:
continue
del rsps[id]['payload']
@@ -29,12 +29,15 @@
import confluent.config.configmanager as cfm
import confluent.collective.manager as collective
import confluent.neighutil as neighutil
import confluent.noderange as noderange
import confluent.util as util
import confluent.log as log
import confluent.netutil as netutil
import eventlet.green.select as select
import eventlet.green.socket as socket
import eventlet.greenpool as gp
import time
try:
from eventlet.green.urllib.request import urlopen
@@ -59,6 +62,8 @@ def active_scan(handler, protocol=None):
for scanned in scan(['urn:dmtf-org:service:redfish-rest:1']):
for addr in scanned['addresses']:
ip = addr[0].partition('%')[0] # discard scope if present
if ip not in neighutil.neightable:
neighutil.update_neigh()
if ip not in neighutil.neightable:
continue
if addr in known_peers:
@@ -118,7 +123,6 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
newmacs = set([])
machandlers = {}
r, _, _ = select.select((net4, net6), (), (), 60)
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
@@ -128,10 +132,12 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
method, _, _ = rsp[0].split(b' ', 2)
if method == b'NOTIFY':
ip = peer[0].partition('%')[0]
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
if ip not in neighutil.neightable:
neighutil.update_neigh()
if ip not in neighutil.neightable:
continue
mac = neighutil.neightable[ip]
known_peers.add(peer)
newmacs.add(mac)
@@ -184,10 +190,15 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
# planned for
cfg = cfm.ConfigManager(None)
cfd = cfg.get_node_attributes(
node, 'deployment.pendingprofile')
node, ['deployment.pendingprofile', 'collective.managercandidates'])
if not cfd.get(node, {}).get(
'deployment.pendingprofile', {}).get('value', None):
break
candmgrs = cfd.get(node, {}).get('collective.managercandidates', {}).get('value', None)
if candmgrs:
candmgrs = noderange.NodeRange(candmgrs, cfg).nodes
if collective.get_myname() not in candmgrs:
break
currtime = time.time()
seconds = int(currtime)
msecs = int(currtime * 1000 % 1000)
@@ -200,6 +211,8 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
cfg, node, ifidx=iface)
if ncfg.get('matchesnodename', None):
reply += 'DEFAULTNET: 1\r\n'
elif not netutil.address_is_local(peer[0]):
continue
if not isinstance(reply, bytes):
reply = reply.encode('utf8')
s.sendto(reply, peer)
@@ -274,19 +287,34 @@ def _find_service(service, target):
if timeout < 0:
timeout = 0
r, _, _ = select.select((net4, net6), (), (), timeout)
querypool = gp.GreenPool()
pooltargs = []
for nid in peerdata:
for url in peerdata[nid].get('urls', ()):
if url.endswith('/desc.tmpl'):
info = urlopen(url).read()
if b'<friendlyName>Athena</friendlyName>' in info:
peerdata[nid]['services'] = ['service:thinkagile-storage']
yield peerdata[nid]
pooltargs.append((url, peerdata[nid]))
for pi in querypool.imap(check_cpstorage, pooltargs):
if pi is not None:
yield pi
def check_cpstorage(urldata):
url, data = urldata
try:
info = urlopen(url, timeout=1).read()
if b'<friendlyName>Athena</friendlyName>' in info:
data['services'] = ['service:thinkagile-storage']
return data
except Exception:
pass
return None
def _parse_ssdp(peer, rsp, peerdata):
ip = peer[0].partition('%')[0]
nid = ip
mac = None
if ip not in neighutil.neightable:
neighutil.update_neigh()
if ip in neighutil.neightable:
nid = neighutil.neightable[ip]
mac = nid
+3
View File
@@ -34,6 +34,8 @@ def handle_connection(incoming, outgoing):
for mysock in r:
data = mysock.recv(32768)
if not data:
incoming.close()
outgoing.close()
return
if mysock == incoming:
outgoing.sendall(data)
@@ -72,6 +74,7 @@ def forward_video():
vidclient.setsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG, 1456)
except Exception:
conn.close()
vidclient.close()
continue
eventlet.spawn_n(handle_connection, conn, vidclient)
+4 -15
View File
@@ -65,16 +65,6 @@ opmap = {
}
class RobustCookie(Cookie.SimpleCookie):
# this is very bad form, but BaseCookie has a terrible flaw
def _BaseCookie__set(self, K, rval, cval):
try:
super(RobustCookie, self)._BaseCookie__set(K, rval, cval)
except Cookie.CookieError:
# empty value if SimpleCookie rejects
dict.__setitem__(self, K, Cookie.Morsel())
def group_creation_resources():
yield confluent.messages.Attributes(
kv={'name': None}, desc="Name of the group").html() + '<br>'
@@ -284,11 +274,10 @@ def _authorize_request(env, operation):
if element.startswith('/sessions/current/'):
element = None
if 'HTTP_COOKIE' in env:
#attempt to use the cookie. If it matches
cc = RobustCookie()
cc.load(env['HTTP_COOKIE'])
if 'confluentsessionid' in cc:
sessionid = cc['confluentsessionid'].value
cidx = (env['HTTP_COOKIE']).find('confluentsessionid=')
if cidx >= 0:
sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51]
sessid = sessionid
sessid = sessionid
if sessionid in httpsessions:
if _csrf_valid(env, httpsessions[sessionid]):
+1
View File
@@ -1147,6 +1147,7 @@ class BootDevice(ConfluentChoiceMessage):
'default',
'cd',
'floppy',
'usb',
])
valid_bootmodes = set([
+5 -2
View File
@@ -82,8 +82,11 @@ _idxtoifnamemap = {}
def _rebuildidxmap():
_idxtoifnamemap.clear()
for iname in os.listdir('/sys/class/net'):
ci = int(open('/sys/class/net/{0}/ifindex'.format(iname)).read())
_idxtoifnamemap[ci] = iname
try:
ci = int(open('/sys/class/net/{0}/ifindex'.format(iname)).read())
_idxtoifnamemap[ci] = iname
except Exception: # there may be non interface in /sys/class/net
pass
def myiptonets(svrip):
+16 -10
View File
@@ -139,7 +139,10 @@ def get_fingerprint(switch, port, configmanager, portmatch):
continue
if info.get('switch', None) != switch:
continue
if portmatch(info.get('port'), port):
if portmatch(info.get('portid', None), port):
return ('sha256$' + b64tohex(info['peersha256fingerprint']),
info.get('verified', False))
elif portmatch(info.get('port', None), port):
return ('sha256$' + b64tohex(info['peersha256fingerprint']),
info.get('verified', False))
return None, False
@@ -148,11 +151,10 @@ def get_fingerprint(switch, port, configmanager, portmatch):
def _extract_extended_desc(info, source, integritychecked):
source = str(source)
info['verified'] = bool(integritychecked)
if source.startswith('Lenovo SMM;'):
info['peerdescription'] = 'Lenovo SMM'
if ';S2=' in source:
info['peersha256fingerprint'] = source.replace('Lenovo SMM;S2=',
'')
if source.startswith('Lenovo ') and ';S2=' in source:
desc, fprint = source.split(';S2=', 1)
info['peerdescription'] = desc
info['peersha256fingerprint'] = fprint
else:
info['peerdescription'] = source
@@ -236,19 +238,23 @@ def _extract_neighbor_data_b(args):
idx = oidindex[0][-1]
idxtoifname[idx] = _lldpdesc_to_ifname(sid, idx, str(oidindex[1]))
for remotedesc in conn.walk('1.0.8802.1.1.2.1.4.1.1.10'):
iname = idxtoifname[remotedesc[0][-2]]
iname = idxtoifname.get(remotedesc[0][-2],
idxtoportid[remotedesc[0][-2]])
_init_lldp(lldpdata, iname, remotedesc[0][-2], idxtoportid, switch)
_extract_extended_desc(lldpdata[iname], remotedesc[1], user)
for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.9'):
iname = idxtoifname[remotename[0][-2]]
iname = idxtoifname.get(remotename[0][-2],
idxtoportid[remotename[0][-2]])
_init_lldp(lldpdata, iname, remotename[0][-2], idxtoportid, switch)
lldpdata[iname]['peername'] = str(remotename[1])
for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.7'):
iname = idxtoifname[remotename[0][-2]]
iname = idxtoifname.get(remotename[0][-2],
idxtoportid[remotename[0][-2]])
_init_lldp(lldpdata, iname, remotename[0][-2], idxtoportid, switch)
lldpdata[iname]['peerportid'] = sanitize(remotename[1])
for remoteid in conn.walk('1.0.8802.1.1.2.1.4.1.1.5'):
iname = idxtoifname[remoteid[0][-2]]
iname = idxtoifname.get(remoteid[0][-2],
idxtoportid[remoteid[0][-2]])
_init_lldp(lldpdata, iname, remoteid[0][-2], idxtoportid, switch)
lldpdata[iname]['peerchassisid'] = sanitize(remoteid[1])
for entry in lldpdata:
+48 -15
View File
@@ -82,6 +82,8 @@ _blacklistnames = (
def _namesmatch(switchdesc, userdesc):
if switchdesc is None:
return False
if switchdesc == userdesc:
return True
try:
@@ -138,7 +140,9 @@ def _affluent_map_switch(args):
wc = webclient.SecureHTTPConnection(
switch, 443, verifycallback=kv, timeout=5)
wc.set_basic_credentials(user, password)
macs = wc.grab_json_response('/affluent/macs/by-port')
macs, retcode = wc.grab_json_response_with_status('/affluent/macs/by-port')
if retcode != 200:
raise Exception("No affluent detected")
_macsbyswitch[switch] = macs
for iface in macs:
@@ -193,6 +197,7 @@ def _map_switch_backend(args):
haveqbridge = False
mactobridge = {}
conn = snmp.Session(switch, password, user)
ifnamemap = get_portnamemap(conn)
for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'):
haveqbridge = True
oid, bridgeport = vb
@@ -214,16 +219,37 @@ def _map_switch_backend(args):
*([int(x) for x in oid[-6:]])
)
mactobridge[macaddr] = int(bridgeport)
vlanstocheck = set([])
try:
#ciscoiftovlanmap = {}
for vb in conn.walk('.1.3.6.1.4.1.9.9.68.1.2.2.1.2'):
vlanstocheck.add(vb[1])
#ciscotrunktovlanmap = {}
for vb in conn.walk('.1.3.6.1.4.1.9.9.46.1.6.1.1.5'):
vlanstocheck.add(vb[1])
except Exception:
# We might have crashed snmp on a non-cisco switch
# in such a case, delay 8 seconds to allow recovery to complete
eventlet.sleep(8)
if not vlanstocheck:
vlanstocheck.add(None)
bridgetoifmap = {}
for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'):
bridgeport, ifidx = vb
bridgeport = int(str(bridgeport).rsplit('.', 1)[1])
try:
bridgetoifmap[bridgeport] = int(ifidx)
except ValueError:
# ifidx might be '', skip in such a case
continue
ifnamemap = get_portnamemap(conn)
for vlan in vlanstocheck:
if vlan:
if user:
conn = snmp.Session(switch, password, user, 'vlan-{}'.format(vlan))
else:
if not isinstance(password, str):
password = password.decode('utf8')
conn = snmp.Session(switch, '{}@{}'.format(password, vlan))
for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'):
bridgeport, ifidx = vb
bridgeport = int(str(bridgeport).rsplit('.', 1)[1])
try:
bridgetoifmap[bridgeport] = int(ifidx)
except ValueError:
# ifidx might be '', skip in such a case
continue
maccounts = {}
bridgetoifvalid = False
for mac in mactobridge:
@@ -375,12 +401,19 @@ def _full_updatemacmap(configmanager):
continue
if curswitch not in _switchportmap:
_switchportmap[curswitch] = {}
if portname in _switchportmap[curswitch]:
log.log({'error': 'Duplicate switch topology config '
'for {0} and {1}'.format(
node,
if (portname in _switchportmap[curswitch] and
_switchportmap[curswitch][portname] != node):
if _switchportmap[curswitch][portname] is None:
errstr = ('Duplicate switch attributes for {0} and '
'a previously logged duplicate'.format(
node))
else:
errstr = ('Duplicate switch topology config '
'for {0} and {1}'.format(
node,
_switchportmap[curswitch][
portname])})
portname]))
log.log({'error': errstr})
_switchportmap[curswitch][portname] = None
else:
_switchportmap[curswitch][portname] = node
+61 -7
View File
@@ -40,6 +40,22 @@ from libarchive.ffi import (
def relax_umask():
os.umask(0o22)
def makedirs(path, mode):
try:
os.makedirs(path, 0o755)
except OSError as e:
if e.errno != 17:
raise
def symlink(src, targ):
try:
os.symlink(src, targ)
except OSError as e:
if e.errno != 17:
raise
def update_boot(profilename):
if profilename.startswith('/var/lib/confluent/public'):
profiledir = profilename
@@ -59,6 +75,7 @@ def update_boot(profilename):
update_boot_esxi(profiledir, profile, label)
def update_boot_esxi(profiledir, profile, label):
profname = os.path.basename(profiledir)
kernelargs = profile.get('kernelargs', '')
oum = os.umask(0o22)
bootcfg = open('{0}/distribution/BOOT.CFG'.format(profiledir), 'r').read()
@@ -89,7 +106,7 @@ def update_boot_esxi(profiledir, profile, label):
else:
newbootcfg += cfgline + '\n'
efibootcfg += cfgline + '\n'
os.makedirs('{0}/boot/efi/boot/'.format(profiledir), 0o755)
makedirs('{0}/boot/efi/boot/'.format(profiledir), 0o755)
bcfgout = os.open('{0}/boot/efi/boot/boot.cfg'.format(profiledir), os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o644)
bcfg = os.fdopen(bcfgout, 'w')
try:
@@ -102,7 +119,7 @@ def update_boot_esxi(profiledir, profile, label):
bcfg.write(newbootcfg)
finally:
bcfg.close()
os.symlink('/var/lib/confluent/public/site/initramfs.tgz',
symlink('/var/lib/confluent/public/site/initramfs.tgz',
'{0}/boot/site.tgz'.format(profiledir))
for fn in filesneeded:
if fn.startswith('/'):
@@ -110,8 +127,10 @@ def update_boot_esxi(profiledir, profile, label):
sourcefile = '{0}/distribution/{1}'.format(profiledir, fn)
if not os.path.exists(sourcefile):
sourcefile = '{0}/distribution/{1}'.format(profiledir, fn.upper())
os.symlink(sourcefile, '{0}/boot/{1}'.format(profiledir, fn))
os.symlink('{0}/distribution/EFI/BOOT/BOOTX64.EFI'.format(profiledir), '{0}/boot/efi/boot/bootx64.efi'.format(profiledir))
symlink(sourcefile, '{0}/boot/{1}'.format(profiledir, fn))
symlink('{0}/distribution/EFI/BOOT/BOOTX64.EFI'.format(profiledir), '{0}/boot/efi/boot/bootx64.efi'.format(profiledir))
if os.path.exists('{0}/distribution/EFI/BOOT/CRYPTO64.EFI'.format(profiledir)):
symlink('{0}/distribution/EFI/BOOT/CRYPTO64.EFI'.format(profiledir), '{0}/boot/efi/boot/crypto64.efi'.format(profiledir))
ipout = os.open(profiledir + '/boot.ipxe', os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o644)
ipxeout = os.fdopen(ipout, 'w')
try:
@@ -124,10 +143,11 @@ def update_boot_esxi(profiledir, profile, label):
ipxeout.close()
subprocess.check_call(
['/opt/confluent/bin/dir2img', '{0}/boot'.format(profiledir),
'{0}/boot.img'.format(profiledir)], preexec_fn=relax_umask)
'{0}/boot.img'.format(profiledir), profname], preexec_fn=relax_umask)
def update_boot_linux(profiledir, profile, label):
profname = os.path.basename(profiledir)
kernelargs = profile.get('kernelargs', '')
grubcfg = "set timeout=5\nmenuentry '"
grubcfg += label
@@ -162,7 +182,7 @@ def update_boot_linux(profiledir, profile, label):
ipxeout.close()
subprocess.check_call(
['/opt/confluent/bin/dir2img', '{0}/boot'.format(profiledir),
'{0}/boot.img'.format(profiledir)], preexec_fn=relax_umask)
'{0}/boot.img'.format(profiledir), profname], preexec_fn=relax_umask)
def extract_entries(entries, flags=0, callback=None, totalsize=None, extractlist=None):
@@ -213,6 +233,7 @@ def check_centos(isoinfo):
ver = None
arch = None
cat = None
isstream = ''
for entry in isoinfo[0]:
if 'centos-release-7' in entry:
dotsplit = entry.split('.')
@@ -225,9 +246,25 @@ def check_centos(isoinfo):
arch = entry.split('.')[-2]
cat = 'el8'
break
elif 'centos-stream-release-8' in entry:
ver = entry.split('-')[3]
arch = entry.split('.')[-2]
cat = 'el8'
isstream = '_stream'
break
elif 'centos-linux-release-8' in entry:
ver = entry.split('-')[3]
arch = entry.split('.')[-2]
cat = 'el8'
break
else:
return None
return {'name': 'centos-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': cat}
if arch == 'noarch' and '.discinfo' in isoinfo[1]:
prodinfo = isoinfo[1]['.discinfo']
arch = prodinfo.split(b'\n')[2]
if not isinstance(arch, str):
arch = arch.decode('utf-8')
return {'name': 'centos{2}-{0}-{1}'.format(ver, arch, isstream), 'method': EXTRACT, 'category': cat}
def check_esxi(isoinfo):
if '.DISCINFO' not in isoinfo[1]:
@@ -324,9 +361,26 @@ def check_sles(isoinfo):
return None
def _priv_check_oraclelinux(isoinfo):
ver = None
arch = None
for entry in isoinfo[0]:
if 'oraclelinux-release-' in entry and 'release-el7' not in entry:
ver = entry.split('-')[2]
arch = entry.split('.')[-2]
break
else:
return None
major = ver.split('.', 1)[0]
return {'name': 'oraclelinux-{0}-{1}'.format(ver, arch), 'method': EXTRACT,
'category': 'el{0}'.format(major)}
def check_rhel(isoinfo):
ver = None
arch = None
isoracle = _priv_check_oraclelinux(isoinfo)
if isoracle:
return isoracle
for entry in isoinfo[0]:
if 'redhat-release-7' in entry:
dotsplit = entry.split('.')
@@ -110,7 +110,7 @@ def exithandler():
atexit.register(exithandler)
_ipmiworkers = greenpool.GreenPool()
_ipmiworkers = greenpool.GreenPool(128)
_ipmithread = None
_ipmiwaiters = []
+77 -11
View File
@@ -1,12 +1,16 @@
import confluent.config.configmanager as configmanager
import confluent.collective.manager as collective
import confluent.netutil as netutil
import confluent.noderange as noderange
import confluent.sshutil as sshutil
import confluent.util as util
import eventlet.green.socket as socket
import eventlet.green.subprocess as subprocess
import confluent.discovery.handlers.xcc as xcc
import confluent.discovery.handlers.tsm as tsm
import crypt
import json
import os
import time
import yaml
@@ -199,7 +203,7 @@ def handle_request(env, start_response):
start_response('200 OK', (('Content-Type', 'text/plain'),))
yield cert
elif env['PATH_INFO'] == '/self/nodelist':
nodes, _ = get_cluster_list(cfg)
nodes, _ = get_cluster_list(nodename, cfg)
if isgeneric:
start_response('200 OK', (('Content-Type', 'text/plain'),))
for node in util.natural_sort(nodes):
@@ -207,6 +211,24 @@ def handle_request(env, start_response):
else:
start_response('200 OK', (('Content-Type', retype),))
yield dumper(sorted(nodes))
elif env['PATH_INFO'] == '/self/remoteconfigbmc':
if reqbody:
try:
reqbody = yaml.safe_load(reqbody)
except Exception:
reqbody = None
if not reqbody:
start_response('400 bad request', ())
cfgmod = reqbody.get('configmod', 'unspecified')
if cfgmod == 'xcc':
xcc.remote_nodecfg(nodename, cfg)
elif cfgmod == 'tsm':
tsm.remote_nodecfg(nodename, cfg)
else:
start_response('500 unsupported configmod', ())
yield 'Unsupported configmod "{}"'.format(cfgmod)
start_response('200 Ok', ())
yield 'complete'
elif env['PATH_INFO'] == '/self/updatestatus':
update = yaml.safe_load(reqbody)
if update['status'] == 'staged':
@@ -236,15 +258,58 @@ def handle_request(env, start_response):
else:
start_response('500 Error', (('Content-Type', 'text/plain'),))
yield 'No pending profile detected, unable to accept status update'
elif env['PATH_INFO'] == '/self/saveapikey':
cfg.set_node_attributes({
nodename: {'deployment.sealedapikey': {'value': reqbody}}})
start_response('200 OK', ())
yield ''
elif env['PATH_INFO'].startswith('/self/scriptlist/'):
scriptcat = env['PATH_INFO'].replace('/self/scriptlist/', '')
if '..' in scriptcat:
start_response('400 Bad Requst', ())
yield ''
return
deployinfo = cfg.get_node_attributes(
nodename, ('deployment.*',))
deployinfo = deployinfo.get(nodename, {})
profile = deployinfo.get(
'deployment.pendingprofile', {}).get('value', '')
if not profile:
profile = deployinfo.get(
'deployment.stagedprofile', {}).get('value', '')
if not profile:
profile = deployinfo.get(
'deployment.profile', {}).get('value', '')
slist = None
try:
slist = os.listdir('/var/lib/confluent/public/os/{0}/scripts/{1}.d/'.format(profile, scriptcat))
except OSError:
pass
if slist:
start_response('200 OK', (('Content-Type', 'application/yaml'),))
yield yaml.safe_dump(util.natural_sort(slist), default_flow_style=False)
else:
start_response('200 OK', ())
yield ''
else:
start_response('404 Not Found', ())
yield 'Not found'
def get_cluster_list(cfg=None):
def get_cluster_list(nodename=None, cfg=None):
if cfg is None:
cfg = configmanager.ConfigManager(None)
nodes = set(cfg.list_nodes())
nodes = None
if nodename is not None:
sshpeers = cfg.get_node_attributes(nodename, 'ssh.trustnodes')
sshpeers = sshpeers.get(nodename, {}).get('ssh.trustnodes', {}).get(
'value', None)
if sshpeers:
nodes = noderange.NodeRange(sshpeers, cfg).nodes
autonodes = False
if nodes is None:
autonodes = True
nodes = set(cfg.list_nodes())
domain = None
for node in list(util.natural_sort(nodes)):
if domain is None:
@@ -253,12 +318,13 @@ def get_cluster_list(cfg=None):
'value', None)
for extraname in get_extra_names(node, cfg):
nodes.add(extraname)
for mgr in configmanager.list_collective():
nodes.add(mgr)
if domain and domain not in mgr:
nodes.add('{0}.{1}'.format(mgr, domain))
myname = collective.get_myname()
nodes.add(myname)
if domain and domain not in myname:
nodes.add('{0}.{1}'.format(myname, domain))
if autonodes:
for mgr in configmanager.list_collective():
nodes.add(mgr)
if domain and domain not in mgr:
nodes.add('{0}.{1}'.format(mgr, domain))
myname = collective.get_myname()
nodes.add(myname)
if domain and domain not in myname:
nodes.add('{0}.{1}'.format(myname, domain))
return nodes, domain
+1 -1
View File
@@ -79,7 +79,7 @@ def randomstring(length=20):
if length % 4 > 0:
chunksize += 1
strval = base64.urlsafe_b64encode(os.urandom(chunksize * 3))
return stringify(strval[0:length-1])
return stringify(strval[0:length])
def securerandomnumber(low=0, high=4294967295):
+8 -4
View File
@@ -13,11 +13,15 @@ rpmbuild -bb confluent-genesis.spec
rm -rf /usr/lib/dracut/modules.d/97genesis
cd -
# getting src rpms would be nice, but centos isn't consistent..
# skipcpio | xzcat | cpio -dumiv
# rpm -qf $(find . -type f | sed -e 's/^.//') |sort -u|grep -v 'not owned' > rpmlist
# /usr/lib/dracut/skipcpio /opt/confluent/genesis/x86_64/boot/initramfs/distribution | xzcat | cpio -dumiv
# rpm -qf $(find . -type f | sed -e 's/^.//') |sort -u|grep -v 'not owned' > ../rpmlist
# for f in $(find . -type f | sed -e 's/^.//'); do echo -n $f:; rpm -qf $f ; done > ../annotedrprmlist
# for i in $(cat rpmlist); do rpm -qi $i|grep Source; done |awk '{print $4}'|sort -u > srcrpmlist
# for i in $(cat ../srcrpmlist); do wget http://vault.centos.org/8.2.2004/BaseOS/Source/SPackages/$i; done
# http://vault.centos.org/8.2.2004/AppStream/Source/SPackages/$i
# for i in $(cat ../srcrpmlist); do wget --continue http://vault.centos.org/8.2.2004/BaseOS/Source/SPackages/$i; done
# ls > downloadedsrcpmlist
# diff -u srcpmlist downloadedsrcrpmlist
# diff -u srcrpmlist downloadedsrcpmrlist |grep ^-|grep -v srcrpmlist
# for i in $(diff -u srcrpmlist downloadedsrcpmrlist |grep ^-|grep -v srcrpmlist|sed -e s/-//); do wget --continue http://vault.centos.org/8.2.2004/AppStream/Source/SPackages/$i; done
+1 -1
View File
@@ -1,5 +1,5 @@
%define arch x86_64
Version: 3.0.0
Version: 3.1.0
Release: 1
Name: confluent-genesis-%{arch}
BuildArch: noarch
+2 -2
View File
@@ -39,7 +39,7 @@ def makeboot_tree(distribution, profiledir):
os.link('/var/lib/confluent/public/site/site-initramfs.gz', trginitramfs)
profileinfo = os.path.join(profiledir, 'profile.yaml')
with open(profileinfo) as info:
profile = yaml.load(info)
profile = yaml.safe_load(info)
cfgfile = os.path.join(efidir, 'grub.cfg')
with open(cfgfile, 'w') as grubcfg:
grubcfg.write('set timeout=5\n')
@@ -57,4 +57,4 @@ def makeboot_tree(distribution, profiledir):
if __name__ == '__main__':
makeboot_tree(sys.argv[1], sys.argv[2])
makeboot_tree(sys.argv[1], sys.argv[2])
+19
View File
@@ -0,0 +1,19 @@
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
# and modify the script below if wanting to use the iso instead of tgz
# It checks for mellanox devices and opts not to install, so this script could be added
# to a general profile without causing mofed to install on non-mellanox systems
. /etc/confluent/functions
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
# Uncomment the following three lines and comment out the next
# two lines to use the .iso instead of the tgz packaging
#fetch_remote ofed/ofed.iso
#mkdir MLNX_OFED
#mount -o loop ofed.iso MLNX_OFED
fetch_remote mofed/mofed.tgz
tar xf mofed.tgz
# The rest is common between tar and iso
cd MLNX_OFED*
mount -o loop ofed
./mlnxofedinstall --force
fi
@@ -0,0 +1,18 @@
#!/bin/bash
mkdir -p /etc/pki/tls/certs
echo -n "" >> /tmp/net.ifaces
cat /tls/*.0 >> /etc/pki/tls/certs/ca-bundle.crt
if ! grep console= /proc/cmdline >& /dev/null; then
autocons=$(/opt/confluent/bin/autocons)
if [ -n "$autocons" ]; then
echo console=$autocons |sed -e 's!/dev/!!' >> /tmp/01-autocons.conf
autocons=${autocons%,*}
echo $autocons > /tmp/01-autocons.devnode
echo "Detected firmware specified console at $(cat /tmp/01-autocons.conf)" > $autocons
echo "Modify profile.yaml and run updateboot to have nodeconsole work by adding console=$(cat /tmp/01-autocons.conf)" > $autocons
fi
fi
if grep console=ttyS /proc/cmdline >& /dev/null; then
echo "Serial console has been requested in the kernel arguments, the local video may not show progress" > /dev/tty1
fi
@@ -0,0 +1,126 @@
#!/bin/sh
[ -e /tmp/confluent.initq ] && return 0
if [ -f /tmp/dd_disk ]; then
for dd in $(cat /tmp/dd_disk); do
if [ -e $dd ]; then
driver-updates --disk $dd $dd
fi
done
fi
TRIES=0
oum=$(umask)
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
ip link set $currif up
done
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
done
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
mkdir -p /var/log/xcat
#TODO: blkid --label <whatever> to find mounted api
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
mgr=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info| sed -e 's/^EXTMGRINFO: //' | awk -F'|' '{print $1 " " $2 " " $3}' |grep 1$ | awk 'NR < 2')
if [ -z "$mgr" ]; then
mgr=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info| sed -e 's/^EXTMGRINFO: //' | awk -F'|' '{print $1 " " $2 " " $3}' | awk 'NR < 2')
fi
mgtiface=$(echo $mgr | awk '{print $2}')
mgr=$(echo $mgr | awk '{print $1}')
if [ ! -f /etc/confluent/confluent.apikey ]; then
/opt/confluent/bin/clortho $nodename $mgr > /etc/confluent/confluent.apikey
fi
if echo $mgr | grep '%' > /dev/null; then
echo $mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
fi
apikey=$(cat /etc/confluent/confluent.apikey)
if echo $mgr | grep ':' > /dev/null; then
mgr="[$mgr]"
fi
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -H "CONFLUENT_MGTIFACE: $mgtiface" https://$mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
cat /tls/*.pem > /etc/confluent/ca.pem
ifidx=$(cat /tmp/confluent.ifidx)
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
ifname=${ifname%:}
echo $ifname > /tmp/net.ifaces
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
dnsdomain=${dnsdomain#dnsdomain: }
hostname=$nodename
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
hostname=$hostname.$dnsdomain
fi
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
mgr=${mgr#deploy_server: }
if ! grep XCAT /proc/cmdline > /dev/null; then
echo XCAT=$mgr:3001 >> /etc/cmdline.d/01-confluent.conf
fi
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
profilename=${profilename#profile: }
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
proto=${proto#protocol: }
textconsole=$(grep ^textconsole: /etc/confluent/confluent.deploycfg)
textconsole=${textconsole#textconsole: }
if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
autocons=$(cat /tmp/01-autocons.devnode)
if [ ! -z "$autocons" ]; then
echo Auto-configuring installed system to use text console
echo Auto-configuring installed system to use text console > $autocons
cp /tmp/01-autocons.conf /etc/cmdline.d/
else
echo "Unable to automatically detect requested text console"
fi
fi
echo imgurl=$proto://$mgr/confluent-public/os/$profilename/rootimg.cpio.gz >> /etc/cmdline.d/01-confluent.conf
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
autoconfigmethod=${autoconfigmethod#ipv4_method: }
if [ "$autoconfigmethod" = "dhcp" ]; then
echo ip=$ifname:dhcp >> /etc/cmdline.d/01-confluent.conf
else
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 ipv4_netmask: /etc/confluent/confluent.deploycfg)
v4nm=${v4nm#ipv4_netmask: }
echo ip=$v4addr::$v4gw:$v4nm:$hostname:$ifname:none >> /etc/cmdline.d/01-confluent.conf
mkdir -p /etc/sysconfig/network-scripts
ifcfg=/etc/sysconfig/network-scripts/ifcfg-$ifname
echo DEVICE=$ifname >> $ifcfg
echo NAME=$ifname >> $ifcfg
echo IPADDR=$v4addr >> $ifcfg
echo GATEWAY=$v4gw >> $ifcfg
echo NETMASK=$v4nm >> $ifcfg
fi
nameserversec=0
while read -r entry; do
if [ $nameserversec = 1 ]; then
if [[ $entry == "-"* ]] && [[ $entry != "- ''" ]]; then
echo nameserver=${entry#- } >> /etc/cmdline.d/01-confluent.conf
continue
fi
fi
nameserversec=0
if [ "${entry%:*}" = "nameservers" ]; then
nameserversec=1
continue
fi
done < /etc/confluent/confluent.deploycfg
@@ -0,0 +1,40 @@
#!/bin/bash
BUNDLENAME=/sysroot/etc/pki/tls/certs/ca-bundle.crt
while [ -h $BUNDLENAME ]; do
BUNDLENAME=/sysroot/$(readlink $BUNDLENAME)
done
cat /tls/*.0 >> $BUNDLENAME
mkdir -p /sysroot/etc/confluent/
chmod 700 /sysroot/etc/confluent
cp -a /tls /sysroot/etc/confluent
cp /etc/confluent/* /sysroot/etc/confluent
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg | awk '{print $2}')
if [ "null" = "$rootpw" -o "" = $rootpw ]; then
rootpw='*'
fi
sed -i "s!root:[^:]*:!root:$rootpw:!" /sysroot/etc/shadow
mkdir -p /sysroot/root/.ssh
chmod 700 /sysroot/root/.ssh
cat /ssh/*.rootpubkey > /sysroot/root/.ssh/authorized_keys
chmod 600 /sysroot/root/.ssh/authorized_keys
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
cp /etc/sysconfig/network-scripts/* /sysroot/etc/sysconfig/network-scripts/
ifname=$(ip link|grep ^$(cat /tmp/confluent.ifidx) | awk '{print $2}'|sed -e 's/://')
mkdir /sysroot/tmp
ip link set $ifname down; ip link set $ifname up
while ! ip addr show dev $ifname|grep fe80 > /dev/null; do
sleep 0.1
done
while ip addr|grep tentative > /dev/null; do
sleep 0.1
done
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export mgr profile
curl -Ssf https://$mgr/confluent-public/os/$profile/scripts/earlyboot.sh > /sysroot/etc/confluent/earlyboot.sh
chroot /sysroot bash /etc/confluent/earlyboot.sh
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info|awk '{print $2}')
export mgr profile nodename
curl -sSf https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions
. /tmp/functions
run_remote setupssh.sh
+38
View File
@@ -0,0 +1,38 @@
run_remote() {
requestedcmd="'$*'"
echo
echo '---------------------------------------------------------------------------'
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $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 >& /dev/null
fi
shift
./$cmd $*
retcode=$?
echo "$requestedcmd exited with code $retcode"
cd - > /dev/null
return $retcode
}
run_remote_python() {
echo
echo '---------------------------------------------------------------------------'
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
tmpdir=$(mktemp -d)
echo Executing in $tmpdir
cd $tmpdir
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
/usr/libexec/platform-python $*
retcode=$?
echo "'$*' exited with code $retcode"
cd - > /dev/null
return $retcode
}
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
rm /etc/ssh/*host*key* >& /dev/null
ssh-keygen -A
/usr/libexec/platform-python /etc/confluent/apiclient >& /dev/null
for pubkey in /etc/ssh/ssh_host*key.pub; do
certfile=${pubkey/.pub/-cert.pub}
/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/sshcert $pubkey > $certfile
echo HostCertificate $certfile >> /etc/ssh/sshd_config
done
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config
echo IgnoreRhosts no >> /etc/ssh/sshd_config
if [ -d /etc/ssh/ssh_config.d/ ]; then
sshconf=/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 -Ssf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/nodelist > /tmp/allnodes
cp /tmp/allnodes /etc/ssh/shosts.equiv
cp /tmp/allnodes /root/.shosts
rm /tmp/allnodes