| package commands |
| |
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| kernelmods "cos-extensions/extensions/kernel-mods" |
| "cos-extensions/tools/gcs" |
| "cos-extensions/tools/osutils" |
| |
| "github.com/golang/glog" |
| "github.com/spf13/cobra" |
| ) |
| |
| const ( |
| defaultLocalDir = "/var/lib/modules" |
| osReleasePath = "/etc/os-release" |
| defaultGCSBucket = "cos-tools" |
| ) |
| |
| var ( |
| statelessPaths = []string{"/var/lib/cloud", "/mnt/disks", "/tmp", "/etc"} |
| kernelModsListCmd = &cobra.Command{ |
| Use: "kernel-mods", |
| Short: "Lists the available compiled kernel modules.", |
| Long: `Lists the available compiled kernel modules |
| |
| Additional Description: |
| cos-extensions list kernel-mods [flags] lists all the available compiled kernel modules |
| in a gcs bucket directory.`, |
| Args: cobra.NoArgs, |
| PreRunE: func(cmd *cobra.Command, args []string) error { |
| installed, _ := cmd.Flags().GetBool("installed") |
| downloaded, _ := cmd.Flags().GetBool("downloaded") |
| isGCSset := cmd.Flags().Changed("gcs_path") |
| |
| if installed && isGCSset { |
| return fmt.Errorf("flags installed and gcs_path cannot be used together.") |
| } |
| if downloaded && isGCSset { |
| return fmt.Errorf("flags downloaded and gcs_path cannot be used together.") |
| } |
| if downloaded && installed { |
| return fmt.Errorf("flags downloaded and installed cannot be used together.") |
| } |
| return nil |
| }, |
| RunE: runKernelModsList, |
| } |
| |
| kernelModsInstallCmd = &cobra.Command{ |
| Use: "kernel-mods [module files] [flags] -- [module params]", |
| DisableFlagsInUseLine: true, |
| Short: "Installs kernel modules.", |
| Example: `cos-extensions install kernel-mods a.ko,b.ko --local-dir='/module' installs a.ko, b.ko from default gcs path to '/module' |
| cos-extensions install kernel-mods -r installs all modules in a gcs path recursively to default local directory |
| cos-extensions install kernel-mods var/a.ko --local-dir='/module' installs module a.ko from gcs sub directory 'var' to '/modules/var'`, |
| Long: `Installs compiled kernel modules |
| |
| Additional Description: |
| cos-extensions install kernel-mods [module files] downloads specified complied modules from a gcs bucket directory |
| [flags] -- [module params] and loads them to the kernel. Each module file should be |
| separated by a comma. |
| Note that module params should be used when downloading only one |
| module for appropriate behavior.`, |
| PreRunE: func(cmd *cobra.Command, args []string) error { |
| localOnly, _ := cmd.Flags().GetBool("local_only") |
| downloadOnly, _ := cmd.Flags().GetBool("download_only") |
| recursive, _ := cmd.Flags().GetBool("recursive") |
| |
| if localOnly && downloadOnly { |
| return fmt.Errorf("flags local_only and download_only cannot be used together.") |
| } else if localOnly && recursive { |
| return fmt.Errorf("flags local_only and recursive cannot be used together.") |
| } |
| if len(args) == 0 && !recursive { |
| return fmt.Errorf("requires at least 1 arg(s), only received 0. Arguments are only not required with the recursive flag.") |
| } else if len(args) != 0 && recursive { |
| return fmt.Errorf("flag recursive cannot be used with module files.") |
| } |
| return nil |
| }, |
| RunE: runKernelModsInstall, |
| } |
| ) |
| |
| func init() { |
| // Registering subcommands for kernel-mods extension |
| listCmd.AddCommand(kernelModsListCmd) |
| installCmd.AddCommand(kernelModsInstallCmd) |
| |
| defaultGCSPath, err := setDefaultGCSPath() |
| if err != nil { |
| glog.Errorf("Failed to retrieve default GCS path: %v\n", err) |
| } |
| |
| // Flag definitions for cos-extensions install kernel-mods |
| kernelModsInstallCmd.Flags().String("gcs_path", defaultGCSPath, |
| "GCS path which stores the available kernel modules to be installed. The expected syntax is gs://<bucket name>/<directory path>.") |
| kernelModsInstallCmd.Flags().String("local_dir", defaultLocalDir, "local directory where the kernel modules would be installed.") |
| kernelModsInstallCmd.Flags().BoolP("download_only", "d", false, "whether to download the module to the local directory without installing. "+ |
| "This flag cannot be used with the local_only flag.") |
| kernelModsInstallCmd.Flags().BoolP("local_only", "l", false, "whether to install modules directly from local directory without downloading. "+ |
| "This flag cannot be used with the download_only flag.") |
| kernelModsInstallCmd.Flags().BoolP("recursive", "r", false, "whether to install modules from a gcs bucket directory and its subdirectories. "+ |
| "This flag cannot be used with the local_only flag nor specified module files. Note that the modules are downloaded with their subdirectory path.") |
| |
| // Flag definitions for cos-extensions list kernel-mods |
| kernelModsListCmd.Flags().String("gcs_path", defaultGCSPath, |
| "GCS path from which available kernel modules would be listed. The expected syntax is gs://<bucket name>/<directory path>.") |
| kernelModsListCmd.Flags().String("local_dir", defaultLocalDir, "local directory from which kernel modules would be listed.") |
| kernelModsListCmd.Flags().BoolP("downloaded", "d", false, "whether to list only the downloaded compiled modules in a local directory. "+ |
| "This can be used with local_dir flag, else it uses default directory. It can be used with the recursive flag to include "+ |
| "downloaded modules in the subdirectories.") |
| kernelModsListCmd.Flags().BoolP("installed", "i", false, "whether to list only the installed kernel modules in a local directory. "+ |
| "This can be used with local_dir flag, else it uses the default directory. It can be used with the recursive flag to include "+ |
| "installed modules in subdirectories.") |
| kernelModsListCmd.Flags().BoolP("recursive", "r", false, "whether to list all the available compiled modules in the gcs path and its subdirectories. "+ |
| "This can be used with gcs_path flag, else it uses the default gcs path. It can be used with installed flag to list all installed kernels in the "+ |
| "directory and its subdirectories. It can also be used with the downloaded flag.") |
| } |
| |
| // runKernelModsList displays all available kernel modules in a gcs path if no flags |
| // are given or the gcs_path is set. It also displays all downloaded modules in a local |
| // directory if the downloaded flag is set, and all installed/loaded modules if the |
| // installed flag is set. |
| func runKernelModsList(cmd *cobra.Command, args []string) error { |
| ctx := context.Background() |
| downloaded, _ := cmd.Flags().GetBool("downloaded") |
| installed, _ := cmd.Flags().GetBool("installed") |
| |
| // Displays all available modules if installed and downloaded are not set. |
| // Default behavior. |
| if !downloaded && !installed { |
| err := displayAvailableModules(ctx, cmd) |
| if err != nil { |
| return fmt.Errorf("failed to list available kernel modules: %v", err) |
| } |
| } |
| |
| if downloaded { |
| err := displayDownloadedModules(cmd) |
| if err != nil { |
| return fmt.Errorf("failed to list downloaded kernel modules: %v", err) |
| } |
| } |
| |
| if installed { |
| err := displayInstalledModules(cmd) |
| if err != nil { |
| return fmt.Errorf("failed to list installed kernel modules: %v", err) |
| } |
| } |
| |
| return nil |
| } |
| |
| // runKernelModsInstall downloads the specified module files from a GCS path to a local directory. |
| // If the download_only flag is not set, loads the downloaded modules into the kernel. |
| // If local_only flag is set, loads the specified module files from a local directory into the kernel. |
| func runKernelModsInstall(cmd *cobra.Command, args []string) error { |
| ctx := context.Background() |
| |
| gcsPath, err := cmd.Flags().GetString("gcs_path") |
| if err != nil { |
| return fmt.Errorf("failed to retrieve gcs path (%q): %v", gcsPath, err) |
| } |
| localDir, err := cmd.Flags().GetString("local_dir") |
| if err != nil { |
| return fmt.Errorf("failed to retrieve gcs path (%q): %v", localDir, err) |
| } |
| localOnly, _ := cmd.Flags().GetBool("local_only") |
| downloadOnly, _ := cmd.Flags().GetBool("download_only") |
| recursive, _ := cmd.Flags().GetBool("recursive") |
| |
| // Displaying warning if local directory is stateless. |
| absPath, err := filepath.Abs(localDir) |
| if err != nil { |
| glog.Errorf("Failed to retrieve absolute path for file (%s): %v", localDir, err) |
| } else { |
| for _, dir := range statelessPaths { |
| if strings.HasPrefix(absPath, dir) { |
| glog.Warningf("WARNING: This local directory (%s) is stateless and would not retain downloaded files.", localDir) |
| } |
| } |
| } |
| |
| var downloadedModules, moduleFiles []string |
| if !recursive { |
| // Retrieving modules from positional arguments. |
| // Expected syntax of modules: mod1.ko,mod2.ko,mod3.ko |
| moduleFiles = strings.Split(args[0], ",") |
| } |
| |
| // Skipping downloading the module from gcs bucket if flag local_only is set. |
| if !localOnly { |
| // Creating a gcs client |
| client, err := gcs.NewClient(ctx) |
| if err != nil { |
| return fmt.Errorf("failed to create a new gcs client: %v", err) |
| } |
| var cfg gcs.GCSConfig |
| if err = cfg.Init(ctx, gcsPath, client); err != nil { |
| return fmt.Errorf("failed to initialize gcs config: %v", err) |
| } |
| |
| glog.Infof("Beginning download for module file(s): %s", moduleFiles) |
| downloadedModules, err = kernelmods.DownloadModules(ctx, &cfg, moduleFiles, localDir, recursive) |
| if err != nil { |
| return fmt.Errorf("failed to download modules (%s) from gcs path (%s): %v", moduleFiles, gcsPath, err) |
| } |
| } else { |
| // Retrieving the modules full path in the local directory |
| for _, module := range moduleFiles { |
| // Skipping if file doesn't exist. |
| modulePath := filepath.Join(localDir, module) |
| if _, err := os.Stat(modulePath); errors.Is(err, os.ErrNotExist) { |
| glog.Errorf("File (%s) does not exist [skipping]: %v\n", modulePath, err) |
| continue |
| } |
| downloadedModules = append(downloadedModules, modulePath) |
| } |
| } |
| |
| // Skipping installation if flag download_only set or downloadedModules is empty. |
| var installedModules []string |
| if !downloadOnly && len(downloadedModules) > 0 { |
| glog.Infof("Beginning installation of module file(s): %s", downloadedModules) |
| var moduleArgs []string |
| if len(args) > 1 { |
| moduleArgs = args[1:] |
| if len(downloadedModules) > 1 { |
| glog.Warning("WARNING: The module parameters set will be applied to all modules being installed. " + |
| "This might lead to unfavorable results if different settings are required for individual modules.") |
| |
| } |
| } |
| installedModules, err = kernelmods.InstallModules(downloadedModules, moduleArgs) |
| if err != nil { |
| return fmt.Errorf("failed to complete installation of modules (%s): %v", downloadedModules, err) |
| } |
| } |
| |
| // Generating log summary |
| glog.Info("Installation summary: ") |
| if !localOnly { |
| glog.Infof("Downloaded %d module file(s) - (%s)", len(downloadedModules), downloadedModules) |
| } |
| if !downloadOnly { |
| glog.Infof("Installed %d module(s) - (%s)", len(installedModules), installedModules) |
| } |
| return nil |
| } |
| |
| func setDefaultGCSPath() (string, error) { |
| defaultGCSPath := fmt.Sprintf("gs://%s", defaultGCSBucket) |
| osfile, err := osutils.Parse(osReleasePath) |
| if err != nil { |
| return "", fmt.Errorf("failed to parse os release file: %v", err) |
| } |
| buildID, ok := osfile["BUILD_ID"] |
| if !ok { |
| return "", fmt.Errorf("failed to retrieve build id from os release file (%s)", osReleasePath) |
| } |
| defaultGCSPath = fmt.Sprintf("%s/%s/kernel-mods", defaultGCSPath, buildID) |
| return defaultGCSPath, nil |
| } |
| |
| func displayInstalledModules(cmd *cobra.Command) error { |
| localDir, err := cmd.Flags().GetString("local_dir") |
| if err != nil { |
| return fmt.Errorf("failed to retieve local directory (%q): %v", localDir, err) |
| } |
| recursive, _ := cmd.Flags().GetBool("recursive") |
| |
| installedModules, err := kernelmods.ListInstalledModules(localDir, recursive) |
| if err != nil { |
| return fmt.Errorf("failed to retrieve installed modules in directory (%s): %v", localDir, err) |
| } |
| if len(installedModules) > 0 { |
| fmt.Fprintf(os.Stdout, "Installed modules in local directory (%s):\n", localDir) |
| fmt.Fprintf(os.Stdout, "%-20s %-20s\n", "Module Name", "Path") |
| for filePath, name := range installedModules { |
| fmt.Fprintf(os.Stdout, "%-20s %-20s\n", name, filePath) |
| } |
| } else { |
| glog.Infof("No installed modules found in directory (%s)", localDir) |
| } |
| return nil |
| } |
| |
| func displayDownloadedModules(cmd *cobra.Command) error { |
| localDir, err := cmd.Flags().GetString("local_dir") |
| if err != nil { |
| return fmt.Errorf("failed to retieve local directory (%q): %v", localDir, err) |
| } |
| recursive, _ := cmd.Flags().GetBool("recursive") |
| |
| downloadedModules, err := kernelmods.ListDownloadedModules(localDir, recursive) |
| if err != nil { |
| return fmt.Errorf("failed to retrieve downloaded modules in directory (%s): %v", localDir, err) |
| } |
| |
| if len(downloadedModules) > 0 { |
| fmt.Fprintf(os.Stdout, "Downloaded modules in local directory (%s):\n", localDir) |
| fmt.Fprintf(os.Stdout, "%-20s %-20s\n", "Module Name", "Path") |
| for filePath, name := range downloadedModules { |
| fmt.Fprintf(os.Stdout, "%-20s %-20s\n", name, filePath) |
| } |
| } else { |
| glog.Infof("No downloaded modules found in directory (%s)", localDir) |
| } |
| return nil |
| } |
| |
| func displayAvailableModules(ctx context.Context, cmd *cobra.Command) error { |
| gcsPath, err := cmd.Flags().GetString("gcs_path") |
| if err != nil { |
| return fmt.Errorf("failed to retieve gcs path (%q): %v", gcsPath, err) |
| } |
| recursive, _ := cmd.Flags().GetBool("recursive") |
| |
| // Creating a gcs client |
| client, err := gcs.NewClient(ctx) |
| if err != nil { |
| return fmt.Errorf("failed to create a new gcs client: %v", err) |
| } |
| |
| var cfg gcs.GCSConfig |
| if err = cfg.Init(ctx, gcsPath, client); err != nil { |
| return fmt.Errorf("failed to initialize gcs config: %v", err) |
| } |
| availableModules, err := cfg.ListModules(ctx, recursive) |
| if err != nil { |
| return fmt.Errorf("failed to retieve available modules from gcs: %v", err) |
| } |
| |
| fmt.Fprintf(os.Stdout, "Available modules in gcs path (%s):\n", gcsPath) |
| for _, mod := range availableModules { |
| fmt.Fprintln(os.Stdout, mod) |
| } |
| return nil |
| } |