mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 00:31:09 +00:00
Compare commits
515 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c76d94a6d | |||
| 7c8f85eb06 | |||
| ee067fa3c0 | |||
| 4a2e943f84 | |||
| 97e29a5655 | |||
| cb67c83287 | |||
| e11c3516e9 | |||
| 1f6987bafc | |||
| f4f5b4d1b6 | |||
| fd301c609f | |||
| 53b9a13a51 | |||
| cd91ed0b94 | |||
| 4dc54b92d5 | |||
| 08a3a0ee76 | |||
| b601cd97c7 | |||
| 1d80e2703c | |||
| 7304c8e3b7 | |||
| d84a76dbc6 | |||
| 4f1b6b1fac | |||
| 77c5b70ad9 | |||
| c7d87b755a | |||
| f0c5ac557f | |||
| 5d4f0662d1 | |||
| edc3a3e9f3 | |||
| 3d53b76317 | |||
| 683a160c20 | |||
| 82be3c91d5 | |||
| 9cf199a53e | |||
| e735a12b3a | |||
| cbd457b464 | |||
| dd2119c6d9 | |||
| 4640cb194f | |||
| fb10221e1b | |||
| d82a982857 | |||
| e6f6919ef4 | |||
| 1a40842f06 | |||
| 82e0d9c434 | |||
| 83a4df49c7 | |||
| c442c8704d | |||
| 28b88bdb12 | |||
| 29d0e90487 | |||
| a6a1907611 | |||
| ba7a0f7eab | |||
| 0961174425 | |||
| 005adec437 | |||
| 6943c2dc0f | |||
| 6833cd9c53 | |||
| 8fd39c36bb | |||
| 2fc4483bba | |||
| 4453ba3b64 | |||
| ca4955101d | |||
| 187fda4bb8 | |||
| 7ab7600492 | |||
| f2b9a4fa5d | |||
| ef1f51ef98 | |||
| 21b1ac7690 | |||
| feaef79060 | |||
| 8c13816331 | |||
| e07e6ed152 | |||
| 0afc3eb03a | |||
| 0fd07e8427 | |||
| fc5c1aa90f | |||
| 30c4d6b863 | |||
| cfb31a0d8d | |||
| e9d4174ce5 | |||
| ed33b4e93f | |||
| 4b6d41d2f8 | |||
| acce4de739 | |||
| 89bd7c6053 | |||
| 71ca9ef76c | |||
| 1c4f1ae817 | |||
| e6dc383d25 | |||
| 329f2b4485 | |||
| bee9f18197 | |||
| 1af898dcb8 | |||
| 332068074d | |||
| 2df902e80e | |||
| 7a602f58b2 | |||
| c563f48c71 | |||
| c1747ad24c | |||
| 1ddf735590 | |||
| f482d2ead9 | |||
| 58ee85f39e | |||
| 1d6009a2f2 | |||
| 6d15633a95 | |||
| dc7c9f4a3d | |||
| 956e473fa6 | |||
| 626f16cb6f | |||
| 30aa6f382c | |||
| fe6d44a4bb | |||
| 298be3b30a | |||
| 80296b6cbc | |||
| 41b722c3f7 | |||
| 0f955cd068 | |||
| c3e918fc5f | |||
| 8f1a1130a8 | |||
| 6e8d8dabd1 | |||
| a92edc7924 | |||
| 714fefe31b | |||
| c91af840e5 | |||
| 2235faa76d | |||
| 8f58567a70 | |||
| cf4475cfcc | |||
| a94b9235e8 | |||
| bb04faed04 | |||
| 33ed1a5e64 | |||
| 1ade704daa | |||
| 34b03da494 | |||
| 4f18294d93 | |||
| 7154a1d60c | |||
| 69fa3f10c0 | |||
| 36ca68f44d | |||
| ede941c0d9 | |||
| 294ef8e88c | |||
| b61e5fb1ff | |||
| b4a33b8102 | |||
| 9d5432f8cd | |||
| abf12f2b96 | |||
| 5d08919769 | |||
| 8d726bced9 | |||
| 945dff09f3 | |||
| 7a3e1dfde3 | |||
| c0cc673c63 | |||
| 3aea1ec7d6 | |||
| 08a5bffa90 | |||
| 8c193fe33f | |||
| bc624d9360 | |||
| 7bd41af2cc | |||
| 4f21e62705 | |||
| dde6ceadfb | |||
| 9d979256eb | |||
| 8ec836f492 | |||
| 07005d83ca | |||
| 411b2fae49 | |||
| 762a8ee73f | |||
| f2d9c3868b | |||
| 9715247cc6 | |||
| 166e4599b9 | |||
| d231326dfb | |||
| 162e4d1d1e | |||
| 8e407cb96e | |||
| d06be55568 | |||
| 8db6382629 | |||
| 663b320868 | |||
| 517242df47 | |||
| c6adf8175a | |||
| 38a95131f9 | |||
| 5d416cb1b1 | |||
| 451e3ba224 | |||
| 6564f8de72 | |||
| bb414524ad | |||
| bab0a77cb6 | |||
| 010c8a0a23 | |||
| c0bcc3791d | |||
| 172c57c6f1 | |||
| 91dce25a8b | |||
| 62be16442c | |||
| d0e73c887b | |||
| b7a5101a34 | |||
| 930ff3e20d | |||
| 39fb229ef1 | |||
| 60f8f964f5 | |||
| 8f01f22bb5 | |||
| d779f015d6 | |||
| bf004fb7b9 | |||
| 21b8534fef | |||
| 3b69c54203 | |||
| 9954b227a9 | |||
| c0891faaeb | |||
| 86e612b4bf | |||
| 6f2be355ef | |||
| b606882327 | |||
| 10f0fabb8c | |||
| 0d3e2cee6c | |||
| 3b55f500ce | |||
| 3ba3394a54 | |||
| a6a2f2f2de | |||
| 8e5ee6c9d8 | |||
| ceaf641c1a | |||
| 01722c18c4 | |||
| 8fb889ba73 | |||
| eaffb342b2 | |||
| 1da27083cc | |||
| 67b3c48dc9 | |||
| 8ca9a44476 | |||
| 02f301b5d0 | |||
| 33271451d7 | |||
| f68f9f4693 | |||
| a4e152c17d | |||
| 272c456435 | |||
| 894290f577 | |||
| 34804b2d5f | |||
| 77bb03e96c | |||
| 12bb5d583a | |||
| 03bdbfc8ed | |||
| 661b2ae815 | |||
| ddb8c4cce4 | |||
| 17fff4997b | |||
| 7b3129a1a2 | |||
| d183a3f99c | |||
| b3b3627bf9 | |||
| c1afc144cb | |||
| ac1f7c57b6 | |||
| 19e9c6910d | |||
| e38cd5d3e5 | |||
| f1d3e47439 | |||
| f7a2e51f9c | |||
| f3d0bd9079 | |||
| 296a0e88b4 | |||
| c60bf68cbc | |||
| 466ed74961 | |||
| 838c0920eb | |||
| 6ad0e773de | |||
| 5a7d98c6b8 | |||
| 13fc5d9f37 | |||
| 559e88b144 | |||
| 5f801e6683 | |||
| 7a6b03097b | |||
| 6502573d90 | |||
| 3dd09b95e4 | |||
| a595d31e94 | |||
| b157e55f00 | |||
| 60fe306890 | |||
| bd2288ccb7 | |||
| 789376029d | |||
| 1d4505ff3c | |||
| 876b59c1f0 | |||
| 58d9bc1816 | |||
| 17af9c74b8 | |||
| 0d720baf25 | |||
| 49e614eb32 | |||
| cdefb400f9 | |||
| 3ffeef5cf3 | |||
| 5ae3f4c62a | |||
| 2f8dfac9bc | |||
| 75db6da621 | |||
| fa5b1c671e | |||
| 72e26caf36 | |||
| 21f691cbd8 | |||
| d07e6f86c0 | |||
| 3a0172cccc | |||
| 59a31d38a2 | |||
| a17695ad06 | |||
| 7377c44e0f | |||
| fc82021f2b | |||
| 9ad9912ef1 | |||
| 7618fa8b63 | |||
| 170e585e57 | |||
| 72cace5a50 | |||
| d0373977b3 | |||
| f03525aac5 | |||
| fcb3d917db | |||
| 16ad4e776f | |||
| 87454c1ab1 | |||
| fa3e1202c4 | |||
| 41675e528f | |||
| a3d386dc39 | |||
| e5051408e5 | |||
| fa7cd2940e | |||
| e5736ecb51 | |||
| b90718982e | |||
| 07a7357c87 | |||
| dec7454866 | |||
| bcc631f88d | |||
| 9203ac32e9 | |||
| 141760ea19 | |||
| c7628a3638 | |||
| 5fdd6973f1 | |||
| 07f91d792a | |||
| 56b644ead9 | |||
| ea88ccb0ad | |||
| 382feea68d | |||
| 0dfe66f1b2 | |||
| cfccb046bc | |||
| 70f91d59b2 | |||
| 4d63908164 | |||
| 39c00323b3 | |||
| b0e23121a8 | |||
| 85629dea64 | |||
| 93269a05eb | |||
| 7aef012a42 | |||
| 7b89054b35 | |||
| 3e7cdd1caa | |||
| 3aa91b61e5 | |||
| 6763c86387 | |||
| 63b737dc52 | |||
| 55e60d52fd | |||
| 3730ba049f | |||
| 4fb263e643 | |||
| 0b28d64c83 | |||
| 68ce3d039d | |||
| 9757cd1ae3 | |||
| c9452e65e8 | |||
| cd07e0e212 | |||
| f475d58955 | |||
| ec023831a5 | |||
| 6e092934e7 | |||
| e03f010eac | |||
| 2cd75ef425 | |||
| e90f2829ab | |||
| 8f927d94e9 | |||
| 8a4ef0b1fe | |||
| 814f420852 | |||
| 80242e35b3 | |||
| a1ac234b73 | |||
| d082610678 | |||
| 0857716f64 | |||
| 49a504972f | |||
| 62ab361ef8 | |||
| ac68f1f22c | |||
| 7972834a62 | |||
| 9c9d71882c | |||
| 913a26aec9 | |||
| 063bfc17a5 | |||
| 8b150a9047 | |||
| b91a194184 | |||
| 06d18cec63 | |||
| 0434f38ea1 | |||
| bfbb7c2843 | |||
| 3a6932ea6d | |||
| 6e4d9d9eb4 | |||
| 2d906e1886 | |||
| e9a2f57ad8 | |||
| c254564f02 | |||
| fe27cdea4a | |||
| a4ea5e5c4b | |||
| 06ee48f702 | |||
| 77eec1a791 | |||
| b60b78327f | |||
| 2e84c73baa | |||
| 67f607a8f1 | |||
| eca1854d56 | |||
| ba90609f3b | |||
| c8094276d0 | |||
| d299db3442 | |||
| 9f168aee73 | |||
| ee19386d8c | |||
| ef9083062b | |||
| cca0c57e90 | |||
| 79e3ad53f8 | |||
| b63d75f2bb | |||
| 378929579f | |||
| eee8bbb498 | |||
| a00fd325aa | |||
| 02d2ce6bfd | |||
| 7b19e67bf0 | |||
| 3624013889 | |||
| bd0b16992d | |||
| 4ecbbdfd9a | |||
| 5768514ba9 | |||
| f88b44dee2 | |||
| d0c97b7623 | |||
| 04505e1bbb | |||
| d613d0f546 | |||
| 47fc233cce | |||
| 8f80add0f1 | |||
| 0a527f5f39 | |||
| 3e8c6d1ea6 | |||
| a01b7c6503 | |||
| 83e3627b47 | |||
| 37b75ba777 | |||
| aa5de3c6a3 | |||
| d4c535d038 | |||
| 94b8559777 | |||
| f2f25fe912 | |||
| c0629fcce5 | |||
| 4952e87309 | |||
| 533244458d | |||
| 20f02b5ef7 | |||
| df47c6d0fd | |||
| 97ee8e2372 | |||
| 74c6848a0b | |||
| b75979f3ec | |||
| 50d5cead06 | |||
| e6b0b235e9 | |||
| 00eb9e3c9d | |||
| b77d8b1f21 | |||
| d7190c893f | |||
| f82829aa0c | |||
| 203dabfb0b | |||
| 9441221150 | |||
| 8de6f4356d | |||
| fe78034eaa | |||
| 691d92f735 | |||
| 8ca1f80ef6 | |||
| 3cacbf283a | |||
| 0993ef0c11 | |||
| c950524386 | |||
| 89e573207c | |||
| f16cf4387f | |||
| fa60a9dc9f | |||
| 2268d7f65a | |||
| abd5f4e9f2 | |||
| f6e658c341 | |||
| 44ec390f40 | |||
| 6ab91b50af | |||
| 1f32ef2310 | |||
| 22cb2bdc40 | |||
| f633c93d0f | |||
| 610bc793b7 | |||
| 85375cc733 | |||
| af4ca64da8 | |||
| 6d87d11f5e | |||
| 9591ff4718 | |||
| 439143115e | |||
| b14b34bdbd | |||
| 899ce7f055 | |||
| 7e209e412a | |||
| 7051f467bb | |||
| b88ccc292c | |||
| c47066d713 | |||
| 8ddcf45e1d | |||
| 53d2b873a2 | |||
| bc7dbeebea | |||
| 1b2d8a0596 | |||
| f3e7d4ab67 | |||
| b27542ec00 | |||
| 58cc9840b3 | |||
| 84d23bb1fd | |||
| 44405baaf3 | |||
| ae915cd735 | |||
| c7a323b370 | |||
| 9e070a14d4 | |||
| 189ba525d3 | |||
| 5068cda2cf | |||
| 95c5253944 | |||
| ca2dcd2dbe | |||
| dba6e7f378 | |||
| 0b7247d386 | |||
| 75add230b6 | |||
| 2c90227f63 | |||
| 0cff70c607 | |||
| 75082c4418 | |||
| 9a1c9eb43f | |||
| 89bd798f8b | |||
| bf10e58f00 | |||
| cbf2cdcdc5 | |||
| 987587aaf8 | |||
| ad25c31d3f | |||
| b1018d648e | |||
| a18d0f10b7 | |||
| 65ac3de21b | |||
| c9d9a3cc54 | |||
| 957b979dde | |||
| 48c4a2e062 | |||
| 285a159ba5 | |||
| 8ea2ba046e | |||
| f16daa44dd | |||
| 03fce4f762 | |||
| 809099c8f8 | |||
| a875f0d3e1 | |||
| 6ee9f96e08 | |||
| fcc62b7cb7 | |||
| 11cb47c03e | |||
| afc78513a1 | |||
| 45f22c3e31 | |||
| d0f978548e | |||
| 052d16aa49 | |||
| 7ddd2c2e6e | |||
| 9186c8142c | |||
| c07248d58e | |||
| 013de4f881 | |||
| 795b4c513d | |||
| 4056264991 | |||
| 77c8e93a8d | |||
| 1695e222ec | |||
| b2a793568a | |||
| 3ebd43fe0c | |||
| 2e20e0303f | |||
| da2df63498 | |||
| 896a4ff1ff | |||
| a14effa4f2 | |||
| 66c79a5f42 | |||
| 389c37659e | |||
| 89af8661da | |||
| 41437c511b | |||
| 75f0aaeee9 | |||
| 68f9688292 | |||
| 67204f79a1 | |||
| e9dd7da775 | |||
| 8f1be7c905 | |||
| bc684b82b4 | |||
| 5e72d40c03 | |||
| 7ab2aff7f2 | |||
| c3fc931ba4 | |||
| 2c61cee72d | |||
| 190ec2473b | |||
| 9ebb8f27ec | |||
| 0db7d4f9d2 | |||
| 5d1315098f | |||
| fc29ddb913 | |||
| b06b77f5a5 | |||
| 97a5bf0806 | |||
| 36e61f301a | |||
| e476c7b9e6 | |||
| a523df80e8 | |||
| 8f4982e850 | |||
| a167d19441 | |||
| 2237e0e173 | |||
| b08b62614a | |||
| e93e4abd83 | |||
| 430b24081c | |||
| ec54ecc9cd | |||
| f787461759 | |||
| 7312d8e092 | |||
| 884a6be68c | |||
| a35a7e74c3 | |||
| ffdf799713 | |||
| b9d0da0416 | |||
| 3fd930137d | |||
| 71a263366d | |||
| 7b150773df | |||
| 53f4665358 | |||
| e5fff430be | |||
| 55da173b27 |
@@ -0,0 +1,30 @@
|
||||
# Confluent
|
||||
|
||||
 [](https://github.com/xcat2/confluent/blob/master/LICENSE)
|
||||
|
||||
Confluent is a software package to handle essential bootstrap and operation of scale-out server configurations.
|
||||
It supports stateful and stateless deployments for various operating systems.
|
||||
|
||||
Check [this page](https://hpc.lenovo.com/users/documentation/whatisconfluent.html
|
||||
) for a more detailed list of features.
|
||||
|
||||
Confluent is the modern successor of [xCAT](https://github.com/xcat2/xcat-core).
|
||||
If you're coming from xCAT, check out [this comparison](https://hpc.lenovo.com/users/documentation/confluentvxcat.html).
|
||||
|
||||
# Documentation
|
||||
|
||||
Confluent documentation is hosted on hpc.lenovo.com: https://hpc.lenovo.com/users/documentation/
|
||||
|
||||
# Download
|
||||
|
||||
Get the latest version from: https://hpc.lenovo.com/users/downloads/
|
||||
|
||||
Check release notes on: https://hpc.lenovo.com/users/news/
|
||||
|
||||
# Open Source License
|
||||
|
||||
Confluent is made available under the Apache 2.0 license: https://opensource.org/license/apache-2-0
|
||||
|
||||
# Developers
|
||||
|
||||
Want to help? Submit a [Pull Request](https://github.com/xcat2/confluent/pulls).
|
||||
@@ -1,6 +1,8 @@
|
||||
Name: confluent-client
|
||||
Project: https://hpc.lenovo.com/users/
|
||||
Source: https://github.com/lenovo/confluent
|
||||
Upstream-Name: confluent-client
|
||||
|
||||
All file of the Confluent-client software is distributed under the terms of an Apache-2.0 license as indicated below:
|
||||
|
||||
Files: *
|
||||
Copyright: 2014-2019 Lenovo
|
||||
@@ -11,7 +13,8 @@ Copyright: 2014 IBM Corporation
|
||||
2015-2019 Lenovo
|
||||
License: Apache-2.0
|
||||
|
||||
File: sortutil.py
|
||||
File: sortutil.py,
|
||||
tlvdata.py
|
||||
Copyright: 2014 IBM Corporation
|
||||
2015-2016 Lenovo
|
||||
License: Apache-2.0
|
||||
@@ -19,3 +22,56 @@ License: Apache-2.0
|
||||
File: tlv.py
|
||||
Copyright: 2014 IBM Corporation
|
||||
License: Apache-2.0
|
||||
|
||||
License: Apache-2.0
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import sys
|
||||
|
||||
@@ -84,6 +85,7 @@ fullline = sys.stdin.readline()
|
||||
printpending = True
|
||||
clearpending = False
|
||||
holdoff = 0
|
||||
padded = None
|
||||
while fullline:
|
||||
for line in fullline.split('\n'):
|
||||
if not line:
|
||||
@@ -92,13 +94,18 @@ while fullline:
|
||||
line = 'UNKNOWN: ' + line
|
||||
if options.log:
|
||||
node, output = line.split(':', 1)
|
||||
output = output.lstrip()
|
||||
if padded is None:
|
||||
if output.startswith(' '):
|
||||
padded = True
|
||||
else:
|
||||
padded = False
|
||||
if padded:
|
||||
output = re.sub(r'^ ', '', output)
|
||||
currlog = options.log.format(node=node, nodename=node)
|
||||
with open(currlog, mode='a') as log:
|
||||
log.write(output + '\n')
|
||||
continue
|
||||
node, output = line.split(':', 1)
|
||||
output = output.lstrip()
|
||||
grouped.add_line(node, output)
|
||||
if options.watch:
|
||||
if not holdoff:
|
||||
|
||||
@@ -157,7 +157,7 @@ def main():
|
||||
elif attrib.endswith('.ipv6_address') and val:
|
||||
ip6bynode[node][currnet] = val.split('/', 1)[0]
|
||||
elif attrib.endswith('.hostname'):
|
||||
namesbynode[node][currnet] = re.split('\s+|,', val)
|
||||
namesbynode[node][currnet] = re.split(r'\s+|,', val)
|
||||
for node in ip4bynode:
|
||||
mydomain = domainsbynode.get(node, None)
|
||||
for ipdb in (ip4bynode, ip6bynode):
|
||||
|
||||
Executable
+173
@@ -0,0 +1,173 @@
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__author__ = 'tkucherera'
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog <start_node> -i <interface> <end_node> -e <eface>",
|
||||
)
|
||||
argparser.add_option('-i', '--interface', type='str',
|
||||
help='interface to check path against for the start node')
|
||||
argparser.add_option('-e', '--eface', type='str',
|
||||
help='interface to check path against for the end node')
|
||||
argparser.add_option('-c', '--cumulus', action="store_true", dest="cumulus",
|
||||
help='return layer 2 route through cumulus switches only')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
try:
|
||||
start_node = args[0]
|
||||
end_node = args[1]
|
||||
interface = options.interface
|
||||
eface = options.eface
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
session = client.Command()
|
||||
|
||||
def get_neighbors(switch):
|
||||
switch_neigbors = []
|
||||
url = '/networking/neighbors/by-switch/{0}/by-peername/'.format(switch)
|
||||
for neighbor in session.read(url):
|
||||
switch = neighbor['item']['href'].strip('/')
|
||||
if switch in all_switches:
|
||||
switch_neigbors.append(switch)
|
||||
return switch_neigbors
|
||||
|
||||
def find_path(start, end, path=[]):
|
||||
path = path + [start]
|
||||
if start == end:
|
||||
return path # If start and end are the same, return the path
|
||||
|
||||
for node in get_neighbors(start):
|
||||
if node not in path:
|
||||
new_path = find_path(node, end, path)
|
||||
if new_path:
|
||||
return new_path # If a path is found, return it
|
||||
|
||||
return None # If no path is found, return None
|
||||
|
||||
def is_cumulus(switch):
|
||||
try:
|
||||
read_attrib = subprocess.check_output(['nodeattrib', switch, 'hardwaremanagement.method'])
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
for attribs in read_attrib.decode('utf-8').split('\n'):
|
||||
if len(attribs.split(':')) > 1:
|
||||
attrib = attribs.split(':')
|
||||
if attrib[2].strip() == 'affluent':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def host_to_switch(node, interface=None):
|
||||
# first check the the node config to see what switches are connected
|
||||
# if host is in rhel can use nmstate package
|
||||
if node in all_switches:
|
||||
return [node]
|
||||
switches = []
|
||||
netarg = 'net.*.switch'
|
||||
if interface:
|
||||
netarg = 'net.{0}.switch'.format(interface)
|
||||
try:
|
||||
read_attrib = subprocess.check_output(['nodeattrib', node, netarg])
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
for attribs in read_attrib.decode('utf-8').split('\n'):
|
||||
attrib = attribs.split(':')
|
||||
try:
|
||||
if ' net.mgt.switch' in attrib or attrib[2] == '':
|
||||
continue
|
||||
except IndexError:
|
||||
continue
|
||||
switch = attrib[2].strip()
|
||||
if is_cumulus(switch) and options.cumulus:
|
||||
switches.append(switch)
|
||||
else:
|
||||
switches.append(switch)
|
||||
return switches
|
||||
|
||||
def path_between_nodes(start_switches, end_switches):
|
||||
for start_switch in start_switches:
|
||||
for end_switch in end_switches:
|
||||
if start_switch == end_switch:
|
||||
return [start_switch]
|
||||
else:
|
||||
path = find_path(start_switch, end_switch)
|
||||
if path:
|
||||
return path
|
||||
else:
|
||||
return 'No path found'
|
||||
|
||||
|
||||
all_switches = []
|
||||
for res in session.read('/networking/neighbors/by-switch/'):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
switch = (res['item']['href'].replace('/', ''))
|
||||
all_switches.append(switch)
|
||||
|
||||
end_nodeslist = []
|
||||
nodelist = '/noderange/{0}/nodes/'.format(end_node)
|
||||
for res in session.read(nodelist):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
elem=(res['item']['href'].replace('/', ''))
|
||||
end_nodeslist.append(elem)
|
||||
|
||||
start_switches = host_to_switch(start_node, interface)
|
||||
for end_node in end_nodeslist:
|
||||
if end_node:
|
||||
end_switches = host_to_switch(end_node, eface)
|
||||
if not end_switches:
|
||||
print('Error: net.{0}.switch attribute is not valid')
|
||||
continue
|
||||
path = path_between_nodes(start_switches, end_switches)
|
||||
print(f'{start_node} to {end_node}: {path}')
|
||||
|
||||
# TODO dont put switches that are connected through management interfaces.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from collections import deque
|
||||
import optparse
|
||||
import os
|
||||
import select
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
devnull = None
|
||||
|
||||
def run():
|
||||
global devnull
|
||||
devnull = open(os.devnull, 'rb')
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] noderange commandexpression",
|
||||
epilog="Expressions are the same as in attributes, e.g. "
|
||||
"'ipmitool -H {hardwaremanagement.manager}' will be expanded.")
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-k', '--security', action='store_true',
|
||||
help='Update SSH setup')
|
||||
argparser.add_option('-F', '--sync', action='store_true',
|
||||
help='Run the syncfiles associated with the currently completed OS profile on the noderange')
|
||||
argparser.add_option('-P', '--scripts',
|
||||
help='Re-run specified scripts, with full path under scripts, e.g. post.d/first,firstboot.d/second')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to run remote ssh command to, '
|
||||
'prompting if over the threshold')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
# processes can be unkind for the unaware on memory pressure and such...
|
||||
#argparser.disable_interspersed_args()
|
||||
(options, args) = argparser.parse_args()
|
||||
if len(args) < 1:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
client.check_globbing(args[0])
|
||||
concurrentprocs = options.count
|
||||
c = client.Command()
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
|
||||
c.stop_if_noderange_over(args[0], options.maxnodes)
|
||||
nodemap = {}
|
||||
cmdparms = []
|
||||
nodes = []
|
||||
for res in c.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
break
|
||||
node = res['item']['href'][:-1]
|
||||
nodes.append(node)
|
||||
|
||||
cmdstorun = []
|
||||
if options.security:
|
||||
cmdstorun.append(['run_remote', 'setupssh'])
|
||||
if options.sync:
|
||||
cmdstorun.append(['run_remote_python', 'syncfileclient'])
|
||||
if options.scripts:
|
||||
for script in options.scripts.split(','):
|
||||
cmdstorun.append(['run_remote', script])
|
||||
if not cmdstorun:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
idxbynode = {}
|
||||
cmdvbase = ['bash', '/etc/confluent/functions']
|
||||
for sshnode in nodes:
|
||||
idxbynode[sshnode] = 1
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[0]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(sshnode, cmdv, all, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((sshnode, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
desc = pipedesc[r]
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
data = r.readline()
|
||||
if data:
|
||||
if desc['type'] == 'stdout':
|
||||
if node not in pernodeout:
|
||||
pernodeout[node] = []
|
||||
pernodeout[node].append(data)
|
||||
else:
|
||||
data = client.stringify(data)
|
||||
sys.stderr.write('{0}: {1}'.format(node, data))
|
||||
sys.stderr.flush()
|
||||
else:
|
||||
pop = desc['popen']
|
||||
ret = pop.poll()
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout':
|
||||
if idxbynode[node] < len(cmdstorun):
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[idxbynode[node]]
|
||||
idxbynode[node] += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
elif pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
line = line.lstrip('\x08')
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
all.add(nopen.stdout)
|
||||
all.add(nopen.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -22,6 +22,7 @@ import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import shlex
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
@@ -53,6 +54,8 @@ argparser.add_option('-p', '--prompt', action='store_true',
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Prompt if trying to set attributes on more '
|
||||
'than specified number of nodes')
|
||||
argparser.add_option('-s', '--set', dest='set', metavar='settings.batch',
|
||||
default=False, help='set attributes using a batch file')
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
|
||||
@@ -109,10 +112,28 @@ elif options.clear or options.environment or options.prompt:
|
||||
sys.stderr.write('Attribute names required with specified options\n')
|
||||
argparser.print_help()
|
||||
exitcode = 400
|
||||
|
||||
elif options.set:
|
||||
arglist = [noderange]
|
||||
showtype='current'
|
||||
argfile = open(options.set, 'r')
|
||||
argset = argfile.readline()
|
||||
while argset:
|
||||
try:
|
||||
argset = argset[:argset.index('#')]
|
||||
except ValueError:
|
||||
pass
|
||||
argset = argset.strip()
|
||||
if argset:
|
||||
arglist += shlex.split(argset)
|
||||
argset = argfile.readline()
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
exitcode=client.updateattrib(session,arglist,nodetype, noderange, options, None)
|
||||
if exitcode != 0:
|
||||
sys.exit(exitcode)
|
||||
|
||||
# Lists all attributes
|
||||
|
||||
if len(args) > 0:
|
||||
# setting output to all so it can search since if we do have something to search, we want to show all outputs even if it is blank.
|
||||
if requestargs is None:
|
||||
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__author__ = 'tkucherera'
|
||||
|
||||
from getpass import getpass
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange> <username> <new_password>")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Number of nodes to affect before prompting for confirmation')
|
||||
argparser.add_option('-p', '--prompt', action='store_true',
|
||||
help='Prompt for password values interactively')
|
||||
argparser.add_option('-e', '--environment', action='store_true',
|
||||
help='Set passwod, but from environment variable of '
|
||||
'same name')
|
||||
|
||||
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
|
||||
try:
|
||||
noderange = args[0]
|
||||
username = args[1]
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
client.check_globbing(noderange)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
|
||||
if options.prompt:
|
||||
oneval = 1
|
||||
twoval = 2
|
||||
while oneval != twoval:
|
||||
oneval = getpass('Enter pass for {0}: '.format(username))
|
||||
twoval = getpass('Confirm pass for {0}: '.format(username))
|
||||
if oneval != twoval:
|
||||
print('Values did not match.')
|
||||
new_password = twoval
|
||||
|
||||
elif len(args) == 3:
|
||||
if options.environment:
|
||||
key = args[2]
|
||||
new_password = os.environ.get(key, os.environ[key.upper()])
|
||||
else:
|
||||
new_password = args[2]
|
||||
else:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
errorNodes = set([])
|
||||
uid_dict = {}
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
|
||||
for rsp in session.read('/noderange/{0}/configuration/management_controller/users/all'.format(noderange)):
|
||||
databynode = rsp["databynode"]
|
||||
for node in databynode:
|
||||
if 'error' in rsp['databynode'][node]:
|
||||
print(node, ':', rsp['databynode'][node]['error'])
|
||||
errorNodes.add(node)
|
||||
continue
|
||||
for user in rsp['databynode'][node]['users']:
|
||||
if user['username'] == username:
|
||||
if not user['uid'] in uid_dict:
|
||||
uid_dict[user['uid']] = node
|
||||
continue
|
||||
uid_dict[user['uid']] = uid_dict[user['uid']] + ',{}'.format(node)
|
||||
break
|
||||
|
||||
if not uid_dict:
|
||||
print("Error: Could not reach target node's bmc user")
|
||||
sys.exit(1)
|
||||
|
||||
for uid in uid_dict:
|
||||
success = session.simple_noderange_command(uid_dict[uid], 'configuration/management_controller/users/{0}'.format(uid), new_password, key='password', errnodes=errorNodes) # = 0 if successful
|
||||
|
||||
allNodes = set([])
|
||||
|
||||
for node in session.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
if 'error' in node and success != 0:
|
||||
sys.exit(success)
|
||||
allNodes.add(node['item']['href'].replace("/", ""))
|
||||
|
||||
goodNodes = allNodes - errorNodes
|
||||
|
||||
for node in goodNodes:
|
||||
print(node + ": Password Change Successful")
|
||||
|
||||
|
||||
sys.exit(success)
|
||||
@@ -303,9 +303,14 @@ else:
|
||||
'/noderange/{0}/configuration/management_controller/extended/all'.format(noderange),
|
||||
session, printbmc, options, attrprefix='bmc.')
|
||||
if options.extra:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/extra'.format(noderange),
|
||||
session, printextbmc, options)
|
||||
if options.advanced:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/extra_advanced'.format(noderange),
|
||||
session, printextbmc, options)
|
||||
else:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/extra'.format(noderange),
|
||||
session, printextbmc, options)
|
||||
if printsys or options.exclude:
|
||||
if printsys == 'all':
|
||||
printsys = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015 Lenovo
|
||||
@@ -23,6 +23,7 @@ path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
import confluent.logreader as logreader
|
||||
@@ -41,6 +42,10 @@ argparser.add_option('-t', '--tile', action='store_true', default=False,
|
||||
help='Tile console windows in the terminal')
|
||||
argparser.add_option('-l', '--log', action='store_true', default=False,
|
||||
help='Enter log replay mode instead of showing a live console')
|
||||
|
||||
argparser.add_option('-T', '--Timestamp', action='store_true', default=False,
|
||||
help= 'Dump log in stdout with timestamps')
|
||||
|
||||
argparser.add_option('-w','--windowed', action='store_true', default=False,
|
||||
help='Open terminal windows for each node. The '
|
||||
'environment variable NODECONSOLE_WINDOWED_COMMAND '
|
||||
@@ -91,6 +96,16 @@ if options.log:
|
||||
logreader.replay_to_console(logname)
|
||||
sys.exit(0)
|
||||
|
||||
if options.Timestamp:
|
||||
logname = args[0]
|
||||
if not os.path.exists(logname) and logname[0] != '/':
|
||||
logname = os.path.join('/var/log/confluent/consoles', logname)
|
||||
if not os.path.exists(logname):
|
||||
sys.stderr.write('Unable to locate {0} on local system\n'.format(logname))
|
||||
sys.exit(1)
|
||||
logreader.dump_to_console(logname)
|
||||
sys.exit(0)
|
||||
|
||||
def kill(noderange):
|
||||
sess = client.Command()
|
||||
envstring=os.environ.get('NODECONSOLE_WINDOWED_COMMAND')
|
||||
@@ -106,12 +121,20 @@ def kill(noderange):
|
||||
nodes.append(node)
|
||||
|
||||
for node in nodes:
|
||||
ps_data=subprocess.Popen(['ps', '-auxww' ], stdout=subprocess.PIPE)
|
||||
wintr=ps_data.communicate()[0]
|
||||
for line in wintr.decode('utf-8').split('\n'):
|
||||
if confettypath in line and envstring in line and node in line:
|
||||
pid_line = [x for x in line.split(' ') if x != '']
|
||||
ps_data=subprocess.Popen(['kill', '-9', pid_line[1] ], stdout=subprocess.PIPE)
|
||||
s=socket.socket(socket.AF_UNIX)
|
||||
winid=None
|
||||
try:
|
||||
win=subprocess.Popen(['xwininfo', '-tree', '-root'], stdout=subprocess.PIPE)
|
||||
wintr=win.communicate()[0]
|
||||
for line in wintr.decode('utf-8').split('\n'):
|
||||
if 'console: {0}'.format(node) in line or 'confetty' in line:
|
||||
win_obj = [ele for ele in line.split(' ') if ele.strip()]
|
||||
winid = win_obj[0]
|
||||
except:
|
||||
print("Error: cannot retrieve window id of node {}".format(node))
|
||||
|
||||
if winid:
|
||||
ps_data=subprocess.Popen(['xkill', '-id', winid ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
@@ -220,7 +243,7 @@ if options.windowed:
|
||||
elif 'Height' in line:
|
||||
window_height = int(line.split(':')[1])
|
||||
elif '-geometry' in line:
|
||||
l = re.split(' |x|\+', line)
|
||||
l = re.split(' |x|-|\\+', line)
|
||||
l_nosp = [ele for ele in l if ele.strip()]
|
||||
wmxo = int(l_nosp[1])
|
||||
wmyo = int(l_nosp[2])
|
||||
@@ -259,7 +282,7 @@ if options.windowed:
|
||||
else:
|
||||
pass
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
xopen=subprocess.Popen(envlist + [confettypath, '-m', '5', 'start', '/nodes/{0}/console/session'.format(node)] , stdin=devnull)
|
||||
xopen=subprocess.Popen(envlist + [confettypath, '-c', '/tmp/controlpath-{0}'.format(node), '-m', '5', 'start', '/nodes/{0}/console/session'.format(node)] , stdin=devnull)
|
||||
sys.exit(0)
|
||||
#end of wcons
|
||||
if options.tile:
|
||||
@@ -273,8 +296,13 @@ if options.tile:
|
||||
sys.exit(1)
|
||||
nodes.append(node)
|
||||
initial = True
|
||||
in_tmux = False
|
||||
pane = 0
|
||||
sessname = 'nodeconsole_{0}'.format(os.getpid())
|
||||
if os.environ.get("TMUX"):
|
||||
initial = False
|
||||
in_tmux = True
|
||||
subprocess.call(['tmux', 'rename-session', sessname])
|
||||
for node in sortutil.natural_sort(nodes):
|
||||
panename = '{0}:{1}'.format(sessname, pane)
|
||||
if initial:
|
||||
@@ -295,7 +323,8 @@ if options.tile:
|
||||
pane += 1
|
||||
subprocess.call(['tmux', 'select-pane', '-t', sessname])
|
||||
subprocess.call(['tmux', 'set-option', '-t', panename, 'pane-border-status', 'top'], stderr=null)
|
||||
os.execlp('tmux', 'tmux', 'attach', '-t', sessname)
|
||||
if not in_tmux:
|
||||
os.execlp('tmux', 'tmux', 'attach', '-t', sessname)
|
||||
else:
|
||||
os.execl(confettypath, confettypath, 'start',
|
||||
'/nodes/{0}/console/session'.format(args[0]))
|
||||
'/nodes/{0}/console/session'.format(args[0]))
|
||||
|
||||
@@ -81,6 +81,12 @@ def main(args):
|
||||
if not args.profile and args.network:
|
||||
sys.stderr.write('Both noderange and a profile name are required arguments to request a network deployment\n')
|
||||
return 1
|
||||
if args.clear and args.profile:
|
||||
sys.stderr.write(
|
||||
'The -c/--clear option should not be used with a profile, '
|
||||
'it is a request to not deploy any profile, and will clear '
|
||||
'whatever the current profile is without being specified\n')
|
||||
return 1
|
||||
if extra:
|
||||
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
|
||||
c = client.Command()
|
||||
@@ -90,17 +96,6 @@ def main(args):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
sys.exit(1)
|
||||
if not args.clear and args.network and not args.prepareonly:
|
||||
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
|
||||
bootmode='uefi',
|
||||
persistent=False,
|
||||
errnodes=errnodes)
|
||||
if errnodes:
|
||||
sys.stderr.write(
|
||||
'Unable to set boot device for following nodes: {0}\n'.format(
|
||||
','.join(errnodes)))
|
||||
return 1
|
||||
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
|
||||
if args.clear:
|
||||
cleararm(args.noderange, c)
|
||||
clearpending(args.noderange, c)
|
||||
@@ -120,7 +115,7 @@ def main(args):
|
||||
for profname in profnames:
|
||||
sys.stderr.write(' ' + profname + '\n')
|
||||
else:
|
||||
sys.stderr.write('No deployment profiles available, try osdeploy fiimport or imgutil capture\n')
|
||||
sys.stderr.write('No deployment profiles available, try osdeploy import or imgutil capture\n')
|
||||
sys.exit(1)
|
||||
armonce(args.noderange, c)
|
||||
setpending(args.noderange, args.profile, c)
|
||||
@@ -166,8 +161,17 @@ def main(args):
|
||||
else:
|
||||
print('{0}: {1}{2}'.format(node, profile, armed))
|
||||
sys.exit(0)
|
||||
if args.network and not args.prepareonly:
|
||||
return rc
|
||||
if not args.clear and args.network and not args.prepareonly:
|
||||
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
|
||||
bootmode='uefi',
|
||||
persistent=False,
|
||||
errnodes=errnodes)
|
||||
if errnodes:
|
||||
sys.stderr.write(
|
||||
'Unable to set boot device for following nodes: {0}\n'.format(
|
||||
','.join(errnodes)))
|
||||
return 1
|
||||
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -15,13 +15,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__author__ = 'jjohnson2,alin37'
|
||||
__author__ = 'jjohnson2,alin37,wgrzeda'
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
@@ -39,9 +41,12 @@ def main():
|
||||
" or: %prog [options] noderange <nodeattribute>...")
|
||||
argparser.add_option('-b', '--blame', action='store_true',
|
||||
help='Show information about how attributes inherited')
|
||||
argparser.add_option('-d', '--delim', metavar="STRING", default = "\n",
|
||||
help='Delimiter separating the values')
|
||||
(options, args) = argparser.parse_args()
|
||||
noderange=""
|
||||
nodelist=""
|
||||
list = []
|
||||
try:
|
||||
noderange = args[0]
|
||||
nodelist = '/noderange/{0}/nodes/'.format(noderange)
|
||||
@@ -61,7 +66,9 @@ def main():
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
print(res['item']['href'].replace('/', ''))
|
||||
elem=(res['item']['href'].replace('/', ''))
|
||||
list.append(elem)
|
||||
print(options.delim.join(list))
|
||||
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ def run():
|
||||
'prompting if over the threshold')
|
||||
argparser.add_option('-l', '--loginname', type='str',
|
||||
help='Username to use when connecting, defaults to current user.')
|
||||
argparser.add_option('-s', '--substitutename',
|
||||
help='Use a different name other than the nodename for rsync')
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of nodes to concurrently rsync')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
@@ -63,10 +65,20 @@ def run():
|
||||
c = client.Command()
|
||||
cmdstr = ' '.join(args[:-1])
|
||||
cmdstr = 'rsync -av --info=progress2 ' + cmdstr
|
||||
if options.loginname:
|
||||
cmdstr += ' {}@'.format(options.loginname) + '{node}:' + targpath
|
||||
|
||||
targname = options.substitutename
|
||||
if targname and '{' in targname:
|
||||
targname = targname + ':'
|
||||
elif targname:
|
||||
targname = '{node}' + targname + ':'
|
||||
else:
|
||||
cmdstr += ' {node}:' + targpath
|
||||
targname = '{node}:'
|
||||
|
||||
if options.loginname:
|
||||
cmdstr += ' {}@'.format(options.loginname) + targname + targpath
|
||||
else:
|
||||
cmdstr += ' {}'.format(targname) + targpath
|
||||
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import csv
|
||||
import fcntl
|
||||
import io
|
||||
import numpy as np
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
@@ -35,7 +32,31 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def plot(gui, output, plotdata, bins):
|
||||
def iterm_draw(data):
|
||||
databuf = data.getbuffer()
|
||||
datalen = len(databuf)
|
||||
data = base64.b64encode(databuf).decode('utf8')
|
||||
sys.stdout.write(
|
||||
'\x1b]1337;File=inline=1;size={}:'.format(datalen))
|
||||
sys.stdout.write(data)
|
||||
sys.stdout.write('\a')
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def kitty_draw(data):
|
||||
data = base64.b64encode(data.getbuffer())
|
||||
while data:
|
||||
chunk, data = data[:4096], data[4096:]
|
||||
m = 1 if data else 0
|
||||
sys.stdout.write('\x1b_Ga=T,f=100,m={};'.format(m))
|
||||
sys.stdout.write(chunk.decode('utf8'))
|
||||
sys.stdout.write('\x1b\\')
|
||||
sys.stdout.flush()
|
||||
sys.stdout.write('\n')
|
||||
|
||||
|
||||
def plot(gui, output, plotdata, bins, fmt):
|
||||
import matplotlib as mpl
|
||||
if gui and mpl.get_backend() == 'agg':
|
||||
sys.stderr.write('Error: No GUI backend available and -g specified!\n')
|
||||
@@ -51,8 +72,13 @@ def plot(gui, output, plotdata, bins):
|
||||
tdata = io.BytesIO()
|
||||
plt.savefig(tdata)
|
||||
if not gui and not output:
|
||||
writer = DumbWriter()
|
||||
writer.draw(tdata)
|
||||
if fmt == 'sixel':
|
||||
writer = DumbWriter()
|
||||
writer.draw(tdata)
|
||||
elif fmt == 'kitty':
|
||||
kitty_draw(tdata)
|
||||
elif fmt == 'iterm':
|
||||
iterm_draw(tdata)
|
||||
return n, bins
|
||||
|
||||
def textplot(plotdata, bins):
|
||||
@@ -81,7 +107,8 @@ histogram = False
|
||||
aparser = argparse.ArgumentParser(description='Quick access to common statistics')
|
||||
aparser.add_argument('-c', type=int, default=0, help='Column number to analyze (default is last column)')
|
||||
aparser.add_argument('-d', default=None, help='Value used to separate columns')
|
||||
aparser.add_argument('-x', default=False, action='store_true', help='Output histogram in sixel format')
|
||||
aparser.add_argument('-x', default=False, action='store_true', help='Output histogram in graphical format')
|
||||
aparser.add_argument('-f', default='sixel', help='Format for histogram output (sixel/iterm/kitty)')
|
||||
aparser.add_argument('-s', default=0, help='Number of header lines to skip before processing')
|
||||
aparser.add_argument('-g', default=False, action='store_true', help='Open histogram in separate graphical window')
|
||||
aparser.add_argument('-o', default=None, help='Output histogram to the specified filename in PNG format')
|
||||
@@ -138,7 +165,7 @@ while data:
|
||||
data = list(csv.reader([data], delimiter=delimiter))[0]
|
||||
n = None
|
||||
if args.g or args.o or args.x:
|
||||
n, bins = plot(args.g, args.o, plotdata, bins=args.b)
|
||||
n, bins = plot(args.g, args.o, plotdata, bins=args.b, fmt=args.f)
|
||||
if args.t:
|
||||
n, bins = textplot(plotdata, bins=args.b)
|
||||
print('Samples: {5} Min: {3} Median: {0} Mean: {1} Max: {4} StandardDeviation: {2} Sum: {6}'.format(np.median(plotdata), np.mean(plotdata), np.std(plotdata), np.min(plotdata), np.max(plotdata), len(plotdata), np.sum(plotdata)))
|
||||
|
||||
@@ -595,7 +595,7 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
|
||||
else:
|
||||
printmissing.add(attr)
|
||||
for missing in printmissing:
|
||||
sys.stderr.write('Error: {0} not a valid attribute\n'.format(attr))
|
||||
sys.stderr.write('Error: {0} not a valid attribute\n'.format(missing))
|
||||
return exitcode
|
||||
|
||||
|
||||
@@ -668,6 +668,9 @@ def updateattrib(session, updateargs, nodetype, noderange, options, dictassign=N
|
||||
for attrib in updateargs[1:]:
|
||||
keydata[attrib] = None
|
||||
for res in session.update(targpath, keydata):
|
||||
for node in res.get('databynode', {}):
|
||||
for warnmsg in res['databynode'][node].get('_warnings', []):
|
||||
sys.stderr.write('Warning: ' + warnmsg + '\n')
|
||||
if 'error' in res:
|
||||
if 'errorcode' in res:
|
||||
exitcode = res['errorcode']
|
||||
@@ -702,6 +705,14 @@ def updateattrib(session, updateargs, nodetype, noderange, options, dictassign=N
|
||||
noderange, 'attributes/all', dictassign[key], key)
|
||||
else:
|
||||
if "=" in updateargs[1]:
|
||||
update_ready = True
|
||||
for arg in updateargs[1:]:
|
||||
if not '=' in arg:
|
||||
update_ready = False
|
||||
exitcode = 1
|
||||
if not update_ready:
|
||||
sys.stderr.write('Error: {0} Can not set and read at the same time!\n'.format(str(updateargs[1:])))
|
||||
sys.exit(exitcode)
|
||||
try:
|
||||
for val in updateargs[1:]:
|
||||
val = val.split('=', 1)
|
||||
|
||||
@@ -296,5 +296,47 @@ def replay_to_console(txtfile):
|
||||
sys.exit(1)
|
||||
_replay_to_console(txtfile, binfile)
|
||||
|
||||
def dump_to_console(txtfile):
|
||||
if os.path.exists(txtfile + '.cbl'):
|
||||
binfile = txtfile + '.cbl'
|
||||
elif '.' not in txtfile:
|
||||
if '/' not in txtfile:
|
||||
txtfile = os.getcwd() + '/' + txtfile
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(txtfile + '.cbl'))
|
||||
sys.exit(1)
|
||||
else:
|
||||
fileparts = txtfile.split('.')
|
||||
prefix = '.'.join(fileparts[:-1])
|
||||
binfile = prefix + '.cbl.' + fileparts[-1]
|
||||
if not os.path.exists(binfile):
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(binfile))
|
||||
sys.exit(1)
|
||||
replay = LogReplay(txtfile, binfile)
|
||||
quitit = False
|
||||
writeout('\x1b[2J\x1b[;H')
|
||||
prepend = ''
|
||||
try:
|
||||
while not quitit:
|
||||
newdata, delay = replay.get_output(False)
|
||||
if newdata:
|
||||
if prepend:
|
||||
newdata = prepend + newdata
|
||||
prepend = b''
|
||||
writeout(newdata.replace(b'\r\n', '\r\n[ {0} ] '.format(time.strftime('%m/%d %H:%M:%S', time.localtime(replay.laststamp))).encode('utf8')))
|
||||
newdata = ''
|
||||
try:
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
sys.stdout.buffer.flush()
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
except Exception:
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
raise
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
replay_to_console(sys.argv[1])
|
||||
|
||||
@@ -98,17 +98,24 @@ class GroupedData(object):
|
||||
self.byoutput = {}
|
||||
self.header = {}
|
||||
self.client = confluentconnection
|
||||
self.detectedpad = None
|
||||
|
||||
def generate_byoutput(self):
|
||||
self.byoutput = {}
|
||||
thepad = self.detectedpad if self.detectedpad else ''
|
||||
for n in self.bynode:
|
||||
output = '\n'.join(self.bynode[n])
|
||||
output = ''
|
||||
for ln in self.bynode[n]:
|
||||
output += ln.replace(thepad, '', 1) + '\n'
|
||||
if output not in self.byoutput:
|
||||
self.byoutput[output] = set([n])
|
||||
else:
|
||||
self.byoutput[output].add(n)
|
||||
|
||||
def add_line(self, node, line):
|
||||
wspc = re.search(r'^\s*', line).group()
|
||||
if self.detectedpad is None or len(wspc) < len(self.detectedpad):
|
||||
self.detectedpad = wspc
|
||||
if node not in self.bynode:
|
||||
self.bynode[node] = [line]
|
||||
else:
|
||||
@@ -219,4 +226,4 @@ if __name__ == '__main__':
|
||||
if not line:
|
||||
continue
|
||||
groupoutput.add_line(*line.split(': ', 1))
|
||||
groupoutput.print_deviants()
|
||||
groupoutput.print_deviants()
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
%define name confluent_client
|
||||
%define version #VERSION#
|
||||
%define fversion %{lua:
|
||||
sv, _ = string.gsub("#VERSION#", "[~+]", "-")
|
||||
print(sv)
|
||||
}
|
||||
%define release 1
|
||||
|
||||
Summary: Client libraries and utilities for confluent
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: %{release}
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Source0: %{name}-%{fversion}.tar.gz
|
||||
License: Apache2
|
||||
Group: Development/Libraries
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
|
||||
@@ -21,7 +25,7 @@ This package enables python development and command line access to
|
||||
a confluent server.
|
||||
|
||||
%prep
|
||||
%setup -n %{name}-%{version} -n %{name}-%{version}
|
||||
%setup -n %{name}-%{fversion}
|
||||
|
||||
%build
|
||||
%if "%{dist}" == ".el7"
|
||||
|
||||
@@ -153,11 +153,11 @@ _confluent_osimage_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 2 ]; then
|
||||
COMPREPLY=($(compgen -W "initialize import updateboot rebase" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "initialize import importcheck updateboot rebase" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'initialize' ]; then
|
||||
COMPREPLY=($(compgen -W "-h -u -s -t -i" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
elif [ ${CMPARGS[1]} == 'import' ]; then
|
||||
elif [ ${CMPARGS[1]} == 'import' ] || [ ${CMPARGS[1]} == 'importcheck' ]; then
|
||||
compopt -o default
|
||||
COMPREPLY=()
|
||||
return
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
l2traceroute(8) -- returns the layer 2 route through an Ethernet network managed by confluent given 2 end points.
|
||||
==============================
|
||||
## SYNOPSIS
|
||||
`l2traceroute [options] <start_node> <end_noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
**l2traceroute** is a command that returns the layer 2 route for the configered interfaces in nodeattrib.
|
||||
It can also be used with the -i and -e options to check against specific interfaces on the endpoints.
|
||||
|
||||
|
||||
## PREREQUISITES
|
||||
**l2traceroute** the net.<interface>.switch attributes have to be set on the end points if endpoint is not a switch
|
||||
|
||||
|
||||
## OPTIONS
|
||||
* ` -e` EFACE, --eface=INTERFACE
|
||||
interface to check against for the second end point
|
||||
* ` -i` INTERFACE, --interface=INTERFACE
|
||||
interface to check against for the first end point
|
||||
* ` -c` CUMULUS, --cumulus=CUMULUS
|
||||
return layer 2 route through cumulus switches only
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
|
||||
## EXAMPLES
|
||||
* Checking route between two nodes:
|
||||
`# l2traceroute_client n244 n1851`
|
||||
`n244 to n1851: ['switch114']`
|
||||
|
||||
* Checking route from one node to multiple nodes:
|
||||
`# l2traceroute_client n244 n1833,n1851`
|
||||
`n244 to n1833: ['switch114', 'switch7', 'switch32', 'switch253', 'switch85', 'switch72', 'switch21', 'switch2', 'switch96', 'switch103', 'switch115']
|
||||
n244 to n1851: ['switch114']`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
nodeapply(8) -- Execute command on many nodes in a noderange through ssh
|
||||
=========================================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeapply [options] <noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Provides shortcut access to a number of common operations against deployed
|
||||
nodes. These operations include refreshing ssh certificates and configuration,
|
||||
rerunning syncflies, and executing specified postscripts.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-k`, `--security`
|
||||
Refresh SSH configuration (hosts.equiv and node SSH certificates)
|
||||
|
||||
* `-F`, `--sync`
|
||||
Rerun syncfiles from deployed profile
|
||||
|
||||
* `-P SCRIPTS`, `--scripts=SCRIPTS`
|
||||
Re-run specified scripts, with full path under scripts specified, e.g. post.d/scriptname,firstboot.d/otherscriptname
|
||||
|
||||
* `-c COUNT`, `-f COUNT`, `--count=COUNT`
|
||||
Specify the maximum number of instances to run concurrently
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`
|
||||
Specify a maximum number of nodes to run remote ssh command to, prompting
|
||||
if over the threshold
|
||||
@@ -7,7 +7,8 @@ nodeattrib(8) -- List or change confluent nodes attributes
|
||||
`nodeattrib <noderange> [<nodeattribute1=value1> <nodeattribute2=value2> ...]`
|
||||
`nodeattrib -c <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib -e <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib <noderange> -s <attributes.batch> ...`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -53,12 +54,18 @@ to a blank value will allow masking a group defined attribute with an empty valu
|
||||
* `-e`, `--environment`:
|
||||
Set specified attributes based on exported environment variable of matching name.
|
||||
Environment variable names may be lower case or all upper case.
|
||||
Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE
|
||||
Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE)
|
||||
|
||||
* `-p`, `--prompt`:
|
||||
Request interactive prompting to provide values rather than the command line
|
||||
or environment variables.
|
||||
|
||||
|
||||
* `-s`, `--set`:
|
||||
Set attributes using a batch file rather than the command line. The attributes in the batch file
|
||||
can be specified as one line of key=value pairs simmilar to command line or each attribute can
|
||||
be in its own line. Lines that start with # sign will be read as a comment. See EXAMPLES for batch
|
||||
file syntax.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Prompt if trying to set attributes on more than
|
||||
specified number of nodes.
|
||||
@@ -116,6 +123,25 @@ to a blank value will allow masking a group defined attribute with an empty valu
|
||||
`d1: net.pxe.switch: pxeswitch1`
|
||||
`d1: net.switch:`
|
||||
|
||||
* Setting attributes using a batch file with syntax similar to command line:
|
||||
`# cat nodeattributes.batch`
|
||||
`# power`
|
||||
`power.psu1.outlet=3 power.psu1.pdu=pdu2`
|
||||
`# nodeattrib n41 -s nodeattributes.batch`
|
||||
`n41: 3`
|
||||
`n41: pdu2`
|
||||
|
||||
* Setting attributes using a batch file with syntax where each attribute is in its own line:
|
||||
`# cat nodeattributes.batch`
|
||||
`# management`
|
||||
`custom.mgt.switch=switch_main`
|
||||
`custom.mgt.switch.port=swp4`
|
||||
`# nodeattrib n41 -s nodeattributes.batch`
|
||||
`n41: switch_main`
|
||||
`n41: swp4`
|
||||
|
||||
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
nodegroupattrib(8), nodeattribexpressions(5)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
nodebmcpassword(8) -- Change management controller password for a specified user
|
||||
=========================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodebmcpassword <noderange> <username> <new_password>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodebmcpassword` allows you to change the management controller password for a user on a specified noderange
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Number of nodes to affect before prompting for
|
||||
confirmation
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES:
|
||||
|
||||
* Reset the management controller for nodes n1 through n4:
|
||||
`# nodebmcreset n1-n4`
|
||||
`n1: Password Change Successful`
|
||||
`n2: Password Change Successful`
|
||||
`n3: Password Change Successful`
|
||||
`n4: Password Change Successful`
|
||||
@@ -29,6 +29,11 @@ console process which will result in the console window closing.
|
||||
Perform a log reply on the current, local log in /var/log/confluent/consoles.
|
||||
If in collective mode, this only makes sense to use on the current collective
|
||||
manager at this time.
|
||||
|
||||
* `-T`, `--Timestamp`:
|
||||
Dump the log with Timpstamps on the current, local log in /var/log/confluent/consoles.
|
||||
If in collective mode, this only makes sense to use on the current collective
|
||||
manager at this time.
|
||||
|
||||
* `-w`, `--windowed`:
|
||||
Open terminal windows for each node. The
|
||||
|
||||
@@ -4,7 +4,7 @@ nodelist(8) -- List confluent nodes and their attributes
|
||||
## SYNOPSIS
|
||||
|
||||
`nodelist <noderange>`
|
||||
`nodelist <noderange> [-b] <nodeattribute>...`
|
||||
`nodelist <noderange> [-b] [-d] {string} <nodeattribute>...`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -24,7 +24,8 @@ all attributes that begin with `net.` and end with `switch`.
|
||||
|
||||
* `-b`, `--blame`:
|
||||
Annotate inherited and expression based attributes to show their base value.
|
||||
|
||||
* `-d`, `--delim`:
|
||||
Choose a delimiter to separat the values. Default - ENTER.
|
||||
## EXAMPLES
|
||||
* Listing matching nodes of a simple noderange:
|
||||
`# nodelist n1-n4`
|
||||
|
||||
@@ -16,6 +16,9 @@ noderange. This will present progress as percentage for all nodes.
|
||||
Specify how many rsync executions to do concurrently. If noderange
|
||||
exceeds the count, then excess nodes will wait until one of the
|
||||
active count completes.
|
||||
|
||||
* `-s`, `--substitutename`:
|
||||
'Use a different name other than the nodename for rsync'
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to run rsync to, prompting if over the
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
VERSION=`git describe|cut -d- -f 1`
|
||||
NUMCOMMITS=`git describe|cut -d- -f 2`
|
||||
if [ "$NUMCOMMITS" != "$VERSION" ]; then
|
||||
VERSION=$VERSION.dev$NUMCOMMITS.g`git describe|cut -d- -f 3`
|
||||
LASTNUM=$(echo $VERSION|rev|cut -d . -f 1|rev)
|
||||
LASTNUM=$((LASTNUM+1))
|
||||
FIRSTPART=$(echo $VERSION|rev|cut -d . -f 2- |rev)
|
||||
VERSION=${FIRSTPART}.${LASTNUM}
|
||||
VERSION=$VERSION~dev$NUMCOMMITS+`git describe|cut -d- -f 3`
|
||||
fi
|
||||
sed -e "s/#VERSION#/$VERSION/" confluent_osdeploy.spec.tmpl > confluent_osdeploy.spec
|
||||
cd ..
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
cd $(dirname $0)
|
||||
VERSION=`git describe|cut -d- -f 1`
|
||||
NUMCOMMITS=`git describe|cut -d- -f 2`
|
||||
if [ "$NUMCOMMITS" != "$VERSION" ]; then
|
||||
VERSION=$VERSION.dev$NUMCOMMITS.g`git describe|cut -d- -f 3`
|
||||
LASTNUM=$(echo $VERSION|rev|cut -d . -f 1|rev)
|
||||
LASTNUM=$((LASTNUM+1))
|
||||
FIRSTPART=$(echo $VERSION|rev|cut -d . -f 2- |rev)
|
||||
VERSION=${FIRSTPART}.${LASTNUM}
|
||||
VERSION=$VERSION~dev$NUMCOMMITS+`git describe|cut -d- -f 3`
|
||||
fi
|
||||
sed -e "s/#VERSION#/$VERSION/" confluent_osdeploy-aarch64.spec.tmpl > confluent_osdeploy-aarch64.spec
|
||||
cd ..
|
||||
@@ -29,4 +34,5 @@ mv confluent_el8bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
mv confluent_el9bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
rm -rf el9bin
|
||||
rm -rf el8bin
|
||||
rpmbuild -ba confluent_osdeploy-aarch64.spec
|
||||
podman run --privileged --rm -v $HOME:/root el8builder rpmbuild -ba /root/confluent/confluent_osdeploy/confluent_osdeploy-aarch64.spec
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ class InvalidApiKey(Exception):
|
||||
def msg_align(len):
|
||||
return (len + 3) & ~3
|
||||
|
||||
try:
|
||||
_protoparm = ssl.PROTOCOL_TLS_CLIENT
|
||||
except AttributeError:
|
||||
_protoparm = ssl.PROTOCOL_SSLv23
|
||||
cryptname = ctypes.util.find_library('crypt')
|
||||
if not cryptname:
|
||||
if os.path.exists('/lib64/libcrypt.so.2') or os.path.exists('/usr/lib64/libcrypt.so.2'):
|
||||
@@ -223,7 +227,7 @@ def get_apikey(nodename, hosts, errout=None):
|
||||
return apikey
|
||||
|
||||
class HTTPSClient(client.HTTPConnection, object):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False):
|
||||
self.phmac = phmac
|
||||
self.errout = None
|
||||
if errout:
|
||||
@@ -287,7 +291,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
if self.phmac:
|
||||
with open(phmac, 'r') as hmacin:
|
||||
self.stdheaders['CONFLUENT_CRYPTHMAC'] = hmacin.read()
|
||||
else:
|
||||
elif not checkonly:
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, self.hosts, errout=self.errout)
|
||||
if mgtiface:
|
||||
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
|
||||
@@ -305,7 +309,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
def check_connections(self):
|
||||
foundsrv = None
|
||||
hosts = self.hosts
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx = ssl.SSLContext(_protoparm)
|
||||
ctx.load_verify_locations('/etc/confluent/ca.pem')
|
||||
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||
ctx.check_hostname = True
|
||||
@@ -321,7 +325,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
foundsrv = host
|
||||
psock.close()
|
||||
break
|
||||
except OSError:
|
||||
except (OSError, socket.error):
|
||||
continue
|
||||
except ssl.SSLError:
|
||||
continue
|
||||
@@ -353,11 +357,12 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
raise Exception('Unable to reach any hosts')
|
||||
return foundsrv
|
||||
|
||||
def connect(self):
|
||||
def connect(self, tries=3):
|
||||
addrinf = socket.getaddrinfo(self.host, self.port)[0]
|
||||
psock = socket.socket(addrinf[0])
|
||||
psock.settimeout(15)
|
||||
psock.connect(addrinf[4])
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx = ssl.SSLContext(_protoparm)
|
||||
ctx.load_verify_locations('/etc/confluent/ca.pem')
|
||||
host = self.host.split('%', 1)[0]
|
||||
if '[' not in host and ':' in host:
|
||||
@@ -375,6 +380,10 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
self.errout.write(errmsg)
|
||||
self.errout.flush()
|
||||
sys.exit(1)
|
||||
except socket.timeout:
|
||||
if not tries:
|
||||
raise
|
||||
return self.connect(tries=tries-1)
|
||||
|
||||
def grab_url(self, url, data=None, returnrsp=False):
|
||||
return self.grab_url_with_status(url, data, returnrsp)[1]
|
||||
@@ -459,7 +468,7 @@ if __name__ == '__main__':
|
||||
outf.write(chunk)
|
||||
chunk = reader.read(16384)
|
||||
sys.exit(0)
|
||||
client = HTTPSClient(usejson, errout=errout, phmac=phmac)
|
||||
client = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly)
|
||||
if waitfor:
|
||||
status = 201
|
||||
while status != waitfor:
|
||||
|
||||
@@ -8,6 +8,10 @@ import sys
|
||||
import time
|
||||
import shlex
|
||||
import subprocess
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from importlib.machinery import SourceFileLoader
|
||||
def load_source(mod, path):
|
||||
@@ -82,7 +86,7 @@ def map_idx_to_name():
|
||||
for line in subprocess.check_output(['ip', 'l']).decode('utf8').splitlines():
|
||||
if line.startswith(' ') and 'link/' in line:
|
||||
typ = line.split()[0].split('/')[1]
|
||||
devtype[prevdev] = typ if type != 'ether' else 'ethernet'
|
||||
devtype[prevdev] = typ if typ != 'ether' else 'ethernet'
|
||||
if line.startswith(' '):
|
||||
continue
|
||||
idx, iface, rst = line.split(':', 2)
|
||||
@@ -107,6 +111,106 @@ def get_interface_name(iname, settings):
|
||||
return iname
|
||||
return None
|
||||
|
||||
class NetplanManager(object):
|
||||
def __init__(self, deploycfg):
|
||||
self.cfgbydev = {}
|
||||
self.read_connections()
|
||||
self.deploycfg = deploycfg
|
||||
|
||||
def read_connections(self):
|
||||
for plan in glob.glob('/etc/netplan/*.y*ml'):
|
||||
with open(plan) as planfile:
|
||||
planinfo = yaml.safe_load(planfile)
|
||||
if not planinfo:
|
||||
continue
|
||||
nicinfo = planinfo.get('network', {}).get('ethernets', {})
|
||||
for devname in nicinfo:
|
||||
if devname == 'lo':
|
||||
continue
|
||||
if 'gateway4' in nicinfo[devname]:
|
||||
# normalize deprecated syntax on read in
|
||||
gw4 = nicinfo[devname]['gateway4']
|
||||
del nicinfo[devname]['gateway4']
|
||||
routeinfo = nicinfo[devname].get('routes', [])
|
||||
for ri in routeinfo:
|
||||
if ri.get('via', None) == gw4 and ri.get('to', None) in ('default', '0.0.0.0/0', '0/0'):
|
||||
break
|
||||
else:
|
||||
routeinfo.append({
|
||||
'to': 'default',
|
||||
'via': gw4
|
||||
})
|
||||
nicinfo[devname]['routes'] = routeinfo
|
||||
self.cfgbydev[devname] = nicinfo[devname]
|
||||
|
||||
def apply_configuration(self, cfg):
|
||||
devnames = cfg['interfaces']
|
||||
if len(devnames) != 1:
|
||||
raise Exception('Multi-nic team/bonds not yet supported')
|
||||
stgs = cfg['settings']
|
||||
needcfgapply = False
|
||||
for devname in devnames:
|
||||
needcfgwrite = False
|
||||
# ipv6_method missing at uconn...
|
||||
if stgs.get('ipv6_method', None) == 'static':
|
||||
curraddr = stgs['ipv6_address']
|
||||
currips = self.getcfgarrpath([devname, 'addresses'])
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
if stgs.get('ipv4_method', None) == 'static':
|
||||
curraddr = stgs['ipv4_address']
|
||||
currips = self.getcfgarrpath([devname, 'addresses'])
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
gws = []
|
||||
gws.append(stgs.get('ipv4_gateway', None))
|
||||
gws.append(stgs.get('ipv6_gateway', None))
|
||||
for gwaddr in gws:
|
||||
if gwaddr:
|
||||
cfgroutes = self.getcfgarrpath([devname, 'routes'])
|
||||
for rinfo in cfgroutes:
|
||||
if rinfo.get('via', None) == gwaddr:
|
||||
break
|
||||
else:
|
||||
needcfgwrite = True
|
||||
cfgroutes.append({'via': gwaddr, 'to': 'default'})
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
dnsdomain = self.deploycfg.get('dnsdomain', '')
|
||||
if dnsips:
|
||||
currdnsips = self.getcfgarrpath([devname, 'nameservers', 'addresses'])
|
||||
for dnsip in dnsips:
|
||||
if dnsip and dnsip not in currdnsips:
|
||||
needcfgwrite = True
|
||||
currdnsips.append(dnsip)
|
||||
if dnsdomain:
|
||||
currdnsdomain = self.getcfgarrpath([devname, 'nameservers', 'search'])
|
||||
if dnsdomain not in currdnsdomain:
|
||||
needcfgwrite = True
|
||||
currdnsdomain.append(dnsdomain)
|
||||
if needcfgwrite:
|
||||
needcfgapply = True
|
||||
newcfg = {'network': {'version': 2, 'ethernets': {devname: self.cfgbydev[devname]}}}
|
||||
oumask = os.umask(0o77)
|
||||
with open('/etc/netplan/{0}-confluentcfg.yaml'.format(devname), 'w') as planout:
|
||||
planout.write(yaml.dump(newcfg))
|
||||
os.umask(oumask)
|
||||
if needcfgapply:
|
||||
subprocess.call(['netplan', 'apply'])
|
||||
|
||||
def getcfgarrpath(self, devpath):
|
||||
currptr = self.cfgbydev
|
||||
for k in devpath[:-1]:
|
||||
if k not in currptr:
|
||||
currptr[k] = {}
|
||||
currptr = currptr[k]
|
||||
if devpath[-1] not in currptr:
|
||||
currptr[devpath[-1]] = []
|
||||
return currptr[devpath[-1]]
|
||||
|
||||
|
||||
|
||||
class WickedManager(object):
|
||||
def __init__(self):
|
||||
self.teamidx = 0
|
||||
@@ -193,7 +297,8 @@ class WickedManager(object):
|
||||
|
||||
|
||||
class NetworkManager(object):
|
||||
def __init__(self, devtypes):
|
||||
def __init__(self, devtypes, deploycfg):
|
||||
self.deploycfg = deploycfg
|
||||
self.connections = {}
|
||||
self.uuidbyname = {}
|
||||
self.uuidbydev = {}
|
||||
@@ -243,7 +348,7 @@ class NetworkManager(object):
|
||||
bondcfg[stg] = deats[stg]
|
||||
if member in self.uuidbyname:
|
||||
subprocess.check_call(['nmcli', 'c', 'del', self.uuidbyname[member]])
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'team-slave', 'master', team, 'con-name', member, 'connection.interface-name', member])
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond-slave', 'master', team, 'con-name', member, 'connection.interface-name', member])
|
||||
if bondcfg:
|
||||
args = []
|
||||
for parm in bondcfg:
|
||||
@@ -253,6 +358,7 @@ class NetworkManager(object):
|
||||
|
||||
def apply_configuration(self, cfg):
|
||||
cmdargs = {}
|
||||
cmdargs['connection.autoconnect'] = 'yes'
|
||||
stgs = cfg['settings']
|
||||
cmdargs['ipv6.method'] = stgs.get('ipv6_method', 'link-local')
|
||||
if stgs.get('ipv6_address', None):
|
||||
@@ -264,6 +370,20 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
|
||||
if stgs.get('ipv6_gateway', None):
|
||||
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
if not dnsips:
|
||||
dnsips = []
|
||||
dns4 = []
|
||||
dns6 = []
|
||||
for dnsip in dnsips:
|
||||
if '.' in dnsip:
|
||||
dns4.append(dnsip)
|
||||
elif ':' in dnsip:
|
||||
dns6.append(dnsip)
|
||||
if dns4:
|
||||
cmdargs['ipv4.dns'] = ','.join(dns4)
|
||||
if dns6:
|
||||
cmdargs['ipv6.dns'] = ','.join(dns6)
|
||||
if len(cfg['interfaces']) > 1: # team time.. should be..
|
||||
if not cfg['settings'].get('team_mode', None):
|
||||
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
|
||||
@@ -276,26 +396,45 @@ class NetworkManager(object):
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'team', 'con-name', cname, 'connection.interface-name', cname, 'team.runner', stgs['team_mode']] + cargs)
|
||||
if stgs['team_mode'] == 'lacp':
|
||||
stgs['team_mode'] = '802.3ad'
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
for iface in cfg['interfaces']:
|
||||
self.add_team_member(cname, iface)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', cname])
|
||||
else:
|
||||
cname = stgs.get('connection_name', None)
|
||||
iname = list(cfg['interfaces'])[0]
|
||||
if not cname:
|
||||
cname = iname
|
||||
ctype = self.devtypes.get(iname, None)
|
||||
if not ctype:
|
||||
sys.stderr.write("Warning, no device found for interface_name ({0}), skipping setup\n".format(iname))
|
||||
return
|
||||
if stgs.get('vlan_id', None):
|
||||
vlan = stgs['vlan_id']
|
||||
if ctype == 'infiniband':
|
||||
vlan = '0x{0}'.format(vlan) if not vlan.startswith('0x') else vlan
|
||||
cmdargs['infiniband.parent'] = iname
|
||||
cmdargs['infiniband.p-key'] = vlan
|
||||
iname = '{0}.{1}'.format(iname, vlan[2:])
|
||||
elif ctype == 'ethernet':
|
||||
ctype = 'vlan'
|
||||
cmdargs['vlan.parent'] = iname
|
||||
cmdargs['vlan.id'] = vlan
|
||||
iname = '{0}.{1}'.format(iname, vlan)
|
||||
else:
|
||||
sys.stderr.write("Warning, unknown interface_name ({0}) device type ({1}) for VLAN/PKEY, skipping setup\n".format(iname, ctype))
|
||||
return
|
||||
cname = iname if not cname else cname
|
||||
u = self.uuidbyname.get(cname, None)
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
if u:
|
||||
cmdargs['connection.interface-name'] = iname
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', u])
|
||||
else:
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', self.devtypes[iname], 'con-name', cname, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', ctype, 'con-name', cname, 'connection.interface-name', iname] + cargs)
|
||||
self.read_connections()
|
||||
u = self.uuidbyname.get(cname, None)
|
||||
if u:
|
||||
@@ -315,8 +454,18 @@ if __name__ == '__main__':
|
||||
myaddrs = apiclient.get_my_addresses()
|
||||
srvs, _ = apiclient.scan_confluents()
|
||||
doneidxs = set([])
|
||||
dc = None
|
||||
if not srvs: # the multicast scan failed, fallback to deploycfg cfg file
|
||||
with open('/etc/confluent/confluent.deploycfg', 'r') as dci:
|
||||
for cfgline in dci.read().split('\n'):
|
||||
if cfgline.startswith('deploy_server:'):
|
||||
srvs = [cfgline.split()[1]]
|
||||
break
|
||||
for srv in srvs:
|
||||
s = socket.create_connection((srv, 443))
|
||||
try:
|
||||
s = socket.create_connection((srv, 443))
|
||||
except socket.error:
|
||||
continue
|
||||
myname = s.getsockname()
|
||||
s.close()
|
||||
if len(myname) == 4:
|
||||
@@ -329,8 +478,27 @@ if __name__ == '__main__':
|
||||
curridx = addr[-1]
|
||||
if curridx in doneidxs:
|
||||
continue
|
||||
status, nc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/netcfg')
|
||||
for tries in (1, 2, 3):
|
||||
try:
|
||||
status, nc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/netcfg')
|
||||
break
|
||||
except Exception:
|
||||
if tries == 3:
|
||||
raise
|
||||
time.sleep(1)
|
||||
continue
|
||||
nc = json.loads(nc)
|
||||
if not dc:
|
||||
for tries in (1, 2, 3):
|
||||
try:
|
||||
status, dc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/deploycfg2')
|
||||
break
|
||||
except Exception:
|
||||
if tries == 3:
|
||||
raise
|
||||
time.sleep(1)
|
||||
continue
|
||||
dc = json.loads(dc)
|
||||
iname = get_interface_name(idxmap[curridx], nc.get('default', {}))
|
||||
if iname:
|
||||
for iname in iname.split(','):
|
||||
@@ -355,9 +523,13 @@ if __name__ == '__main__':
|
||||
netname_to_interfaces['default']['interfaces'] -= netname_to_interfaces[netn]['interfaces']
|
||||
if not netname_to_interfaces['default']['interfaces']:
|
||||
del netname_to_interfaces['default']
|
||||
# Make sure VLAN/PKEY connections are created last
|
||||
netname_to_interfaces = dict(sorted(netname_to_interfaces.items(), key=lambda item: 'vlan_id' in item[1]['settings']))
|
||||
rm_tmp_llas(tmpllas)
|
||||
if os.path.exists('/usr/sbin/netplan'):
|
||||
nm = NetplanManager(dc)
|
||||
if os.path.exists('/usr/bin/nmcli'):
|
||||
nm = NetworkManager(devtypes)
|
||||
nm = NetworkManager(devtypes, dc)
|
||||
elif os.path.exists('/usr/sbin/wicked'):
|
||||
nm = WickedManager()
|
||||
for netn in netname_to_interfaces:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
is_suse=false
|
||||
is_rhel=false
|
||||
|
||||
if test -f /boot/efi/EFI/redhat/grub.cfg; then
|
||||
grubcfg="/boot/efi/EFI/redhat/grub.cfg"
|
||||
grub2-mkconfig -o $grubcfg
|
||||
is_rhel=true
|
||||
elif test -f /boot/efi/EFI/sle_hpc/grub.cfg; then
|
||||
grubcfg="/boot/efi/EFI/sle_hpc/grub.cfg"
|
||||
grub2-mkconfig -o $grubcfg
|
||||
is_suse=true
|
||||
else
|
||||
echo "Expected File missing: Check if os sle_hpc or redhat"
|
||||
exit
|
||||
fi
|
||||
|
||||
# working on SUSE
|
||||
if $is_suse; then
|
||||
start=false
|
||||
num_line=0
|
||||
lines_to_edit=()
|
||||
while read line; do
|
||||
((num_line++))
|
||||
if [[ $line == *"grub_platform"* ]]; then
|
||||
start=true
|
||||
fi
|
||||
if $start; then
|
||||
if [[ $line != "#"* ]];then
|
||||
lines_to_edit+=($num_line)
|
||||
fi
|
||||
fi
|
||||
if [[ ${#line} -eq 2 && $line == *"fi" ]]; then
|
||||
if $start; then
|
||||
start=false
|
||||
fi
|
||||
fi
|
||||
done < grub_cnf.cfg
|
||||
|
||||
for line_num in "${lines_to_edit[@]}"; do
|
||||
line_num+="s"
|
||||
sed -i "${line_num},^,#," $grubcfg
|
||||
done
|
||||
sed -i 's,^terminal,#terminal,' $grubcfg
|
||||
fi
|
||||
|
||||
# Working on Redhat
|
||||
if $is_rhel; then
|
||||
sed -i 's,^serial,#serial, ; s,^terminal,#terminal,' $grubcfg
|
||||
fi
|
||||
@@ -3,6 +3,9 @@
|
||||
[ -f /opt/confluent/bin/apiclient ] && confapiclient=/opt/confluent/bin/apiclient
|
||||
[ -f /etc/confluent/apiclient ] && confapiclient=/etc/confluent/apiclient
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
if [ "$pubkey" = /etc/ssh/ssh_host_key.pub ]; then
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
rm $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
|
||||
@@ -26,7 +26,7 @@ mkdir -p opt/confluent/bin
|
||||
mkdir -p stateless-bin
|
||||
cp -a el8bin/* .
|
||||
ln -s el8 el9
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 coreos el9; do
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -76,7 +76,7 @@ cp -a esxi7 esxi8
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
#cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu22.04 esxi6 esxi7 esxi8 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
if [ -d ${os}disklessout ]; then
|
||||
|
||||
@@ -28,7 +28,7 @@ This contains support utilities for enabling deployment of x86_64 architecture s
|
||||
#cp start_root urlmount ../stateless-bin/
|
||||
#cd ..
|
||||
ln -s el8 el9
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 coreos el9; do
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -42,7 +42,7 @@ for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 coreo
|
||||
mv ../addons.cpio .
|
||||
cd ..
|
||||
done
|
||||
for os in el7 el8 suse15 el9 ubuntu20.04; do
|
||||
for os in el7 el8 suse15 el9 ubuntu20.04 ubuntu22.04 ubuntu24.04; do
|
||||
mkdir ${os}disklessout
|
||||
cd ${os}disklessout
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -78,7 +78,7 @@ cp -a esxi7 esxi8
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 esxi6 esxi7 esxi8 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -11,6 +11,13 @@ import struct
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def get_partname(devname, idx):
|
||||
if devname[-1] in '0123456789':
|
||||
return '{}p{}'.format(devname, idx)
|
||||
else:
|
||||
return '{}{}'.format(devname, idx)
|
||||
|
||||
|
||||
def get_next_part_meta(img, imgsize):
|
||||
if img.tell() == imgsize:
|
||||
return None
|
||||
@@ -53,10 +60,13 @@ class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
self.disk = disk
|
||||
|
||||
def run(self, command):
|
||||
def run(self, command, check=True):
|
||||
command = command.split()
|
||||
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
if check:
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
else:
|
||||
return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8')
|
||||
|
||||
def fixup(rootdir, vols):
|
||||
devbymount = {}
|
||||
@@ -166,6 +176,8 @@ def fixup(rootdir, vols):
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if targblock.endswith('p') and 'nvme' in targblock:
|
||||
targblock = targblock[:-1]
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
@@ -224,7 +236,8 @@ def install_to_disk(imgpath):
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
parted = PartedRunner(instdisk)
|
||||
dinfo = parted.run('unit s print')
|
||||
# do this safer, unit s print might bomb
|
||||
dinfo = parted.run('unit s print', check=False)
|
||||
dinfo = dinfo.split('\n')
|
||||
sectors = 0
|
||||
sectorsize = 0
|
||||
@@ -258,7 +271,7 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
|
||||
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
|
||||
vol['targetdisk'] = get_partname(instdisk , volidx)
|
||||
curroffset += size + 1
|
||||
if not lvmvols:
|
||||
if swapsize:
|
||||
@@ -268,10 +281,10 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
|
||||
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
|
||||
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
|
||||
else:
|
||||
parted.run('mkpart lvm {}s 100%'.format(curroffset))
|
||||
lvmpart = instdisk + '{}'.format(volidx + 1)
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
|
||||
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
|
||||
|
||||
@@ -125,4 +125,6 @@ if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
fi
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import time
|
||||
import importlib
|
||||
import tempfile
|
||||
import json
|
||||
@@ -223,6 +224,7 @@ def synchronize():
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(2)
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
|
||||
@@ -7,41 +7,127 @@ if [ -f /tmp/dd_disk ]; then
|
||||
fi
|
||||
done
|
||||
fi
|
||||
mkdir -p /etc/confluent
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
TRIES=0
|
||||
echo -n > /etc/confluent/confluent.info
|
||||
cd /sys/class/net
|
||||
while ! awk -F'|' '{print $3}' /etc/confluent/confluent.info |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
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
#TODO: blkid --label <whatever> to find mounted api
|
||||
|
||||
shutdownnic=""
|
||||
oum=$(umask)
|
||||
python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
|
||||
ifidx=$(cat /tmp/confluent.ifidx)
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
umask 0077
|
||||
mkdir -p /etc/confluent
|
||||
echo -n > /etc/confluent/confluent.info
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
TRIES=5
|
||||
while [ ! -e /dev/disk ] && [ $TRIES -gt 0 ]; do
|
||||
sleep 2
|
||||
TRIES=$((TRIES - 1))
|
||||
done
|
||||
if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
tmnt=/tmp/idntmnt
|
||||
mkdir -p $tmnt
|
||||
tcfg=/tmp/idnttmp
|
||||
mount /dev/disk/by-label/CNFLNT_IDNT $tmnt
|
||||
cd $tmnt
|
||||
deploysrvs=$(sed -n '/^deploy_servers:/, /^[^-]/p' cnflnt.yml |grep ^-|sed -e 's/^- //'|grep -v :)
|
||||
|
||||
nodename=$(grep ^nodename: cnflnt.yml|awk '{print $2}')
|
||||
ln -s /opt/confluent/bin/clortho /opt/confluent/bin/genpasshmac
|
||||
hmackeyfile=/tmp/hmackeyfile
|
||||
passfile=/etc/confluent/confluent.apikey
|
||||
passcrypt=/tmp/passcrypt
|
||||
hmacfile=/tmp/hmacfile
|
||||
echo -n $(grep ^apitoken: cnflnt.yml|awk '{print $2}') > $hmackeyfile;
|
||||
/opt/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
|
||||
echo "NODENAME: "$nodename > /etc/confluent/confluent.info
|
||||
for dsrv in $deploysrvs; do
|
||||
echo 'MANAGER: '$dsrv >> /etc/confluent/confluent.info
|
||||
done
|
||||
sed -n '/^net_cfgs:/, /^[^- ]/{/^[^- ]/!p}' cnflnt.yml |sed -n '/^-/, /^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
|
||||
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
|
||||
autoconfigmethod=${autoconfigmethod#ipv4_method: }
|
||||
for i in /sys/class/net/*; do
|
||||
ip link set $(basename $i) down
|
||||
udevadm info $i | grep ID_NET_DRIVER=cdc_ether > /dev/null && continue
|
||||
ip link set $(basename $i) up
|
||||
done
|
||||
sleep 10
|
||||
usedhcp=0
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK| awk '{print $2}' | sed -e 's/:$//'); do
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
usedhcp=1
|
||||
else
|
||||
v4addr=$(grep ^ipv4_address: $tcfg)
|
||||
v4addr=${v4addr#ipv4_address: }
|
||||
v4plen=${v4addr#*/}
|
||||
v4addr=${v4addr%/*}
|
||||
v4gw=$(grep ^ipv4_gateway: $tcfg)
|
||||
v4gw=${v4gw#ipv4_gateway: }
|
||||
ip addr add dev $NICGUESS $v4addr/$v4plen
|
||||
if [ "$v4gw" = "null" ]; then
|
||||
v4gw=""
|
||||
fi
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route add default via $v4gw
|
||||
fi
|
||||
v4nm=$(grep ipv4_netmask: $tcfg)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
TESTSRV=$(python /opt/confluent/bin/apiclient -c 2> /dev/null)
|
||||
if [ ! -z "$TESTSRV" ]; then
|
||||
python /opt/confluent/bin/apiclient -p $hmacfile /confluent-api/self/registerapikey $passcrypt
|
||||
mgr=$TESTSRV
|
||||
ifname=$NICGUESS
|
||||
shutdownnic=$ifname
|
||||
break
|
||||
fi
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route del default via $v4gw
|
||||
fi
|
||||
ip -4 addr flush dev $NICGUESS
|
||||
fi
|
||||
done
|
||||
fi
|
||||
TRIES=0
|
||||
if [ "$usedhcp" = 1 ]; then
|
||||
echo ip=$ifname:dhcp >> /etc/cmdline.d/01-confluent.conf
|
||||
elif [ -z "$ifname" ]; then
|
||||
cd /sys/class/net
|
||||
while ! awk -F'|' '{print $3}' /etc/confluent/confluent.info |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 "" > /etc/cmdline.d/01-confluent.conf
|
||||
else
|
||||
echo ip=$v4addr::$v4gw:$v4nm:$hostname:$ifname:none > /etc/cmdline.d/01-confluent.conf
|
||||
fi
|
||||
python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
|
||||
if [ ! -z "$shutdownnic" ]; then
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route del default via $v4gw
|
||||
fi
|
||||
ip -4 addr flush dev $shutdownnic
|
||||
fi
|
||||
# restart cmdline
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
echo -n "" > /tmp/confluent.initq
|
||||
if [ -z "$ifname" ]; then
|
||||
ifidx=$(cat /tmp/confluent.ifidx)
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
fi
|
||||
if [ ! -z "$ifname" ]; then
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
fi
|
||||
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 [ -z "$mgr" ]; then
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
|
||||
mgr=${mgr#deploy_server: }
|
||||
fi
|
||||
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
|
||||
profilename=${profilename#profile: }
|
||||
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
|
||||
@@ -65,21 +151,23 @@ kickstart=$proto://$mgr/confluent-public/os/$profilename/kickstart
|
||||
root=anaconda-net:$proto://$mgr/confluent-public/os/$profilename/distribution
|
||||
export kickstart
|
||||
export root
|
||||
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=""
|
||||
if [ -z "$autoconfigmethod" ]; then
|
||||
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
|
||||
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
|
||||
fi
|
||||
nameserversec=0
|
||||
while read -r entry; do
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -5,6 +5,7 @@ import json
|
||||
import os
|
||||
import shutil
|
||||
import pwd
|
||||
import time
|
||||
import grp
|
||||
try:
|
||||
from importlib.machinery import SourceFileLoader
|
||||
@@ -223,6 +224,7 @@ def synchronize():
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(2)
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
|
||||
+10
-6
@@ -155,7 +155,7 @@ fi
|
||||
ready=0
|
||||
while [ $ready = "0" ]; do
|
||||
get_remote_apikey
|
||||
if [[ $confluent_mgr == *:* ]]; then
|
||||
if [[ $confluent_mgr == *:* ]] && [[ $confluent_mgr != "["* ]]; then
|
||||
confluent_mgr="[$confluent_mgr]"
|
||||
fi
|
||||
tmperr=$(mktemp)
|
||||
@@ -189,8 +189,15 @@ cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
EOC
|
||||
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
linktype=$(ip link show dev ${ifname}|grep link/|awk '{print $1}')
|
||||
if [ "$linktype" = link/infiniband ]; then
|
||||
linktype="infiniband"
|
||||
else
|
||||
linktype="ethernet"
|
||||
fi
|
||||
echo type=$linktype >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
type=ethernet
|
||||
autoconnect-retries=1
|
||||
EOC
|
||||
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
@@ -199,9 +206,6 @@ multi-connect=1
|
||||
permissions=
|
||||
wait-device-timeout=60000
|
||||
|
||||
[ethernet]
|
||||
mac-address-blacklist=
|
||||
|
||||
EOC
|
||||
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
auto6configmethod=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
@@ -320,7 +324,7 @@ fi
|
||||
echo '[proxy]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
|
||||
confluent_websrv=$confluent_mgr
|
||||
if [[ $confluent_websrv == *:* ]]; then
|
||||
if [[ $confluent_websrv == *:* ]] && [[ $confluent_websrv != "["* ]]; then
|
||||
confluent_websrv="[$confluent_websrv]"
|
||||
fi
|
||||
echo -n "Initializing ssh..."
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -10,6 +10,15 @@ import stat
|
||||
import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
bootuuid = None
|
||||
|
||||
def get_partname(devname, idx):
|
||||
if devname[-1] in '0123456789':
|
||||
return '{}p{}'.format(devname, idx)
|
||||
else:
|
||||
return '{}{}'.format(devname, idx)
|
||||
|
||||
def get_next_part_meta(img, imgsize):
|
||||
if img.tell() == imgsize:
|
||||
@@ -53,10 +62,13 @@ class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
self.disk = disk
|
||||
|
||||
def run(self, command):
|
||||
def run(self, command, check=True):
|
||||
command = command.split()
|
||||
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
if check:
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
else:
|
||||
return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8')
|
||||
|
||||
def fixup(rootdir, vols):
|
||||
devbymount = {}
|
||||
@@ -125,9 +137,14 @@ def fixup(rootdir, vols):
|
||||
sys.stdout.flush()
|
||||
for metafs in ('proc', 'sys', 'dev'):
|
||||
subprocess.check_call(['mount', '-o', 'bind', '/{}'.format(metafs), os.path.join(rootdir, metafs)])
|
||||
with open(os.path.join(rootdir, 'etc/sysconfig/grub')) as defgrubin:
|
||||
if os.path.exists(os.path.join(rootdir, 'etc/lvm/devices/system.devices')):
|
||||
os.remove(os.path.join(rootdir, 'etc/lvm/devices/system.devices'))
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
with open(os.path.join(rootdir, 'etc/sysconfig/grub'), 'w') as defgrubout:
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
newline = []
|
||||
@@ -136,8 +153,34 @@ def fixup(rootdir, vols):
|
||||
continue
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/')
|
||||
subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg])
|
||||
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/').replace('//', '/')
|
||||
grubcfg = grubcfg.split('\n')
|
||||
if not grubcfg[-1]:
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
if os.stat(rgcfg).st_size > 256:
|
||||
grubcfg = gcfg
|
||||
else:
|
||||
with open(rgcfg, 'r') as gin:
|
||||
tgrubcfg = gin.read()
|
||||
tgrubcfg = tgrubcfg.split('\n')
|
||||
if 'search --no-floppy --fs-uuid --set=dev' in tgrubcfg[0]:
|
||||
tgrubcfg[0] = 'search --no-floppy --fs-uuid --set=dev ' + bootuuid
|
||||
with open(rgcfg, 'w') as gout:
|
||||
for gcline in tgrubcfg:
|
||||
gout.write(gcline)
|
||||
gout.write('\n')
|
||||
try:
|
||||
subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg])
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
print(rootdir)
|
||||
print(grubcfg)
|
||||
time.sleep(86400)
|
||||
newroot = None
|
||||
with open('/etc/shadow') as shadowin:
|
||||
shents = shadowin.read().split('\n')
|
||||
@@ -166,6 +209,8 @@ def fixup(rootdir, vols):
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if 'nvme' in targblock and targblock[-1] == 'p':
|
||||
targblock = targblock[:-1]
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
@@ -184,6 +229,7 @@ def had_swap():
|
||||
return False
|
||||
|
||||
def install_to_disk(imgpath):
|
||||
global bootuuid
|
||||
lvmvols = {}
|
||||
deftotsize = 0
|
||||
mintotsize = 0
|
||||
@@ -224,7 +270,7 @@ def install_to_disk(imgpath):
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
parted = PartedRunner(instdisk)
|
||||
dinfo = parted.run('unit s print')
|
||||
dinfo = parted.run('unit s print', check=False)
|
||||
dinfo = dinfo.split('\n')
|
||||
sectors = 0
|
||||
sectorsize = 0
|
||||
@@ -258,7 +304,7 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
|
||||
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
|
||||
vol['targetdisk'] = get_partname(instdisk, volidx)
|
||||
curroffset += size + 1
|
||||
if not lvmvols:
|
||||
if swapsize:
|
||||
@@ -268,10 +314,10 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
|
||||
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
|
||||
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
|
||||
else:
|
||||
parted.run('mkpart lvm {}s 100%'.format(curroffset))
|
||||
lvmpart = instdisk + '{}'.format(volidx + 1)
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
|
||||
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
|
||||
@@ -366,6 +412,14 @@ def install_to_disk(imgpath):
|
||||
sys.stdout.write('\x1b[1K\rDone writing {0}'.format(vol['mount']))
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
if vol['mount'] == '/boot':
|
||||
tbootuuid = subprocess.check_output(['blkid', vol['targetdisk']])
|
||||
if b'UUID="' in tbootuuid:
|
||||
bootuuid = tbootuuid.split(b'UUID="', 1)[1].split(b'"')[0].decode('utf8')
|
||||
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
@@ -373,4 +427,9 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
install_to_disk(os.environ['mountsrc'])
|
||||
try:
|
||||
install_to_disk(os.environ['mountsrc'])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
time.sleep(86400)
|
||||
raise
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
. /lib/dracut-lib.sh
|
||||
confluent_whost=$confluent_mgr
|
||||
if [[ "$confluent_whost" == *:* ]]; then
|
||||
if [[ "$confluent_whost" == *:* ]] && [[ "$confluent_whost" != "["* ]]; then
|
||||
confluent_whost="[$confluent_mgr]"
|
||||
fi
|
||||
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
|
||||
@@ -127,5 +127,7 @@ if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
fi
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -31,6 +31,9 @@ if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
fi
|
||||
lvm vgchange -a n
|
||||
udevadm control -e
|
||||
if [ -f /sysroot/etc/lvm/devices/system.devices ]; then
|
||||
rm /sysroot/etc/lvm/devices/system.devices
|
||||
fi
|
||||
chroot /sysroot /usr/lib/systemd/systemd-udevd --daemon
|
||||
chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python image2disk.py"
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
|
||||
@@ -16,6 +16,7 @@ if [ -z "$confluent_mgr" ]; then
|
||||
fi
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
hostnamectl set-hostname $nodename
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
mkdir -p /var/log/confluent
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -7,8 +7,8 @@ if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
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 $autocons > /tmp/01-autocons.devnode
|
||||
echo "Detected firmware specified console at $(cat /tmp/01-autocons.conf)" > $autocons
|
||||
echo "Initializing auto detected console when installer starts" > $autocons
|
||||
fi
|
||||
fi
|
||||
@@ -16,4 +16,5 @@ 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
|
||||
. /lib/anaconda-lib.sh
|
||||
echo rd.fcoe=0 > /etc/cmdline.d/nofcoe.conf
|
||||
wait_for_kickstart
|
||||
|
||||
+107
-46
@@ -1,6 +1,16 @@
|
||||
#!/bin/sh
|
||||
[ -e /tmp/confluent.initq ] && return 0
|
||||
. /lib/dracut-lib.sh
|
||||
setsid sh -c 'exec bash <> /dev/tty2 >&0 2>&1' &
|
||||
if [ -f /tmp/dd_disk ]; then
|
||||
for dd in $(cat /tmp/dd_disk); do
|
||||
if [ -e $dd ]; then
|
||||
driver-updates --disk $dd $dd
|
||||
rm $dd
|
||||
fi
|
||||
done
|
||||
rm /tmp/dd_disk
|
||||
fi
|
||||
udevadm trigger
|
||||
udevadm trigger --type=devices --action=add
|
||||
udevadm settle
|
||||
@@ -19,13 +29,6 @@ function confluentpython() {
|
||||
/usr/bin/python2 $*
|
||||
fi
|
||||
}
|
||||
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
|
||||
vlaninfo=$(getarg vlan)
|
||||
if [ ! -z "$vlaninfo" ]; then
|
||||
vldev=${vlaninfo#*:}
|
||||
@@ -40,7 +43,7 @@ mkdir -p /etc/confluent
|
||||
echo -n > /etc/confluent/confluent.info
|
||||
umask $oum
|
||||
TRIES=5
|
||||
while [ ! -e /dev/disk ] && [ $TRIES -gt 0 ]; do
|
||||
while [ ! -e /dev/disk/by-label ] && [ $TRIES -gt 0 ]; do
|
||||
sleep 2
|
||||
TRIES=$((TRIES - 1))
|
||||
done
|
||||
@@ -55,21 +58,58 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
sed -n '/^net_cfgs:/, /^[^- ]/{/^[^- ]/!p}' cnflnt.yml |sed -n '/^-/, /^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
|
||||
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
|
||||
autoconfigmethod=${autoconfigmethod#ipv4_method: }
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
/usr/libexec/nm-initrd-generator ip=:dhcp
|
||||
else
|
||||
v4addr=$(grep ^ipv4_address: $tcfg)
|
||||
v4addr=${v4addr#ipv4_address: }
|
||||
v4addr=${v4addr%/*}
|
||||
v4gw=$(grep ^ipv4_gateway: $tcfg)
|
||||
v4gw=${v4gw#ipv4_gateway: }
|
||||
if [ "$v4gw" = "null" ]; then
|
||||
v4gw=""
|
||||
fi
|
||||
v4nm=$(grep ipv4_netmask: $tcfg)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
/usr/libexec/nm-initrd-generator ip=$v4addr::$v4gw:$v4nm:$hostname::none
|
||||
fi
|
||||
for i in /sys/class/net/*; do
|
||||
ip link set $(basename $i) down
|
||||
udevadm info $i | grep ID_NET_DRIVER=cdc_ether > /dev/null && continue
|
||||
ip link set $(basename $i) up
|
||||
done
|
||||
TRIES=30
|
||||
DETECTED=0
|
||||
while [ "$DETECTED" = 0 ] && [ $TRIES -gt 0 ]; do
|
||||
TRIES=$((TRIES - 1))
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK| awk '{print $2}' | sed -e 's/:$//'); do
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
/usr/libexec/nm-initrd-generator ip=$NICGUESS:dhcp
|
||||
else
|
||||
v4addr=$(grep ^ipv4_address: $tcfg)
|
||||
v4addr=${v4addr#ipv4_address: }
|
||||
v4plen=${v4addr#*/}
|
||||
v4addr=${v4addr%/*}
|
||||
v4gw=$(grep ^ipv4_gateway: $tcfg)
|
||||
v4gw=${v4gw#ipv4_gateway: }
|
||||
ip addr add dev $NICGUESS $v4addr/$v4plen
|
||||
if [ "$v4gw" = "null" ]; then
|
||||
v4gw=""
|
||||
fi
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route add default via $v4gw
|
||||
fi
|
||||
v4nm=$(grep ipv4_netmask: $tcfg)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
DETECTED=0
|
||||
for dsrv in $deploysrvs; do
|
||||
if curl --capath /tls/ -s --connect-timeout 3 https://$dsrv/confluent-public/ > /dev/null; then
|
||||
rm /run/NetworkManager/system-connections/*
|
||||
/usr/libexec/nm-initrd-generator ip=$v4addr::$v4gw:$v4nm:$hostname:$NICGUESS:none
|
||||
DETECTED=1
|
||||
ifname=$NICGUESS
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route del default via $v4gw
|
||||
fi
|
||||
ip addr flush dev $NICGUESS
|
||||
if [ $DETECTED = 1 ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK| awk '{print $2}' | sed -e 's/:$//'); do
|
||||
ip addr flush dev $NICGUESS
|
||||
ip link set $NICGUESS down
|
||||
done
|
||||
NetworkManager --configure-and-quit=initrd --no-daemon
|
||||
hmackeyfile=/tmp/cnflnthmackeytmp
|
||||
echo -n $(grep ^apitoken: cnflnt.yml|awk '{print $2}') > $hmackeyfile
|
||||
@@ -85,10 +125,10 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
echo 'MANAGER: '$deploysrv >> /etc/confluent/confluent.info
|
||||
done
|
||||
for deployer in $deploysrvs; do
|
||||
if curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt -k https://$deployer/confluent-api/self/registerapikey; then
|
||||
if curl --capath /tls/ -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_CRYPTHMAC: $(cat $hmacfile)" -d@$passcrypt -k https://$deployer/confluent-api/self/registerapikey; then
|
||||
cp $passfile /etc/confluent/confluent.apikey
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg
|
||||
curl --capath /tls/ -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$deployer/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg)
|
||||
confluent_profile=${confluent_profile#profile: }
|
||||
mgr=$deployer
|
||||
@@ -114,7 +154,7 @@ if ! grep MANAGER: /etc/confluent/confluent.info; then
|
||||
for mac in $(ip -br link|grep -v LOOPBACK|awk '{print $3}'); do
|
||||
myids=$myids"/mac="$mac
|
||||
done
|
||||
myname=$(curl -sH "CONFLUENT_IDS: $myids" https://$confluenthttpsrv/confluent-api/self/whoami)
|
||||
myname=$(curl --capath /tls/ -sH "CONFLUENT_IDS: $myids" https://$confluenthttpsrv/confluent-api/self/whoami)
|
||||
if [ ! -z "$myname" ]; then
|
||||
echo NODENAME: $myname > /etc/confluent/confluent.info
|
||||
echo MANAGER: $confluentsrv >> /etc/confluent/confluent.info
|
||||
@@ -146,17 +186,19 @@ if [ ! -z "$autocons" ]; then
|
||||
errout="-e $autocons"
|
||||
fi
|
||||
while ! confluentpython /opt/confluent/bin/apiclient $errout /confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg; do
|
||||
sleep 10
|
||||
sleep 10
|
||||
done
|
||||
ifidx=$(cat /tmp/confluent.ifidx 2> /dev/null)
|
||||
if [ ! -z "$ifidx" ]; then
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
ifname=${ifname%@*}
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
else
|
||||
ip -br a|grep UP|awk '{print $1}' > /tmp/net.ifaces
|
||||
ifname=$(cat /tmp/net.ifaces)
|
||||
if [ -z "$ifname" ]; then
|
||||
if [ ! -z "$ifidx" ]; then
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
ifname=${ifname%@*}
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
else
|
||||
ip -br a|grep UP|awk '{print $1}' > /tmp/net.ifaces
|
||||
ifname=$(cat /tmp/net.ifaces)
|
||||
fi
|
||||
fi
|
||||
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
@@ -185,23 +227,38 @@ 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
|
||||
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
|
||||
/opt/confluent/bin/autocons -c > /dev/null
|
||||
cp /tmp/01-autocons.conf /etc/cmdline.d/
|
||||
else
|
||||
echo "Unable to automatically detect requested text console"
|
||||
fi
|
||||
cp /tmp/01-autocons.conf /etc/cmdline.d/
|
||||
else
|
||||
echo "Unable to automatically detect requested text console"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo inst.repo=$proto://$mgr/confluent-public/os/$profilename/distribution >> /etc/cmdline.d/01-confluent.conf
|
||||
. /etc/os-release
|
||||
if [ "$ID" = "dracut" ]; then
|
||||
ID=$(echo $PRETTY_NAME|awk '{print $1}')
|
||||
VERSION_ID=$(echo $VERSION|awk '{print $1}')
|
||||
if [ "$ID" = "Oracle" ]; then
|
||||
ID=OL
|
||||
elif [ "$ID" = "Red" ]; then
|
||||
ID=RHEL
|
||||
fi
|
||||
fi
|
||||
ISOSRC=$(blkid -t TYPE=iso9660|grep -Ei ' LABEL="'$ID-$VERSION_ID|sed -e s/:.*//)
|
||||
if [ -z "$ISOSRC" ]; then
|
||||
echo inst.repo=$proto://$mgr/confluent-public/os/$profilename/distribution >> /etc/cmdline.d/01-confluent.conf
|
||||
root=anaconda-net:$proto://$mgr/confluent-public/os/$profilename/distribution
|
||||
export root
|
||||
else
|
||||
echo inst.repo=cdrom:$ISOSRC >> /etc/cmdline.d/01-confluent.conf
|
||||
fi
|
||||
echo inst.ks=$proto://$mgr/confluent-public/os/$profilename/kickstart >> /etc/cmdline.d/01-confluent.conf
|
||||
kickstart=$proto://$mgr/confluent-public/os/$profilename/kickstart
|
||||
root=anaconda-net:$proto://$mgr/confluent-public/os/$profilename/distribution
|
||||
export kickstart
|
||||
export root
|
||||
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
|
||||
autoconfigmethod=${autoconfigmethod#ipv4_method: }
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
@@ -281,4 +338,8 @@ if [ -e /lib/nm-lib.sh ]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK| awk '{print $2}' | sed -e 's/:$//'); do
|
||||
ip addr flush dev $NICGUESS
|
||||
ip link set $NICGUESS down
|
||||
done
|
||||
|
||||
|
||||
@@ -33,15 +33,7 @@
|
||||
reboot
|
||||
|
||||
%packages
|
||||
@^minimal-environment
|
||||
#-kernel-uek # This can opt out of the UEK for the relevant distribution
|
||||
bind-utils
|
||||
chrony
|
||||
pciutils
|
||||
python3
|
||||
rsync
|
||||
tar
|
||||
-iwl*-firmware
|
||||
%include /tmp/pkglist
|
||||
%include /tmp/addonpackages
|
||||
%include /tmp/cryptpkglist
|
||||
%end
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@^minimal-environment
|
||||
#-kernel-uek # This can opt out of the UEK for the relevant distribution
|
||||
bind-utils
|
||||
chrony
|
||||
pciutils
|
||||
python3
|
||||
rsync
|
||||
tar
|
||||
-iwl*-firmware
|
||||
@@ -0,0 +1,4 @@
|
||||
clearpart --all --initlabel
|
||||
ignoredisk --only-use %%INSTALLDISK%%
|
||||
autopart --nohome %%LUKSHOOK%%
|
||||
|
||||
@@ -27,7 +27,7 @@ with open('/etc/confluent/confluent.deploycfg') as dplcfgfile:
|
||||
_, profile = line.split(' ', 1)
|
||||
if line.startswith('ipv4_method: '):
|
||||
_, v4cfg = line.split(' ', 1)
|
||||
if v4cfg == 'static' or v4cfg =='dhcp':
|
||||
if v4cfg == 'static' or v4cfg =='dhcp' or not server6:
|
||||
server = server4
|
||||
if not server:
|
||||
server = '[{}]'.format(server6)
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -87,10 +87,17 @@ done
|
||||
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
LUKSPARTY=''
|
||||
touch /tmp/cryptpkglist
|
||||
touch /tmp/pkglist
|
||||
touch /tmp/addonpackages
|
||||
if [ "$cryptboot" == "tpm2" ]; then
|
||||
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
|
||||
echo $cryptboot >> /tmp/cryptboot
|
||||
lukspass=$(python3 /opt/confluent/bin/apiclient /confluent-api/self/profileprivate/pending/luks.key 2> /dev/null)
|
||||
if [ -z "$lukspass" ]; then
|
||||
lukspass=$(python3 -c 'import os;import base64;print(base64.b64encode(os.urandom(66)).decode())')
|
||||
fi
|
||||
echo $lukspass > /etc/confluent/luks.key
|
||||
chmod 000 /etc/confluent/luks.key
|
||||
LUKSPARTY="--encrypted --passphrase=$lukspass"
|
||||
echo $cryptboot >> /tmp/cryptboot
|
||||
echo clevis-dracut >> /tmp/cryptpkglist
|
||||
fi
|
||||
|
||||
@@ -102,16 +109,19 @@ confluentpython /opt/confluent/bin/apiclient /confluent-public/os/$confluent_pro
|
||||
run_remote pre.custom
|
||||
run_remote_parts pre.d
|
||||
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart -o /tmp/kickstart.base
|
||||
if grep '^%include /tmp/pkglist' /tmp/kickstart.* > /dev/null; then
|
||||
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/packagelist -o /tmp/pkglist
|
||||
fi
|
||||
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || touch /tmp/installdisk
|
||||
if [ ! -e /tmp/installdisk ]; then
|
||||
run_remote_python getinstalldisk
|
||||
fi
|
||||
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/partitioning -o /tmp/partitioning.template
|
||||
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || rm /tmp/installdisk
|
||||
if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then
|
||||
echo clearpart --all --initlabel >> /tmp/partitioning
|
||||
echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning
|
||||
echo autopart --nohome $LUKSPARTY >> /tmp/partitioning
|
||||
dd if=/dev/zero of=/dev/$(cat /tmp/installdisk) bs=1M count=1 >& /dev/null
|
||||
INSTALLDISK=$(cat /tmp/installdisk)
|
||||
sed -e s/%%INSTALLDISK%%/$INSTALLDISK/ -e "s!%%LUKSHOOK%%!$LUKSPARTY!" /tmp/partitioning.template > /tmp/partitioning
|
||||
vgchange -a n >& /dev/null
|
||||
wipefs -a -f /dev/$INSTALLDISK >& /dev/null
|
||||
fi
|
||||
kill $logshowpid
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/sh
|
||||
grep HostCert /etc/ssh/sshd_config.anaconda >> /mnt/sysimage/etc/ssh/sshd_config
|
||||
echo HostbasedAuthentication yes >> /mnt/sysimage/etc/ssh/sshd_config
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /mnt/sysimage/etc/ssh/sshd_config
|
||||
echo IgnoreRhosts no >> /mnt/sysimage/etc/ssh/sshd_config
|
||||
targssh=/mnt/sysimage/etc/ssh/sshd_config
|
||||
if [ -d /mnt/sysimage/etc/ssh/sshd_config.d/ ]; then
|
||||
targssh=/mnt/sysimage/etc/ssh/sshd_config.d/90-confluent.conf
|
||||
fi
|
||||
grep HostCert /etc/ssh/sshd_config.anaconda >> $targssh
|
||||
echo HostbasedAuthentication yes >> $targssh
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> $targssh
|
||||
echo IgnoreRhosts no >> $targssh
|
||||
sshconf=/mnt/sysimage/etc/ssh/ssh_config
|
||||
if [ -d /mnt/sysimage/etc/ssh/ssh_config.d/ ]; then
|
||||
sshconf=/mnt/sysimage/etc/ssh/ssh_config.d/01-confluent.conf
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
cryptdisk=$(blkid -t TYPE="crypto_LUKS"|sed -e s/:.*//)
|
||||
clevis luks bind -f -d $cryptdisk -k - tpm2 '{}' < /etc/confluent/confluent.apikey
|
||||
cryptsetup luksRemoveKey $cryptdisk < /etc/confluent/confluent.apikey
|
||||
clevis luks bind -f -d $cryptdisk -k - tpm2 '{}' < /etc/confluent/luks.key
|
||||
chmod 000 /etc/confluent/luks.key
|
||||
#cryptsetup luksRemoveKey $cryptdisk < /etc/confluent/confluent.apikey
|
||||
|
||||
+15
-5
@@ -120,7 +120,7 @@ fi
|
||||
ready=0
|
||||
while [ $ready = "0" ]; do
|
||||
get_remote_apikey
|
||||
if [[ $confluent_mgr == *:* ]]; then
|
||||
if [[ $confluent_mgr == *:* ]] && [[ $confluent_mgr != "["* ]]; then
|
||||
confluent_mgr="[$confluent_mgr]"
|
||||
fi
|
||||
tmperr=$(mktemp)
|
||||
@@ -154,8 +154,14 @@ cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
EOC
|
||||
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
linktype=$(ip link show dev ${ifname}|grep link/|awk '{print $1}')
|
||||
if [ "$linktype" = link/infiniband ]; then
|
||||
linktype="infiniband"
|
||||
else
|
||||
linktype="ethernet"
|
||||
fi
|
||||
echo type=$linktype >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
type=ethernet
|
||||
autoconnect-retries=1
|
||||
EOC
|
||||
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
@@ -164,10 +170,14 @@ multi-connect=1
|
||||
permissions=
|
||||
wait-device-timeout=60000
|
||||
|
||||
[ethernet]
|
||||
mac-address-blacklist=
|
||||
EOC
|
||||
if [ "$linktype" = infiniband ]; then
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
[infiniband]
|
||||
transport-mode=datagram
|
||||
|
||||
EOC
|
||||
fi
|
||||
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
auto6configmethod=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
@@ -278,7 +288,7 @@ fi
|
||||
echo '[proxy]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
|
||||
confluent_websrv=$confluent_mgr
|
||||
if [[ $confluent_websrv == *:* ]]; then
|
||||
if [[ $confluent_websrv == *:* ]] && [[ $confluent_websrv != "["* ]]; then
|
||||
confluent_websrv="[$confluent_websrv]"
|
||||
fi
|
||||
echo -n "Initializing ssh..."
|
||||
|
||||
@@ -9,9 +9,16 @@ HOME=$(getent passwd $(whoami)|cut -d: -f 6)
|
||||
export HOME
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
if [ -z "$confluent_mgr" ] || [ "$confluent_mgr" == "null" ] || ! ping -c 1 $confluent_mgr >& /dev/null; then
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
fi
|
||||
confluent_websrv=$confluent_mgr
|
||||
if [[ "$confluent_mgr" == *:* ]]; then
|
||||
confluent_websrv="[$confluent_mgr]"
|
||||
fi
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
export nodename confluent_mgr confluent_profile confluent_websrv
|
||||
. /etc/confluent/functions
|
||||
(
|
||||
exec >> /var/log/confluent/confluent-firstboot.log
|
||||
@@ -34,7 +41,7 @@ if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
run_remote_config firstboot.d
|
||||
fi
|
||||
|
||||
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_websrv/confluent-api/self/updatestatus
|
||||
systemctl disable firstboot
|
||||
rm /etc/systemd/system/firstboot.service
|
||||
rm /etc/confluent/firstboot.ran
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -10,6 +10,22 @@ import stat
|
||||
import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
bootuuid = None
|
||||
vgname = 'localstorage'
|
||||
oldvgname = None
|
||||
|
||||
def convert_lv(oldlvname):
|
||||
if oldvgname is None:
|
||||
return None
|
||||
return oldlvname.replace(oldvgname, vgname)
|
||||
|
||||
def get_partname(devname, idx):
|
||||
if devname[-1] in '0123456789':
|
||||
return '{}p{}'.format(devname, idx)
|
||||
else:
|
||||
return '{}{}'.format(devname, idx)
|
||||
|
||||
def get_next_part_meta(img, imgsize):
|
||||
if img.tell() == imgsize:
|
||||
@@ -45,6 +61,8 @@ def get_image_metadata(imgpath):
|
||||
header = img.read(16)
|
||||
if header == b'\x63\x7b\x9d\x26\xb7\xfd\x48\x30\x89\xf9\x11\xcf\x18\xfd\xff\xa1':
|
||||
for md in get_multipart_image_meta(img):
|
||||
if md.get('device', '').startswith('/dev/zram'):
|
||||
continue
|
||||
yield md
|
||||
else:
|
||||
raise Exception('Installation from single part image not supported')
|
||||
@@ -53,10 +71,13 @@ class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
self.disk = disk
|
||||
|
||||
def run(self, command):
|
||||
def run(self, command, check=True):
|
||||
command = command.split()
|
||||
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
if check:
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
else:
|
||||
return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8')
|
||||
|
||||
def fixup(rootdir, vols):
|
||||
devbymount = {}
|
||||
@@ -75,14 +96,14 @@ def fixup(rootdir, vols):
|
||||
if tab.startswith('#ORIGFSTAB#'):
|
||||
if entry[1] in devbymount:
|
||||
targetdev = devbymount[entry[1]]
|
||||
if targetdev.startswith('/dev/localstorage/'):
|
||||
if targetdev.startswith('/dev/{}/'.format(vgname)):
|
||||
entry[0] = targetdev
|
||||
else:
|
||||
uuid = subprocess.check_output(['blkid', '-s', 'UUID', '-o', 'value', targetdev]).decode('utf8')
|
||||
uuid = uuid.strip()
|
||||
entry[0] = 'UUID={}'.format(uuid)
|
||||
elif entry[2] == 'swap':
|
||||
entry[0] = '/dev/mapper/localstorage-swap'
|
||||
entry[0] = '/dev/mapper/{}-swap'.format(vgname.replace('-', '--'))
|
||||
entry[0] = entry[0].ljust(42)
|
||||
entry[1] = entry[1].ljust(16)
|
||||
entry[3] = entry[3].ljust(28)
|
||||
@@ -125,19 +146,99 @@ def fixup(rootdir, vols):
|
||||
sys.stdout.flush()
|
||||
for metafs in ('proc', 'sys', 'dev'):
|
||||
subprocess.check_call(['mount', '-o', 'bind', '/{}'.format(metafs), os.path.join(rootdir, metafs)])
|
||||
with open(os.path.join(rootdir, 'etc/sysconfig/grub')) as defgrubin:
|
||||
if os.path.exists(os.path.join(rootdir, 'etc/lvm/devices/system.devices')):
|
||||
os.remove(os.path.join(rootdir, 'etc/lvm/devices/system.devices'))
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
kcmdline = os.path.join(rootdir, 'etc/kernel/cmdline')
|
||||
if os.path.exists(kcmdline):
|
||||
with open(kcmdline) as kcmdlinein:
|
||||
kcmdlinecontent = kcmdlinein.read()
|
||||
newkcmdlineent = []
|
||||
for ent in kcmdlinecontent.split():
|
||||
if ent.startswith('resume='):
|
||||
newkcmdlineent.append('resume={}'.format(newswapdev))
|
||||
elif ent.startswith('root='):
|
||||
newkcmdlineent.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
newkcmdlineent.append(ent)
|
||||
else:
|
||||
newkcmdlineent.append(ent)
|
||||
with open(kcmdline, 'w') as kcmdlineout:
|
||||
kcmdlineout.write(' '.join(newkcmdlineent) + '\n')
|
||||
for loadent in glob.glob(os.path.join(rootdir, 'boot/loader/entries/*.conf')):
|
||||
with open(loadent) as loadentin:
|
||||
currentry = loadentin.read().split('\n')
|
||||
with open(loadent, 'w') as loadentout:
|
||||
for cfgline in currentry:
|
||||
cfgparts = cfgline.split()
|
||||
if not cfgparts or cfgparts[0] != 'options':
|
||||
loadentout.write(cfgline + '\n')
|
||||
continue
|
||||
newcfgparts = [cfgparts[0]]
|
||||
for cfgpart in cfgparts[1:]:
|
||||
if cfgpart.startswith('root='):
|
||||
newcfgparts.append('root={}'.format(newrootdev))
|
||||
elif cfgpart.startswith('resume='):
|
||||
newcfgparts.append('resume={}'.format(newswapdev))
|
||||
elif cfgpart.startswith('rd.lvm.lv='):
|
||||
cfgpart = convert_lv(cfgpart)
|
||||
if cfgpart:
|
||||
newcfgparts.append(cfgpart)
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
loadentout.write(' '.join(newcfgparts) + '\n')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
with open(os.path.join(rootdir, 'etc/sysconfig/grub'), 'w') as defgrubout:
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
newline = []
|
||||
for ent in gline:
|
||||
if ent.startswith('resume=') or ent.startswith('rd.lvm.lv'):
|
||||
continue
|
||||
newline.append(ent)
|
||||
if ent.startswith('resume='):
|
||||
newline.append('resume={}'.format(newswapdev))
|
||||
elif ent.startswith('root='):
|
||||
newline.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
newline.append(ent)
|
||||
elif '""' in ent:
|
||||
newline.append('""')
|
||||
else:
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/')
|
||||
subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg])
|
||||
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/').replace('//', '/')
|
||||
grubcfg = grubcfg.split('\n')
|
||||
if not grubcfg[-1]:
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
if os.stat(rgcfg).st_size > 256:
|
||||
grubcfg = gcfg
|
||||
else:
|
||||
with open(rgcfg, 'r') as gin:
|
||||
tgrubcfg = gin.read()
|
||||
tgrubcfg = tgrubcfg.split('\n')
|
||||
if 'search --no-floppy --fs-uuid --set=dev' in tgrubcfg[0]:
|
||||
tgrubcfg[0] = 'search --no-floppy --fs-uuid --set=dev ' + bootuuid
|
||||
with open(rgcfg, 'w') as gout:
|
||||
for gcline in tgrubcfg:
|
||||
gout.write(gcline)
|
||||
gout.write('\n')
|
||||
try:
|
||||
subprocess.check_call(['chroot', rootdir, 'grub2-mkconfig', '-o', grubcfg])
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
print(rootdir)
|
||||
print(grubcfg)
|
||||
time.sleep(86400)
|
||||
newroot = None
|
||||
with open('/etc/shadow') as shadowin:
|
||||
shents = shadowin.read().split('\n')
|
||||
@@ -166,6 +267,8 @@ def fixup(rootdir, vols):
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if targblock.endswith('p') and 'nvme' in targblock:
|
||||
targblock = targblock[:-1]
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
@@ -183,7 +286,14 @@ def had_swap():
|
||||
return True
|
||||
return False
|
||||
|
||||
newrootdev = None
|
||||
newswapdev = None
|
||||
def install_to_disk(imgpath):
|
||||
global bootuuid
|
||||
global newrootdev
|
||||
global newswapdev
|
||||
global vgname
|
||||
global oldvgname
|
||||
lvmvols = {}
|
||||
deftotsize = 0
|
||||
mintotsize = 0
|
||||
@@ -215,6 +325,13 @@ def install_to_disk(imgpath):
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
if fs['device'].startswith('/dev/mapper'):
|
||||
oldvgname = fs['device'].rsplit('/', 1)[-1]
|
||||
# if node has - then /dev/mapper will double up the hypen
|
||||
if '_' in oldvgname and '-' in oldvgname.split('_')[-1]:
|
||||
oldvgname = oldvgname.rsplit('-', 1)[0].replace('--', '-')
|
||||
osname = oldvgname.split('_')[0]
|
||||
nodename = socket.gethostname().split('.')[0]
|
||||
vgname = '{}_{}'.format(osname, nodename)
|
||||
lvmvols[fs['device'].replace('/dev/mapper/', '')] = fs
|
||||
deflvmsize += fs['initsize']
|
||||
minlvmsize += fs['minsize']
|
||||
@@ -224,7 +341,7 @@ def install_to_disk(imgpath):
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
parted = PartedRunner(instdisk)
|
||||
dinfo = parted.run('unit s print')
|
||||
dinfo = parted.run('unit s print', check=False)
|
||||
dinfo = dinfo.split('\n')
|
||||
sectors = 0
|
||||
sectorsize = 0
|
||||
@@ -258,7 +375,9 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
|
||||
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
|
||||
vol['targetdisk'] = get_partname(instdisk, volidx)
|
||||
if vol['mount'] == '/':
|
||||
newrootdev = vol['targetdisk']
|
||||
curroffset += size + 1
|
||||
if not lvmvols:
|
||||
if swapsize:
|
||||
@@ -268,13 +387,14 @@ def install_to_disk(imgpath):
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
|
||||
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
|
||||
newswapdev = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['mkswap', newswapdev])
|
||||
else:
|
||||
parted.run('mkpart lvm {}s 100%'.format(curroffset))
|
||||
lvmpart = instdisk + '{}'.format(volidx + 1)
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
|
||||
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
|
||||
subprocess.check_call(['vgcreate', vgname, lvmpart])
|
||||
vginfo = subprocess.check_output(['vgdisplay', vgname, '--units', 'b']).decode('utf8')
|
||||
vginfo = vginfo.split('\n')
|
||||
pesize = 0
|
||||
pes = 0
|
||||
@@ -301,13 +421,17 @@ def install_to_disk(imgpath):
|
||||
extents += 1
|
||||
if vol['mount'] == '/':
|
||||
lvname = 'root'
|
||||
|
||||
else:
|
||||
lvname = vol['mount'].replace('/', '_')
|
||||
subprocess.check_call(['lvcreate', '-l', '{}'.format(extents), '-y', '-n', lvname, 'localstorage'])
|
||||
vol['targetdisk'] = '/dev/localstorage/{}'.format(lvname)
|
||||
subprocess.check_call(['lvcreate', '-l', '{}'.format(extents), '-y', '-n', lvname, vgname])
|
||||
vol['targetdisk'] = '/dev/{}/{}'.format(vgname, lvname)
|
||||
if vol['mount'] == '/':
|
||||
newrootdev = vol['targetdisk']
|
||||
if swapsize:
|
||||
subprocess.check_call(['lvcreate', '-y', '-l', '{}'.format(swapsize // pesize), '-n', 'swap', 'localstorage'])
|
||||
subprocess.check_call(['mkswap', '/dev/localstorage/swap'])
|
||||
subprocess.check_call(['lvcreate', '-y', '-l', '{}'.format(swapsize // pesize), '-n', 'swap', vgname])
|
||||
subprocess.check_call(['mkswap', '/dev/{}/swap'.format(vgname)])
|
||||
newswapdev = '/dev/{}/swap'.format(vgname)
|
||||
os.makedirs('/run/imginst/targ')
|
||||
for vol in allvols:
|
||||
with open(vol['targetdisk'], 'wb') as partition:
|
||||
@@ -366,6 +490,14 @@ def install_to_disk(imgpath):
|
||||
sys.stdout.write('\x1b[1K\rDone writing {0}'.format(vol['mount']))
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
if vol['mount'] == '/boot':
|
||||
tbootuuid = subprocess.check_output(['blkid', vol['targetdisk']])
|
||||
if b'UUID="' in tbootuuid:
|
||||
bootuuid = tbootuuid.split(b'UUID="', 1)[1].split(b'"')[0].decode('utf8')
|
||||
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
@@ -373,4 +505,9 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
install_to_disk(os.environ['mountsrc'])
|
||||
try:
|
||||
install_to_disk(os.environ['mountsrc'])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
time.sleep(86400)
|
||||
raise
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
. /lib/dracut-lib.sh
|
||||
confluent_whost=$confluent_mgr
|
||||
if [[ "$confluent_whost" == *:* ]]; then
|
||||
if [[ "$confluent_whost" == *:* ]] && [[ "$confluent_whost" != "["* ]]; then
|
||||
confluent_whost="[$confluent_mgr]"
|
||||
fi
|
||||
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
|
||||
@@ -127,5 +127,7 @@ if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
fi
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -30,7 +30,11 @@ if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
done
|
||||
fi
|
||||
lvm vgchange -a n
|
||||
/sysroot/usr/sbin/wipefs -a /dev/$(cat /sysroot/tmp/installdisk)
|
||||
udevadm control -e
|
||||
if [ -f /sysroot/etc/lvm/devices/system.devices ]; then
|
||||
rm /sysroot/etc/lvm/devices/system.devices
|
||||
fi
|
||||
chroot /sysroot /usr/lib/systemd/systemd-udevd --daemon
|
||||
chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python image2disk.py"
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
|
||||
@@ -16,6 +16,7 @@ if [ -z "$confluent_mgr" ]; then
|
||||
fi
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
hostnamectl set-hostname $nodename
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
mkdir -p /var/log/confluent
|
||||
|
||||
@@ -5,9 +5,16 @@
|
||||
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
if [ -z "$confluent_mgr" ] || [ "$confluent_mgr" == "null" ] || ! ping -c 1 $confluent_mgr >& /dev/null; then
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
fi
|
||||
confluent_websrv=$confluent_mgr
|
||||
if [[ "$confluent_mgr" == *:* ]]; then
|
||||
confluent_websrv="[$confluent_mgr]"
|
||||
fi
|
||||
export nodename confluent_mgr confluent_profile confluent_websrv
|
||||
. /etc/confluent/functions
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
@@ -16,9 +23,9 @@ exec 2>> /var/log/confluent/confluent-post.log
|
||||
chmod 600 /var/log/confluent/confluent-post.log
|
||||
tail -f /var/log/confluent/confluent-post.log > /dev/console &
|
||||
logshowpid=$!
|
||||
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /etc/systemd/system/firstboot.service
|
||||
curl -f https://$confluent_websrv/confluent-public/os/$confluent_profile/scripts/firstboot.service > /etc/systemd/system/firstboot.service
|
||||
mkdir -p /opt/confluent/bin
|
||||
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /opt/confluent/bin/firstboot.sh
|
||||
curl -f https://$confluent_websrv/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /opt/confluent/bin/firstboot.sh
|
||||
chmod +x /opt/confluent/bin/firstboot.sh
|
||||
systemctl enable firstboot
|
||||
selinuxpolicy=$(grep ^SELINUXTYPE /etc/selinux/config |awk -F= '{print $2}')
|
||||
@@ -33,7 +40,7 @@ run_remote_parts post.d
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_websrv/confluent-api/self/updatestatus
|
||||
|
||||
kill $logshowpid
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -94,6 +94,7 @@ while ! grep NODENAME /etc/confluent/confluent.info; do
|
||||
fi
|
||||
done
|
||||
node=$(grep NODENAME: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
mgr=$(python /opt/confluent/bin/apiclient -c)
|
||||
APIKEY=$(cat /etc/confluent/confluent.apikey)
|
||||
|
||||
@@ -108,7 +109,6 @@ while [ -z "$APIKEY" ]; do
|
||||
(/clortho $node $mgr || /opt/confluent/bin/clortho $node $mgr) > /etc/confluent/confluent.apikey
|
||||
APIKEY=$(cat /etc/confluent/confluent.apikey)
|
||||
done
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
/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
|
||||
@@ -116,4 +116,5 @@ profile=$(grep ^profile: /etc/confluent/confluent.deploycfg.new | sed -e 's/^pro
|
||||
mv /etc/confluent/confluent.deploycfg.new /etc/confluent/confluent.deploycfg
|
||||
export node mgr profile
|
||||
. /tmp/modinstall
|
||||
localcli network firewall load
|
||||
exec /bin/install
|
||||
|
||||
@@ -174,6 +174,8 @@ dnsdomain=${dnsdomain#dnsdomain: }
|
||||
echo search $dnsdomain >> /etc/resolv.conf
|
||||
echo -n "Initializing ssh..."
|
||||
ssh-keygen -A
|
||||
mkdir -p /usr/share/empty.sshd
|
||||
rm /etc/ssh/ssh_host_dsa_key*
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
privfile=${pubkey%.pub}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
+2
-2
@@ -116,7 +116,7 @@ fi
|
||||
ready=0
|
||||
while [ $ready = "0" ]; do
|
||||
get_remote_apikey
|
||||
if [[ $confluent_mgr == *:* ]]; then
|
||||
if [[ $confluent_mgr == *:* ]] && [[ $confluent_mgr != "["* ]]; then
|
||||
confluent_mgr="[$confluent_mgr]"
|
||||
fi
|
||||
tmperr=$(mktemp)
|
||||
@@ -161,7 +161,7 @@ else
|
||||
echo -e "BOOTPROTO='static'\nSTARTMODE='auto'" >> /run/confluent/ifcfg-$ifname
|
||||
echo "IPADDR='$v4addr/$v4nm'" >> /run/confluent/ifcfg-$ifname
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
echo defafult $v4gw - $ifname > /run/confluent/ifroute-$ifname
|
||||
echo default $v4gw - $ifname > /run/confluent/ifroute-$ifname
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -138,4 +138,6 @@ if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
fi
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -10,6 +10,12 @@ dynamic behavior and replace with static configuration.
|
||||
<hwclock>UTC</hwclock>
|
||||
<timezone>%%TIMEZONE%%</timezone>
|
||||
</timezone>
|
||||
<firstboot>
|
||||
<firstboot_enabled config:type="boolean">false</firstboot_enabled>
|
||||
</firstboot>
|
||||
<kdump>
|
||||
<add_crash_kernel config:type="boolean">false</add_crash_kernel>
|
||||
</kdump>
|
||||
<general>
|
||||
<self_update config:type="boolean">false</self_update>
|
||||
<mode>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
# WARNING
|
||||
# be careful when editing files here as this script is called
|
||||
# in parallel to other copy operations, so changes to files can be lost
|
||||
discnum=$(basename $1)
|
||||
if [ "$discnum" != 1 ]; then exit 0; fi
|
||||
if [ -e $2/boot/kernel ]; then exit 0; fi
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
# remove online repos
|
||||
grep -lE "baseurl=https?://download.opensuse.org" /etc/zypp/repos.d/*repo | xargs rm --
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
@@ -10,6 +10,12 @@ dynamic behavior and replace with static configuration.
|
||||
<hwclock>UTC</hwclock>
|
||||
<timezone>%%TIMEZONE%%</timezone>
|
||||
</timezone>
|
||||
<firstboot>
|
||||
<firstboot_enabled config:type="boolean">false</firstboot_enabled>
|
||||
</firstboot>
|
||||
<kdump>
|
||||
<add_crash_kernel config:type="boolean">false</add_crash_kernel>
|
||||
</kdump>
|
||||
<general>
|
||||
<self_update config:type="boolean">false</self_update>
|
||||
<mode>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
# WARNING
|
||||
# be careful when editing files here as this script is called
|
||||
# in parallel to other copy operations, so changes to files can be lost
|
||||
discnum=$(basename $1)
|
||||
if [ "$discnum" != 1 ]; then exit 0; fi
|
||||
if [ -e $2/boot/kernel ]; then exit 0; fi
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
# remove online repos
|
||||
grep -lE "baseurl=https?://download.opensuse.org" /etc/zypp/repos.d/*repo | xargs rm --
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
@@ -7,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
@@ -227,9 +230,16 @@ def synchronize():
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
@@ -277,10 +287,21 @@ def synchronize():
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
synchronize()
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
|
||||
-11
@@ -41,15 +41,6 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
sed -n '/^net_cfgs:/,/^[^- ]/{/^[^- ]/!p}' cnflnt.yml |sed -n '/^-/,/^-/{/^-/!p}'| sed -e 's/^[- ]*//'> $tcfg
|
||||
autoconfigmethod=$(grep ^ipv4_method: $tcfg)
|
||||
autoconfigmethod=${autoconfigmethod#ipv4_method: }
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
|
||||
for dsrv in $deploysrvs; do
|
||||
if wget https://$dsrv/confluent-public/ --tries=1 --timeout=1 -O /dev/null > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
NIC=$NICGUESS
|
||||
fi
|
||||
done
|
||||
done
|
||||
deploysrvs=$dsrv
|
||||
if [ "$autoconfigmethod" = "static" ]; then
|
||||
setdebopt netcfg/disable_dhcp true boolean
|
||||
v4addr=$(grep ^ipv4_address: $tcfg|cut -d: -f 2|sed -e 's/ //')
|
||||
@@ -212,5 +203,3 @@ if [ ! -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
setdebopt netcfg/confirm_static false boolean
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ d-i partman/confirm_write_new_label boolean true
|
||||
d-i partman/choose_partition select finish
|
||||
d-i partman/confirm boolean true
|
||||
d-i partman/confirm_nooverwrite boolean true
|
||||
d-i preseed/late_command string sleep 86400
|
||||
d-i partman/early_command string sleep 86400
|
||||
d-i passwd/make-user boolean false
|
||||
d-i clock-setup/utc boolean true
|
||||
d-i apt-setup/multiverse boolean false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
if ! grep console= /proc/cmdline > /dev/null; then
|
||||
mkdir -p /custom-installation
|
||||
/opt/confluent/bin/autocons > /custom-installation/autocons.info
|
||||
cons=$(cat /custom-installation/autocons.info)
|
||||
if [ ! -z "$cons" ]; then
|
||||
|
||||
@@ -58,6 +58,10 @@ if ! grep console= /proc/cmdline > /dev/null; then
|
||||
echo "Automatic console configured for $autocons"
|
||||
fi
|
||||
echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd
|
||||
modprobe ib_ipoib
|
||||
modprobe ib_umad
|
||||
modprobe hfi1
|
||||
modprobe mlx5_ib
|
||||
cd /sys/class/net
|
||||
for nic in *; do
|
||||
ip link set $nic up
|
||||
@@ -107,7 +111,7 @@ if [ "$v6meth" = static ]; then
|
||||
ip route add default via $v6gw
|
||||
fi
|
||||
fi
|
||||
v4meth=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
v4meth=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
if [ "$v4meth" = static ]; then
|
||||
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
v4prefix=$(grep ^prefix: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=First Boot Process
|
||||
Requires=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/confluent/bin/firstboot.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is executed on the first boot after install has
|
||||
# completed. It is best to edit the middle of the file as
|
||||
# noted below so custom commands are executed before
|
||||
# the script notifies confluent that install is fully complete.
|
||||
|
||||
HOME=$(getent passwd $(whoami)|cut -d: -f 6)
|
||||
export HOME
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
(
|
||||
exec >> /var/log/confluent/confluent-firstboot.log
|
||||
exec 2>> /var/log/confluent/confluent-firstboot.log
|
||||
chmod 600 /var/log/confluent/confluent-firstboot.log
|
||||
while ! ping -c 1 $confluent_mgr >& /dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
touch /etc/confluent/firstboot.ran
|
||||
|
||||
run_remote firstboot.custom
|
||||
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
|
||||
run_remote_parts firstboot.d
|
||||
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/firstboot.d/
|
||||
run_remote_config firstboot.d
|
||||
fi
|
||||
|
||||
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
systemctl disable firstboot
|
||||
rm /etc/systemd/system/firstboot.service
|
||||
rm /etc/confluent/firstboot.ran
|
||||
) &
|
||||
tail --pid $! -F /var/log/confluent/confluent-firstboot.log > /dev/console
|
||||
@@ -0,0 +1,95 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
raise Exception("Skipping multipath devname")
|
||||
self.name = devname
|
||||
self.wwn = None
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.mdcontainer = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
if not isinstance(qprop, str):
|
||||
qprop = qprop.decode('utf8')
|
||||
for prop in qprop.split('\n'):
|
||||
if '=' not in prop:
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
self.path = v
|
||||
elif k == 'ID_WWN':
|
||||
self.wwn = v
|
||||
elif k == 'MD_CONTAINER':
|
||||
self.mdcontainer = v
|
||||
attrs = subprocess.check_output(['udevadm', 'info', '-a', devnode])
|
||||
if not isinstance(attrs, str):
|
||||
attrs = attrs.decode('utf8')
|
||||
for attr in attrs.split('\n'):
|
||||
if '==' not in attr:
|
||||
continue
|
||||
k, v = attr.split('==', 1)
|
||||
k = k.strip()
|
||||
if k == 'ATTRS{size}':
|
||||
self.size = v.replace('"', '')
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
if self.driver == 'ahci':
|
||||
return 2
|
||||
if self.driver.startswith('megaraid'):
|
||||
return 3
|
||||
if self.driver.startswith('mpt'):
|
||||
return 4
|
||||
return 99
|
||||
|
||||
def __repr__(self):
|
||||
return repr({
|
||||
'name': self.name,
|
||||
'path': self.path,
|
||||
'wwn': self.wwn,
|
||||
'driver': self.driver,
|
||||
'size': self.size,
|
||||
'model': self.model,
|
||||
})
|
||||
|
||||
|
||||
def main():
|
||||
disks = []
|
||||
for disk in sorted(os.listdir('/sys/class/block')):
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,434 @@
|
||||
#!/usr/bin/python3
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import shutil
|
||||
import socket
|
||||
import stat
|
||||
import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
bootuuid = None
|
||||
|
||||
|
||||
def get_partname(devname, idx):
|
||||
if devname[-1] in '0123456789':
|
||||
return '{}p{}'.format(devname, idx)
|
||||
else:
|
||||
return '{}{}'.format(devname, idx)
|
||||
|
||||
def get_next_part_meta(img, imgsize):
|
||||
if img.tell() == imgsize:
|
||||
return None
|
||||
pathlen = struct.unpack('!H', img.read(2))[0]
|
||||
mountpoint = img.read(pathlen).decode('utf8')
|
||||
jsonlen = struct.unpack('!I', img.read(4))[0]
|
||||
metadata = json.loads(img.read(jsonlen).decode('utf8'))
|
||||
img.seek(16, 1) # skip the two 64-bit values we don't use, they are in json
|
||||
nextlen = struct.unpack('!H', img.read(2))[0]
|
||||
img.seek(nextlen, 1) # skip filesystem type
|
||||
nextlen = struct.unpack('!H', img.read(2))[0]
|
||||
img.seek(nextlen, 1) # skip orig devname (redundant with json)
|
||||
nextlen = struct.unpack('!H', img.read(2))[0]
|
||||
img.seek(nextlen, 1) # skip padding
|
||||
nextlen = struct.unpack('!Q', img.read(8))[0]
|
||||
img.seek(nextlen, 1) # go to next section
|
||||
return metadata
|
||||
|
||||
def get_multipart_image_meta(img):
|
||||
img.seek(0, 2)
|
||||
imgsize = img.tell()
|
||||
img.seek(16)
|
||||
seekamt = img.read(1)
|
||||
img.seek(struct.unpack('B', seekamt)[0], 1)
|
||||
partinfo = get_next_part_meta(img, imgsize)
|
||||
while partinfo:
|
||||
yield partinfo
|
||||
partinfo = get_next_part_meta(img, imgsize)
|
||||
|
||||
def get_image_metadata(imgpath):
|
||||
with open(imgpath, 'rb') as img:
|
||||
header = img.read(16)
|
||||
if header == b'\x63\x7b\x9d\x26\xb7\xfd\x48\x30\x89\xf9\x11\xcf\x18\xfd\xff\xa1':
|
||||
for md in get_multipart_image_meta(img):
|
||||
yield md
|
||||
else:
|
||||
raise Exception('Installation from single part image not supported')
|
||||
|
||||
class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
self.disk = disk
|
||||
|
||||
def run(self, command, check=True):
|
||||
command = command.split()
|
||||
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
|
||||
if check:
|
||||
return subprocess.check_output(command).decode('utf8')
|
||||
else:
|
||||
return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8')
|
||||
|
||||
def fixup(rootdir, vols):
|
||||
devbymount = {}
|
||||
for vol in vols:
|
||||
devbymount[vol['mount']] = vol['targetdisk']
|
||||
fstabfile = os.path.join(rootdir, 'etc/fstab')
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
while not fstab[0]:
|
||||
fstab = fstab[1:]
|
||||
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
|
||||
os.unlink(os.path.join(rootdir, '.autorelabel'))
|
||||
with open(fstabfile, 'w') as tfile:
|
||||
for tab in fstab:
|
||||
entry = tab.split()
|
||||
if tab.startswith('#ORIGFSTAB#'):
|
||||
if entry[1] in devbymount:
|
||||
targetdev = devbymount[entry[1]]
|
||||
if targetdev.startswith('/dev/localstorage/'):
|
||||
entry[0] = targetdev
|
||||
else:
|
||||
uuid = subprocess.check_output(['blkid', '-s', 'UUID', '-o', 'value', targetdev]).decode('utf8')
|
||||
uuid = uuid.strip()
|
||||
entry[0] = 'UUID={}'.format(uuid)
|
||||
elif entry[2] == 'swap':
|
||||
entry[0] = '/dev/mapper/localstorage-swap'
|
||||
entry[0] = entry[0].ljust(42)
|
||||
entry[1] = entry[1].ljust(16)
|
||||
entry[3] = entry[3].ljust(28)
|
||||
tab = '\t'.join(entry)
|
||||
tfile.write(tab + '\n')
|
||||
with open(os.path.join(rootdir, 'etc/hostname'), 'w') as nameout:
|
||||
nameout.write(socket.gethostname() + '\n')
|
||||
selinuxconfig = os.path.join(rootdir, 'etc/selinux/config')
|
||||
policy = None
|
||||
if os.path.exists(selinuxconfig):
|
||||
with open(selinuxconfig) as cfgin:
|
||||
sec = cfgin.read().split('\n')
|
||||
for l in sec:
|
||||
l = l.split('#', 1)[0]
|
||||
if l.startswith('SELINUXTYPE='):
|
||||
_, policy = l.split('=')
|
||||
for sshkey in glob.glob(os.path.join(rootdir, 'etc/ssh/*_key*')):
|
||||
os.unlink(sshkey)
|
||||
for sshkey in glob.glob('/etc/ssh/*_key*'):
|
||||
newkey = os.path.join(rootdir, sshkey[1:])
|
||||
shutil.copy2(sshkey, newkey)
|
||||
finfo = os.stat(sshkey)
|
||||
os.chown(newkey, finfo[stat.ST_UID], finfo[stat.ST_GID])
|
||||
|
||||
# Will use confignet to handle networking for ubuntu
|
||||
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
|
||||
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
|
||||
if policy:
|
||||
sys.stdout.write('Applying SELinux labeling...')
|
||||
sys.stdout.flush()
|
||||
subprocess.check_call(['setfiles', '-r', rootdir, os.path.join(rootdir, 'etc/selinux/{}/contexts/files/file_contexts'.format(policy)), os.path.join(rootdir, 'etc')])
|
||||
subprocess.check_call(['setfiles', '-r', rootdir, os.path.join(rootdir, 'etc/selinux/{}/contexts/files/file_contexts'.format(policy)), os.path.join(rootdir, 'opt')])
|
||||
sys.stdout.write('Done\n')
|
||||
sys.stdout.flush()
|
||||
for metafs in ('proc', 'sys', 'dev'):
|
||||
subprocess.check_call(['mount', '-o', 'bind', '/{}'.format(metafs), os.path.join(rootdir, metafs)])
|
||||
if os.path.exists(os.path.join(rootdir, 'etc/lvm/devices/system.devices')):
|
||||
os.remove(os.path.join(rootdir, 'etc/lvm/devices/system.devices'))
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
newline = []
|
||||
for ent in gline:
|
||||
if ent.startswith('resume=') or ent.startswith('rd.lvm.lv'):
|
||||
continue
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
grubcfg = subprocess.check_output(['find', os.path.join(rootdir, 'boot'), '-name', 'grub.cfg']).decode('utf8').strip().replace(rootdir, '/').replace('//', '/')
|
||||
grubcfg = grubcfg.split('\n')
|
||||
if not grubcfg[-1]:
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
if os.stat(rgcfg).st_size > 256:
|
||||
grubcfg = gcfg
|
||||
else:
|
||||
with open(rgcfg, 'r') as gin:
|
||||
tgrubcfg = gin.read()
|
||||
tgrubcfg = tgrubcfg.split('\n')
|
||||
if 'search --no-floppy --fs-uuid --set=dev' in tgrubcfg[0]:
|
||||
tgrubcfg[0] = 'search --no-floppy --fs-uuid --set=dev ' + bootuuid
|
||||
elif 'search.fs_uuid ' in tgrubcfg[0] and 'root' in tgrubcfg[0]:
|
||||
tgrubcfg[0] = 'search.fs_uuid ' + bootuuid + ' root'
|
||||
with open(rgcfg, 'w') as gout:
|
||||
for gcline in tgrubcfg:
|
||||
gout.write(gcline)
|
||||
gout.write('\n')
|
||||
try:
|
||||
# must fixup root@d2:/boot/efi/EFI# cat ubuntu/grub.cfg ... uuid
|
||||
subprocess.check_call(['chroot', rootdir, 'grub-mkconfig', '-o', grubcfg])
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
print(rootdir)
|
||||
print(grubcfg)
|
||||
time.sleep(86400)
|
||||
newroot = None
|
||||
with open('/etc/shadow') as shadowin:
|
||||
shents = shadowin.read().split('\n')
|
||||
for shent in shents:
|
||||
shent = shent.split(':')
|
||||
if not shent:
|
||||
continue
|
||||
if shent[0] == 'root' and shent[1] not in ('*', '!!', ''):
|
||||
newroot = shent[1]
|
||||
if newroot:
|
||||
shlines = None
|
||||
with open(os.path.join(rootdir, 'etc/shadow')) as oshadow:
|
||||
shlines = oshadow.read().split('\n')
|
||||
with open(os.path.join(rootdir, 'etc/shadow'), 'w') as oshadow:
|
||||
for line in shlines:
|
||||
if line.startswith('root:'):
|
||||
line = line.split(':')
|
||||
line[1] = newroot
|
||||
line = ':'.join(line)
|
||||
oshadow.write(line + '\n')
|
||||
partnum = None
|
||||
targblock = None
|
||||
for vol in vols:
|
||||
if vol['mount'] == '/boot/efi':
|
||||
targdev = vol['targetdisk']
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if targblock.endswith('p') and 'nvme' in targblock:
|
||||
targblock = targblock[:-1]
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
#other network interfaces
|
||||
|
||||
|
||||
def had_swap():
|
||||
with open('/etc/fstab') as tabfile:
|
||||
tabs = tabfile.read().split('\n')
|
||||
for tab in tabs:
|
||||
tab = tab.split()
|
||||
if len(tab) < 3:
|
||||
continue
|
||||
if tab[2] == 'swap':
|
||||
return True
|
||||
return False
|
||||
|
||||
def install_to_disk(imgpath):
|
||||
global bootuuid
|
||||
lvmvols = {}
|
||||
deftotsize = 0
|
||||
mintotsize = 0
|
||||
deflvmsize = 0
|
||||
minlvmsize = 0
|
||||
biggestsize = 0
|
||||
biggestfs = None
|
||||
plainvols = {}
|
||||
allvols = []
|
||||
swapsize = 0
|
||||
if had_swap():
|
||||
with open('/proc/meminfo') as meminfo:
|
||||
swapsize = meminfo.read().split('\n')[0]
|
||||
swapsize = int(swapsize.split()[1])
|
||||
if swapsize < 2097152:
|
||||
swapsize = swapsize * 2
|
||||
elif swapsize > 8388608 and swapsize < 67108864:
|
||||
swapsize = swapsize * 0.5
|
||||
elif swapsize >= 67108864:
|
||||
swapsize = 33554432
|
||||
swapsize = int(swapsize * 1024)
|
||||
deftotsize = swapsize
|
||||
mintotsize = swapsize
|
||||
for fs in get_image_metadata(imgpath):
|
||||
allvols.append(fs)
|
||||
deftotsize += fs['initsize']
|
||||
mintotsize += fs['minsize']
|
||||
if fs['initsize'] > biggestsize:
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
if fs['device'].startswith('/dev/mapper'):
|
||||
lvmvols[fs['device'].replace('/dev/mapper/', '')] = fs
|
||||
deflvmsize += fs['initsize']
|
||||
minlvmsize += fs['minsize']
|
||||
else:
|
||||
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
|
||||
with open('/tmp/installdisk') as diskin:
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
parted = PartedRunner(instdisk)
|
||||
dinfo = parted.run('unit s print', check=False)
|
||||
dinfo = dinfo.split('\n')
|
||||
sectors = 0
|
||||
sectorsize = 0
|
||||
for inf in dinfo:
|
||||
if inf.startswith('Disk {0}:'.format(instdisk)):
|
||||
_, sectors = inf.split(': ')
|
||||
sectors = int(sectors.replace('s', ''))
|
||||
if inf.startswith('Sector size (logical/physical):'):
|
||||
_, sectorsize = inf.split(':')
|
||||
sectorsize = sectorsize.split('/')[0]
|
||||
sectorsize = sectorsize.replace('B', '')
|
||||
sectorsize = int(sectorsize)
|
||||
# for now, only support resizing/growing the largest partition
|
||||
minexcsize = deftotsize - biggestfs['initsize']
|
||||
mintotsize = deftotsize - biggestfs['initsize'] + biggestfs['minsize']
|
||||
minsectors = mintotsize // sectorsize
|
||||
if sectors < (minsectors + 65536):
|
||||
raise Exception('Disk too small to fit image')
|
||||
biggestsectors = sectors - (minexcsize // sectorsize)
|
||||
biggestsize = sectorsize * biggestsectors
|
||||
parted.run('mklabel gpt')
|
||||
curroffset = 2048
|
||||
for volidx in sorted(plainvols):
|
||||
vol = plainvols[volidx]
|
||||
if vol is not biggestfs:
|
||||
size = vol['initsize'] // sectorsize
|
||||
else:
|
||||
size = biggestsize // sectorsize
|
||||
size += 2047 - (size % 2048)
|
||||
end = curroffset + size
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
|
||||
vol['targetdisk'] = get_partname(instdisk, volidx)
|
||||
curroffset += size + 1
|
||||
if not lvmvols:
|
||||
if swapsize:
|
||||
swapsize = swapsize // sectorsize
|
||||
swapsize += 2047 - (size % 2048)
|
||||
end = curroffset + swapsize
|
||||
if end > sectors:
|
||||
end = sectors
|
||||
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
|
||||
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
|
||||
else:
|
||||
parted.run('mkpart lvm {}s 100%'.format(curroffset))
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
|
||||
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
|
||||
vginfo = vginfo.split('\n')
|
||||
pesize = 0
|
||||
pes = 0
|
||||
for infline in vginfo:
|
||||
infline = infline.split()
|
||||
if len(infline) >= 3 and infline[:2] == ['PE', 'Size']:
|
||||
pesize = int(infline[2])
|
||||
if len(infline) >= 5 and infline[:2] == ['Free', 'PE']:
|
||||
pes = int(infline[4])
|
||||
takeaway = swapsize // pesize
|
||||
for volidx in lvmvols:
|
||||
vol = lvmvols[volidx]
|
||||
if vol is biggestfs:
|
||||
continue
|
||||
takeaway += vol['initsize'] // pesize
|
||||
takeaway += 1
|
||||
biggestextents = pes - takeaway
|
||||
for volidx in lvmvols:
|
||||
vol = lvmvols[volidx]
|
||||
if vol is biggestfs:
|
||||
extents = biggestextents
|
||||
else:
|
||||
extents = vol['initsize'] // pesize
|
||||
extents += 1
|
||||
if vol['mount'] == '/':
|
||||
lvname = 'root'
|
||||
else:
|
||||
lvname = vol['mount'].replace('/', '_')
|
||||
subprocess.check_call(['lvcreate', '-l', '{}'.format(extents), '-y', '-n', lvname, 'localstorage'])
|
||||
vol['targetdisk'] = '/dev/localstorage/{}'.format(lvname)
|
||||
if swapsize:
|
||||
subprocess.check_call(['lvcreate', '-y', '-l', '{}'.format(swapsize // pesize), '-n', 'swap', 'localstorage'])
|
||||
subprocess.check_call(['mkswap', '/dev/localstorage/swap'])
|
||||
os.makedirs('/run/imginst/targ')
|
||||
for vol in allvols:
|
||||
with open(vol['targetdisk'], 'wb') as partition:
|
||||
partition.write(b'\x00' * 1 * 1024 * 1024)
|
||||
subprocess.check_call(['mkfs.{}'.format(vol['filesystem']), vol['targetdisk']])
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
|
||||
source = vol['mount'].replace('/', '_')
|
||||
source = '/run/imginst/sources/' + source
|
||||
blankfsstat = os.statvfs('/run/imginst/targ')
|
||||
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
|
||||
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
|
||||
with subprocess.Popen(['cp', '-ax', source + '/.', '/run/imginst/targ']) as copier:
|
||||
stillrunning = copier.poll()
|
||||
lastprogress = 0.0
|
||||
while stillrunning is None:
|
||||
currfsstat = os.statvfs('/run/imginst/targ')
|
||||
currused = (currfsstat.f_blocks - currfsstat.f_bfree) * currfsstat.f_bsize
|
||||
currused -= blankused
|
||||
with open('/proc/meminfo') as meminf:
|
||||
for line in meminf.read().split('\n'):
|
||||
if line.startswith('Dirty:'):
|
||||
_, dirty, _ = line.split()
|
||||
dirty = int(dirty) * 1024
|
||||
progress = (currused - dirty) / vol['minsize']
|
||||
if progress < lastprogress:
|
||||
progress = lastprogress
|
||||
if progress > 0.99:
|
||||
progress = 0.99
|
||||
lastprogress = progress
|
||||
progress = progress * 100
|
||||
sys.stdout.write('\x1b[1K\rWriting {0}: {1:3.2f}%'.format(vol['mount'], progress))
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.5)
|
||||
stillrunning = copier.poll()
|
||||
if stillrunning != 0:
|
||||
raise Exception("Error copying volume")
|
||||
with subprocess.Popen(['sync']) as syncrun:
|
||||
stillrunning = syncrun.poll()
|
||||
while stillrunning is None:
|
||||
with open('/proc/meminfo') as meminf:
|
||||
for line in meminf.read().split('\n'):
|
||||
if line.startswith('Dirty:'):
|
||||
_, dirty, _ = line.split()
|
||||
dirty = int(dirty) * 1024
|
||||
progress = (vol['minsize'] - dirty) / vol['minsize']
|
||||
if progress < lastprogress:
|
||||
progress = lastprogress
|
||||
if progress > 0.99:
|
||||
progress = 0.99
|
||||
lastprogress = progress
|
||||
progress = progress * 100
|
||||
sys.stdout.write('\x1b[1K\rWriting {0}: {1:3.2f}%'.format(vol['mount'], progress))
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.5)
|
||||
stillrunning = syncrun.poll()
|
||||
sys.stdout.write('\x1b[1K\rDone writing {0}'.format(vol['mount']))
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
if vol['mount'] == '/boot':
|
||||
tbootuuid = subprocess.check_output(['blkid', vol['targetdisk']])
|
||||
if b'UUID="' in tbootuuid:
|
||||
bootuuid = tbootuuid.split(b'UUID="', 1)[1].split(b'"')[0].decode('utf8')
|
||||
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
fixup('/run/imginst/targ', allvols)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
install_to_disk(os.environ['mountsrc'])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
time.sleep(86400)
|
||||
raise
|
||||
|
||||
@@ -8,7 +8,7 @@ for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed
|
||||
fi
|
||||
done
|
||||
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
|
||||
if grep confluennt_imagemethtod=untethered /proc/cmdline > /dev/null; then
|
||||
if grep confluent_imagemethod=untethered /proc/cmdline > /dev/null; then
|
||||
mount -t tmpfs untethered /mnt/remoteimg
|
||||
curl https://$confluent_mgr/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs
|
||||
else
|
||||
@@ -127,11 +127,13 @@ chmod +x /sysroot/opt/confluent/bin/onboot.sh
|
||||
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
|
||||
ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service
|
||||
cp /etc/confluent/functions /sysroot/etc/confluent/functions
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
. /etc/confluent/functions
|
||||
run_remote installimage
|
||||
exec reboot -f
|
||||
fi
|
||||
mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
. /etc/confluent/functions
|
||||
# the image will be used to deploy itself
|
||||
# provide both access to image (for parsing metadata)
|
||||
# and existing mounts of image (to take advantage of caching)
|
||||
mount -o bind /sys /sysroot/sys
|
||||
mount -o bind /dev /sysroot/dev
|
||||
mount -o bind /proc /sysroot/proc
|
||||
mount -o bind /run /sysroot/run
|
||||
|
||||
|
||||
if [ ! -f /tmp/mountparts.sh ]; then
|
||||
mkdir -p /sysroot/run/imginst/sources/_
|
||||
mount -o bind /mnt/remote /sysroot/run/imginst/sources/_
|
||||
else
|
||||
for srcmount in $(cat /tmp/mountparts.sh | awk '{print $2}'); do
|
||||
srcname=${srcmount#/dev/mapper/mproot}
|
||||
srcdir=$(echo $srcmount | sed -e 's!/dev/mapper/mproot!/mnt/remote!' -e 's!_!/!g')
|
||||
mkdir -p /sysroot/run/imginst/sources/$srcname
|
||||
mount -o bind $srcdir /sysroot/run/imginst/sources/$srcname
|
||||
done
|
||||
fi
|
||||
cd /sysroot/run
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python getinstalldisk"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_parts pre.d"
|
||||
if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
echo 'Unable to find a suitable installation target device, ssh to port 2222 to investigate'
|
||||
while [ ! -f /sysroot/tmp/installdisk ]; do
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
lvm vgchange -a n
|
||||
udevadm control -e
|
||||
if [ -f /sysroot/etc/lvm/devices/system.devices ]; then
|
||||
rm /sysroot/etc/lvm/devices/system.devices
|
||||
fi
|
||||
chroot /sysroot /usr/lib/systemd/systemd-udevd --daemon
|
||||
chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python image2disk.py"
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
|
||||
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
|
||||
kill -HUP $(cat /run/sshd.pid)
|
||||
cat /tls/*.pem > /sysroot/run/imginst/targ/usr/local/share/ca-certificates/confluent.crt
|
||||
chroot /sysroot/run/imginst/targ update-ca-certificates
|
||||
|
||||
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
|
||||
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Confluent onboot hook
|
||||
Requires=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/confluent/bin/onboot.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is executed on each boot as it is
|
||||
# completed. It is best to edit the middle of the file as
|
||||
# noted below so custom commands are executed before
|
||||
# the script notifies confluent that install is fully complete.
|
||||
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
v4meth=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
if [ "$v4meth" = "null" -o -z "$v4meth" ]; then
|
||||
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
fi
|
||||
if [ -z "$confluent_mgr" ]; then
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
fi
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
timedatectl set-timezone $(grep ^timezone: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
hostnamectl set-hostname $nodename
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
exec >> /var/log/confluent/confluent-onboot.log
|
||||
exec 2>> /var/log/confluent/confluent-onboot.log
|
||||
chmod 600 /var/log/confluent/confluent-onboot.log
|
||||
tail -f /var/log/confluent/confluent-onboot.log > /dev/console &
|
||||
logshowpid=$!
|
||||
|
||||
run_remote_python syncfileclient
|
||||
run_remote_python confignet
|
||||
|
||||
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
|
||||
run_remote_parts onboot.d
|
||||
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/
|
||||
run_remote_config onboot.d
|
||||
|
||||
#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
kill $logshowpid
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is executed 'chrooted' into a cloned disk target before rebooting
|
||||
#
|
||||
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
exec >> /var/log/confluent/confluent-post.log
|
||||
exec 2>> /var/log/confluent/confluent-post.log
|
||||
chmod 600 /var/log/confluent/confluent-post.log
|
||||
tail -f /var/log/confluent/confluent-post.log > /dev/console &
|
||||
logshowpid=$!
|
||||
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.service > /etc/systemd/system/firstboot.service
|
||||
mkdir -p /opt/confluent/bin
|
||||
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /opt/confluent/bin/firstboot.sh
|
||||
chmod +x /opt/confluent/bin/firstboot.sh
|
||||
systemctl enable firstboot
|
||||
run_remote_python syncfileclient
|
||||
run_remote_python confignet
|
||||
run_remote post.custom
|
||||
# post scripts may be placed into post.d, e.g. post.d/01-firstaction.sh, post.d/02-secondaction.sh
|
||||
run_remote_parts post.d
|
||||
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
|
||||
kill $logshowpid
|
||||
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/python3
|
||||
import random
|
||||
import time
|
||||
import subprocess
|
||||
import importlib
|
||||
import tempfile
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import pwd
|
||||
import grp
|
||||
import sys
|
||||
from importlib.machinery import SourceFileLoader
|
||||
try:
|
||||
apiclient = SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient').load_module()
|
||||
except FileNotFoundError:
|
||||
apiclient = SourceFileLoader('apiclient', '/etc/confluent/apiclient').load_module()
|
||||
|
||||
|
||||
def partitionhostsline(line):
|
||||
comment = ''
|
||||
try:
|
||||
cmdidx = line.index('#')
|
||||
comment = line[cmdidx:]
|
||||
line = line[:cmdidx].strip()
|
||||
except ValueError:
|
||||
pass
|
||||
if not line:
|
||||
return '', [], comment
|
||||
ipaddr, names = line.split(maxsplit=1)
|
||||
names = names.split()
|
||||
return ipaddr, names, comment
|
||||
|
||||
class HostMerger(object):
|
||||
def __init__(self):
|
||||
self.byip = {}
|
||||
self.byname = {}
|
||||
self.sourcelines = []
|
||||
self.targlines = []
|
||||
|
||||
def read_source(self, sourcefile):
|
||||
with open(sourcefile, 'r') as hfile:
|
||||
self.sourcelines = hfile.read().split('\n')
|
||||
while not self.sourcelines[-1]:
|
||||
self.sourcelines = self.sourcelines[:-1]
|
||||
for x in range(len(self.sourcelines)):
|
||||
line = self.sourcelines[x]
|
||||
currip, names, comment = partitionhostsline(line)
|
||||
if currip:
|
||||
self.byip[currip] = x
|
||||
for name in names:
|
||||
self.byname[name] = x
|
||||
|
||||
def read_target(self, targetfile):
|
||||
with open(targetfile, 'r') as hfile:
|
||||
lines = hfile.read().split('\n')
|
||||
if not lines[-1]:
|
||||
lines = lines[:-1]
|
||||
for y in range(len(lines)):
|
||||
line = lines[y]
|
||||
currip, names, comment = partitionhostsline(line)
|
||||
if currip in self.byip:
|
||||
x = self.byip[currip]
|
||||
if self.sourcelines[x] is None:
|
||||
# have already consumed this enntry
|
||||
continue
|
||||
self.targlines.append(self.sourcelines[x])
|
||||
self.sourcelines[x] = None
|
||||
continue
|
||||
for name in names:
|
||||
if name in self.byname:
|
||||
x = self.byname[name]
|
||||
if self.sourcelines[x] is None:
|
||||
break
|
||||
self.targlines.append(self.sourcelines[x])
|
||||
self.sourcelines[x] = None
|
||||
break
|
||||
else:
|
||||
self.targlines.append(line)
|
||||
|
||||
def write_out(self, targetfile):
|
||||
while not self.targlines[-1]:
|
||||
self.targlines = self.targlines[:-1]
|
||||
if not self.targlines:
|
||||
break
|
||||
while not self.sourcelines[-1]:
|
||||
self.sourcelines = self.sourcelines[:-1]
|
||||
if not self.sourcelines:
|
||||
break
|
||||
with open(targetfile, 'w') as hosts:
|
||||
for line in self.targlines:
|
||||
hosts.write(line + '\n')
|
||||
for line in self.sourcelines:
|
||||
if line is not None:
|
||||
hosts.write(line + '\n')
|
||||
|
||||
|
||||
class CredMerger:
|
||||
def __init__(self):
|
||||
try:
|
||||
with open('/etc/login.defs', 'r') as ldefs:
|
||||
defs = ldefs.read().split('\n')
|
||||
except FileNotFoundError:
|
||||
defs = []
|
||||
lkup = {}
|
||||
self.discardnames = {}
|
||||
self.shadowednames = {}
|
||||
for line in defs:
|
||||
try:
|
||||
line = line[:line.index('#')]
|
||||
except ValueError:
|
||||
pass
|
||||
keyval = line.split()
|
||||
if len(keyval) < 2:
|
||||
continue
|
||||
lkup[keyval[0]] = keyval[1]
|
||||
self.uidmin = int(lkup.get('UID_MIN', 1000))
|
||||
self.uidmax = int(lkup.get('UID_MAX', 60000))
|
||||
self.gidmin = int(lkup.get('GID_MIN', 1000))
|
||||
self.gidmax = int(lkup.get('GID_MAX', 60000))
|
||||
self.shadowlines = None
|
||||
|
||||
def read_passwd(self, source, targfile=False):
|
||||
self.read_generic(source, self.uidmin, self.uidmax, targfile)
|
||||
|
||||
def read_group(self, source, targfile=False):
|
||||
self.read_generic(source, self.gidmin, self.gidmax, targfile)
|
||||
|
||||
def read_generic(self, source, minid, maxid, targfile):
|
||||
if targfile:
|
||||
self.targdata = []
|
||||
else:
|
||||
self.sourcedata = []
|
||||
with open(source, 'r') as inputfile:
|
||||
for line in inputfile.read().split('\n'):
|
||||
try:
|
||||
name, _, uid, _ = line.split(':', 3)
|
||||
uid = int(uid)
|
||||
except ValueError:
|
||||
continue
|
||||
if targfile:
|
||||
if uid < minid or uid > maxid:
|
||||
self.targdata.append(line)
|
||||
else:
|
||||
self.discardnames[name] = 1
|
||||
else:
|
||||
if name[0] in ('+', '#', '@'):
|
||||
self.sourcedata.append(line)
|
||||
elif uid >= minid and uid <= maxid:
|
||||
self.sourcedata.append(line)
|
||||
|
||||
def read_shadow(self, source):
|
||||
self.shadowlines = []
|
||||
try:
|
||||
with open(source, 'r') as inshadow:
|
||||
for line in inshadow.read().split('\n'):
|
||||
try:
|
||||
name, _ = line.split(':' , 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if name in self.discardnames:
|
||||
continue
|
||||
self.shadowednames[name] = 1
|
||||
self.shadowlines.append(line)
|
||||
except FileNotFoundError:
|
||||
return
|
||||
|
||||
def write_out(self, outfile):
|
||||
with open(outfile, 'w') as targ:
|
||||
for line in self.targdata:
|
||||
targ.write(line + '\n')
|
||||
for line in self.sourcedata:
|
||||
targ.write(line + '\n')
|
||||
if outfile == '/etc/passwd':
|
||||
if self.shadowlines is None:
|
||||
self.read_shadow('/etc/shadow')
|
||||
with open('/etc/shadow', 'w') as shadout:
|
||||
for line in self.shadowlines:
|
||||
shadout.write(line + '\n')
|
||||
for line in self.sourcedata:
|
||||
name, _ = line.split(':', 1)
|
||||
if name[0] in ('+', '#', '@'):
|
||||
continue
|
||||
if name in self.shadowednames:
|
||||
continue
|
||||
shadout.write(name + ':!:::::::\n')
|
||||
if outfile == '/etc/group':
|
||||
if self.shadowlines is None:
|
||||
self.read_shadow('/etc/gshadow')
|
||||
with open('/etc/gshadow', 'w') as shadout:
|
||||
for line in self.shadowlines:
|
||||
shadout.write(line + '\n')
|
||||
for line in self.sourcedata:
|
||||
name, _ = line.split(':' , 1)
|
||||
if name in self.shadowednames:
|
||||
continue
|
||||
shadout.write(name + ':!::\n')
|
||||
|
||||
def appendonce(basepath, filename):
|
||||
with open(filename, 'rb') as filehdl:
|
||||
thedata = filehdl.read()
|
||||
targname = filename.replace(basepath, '')
|
||||
try:
|
||||
with open(targname, 'rb') as filehdl:
|
||||
targdata = filehdl.read()
|
||||
except IOError:
|
||||
targdata = b''
|
||||
if thedata in targdata:
|
||||
return
|
||||
with open(targname, 'ab') as targhdl:
|
||||
targhdl.write(thedata)
|
||||
|
||||
def synchronize():
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
appendoncedir = tempfile.mkdtemp()
|
||||
try:
|
||||
ac = apiclient.HTTPSClient()
|
||||
myips = []
|
||||
ipaddrs = subprocess.check_output(['ip', '-br', 'a']).split(b'\n')
|
||||
for line in ipaddrs:
|
||||
isa = line.split()
|
||||
if len(isa) < 3 or isa[1] != b'UP':
|
||||
continue
|
||||
for addr in isa[2:]:
|
||||
if addr.startswith(b'fe80::') or addr.startswith(b'169.254'):
|
||||
continue
|
||||
addr = addr.split(b'/')[0]
|
||||
if not isinstance(addr, str):
|
||||
addr = addr.decode('utf8')
|
||||
myips.append(addr)
|
||||
data = json.dumps({'merge': tmpdir, 'appendonce': appendoncedir, 'myips': myips})
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles', data)
|
||||
if status >= 300:
|
||||
sys.stderr.write("Error starting syncfiles - {}:\n".format(status))
|
||||
sys.stderr.write(rsp.decode('utf8'))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
return status
|
||||
if status == 202:
|
||||
lastrsp = ''
|
||||
while status != 204:
|
||||
time.sleep(1+(2*random.random()))
|
||||
status, rsp = ac.grab_url_with_status('/confluent-api/self/remotesyncfiles')
|
||||
if not isinstance(rsp, str):
|
||||
rsp = rsp.decode('utf8')
|
||||
if status == 200:
|
||||
lastrsp = rsp
|
||||
pendpasswd = os.path.join(tmpdir, 'etc/passwd')
|
||||
if os.path.exists(pendpasswd):
|
||||
cm = CredMerger()
|
||||
cm.read_passwd(pendpasswd, targfile=False)
|
||||
cm.read_passwd('/etc/passwd', targfile=True)
|
||||
cm.write_out('/etc/passwd')
|
||||
pendgroup = os.path.join(tmpdir, 'etc/group')
|
||||
if os.path.exists(pendgroup):
|
||||
cm = CredMerger()
|
||||
cm.read_group(pendgroup, targfile=False)
|
||||
cm.read_group('/etc/group', targfile=True)
|
||||
cm.write_out('/etc/group')
|
||||
pendhosts = os.path.join(tmpdir, 'etc/hosts')
|
||||
if os.path.exists(pendhosts):
|
||||
cm = HostMerger()
|
||||
cm.read_source(pendhosts)
|
||||
cm.read_target('/etc/hosts')
|
||||
cm.write_out('/etc/hosts')
|
||||
for dirn in os.walk(appendoncedir):
|
||||
for filen in dirn[2]:
|
||||
appendonce(appendoncedir, os.path.join(dirn[0], filen))
|
||||
if lastrsp:
|
||||
lastrsp = json.loads(lastrsp)
|
||||
opts = lastrsp.get('options', {})
|
||||
for fname in opts:
|
||||
uid = -1
|
||||
gid = -1
|
||||
for opt in opts[fname]:
|
||||
if opt == 'owner':
|
||||
try:
|
||||
uid = pwd.getpwnam(opts[fname][opt]['name']).pw_uid
|
||||
except KeyError:
|
||||
uid = opts[fname][opt]['id']
|
||||
elif opt == 'group':
|
||||
try:
|
||||
gid = grp.getgrnam(opts[fname][opt]['name']).gr_gid
|
||||
except KeyError:
|
||||
gid = opts[fname][opt]['id']
|
||||
elif opt == 'permissions':
|
||||
os.chmod(fname, int(opts[fname][opt], 8))
|
||||
if uid != -1 or gid != -1:
|
||||
os.chown(fname, uid, gid)
|
||||
return status
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(appendoncedir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
status = 202
|
||||
while status not in (204, 200):
|
||||
try:
|
||||
status = synchronize()
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.flush()
|
||||
status = 300
|
||||
if status not in (204, 200):
|
||||
time.sleep((random.random()*3)+2)
|
||||
+26
-10
@@ -9,33 +9,49 @@ MGR=$(grep ^EXTMGRINFO: /custom-installation/confluent/confluent.info |awk -F'|'
|
||||
MGR=$(grep ^MANAGER: /custom-installation/confluent/confluent.info|head -n 1| awk '{print $2}')
|
||||
MGTIFACE=$(grep $MGR /custom-installation/confluent/confluent.info | grep ^EXTMGRINFO: | head -n 1 | awk -F'|' '{print $2}')
|
||||
oum=$(umask)
|
||||
umask 077
|
||||
chroot . custom-installation/confluent/bin/clortho $NODENAME $MGR > /root/custom-installation/confluent/confluent.apikey
|
||||
MGR=[$MGR]
|
||||
deploycfg=/root/custom-installation/confluent/confluent.deploycfg
|
||||
netcfgfile=$deploycfg
|
||||
umask 077
|
||||
if [ -e /tmp/cnflnthmackeytmp ]; then
|
||||
netcfgfile=/tmp/idnttmp
|
||||
hmackeyfile=/tmp/cnflnthmackeytmp
|
||||
#echo -n $(grep ^apitoken: /tmp/identdata/cnflnt.yml|awk '{print $2}') > $hmackeyfile
|
||||
passfile=/tmp/cnflnttmppassfile
|
||||
passcrypt=/tmp/cnflntcryptfile
|
||||
hmacfile=/tmp/cnflnthmacfile
|
||||
chroot . ln -sf /custom-installation/confluent/bin/clortho custom-installation/confluent/bin/genpasshmac
|
||||
cp $hmackeyfile tmp
|
||||
chroot . custom-installation/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
|
||||
chroot . curl -f -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_CRYPTHMAC: $(cat /root/$hmacfile)" -d @/tmp/cnflntcryptfile https://$MGR/confluent-api/self/registerapikey
|
||||
cp /root/$passfile /root/custom-installation/confluent/confluent.apikey
|
||||
DEVICE=$(cat /tmp/autodetectnic)
|
||||
else
|
||||
chroot . custom-installation/confluent/bin/clortho $NODENAME $MGR > /root/custom-installation/confluent/confluent.apikey
|
||||
MGR=[$MGR]
|
||||
nic=$(grep ^MANAGER /custom-installation/confluent/confluent.info|grep fe80::|sed -e s/.*%//|head -n 1)
|
||||
nic=$(ip link |grep ^$nic:|awk '{print $2}')
|
||||
DEVICE=${nic%:}
|
||||
fi
|
||||
if [ -z "$MGTIFACE" ]; then
|
||||
chroot . usr/bin/curl -f -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_APIKEY: $(cat /root//custom-installation/confluent/confluent.apikey)" https://${MGR}/confluent-api/self/deploycfg > $deploycfg
|
||||
else
|
||||
chroot . usr/bin/curl -f -H "CONFLUENT_MGTIFACE: $MGTIFACE" -H "CONFLUENT_NODENAME: $NODENAME" -H "CONFLUENT_APIKEY: $(cat /root//custom-installation/confluent/confluent.apikey)" https://${MGR}/confluent-api/self/deploycfg > $deploycfg
|
||||
fi
|
||||
umask $oum
|
||||
nic=$(grep ^MANAGER /custom-installation/confluent/confluent.info|grep fe80::|sed -e s/.*%//|head -n 1)
|
||||
nic=$(ip link |grep ^$nic:|awk '{print $2}')
|
||||
DEVICE=${nic%:}
|
||||
ipv4m=$(grep ^ipv4_method $deploycfg|awk '{print$2}')
|
||||
ipv4m=$(grep ^ipv4_method $netcfgfile|awk '{print$2}')
|
||||
. /scripts/functions
|
||||
if [ "$ipv4m" = "dhcp" ]; then
|
||||
IP=dhcp
|
||||
configure_networking
|
||||
elif [ "$ipv4m" = "static" ]; then
|
||||
v4addr=$(grep ^ipv4_address: $deploycfg)
|
||||
v4addr=$(grep ^ipv4_address: $netcfgfile| sed -e 's!/.*!!')
|
||||
v4addr=${v4addr#ipv4_address: }
|
||||
v4gw=$(grep ^ipv4_gateway: $deploycfg)
|
||||
v4gw=$(grep ^ipv4_gateway: $netcfgfile)
|
||||
v4gw=${v4gw#ipv4_gateway: }
|
||||
if [ "$v4gw" = "null" ]; then
|
||||
v4gw=""
|
||||
fi
|
||||
v4nm=$(grep ipv4_netmask: $deploycfg)
|
||||
v4nm=$(grep ipv4_netmask: $netcfgfile)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
dnsdomain=$(grep ^dnsdomain: $deploycfg)
|
||||
dnsdomain=${dnsdomain#dnsdomain: }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user