diff --git a/my-app/app/admin/admin-sidebar.tsx b/my-app/app/admin/admin-sidebar.tsx index 7b67aa9..895e869 100644 --- a/my-app/app/admin/admin-sidebar.tsx +++ b/my-app/app/admin/admin-sidebar.tsx @@ -16,7 +16,7 @@ import { SidebarMenuItem, SidebarFooter, } from "@/components/ui/sidebar" -import { Camera, Copy, CopyPlusIcon, Edit, LayoutDashboard, Rocket, Server, User } from "lucide-react" +import { Copy, CopyPlusIcon, Edit, LayoutDashboard, Rocket, Server, User } from "lucide-react" import { IconUsersGroup } from "@tabler/icons-react" const data = { @@ -97,24 +97,18 @@ const data = { title: "Guides", url: "#", items: [ - { - title: "Users", - url: "/admin/guides/users", - isActive: false, - icon: User - }, { title: "Templates", url: "/admin/guides/templates", isActive: false, - icon: CopyPlusIcon + icon: Copy }, { - title: "Snapshots", - url: "/admin/guides/snapshots", + title: "Users & Groups", + url: "/admin/guides/users", isActive: false, - icon: Camera - } + icon: User + }, ], } ], @@ -128,7 +122,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { - + Logo
diff --git a/my-app/app/admin/guides/templates/page.tsx b/my-app/app/admin/guides/templates/page.tsx index b01f9e5..1411d1d 100644 --- a/my-app/app/admin/guides/templates/page.tsx +++ b/my-app/app/admin/guides/templates/page.tsx @@ -2,25 +2,756 @@ import { AuthGuard } from "@/components/auth-guard" import { PageLayout } from "@/app/admin/admin-page-layout" +import { Card, CardContent, CardHeader } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" +import { + Server, + Settings, + Info, + TriangleAlert, + List, + Link2, + PenBox, + LucideMousePointerClick, + FileText, + User, + Boxes, + ImageIcon, + Eye, + Tag, + Network, + Search, + Upload, +} from "lucide-react" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { MarkdownRenderer } from "@/components/ui/markdown-renderer" +import Link from "next/link" +import Image from "next/image" +import { useState, useEffect } from "react" -const breadcrumbs = [{ label: "Templates Guide", href: "/admin/guides/templates" }] +const breadcrumbs = [{ label: "Template Creation & Management Guide", href: "/admin/guides/templates" }] + +// Table of Contents structure +const tableOfContents = [ + { id: "step-1", title: "Step 1: Create Template", level: 1 }, + { id: "pool-creation", title: "1.1 Create Template Pool", level: 2 }, + { id: "vnet-creation", title: "1.2 Create Template Virtual Network (VNet)", level: 2 }, + { id: "vm-creation", title: "1.3 Create and Configure Virtual Machines", level: 2 }, + { id: "vm-preparation", title: "1.4 Prepare VMs for Template Conversion", level: 2 }, + { id: "step-2", title: "Step 2: Publish Template", level: 1 }, + { id: "publishing-wizard", title: "2.1 Navigate to Publishing Wizard", level: 2 }, + { id: "configure-details", title: "2.2 Configure Template Details", level: 2 }, + { id: "select-template", title: "2.2.1 Select Template", level: 3 }, + { id: "description", title: "2.2.2 Description", level: 3 }, + { id: "authors", title: "2.2.3 Authors", level: 3 }, + { id: "vm-count", title: "2.2.4 VM Count", level: 3 }, + { id: "image", title: "2.2.5 Image", level: 3 }, + { id: "preview-template", title: "2.2.6 Preview Template", level: 3 }, + { id: "visibility", title: "2.2.7 Visibility", level: 3 }, + { id: "step-3", title: "Step 3: Manage Template", level: 1 } +] + +// Table of Contents Component +function TableOfContents() { + const [maxHeight, setMaxHeight] = useState('calc(100vh - 2rem)') + + useEffect(() => { + const updateHeight = () => { + setMaxHeight(`${window.innerHeight - 32}px`) // 32px for top/bottom padding + } + + updateHeight() + window.addEventListener('resize', updateHeight) + + return () => { + window.removeEventListener('resize', updateHeight) + } + }, []) + + const scrollToSection = (id: string) => { + const element = document.getElementById(id) + if (element) { + const offset = 80 // Account for any fixed headers + const elementPosition = element.getBoundingClientRect().top + const offsetPosition = elementPosition + window.pageYOffset - offset + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }) + } + } + + return ( +
+ + +
+ + Table of Contents +
+
+ + + +
+
+ ) +} + +const markdownExampleText = `## Headers + +# This is a Heading h1 +## This is a Heading h2 +### This is a Heading h3 + +## Emphasis + +*This text will be italic* +_This will also be italic_ + +**This text will be bold** +__This will also be bold__ + +_You **can** combine them_ + +## Lists + +### Unordered + +* Item 1 +* Item 2 +* Item 2a +* Item 2b + * Item 3a + * Item 3b + +### Ordered + +1. Item 1 +2. Item 2 +3. Item 3 + 1. Item 3a + 2. Item 3b + +## Images + +![This is an alt text.](/2-Linux-VM-Via-ISO.png) + +## Links + +Wow look at [Cheese dot com](https://cheese.com). + +## Blockquotes + +> Markdown is a lightweight markup language with plain-text-formatting syntax, created in 2004 by John Gruber with Aaron Swartz. +> +>> Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. + +## Tables + +| Left columns | Right columns | +| ------------- |:-------------:| +| left foo | right foo | +| left bar | right bar | +| left baz | right baz | + +## Blocks of code + +${'```'}typescript +// 3. Wait for running VMs to be stopped +// If a VM cannot be verified as stopped, this function will error out +for _, vm := range runningVMs { + if err := cs.ProxmoxService.WaitForStopped(vm.NodeName, vm.VmId); err != nil { + log.Printf("Error waiting for VM %d to stop: %v", vm.VmId, err) + return fmt.Errorf("failed to confirm VM %d is stopped: %w", vm.VmId, err) + } +} +${'```'} + +## Inline code + +This code is ${'`'}inline${'`'}.` export default function AdminTemplatesGuide() { return ( + -
-
-
-

Templates

-

- Placeholder -

+
+
+
+
+

+ Template Creation & Management +

+

+ Complete guide for creating, publishing, and managing VM templates in Kamino through Proxmox integration +

+
+ + {/* Template Workflow Infographic */} + + +
+
+ +
Proxmox
+
Create Template
+
+
+
+
+
+ +
Admin Panel
+
Publish Template
+
+
+
+
+
+ +
Admin Panel
+
Manage Template
+
+
+
+
+ + {/* Creating Templates Section */} +
+
+
+

+ Step 1: Create Template +

+ +
+ {/* Pool Creation */} +
+

+ + 1.1 Create Template Pool +

+

+ Pools in Proxmox help organize and manage related VMs. All template VMs must be placed in a designated pool with the correct naming convention. +

+
    +
  1. Navigate to Datacenter → Pools in the Proxmox web interface
  2. +
  3. Click “Create” to add a new pool
  4. +
  5. Use the naming convention: kamino_template_[template_name]
  6. +
  7. Example: kamino_template_test_linux → Template name will be “Test Linux”
  8. +
+ +
+ Proxmox Create Pool - Shows the pool creation dialog with naming convention +
+
+ + {/* VNet Creation */} +
+

+ + 1.2 Create Template Virtual Network (VNet) +

+

+ Each template requires its own isolated network to prevent conflicts and ensure proper network segmentation. +

+
    +
  1. Navigate to Datacenter → SDN → VNets
  2. +
  3. Click “Create” to add a new virtual network
  4. +
  5. Next, Name the VNet. Keep in mind the name has a limit of just 8 characters
  6. +
  7. Choose a unique VLAN tag above 1000 (e.g., 1001, 1002, etc.)
  8. +
  9. Important: After creating the VNet, go to Datacenter → SDN and click “Apply” to activate the changes
  10. +
+ + + +
+ Warning + + Ensure your VLAN tag is unique across all templates and above 1000 to avoid conflicts with system networks. + +
+
+ +
+
+ Proxmox Create VNet - Main VNet creation interface +

VNet creation interface in Proxmox SDN

+
+
+
+ Proxmox VNet Dialog - Configuration options for the virtual network +

VNet configuration dialog

+
+
+ Proxmox Apply SDN Changes - Applying network configuration changes +

Applying SDN configuration changes

+
+
+
+
+ + {/* VM Creation */} +
+

+ + 1.3 Create and Configure Virtual Machines +

+

+ Set up all VMs within the created pool with your desired operating systems and configurations. Follow the specific steps below for different OS types. +

+ + {/* ISO Upload */} +
+
Uploading ISO Files
+ + + +
+ Info + + Prior to uploading an ISO, check if your OS is not already a template in the Templates pool in Proxmox these templates should have VmIDs in the range of the 4000s. To use these, simply clone the desired template into your pool. + +
+
+ +

+ Upload the necessary ISO files to your mufasa-proxmox. +

+
+ Proxmox Uploading ISO - Process for uploading ISO files to Proxmox storage +

Uploading ISO files to Proxmox storage

+
+
+ + {/* Linux VM Configuration */} +
+
Linux VM Configuration
+

+ Follow these steps to create a Linux-based virtual machine. Ensure you select the appropriate settings for optimal performance. +

+
+
+ Linux VM Creation - Initial VM creation from ISO +

Creating a new Linux VM from ISO

+
+
+
+ Linux VM General Settings - Basic VM configuration options +

General VM settings and pool assignment

+
+
+ Linux VM OS Settings - Operating system configuration +

Operating system selection and configuration

+
+
+
+
+ Linux VM Disk Settings - Storage configuration for the VM +

Disk configuration and storage allocation

+
+
+ Linux VM Network Settings - Network configuration using the created VNet +

Network configuration with the template VNet

+
+
+
+
+ + {/* Windows VM Configuration */} +
+
Windows VM Configuration
+

+ Windows VMs require specific drivers and configurations for optimal performance in a virtualized environment. Pay attention to driver selection and hardware compatibility. +

+
+
+
+ Windows VM OS Selection - Choosing Windows operating system type +

Windows OS type selection

+
+
+ Windows VM Driver Loading - Loading VirtIO drivers for better performance +

Loading VirtIO drivers during installation

+
+
+ Windows VM Architecture - Selecting appropriate architecture +

Architecture and system type selection

+
+
+ Windows VM VirtIO Configuration - Final VirtIO driver configuration +

VirtIO driver configuration for optimal performance

+
+
+
+
+
+ + {/* VM Preparation */} +
+

+ + 1.4 Prepare VMs for Template Conversion +

+

+ Before converting VMs to templates, proper cleanup and optimization ensures better performance and smaller template sizes. +

+
    +
  1. Complete Shutdown: Ensure all VMs are completely shut down before proceeding
  2. +
+
+
+
+ +
+

+ Step 2: Publish Template +

+ +

+ Once your VMs are properly configured and prepared, use the Kamino admin panel to convert them into deployable templates. This process will convert the VMs to Proxmox templates and make them available for users to deploy. +

+ + + +
+ Warning + + Publishing a template will automatically: +
    +
  • Remove all snapshots from VMs in the Proxmox pool
  • +
  • Convert VMs to read-only VM templates in Proxmox
  • +
  • Make future VM modifications extremely difficult
  • +
+ Ensure all configurations are finalized before proceeding. +
+
+
+ +
+
+

+ + 2.1 Navigate to Publishing Wizard +

+ +

+ In the Kamino admin panel, go to{' '} + + Publish Templates + +

+
+ +
+

+ + 2.2 Configure Template Details +

+ +
+
+

+ + 2.2.1 Select Template +

+ +
+

+ Choose the Proxmox pool containing the VMs you wish to publish as a template from the dropdown selection. The template name will be auto-generated from the pool name. +

+ Select Template Pod +
+
+ +
+

+ + 2.2.2 Description +

+ +

+ Provide a detailed description or steps to complete this template. This field supports markdown formatting. +

+
+ + + + + + Plaintext + Rendered Markdown + + + + +
+
+                                      {markdownExampleText}
+                                    
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+

+ + 2.2.3 Authors +

+ +

+ Specify the authors or creators of the template (max 255 characters). For example if you are part of a club you could put the club name with your name as the author. EX (SWIFT, Maxwell Caron) +

+
+ +
+

+ + 2.2.4 VM Count +

+ +

+ Specify how many VMs the template contains (1-12) +

+
+ +
+

+ + 2.2.5 Image +

+ +
+

+ Optionally upload an image to represent the template visually (max 10MB). You will be able to crop your image upon upload. +

+
+ Template Publish Image +

Crop pod template image

+
+
+
+ +
+

+ + 2.2.6 Preview Template +

+ +
+

+ View an exact preview of what your template will look like to users once published. You will be able to view both the small card view and the detailed view. +

+
+
+ Template Publish Small Preview +

Small pod template preview

+
+
+ Template Publish Large Preview +

Large popup pod template preview

+
+
+
+
+ +
+

+ + 2.2.7 Visibility +

+ +

+ The final step is to choose whether the template should be publicly visible to all users or kept private when published. This can be changed later in the template management section. +

+
+
+
+
+
+ +
+

+ Step 3: Manage Template +

+ + + +
+ Info + + Deleting a template will not delete the underlying VMs in Proxmox. You must manually delete the VMs from Proxmox if you wish to remove them. + +
+
+ +
    +
  1. Admin Panel: In the Kamino admin panel, navigate to the All Templates section
  2. +
  3. Manage: Click on the actions dropdown menu on the far right of the table row. You can edit all of the template details, quickly toggle the visibility of the template, or delete the template.
  4. +
+
+ +
+
- {/* content */}
diff --git a/my-app/app/admin/guides/users/page.tsx b/my-app/app/admin/guides/users/page.tsx index af2372d..d44698f 100644 --- a/my-app/app/admin/guides/users/page.tsx +++ b/my-app/app/admin/guides/users/page.tsx @@ -2,8 +2,12 @@ import { AuthGuard } from "@/components/auth-guard" import { PageLayout } from "@/app/admin/admin-page-layout" +import { Card, CardContent } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" +import { Settings, Database, Server, Info, Plus, Minus } from "lucide-react" +import Link from "next/link" -const breadcrumbs = [{ label: "Users Guide", href: "/admin/guides/users" }] +const breadcrumbs = [{ label: "Users & Groups Guide", href: "/admin/guides/users" }] export default function AdminUsersGuide() { @@ -12,15 +16,197 @@ export default function AdminUsersGuide() { -
-
-
-

Users

-

- Placeholder +

+
+
+
+

+ User & Group Management +

+

+ Comprehensive guide for managing users and groups through Active Directory with automated Proxmox synchronization

- {/* content */} + + {/* System Architecture Infographic */} + + +
+
+ +
Admin Panel
+
User Management
+
+
+
+
+
+ +
Active Directory
+
LDAP Operations
+
+
+
+
+
+ +
Proxmox
+
VM Access Control
+
+
+
+
+ + {/* User Management Section */} +
+
+

+ User Management +

+ +

+ Users are created, modified, and removed through the Users Kamino admin panel page. All changes are synchronized with Active Directory and Proxmox to ensure consistent access control. +

+
+ + {/* Alert for Usernames and Passwords */} + + +
+ Info + +
+

Usernames

+
    +
  • Must be unique across the entire Active Directory domain
  • +
  • Must be between 3-20 characters long
  • +
  • Can include letters and numbers
  • +
+
+
+

Passwords

+
    +
  • Must be between 8-128 characters long
  • +
  • Must include at least one letter and number
  • +
+
+
+
+
+ +
+
+

+ + Creating Users +

+
    +
  1. + Create New User: Click “Add User” and fill in required details. There are two methods of user creation: +
      +
    • Single: Create a single user with a simple username and password
    • +
    • Bulk: Input a comma and newline separated list of usernames and passwords
    • +
    +
  2. +
+
+ +
+

+ + Modifying Users +

+
    +
  1. Group Membership: Add or remove the user from groups to change access permissions. Groups that contain “Kamino” or “Admin” cannot be modified
  2. +
+
+ +
+

+ + Removing Users +

+
    +
  1. Disable Account: Disable the account to prevent access
  2. +
  3. Delete Account: Remove the account from Active Directory and all connected systems
  4. +
+
+ +
+
+ + {/* Group Management Section */} +
+
+

+ Group Management +

+ +

+ Groups are created, modified, and removed through the Groups Kamino admin panel page. All changes are synchronized with Active Directory and Proxmox to ensure consistent access control. + Admins can use groups if they want to clone a single pod for multiple users to interact with. For example, if a competition were to be held, an admin could create a group for each team, clone a pod for each group, and add users to the appropriate groups instead of making a user for each team. +

+
+ + + +
+ Info + +
+

Group Names

+
    +
  • Must be unique across the entire Active Directory domain
  • +
  • Must be between 3-63 characters long
  • +
  • Can include letters, numbers, hyphens, and underscores
  • +
  • Groups containing “Kamino” or “Admin” cannot be modified or removed
  • +
+
+
+
+
+ +
+
+

+ + Creating Groups +

+
    +
  1. + Create New Group: Click “Add Group” and specify the group name. There are three methods of group creation: +
      +
    • Single: Create a single group with a unique name
    • +
    • Bulk: Input a newline separated list of group names
    • +
    • Prefix: Create multiple groups with a common prefix and a numbered suffix
    • +
    +
  2. +
+
+ +
+

+ + Modifying Groups +

+
    +
  1. Edit Properties: Rename the group
  2. +
+
+ +
+

+ + Removing Groups +

+
    +
  1. Delete Group: Select the group to be removed and click “Delete”
  2. +
+
+ +
+
+
diff --git a/my-app/app/admin/pods/templates/publish/image-dropzone-with-cropper.tsx b/my-app/app/admin/pods/templates/publish/image-dropzone-with-cropper.tsx index be7a6fa..f58a93d 100644 --- a/my-app/app/admin/pods/templates/publish/image-dropzone-with-cropper.tsx +++ b/my-app/app/admin/pods/templates/publish/image-dropzone-with-cropper.tsx @@ -178,7 +178,7 @@ const TemplateImageDropzoneWithCropper = ({ onClick={handleReset} className="shrink-0" > - +
@@ -189,15 +189,15 @@ const TemplateImageDropzoneWithCropper = ({ variant="outline" onClick={handleCropCancel} > - + Cancel
diff --git a/my-app/app/admin/pods/templates/publish/step-one.tsx b/my-app/app/admin/pods/templates/publish/step-one.tsx index 94d9d46..ceb0b1e 100644 --- a/my-app/app/admin/pods/templates/publish/step-one.tsx +++ b/my-app/app/admin/pods/templates/publish/step-one.tsx @@ -17,6 +17,7 @@ import { AlertTriangle } from "lucide-react" import { getUnpublishedTemplates } from "@/lib/api" import { UnpublishedPodTemplate } from "@/lib/types" import { Separator } from "@/components/ui/separator" +import { formatPodName } from "@/lib/utils" interface StepOneProps { selectedTemplate: string @@ -92,7 +93,7 @@ export function StepOne({ selectedTemplate, onTemplateSelect, onNext }: StepOneP {templates.map((template) => ( - {template.name} + {formatPodName(template.name)} ))} @@ -109,9 +110,14 @@ export function StepOne({ selectedTemplate, onTemplateSelect, onNext }: StepOneP Notice - Publishing this template will convert all VMs in the pool to templates. - Once converted, it will be very difficult to make edits to the environment. - Please ensure you have completed all necessary configurations before proceeding. +
+

This will attempt to convert all VMs to templates, meaning:

+
    +
  • No further changes can be made to the VMs
  • +
  • All running VMs will be shutdown
  • +
  • All VM snapshots will be deleted
  • +
+
)} diff --git a/my-app/app/admin/pods/templates/publish/step-three.tsx b/my-app/app/admin/pods/templates/publish/step-three.tsx index 19da1e5..cf08736 100644 --- a/my-app/app/admin/pods/templates/publish/step-three.tsx +++ b/my-app/app/admin/pods/templates/publish/step-three.tsx @@ -1,7 +1,7 @@ "use client" import { useId, useState } from "react" -import { Eye, EyeOff, RocketIcon, ServerIcon, CalendarIcon, Rocket } from "lucide-react" +import { Eye, EyeOff, RocketIcon, ServerIcon, Calendar, Rocket, User } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" @@ -9,15 +9,23 @@ import { Switch } from "@/components/ui/switch" import { Separator } from "@/components/ui/separator" import { MarkdownRenderer } from "@/components/ui/markdown-renderer" import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog" +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion" import { ScrollArea } from "@/components/ui/scroll-area" import { VisuallyHidden } from "radix-ui" import Image from "next/image" +import { formatPodName } from "@/lib/utils" interface StepThreeProps { selectedTemplate: string description: string vmCount: number imageFiles: File[] + authors?: string isTemplateVisible: boolean onVisibilityChange: (templateVisible: boolean) => void onSubmit: () => void @@ -30,6 +38,7 @@ export function StepThree({ description, vmCount, imageFiles, + authors, isTemplateVisible, onVisibilityChange, onSubmit, @@ -58,7 +67,7 @@ export function StepThree({

Template Preview

{/* Pod Image */} @@ -88,32 +97,38 @@ export function StepThree({ {/* Pod Content */}
- {/* Pod Name */} -

{selectedTemplate}

- - {/* Pod Description */} -
-
- -
-
+ {/* Date, title, & authors */} +
+

+ + {new Date().toLocaleDateString(undefined, { + year: "numeric", + month: "short", + day: "numeric", + })} +

+

+ {formatPodName(selectedTemplate)} +

+ {authors && ( +
+ + {authors} +
+ )}
{/* Pod Stats */}
-
+
{/* VMs */}
-
{vmCount}
+
{vmCount}
- + {vmCount === 1 ? "VM" : "VMs"}
@@ -126,10 +141,10 @@ export function StepThree({ {/* Deployments */}
-
0
+
0
- + Deployments
@@ -143,8 +158,6 @@ export function StepThree({
- {/* */} - {/* Visibility Settings */}

Visibility

@@ -176,20 +189,6 @@ export function StepThree({
- {/* */} - - {/* Ready to Publish */} - {/*
-
- -

Ready to Publish

-
-

- Your template configuration is complete and ready to be published. - Once published, the template will be {isTemplateVisible ? "immediately available to users" : "saved but hidden from users"}. -

-
*/} - {/* Navigation Buttons */}
-
+ {/* Bottom buttons - fixed at bottom */} + +
) -} +} \ No newline at end of file diff --git a/my-app/app/admin/pods/templates/publish/step-two.tsx b/my-app/app/admin/pods/templates/publish/step-two.tsx index 5511b2e..2e6630c 100644 --- a/my-app/app/admin/pods/templates/publish/step-two.tsx +++ b/my-app/app/admin/pods/templates/publish/step-two.tsx @@ -6,6 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import TemplateImageDropzoneWithCropper from "./image-dropzone-with-cropper" import { Separator } from "@/components/ui/separator" @@ -65,7 +66,7 @@ export function StepTwo({ Configure Template Details - Add description, image, and specify the number of VMs + Add a description, list of authors, number of VMs, and an image to your template. @@ -75,7 +76,7 @@ export function StepTwo({