General Actions:
Log-in
xwiki
Community Group zones
Subsites
:
Code Reviews
SCM Management
Package Search
Bugster
Bugzilla
Test Machines
Planet
Mailing Lists
Elections & Polls
ARC Case Logs
Source Juicer
Package Factory
Auth
Collectives
Discussions
Documentation
Download
Source Browser
Search
Zones Project Documents
»
zones on shared storage proposal
en
Page Actions:
Export
:
Export as PDF
Export as RTF
Export as HTML
View
:
Comments
Attachments
History
Information
View Source
More actions
:
Print preview
Wiki source code of
zones on shared storage proposal
Hide line numbers
1: {{{ 2: 3: " please ensure that the vim modeline option is not disabled 4: vim:textwidth=72 5: 6: ------------------------------------------------------------------------------- 7: Zones on shared storage (v1.3) 8: 9: 10: ---------- 11: A. INTRODUCTION 12: 13: The most commonly requested zones feature is support for the NFS server 14: within a zone (4964859). The next two most requested zones features are 15: support for hosting zones on shared storage, specifically NFS and iSCSI. 16: These last two requests are being tracked via the following bugs: 17: 18: 6688400 Want zonepath on iscsi targets 19: 4963321 RFE: hosting root filesystems for Zones on NFS servers 20: 21: This document proposed a plan to address the latter two issues. These 22: proposed changes will also start to bring the zones storage 23: administration closer in line with the experience provided by other 24: virtualization technologies (VTs, examples of which are xVM, LDOMs, and 25: VirtualBox). 26: 27: 28: ---------- 29: B. BACKGROUND 30: 31: Currently, it is possible to combine existing supported technologies 32: (FC, iSCSI, NFS, lofi, zfs, etc) to host zones on shared storage, and 33: there are a few blog entries out there describing how to create such 34: configurations[00]. While these configurations are usable and 35: technically supportable, they require extensive configuration of 36: multiple different technologies, making them complex, potentially 37: fragile (since they are not regularly tested), and out of reach for most 38: customers. 39: 40: Creating these configurations today also causes problems with the use of 41: other existing zones features, the most obvious of which is zone 42: migration. These configurations complicate zone migration because all 43: of the additional global zone configuration that was required to host 44: the zone on shared storage must be tracked and then migrated along with 45: the zone data itself. VM Migration is critical component of all 46: existing VTs and anything that can be done to improve zone migration 47: support would be a great benefit to zones administrators. 48: 49: All the other virtualization technologies currently available in Solaris 50: support the hosting of Virtual Machines (VMs) on shared storage. They 51: all do this in the same fashion, which involves taking a "storage 52: object"[01] which is accessible from the "global zone"[02], and making 53: it visible within the VM as a local disk. In this document we’re refer 54: to this process as "encapsulation", since the "disk" which a VM thinks 55: it’s accessing is actually contained (ie encapsulated) within some 56: storage object in the global zone. 57: 58: This encapsulation has advantages and disadvantages. Encapsulation 59: makes it easier to manage the storage associated with VMs. These 60: storage objects may be accessible from multiple hosts simultaneously. 61: They can be backed up, restored, moved around, and copied as individual 62: files instead of filesystems. One disadvantage of the encapsulation 63: used by these other VTs is that currently on Solaris, there is no way to 64: open up these encapsulated disks and access their contents from the 65: global zone. Currently, the only way to access these encapsulated disks 66: is to import them into a running VM. 67: 68: Another interesting development with these encapsulated disks used by 69: other VTs is the proliferation of storage object formats. These custom 70: storage objects formats allows for features which are transparent to the 71: VM using the disks and independent of any features offered by the 72: underlying global zone. While specific feature sets may vary, common 73: features that may be supported are things like compression, sparseness, 74: dedup, snapshotting, and rollback. Management of all these storage 75: object features is usually done from the global zone. Examples of some 76: of these different formats are: 77: 78: VDI - VirtualBox Virtual HDD 79: VHD - Microsoft Virtual Hard Disk 80: VMDK - VMWare Virtual Machine Disk 81: 82: These formats are also commonly used for moving and sharing VM images, 83: and it’s not uncommon for users to take VMs encapsulated in one format 84: and convert them into another. 85: 86: 87: ---------- 88: C. PROPOSAL / DESCRIPTION 89: 90: The essence of this proposal is to enhance zonecfg(1m) to allow for the 91: specification of shared storage objects which can be used to encapsulate 92: zone filesystems, including the zones root filesystem. Once shared 93: storage objects are specified in zonecfg(1m) the management of this 94: shared storage will be handled automatically by the zones framework. 95: This will allow administrators to host zones on shared storage with no 96: additional system configuration being required outside of zonecfg(1m). 97: It will also provide zones with a consistent global zone storage usage 98: and administration experience compared with other VTs. All the existing 99: zoneadm(1m) operations that administrators can do today should continue 100: to work seamlessly with this new support for shared storage objects. 101: 102: The proposal is broken down into the following subsections: 103: C.0 Out of scope 104: C.1 Zones enhancements 105: C.2 Storage object uid/gid handling 106: C.3 Taskq enhancements 107: C.4 Zfs enhancements 108: C.5 Lofi and lofiadm(1m) enhancements 109: C.6 Performance considerations 110: C.7 Phased delivery 111: C.8 Future work 112: 113: 114: ---------- 115: C.0 Out of scope 116: 117: Before discussing the details of the changes being proposed, here are 118: the things this proposal will not address: 119: 120: - Hosting zones natively on NFS. We will not be considering 121: enhancements which would allow zone roots to live natively (ie, 122: unencapsulated) on NFS. Supporting this functionality is not prevented 123: by anything proposed herein, but initially we are not pursuing this 124: option because: 125: 126: - Encapsulation allows us to host zones on a myriad of storage 127: in addition to NFS. 128: - Encapsulation brings the zones storage feature set closer in 129: line with other VTs. 130: - Encapsulation will likely be easier to implement than 131: unencapsulated hosting of a zone root filesystems on NFS. 132: 133: - Advanced shared storage configuration. Initially there will be no 134: support for advanced shared storage configuration options. If 135: advanced configuration options need to be used, they will need to be 136: setup in the global zone outside the zones framework. That said, 137: nothing in this proposal should prevent us from adding support for 138: more complex configurations in the future, should they become popular. 139: Some examples of what would be considered advanced shared storage 140: configuration options are (and this is by no means a complete list): 141: 142: - iSCSI target discovery management 143: - iSCSI target parameter configuration (CHAP, etc) 144: - custom vdisk creation options 145: - encapsulated zpools spanning multiple storage objects 146: - custom zpool/zfs creation options 147: 148: 149: ---------- 150: C.1 Zones enhancements 151: 152: This is a list of proposed enhancements to specific zones subsystems. 153: It is broken down into the following categories: 154: C.1.i Zonecfg(1m) 155: C.1.ii Storage object uri (so-uri) format 156: C.1.iii Zoneadm(1m) install 157: C.1.iv Zoneadm(1m) attach 158: C.1.v Zoneadm(1m) boot 159: C.1.vi Zoneadm(1m) detach 160: C.1.vii Zoneadm(1m) uninstall 161: C.1.viii Zoneadm(1m) clone 162: C.1.ix Zoneadm(1m) move 163: C.1.x Zonecfg(1m) misc changes 164: 165: 166: ---------- 167: C.1.i Zonecfg(1m) 168: 169: The zonecfg(1m) command will be enhanced with the following two new 170: resources and associated properties: 171: 172: rootzpool resource 173: src resource property 174: install-size resource property 175: zpool-preserve resource property 176: dataset resource property 177: user resource property 178: 179: zpool resource 180: src resource property 181: install-size resource property 182: zpool-preserve resource property 183: name resource property 184: user resource property 185: 186: The new resource and properties will be defined as follows: 187: 188: "rootzpool" 189: - Status: Optional. 190: - Description: Identifies a shared storage object (and it’s 191: associated parameters) which will be used to contain the root zfs 192: filesystem for a zone. Only one "rootzpool" may be defined 193: per zone. 194: 195: "zpool" 196: - Status: Optional. 197: - Description: Identifies a shared storage object (and it’s 198: associated parameters) which will be made available to the 199: zone as a delegated zfs dataset. Multiple "zpool" resources 200: may be defined per zone. 201: 202: "src" 203: - Status: Required. 204: - Format: Storage object uri (so-uri). (See definition below.) 205: - Description: Identifies the storage object associated with this 206: resource. 207: 208: "install-size" 209: - Status: Optional. 210: - Format: Integer. Defaults to bytes, but can be flagged as 211: gigabytes, kilobytes, or megabytes, with a g, k, or m suffix, 212: respectively. 213: - Description: If the specified storage object doesn’t exist at zone 214: install time it will be created with this specific size. This 215: property has no effect for storage objects which already exist and 216: have a pre-defined size. 217: 218: "zpool-preserve" 219: - Status: Optional. 220: - Format: Boolean. Defaults to false. 221: - Description: When doing an install, if this property if this 222: property is set to true and a zpool already exists on the 223: specified storage object it will be used. When doing a destroy, 224: if this property is set to true, the root zpool will not be 225: destroyed. 226: 227: "dataset" 228: - Status: Optional 229: - Format: zfs filesystem name component (can’t contain a ’/’) 230: - Description: Name of a dataset within the root zpool to delegate 231: to the zone. 232: 233: "name" 234: - Status: Required 235: - Format: zfs filesystem name component (can’t contain a ’/’) 236: - Description: Used as part of the name for a zpool which will be 237: delegated to the zone. 238: 239: "user" 240: - Status: Optional 241: - Format: User name string 242: - Description: User name to use when accessing a path:// or nfs:// based 243: storage object. 244: 245: Zonecfg(1m) "verify" will verify the syntax of any "rootzpool" resource 246: group (and its properties), but it will NOT verify the accessibility of 247: any storage specified by by a so-uri. (This is because accessing the 248: storage specified by an so-uri could require configuration changes to 249: other subsystems.) 250: 251: These new resources should provide zone administrators with lots of 252: flexibility when it comes to deploying zones on shared storage. Some of 253: the likely zone deployment models that are envisioned and enabled by 254: this proposal are: 255: 256: - A zone with an encapsulated "rootzpool" zpool. In this scenario the 257: OS will be stored in the "rootzpool", and all non-OS software and data 258: will also be stored in datasets which are descendent from the zone root 259: dataset. This means that system operations which snapshot and clone the 260: OS will also snapshot and clone non-OS software and data. 261: 262: - A zone with an encapsulated "rootzpool" zpool and a "dataset" defined 263: within the "rootzpool". In this scenario, the OS will be stored in in 264: the "rootzpool", and all non-OS software and data will be stored within 265: "dataset", which is also contained within the "rootzpool", but is not a 266: direct descendent of any zone root dataset. This means that system 267: operations which snapshot and clone the OS will not affect non-OS 268: software and data. 269: 270: - A zone with an encapsulated "rootzpool" and one or more encapsulated 271: "zpool"s. In this scenario, the OS will be stored in in the 272: "rootzpool", and all non-OS software and data will be stored within 273: other "zpool"s. This means that system operations which snapshot and 274: clone the OS will not affect non-OS software and data. 275: 276: More information about these new resources and how they will be managed 277: by the zones framework is available below. 278: 279: 280: ---------- 281: C.1.ii Storage object uri (so-uri) format 282: 283: The storage object uri (so-uri) syntax[03] will conform to the standard 284: uri format defined in RFC 3986 [04]. The nfs URI scheme is defined in 285: RFC 2224 [05]. The so-uri syntax can be summarised as follows: 286: 287: File and vdisk storage objects: 288: 289: path:///<file-absolute> 290: nfs://<host>[:port]/<file-absolute> 291: 292: Device storage objects: 293: 294: path:///dev/<file-absolute> 295: fc:///wwn[@<lun>] 296: iscsi:///alias=<alias>[@<lun>] 297: iscsi:///target=<target>[@<lun>] 298: iscsi://host[:port]/[tpgt=<tpgt>/]target=<target>[@<lun>] 299: 300: File storage objects point to plain files on a local, nfs, or cifs 301: filesystems. These files are used to contain zpools which store zone 302: datasets. These are the simplest types of storage objects. Once 303: created, they have a fixed size, can’t be grown, and don’t support 304: advanced features like snapshotting, etc. Some example file so-uris 305: are: 306: 307: path:///export/xvm/vm1.disk 308: - a local file 309: path:///net/heaped.sfbay/export/xvm/1.disk 310: - a nfs file accessible via autofs 311: nfs://heaped.sfbay/export/xvm/1.disk 312: - same file specified directly via a nfs so-uri 313: 314: Vdisk storage objects are similar to file storage objects in that they 315: can live on local, nfs, or cifs filesystems, but they each have their 316: own special data format and varying feature sets, with support for things 317: like snapshotting, etc.. Some common vdisk formats are: VDI, VMDK and 318: VHD. Some example vdisk so-uris are: 319: 320: path:///export/xvm/vm1.vmdk 321: - a local vdisk image directory 322: path:///net/heaped.sfbay/export/xvm/1.vmdk 323: - a nfs vdisk image directory accessible via autofs 324: nfs://heaped.sfbay/export/xvm/1.vmdk 325: - same vdisk image directory specified directly via a nfs so-uri 326: 327: Device storage objects specify block storage devices in a host 328: independent fashion. Some /dev device names may already be named in a 329: host independent fashion. In this case the admin can simply specify the 330: /dev device path for this device as the so-uri. When configuring FC or 331: iscsi storage on different hosts, the storage configuration normally 332: lives outside of zonecfg, and the configured storage may have varying 333: /dev/dsk/cXtXdX* names. In these cases, the so-uri syntax provides a 334: way to specify storage in a host independent fashion, and during zone 335: management operations, the zones framework can map this storage to a 336: host specific device path. Some example device so-uris are: 337: 338: path:///dev/vx/dsk/zone1/rootvol 339: - a Veritas volume that is accessible from multiple hosts using the 340: same name 341: fc:///20000014c347492a@0 342: - lun 0 of a fc disk with the specified wwn 343: iscsi:///alias=oracle zone root@0 344: - lun 0 of an iscsi disk with the specified alias. 345: iscsi:///target=iqn.1986-03.com.sun:02:38abfd16-78c5-c58e-e629-ea77a33c6740 346: - lun 0 of an iscsi disk with the specified target id. 347: 348: 349: ---------- 350: C.1.iii Zoneadm(1m) install 351: 352: When a zone is installed via the zoneadm(1m) "install" subcommand, the 353: zones subsystem will first verify that any required so-uris exist and 354: are accessible. 355: 356: If an so-uri points to a plain file (or a file on an nfs server), and 357: the file object does not exist, the object will be created with the 358: install-size that was specified via zonecfg(1m). If the so-uri does not 359: exist and an install-size was not specified via zonecfg(1m) an error 360: will be generated and the install will fail. 361: 362: If the so-uri points to a vdisk, the vdisk must have been previously 363: created using vdiskadm(1m). 364: 365: If an so-uri points to an explicit nfs server, the zones framework will 366: need to mount the nfs filesystem containing storage object. The nfs 367: server share containing the specified object will be mounted at: 368: 369: /var/zones/nfsmount/<host>/<nfs-share-name> 370: 371: If an so-uri points to a fibre channel lun, the zones subsystem will 372: verify that the specified wwn corresponds to a global zone accessible 373: fibre channel disk device. 374: 375: If an so-uri points to an iSCSI target or alias, the zones subsystem 376: will verify that the iSCSI device is accessible on the local system. If 377: an so-uri points to a static iSCSI target and that target is not 378: already accessible on the local host, then the zones subsystem will 379: enable static discovery for the local iSCSI initiator and attempt to 380: apply the specified static iSCSI configuration. If the iSCSI target 381: device is not accessible then the install will fail. 382: 383: Once a zones install has verified that any required so-uri exists and is 384: accessible, the zones subsystem will need to initialise the so-uri. In 385: the case of a path or nfs path, this will involve creating a zpool 386: within the specified file. In the case of a vdisk, fibre channel lun, 387: or iSCSI lun, this will involve creating a EFI/GPT partition on the 388: device which uses the entire disk, then a zpool will be created within 389: this partition. For data protection purposes, if a storage object 390: contains any pre-existing partitions, zpools, or ufs filesystems, the 391: install will fail with an appropriate error message. To continue the 392: installation and overwrite any pre-existing data, the user will be able 393: to specify a new ’-f’ option to zoneadm(1m) install. (This option 394: mimics the ’-f’ option used by zpool(1m) create.) 395: 396: If zpool-preserve is set to true, then before initialising any target 397: storage objects, the zones subsystem will attempt to import a 398: pre-existing zpool from those objects. This will allow users to 399: pre-create a zpool with custom creation time options, for use with 400: zones. To successfully import a pre-created zpool for a zone install, 401: that zpool must not be attached. (Ie, any pre-created zpool must be 402: exported from the system where it was created before a zone can be 403: installed on it.) Once the zpool is imported the install process will 404: check for the existence of a /ROOT filesystem within the zpool. If this 405: filesystem exists the install will fail with an appropriate error 406: message. To continue the installation the user will need to specify the 407: ’-f’ option to zoneadm(1m) install, which will cause the zones framework 408: to delete the pre-existing /ROOT filesystem within the zpool. (This is 409: done because during install the zones root filesystem will be created 410: under /ROOT. See [07] of more details.) 411: 412: The newly created or imported root zpool will be named after the zone to 413: which it is associated, with the assigned name being "<zonename>_rpool". 414: This zpool will then be mounted on the zones zonepath and then the 415: install process will continue normally[07]. 416: 417: XXX: use altroot at zpool creation or just manually mount zpool? 418: 419: If the user has specified a "zpool" resource, then the zones framework 420: will configure, initialize, and/or import it in a similar manner to a 421: zpool specified by the "rootzpool" resource. The key differences are 422: that the name of the newly created or imported zpool will be 423: "<zonename>_<name>". The specified zpool will also have the zfs "zoned" 424: property set to "on", hence it will not be mounted anywhere in the 425: global zone. 426: 427: XXX: do we need "zpool import -O file-system-property=" to set the 428: zoned property upon import. 429: 430: Once a zone configured with a so-uri is in the installed state, the 431: zones framework needs a mechanism to mark that storage as in use to 432: prevent it from being accessed by multiple hosts simultaneously. The 433: most likely situation where this could happen is via a zoneadm(1m) 434: attach on a remote host. The easiest way to achieve this is to keep the 435: zpools associated with the storage imported at all times, and leverage 436: the existing zpool support for detecting and preventing multi-host 437: access. 438: 439: So whenever a global zone boots and the zones smf service runs, it will 440: attempt to configure and import any shared storage objects associated 441: with installed zones. It will then continue to behave as it does today 442: and boot any installed zones that have the autoboot property set. If 443: any shared storage objects fail to configure or import, then: 444: 445: - the zones associated with the failed storage will be transitioned 446: to the "configured" state. 447: - an error message will be emitted to the zones smf log file. 448: - after booting any remaining installed zones that have autoboot set 449: to true, the zones smf service will enter the "maintenance" state, 450: there by prompting the administrator to look at the zones smf log 451: file. 452: 453: After fixing any problems with shared storage accessibility, the 454: admin should be able to simply re-attach the zone to the system. 455: 456: Currently the zones smf service is dependent upon multi-user-server, so 457: all networking services required for access to shared storage should be 458: properly configured well before we try to import any shared storage 459: associated with zones. 460: 461: On system shutdown, the zones system will NOT export zpools contained 462: within storage object used by the zone. Zpools contained within storage 463: objects assigned to installed zones will only be exported during zone 464: detach. More details about the behaviour of zone detach is provided 465: below. 466: 467: 468: ---------- 469: C.1.iv Zoneadm(1m) attach 470: 471: The zoneadm(1m) attach operation moves a zone from the configured to the 472: installed state. This operation will behave similarly to to a 473: zoneadm(1m) install wrt so-uri management. One key difference between 474: attach and install is that during an attach, any missing storage objects 475: will not be automatically created and instead the attach will fail. 476: Once all the storage objects associated with an attaching zone are 477: configured and accessible, the attach operation will attempt to import 478: and mount the zpools contained within those storage objects. If zfs 479: detects that any zpools are in use by another host the attach will fail. 480: A new ’-f’ option will be added to zoneadm(1m) which will allow the user 481: to force import and mount any zpools which appear to still be in use. 482: (once again, this option mimics the ’-f’ option used by zpool(1m) 483: import.) 484: 485: Currently, when a zone is attached to a system a software inventory 486: check is done to verify that the contents of the zone are in sync with 487: the global zone. If they are then the zone is successfully attached. 488: If the contents are older than what is present in the global zone, then 489: the attach fails unless the user specifies the "-u" update option. If 490: the contents of the zone are newer than what is in the global zone then 491: the attach will fail. Encapsulated zones within zpools changes the 492: behaviour of attach slightly because if the zone was created on a host 493: with newer global zone bits, it’s possible that the zpool/zfs filesystem 494: versions associated with that zpool may be newer than what the host 495: supports. If this is the case we will be unable to import the zone to 496: check it’s software contents. In practice this is not a problem because 497: we know that if the zpool/zfs versions used by an encapsulated zone are 498: later than that supported by the global zone, the software contents of 499: that zone are also guaranteed to be later than what we have in our 500: global zone, so the attach would fail regardless. In the case of older 501: zpool/zfs encapsulated zpool versions, these can be imported without a 502: problem and the version numbers will remain unchanged by the attach 503: operation. 504: 505: 506: ---------- 507: C.1.v Zoneadm(1m) boot 508: 509: The "dataset" property within a "rootzpool" is designed to provide a 510: convenient mechanism for zones to store data within the root zpool, but 511: outside of the /ROOT filesystem (which is subject to snapshotting and 512: cloning during boot environment management operations). To keep things 513: simple, the dataset property can only specify a single path component. 514: (ie, it can not contain any ’/’ characters.) If the user has specified 515: the "dataset" property for a "rootzpool" resource, then when the zone is 516: booted the framework will check for the presence of a zfs filesystem 517: named /dataset/<dataset> within the root zpool. If no such filesystem 518: exists it will be created. If this zfs filesystem is created by the 519: zones framework, it will have the zfs mountpoint property set to none. 520: This filesystem will then be be delegated to the zone as if a 521: zonecfg(1m) "dataset" resource with a name property value set to 522: "<zonename>_rpool/dataset/<dataset>" existed. 523: 524: For example, if an admin wanted to configure a zone contained within a 525: single storage object, but still have a separate dataset where they can 526: manage snapshots and clones of their data independently of their root 527: filesystem and software, then they could create a configuration as 528: follows: 529: 530: ---8<--- 531: zonecfg -z ibis 532: ... 533: add rootzpool 534: set src=iscsi:///alias=ibis zone@0 535: set dataset=oracle_data 536: end 537: ---8<--- 538: 539: once this zone was booted, the zone would have access to the following 540: zfs dataset: ibis_rpool/dataset/oracle_data, and it could mount, 541: snapshot, and clone this dataset. All independently of any zone ROOT 542: dataset snapshots and clones created by system management tools, 543: image-updates, etc. 544: 545: If a zone has any "zpool" resources specified, when the zone is booted 546: those zpools will be delegated to the zone. 547: 548: 549: ---------- 550: C.1.vi Zoneadm(1m) detach 551: 552: The zoneadm(1m) detach operation will be enhanced so that when detaching 553: a zone from a system, any zpools residing in storage objects associated 554: with the detaching zone will be exported. This will allow the 555: administrator to attach the zone on another host without having to 556: specify the force flag (’-f’) to zoneadm(1m) attach. Also, if any 557: static iSCSI storage objects are associated with the zone, the iSCSI 558: initiator configuration for those targets will be removed. (Although 559: iSCSI initiator static discovery will remain enabled.) 560: 561: 562: ---------- 563: C.1.vii Zoneadm(1m) uninstall 564: 565: If a user initiates a zoneadm(1m) uninstall operation, then for each 566: zpool specified via a "rootzpool" or "zpool" resource, if the 567: "zpool-preserve" property is set to false, the zpool contained within 568: the specified storage object will be destroyed. If the "zpool-preserve" 569: property is set to true, then for a "rootzpool" resource only the /ROOT 570: filesystem (and any of it’s descendents) within the root zpool will be 571: destroyed, and then all the zones zpools will be exported. 572: 573: After destroying or exporting the zpool, any associated storage object 574: will be unconfigured as described in the zoneadm(1m) detach section 575: above. 576: 577: 578: ---------- 579: C.1.viii Zoneadm(1m) clone 580: 581: Normally when cloning a zone which lives on a zfs filesystem the zones 582: framework will take a zfs(1m) snapshot of the source zone and then do a 583: zfs(1m) clone operation to create a filesystem for the new zone which is 584: being instantiated. This works well when all the zones on a given 585: system live on local storage in a single zfs filesystem, but this model 586: doesn’t work well for zones with encapsulated roots. First, with 587: encapsulated roots each zone has it’s own zpool, and zfs(1m) does not 588: support cloning across zpools. Second, zfs(1m) snapshotting/cloning 589: within the source zpool and then mounting the resultant filesystem onto 590: the target zones zoneroot would introduce dependencies between zones, 591: complicating things like zone migration. 592: 593: Hence, for cloning operations, if the source zone has an encapsulated 594: root, zoneadm(1m) will not use zfs(1m) snapshot/clone. Currently 595: zoneadm(1m) will fall back to the use of find+cpio to clone zones if it 596: is unable to use zfs(1m) snapshot/clone. We could just fall back to 597: this default behaviour for encapsulated root zones, but find+cpio are 598: not error free and can have problem with large files. So we propose to 599: update zoneadm(1m) clone to detect when both the source and target zones 600: are using separate zfs filesystems, and in that case attempt to use zfs 601: send/recv before falling back to find+cpio. 602: 603: Today, the zoneadm(1m) clone operations ignores any additional storage 604: (specified via the "fs", "device", or "dataset" resources) that may be 605: associated with the zone. Similarly, the clone operation will ignore 606: additional storage associated with any "zpool" resources. 607: 608: Since zoneadm(1m) clone will be enhanced to support cloning between 609: encapsulated root zones and un-encapsulated root zones, zoneadm(1m) 610: clone will be documented as the recommended migration mechanism for 611: users who which to migrate existing zones from one format to another. 612: 613: 614: ---------- 615: C.1.ix Zoneadm(1m) move 616: 617: Since an installed zone with a "rootzpool" resource will have zfs 618: datasets mounted on it’s zonepath, the zoneadm(1m) move subcommand will 619: have to handle unmounting and remounting the "rootzpool" in the process 620: of doing the move operation. 621: 622: 623: ---------- 624: C.1.x Zonecfg(1m) misc changes 625: 626: Currently renaming of a zone is done via zonecfg(1m) by setting the 627: zonename property to the new desired zone name. This operation can be 628: done while the zone is in the "configured" or "installed" state. This 629: presents a problem for installed zones which have a "rootzpool" or 630: "zpool" resource, since these zones zones have associated zpools and the 631: names of these zpools are dependent upon the zone’s name. Hence, 632: zonecfg(1m) will need to be modified to prevent the renaming of 633: installed installed zones which have a "rootzpool" or "zpool" resource. 634: zonecfg(1m). To rename these zones, the administrator will need to 635: detach the zone via zoneadm(1m), rename it via zonecfg(1m), and 636: subsequently re-attach the zone via zoneadm(1m). 637: 638: Another non-obvious impact upon zonecfg(1m) operation is that since 639: "rootzpool" or "zpool" resource are only configured during zone install 640: and attach, it will not be possible to add, remove, or modify 641: "rootzpool" or "zpool" resource for an installed zone. Once again, to 642: change these resources the admin will need to detach and re-attach the 643: zone. 644: 645: 646: ---------- 647: C.2 Storage object uid/gid handling 648: 649: One issue faced by all VTs that support shared storage is dealing with 650: file access permissions of storage objects accessible via NFS. This 651: issue doesn’t affect device based shared storage, or local files and 652: vdisks, since these types of storage are always accessible, regardless 653: of the uid of the access process (as long as the accessing process has 654: the necessary privileges). But when accessing files and vdisk via NFS, 655: the accessing process can not use privileges to circumvent restrictive 656: file access permissions. This issue is also complicated by the fact 657: that by default most NFS server will map all accesses by remote root 658: user to a different uid, usually "nobody". (a process known as "root 659: squashing".) 660: 661: We propose multiple mechanisms for avoiding problems caused by root 662: squashing. 663: 664: If the optional storage object "user" parameter has been specified, then 665: the zones framework will assume the uid of the specified user when 666: attempting to create or access the storage object via nfs. The "user" 667: parameter overrides all the other uid handling mechanism described 668: below. 669: 670: If a file or vdisk storage object exists on an nfs server, the framework 671: will temporarily switch its uid and gid to match the owner and group of 672: the file/vdisk and ensure that the object is readable and writable by 673: it’s owner (updating the permissions if necessary) 674: 675: If a file storage object doesn’t exist on an nfs server, the framework 676: will temporarily switch it’s uid and gid to match the owner and group of 677: the last path element in the storage object that does exist. It will 678: then ensure that the last path component is writable by it’s owner 679: (updating the permissions if necessary) and create the subsequent path 680: elements (with permissions 0600 for files and 0700 for directories). 681: 682: Finally, if a storage object doesn’t exist and the framework attempts to 683: create that object (as described above), only to discover that the 684: subsequently created objects uid doesn’t match the current uid, then the 685: framework will destroy the object and generate an error indicating that 686: root-squashing has been detected. The user can resolve this error 687: multiple ways: by disabling root-squashing, by specifying a "user" 688: parameter, or by changing the ownership of the directory which will 689: contain the storage objects. 690: 691: 692: ---------- 693: C.3 Taskq enhancements 694: 695: The integration of Duckhorn[08] greatly simplifies the management of cpu 696: resources assigned to zone. This management is partially implemented 697: through the use of dynamic resource pools, where zones and their 698: associated cpu resources can both be bound to a pool. 699: 700: Internally, zfs has worker threads associated with each zpool. These 701: are kernel taskq threads which can run on any cpu which has not been 702: explicitly allocated to a cpu set/partition/pool. 703: 704: So today, for any zones living on zfs filesystems, and running in a 705: dedicated cpu pool, any zfs disk processing associated with that zone is 706: not done by the cpus bound to that zones pool. Essentially all the 707: zones zfs processing is done for "free" by the global zone. 708: 709: With the introduction of zpools encapsulated within storage objects, 710: which are themselves associated with specific zones, it would be 711: desirable to have the zpool worker threads bound to the cpus currently 712: allocated to the zone. Currently, zfs uses taskq threads for each 713: zpool, so one way of doing this would be to introduce a mechanism that 714: allows for the binding of taskqs to pools. 715: 716: Hence we propose the following new interfaces: 717: zfs_poolbind(char *, poolid_t); 718: taskq_poolbind(taskq_t, poolid_t); 719: 720: When a zone, which is bound to a pool, is booted, the zones framework 721: will call zfs_poolbind() for each zpool associated with an encapsulated 722: storage object bound to the zone being booted. 723: 724: Zfs will in turn use the new taskq pool binding interfaces to bind all 725: it’s taskqs to the specified pools. This mapping is transient and zfs 726: will not record or persist this binding in any way. 727: 728: The taskq implementation will be enhanced to allow for binding worker 729: threads to a specific pool. If taskqs threads are created for a taskq 730: which is bound to a specific pool, those new thread will also inherit 731: the same pool bindings. The taskq to pool binding will remain in effect 732: until the taskq is explicitly rebound or the pool to which it is bound 733: is destroyed. 734: 735: 736: ---------- 737: C.4 Zfs enhancements 738: 739: In addition to the zfs_poolbind() interface proposed above. The 740: zpool(1m) "import" command will need to be enhanced. Currently the 741: zpool(1m) import by default scans all storage devices on the system 742: looking for pools to import. The caller can also use the ’-d’ option to 743: specify a directory within which the zpool(1m) command will scan for 744: zpools that may be imported. This scanning involves sampling many 745: objects. When dealing with zpools encapsulated in storage objects, this 746: scanning is unnecessary since we already know the path to the objects 747: which contains the zpool. Hence, the ’-d’ option will be enhanced to 748: allow for the specification of a file or device. The user will also be 749: able to specify this option multiple times, in case the zpool spans 750: multiple objects. 751: 752: 753: ---------- 754: C.5 Lofi and lofiadm(1m) enhancements 755: 756: Currently, there is no way for a global zone to access the contents of a 757: vdisk. Vdisk support was first introduced in VirtualBox. xVM then 758: adopted the VirtualBox code for vdisk support. With both technologies, 759: the only way to access the contents of a vdisk is to export it to a VM. 760: 761: To allow zones to use vdisk devices we propose to leverage the code 762: introduced by by xVM by incorporating it into lofi. This will allow any 763: solaris system to access the contents of vdisk devices. The interface 764: changes to lofi to allow for this are fairly straightforward. 765: 766: A new ’-l’ option will be added to the lofiadm(1m) "-a" device creation 767: mode. The ’-l’ option will indicate to lofi that the new device should 768: have a label associated with it. Normally lofi device are named 769: /dev/lofi/<I> and /dev/rlofi/<I>, where <I> is the lofi device number. 770: When a disk device has a label associated with it, it exports many 771: device nodes with different names. Therefore lofi will need to be 772: enhanced to support these new device names, which multiple nodes 773: per device. These new names will be: 774: 775: /dev/lofi/dsk<I>/p<j> - block device partitions 776: /dev/lofi/dsk<I>/s<j> - block device slices 777: /dev/rlofi/dsk<I>/p<j> - char device partitions 778: /dev/rlofi/dsk<I>/s<j> - char device slices 779: 780: Lofi will also be enhanced to support accessing vdisks in addition to 781: normal files. Accessing a vdisk via lofi will be no different from 782: accessing a disk encapsulated within a file as described above with the 783: -l option. The lofi ’-a’ option will be enhanced to allow for the 784: automatic detection of vdisk targets on the lofi command line. Since 785: all vdisks represent actual disks they all contain a partition/label 786: data, hence when if a vdisk is detected on the command line, the ’-l’ 787: flag is automatically implied. The vdisk formats that will be supported 788: by lofi are whatever vdisk formats happen to be supported by xVM at the 789: time of integration. Since the implementation between lofi and xVM will 790: be shared, as new vdisk format support is added to xVM, it should be 791: immediately supportable via lofi as well. In many cases, vdisk formats 792: may provide their own management features such as snapshotting, 793: compression, encryption, etc. As such, the lofi vdisk support exists 794: purely to access the contents of vdisks. Hence, vdisk based lofi 795: devices will not support other lofi options such as encryption (’-c’) 796: and compression (’-C’ / ’-U’). 797: 798: The current xVM implementation for accessing vdisks involves two drivers 799: and a userland utility. A "frontend" driver runs inside a VM and it 800: exports normal solaris disk interface. It takes IO requests to these 801: disks and transmits them, via a ring buffer, to the "backend" driver 802: running in the global zone. The backend driver then maps these ring 803: requests into a dedicated vdisk process (there is one such process for 804: every vdisk), and this process translates these ring requests into 805: access to a vdisk of the requested format. Given all this existing xVM 806: functionality, the most straightforward way to support vdisk from within 807: lofi would be to leverage the xVM implementation. This will involve 808: re-factoring the existing xVM code, thereby allowing lofi to utilise 809: the "frontend" code which translates strategy io requests into ring 810: buffer requests, and also the "backend" code which exports the ring 811: buffer to userland. The unchanged xVM userland vdisk utility can then 812: be used to map ring buffer requests to the actual vdisk storage. 813: Currently this utility is only available on x86, but since lofi is a 814: cross-platform utility, this proposal will require the delivery of this 815: utility on both sparc and x86. This utility is currently delivered in 816: an xVM private directory, /usr/lib/xen/bin/vdisk. Given that lofi is a 817: more general and cross platform utility as compared to xVM, and also 818: given that that we don’t expect users to access the vdisk management 819: utilities directly, we propose to move the vdisk application to 820: /usr/lib/lofi/bin/vdisk. 821: 822: For RAS purposes, we will need to ensure that this vdisk utility is 823: always running. Hence we will introduce a new lofi smf service 824: svc:/system/lofi:default, which will start a new /usr/lib/lofi/lofid 825: daemon, which will manage the starting, stopping, monitoring, and 826: possible re-start of the vdisk utility. Re-starts of vdisk utility 827: should be transparent (aside from a short performance hiccup) to any 828: zones accessing those vdisks. By default this service will be disabled. 829: If a lofi vdisk device is created, this service will be temporarily 830: enabled. When the last vdisk based lofi device is destroyed, this 831: service will disable itself. 832: 833: XXX: what to do about disk geometry assignment? sigh. 834: 835: Here’s some examples of how this lofi functionality could be used 836: (outside of the zone framework). 837: 838: If an admin wanted to access a disk embedded within a plain file then 839: they could run the following command: 840: lofiadm -a -l /export/xvm/vm1.disk 841: 842: If an admin wanted to access a vdisk (which always implicitly has a disk 843: embedded within it) they could run the following command: 844: lofiadm -a /export/xvm/vm1.vmdk 845: 846: In both the examples above, if there were no previously configured 847: lofi devices on the system, the following new lofi devices would 848: be created: 849: /dev/lofi/dsk0/p# - for # == 0 - 4 850: /dev/lofi/dsk0/s# - for # == 0 - 15 851: /dev/rlofi/dsk0/p# - for # == 0 - 4 852: /dev/rlofi/dsk0/s# - for # == 0 - 15 853: 854: By default, format(1m) will not list these devices in it’s output. But 855: users will be able to treat these devices like regular disks and pass 856: their names to utilities like fdisk(1m), format(1m), prtvtoc(1m), 857: fmthard(1m), zpool(1m), etc. 858: 859: ---------- 860: C.6 Performance considerations 861: 862: As previously mentioned, this proposal primarily simplifies the process 863: of configuring zones on shared storage. In most cases these proposed 864: configurations can be created today, but no one has actually verified 865: that these configurations perform acceptably. Hence, in conjunction 866: with providing functionality to simplify the setup of these configs, 867: we also need to be quantifying their performance to make sure that 868: none of the configurations suffer from gross performance problems. 869: 870: The most straightforward configurations, with the least possibilities 871: for poor performance, are ones using local devices, fibre channel luns, 872: and iSCSI luns. These configuration should perform identically to the 873: configurations where the global zone uses these objects to host zfs 874: filesystems without zones. Additionally, the performance of these 875: configurations will mostly be dependent upon the hardware associated 876: with the storage devices. Hence the performance of these configuration 877: is for the most part uninteresting and performance analysis of these 878: configuration can by skipped. 879: 880: Looking at the performance of storage objects which are local files or 881: nfs files is more interesting. In these cases the zpool that hosts the 882: zone will be accessing it’s storage via the zpool vdev_file vdev_ops_t 883: interface. Currently, this interface doesn’t receive as much use and 884: performance testing as some of the other zpool vdev_ops_t interfaces. 885: Hence it will worthwhile to measure the performance of a zpool backed by 886: a file within another zfs filesystem. Likewise we will want to measure 887: the performance of a zpool backed by a file on an NFS filesystem. 888: Finally, we should compare these two performance points to a zone which 889: is not encapsulated within a zpool, but is instead installed directly on 890: a local zfs filesystem. (These comparisons are not really that 891: interesting when dealing with block device based storage objects.) We 892: will also want to determine if there are any specific NFS mount options 893: that should be used which could affect performance, for example, should 894: "forcedirectio" be enabled? 895: 896: Currently, while it is very common to deploy large numbers of zfs 897: filesystems, systems with large numbers of zpools are not very common. 898: The solution proposed in this project will likely result in an increase 899: of zpools on systems hosting zones. Hence, we should evaluate the 900: impact of an increasing number of zpools on performance scalability. 901: This could be done by comparing the io performance drop-off of an 902: increasing number of zones hosted multiple zfs filesystems in a single 903: zpool vs zones hosted in separate zpools. 904: 905: Finally, it will be important to do performance measurements for vdisk 906: configurations. These configurations are similar to the local file or 907: nfs configurations, but they will be utilising the vdev_disk backend and 908: they will have an additional layer of indirection through lofi. 909: 910: XXX: impact of multiple zpools on arc and l2 arc? talk to mark maybee. 911: 912: 913: ---------- 914: C.7 Phased delivery 915: 916: Customers have been asking for a simple mechanisms to allow hosting of 917: zones on NFS since the introduction of zones. Hence we’d like to get 918: this functionality into the hands of customers as quickly as possible. 919: Also, the approach taken by this proposal to supporting zones on shared 920: storage is different from what was originally anticipated, hence we’d 921: like to get practical experience with this approach at customer sites 922: asap to determine if there are situations where this approach may not 923: meet their requires. To accelerate the delivery of the previously 924: proposed features, we plan to deliver them in three phases: 925: 926: 927: I - Basic Zone encapsulation support 928: 929: This will involve the introduction of the new "rootzpool" and "zpool" 930: resources and support the "file" and "nfs" type storage objects. It 931: will require implementation of all the proposed zone, zfs, and taskq 932: changes. This phase will not require any lofi changes. 933: 934: 935: II - Zone encapsulation on fc and iSCSI storage. 936: 937: This will provide enhanced so-uri support for fibre channel and iSCSI 938: type storage objects. 939: 940: 941: III - Zone encapsulation on vdisk storage. 942: 943: This will involve implementation of the proposed lofi enhancements and 944: enhancement support for vdisk type storage objects. 945: 946: 947: ---------- 948: C.8 Future work 949: 950: In addition to the proposed work above, there are future enhancements 951: that could be made which would extend the functionality proposed above. 952: These are included here because the features proposed above have been 953: designed with this extensibility in mind. 954: 955: One simple enhancement would be the addition a new "rootzpool/zpool" 956: resource property called "zpool-auto-upgrade". If set to true, whenever 957: a zpool is imported by the zones framework (at install, attach, or boot) 958: the zpool and zfs filesystems would be upgraded to the latest version 959: supported by the global zone. Aside from helping to ensure that zones 960: are using the latest and greatest zpool/zfs features, this feature would 961: help ensure that all the encapsulated zones on a system are running on 962: the same zpool/zfs versions. This is important because zfs send and 963: receive are not guaranteed to be compatible across zpool/zfs versions. 964: So by ensure that all zones are running the latest zpool/zfs versions, 965: we increase the chances of being able to use zfs send/recv for zone 966: cloning. 967: 968: Another possible enhancement to the "rootzpool/zpool" resources would be 969: to allow the specification of multiple so-uris to support more complex 970: root zpool configurations. 971: 972: With the introduction of vdisk support, we will have the ability to 973: access VMs created by other VTs. Using this functionality it would be 974: straightforward to enhance the existing zoneadm(1m) attach p2v 975: functionality to be able to do v2v, where the source system image for 976: installing a zone could be a vdisk created by another VT. 977: 978: Finally, a new "storage" zonecfg(1m) resource could be added to allow 979: for the addition of arbitrary storage objects to zones. This "storage" 980: resource would have the similar properties as a "zpool" resource, along 981: with a few additional properties: 982: 983: storage resource 984: src resource property 985: install-size resource property 986: name resource property 987: locking-required resource property 988: 989: The zones framework would ensure that the specified storage objects were 990: accessible from the global zone, and then it would grant the zone access 991: to the raw disk devices. But global zone disk device names can be 992: different on different host, so if global zone device names were used 993: within the non-global zone, this would negatively impact zone migration 994: since the software within the zone would have to be updated to deal with 995: potentially new device names. To avoid this problems and facilitate 996: zone migration, the disk devices would be mapped into the non-global 997: zone with different names which would be global zone independent. 998: Regardless of the global zone disk device name, from within the 999: non-global zone, the devices would be named: 1000: 1001: /dev/storage/<name>/p<j> 1002: /dev/storage/<name>/s<j> 1003: 1004: By basing the names of the device as seen from within the zone on the 1005: "name" specified in the zonecfg(1m) (instead of the raw device names 1006: from the global zone), we can guarantee that these device names will not 1007: change when migrating the zones between hosts. 1008: 1009: One difficulty with allowing the specification of storage objects which 1010: don’t by default contain a zpools is that we can no longer use zfs to 1011: ensure that multiple entities are not accessing these objects at the 1012: same time. Hence other mechanisms will need to be used. In the case of 1013: fibre channel and iSCSI, we should be able to use SCSI reservations. In 1014: the case of file paths we should be able to use standard file locking. 1015: In the case of vdisks, some vdisks support meta-data which can be 1016: utilised to prevent concurrent access, and if a vdisk format does not 1017: allow for this then we will need to fall back to file locking. If for 1018: some reason we are unable to do in use detection for a specific storage 1019: object, then we could alert the user and refuse to use that storage 1020: object unless the user set the optional "locking-required" property to 1021: false. 1022: 1023: 1024: ---------- 1025: D. INTERFACES 1026: 1027: Zonecfg(1m): 1028: rootzpool committed, resource 1029: src committed, resource property 1030: install-size committed, resource property 1031: zpool-preserve committed, resource property 1032: dataset committed, resource property 1033: 1034: zpool resource 1035: src resource property 1036: install-size resource property 1037: zpool-preserve resource property 1038: name resource property 1039: 1040: Zoneadm(1m): 1041: install -f committed, optional flag 1042: attach -f committed, optional flag 1043: 1044: Zones misc: 1045: /var/zones/nfsmount/<host>/<nfs-share-name> 1046: project private, nfs mount point 1047: 1048: Taskq pool binding: 1049: taskq_poolbind(taskq_t, poolid_t) consolidation private 1050: 1051: Zfs pool binding: 1052: zfs_poolbind(char *, poolid_t) consolidation private 1053: 1054: Lofiadm(1m): 1055: -a -l committed, optional flag 1056: -a -v <vdisk-format> committed, optional flag 1057: 1058: Lofi misc: 1059: svc:/system/lofi:default project private 1060: /lib/lofi/lofid project private 1061: /lib/lofi/vdisk consolidation private /w 1062: contract for xVM 1063: 1064: 1065: ---------- 1066: E. ABBREVIATIONS 1067: 1068: VDI - VirtualBox Virtual HDD 1069: VHD - Microsoft Virtual Hard Disk 1070: VM - Virtual Machine 1071: VMDK - VMWare Virtual Machine Disk 1072: VT - Virtualization Technology 1073: 1074: 1075: ---------- 1076: F. FOOTNOTES / REFERENCES 1077: 1078: -- 1079: 00 - Blogs and emails describing custom configurations used to host 1080: zones on shared storage: 1081: 1082: June 2005 1083: Containers on NFS? 1084: http://blogs.sun.com/jph/entry/containers_on_nfs 1085: 1086: February 2008 1087: The quick & dirty guide to zones on iSCSI LUNs 1088: http://opensolaris.org/jive/thread.jspa?messageID=226951 1089: 1090: April 2008 1091: ZoiT: Solaris Zones on iSCSI Targets (aka NAC: Network-Attached Containers) 1092: http://blogs.sun.com/JeffV/entry/zoit_solaris_zones_on_iscsi 1093: 1094: -- 1095: 01 - For the purposes of this document, the term storage object refers 1096: to any device or file which can be used to store data. These "objects" 1097: can take the form of files on a local filesystem, files on a remote 1098: filesystem accessible via NFS, CIFS, etc, local disk devices, FC disk 1099: devices, iSCSI target devices, 1100: 1101: -- 1102: 02 - Each VT has a different name for the zones concept of the "global 1103: zone". With LDOMs we have the "control domain", with xVM we have 1104: "dom0", with VirtualBox we have the "host domain". For the purposes of 1105: this document the "global zone" refers to the OS entity which allocates 1106: resources to and has control over the state of VMs. For simplicity, 1107: this document will always just refer to the "global zone" in place of 1108: all these other terms. 1109: 1110: -- 1111: 03 - Complete so-uri ABNF syntax definition: 1112: 1113: so-uri = path-uri 1114: / nfs-uri 1115: / vpath-uri 1116: / vnfs-uri 1117: / fc-uri 1118: / iscsi-uri 1119: 1120: path-uri = "path://" file-absolute 1121: file-absolute = "/" *( segment "/" ) segment-nz 1122: ; Since file-absolute is always the last component 1123: ; in a uri reserved characters do not need to be 1124: ; percent encoding. To simplify path management 1125: ; we will also not permit ’/./’ or ’/../’ segments. 1126: 1127: nfs-uri = "nfs://" hostport "/" file-absolute 1128: ; Defined in RFC 2224 [05] 1129: 1130: hostport = host [ ":" port ] 1131: 1132: vpath-uri = "vpath://" file-absolute 1133: vnfs-uri = "vnfs://" host [ ":" port ] "/" file-absolute 1134: ; same syntax as nfs-uri 1135: 1136: fc-uri = "fc:/// wwn [ "@" lun ] 1137: wwn = 1000 12HEXDIG 1138: / 2 15HEXDIG 1139: / 50 14HEXDIG 1140: / 60 14HEXDIG 1141: ; see http://en.wikipedia.org/wiki/World_Wide_Name 1142: 1143: iscsi-uri = "iscsi:///" iscsi-alias 1144: / "iscsi:///" iscsi-target 1145: / "iscsi://" iscsi-static 1146: 1147: iscsi-alias = "alias=" iscsi-alias-name [ "@" lun ] 1148: iscsi-alias-name = 1*255pchar 1149: ; Defined in RFC 3720 [06] 1150: ; any "@" chars must be percent encoded 1151: 1152: iscsi-target = "target=" iscsi-target-iqn [ "@" lun ] 1153: / "target=" iscsi-target-eui [ "@" lun ] 1154: iscsi-target-iqn = "iqn." iqn-date "." reg-name [ ":" 1*pchar ] 1155: ; Defined in RFC 3720 [06] 1156: ; any "@" chars must be percent encoded 1157: iqn-date = 4DIGIT "-" "0" %x31-39 ; "XXXX-01" - "XXXX-09" 1158: / 4DIGIT "-" "1" %x30-32 ; "XXXX-10" - "XXXX-12" 1159: iscsi-target-eui = XXX 1160: ; Defined in RFC 3720 [06] 1161: ; any "@" chars must be percent encoded 1162: 1163: iscsi-static = hostport "/" [ "tpgt=" iscsi-tpgt "/" ] iscsi-target 1164: 1165: iscsi-tpgt = DIGIT ; 0-9 1166: / %x31-39 1*3DIGIT ; 10-9999 1167: / %x31-35 4DIGIT ; 10000-59999 1168: / "6" %x30-34 3DIGIT ; 60000-64999 1169: / "65" %x30-34 2DIGIT ; 65000-65499 1170: / "655" %x30-32 1DIGIT ; 65500-65529 1171: / "6553" %x30-35 ; 65530-65535 1172: 1173: lun = DIGIT ; 0-9 1174: / %x31-39 1*3DIGIT ; 10-9999 1175: / "1" %x30-35 3DIGIT ; 10000-15999 1176: / "16" %x30-32 2DIGIT ; 16000-16299 1177: / "163" %x30-37 1DIGIT ; 16300-16379 1178: / "1638" %x30-33 ; 16380-16383 1179: 1180: DIGIT = Defined in RFC 3986 [04] 1181: HEXDIG = Defined in RFC 3986 [04] 1182: segment-nz = Defined in RFC 3986 [04] 1183: segment = Defined in RFC 3986 [04] 1184: host = Defined in RFC 3986 [04] 1185: port = Defined in RFC 3986 [04] 1186: 1187: -- 1188: 04 - RFC 3986: Uniform Resource Identifier (URI): Generic Syntax 1189: http://www.ietf.org/rfc/rfc3986.txt 1190: http://www.websitedev.de/temp/rfc3986-check.html.gz 1191: 1192: -- 1193: 05 - RFC 2224 NFS URL Scheme 1194: http://www.ietf.org/rfc/rfc2224.txt 1195: 1196: -- 1197: 06 - RFC 3720: Internet Small Computer Systems Interface (iSCSI) 1198: 1199: -- 1200: 07 - Zones/SNAP Design - 8/25/2008 1201: http://www.opensolaris.org/jive/thread.jspa?messageID=272726񂥖 1202: 1203: -- 1204: 08 - PSARC/2006/496 Improved Zones/RM Integration 1205: http://arc.opensolaris.org/caselog/PSARC/2006/496 1206: 1207: 1208: ------------------------------------------------------------------------------- 1209: }}}
Collectives
Community Group
Academic and Research
Accessibility
Advocacy
Appliances
Approachability
Architecture Process and Tools
BrandZ
Chinese Users
Community Advisory Board
DTrace
Databases
Desktop
Device Drivers
Distribution
Documentation
Emerging Platforms
Fault Management
Games on OpenSolaris
HA Clusters
HPC Developer
Installation and Packaging
Internationalization and Localization
Laptop
Logical Domains
Modular Debugger (MDB)
NFS
Networking
OS/Net (ON)
Observability
OpenSolaris Governing Board
OpenSolaris Printing
Performance
Power Management
PowerPC
Security
Service Management Facility (smf(5))
Software Porters
Solaris Volume Manager
Storage
Systems Administration Community Group
Testing
Tools Home
Unix File Systems (UFS)
Website
X Window System
Xen
ZFS
Zones
Project
ADSL Modem Enhancement
ARC Process Definition
ARM Platform Port
BIND Update
Bluetooth Stack & Drivers
Brocade FC HBA - Initiator
Brocade FC HBA - Target
Brussels - unified network link configuration
CIFS Server
CIFS client for Solaris
CMT
COMSTAR: Common Multiprotocol SCSI Target
CPU Observability
Caiman, Solaris Install Revisited
Celeste
Chime Visualization Tool for DTrace
Clearview: Network Interface Coherence
Cluster Agent: Informix Dynamic Server
Cluster Agent: OpenSolaris Container
Cluster Agent: OpenSolaris xVM
Cluster Agent: Oracle E-Business Suite
Cluster Agent: Samba
Cluster Agent: Tomcat
Cluster agent: PostgreSQL
Coarse Data Flow Parallelism
Colorado: Open HA Cluster on OpenSolaris
Command Assistant
Common Array Manager
Companion - /opt/sfw: Free and Open Source software
Content
Contest
Credentials Process Groups
Crossbow: Network Virtualization and Resource Control
Crypto KMS Agent Toolkit
Cryptographic Framework
DTrace Guide
Data Migration Manager
Data Tethers
Deutsches Portal
Device Detection Tool
Device Driver Utility
Device Manager
Device Mapper
Direct Rendering Infrastructure & 3D drivers
Duckwater: Simplified name services management
Easy Tools
Emancipation
Emulex Advanced Ethernet Device Driver
Emulex Fibre Channel Device Driver
Enable/Enhance Solaris support for Intel Platform
Enhance the support of USB webcams
Enhanced SMF Profiles
Enhancements for AMD-based Platforms
Erlang DTrace Integration
Ethernet bridge module for Solaris
Evaluate Conary
Events Registry
Ext3 file system support
Facilitation
Fibre Channel over Ethernet
Fine Grained Access Policy (FGAP)
Fingerprint Authentication
Flexible Mandatory Access Control
Forensic Tools
Fully Open X Project
Fuse on Solaris
Generic Machine Check Architecture Improvements
Generic RAID Configuration Utility
Google SOC
HA-JBoss
HA-MySQL
HPC Stack
Hadoop Live CD
Hitachi
HoneyComb Fixed Content Storage
Hotplug Framework
IP Datapath Refactoring
IP over Infiniband
IPsec Tunnel Reform
Image Packaging System
Immutable Service Containers
Improved Performance MIB
Indiana
Innovation Awards
Input Method
Intel Graphics
Internet Key Exchange, version 2 (IKEv2)
Interrupt Resource Management
JKstat - a java binding for libkstat
JeOS - Just enough Operating System
Journaled File System (JFS)
K Desktop Environment
Kerberos
Kernel SSL Enhancements
Kernel Sockets
Key Management Framework
Korn Shell 93 integration/migration project
LDoms Manager
Labeled IPsec
LatencyTOP
Layer 2 Filtering
Lending
Link Layer Discovery
Live Media: Technologies for distributions running from CD and other media
Locale Data
MIPS Platform Port
MRSL.NONsharedDevice
Mega_sas
Mexico
MilaX minimal Live Distribution
Mozilla DTrace
Multi-lingual Glossary
Multi-pathing software (MPxIO)
Multiple DOI
Multiple disk sector size support
Muskoka: An open repository for OpenSolaris technical content
NFS RDMA transport update and performance analysis
NFS Server in non-Global Zones
NFS version 4.1 pNFS
NFSv4 namespace extensions
NPort ID Virtualization (NPIV)
NUMA
Navigator
Nemo: A Framework for High-Performance Networking
Network Auto-Magic
Network Data Management Protocol
Network MIBs
Network Performance Projects
Network Power Management
Network Storage
Network Time Protocol (NTP)
Nevada Globalization
New Design of 4over6 Mechanism Based on OpenSolaris
Nightingale: Port Songbird to OpenSolaris
OHACGE Script Based Plug-in
ON/Nevada (ONNV) Project
Object Storage Device (OSD) support for Solaris
Open Development Infrastructure
Open HA Cluster Utilities
Open Sound System
OpenGrok
OpenPegasus CIM Server
OpenRTI
OpenSolaris Busybox
OpenSolaris Desktop
OpenSolaris Hispano
OpenSolaris Security Audit
OpenSolaris support for the QEMU processor emulator: host and guest
PEF: Packet Event Framework
Performance Wrappers
Pkgfactory
Polski Portal
Portail Francophone
Portal Brasileiro de OpenSolaris
Portals
Power Management Usability Interfaces
Presto: Automatic Printing Configuration
Printable Many Page Solaris Manuals
Promise SuperTrak RAID HBA Driver
QLogic Converged Network Adapter GLDv3 NIC Driver
Quagga Routing Protocol Suite Integration
RBridge (IETF TRILL) support
RDMA Offload Framework
Release Notes
Reno: Login Process Enhancements for Interop
Resource Management
SAM/QFS
SCM Migration Project
SCSI RDMA Protocol
SDcard Drivers
SFW
SM-HBA Based SAS HBA Management
SMF Documentation
SVR4 Packaging
Sensor Abstraction Layer
Session Initiation Protocol
Shell: bourne shell, korn shell, C shell, etc.
Sierra: Intel WiFi Chipsets Support
Simple Panels
Solaris PowerPC Port
Solaris iSCSI Target
SourceJuicer
Sparks: name service switch/nscd enhancements
Squashfs
Star integration/migration project
Starfish
Starter Kit
Storage Power Management
Sun Security Toolkit
Sun StorageTek Availability Suite
Support for OpenFabrics User Verbs / API on OpenSolaris OS
Support gcc4/GCCfss in Solaris
Suspend/Resume
Systemz
TIPC
Tamarack: Removable Media Enhancements in Solaris
Test Development
Tickless Kernel Architecture
Trademarks
Trusted Platform Module support
Trusted networking interface policy database for Trusted Extensions
Use Case
Userland
VRRP: Virtual Router Redundancy Protocol Implementation
VSCAN service
Validated Execution Project
Virtual Console
Virtual Network Machines
Visual Panels
Visualization for HPC
Volo
Web Stack
Website
Winchester: Schema mapping and ID mapping for AD Interoperability
Wireless USB Support
Wireless Wide Area Network
X Consolidation
X Keyboard Layout
Xen Gate
Xfce: A lightweight desktop environment
ZFS Boot and Install
ZFS on disk encryption support
Zone Manager
Zone Statistics
gcore
iSCSI Extensions for Remote DMA (iSER)
iSNS Server
libMicro - portable microbenchmarks
lofi compression and cryptography support
lx64 brand
s10brand
x86 Generic FMA Topology Enumerator
Český portál
Русский портал
البوابة العربية
भारतीय पोर्टल
中国门户
日本ポータル
한국 포탈
User Group
Adelaide
Argentina
Arizona
Atlanta OpenSolaris Users Group
Baltimore-Washington
Bangalore
Bangkok
Bangladesh
Beijing
Berlin
Bhimavaram
Bloomington
Brasilia
Bélem
Capital Region
Cardiff
Caribbean OSUG
Ceara
Charlotte
Chennai
Chihuahua
Chile
Cleveland
Colombia
Columbus
Connecticut
Cracow
Czech
Dallas/Ft. Worth
Danish
Delaware
Edinburgh
Egypt
Finland
Florida
Frankfurt
Front Range
Great Lakes
Greece
Hangzhou
Hawaii
Hong Kong
Houston
Hyderabad
Indonesia
Inicio
Irish
Israel
Istanbul
Italian
Jinan
Jogja
Kabul
Kansas City
Latvia
London
Madurai
Manchester
Mato Grosso
Melbourne
Minas Gerais
Minnesota
Montreal
Moscow
Mumbai
Munich
NEA
Netherlands
New England
New York City OpenSolaris User Group
New Zealand
Noroeste
Nuremberg-Erlangen
Oklahoma City
Osnabrück
Peru
Philadelphia
Piaski
Pittsburgh
Porto Alegre
Puget Sound
Pune
Queensland
Research Triangle Park
Rio de Janeiro
Romania
Russia
San Antonio
San Diego
San Francisco
Scottish
Serbia
Shenzhen
Silicon Valley
Singapore
Slovak
South African
Southern Connecticut
St. Louis
Stavanger
Sweden
Switzerland
Sydney
Szczecin
São Paulo
Taiwan OpenSolaris User Group
Tecum
Thames Valley
Tokyo
Toronto
Trondheim
Tulsa
Turkey
Ufa OSUG
Ukraine OpenSolaris User Group
University of Melbourne
Vale do Paraíba
Vancouver
Venezuela - Pagina de incio
Vladivostok
Welsh - Cymru
Wisconsin
Xi'an
Community Group zones Pages
BrandZ
BrandZ Bugfixes
Contribute Code to BrandZ
BrandZ Design Doc
BrandZ Downloads
BrandZ Installation
BrandZ Impact on ON Development
BrandZ Project List
BrandZ/SCLA FAQ
Linux Applications
Zones and Containers FAQ
Flash Archives & Zones
Files
Zones Roadmap
Solaris10-Branded Zone Developer Guide
Zones Project Documents
Enhanced Resource Management Project
zones on shared storage proposal


Function parseMangledSwiftSymbol(_:isType:)
public func parseMangledSwiftSymbol(_ mangled: String, isType: Bool = false) throws -> SwiftSymbol
This is likely to be the primary entry point to this file. Pass a string containing a Swift mangled symbol or type, get a parsed SwiftSymbol structure which can then be directly examined or printed.
Parameters
| Name | Type | Description |
|---|---|---|
| mangled | String |
the string to be parsed ("isType` is false, the string should start with a Swift Symbol prefix, _T, _$S or $S). |
| isType | Bool |
if true, no prefix is parsed and, on completion, the first item on the parse stack is returned. |
Throws
a SwiftSymbolParseError error that contains parse position when the error occurred.
Returns
the successfully parsed result


{% block error_detail %}
You must authenticate using the Globus Auth identity with uuid = {{message}}


---
---
{% include head.html %}
{% include head/custom.html %}
{% include_cached browser-upgrade.html %}
{% include_cached masthead.html %}
{% include page__404.html %}
{% if site.search == true %}
{% include_cached search/search_form.html %}
{% endif %}
{% include scripts.html %}


Trilogy
The sexy red couches and the wannabe models have been around so long at Trilogy that it’s hard to tell whose legs we spotted first on opening night at this sexy Mumbai club in Juhu.Needless to say, Trilogy affords more than a sea view.Tryst
A night at Tryst is like attending a spectacular light show, where more than a million color-changing LEDs poke out of almost every corner and bright green absinthe potions swirl around in glasses.If you’ve got the bucks, you’re in the spotlight.Hype
Hype is unabashedly billed as "the country’s first luxury nightclub," claiming a ginormous area of "clubbing bliss" in the heart of South Mumbai."It's the most exclusive place in Mumbai -- the clientele is exclusive, the decor is exclusive, even the alcohol is exclusive," the visibly proud DJ says.Alibii
Alibii is a pulsating nightclub and bar based in South Mumbai inspired by the 7 sins of Greed, Pride, Indulge, Chill, Vanity, Lust and Passion. Alibii is all about organized chaos, with different finishes next to each other creating the illusion of different spaces within a space. All the light fixtures are dynamic and luminous giving a strong character to the interior.Blue Frog
Every Mumbai musician worth their guitar would agree: Blue Frog revolutionized the live music scene in this city, with its egg-shaped seating booths, state of the art acoustics, high-tech LED screens, great burgers and steaks, overpriced drinks and we-don’t-care-what-you-think arrogance.


Born on August 6, 1809, in Somersby, Lincolnshire, England, Alfred Lord Tennyson is one of the most well-loved Victorian poets. Tennyson, the fourth of twelve children, showed an early talent for writing. At the age of twelve he wrote a 6,000-line epic poem. His father, the Reverend George Tennyson, tutored his sons in classical and modern languages. In the 1820s, however, Tennyson's father began to suffer frequent mental breakdowns that were exacerbated by alcoholism. One of Tennyson's brothers had violent quarrels with his father, a second was later confined to an insane asylum, and another became an opium addict.
Tennyson escaped home in 1827 to attend Trinity College, Cambridge. In that same year, he and his brother Charles published Poems by Two Brothers. Although the poems in the book were mostly juvenilia, they attracted the attention of the "Apostles," an undergraduate literary club led by Arthur Hallam. The "Apostles" provided Tennyson, who was tremendously shy, with much needed friendship and confidence as a poet. Hallam and Tennyson became the best of friends; they toured Europe together in 1830 and again in 1832. Hallam's sudden death in 1833 greatly affected the young poet. The long elegy In Memoriam and many of Tennyson's other poems are tributes to Hallam.
In 1830, Tennyson published Poems, Chiefly Lyrical and in 1832 he published a second volume entitled simply Poems. Some reviewers condemned these books as "affected" and "obscure." Tennyson, stung by the reviews, would not publish another book for nine years. In 1836, he became engaged to Emily Sellwood. When he lost his inheritance on a bad investment in 1840, Sellwood's family called off the engagement. In 1842, however, Tennyson's Poems in two volumes was a tremendous critical and popular success. In 1850, with the publication of In Memoriam, Tennyson became one of Britain's most popular poets. He was selected Poet Laureate in succession to Wordsworth. In that same year, he married Emily Sellwood. They had two sons, Hallam and Lionel.
At the age of 41, Tennyson had established himself as the most popular poet of the Victorian era. The money from his poetry (at times exceeding 10,000 pounds per year) allowed him to purchase a house in the country and to write in relative seclusion. His appearance—a large and bearded man, he regularly wore a cloak and a broad brimmed hat—enhanced his notoriety. He read his poetry with a booming voice, often compared to that of Dylan Thomas. In 1859, Tennyson published the first poems of Idylls of the Kings, which sold more than 10,000 copies in one month. In 1884, he accepted a peerage, becoming Alfred Lord Tennyson. Tennyson died on October 6, 1892, and was buried in Westminster Abbey.
Source goes to the Academy of American poets.org


| rasterVis-package | Visualization Methods for Raster Data |
| BTCTheme | Themes for 'raster' with 'lattice'. |
| BuRdTheme | Themes for 'raster' with 'lattice'. |
| bwplot | Box and whisker plots of Raster objects. |
| bwplot-method | Box and whisker plots of Raster objects. |
| bwTheme | Themes for 'raster' with 'lattice'. |
| chooseRegion | Interaction with trellis objects. |
| contourplot | Level and contour plots of Raster objects. |
| contourplot-method | Level and contour plots of Raster objects. |
| densityplot | Density plots for Raster objects. |
| densityplot-method | Density plots for Raster objects. |
| gplot | Use ggplot to plot a Raster* or a SpatRaster object. |
| gplot-method | Use ggplot to plot a Raster* or a SpatRaster object. |
| GrTheme | Themes for 'raster' with 'lattice'. |
| hexbinplot | Formula methods |
| hexbinplot-method | Formula methods |
| histogram | Histogram of Raster objects. |
| histogram-method | Histogram of Raster objects. |
| horizonplot | Horizon plots of Raster objects. |
| horizonplot-method | Horizon plots of Raster objects. |
| hovmoller | Hovmoller plots |
| hovmoller-method | Hovmoller plots |
| identifyRaster | Interaction with trellis objects. |
| identifyRaster-method | Interaction with trellis objects. |
| infernoTheme | Themes for 'raster' with 'lattice'. |
| levelplot | Level and contour plots of Raster objects. |
| levelplot-method | Level and contour plots of Raster objects. |
| magmaTheme | Themes for 'raster' with 'lattice'. |
| plasmaTheme | Themes for 'raster' with 'lattice'. |
| plot3D | Interactive 3D plot of a RasterLayer |
| plot3D-method | Interactive 3D plot of a RasterLayer |
| PuOrTheme | Themes for 'raster' with 'lattice'. |
| rasterTheme | Themes for 'raster' with 'lattice'. |
| rasterVis | Visualization Methods for Raster Data |
| RdBuTheme | Themes for 'raster' with 'lattice'. |
| splom | Scatter plot matrices of Raster objects. |
| splom-method | Scatter plot matrices of Raster objects. |
| streamplot | Vector plots of Raster objects. |
| streamplot-method | Vector plots of Raster objects. |
| streamTheme | Themes for 'raster' with 'lattice'. |
| vectorplot | Vector plots of Raster objects. |
| vectorplot-method | Vector plots of Raster objects. |
| viridisTheme | Themes for 'raster' with 'lattice'. |
| xscale.raster | Themes for 'raster' with 'lattice'. |
| xscale.raster.subticks | Themes for 'raster' with 'lattice'. |
| xyLayer | xyLayer |
| xyplot | xyplot for Raster objects |
| xyplot-method | Formula methods |
| xyplot-method | xyplot for Raster objects |
| YlOrRdTheme | Themes for 'raster' with 'lattice'. |
| yscale.raster | Themes for 'raster' with 'lattice'. |
| yscale.raster.subticks | Themes for 'raster' with 'lattice'. |


--- layout: post title: For the CFO – ERP in the Cloud [Infographic] date: '2012-02-04T00:12:23-08:00' tags: tumblr_url: http://alexhwilliams.tumblr.com/post/17021887931/for-the-cfo-erp-in-the-cloud-infographic ---
http://bit.ly/zcw6ED


Architect your application
- Your application is designed following the Service-Oriented architecture approach.
- You "specify" components providing and consuming services.
- No information about the deployment constraints are given at this step (seperation of preoccupations).


Implements interfaces:
This file is part of Terminalor.
For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
Terminalor core base application class provide global application methods and getters for the Request, Response objects.
Located in /Application/Abstract.php (line 16)
| Class | Description |
|---|---|
| Terminalor_Application | This file is part of Terminalor. |
Collection of user defined commands
Request object
Response object
Register Terminalor autoloader
Get library absolute pathname
Terminalor_Application constuctor
Get assoc array of defined commands
Get request object responsible for commands dispatching and providing access to user specified arguments
Get response object responsible for user interactions such as displaying messages, promt, confirm. And related functionality.
Display help for command with given $commandName if $commandName is null help will be displayed for all available commands
Dispatches application and generates user output, works only under
cli interface, displays help if BadFunctionCallException exception is catched, or --help argument is passed, help can be displayed for all available commands or for specified command
Documentation generated on Tue, 07 Feb 2012 14:40:54 +1100 by phpDocumentor 1.4.4


Aquaponics, refers to any system that combines conventional aquaculture (raising aquatic animals such as snails, fish, crayfish or prawns in tanks) with hydroponics (cultivating plants in water) in a symbiotic environment.
In normal aquaculture, excretions from the animals being raised can accumulate in the water, increasing toxicity. In an aquaponic system, water from an aquaculture system is fed to a hydroponic system where the by-products are broken down by Nitrifying bacteria into nitrates and nitrites, which are utilized by the plants as nutrients, and the water is then recirculated back to the aquaculture system.


Recreating Awkward Gundam Animation
by Brian AshcraftCustom figure artist Dau Lau Boc Khoi has been bringing to life awkward Gundam animation in hand-sculpted polymer clay.
Early Gundam animation was not so great and produced some truly awkward, meme-worthy examples of bad animation. Those images now live on as sculptures.
“Quick project for a stressful week,” wrote the artist on Facebook.
ALERT! ALERT! Unidentified body block: {"url":"https:\/\/www.facebook.com\/daulaubockhoi15\/photos\/a.2680552148670728\/2680552308670712\/","thumbnail":{"id":"v8aqubspuax2o2bcbrkl","format":"jpg"},"type":"FacebookPost"}
ALERT! ALERT! Unidentified body block: {"url":"https:\/\/www.facebook.com\/daulaubockhoi15\/photos\/a.2680552148670728\/2680553145337295\/","thumbnail":{"id":"yinm0mp2syitngout1ae","format":"jpg"},"type":"FacebookPost"}
ALERT! ALERT! Unidentified body block: {"url":"https:\/\/www.facebook.com\/daulaubockhoi15\/photos\/a.2680552148670728\/2680553245337285\/","thumbnail":{"id":"gjpspx3hb8qeeevsxmkc","format":"jpg"},"type":"FacebookPost"}
Typically, Dau Lau Boc Khoi does kickass commissions like this, so it’s fun to see him let loose with these humorous pieces.
Typically, Dau Lau Boc Khoi does kickass commissions like this, so it’s fun to see him let loose with these humorous pieces.


Becoming a programmer
These blogs are going to be continues, meaning they will be updated as I move along in life, one day I feel one way about something the next I might feel a different way about it, these blogs are experiences in my programming life with minimal editing, so yeah its going to be rough but I do not want to do alot of edits to these blogs because I want the content to be what it is when I wrote it, I want to laugh at my mistakes one day. I am going to be randomly adding content to the different blogs, I will come up with a way to notify which blog has been updated. Anyways all of what i write here is me, my opinion as I am learning about my interests nothing here should be taken as the absolute truth, I could be wrong, This is how I understand things.
My programming adventure
The first time I actualy touched a computer or even seen one, these are like my earliest memories, it was in pre-school,
we would walk to our principals house (which was accross the road) like once every month or something and
we could play with her computer, this happened so seldom I only have one real functioning memory of this
particular thing happening, but its pretty vivid.
The second time I remember seeing a computer was when my dad bought us one at home, it was this white huge
desktop that sat ontop of the box, it was running some early version of windows, this is like 1999/2000,
I so would say its windows 99./98, at this point I know one thing about computers
I CAN PLAY GAMES ON THIS THING!
, yeah I grew up a mega gameboy, I loved games but I loved electronic games, TV games!!!
I started out at the computer with paint and MS-word, I would wake up early in the morning and just sit
at the PC creating doodles on paint, thinking about it I laugh at my self, how could a program like paint
manage to captivate me for all those hours? We did have games on our PC but it was the standard card games
that I didnt understand or minesweeper (which to date I dont understand), so those games weren't really fun
for 4/5 year old me, there was another game though it was about getting a monster and 3 people across a river
with a boat and not get any of the humans eaten in the process, I didnt know how to put on this game though
so everyday I would call my dad and he would start it up for me, so when he wasnt around I played with what
I could, word and paint.
After that my primamry school had weekly computer classes, i also remember us having a computer at home
from a really young age, but all up until my first year of Computers Science at Unam I really didn't know
a thing about programming or how computers really worked, I was labeled the tech kid of the family before
that because I was the person to bring your device to when it wasnt acting right and I would magically fix
it in record time, lol yeah I was that kid but really all I was doing most of the time was reading and following
instructions, there things were written all you had to do was read, but I really had no idea how computers
worked.
First year Computer Science, thats when I remember seeing my first programming language, I remember googling
how websites were made and ending up downloading dreamweaver, man I didnt understand jack shit, but I was
facinated by the amount of absolute jiberish you had to write just to get a computer to say "Hello World!"
it was almost funny at first like, what? why would anyone spend so much time writting all this just to make
a computer do something simple?
But I continued with it, I had already registered for computer science after I watched my medicine fly away
the year before, I chose Computer Science because I knew I would be working with computers alot and I liked
to play computer games plus computers don't bleed, trust me the not bleeding part was very important when
I was deciding what I was going to do everyday for the rest of my life.
ALright new day and its time toc continue this story, so I was going to be in Computer Science but had no
real knowledge about what that ment or what I was going to be doing , I swear at this point in life I thought
I was going to be troubleshooting computers and installing anti-viruses my whole life.
The first computer programming language I learned was Visual Basics and it was
DIFFICULT but I liked it, I didnt really understand how to do it but I did understand that I could
make computers do things and that blew my mind, what I learned during my first programming lesson was that
computers will never say no, it just depends on how you speak to them, So I decided then and there that I
would learn to be a very good "computer whisperer".
Another thing I remember becoming very clear to me was that everything that moves on a computer screan is
some how connected to a line of code, in visual basics I we would drag and drop these items onto the screen
and build the interface of our program we would than go and write the code that made these items on the screen
do things, it was kind of like a puppet show, you set up the puppets that the audience(users) will see while
you hide in the background and make things actualy happen, its all just an illusion.
After a few months of Computer Science I managed to make what I consider to be my first actual program,
we had to make a game for our VB class, I must admit I put some work into this I didn't really understand
what I was doing but i managed to come up with the most basic, most laughable version of Super Mario I had
ever seen, it was riddiculus(this deserves it own blog).
After about 3 months of Computer Science I got a chance to go to Malaysia and I jumped at it, God knows where Malaysia is and who lives there but I was going there, I was going to do Bioinformatics I googled it and it was a pretty scary introduvtion, I read about DNA and gentic engineering but it was all being done with computers, what? Anyways I read computers and I really wanted to get out of Namibia so I fugured "Fuck it, The kids will be alright" and boom I am on a plane to unknown lands, I underestimated just how long 5 years really were.
By the time I got to Malaysia I knew there was something called programming and I was determined to get good at it because if I did I could make computers do all sorts of things, so I was determined to be excellent at programming, The first real programming I did was in C++, and it was a pain, it was difficult but I was determined to get good at it, I went all the way from 1st year to about third year without really understanding how to program, I understood how it worked and what it could be used for but I didnt really understand how to do it, but I kept at it, trying and trying to wrap my head around this thing, I spent so much energy on programming I didnt even care about my other classes any more and I still didnt understand why.
This all changed early 3rd year when I discovered Python and BOOM it all started falling in place, python was easy to write because its very close to English and its just so intuitive, all the lessons I had learned in Java, C++, R and VB all started to make sense, I finally understood that programming was like a language, there are rules to follow, I started writting a whole host of scripts in python after that I could finally make my computer do cool things, I could make it clean its own desktop, I could listen to random songs on youtube as I studied, if I found one I liked I didnt have to do much I just coppied its title to a txt file and saved, I had a script that went online and downloaded albums by this artists as well as its wikipedia page, the pages were saved on my google drive and so automatically synced to my ipad, so every morning I would get up pick up my ipad and sync my phone, as I listened to this new artist at school I also took time to read thier wikis lol it gave the albums more meaning.
I had a few cool little scripts on my computer, most of them were inspired by an article I had read online about a lazy programmer who had all these crazy little scripts on his computer that did all the things that he does on a daily bases, it was pretty hilarious but got me thinking, its true we dont have to do all the things we do on a day to day bases, computers are good at repetitvie things. The only problem with my scripts were everyone was making cool apps that other people could easily use over the internet and all my scripts were on my PC, this is how I learned about APIs, at the time I was doing alot of tweeting, like alot, so i decided to build something that I could share with my "friends" on twitter and I built When_did, it was this little script that allowed users to query when other users started tweeting by tweeting something like @blavejr when did @person, the script would than check if the tweet had 4 words, break down the text remove the words @blavejr and "when did", query twitters API for the data and replied. It was alot of fun building it but watching people use it and like it was even more rewarding, watching people laugh and have fun was encouraging, I went on to write a number of other twitter, facebook, whatsapp and telegram bots after that one even landed me into Microsofts Imagibe Cup, but thats another story for another day.
The first taste of commercial programming (My first job), was my internship.
During my final year I had to make a decision wheather to go work for a large security company or this small start up called Epnox Technologies, I was about to go for the larger company because the offered me more money and I was thinking that would look better on my resume, but the semester before my friends had gone out on internship and they had all gone to this huge company and ended up being used to file papers, get coffee or the worst they were put in the call service department, ansering calls from users and troubleshooting thier problems.
I wasnt gonna risk being put in a call center, so I went to Epnox and I dont ever regret going there. The first thing I did at Epnox was try and set up the website and I failed miserably, I didnt have enough HTML and CSS knowledge to get the website up and to make matters worse I knew zero javascript, up until this point my programming had been confined to my little beloved python scripts, but I was determined to learn so I got onto youtube and got a few tutorials after about 3 days I presented my website which was hidious and gracefully rejected by my boss.
I was than re-assigned to the iOS department, we had an Android expert at the office from russia, Artem seemed to know everything Androidy and could code at the speed of light, I didnt even know what language iOS apps were built in, I was working with Nabil a rather cool kid, he was writting PHP CRON jobs and started iOS at the same time as I did, so we set there and watched countless youtube videos trying to learn as fast as possible how to make an app in iOS.
We staretd of with swift and after about 2 weeks the code was flowing and I understood what was going on, I started on the sign up and sign in of the app, and it was major fun, swift was a subtle sexy language, support was pretty tricky to find but stackoverflow always had something to point you into the right direction.
After finishing up on sign in and sign up I had to move on to the map aspect of the app, the app we were building was called PicHere and it was like instagram on a google maps, the atmosphere at Epnox was amazing, working hours were very flexible, we made jokes as we worked, there was no hierachy, working for a start up was amazing. Swift was nice until I needed to integrate google's maps into the app, see google map libraries are written in Objective-C and accessing them with swift was difficult, I tried and tried but couldn't get them to work, we stayed up with Nabil on many nights trying to find a solution but to no avail, I even remember downloading some other map clustering library off github and trying to edit that to fit my needs, the library was a swift re-write of google's Objective-C library and it worked but it was extremely buggy and inconsistent, I spent another week trying to fix it, here I learned indepth how google's library worked.
It wasnt long before Nabil also ran into similar issues, he couldnt get the Objective-C libraries for the chat SDK to work with swift and I watched him feel my pain losing his mind trying to find a fix. One day our project manager Eric Goh walks in and makes the decision that we are going to swicth from swift to Objective-C, we were about a month into development and needed to throw away all the code we had written and start over with Objective-C, we had to learn a new langauge and renbuild everything in record time, I remember the API guy saying there was no way we could do it, Objective-C was too difficult and messy, his Name was Ridz and he was pretty good at what he did, he was an experienced backend developer who know what they were doing.
We surprisingly picked up Objective-C quickly and had managed to get back on track in less than a month, We were in hyperdrive. Objective-C proved tough and messy at first sight but after a while it startes to make sense and becomes intuitive, I actualy liked Objective-C more than I did swift.
My internship was 3 months long but the company gave me a further 4 month full time contract, in total I spent about 7 months at Epnox and I had a great time there, this is when I went from a script kiddie to a junior developer, I learned a great deal about how applications were built and had a better understanding of the process and requirements. Programming was what I wanted to do for the rest of my life, I really enjoyed it. Home and Google Rainmaker.
I started at Rainmaker an over-confident young junior developer, I could somewhat write code at this point, I was able to go from nothing to finished product that actualy works but might not be very scalable but it some how gets the job done. I was called in because they needed someone who could write scripts that can access API's and add functionality to websites, I was pretty confident , I had written scripts accessing API's since college.
I had to start off by learning javascript as fast as possible, at this point in my programming career I have had to learn enough new languages to develop a system that allows me to learn additional languages faster, really I just realised that all langauges do the same basic things the only thing that changes is the syntax, so after about a 2 weeks I was flowing javascript, I didnt really understand what I was doing but I could stitch work together.
I was introduced to Stephan Lambard, who is an extremely experienced javascript backend programmer, he was tasked with sort of mentoring me, getting me to understand more advanced programming concepts and within about 3 weeks I had learned an astonishing much from the man. I learned what an API really is, how to make one and protect it. At this point in time I also learned about the importance of object oriented design, I learned that it is important to sit down and plan before jumping into making even the smallest of projects, messy code will frustrate the living soul out of you.
Another thing I learned from Stephen Lambard which I must admit I didnt understand until my code base became too big to map out in my head, it was to
write clean code
, I cannot now stress enough the importance of writting clean code, readable code with variable names that
are explicit and fully explain what the variable does, I remember reading one of Stephens libraries and I was
amazed by the clearity in the mans code, everything was neat and flowing, it was almost as if it was written
in English. Clean robust readable code will save you hours of work.
Another concept I started to grasp around this time is the concept of Object Oriented Programming, OOP has been one of those things as a programmer I knew how to use it but I didnt really understand it, its just one of those things you think you understand but sometimes a scenario comes up and you are just baffeled. I started reading boooks on OOP trying to fully grasp it and I would say at the time of writting this I do have a better understanding of OOP now.
I was moved around alot at Rainmaker never doing one job (I mean I was programming but I was programming everything and anything), I was the programmer who was sent anywhere where additional hands were needed and I learned a great deal from all this activity, I had OK knowledge of HTML and CSS when I came to Rainmaker, I mean I know what they did but not very much about how to make them do these things, but soon I found myself in the front end developer team of Rainmaker I wasnt only writting them scripts for functionality anymore, I actualy had to design websites now.
They were using bootstraps 4 and MDB to make these amazing websites, when I got re-assigned I didnt like frontend work very much, it all seemed counter-intuitive, html didnt make sense to me, neither did css, "Hoe on earth is this considered programming" was what I also thought when I tried front end work, "where is the logic?" its was all a mess to me, but when the boss decided more hands were needed on the frontend side I jumped at it, I started learning html and CSS, I picked it up pretty quickly too, by the end of the first week I was playing around with animate.css, by the end of the 2nd week I had my first design for a customer, unlike at Epnox this one was actualy very nice and I was proud of myself too.
I kept trying to better my frontend design skills as well as my API and app development skills and thats how we ended up here with this blog kind of platform, its been a really fun experience thus far, I learned alot and continue to learn everyday becoming a programmer isnt something one can do over night, not even within a few months, becoming a programmer takes time, effort and persistence, but you will fall inlove with the process, everyday you will go to work and be amazed, as you learn new things, this quest to become a computer whisperer has changed me in so many ways, the way I look at problems to is very different because of programming, your attention to details will go through the rough, your ability to focus for long hours will also be enhanced, becomng a computer programmer is amazing!