| 
124 | 124 | //  | 
125 | 125 | // Generated Spec Files, Multiple Directories, Device Precedence  | 
126 | 126 | //  | 
127 |  | -// There are systems where the set of available or usable CDI devices  | 
128 |  | -// changes dynamically and this needs to be reflected in the CDI Specs.  | 
129 |  | -// This is done by dynamically regenerating CDI Spec files which are  | 
130 |  | -// affected by these changes.  | 
 | 127 | +// It is often necessary to generate Spec files dynamically. On some  | 
 | 128 | +// systems the available or usable set of CDI devices might change  | 
 | 129 | +// dynamically which then needs to be reflected in CDI Specs. For  | 
 | 130 | +// some device classes it makes sense to enumerate the available  | 
 | 131 | +// devices at every boot and generate Spec file entries for each  | 
 | 132 | +// device found. Some CDI devices might need special client- or  | 
 | 133 | +// request-specific configuration which can only be fulfilled by  | 
 | 134 | +// dynamically generated client-specific entries in transient Spec  | 
 | 135 | +// files.  | 
131 | 136 | //  | 
132 | 137 | // CDI can collect Spec files from multiple directories. Spec files are  | 
133 | 138 | // automatically assigned priorities according to which directory they  | 
 | 
141 | 146 | // separating dynamically generated CDI Spec files from static ones.  | 
142 | 147 | // The default directories are '/etc/cdi' and '/var/run/cdi'. By putting  | 
143 | 148 | // dynamically generated Spec files under '/var/run/cdi', those take  | 
144 |  | -// precedence over static ones in '/etc/cdi'.  | 
 | 149 | +// precedence over static ones in '/etc/cdi'. With this scheme, static  | 
 | 150 | +// Spec files, typically installed by distro-specific packages, go into  | 
 | 151 | +// '/etc/cdi' while all the dynamically generated Spec files, transient  | 
 | 152 | +// or other, go into '/var/run/cdi'.  | 
 | 153 | +//  | 
 | 154 | +// Spec File Generation  | 
 | 155 | +//  | 
 | 156 | +// CDI offers two functions for writing and removing dynamically generated  | 
 | 157 | +// Specs from CDI Spec directories. These functions, WriteSpec() and  | 
 | 158 | +// RemoveSpec() implicitly follow the principle of separating dynamic Specs  | 
 | 159 | +// from the rest and therefore always write to and remove Specs from the  | 
 | 160 | +// last configured directory.  | 
 | 161 | +//  | 
 | 162 | +// Corresponding functions are also provided for generating names for Spec  | 
 | 163 | +// files. These functions follow a simple naming convention to ensure that  | 
 | 164 | +// multiple entities generating Spec files simultaneously on the same host  | 
 | 165 | +// do not end up using conflicting Spec file names. GenerateSpecName(),  | 
 | 166 | +// GenerateNameForSpec(), GenerateTransientSpecName(), and  | 
 | 167 | +// GenerateTransientNameForSpec() all generate names which can be passed  | 
 | 168 | +// as such to WriteSpec() and subsequently to RemoveSpec().  | 
 | 169 | +//  | 
 | 170 | +// Generating a Spec file for a vendor/device class can be done with a  | 
 | 171 | +// code snippet similar to the following:  | 
 | 172 | +//  | 
 | 173 | +// import (  | 
 | 174 | +//     "fmt"  | 
 | 175 | +//     ...  | 
 | 176 | +//     "github.com/container-orchestrated-devices/container-device-interface/specs-go"  | 
 | 177 | +//     "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"  | 
 | 178 | +// )  | 
 | 179 | +//  | 
 | 180 | +// func generateDeviceSpecs() error {  | 
 | 181 | +//     registry := cdi.GetRegistry()  | 
 | 182 | +//     spec := &specs.Spec{  | 
 | 183 | +//         Version: specVersion,  | 
 | 184 | +//         Kind:    vendor+"/"+class,  | 
 | 185 | +//     }  | 
 | 186 | +//  | 
 | 187 | +//     for _, dev := range enumerateDevices() {  | 
 | 188 | +//         spec.Devices = append(spec.Devices, specs.Device{  | 
 | 189 | +//             Name: dev.Name,  | 
 | 190 | +//             ContainerEdits: getContainerEditsForDevice(dev),  | 
 | 191 | +//         })  | 
 | 192 | +//     }  | 
 | 193 | +//  | 
 | 194 | +//     specName, err := cdi.GenerateNameForSpec(spec)  | 
 | 195 | +//     if err != nil {  | 
 | 196 | +//         return fmt.Errorf("failed to generate Spec name: %w", err)  | 
 | 197 | +//     }  | 
 | 198 | +//  | 
 | 199 | +//     return registry.WriteSpec(spec, specName)  | 
 | 200 | +// }  | 
 | 201 | +//  | 
 | 202 | +// Similary, generating and later cleaning up transient Spec files can be  | 
 | 203 | +// done with code fragments similar to the following. These transient Spec  | 
 | 204 | +// files are temporary Spec files with container-specific parametrization.  | 
 | 205 | +// They are typically created before the associated container is created  | 
 | 206 | +// and removed once that container is removed.  | 
 | 207 | +//  | 
 | 208 | +// import (  | 
 | 209 | +//     "fmt"  | 
 | 210 | +//     ...  | 
 | 211 | +//     "github.com/container-orchestrated-devices/container-device-interface/specs-go"  | 
 | 212 | +//     "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"  | 
 | 213 | +// )  | 
 | 214 | +//  | 
 | 215 | +// func generateTransientSpec(ctr Container) error {  | 
 | 216 | +//     registry := cdi.GetRegistry()  | 
 | 217 | +//     devices := getContainerDevs(ctr, vendor, class)  | 
 | 218 | +//     spec := &specs.Spec{  | 
 | 219 | +//         Version: specVersion,  | 
 | 220 | +//         Kind:    vendor+"/"+class,  | 
 | 221 | +//     }  | 
 | 222 | +//  | 
 | 223 | +//     for _, dev := range devices {  | 
 | 224 | +//         spec.Devices = append(spec.Devices, specs.Device{  | 
 | 225 | +//             // the generated name needs to be unique within the  | 
 | 226 | +//             // vendor/class domain on the host/node.  | 
 | 227 | +//             Name: generateUniqueDevName(dev, ctr),  | 
 | 228 | +//             ContainerEdits: getEditsForContainer(dev),  | 
 | 229 | +//         })  | 
 | 230 | +//     }  | 
 | 231 | +//  | 
 | 232 | +//     // transientID is expected to guarantee that the Spec file name  | 
 | 233 | +//     // generated using <vendor, class, transientID> is unique within  | 
 | 234 | +//     // the host/node. If more than one device is allocated with the  | 
 | 235 | +//     // same vendor/class domain, either all generated Spec entries  | 
 | 236 | +//     // should go to a single Spec file (like in this sample snippet),  | 
 | 237 | +//     // or transientID should be unique for each generated Spec file.  | 
 | 238 | +//     transientID := getSomeSufficientlyUniqueIDForContainer(ctr)  | 
 | 239 | +//     specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID)  | 
 | 240 | +//     if err != nil {  | 
 | 241 | +//         return fmt.Errorf("failed to generate Spec name: %w", err)  | 
 | 242 | +//     }  | 
 | 243 | +//  | 
 | 244 | +//     return registry.WriteSpec(spec, specName)  | 
 | 245 | +// }  | 
 | 246 | +//  | 
 | 247 | +// func removeTransientSpec(ctr Container) error {  | 
 | 248 | +//     registry := cdi.GetRegistry()  | 
 | 249 | +//     transientID := getSomeSufficientlyUniqueIDForContainer(ctr)  | 
 | 250 | +//     specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)  | 
 | 251 | +//  | 
 | 252 | +//     return registry.RemoveSpec(specName)  | 
 | 253 | +// }  | 
145 | 254 | //  | 
146 | 255 | // CDI Spec Validation  | 
147 | 256 | //  | 
 | 
0 commit comments