File: misc.py

package info (click to toggle)
charliecloud 0.43-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,084 kB
  • sloc: python: 6,021; sh: 4,284; ansic: 3,863; makefile: 598
file content (216 lines) | stat: -rw-r--r-- 6,860 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# Subcommands not exciting enough for their own module.

import argparse
import inspect
import itertools
import os
import os.path
import sys

import charliecloud as ch
import build_cache as bu
import filesystem as fs
import image as im
import pull
import version


## argparse “actions” ##

class Action_Exit(argparse.Action):

   def __init__(self, *args, **kwargs):
      super().__init__(nargs=0, *args, **kwargs)

class Dependencies(Action_Exit):

   def __call__(self, ap, cli, *args, **kwargs):
      # ch.init() not yet called, so must get verbosity from arguments.
      ch.dependencies_check()
      if (cli.verbose >= 1):
         print("lark path: %s" % os.path.normpath(inspect.getfile(im.lark)))
      sys.exit(0)

class Version(Action_Exit):

   def __call__(self, *args, **kwargs):
      print(version.VERSION)
      sys.exit(0)


## Plain functions ##

# Argument: command line arguments Namespace. Do not need to call sys.exit()
# because caller manages that.

def build_cache(cli):
   if (cli.bucache == ch.Build_Mode.DISABLED):
      ch.FATAL("build-cache subcommand invalid with build cache disabled")
   if (cli.reset):
      bu.cache.reset()
   if (cli.gc):
      bu.cache.garbageinate()
   if (cli.tree):
      bu.cache.tree_print()
   if (cli.dot):
      bu.cache.tree_dot()
   bu.cache.summary_print()

def delete(cli):
   fail_ct = 0
   for ref in cli.image_ref:
      delete_ct = 0
      for img in itertools.chain(im.Image.glob(ref),
                                 im.Image.glob(ref + "_stage[0-9]*")):
         bu.cache.unpack_delete(img)
         to_delete = im.Reference.ref_to_pathstr(str(img))
         bu.cache.branch_delete(to_delete)
         delete_ct += 1
      if (delete_ct == 0):
         fail_ct += 1
         ch.ERROR("no matching image, can’t delete: %s" % ref)
   bu.cache.worktrees_fix()
   if (fail_ct > 0):
      ch.FATAL("unable to delete %d invalid image(s)" % fail_ct)

def gestalt_bucache(cli):
   bu.have_deps()

def gestalt_bucache_dot(cli):
   bu.have_deps()
   bu.have_dot()

def gestalt_logging(cli):
   ch.TRACE("trace")
   ch.DEBUG("debug")
   ch.VERBOSE("verbose")
   ch.INFO("info")
   ch.WARNING("warning")
   ch.ERROR("error")
   if (cli.fail):
      ch.FATAL("the program failed inexplicably")

def gestalt_python_path(cli):
   print(sys.executable)

def gestalt_storage_path(cli):
   print(ch.storage.root)

def import_(cli):
   if (not os.path.exists(cli.path)):
      ch.FATAL("can’t copy: not found: %s" % cli.path)
   if (ch.xattrs_save):
      ch.WARNING("--xattrs unsupported by “ch-image import” (see FAQ)")
   pathstr = im.Reference.ref_to_pathstr(cli.image_ref)
   if (cli.bucache == ch.Build_Mode.ENABLED):
      # Un-tag previously deleted branch, if it exists.
      bu.cache.tag_delete(pathstr, fail_ok=True)
   dst = im.Image(im.Reference(cli.image_ref))
   ch.INFO("importing:    %s" % cli.path)
   ch.INFO("destination:  %s" % dst)
   dst.unpack_clear()
   if (os.path.isdir(cli.path)):
      dst.copy_unpacked(cli.path)
   else:  # tarball, hopefully
      dst.unpack([cli.path])
   bu.cache.adopt(dst)
   if (dst.metadata["history"] == []):
      dst.metadata["history"].append({ "empty_layer": False,
                                       "command":     "ch-image import"})
   dst.metadata_save()
   ch.done_notify()

def list_(cli):
   if (cli.undeletable):
      # list undeletable images
      imgdir = ch.storage.build_cache // "refs/tags"
   else:
      # list images
      imgdir = ch.storage.unpack_base
   if (cli.image_ref is None):
      # list all images
      if (not os.path.isdir(ch.storage.root)):
         ch.FATAL("does not exist: %s" % ch.storage.root)
      if (not ch.storage.valid_p):
         ch.FATAL("not a storage directory: %s" % ch.storage.root)
      images = sorted(imgdir.listdir())
      if (len(images) >= 1):
         img_width = max(len(ref) for ref in images)
         for ref in images:
            img = im.Image(im.Reference(fs.Path(ref).parts[-1]))
            if cli.long:
               print("%-*s | %s" % (img_width, img, img.last_modified.ctime()))
            else:
               print(img)
   else:
      # list specified image
      img = im.Image(im.Reference(cli.image_ref))
      print("details of image:    %s" % img.ref)
      # present locally?
      if (not img.unpack_exist_p):
         stored = "no"
      else:
         img.metadata_load()
         stored = "yes (%s), modified: %s" % (img.metadata["arch"],
                                              img.last_modified.ctime())
      print("in local storage:    %s" % stored)
      # in cache?
      (sid, commit) = bu.cache.find_image(img)
      if (sid is None):
         cached = "no"
      else:
         cached = "yes (state ID %s, commit %s)" % (sid.short, commit[:7])
         if (os.path.exists(img.unpack_path)):
            wdc = bu.cache.worktree_head(img)
            if (wdc is None):
               ch.WARNING("stored image not connected to build cache")
            elif (wdc != commit):
               ch.WARNING("stored image doesn’t match build cache: %s" % wdc)
      print("in build cache:      %s" % cached)
      # present remotely?
      print("full remote ref:     %s" % img.ref.canonical)
      pullet = pull.Image_Puller(img, img.ref)
      try:
         pullet.fatman_load()
         remote = "yes"
         arch_aware = "yes"
         arch_keys = sorted(pullet.architectures.keys())
         try:
            fmt_space = len(max(arch_keys,key=len))
            arch_avail = []
            for key in arch_keys:
               arch_avail.append("%-*s  %s" % (fmt_space, key,
                                               pullet.digests[key][:11]))
         except ValueError:
            # handles case where arch_keys is empty, e.g.
            # mcr.microsoft.com/windows:20H2.
            arch_avail = [None]
      except ch.Image_Unavailable_Error:
         remote = "no (or you are not authorized)"
         arch_aware = "n/a"
         arch_avail = ["n/a"]
      except ch.No_Fatman_Error:
         remote = "yes"
         arch_aware = "no"
         arch_avail = ["unknown"]
      pullet.done()
      print("available remotely:  %s" % remote)
      print("remote arch-aware:   %s" % arch_aware)
      print("host architecture:   %s" % ch.arch_host)
      print("archs available:     %s" % arch_avail[0])
      for arch in arch_avail[1:]:
         print((" " * 21) + arch)

def reset(cli):
   ch.storage.reset()

def undelete(cli):
   if (cli.bucache != ch.Build_Mode.ENABLED):
      ch.FATAL("only available when cache is enabled")
   img = im.Image(im.Reference(cli.image_ref))
   if (img.unpack_exist_p):
      ch.FATAL("image exists; will not overwrite")
   (_, git_hash) = bu.cache.find_deleted_image(img)
   if (git_hash is None):
      ch.FATAL("image not in cache")
   bu.cache.checkout_ready(img, git_hash)