Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support livepatch - part 1 #4678

Merged
merged 10 commits into from
Apr 25, 2022

Conversation

minglumlu
Copy link
Member

This series contains the first part of changes to support live patch:

  1. some existing codes are split into new files;
  2. new functions to parse metadata of live patches from updateinfo;
  3. get the status of running live patch (Xen and Kernel) on host;
  4. get all applicable live patches from updateinfo;
  5. calculate and consolidate guidance.

@minglumlu minglumlu requested review from robhoes, lindig, edwintorok and psafont and removed request for robhoes April 11, 2022 08:58
type component = Xen | Kernel

let component_of_string = function
| "Xen" | "xen" ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to use String.lowercase_ascii before the match is made.

ocaml/xapi/livepatch.ml Show resolved Hide resolved
* livepatch_4_19_19__8_0_20__4_19_19__8_0_22
*)
Re.Posix.compile_pat
{|^[ ]*livepatch_([^ \[]+)__([^ \[]+)__([^ \[]+)__([^ \[]+).*$|}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this regex stronger? ([0-9_]+) seems to capture the expected version strings.

Copy link
Member Author

@minglumlu minglumlu Apr 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version and release strings may contain other characters. Actually we don't have any expectations on the strings. Here we only need to separate them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option might be to chop off the livepatch_ prefix and then split the string on __. I suppose it doesn't really matter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can describe the operation with a regular expression (because the number of fields is known in advance) I would prefer that over a procedural approach of chopping an splitting - let the regex engine do the work. A regex doesn't work if this becomes context sensitive - earlier information informs later decisions. In that case I would again consider using Angstrom because it is still quite declarative.

| true ->
(acc, true)
| false ->
if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be expressed as when clause on the match case.

)
|> get_latest_livepatch

let get_base_build_id () =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this code doing? It seems quite repetitive and using low-level operations. Parsing binary data could be easier using Angstrom which has operations to read bytes and Big Endian, Small Endian numbers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is going to read ELF notes from file /sys/kernel/notes to find out the running kernel's build id.
The file content is organized as some consecutive sections. For each section, at beginning of it, it's a header structure described by headers_struct. And the length of a section is determined by its headers. This function needs to find a specific section and read build id in it.

If Angstrom works for this in better way, I will look into it. Thanks :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, this is code I wrote to read the (binary) header of a FIT file. There is more complex code in that file but it gives you a flavor how to read bytes, multi-byte numbers, and strings.

Copy link
Member

@psafont psafont Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @lindig @psafont
Thanks for your pointers. I've reworked this with Angstrom which looks very great.

ocaml/xapi/livepatch.mli Show resolved Hide resolved
* GNU Lesser General Public License for more details.
*)

module Epoch : sig
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs doucumentation. What are these modules describing?

@minglumlu
Copy link
Member Author

Hi Reviewers,
I reworked based on existing comments. May I please have your review more?
I squashed the fixup as I think the changes in rework are too dramatic. Sorry for this.

Copy link
Contributor

@lindig lindig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code to extract the build information is much better now.

type t = {
component: component
; base_build_id: string
; to_version: string option
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am bit surprised about to_ - it is somewhat unusual. Does this indicate the version and release reached after applying the patch? Maybe a comment could clarify this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It indicates that the version and release reached after applying the live patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that there are comments that explain this in the mli file.

module BuildId = struct
let one_byte =
let open Angstrom in
any_char >>= fun c ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use any_uint8 or any_int8 - which will result in an int.

section' ()
)

let section = section' ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rename section' to section and get rid of this?

let align x =
(* The actual size is aligned with 4. *)
let open Int32 in
(if rem x 4l = 0l then x else add x (sub 4l (rem x 4l))) |> to_int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is using 41 but the comment says something is 4-byte aligned. What is the relevance of 41? What is the result of this function for a given x?

Copy link
Contributor

@lindig lindig Apr 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I misread this. The 1 is actually an l - a size indicator. A comment what align 6 would do would be still helpful. Are we rounding up or down to a multiple of 4? I assume align x rounds x up to the next multiple of 4. I believe it could be simplified (here for integers):

let align4 n = ((n + 3) / 4) * 4

utop # align4 12;;
- : int = 12

utop # align4 10;;
- : int = 12

utop # align4 13;;
- : int = 16

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is great. Thanks.

let get_installed_livepatch_file ~component ~base_build_id =
let lp_dir = get_livepatch_dir ~component ~base_build_id in
let installed_symlink = Filename.concat lp_dir "livepatch.livepatch" in
if not (Sys.file_exists installed_symlink) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be done with a single match:

match Unix.lstat installed_symlink with
| Unix.{st_kind = S_LNK;_} -> ... Option .some
| exception _ -> None


let error_msg = Printf.sprintf "Failed to parse '%s'"

let parse_epoch_version_release epoch_ver_rel =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module looks too complicated to me. I would expect that a version is represented as a list of numbers and that we are comparing two strings to establish their order. I think it needs to be spelled out how strings and numbers are treated: is every string that can be read as a number considered a number, and or still a string? This makes a difference because "01" and "1" are different strings but the same number. Likewise, how is any string that cannot be read as a number treated? I am not sure but I would have started with:

type comp = Number of int | String of string
type version = comp list (* assuming a version is a list of components, each either a string or a number *)
val parse: string -> string * version (* split into package and version *)
val compare: version -> version -> int (* -1, 0, 1 *)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These bits are some existing lines which are moved into a new file only in this PR.

type comp = Number of int | String of string
This is defined as similarly as:
https://github.com/xapi-project/xen-api/pull/4678/files#diff-3c831fd11744c8735ff48fa1f9e436f357cb4316e4394855770ac300ed41dc69R55

val parse: string -> string * version (* split into package and version *)
is implemented like:
https://github.com/xapi-project/xen-api/pull/4678/files#diff-3c831fd11744c8735ff48fa1f9e436f357cb4316e4394855770ac300ed41dc69R183

and val compare: version -> version -> int (* -1, 0, 1 *) is implemented as:
https://github.com/xapi-project/xen-api/pull/4678/files#diff-3c831fd11744c8735ff48fa1f9e436f357cb4316e4394855770ac300ed41dc69R160

There are some examples to explain the rules of comparison:
https://github.com/xapi-project/xen-api/pull/4678/files#diff-3c831fd11744c8735ff48fa1f9e436f357cb4316e4394855770ac300ed41dc69R166

@minglumlu minglumlu force-pushed the private/mingl/CP-32574 branch 2 times, most recently from 91a5854 to 0dd784a Compare April 21, 2022 03:04
type component = Xen | Kernel

let component_of_string x =
match Astring.String.Ascii.lowercase x with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deprecated; use lowercase_ascii instead

@@ -304,6 +334,17 @@ let set_available_updates ~__context =
Helpers.run_in_parallel funs capacity_in_parallel
)
in
let applied_livepatches_of_hosts =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many lines is this function long? I would encourage to split out more functions that are defined in this function. If this is done just to keep the name space clean, I would use a local module rather than making this function larger and larger.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I split some functions out. Indeed, the set_available_updates function was too long.

Copy link
Member

@robhoes robhoes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not easy to get a good overview of the logic and control flow because there is a lot going on in the >1000 changed lines. I think it comes down to testing well. It mostly looks fine.

@robhoes
Copy link
Member

robhoes commented Apr 21, 2022

Please do not merge yet: we are sorting out some build issues first.

@minglumlu
Copy link
Member Author

It's not easy to get a good overview of the logic and control flow because there is a lot going on in the >1000 changed lines. I think it comes down to testing well. It mostly looks fine.

Sure. I will re-do tests before merging this one. And the following PRs will be unit tests and applying live patches.

@minglumlu
Copy link
Member Author

Hi @lindig @robhoes
I appended a new commit for a minor fixup. 170e6c9
Would you please have a review again on it?

@robhoes
Copy link
Member

robhoes commented Apr 22, 2022

Looks like there are now a few conflicts due to the warning-removal PR...

Signed-off-by: Ming Lu <ming.lu@citrix.com>
This commit doesn't change any code. It only move some codes to a few
new files so that they could be used more easily later.

Signed-off-by: Ming Lu <ming.lu@citrix.com>
Signed-off-by: Ming Lu <ming.lu@citrix.com>
Signed-off-by: Ming Lu <ming.lu@citrix.com>
…atches

Signed-off-by: Ming Lu <ming.lu@citrix.com>
Signed-off-by: Ming Lu <ming.lu@citrix.com>
Signed-off-by: Ming Lu <ming.lu@citrix.com>
Signed-off-by: Ming Lu <ming.lu@citrix.com>
This commit fixes the bug introduced in c081442:
prior to c081442, the bool value is for [up_to_date], while after the
commit, it is for [pkg_updates_available].
If there is(are) RPM package updates, it means a host is NOT up to date.

This commit is just for this bug.

Signed-off-by: Ming Lu <ming.lu@citrix.com>
@minglumlu minglumlu force-pushed the private/mingl/CP-32574 branch 2 times, most recently from a9189b4 to 4c0cbc1 Compare April 24, 2022 09:12
Signed-off-by: Ming Lu <ming.lu@citrix.com>
@minglumlu
Copy link
Member Author

minglumlu commented Apr 24, 2022

Tests have been done. And:
fe0634c is a result of rebasing and squashing.
8017dcf is a fixup for an issue found in test.
de7c897 is a fixup for "unused" warnings

@minglumlu minglumlu merged commit e0d286f into xapi-project:master Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants